Skip to content

Commit

Permalink
Rollup merge of rust-lang#94440 - compiler-errors:issue-94282, r=lcnr
Browse files Browse the repository at this point in the history
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: rust-lang#79018
cc: rust-lang#94287
  • Loading branch information
m-ou-se authored Mar 10, 2022
2 parents 321e159 + 109cdc7 commit 20bae16
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 25 deletions.
88 changes: 63 additions & 25 deletions compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,14 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
span: Span,
) -> Result<(), NotConstEvaluatable> {
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)? {
// We are looking at a generic abstract constant.
Some(ct) => {
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(());
}
}
}
_ => {} // don't care
}
if satisfied_from_param_env(tcx, ct, param_env)? {
return Ok(());
}

// We were unable to unify the abstract constant with
Expand Down Expand Up @@ -163,6 +143,33 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
}
}

// 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");
match concrete {
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
Expand All @@ -178,6 +185,37 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
}
}

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.
///
/// This is only able to represent a subset of `MIR`,
Expand Down
Original file line number Diff line number Diff line change
@@ -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!()
}
}
10 changes: 10 additions & 0 deletions src/test/ui/const-generics/generic_const_exprs/issue-94287.rs
Original file line number Diff line number Diff line change
@@ -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("");
}
14 changes: 14 additions & 0 deletions src/test/ui/const-generics/generic_const_exprs/issue-94287.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 20bae16

Please sign in to comment.