about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2021-12-08 16:08:07 +0100
committerGitHub <noreply@github.com>2021-12-08 16:08:07 +0100
commit317f750ff76ae680d6891de0a5087e1aa903bc6c (patch)
tree7a565f4c946b00116f01e2723b768ea13e621bc9
parent7970fab252f1bde4bba96142d0843747c6e9b4ad (diff)
parent3f831c64ccd210754d16d6ab9c287b02e3d3eabe (diff)
downloadrust-317f750ff76ae680d6891de0a5087e1aa903bc6c.tar.gz
rust-317f750ff76ae680d6891de0a5087e1aa903bc6c.zip
Rollup merge of #91551 - b-naber:const-eval-normalization-ice, r=oli-obk
Allow for failure of subst_normalize_erasing_regions in const_eval

Fixes https://github.com/rust-lang/rust/issues/72845

Using associated types that cannot be normalized previously resulted in an ICE. We now allow for normalization failure and return a "TooGeneric" error in that case.

r? ```@RalfJung``` maybe?
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs23
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs5
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs18
-rw-r--r--compiler/rustc_middle/src/ty/normalize_erasing_regions.rs26
-rw-r--r--src/test/ui/const-generics/issues/issue-72845.rs49
-rw-r--r--src/test/ui/const-generics/issues/issue-72845.stderr12
-rw-r--r--src/test/ui/consts/const-eval/const-eval-query-stack.stderr2
9 files changed, 129 insertions, 12 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index cf084faade8..d46950ed903 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -7,6 +7,7 @@ use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
 use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable;
 use rustc_middle::mir;
+use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
 use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{
     self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
@@ -14,7 +15,7 @@ use rustc_middle::ty::{
 use rustc_mir_dataflow::storage::AlwaysLiveLocals;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::Limit;
-use rustc_span::{Pos, Span};
+use rustc_span::{Pos, Span, DUMMY_SP};
 use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
 
 use super::{
@@ -508,7 +509,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
         &self,
         value: T,
-    ) -> T {
+    ) -> Result<T, InterpError<'tcx>> {
         self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
     }
 
@@ -518,8 +519,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
         value: T,
-    ) -> T {
-        frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+    ) -> Result<T, InterpError<'tcx>> {
+        frame
+            .instance
+            .try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+            .or_else(|e| {
+                self.tcx.sess.delay_span_bug(
+                    DUMMY_SP,
+                    format!("failed to normalize {}", e.get_type_for_failure()).as_str(),
+                );
+
+                Err(InterpError::InvalidProgram(InvalidProgramInfo::TooGeneric))
+            })
     }
 
     /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
@@ -554,7 +565,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let layout = from_known_layout(self.tcx, self.param_env, layout, || {
                     let local_ty = frame.body.local_decls[local].ty;
                     let local_ty =
-                        self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty);
+                        self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
                     self.layout_of(local_ty)
                 })?;
                 if let Some(state) = frame.locals.get(local) {
@@ -702,7 +713,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         for const_ in &body.required_consts {
             let span = const_.span;
             let const_ =
-                self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
+                self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?;
             self.mir_const_to_op(&const_, None).map_err(|err| {
                 // If there was an error, set the span of the current frame to this constant.
                 // Avoiding doing this when evaluation succeeds.
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index de9e94ce2ac..e82ce73c814 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -512,7 +512,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             self.param_env,
             self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
                 place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            ))?,
+            )?)?,
             op.layout,
         ));
         Ok(op)
@@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Constant(ref constant) => {
                 let val =
-                    self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
+                    self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
                 // This can still fail:
                 // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
                 //   checked yet.
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index d7f2853fc86..4c95da896a2 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -643,7 +643,7 @@ where
             self.param_env,
             self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
                 place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            ))?,
+            )?)?,
             place_ty.layout,
         ));
         Ok(place_ty)
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 9299ae2b2b9..992cef1cb6a 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -276,7 +276,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
 
             NullaryOp(null_op, ty) => {
-                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
+                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
                 let layout = self.layout_of(ty)?;
                 if layout.is_unsized() {
                     // FIXME: This should be a span_bug (#80742)
@@ -302,7 +302,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Cast(cast_kind, ref operand, cast_ty) => {
                 let src = self.eval_operand(operand, None)?;
-                let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty);
+                let cast_ty =
+                    self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty)?;
                 self.cast(&src, cast_kind, cast_ty, &dest)?;
             }
 
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 4b38105e447..2d301262730 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -7,6 +7,7 @@ use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
+use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
 
 use std::fmt;
 
