about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-06-22 09:17:33 +0200
committerRalf Jung <post@ralfj.de>2020-06-22 09:19:08 +0200
commit7f8fe6a9851aaf493c8657fe7e98145539d466dd (patch)
tree9c0fae265c0ebf1c8a44a7ceb6039470b8342056
parent7754322bcc2852814592c47c3b76c53fefe95a4f (diff)
downloadrust-7f8fe6a9851aaf493c8657fe7e98145539d466dd.tar.gz
rust-7f8fe6a9851aaf493c8657fe7e98145539d466dd.zip
also use relator in interpreter assignment sanity check
-rw-r--r--src/librustc_mir/interpret/eval_context.rs41
-rw-r--r--src/librustc_mir/interpret/operand.rs4
-rw-r--r--src/librustc_mir/interpret/place.rs5
-rw-r--r--src/librustc_mir/transform/validate.rs162
4 files changed, 109 insertions, 103 deletions
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 1a6ed41ba47..b673738cec5 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -15,7 +15,7 @@ use rustc_middle::mir::interpret::{
 };
 use rustc_middle::ty::layout::{self, TyAndLayout};
 use rustc_middle::ty::{
-    self, fold::BottomUpFolder, query::TyCtxtAt, subst::SubstsRef, Ty, TyCtxt, TypeFoldable,
+    self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
 };
 use rustc_span::{source_map::DUMMY_SP, Span};
 use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
@@ -24,6 +24,7 @@ use super::{
     Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy,
     ScalarMaybeUninit, StackPopJump,
 };
+use crate::transform::validate::equal_up_to_regions;
 use crate::util::storage::AlwaysLiveLocals;
 
 pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -220,6 +221,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOf for InterpCx<'mir, 'tcx,
 /// This test should be symmetric, as it is primarily about layout compatibility.
 pub(super) fn mir_assign_valid_types<'tcx>(
     tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
     src: TyAndLayout<'tcx>,
     dest: TyAndLayout<'tcx>,
 ) -> bool {
@@ -234,29 +236,15 @@ pub(super) fn mir_assign_valid_types<'tcx>(
         return false;
     }
 
-    // Type-changing assignments can happen for (at least) two reasons:
-    // 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
-    // 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
-    //    with their late-bound lifetimes are still around and can lead to type differences.
-    // Normalize both of them away.
-    // Also see the related but slightly different pre-monomorphization method in `transform/validate.rs`.
-    let normalize = |ty: Ty<'tcx>| {
-        ty.fold_with(&mut BottomUpFolder {
-            tcx,
-            // Normalize all references to immutable.
-            ty_op: |ty| match ty.kind {
-                ty::Ref(_, pointee, _) => tcx.mk_imm_ref(tcx.lifetimes.re_erased, pointee),
-                _ => ty,
-            },
-            // We just erase all late-bound lifetimes, but this is not fully correct (FIXME):
-            // lifetimes in invariant positions could matter (e.g. through associated types).
-            // We rely on the fact that layout was confirmed to be equal above.
-            lt_op: |_| tcx.lifetimes.re_erased,
-            // Leave consts unchanged.
-            ct_op: |ct| ct,
-        })
-    };
-    normalize(src.ty) == normalize(dest.ty)
+    // Type-changing assignments can happen when subtyping is used. While
+    // all normal lifetimes are erased, higher-ranked types with their
+    // late-bound lifetimes are still around and can lead to type
+    // differences. So we compare ignoring lifetimes.
+    //
+    // Note that this is not fully correct (FIXME):
+    // lifetimes in invariant positions could matter (e.g. through associated types).
+    // We rely on the fact that layout was confirmed to be equal above.
+    equal_up_to_regions(tcx, param_env, src.ty, dest.ty)
 }
 
 /// Use the already known layout if given (but sanity check in debug mode),
@@ -264,6 +252,7 @@ pub(super) fn mir_assign_valid_types<'tcx>(
 #[cfg_attr(not(debug_assertions), inline(always))]
 pub(super) fn from_known_layout<'tcx>(
     tcx: TyCtxtAt<'tcx>,
