@@ -32,6 +32,15 @@ impl<Tag> ScalarExt for ScalarMaybeUndef<Tag> {
3232
3333pub trait EvalContextExt < ' tcx > {
3434 fn resolve_path ( & self , path : & [ & str ] ) -> EvalResult < ' tcx , ty:: Instance < ' tcx > > ;
35+
36+ /// Visit the memory covered by `place` that is frozen -- i.e., NOT
37+ /// what is inside an `UnsafeCell`.
38+ fn visit_frozen (
39+ & self ,
40+ place : MPlaceTy < ' tcx , Borrow > ,
41+ size : Size ,
42+ action : impl FnMut ( Pointer < Borrow > , Size ) -> EvalResult < ' tcx > ,
43+ ) -> EvalResult < ' tcx > ;
3544}
3645
3746
@@ -69,4 +78,126 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
6978 EvalErrorKind :: PathNotFound ( path) . into ( )
7079 } )
7180 }
81+
82+ /// Visit the memory covered by `place` that is frozen -- i.e., NOT
83+ /// what is inside an `UnsafeCell`.
84+ fn visit_frozen (
85+ & self ,
86+ place : MPlaceTy < ' tcx , Borrow > ,
87+ size : Size ,
88+ mut frozen_action : impl FnMut ( Pointer < Borrow > , Size ) -> EvalResult < ' tcx > ,
89+ ) -> EvalResult < ' tcx > {
90+ trace ! ( "visit_frozen(place={:?}, size={:?})" , * place, size) ;
91+ debug_assert_eq ! ( size,
92+ self . size_and_align_of_mplace( place) ?
93+ . map( |( size, _) | size)
94+ . unwrap_or_else( || place. layout. size)
95+ ) ;
96+ // Store how far we proceeded into the place so far. Everything to the left of
97+ // this offset has already been handled, in the sense that the frozen parts
98+ // have had `action` called on them.
99+ let mut end_ptr = place. ptr ;
100+ // Called when we detected an `UnsafeCell` at the given offset and size.
101+ // Calls `action` and advances `end_ptr`.
102+ let mut unsafe_cell_action = |unsafe_cell_offset, unsafe_cell_size| {
103+ // We assume that we are given the fields in increasing offset order,
104+ // and nothing else changes.
105+ let end_offset = end_ptr. get_ptr_offset ( self ) ;
106+ assert ! ( unsafe_cell_offset >= end_offset) ;
107+ let frozen_size = unsafe_cell_offset - end_offset;
108+ // Everything between the end_ptr and this `UnsafeCell` is frozen.
109+ if frozen_size != Size :: ZERO {
110+ frozen_action ( end_ptr. to_ptr ( ) ?, frozen_size) ?;
111+ }
112+ // Update end end_ptr.
113+ end_ptr = end_ptr. ptr_wrapping_offset ( frozen_size+unsafe_cell_size, self ) ;
114+ // Done
115+ Ok ( ( ) )
116+ } ;
117+ // Run a visitor
118+ {
119+ let mut visitor = UnsafeCellVisitor {
120+ ecx : self ,
121+ unsafe_cell_action : |place| {
122+ trace ! ( "unsafe_cell_action on {:?}" , place. ptr) ;
123+ // We need a size to go on.
124+ let ( unsafe_cell_size, _) = self . size_and_align_of_mplace ( place) ?
125+ // for extern types, just cover what we can
126+ . unwrap_or_else ( || place. layout . size_and_align ( ) ) ;
127+ // Now handle this `UnsafeCell`.
128+ unsafe_cell_action ( place. ptr . get_ptr_offset ( self ) , unsafe_cell_size)
129+ } ,
130+ } ;
131+ visitor. visit_value ( place) ?;
132+ }
133+ // The part between the end_ptr and the end of the place is also frozen.
134+ // So pretend there is a 0-sized `UnsafeCell` at the end.
135+ unsafe_cell_action ( place. ptr . get_ptr_offset ( self ) + size, Size :: ZERO ) ?;
136+ // Done!
137+ return Ok ( ( ) ) ;
138+
139+ /// Visiting the memory covered by a `MemPlace`, being aware of
140+ /// whether we are inside an `UnsafeCell` or not.
141+ struct UnsafeCellVisitor < ' ecx , ' a , ' mir , ' tcx , F >
142+ where F : FnMut ( MPlaceTy < ' tcx , Borrow > ) -> EvalResult < ' tcx >
143+ {
144+ ecx : & ' ecx MiriEvalContext < ' a , ' mir , ' tcx > ,
145+ unsafe_cell_action : F ,
146+ }
147+
148+ impl < ' ecx , ' a , ' mir , ' tcx , F > ValueVisitor < ' a , ' mir , ' tcx , Evaluator < ' tcx > >
149+ for UnsafeCellVisitor < ' ecx , ' a , ' mir , ' tcx , F >
150+ where
151+ F : FnMut ( MPlaceTy < ' tcx , Borrow > ) -> EvalResult < ' tcx >
152+ {
153+ type V = MPlaceTy < ' tcx , Borrow > ;
154+
155+ const WANT_FIELDS_SORTED : bool = true ; // sorted? yes please!
156+
157+ #[ inline( always) ]
158+ fn ecx ( & self ) -> & MiriEvalContext < ' a , ' mir , ' tcx > {
159+ & self . ecx
160+ }
161+
162+ // Hook to detect `UnsafeCell`
163+ fn visit_value ( & mut self , v : MPlaceTy < ' tcx , Borrow > ) -> EvalResult < ' tcx >
164+ {
165+ trace ! ( "UnsafeCellVisitor: {:?} {:?}" , * v, v. layout. ty) ;
166+ let is_unsafe_cell = match v. layout . ty . sty {
167+ ty:: Adt ( adt, _) => Some ( adt. did ) == self . ecx . tcx . lang_items ( ) . unsafe_cell_type ( ) ,
168+ _ => false ,
169+ } ;
170+ if is_unsafe_cell {
171+ // We do not have to recurse further, this is an `UnsafeCell`.
172+ ( self . unsafe_cell_action ) ( v)
173+ } else if self . ecx . type_is_freeze ( v. layout . ty ) {
174+ // This is `Freeze`, there cannot be an `UnsafeCell`
175+ Ok ( ( ) )
176+ } else {
177+ // Proceed further
178+ self . walk_value ( v)
179+ }
180+ }
181+
182+ // We have to do *something* for unions
183+ fn visit_union ( & mut self , v : MPlaceTy < ' tcx , Borrow > ) -> EvalResult < ' tcx >
184+ {
185+ // With unions, we fall back to whatever the type says, to hopefully be consistent
186+ // with LLVM IR.
187+ // FIXME Are we consistent? And is this really the behavior we want?
188+ let frozen = self . ecx . type_is_freeze ( v. layout . ty ) ;
189+ if frozen {
190+ Ok ( ( ) )
191+ } else {
192+ ( self . unsafe_cell_action ) ( v)
193+ }
194+ }
195+
196+ // We should never get to a primitive, but always short-circuit somewhere above
197+ fn visit_primitive ( & mut self , _val : ImmTy < ' tcx , Borrow > ) -> EvalResult < ' tcx >
198+ {
199+ bug ! ( "We should always short-circit before coming to a primitive" )
200+ }
201+ }
202+ }
72203}
0 commit comments