Skip to content

Commit 7f8fe6a

Browse files
committed
also use relator in interpreter assignment sanity check
1 parent 7754322 commit 7f8fe6a

File tree

4 files changed

+109
-103
lines changed

4 files changed

+109
-103
lines changed

src/librustc_mir/interpret/eval_context.rs

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::mir::interpret::{
1515
};
1616
use rustc_middle::ty::layout::{self, TyAndLayout};
1717
use rustc_middle::ty::{
18-
self, fold::BottomUpFolder, query::TyCtxtAt, subst::SubstsRef, Ty, TyCtxt, TypeFoldable,
18+
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
1919
};
2020
use rustc_span::{source_map::DUMMY_SP, Span};
2121
use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
@@ -24,6 +24,7 @@ use super::{
2424
Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy,
2525
ScalarMaybeUninit, StackPopJump,
2626
};
27+
use crate::transform::validate::equal_up_to_regions;
2728
use crate::util::storage::AlwaysLiveLocals;
2829

2930
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,
220221
/// This test should be symmetric, as it is primarily about layout compatibility.
221222
pub(super) fn mir_assign_valid_types<'tcx>(
222223
tcx: TyCtxt<'tcx>,
224+
param_env: ParamEnv<'tcx>,
223225
src: TyAndLayout<'tcx>,
224226
dest: TyAndLayout<'tcx>,
225227
) -> bool {
@@ -234,36 +236,23 @@ pub(super) fn mir_assign_valid_types<'tcx>(
234236
return false;
235237
}
236238

237-
// Type-changing assignments can happen for (at least) two reasons:
238-
// 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
239-
// 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
240-
// with their late-bound lifetimes are still around and can lead to type differences.
241-
// Normalize both of them away.
242-
// Also see the related but slightly different pre-monomorphization method in `transform/validate.rs`.
243-
let normalize = |ty: Ty<'tcx>| {
244-
ty.fold_with(&mut BottomUpFolder {
245-
tcx,
246-
// Normalize all references to immutable.
247-
ty_op: |ty| match ty.kind {
248-
ty::Ref(_, pointee, _) => tcx.mk_imm_ref(tcx.lifetimes.re_erased, pointee),
249-
_ => ty,
250-
},
251-
// We just erase all late-bound lifetimes, but this is not fully correct (FIXME):
252-
// lifetimes in invariant positions could matter (e.g. through associated types).
253-
// We rely on the fact that layout was confirmed to be equal above.
254-
lt_op: |_| tcx.lifetimes.re_erased,
255-
// Leave consts unchanged.
256-
ct_op: |ct| ct,
257-
})
258-
};
259-
normalize(src.ty) == normalize(dest.ty)
239+
// Type-changing assignments can happen when subtyping is used. While
240+
// all normal lifetimes are erased, higher-ranked types with their
241+
// late-bound lifetimes are still around and can lead to type
242+
// differences. So we compare ignoring lifetimes.
243+
//
244+
// Note that this is not fully correct (FIXME):
245+
// lifetimes in invariant positions could matter (e.g. through associated types).
246+
// We rely on the fact that layout was confirmed to be equal above.
247+
equal_up_to_regions(tcx, param_env, src.ty, dest.ty)
260248
}
261249