+    param_env: ParamEnv<'tcx>,
     known_layout: Option<TyAndLayout<'tcx>>,
     compute: impl FnOnce() -> InterpResult<'tcx, TyAndLayout<'tcx>>,
 ) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
@@ -272,7 +261,7 @@ pub(super) fn from_known_layout<'tcx>(
         Some(known_layout) => {
             if cfg!(debug_assertions) {
                 let check_layout = compute()?;
-                if !mir_assign_valid_types(tcx.tcx, check_layout, known_layout) {
+                if !mir_assign_valid_types(tcx.tcx, param_env, check_layout, known_layout) {
                     span_bug!(
                         tcx.span,
                         "expected type differs from actual type.\nexpected: {:?}\nactual: {:?}",
@@ -476,7 +465,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // have to support that case (mostly by skipping all caching).
         match frame.locals.get(local).and_then(|state| state.layout.get()) {
             None => {
-                let layout = from_known_layout(self.tcx, layout, || {
+                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);
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 35e433c4bd5..27fa9b2c17c 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -472,6 +472,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // Sanity-check the type we ended up with.
         debug_assert!(mir_assign_valid_types(
             *self.tcx,
+            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
             ))?,
@@ -554,7 +555,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // documentation).
         let val_val = M::adjust_global_const(self, val_val)?;
         // Other cases need layout.
-        let layout = from_known_layout(self.tcx, layout, || self.layout_of(val.ty))?;
+        let layout =
+            from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?;
         let op = match val_val {
             ConstValue::ByRef { alloc, offset } => {
                 let id = self.tcx.create_memory_alloc(alloc);
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 396aec0a8f8..98a1cea97e2 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -652,6 +652,7 @@ where
         // Sanity-check the type we ended up with.
         debug_assert!(mir_assign_valid_types(
             *self.tcx,
+            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
             ))?,
@@ -855,7 +856,7 @@ where
     ) -> InterpResult<'tcx> {
         // We do NOT compare the types for equality, because well-typed code can
         // actually "transmute" `&mut T` to `&T` in an assignment without a cast.
-        if !mir_assign_valid_types(*self.tcx, src.layout, dest.layout) {
+        if !mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) {
             span_bug!(
                 self.cur_span(),
                 "type mismatch when copying!\nsrc: {:?},\ndest: {:?}",
@@ -912,7 +913,7 @@ where
         src: OpTy<'tcx, M::PointerTag>,
         dest: PlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
-        if mir_assign_valid_types(*self.tcx, src.layout, dest.layout) {
+        if mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) {
             // Fast path: Just use normal `copy_op`
             return self.copy_op(src, dest);
         }
diff --git a/src/librustc_mir/transform/validate.rs b/src/librustc_mir/transform/validate.rs
index 803954d258f..5f0edd64c47 100644
--- a/src/librustc_mir/transform/validate.rs
+++ b/src/librustc_mir/transform/validate.rs
@@ -32,6 +32,93 @@ impl<'tcx> MirPass<'tcx> for Validator {
     }
 }
 
+/// Returns whether the two types are equal up to lifetimes.
+/// All lifetimes, including higher-ranked ones, get ignored for this comparison.
+/// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
+///
+/// The point of this function is to approximate "equal up to subtyping".  However,
+/// the approximation is incorrect as variance is ignored.
+pub fn equal_up_to_regions(
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    src: Ty<'tcx>,
+    dest: Ty<'tcx>,
+) -> bool {
+    struct LifetimeIgnoreRelation<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    }
+
+    impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
+        fn tcx(&self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn param_env(&self) -> ty::ParamEnv<'tcx> {
+            self.param_env
+        }
+
+        fn tag(&self) -> &'static str {
+            "librustc_mir::transform::validate"
+        }
+
+        fn a_is_expected(&self) -> bool {
+            true
+        }
+
+        fn relate_with_variance<T: Relate<'tcx>>(
+            &mut self,
+            _: ty::Variance,
+            a: &T,
+            b: &T,
+        ) -> RelateResult<'tcx, T> {
+            // Ignore variance, require types to be exactly the same.
+            self.relate(a, b)
+        }
+
+        fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+            if a == b {
+                // Short-circuit.
+                return Ok(a);
+            }
+            ty::relate::super_relate_tys(self, a, b)
+        }
+
+        fn regions(
+            &mut self,
+            a: ty::Region<'tcx>,
+            _b: ty::Region<'tcx>,
+        ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+            // Ignore regions.
+            Ok(a)
+        }
+
+        fn consts(
+            &mut self,
+            a: &'tcx ty::Const<'tcx>,
+            b: &'tcx ty::Const<'tcx>,
+        ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+            ty::relate::super_relate_consts(self, a, b)
+        }
+
+        fn binders<T>(
+            &mut self,
+            a: &ty::Binder<T>,
+            b: &ty::Binder<T>,
+        ) -> RelateResult<'tcx, ty::Binder<T>>
+        where
+            T: Relate<'tcx>,
+        {
+            self.relate(a.skip_binder(), b.skip_binder())?;
+            Ok(a.clone())
+        }
+    }
+
+    // Instantiate and run relation.
+    let mut relator: LifetimeIgnoreRelation<'tcx> = LifetimeIgnoreRelation { tcx: tcx, param_env };
+    relator.relate(&src, &dest).is_ok()
+}
+
 struct TypeChecker<'a, 'tcx> {
     when: &'a str,
     source: MirSource<'tcx>,
@@ -108,80 +195,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         // all normal lifetimes are erased, higher-ranked types with their
         // late-bound lifetimes are still around and can lead to type
         // differences. So we compare ignoring lifetimes.
-        struct LifetimeIgnoreRelation<'tcx> {
-            tcx: TyCtxt<'tcx>,
-            param_env: ty::ParamEnv<'tcx>,
-        }
-
-        impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
-            fn tcx(&self) -> TyCtxt<'tcx> {
-                self.tcx
-            }
-
-            fn param_env(&self) -> ty::ParamEnv<'tcx> {
-                self.param_env
-            }
-
-            fn tag(&self) -> &'static str {
-                "librustc_mir::transform::validate"
-            }
-
-            fn a_is_expected(&self) -> bool {
-                true
-            }
-
-            fn relate_with_variance<T: Relate<'tcx>>(
-                &mut self,
-                _: ty::Variance,
-                a: &T,
-                b: &T,
-            ) -> RelateResult<'tcx, T> {
-                // Ignore variance, require types to be exactly the same.
-                self.relate(a, b)
-            }
-
-            fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-                if a == b {
-                    // Short-circuit.
-                    return Ok(a);
-                }
-                ty::relate::super_relate_tys(self, a, b)
-            }
-
-            fn regions(
-                &mut self,
-                a: ty::Region<'tcx>,
-                _b: ty::Region<'tcx>,
-            ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-                // Ignore regions.
-                Ok(a)
-            }
-
-            fn consts(
-                &mut self,
-                a: &'tcx ty::Const<'tcx>,
-                b: &'tcx ty::Const<'tcx>,
-            ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
-                ty::relate::super_relate_consts(self, a, b)
-            }
-
-            fn binders<T>(
-                &mut self,
-                a: &ty::Binder<T>,
-                b: &ty::Binder<T>,
-            ) -> RelateResult<'tcx, ty::Binder<T>>
-            where
-                T: Relate<'tcx>,
-            {
-                self.relate(a.skip_binder(), b.skip_binder())?;
-                Ok(a.clone())
-            }
-        }
-
-        // Instantiate and run relation.
-        let mut relator: LifetimeIgnoreRelation<'tcx> =
-            LifetimeIgnoreRelation { tcx: self.tcx, param_env };
-        relator.relate(&src, &dest).is_ok()
+        equal_up_to_regions(self.tcx, param_env, src, dest)
     }
 }