about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-03-10 19:00:05 +0100
committerGitHub <noreply@github.com>2022-03-10 19:00:05 +0100
commitb41374598f3274e28273a447d2d7d82a6f26c1f3 (patch)
tree4b77913b043c34b54088f7874dbe887be0980cb7
parentba14a836c7038da21f5e102aacc7e6d5964f79a6 (diff)
parent109cdc754ed893edb25d2d2c1493023858c8eccb (diff)
downloadrust-b41374598f3274e28273a447d2d7d82a6f26c1f3.tar.gz
rust-b41374598f3274e28273a447d2d7d82a6f26c1f3.zip
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
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs88
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs21
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/issue-94287.rs10
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/issue-94287.stderr14
4 files changed, 108 insertions, 25 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 6655541461d..f880b28b3c8 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -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
@@ -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() {
@@ -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`,
diff --git a/src/test/ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs b/src/test/ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs
new file mode 100644
index 00000000000..df454dae725
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs
@@ -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!()
+    }
+}
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-94287.rs b/src/test/ui/const-generics/generic_const_exprs/issue-94287.rs
new file mode 100644
index 00000000000..643126a4640
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-94287.rs
@@ -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("");
+}
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-94287.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-94287.stderr
new file mode 100644
index 00000000000..c918651ba62
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-94287.stderr
@@ -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
+