262250
/// Use the already known layout if given (but sanity check in debug mode),
263251
/// or compute the layout.
264252
#[cfg_attr(not(debug_assertions), inline(always))]
265253
pub(super) fn from_known_layout<'tcx>(
266254
tcx: TyCtxtAt<'tcx>,
255+
param_env: ParamEnv<'tcx>,
267256
known_layout: Option<TyAndLayout<'tcx>>,
268257
compute: impl FnOnce() -> InterpResult<'tcx, TyAndLayout<'tcx>>,
269258
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
@@ -272,7 +261,7 @@ pub(super) fn from_known_layout<'tcx>(
272261
Some(known_layout) => {
273262
if cfg!(debug_assertions) {
274263
let check_layout = compute()?;
275-
if !mir_assign_valid_types(tcx.tcx, check_layout, known_layout) {
264+
if !mir_assign_valid_types(tcx.tcx, param_env, check_layout, known_layout) {
276265
span_bug!(
277266
tcx.span,
278267
"expected type differs from actual type.\nexpected: {:?}\nactual: {:?}",
@@ -476,7 +465,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
476465
// have to support that case (mostly by skipping all caching).
477466
match frame.locals.get(local).and_then(|state| state.layout.get()) {
478467
None => {
479-
let layout = from_known_layout(self.tcx, layout, || {
468+
let layout = from_known_layout(self.tcx, self.param_env, layout, || {
480469
let local_ty = frame.body.local_decls[local].ty;
481470
let local_ty =
482471
self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty);

src/librustc_mir/interpret/operand.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
472472
// Sanity-check the type we ended up with.
473473
debug_assert!(mir_assign_valid_types(
474474
*self.tcx,
475+
self.param_env,
475476
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
476477
place.ty(&self.frame().body.local_decls, *self.tcx).ty
477478
))?,
@@ -554,7 +555,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
554555
// documentation).
555556
let val_val = M::adjust_global_const(self, val_val)?;
556557
// Other cases need layout.
557-
let layout = from_known_layout(self.tcx, layout, || self.layout_of(val.ty))?;
558+
let layout =
559+
from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?;
558560
let op = match val_val {
559561
ConstValue::ByRef { alloc, offset } => {
560562
let id = self.tcx.create_memory_alloc(alloc);

src/librustc_mir/interpret/place.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ where
652652
// Sanity-check the type we ended up with.
653653
debug_assert!(mir_assign_valid_types(
654654
*self.tcx,
655+
self.param_env,
655656
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
656657
place.ty(&self.frame().body.local_decls, *self.tcx).ty
657658
))?,
@@ -855,7 +856,7 @@ where
855856
) -> InterpResult<'tcx> {
856857
// We do NOT compare the types for equality, because well-typed code can
857858
// actually "transmute" `&mut T` to `&T` in an assignment without a cast.
858-
if !mir_assign_valid_types(*self.tcx, src.layout, dest.layout) {
859+
if !mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) {
859860
span_bug!(
860861
self.cur_span(),
861862
"type mismatch when copying!\nsrc: {:?},\ndest: {:?}",
@@ -912,7 +913,7 @@ where
912913
src: OpTy<'tcx, M::PointerTag>,
913914
dest: PlaceTy<'tcx, M::PointerTag>,
914915
) -> InterpResult<'tcx> {
915-
if mir_assign_valid_types(*self.tcx, src.layout, dest.layout) {
916+
if mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) {
916917
// Fast path: Just use normal `copy_op`
917918
return self.copy_op(src, dest);
918919
}

src/librustc_mir/transform/validate.rs

Lines changed: 88 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,93 @@ impl<'tcx> MirPass<'tcx> for Validator {
3232
}
3333
}
3434

35+
/// Returns whether the two types are equal up to lifetimes.
36+
/// All lifetimes, including higher-ranked ones, get ignored for this comparison.
37+
/// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
38+
///
39+
/// The point of this function is to approximate "equal up to subtyping". However,
40+
/// the approximation is incorrect as variance is ignored.
41+
pub fn equal_up_to_regions(
42+
tcx: TyCtxt<'tcx>,
43+
param_env: ParamEnv<'tcx>,
44+
src: Ty<'tcx>,
45+
dest: Ty<'tcx>,
46+
) -> bool {
47+
struct LifetimeIgnoreRelation<'tcx> {
48+
tcx: TyCtxt<'tcx>,
49+
param_env: ty::ParamEnv<'tcx>,
50+
}
51+
52+
impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
53+
fn tcx(&self) -> TyCtxt<'tcx> {
54+
self.tcx
55+
}
56+
57+
fn param_env(&self) -> ty::ParamEnv<'tcx> {
58+
self.param_env
59+
}
60+
61+
fn tag(&self) -> &'static str {
62+
"librustc_mir::transform::validate"
63+
}
64+
65+
fn a_is_expected(&self) -> bool {
66+
true
67+
}
68+
69+
fn relate_with_variance<T: Relate<'tcx>>(
70+
&mut self,
71+
_: ty::Variance,
72+
a: &T,
73+
b: &T,
74+
) -> RelateResult<'tcx, T> {
75+
// Ignore variance, require types to be exactly the same.
76+
self.relate(a, b)
77+
}
78+
79+
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
80+
if a == b {
81+
// Short-circuit.
82+
return Ok(a);
83+
}
84+
ty::relate::super_relate_tys(self, a, b)
85+
}
86+
87+
fn regions(
88+
&mut self,
89+
a: ty::Region<'tcx>,
90+
_b: ty::Region<'tcx>,
91+
) -> RelateResult<'tcx, ty::Region<'tcx>> {
92+
// Ignore regions.
93+
Ok(a)
94+
}
95+
96+
fn consts(
97+
&mut self,
98+
a: &'tcx ty::Const<'tcx>,
99+
b: &'tcx ty::Const<'tcx>,
100+
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
101+
ty::relate::super_relate_consts(self, a, b)
102+
}
103+
104+
fn binders<T>(
105+
&mut self,
106+
a: &ty::Binder<T>,
107+
b: &ty::Binder<T>,
108+
) -> RelateResult<'tcx, ty::Binder<T>>
109+
where
110+
T: Relate<'tcx>,
111+
{
112+
self.relate(a.skip_binder(), b.skip_binder())?;
113+
Ok(a.clone())
114+
}
115+
}
116+
117+
// Instantiate and run relation.
118+
let mut relator: LifetimeIgnoreRelation<'tcx> = LifetimeIgnoreRelation { tcx: tcx, param_env };
119+
relator.relate(&src, &dest).is_ok()
120+
}
121+
35122
struct TypeChecker<'a, 'tcx> {
36123
when: &'a str,
37124
source: MirSource<'tcx>,
@@ -108,80 +195,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
108195
// all normal lifetimes are erased, higher-ranked types with their
109196
// late-bound lifetimes are still around and can lead to type
110197
// differences. So we compare ignoring lifetimes.
111-
struct LifetimeIgnoreRelation<'tcx> {
112-
tcx: TyCtxt<'tcx>,
113-
param_env: ty::ParamEnv<'tcx>,
114-
}
115-
116-
impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
117-
fn tcx(&self) -> TyCtxt<'tcx> {
118-
self.tcx
119-
}
120-
121-
fn param_env(&self) -> ty::ParamEnv<'tcx> {
122-
self.param_env
123-
}
124-
125-
fn tag(&self) -> &'static str {
126-
"librustc_mir::transform::validate"
127-
}
128-
129-
fn a_is_expected(&self) -> bool {
130-
true
131-
}
132-
133-
fn relate_with_variance<T: Relate<'tcx>>(
134-
&mut self,
135-
_: ty::Variance,
136-
a: &T,
137-
b: &T,
138-
) -> RelateResult<'tcx, T> {
139-
// Ignore variance, require types to be exactly the same.
140-
self.relate(a, b)
141-
}
142-
143-
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
144-
if a == b {
145-
// Short-circuit.
146-
return Ok(a);
147-
}
148-
ty::relate::super_relate_tys(self, a, b)
149-
}
150-
151-
fn regions(
152-
&mut self,
153-
a: ty::Region<'tcx>,
154-
_b: ty::Region<'tcx>,
155-
) -> RelateResult<'tcx, ty::Region<'tcx>> {
156-
// Ignore regions.
157-
Ok(a)
158-
}
159-
160-
fn consts(
161-
&mut self,
162-
a: &'tcx ty::Const<'tcx>,
163-
b: &'tcx ty::Const<'tcx>,
164-
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
165-
ty::relate::super_relate_consts(self, a, b)
166-
}
167-
168-
fn binders<T>(
169-
&mut self,
170-
a: &ty::Binder<T>,
171-
b: &ty::Binder<T>,
172-
) -> RelateResult<'tcx, ty::Binder<T>>
173-
where
174-
T: Relate<'tcx>,
175-
{
176-
self.relate(a.skip_binder(), b.skip_binder())?;
177-
Ok(a.clone())
178-
}
179-
}
180-
181-
// Instantiate and run relation.
182-
let mut relator: LifetimeIgnoreRelation<'tcx> =
183-
LifetimeIgnoreRelation { tcx: self.tcx, param_env };
184-
relator.relate(&src, &dest).is_ok()
198+
equal_up_to_regions(self.tcx, param_env, src, dest)
185199
}
186200
}
187201

0 commit comments

Comments
 (0)