Skip to content

Commit 20bae16

Browse files
authored
Rollup merge of rust-lang#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: rust-lang#79018 cc: rust-lang#94287
2 parents 321e159 + 109cdc7 commit 20bae16

File tree

4 files changed

+108
-25
lines changed

4 files changed

+108
-25
lines changed

compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

+63-25
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,14 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
3535
span: Span,
3636
) -> Result<(), NotConstEvaluatable> {
3737
debug!("is_const_evaluatable({:?})", uv);
38-
if infcx.tcx.features().generic_const_exprs {
39-
let tcx = infcx.tcx;
38+
let tcx = infcx.tcx;
39+
40+
if tcx.features().generic_const_exprs {
4041
match AbstractConst::new(tcx, uv)? {
4142
// We are looking at a generic abstract constant.
4243
Some(ct) => {
43-
for pred in param_env.caller_bounds() {
44-
match pred.kind().skip_binder() {
45-
ty::PredicateKind::ConstEvaluatable(uv) => {
46-
if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
47-
// Try to unify with each subtree in the AbstractConst to allow for
48-
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
49-
// predicate for `(N + 1) * 2`
50-
let result =
51-
walk_abstract_const(tcx, b_ct, |b_ct| {
52-
match try_unify(tcx, ct, b_ct) {
53-
true => ControlFlow::BREAK,
54-
false => ControlFlow::CONTINUE,
55-
}
56-
});
57-
58-
if let ControlFlow::Break(()) = result {
59-
debug!("is_const_evaluatable: abstract_const ~~> ok");
60-
return Ok(());
61-
}
62-
}
63-
}
64-
_ => {} // don't care
65-
}
44+
if satisfied_from_param_env(tcx, ct, param_env)? {
45+
return Ok(());
6646
}
6747

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

146+
// If we're evaluating a foreign constant, under a nightly compiler without generic
147+
// const exprs, AND it would've passed if that expression had been evaluated with
148+
// generic const exprs, then suggest using generic const exprs.
149+
if concrete.is_err()
150+
&& tcx.sess.is_nightly_build()
151+
&& !uv.def.did.is_local()
152+
&& !tcx.features().generic_const_exprs
153+
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
154+
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
155+
{
156+
tcx.sess
157+
.struct_span_fatal(
158+
// Slightly better span than just using `span` alone
159+
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
160+
"failed to evaluate generic const expression",
161+
)
162+
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
163+
.span_suggestion_verbose(
164+
rustc_span::DUMMY_SP,
165+
"consider enabling this feature",
166+
"#![feature(generic_const_exprs)]\n".to_string(),
167+
rustc_errors::Applicability::MaybeIncorrect,
168+
)
169+
.emit();
170+
rustc_errors::FatalError.raise();
171+
}
172+
166173
debug!(?concrete, "is_const_evaluatable");
167174
match concrete {
168175
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
@@ -178,6 +185,37 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
178185
}
179186
}
180187

188+
fn satisfied_from_param_env<'tcx>(
189+
tcx: TyCtxt<'tcx>,
190+
ct: AbstractConst<'tcx>,
191+
param_env: ty::ParamEnv<'tcx>,
192+
) -> Result<bool, NotConstEvaluatable> {
193+
for pred in param_env.caller_bounds() {
194+
match pred.kind().skip_binder() {
195+
ty::PredicateKind::ConstEvaluatable(uv) => {
196+
if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
197+
// Try to unify with each subtree in the AbstractConst to allow for
198+
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
199+
// predicate for `(N + 1) * 2`
200+
let result =
201+
walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) {
202+
true => ControlFlow::BREAK,
203+
false => ControlFlow::CONTINUE,
204+
});
205+
206+
if let ControlFlow::Break(()) = result {
207+
debug!("is_const_evaluatable: abstract_const ~~> ok");
208+
return Ok(true);
209+
}
210+
}
211+
}
212+
_ => {} // don't care
213+
}
214+
}
215+
216+
Ok(false)
217+
}
218+
181219
/// A tree representing an anonymous constant.
182220
///
183221
/// This is only able to represent a subset of `MIR`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(generic_const_exprs)]
2+
3+
use std::str::FromStr;
4+
5+
pub struct If<const CONDITION: bool>;
6+
7+
pub trait True {}
8+
9+
impl True for If<true> {}
10+
11+
pub struct FixedI32<const FRAC: u32>;
12+
13+
impl<const FRAC: u32> FromStr for FixedI32<FRAC>
14+
where
15+
If<{ FRAC <= 32 }>: True,
16+
{
17+
type Err = ();
18+
fn from_str(_s: &str) -> Result<Self, Self::Err> {
19+
unimplemented!()
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// aux-build:issue-94287-aux.rs
2+
// build-fail
3+
4+
extern crate issue_94287_aux;
5+
6+
use std::str::FromStr;
7+
8+
fn main() {
9+
let _ = <issue_94287_aux::FixedI32<16>>::from_str("");
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: failed to evaluate generic const expression
2+
--> $DIR/auxiliary/issue-94287-aux.rs:15:8
3+
|
4+
LL | If<{ FRAC <= 32 }>: True,
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: the crate this constant originates from uses `#![feature(generic_const_exprs)]`
8+
help: consider enabling this feature
9+
|
10+
LL | #![feature(generic_const_exprs)]
11+
|
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)