@@ -18,7 +18,7 @@ use rustc_middle::ty::query::TyCtxtAt;
1818use rustc_middle:: ty:: subst:: SubstsRef ;
1919use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeFoldable } ;
2020use rustc_span:: source_map:: DUMMY_SP ;
21- use rustc_target:: abi:: { Align , HasDataLayout , LayoutOf , Size , TargetDataLayout } ;
21+ use rustc_target:: abi:: { Abi , Align , HasDataLayout , LayoutOf , Size , TargetDataLayout } ;
2222
2323use super :: {
2424 Immediate , MPlaceTy , Machine , MemPlace , MemPlaceMeta , Memory , OpTy , Operand , Place , PlaceTy ,
@@ -210,6 +210,53 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for InterpCx<'mir, 'tcx, M> {
210210 }
211211}
212212
213+ /// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value.
214+ /// This test should be symmetric, as it is primarily about layout compatibility.
215+ pub ( super ) fn mir_assign_valid_types < ' tcx > (
216+ src : TyAndLayout < ' tcx > ,
217+ dest : TyAndLayout < ' tcx > ,
218+ ) -> bool {
219+ if src. ty == dest. ty {
220+ // Equal types, all is good.
221+ return true ;
222+ }
223+ // Type-changing assignments can happen for (at least) two reasons:
224+ // - `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
225+ // - Subtyping is used. While all normal lifetimes are erased, higher-ranked lifetime
226+ // bounds are still around and can lead to type differences.
227+ // There is no good way to check the latter, so we compare layouts instead -- but only
228+ // for values with `Scalar`/`ScalarPair` abi.
229+ // FIXME: Do something more accurate, type-based.
230+ match & src. abi {
231+ Abi :: Scalar ( ..) | Abi :: ScalarPair ( ..) => src. layout == dest. layout ,
232+ _ => false ,
233+ }
234+ }
235+
236+ /// Use the already known layout if given (but sanity check in debug mode),
237+ /// or compute the layout.
238+ #[ cfg_attr( not( debug_assertions) , inline( always) ) ]
239+ pub ( super ) fn from_known_layout < ' tcx > (
240+ known_layout : Option < TyAndLayout < ' tcx > > ,
241+ compute : impl FnOnce ( ) -> InterpResult < ' tcx , TyAndLayout < ' tcx > > ,
242+ ) -> InterpResult < ' tcx , TyAndLayout < ' tcx > > {
243+ match known_layout {
244+ None => compute ( ) ,
245+ Some ( known_layout) => {
246+ if cfg ! ( debug_assertions) {
247+ let check_layout = compute ( ) ?;
248+ assert ! (
249+ mir_assign_valid_types( check_layout, known_layout) ,
250+ "expected type differs from actual type.\n expected: {:?}\n actual: {:?}" ,
251+ known_layout. ty,
252+ check_layout. ty,
253+ ) ;
254+ }
255+ Ok ( known_layout)
256+ }
257+ }
258+ }
259+
213260impl < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
214261 pub fn new (
215262 tcx : TyCtxtAt < ' tcx > ,
@@ -377,7 +424,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
377424 // have to support that case (mostly by skipping all caching).
378425 match frame. locals . get ( local) . and_then ( |state| state. layout . get ( ) ) {
379426 None => {
380- let layout = crate :: interpret :: operand :: from_known_layout ( layout, || {
427+ let layout = from_known_layout ( layout, || {
381428 let local_ty = frame. body . local_decls [ local] . ty ;
382429 let local_ty =
383430 self . subst_from_frame_and_normalize_erasing_regions ( frame, local_ty) ;
0 commit comments