Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error for normalization errors from parent crates that use #![feature(generic_const_exprs)] #94440

Merged
merged 1 commit into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#![feature(derive_default_enum)]
#![feature(hash_drain_filter)]
#![feature(label_break_value)]
#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![feature(crate_visibility_modifier)]
Expand Down
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