Rollup merge of #94440 - compiler-errors:issue-94282, r=lcnr
Better error for normalization errors from parent crates that use `#![feature(generic_const_exprs)]` This PR implements a somewhat rudimentary heuristic to suggest using `#![feature(generic_const_exprs)]` in a child crate when a function from a foreign crate (that may have used `#![feature(generic_const_exprs)]`) fails to normalize during codegen. cc: #79018 cc: #94287
This commit is contained in:
commit
b41374598f
@ -35,34 +35,14 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
|||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<(), NotConstEvaluatable> {
|
) -> Result<(), NotConstEvaluatable> {
|
||||||
debug!("is_const_evaluatable({:?})", uv);
|
debug!("is_const_evaluatable({:?})", uv);
|
||||||
if infcx.tcx.features().generic_const_exprs {
|
let tcx = infcx.tcx;
|
||||||
let tcx = infcx.tcx;
|
|
||||||
|
if tcx.features().generic_const_exprs {
|
||||||
match AbstractConst::new(tcx, uv)? {
|
match AbstractConst::new(tcx, uv)? {
|
||||||
// We are looking at a generic abstract constant.
|
// We are looking at a generic abstract constant.
|
||||||
Some(ct) => {
|
Some(ct) => {
|
||||||
for pred in param_env.caller_bounds() {
|
if satisfied_from_param_env(tcx, ct, param_env)? {
|
||||||
match pred.kind().skip_binder() {
|
return Ok(());
|
||||||
ty::PredicateKind::ConstEvaluatable(uv) => {
|
|
||||||
if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
|
|
||||||
// Try to unify with each subtree in the AbstractConst to allow for
|
|
||||||
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
|
||||||
// predicate for `(N + 1) * 2`
|
|
||||||
let result =
|
|
||||||
walk_abstract_const(tcx, b_ct, |b_ct| {
|
|
||||||
match try_unify(tcx, ct, b_ct) {
|
|
||||||
true => ControlFlow::BREAK,
|
|
||||||
false => ControlFlow::CONTINUE,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let ControlFlow::Break(()) = result {
|
|
||||||
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {} // don't care
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We were unable to unify the abstract constant with
|
// We were unable to unify the abstract constant with
|
||||||
@ -163,6 +143,33 @@ enum FailureKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're evaluating a foreign constant, under a nightly compiler without generic
|
||||||
|
// const exprs, AND it would've passed if that expression had been evaluated with
|
||||||
|
// generic const exprs, then suggest using generic const exprs.
|
||||||
|
if concrete.is_err()
|
||||||
|
&& tcx.sess.is_nightly_build()
|
||||||
|
&& !uv.def.did.is_local()
|
||||||
|
&& !tcx.features().generic_const_exprs
|
||||||
|
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
|
||||||
|
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
|
||||||
|
{
|
||||||
|
tcx.sess
|
||||||
|
.struct_span_fatal(
|
||||||
|
// Slightly better span than just using `span` alone
|
||||||
|
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
||||||
|
"failed to evaluate generic const expression",
|
||||||
|
)
|
||||||
|
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
||||||
|
.span_suggestion_verbose(
|
||||||
|
rustc_span::DUMMY_SP,
|
||||||
|
"consider enabling this feature",
|
||||||
|
"#![feature(generic_const_exprs)]\n".to_string(),
|
||||||
|
rustc_errors::Applicability::MaybeIncorrect,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
rustc_errors::FatalError.raise();
|
||||||
|
}
|
||||||
|
|
||||||
debug!(?concrete, "is_const_evaluatable");
|
debug!(?concrete, "is_const_evaluatable");
|
||||||
match concrete {
|
match concrete {
|
||||||
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
|
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
|
||||||
@ -178,6 +185,37 @@ enum FailureKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn satisfied_from_param_env<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
ct: AbstractConst<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
) -> Result<bool, NotConstEvaluatable> {
|
||||||
|
for pred in param_env.caller_bounds() {
|
||||||
|
match pred.kind().skip_binder() {
|
||||||
|
ty::PredicateKind::ConstEvaluatable(uv) => {
|
||||||
|
if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
|
||||||
|
// Try to unify with each subtree in the AbstractConst to allow for
|
||||||
|
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
||||||
|
// predicate for `(N + 1) * 2`
|
||||||
|
let result =
|
||||||
|
walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) {
|
||||||
|
true => ControlFlow::BREAK,
|
||||||
|
false => ControlFlow::CONTINUE,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let ControlFlow::Break(()) = result {
|
||||||
|
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {} // don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// A tree representing an anonymous constant.
|
/// A tree representing an anonymous constant.
|
||||||
///
|
///
|
||||||
/// This is only able to represent a subset of `MIR`,
|
/// This is only able to represent a subset of `MIR`,
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub struct If<const CONDITION: bool>;
|
||||||
|
|
||||||
|
pub trait True {}
|
||||||
|
|
||||||
|
impl True for If<true> {}
|
||||||
|
|
||||||
|
pub struct FixedI32<const FRAC: u32>;
|
||||||
|
|
||||||
|
impl<const FRAC: u32> FromStr for FixedI32<FRAC>
|
||||||
|
where
|
||||||
|
If<{ FRAC <= 32 }>: True,
|
||||||
|
{
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(_s: &str) -> Result<Self, Self::Err> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
// aux-build:issue-94287-aux.rs
|
||||||
|
// build-fail
|
||||||
|
|
||||||
|
extern crate issue_94287_aux;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = <issue_94287_aux::FixedI32<16>>::from_str("");
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
error: failed to evaluate generic const expression
|
||||||
|
--> $DIR/auxiliary/issue-94287-aux.rs:15:8
|
||||||
|
|
|
||||||
|
LL | If<{ FRAC <= 32 }>: True,
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the crate this constant originates from uses `#![feature(generic_const_exprs)]`
|
||||||
|
help: consider enabling this feature
|
||||||
|
|
|
||||||
|
LL | #![feature(generic_const_exprs)]
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user