@@ -32,6 +32,15 @@ impl<Tag> ScalarExt for ScalarMaybeUndef<Tag> {
32
32
33
33
pub trait EvalContextExt < ' tcx > {
34
34
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 > ;
35
44
}
36
45
37
46
@@ -69,4 +78,126 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
69
78
EvalErrorKind :: PathNotFound ( path) . into ( )
70
79
} )
71
80
}
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
+ }
72
203
}
0 commit comments