@@ -575,6 +576,23 @@ impl<'tcx> Instance<'tcx> {
         }
     }
 
+    #[inline(always)]
+    pub fn try_subst_mir_and_normalize_erasing_regions<T>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        v: T,
+    ) -> Result<T, NormalizationError<'tcx>>
+    where
+        T: TypeFoldable<'tcx> + Clone,
+    {
+        if let Some(substs) = self.substs_for_mir_body() {
+            tcx.try_subst_and_normalize_erasing_regions(substs, param_env, v)
+        } else {
+            tcx.try_normalize_erasing_regions(param_env, v)
+        }
+    }
+
     /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
     /// identity parameters if they are determined to be unused in `instance.def`.
     pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
index c0e1360640f..c472d4a5a4d 100644
--- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
+++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
@@ -115,6 +115,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Monomorphizes a type from the AST by first applying the
     /// in-scope substitutions and then normalizing any associated
     /// types.
+    /// Panics if normalization fails. In case normalization might fail
+    /// use `try_subst_and_normalize_erasing_regions` instead.
     pub fn subst_and_normalize_erasing_regions<T>(
         self,
         param_substs: SubstsRef<'tcx>,
@@ -134,6 +136,30 @@ impl<'tcx> TyCtxt<'tcx> {
         let substituted = value.subst(self, param_substs);
         self.normalize_erasing_regions(param_env, substituted)
     }
+
+    /// Monomorphizes a type from the AST by first applying the
+    /// in-scope substitutions and then trying to normalize any associated
+    /// types. Contrary to `subst_and_normalize_erasing_regions` this does
+    /// not assume that normalization succeeds.
+    pub fn try_subst_and_normalize_erasing_regions<T>(
+        self,
+        param_substs: SubstsRef<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        value: T,
+    ) -> Result<T, NormalizationError<'tcx>>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "subst_and_normalize_erasing_regions(\
+             param_substs={:?}, \
+             value={:?}, \
+             param_env={:?})",
+            param_substs, value, param_env,
+        );
+        let substituted = value.subst(self, param_substs);
+        self.try_normalize_erasing_regions(param_env, substituted)
+    }
 }
 
 struct NormalizeAfterErasingRegionsFolder<'tcx> {
diff --git a/src/test/ui/const-generics/issues/issue-72845.rs b/src/test/ui/const-generics/issues/issue-72845.rs
new file mode 100644
index 00000000000..bea5dc8ba21
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-72845.rs
@@ -0,0 +1,49 @@
+#![feature(generic_const_exprs)]
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+//--------------------------------------------------
+
+trait Depth {
+    const C: usize;
+}
+
+trait Type {
+    type AT: Depth;
+}
+
+//--------------------------------------------------
+
+enum Predicate<const B: bool> {}
+
+trait Satisfied {}
+
+impl Satisfied for Predicate<true> {}
+
+//--------------------------------------------------
+
+trait Spec1 {}
+
+impl<T: Type> Spec1 for T where Predicate<{T::AT::C > 0}>: Satisfied {}
+
+trait Spec2 {}
+
+//impl<T: Type > Spec2 for T where Predicate<{T::AT::C > 1}>: Satisfied {}
+impl<T: Type > Spec2 for T where Predicate<true>: Satisfied {}
+
+//--------------------------------------------------
+
+trait Foo {
+    fn Bar();
+}
+
+impl<T: Spec1> Foo for T {
+    default fn Bar() {}
+}
+
+impl<T: Spec2> Foo for T {
+//~^ ERROR conflicting implementations of trait
+    fn Bar() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/issues/issue-72845.stderr b/src/test/ui/const-generics/issues/issue-72845.stderr
new file mode 100644
index 00000000000..631c8605fb4
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-72845.stderr
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Foo`
+  --> $DIR/issue-72845.rs:44:1
+   |
+LL | impl<T: Spec1> Foo for T {
+   | ------------------------ first implementation here
+...
+LL | impl<T: Spec2> Foo for T {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
index e6fecef9fb3..45a3d901c98 100644
--- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
+++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr
@@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
 LL |     let x: &'static i32 = &X;
    |                            ^ referenced constant has errors
 query stack during panic:
-#0 [normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
+#0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
 #1 [optimized_mir] optimizing MIR for `main`
 #2 [collect_and_partition_mono_items] collect_and_partition_mono_items
 end of query stack