2
2
//!
3
3
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
4
4
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.
5
+ //!
6
+ //! In principle, this is not very complicated: we recursively walk the final value, follow all the
7
+ //! pointers, and move all reachable allocations to the global `tcx` memory. The only complication
8
+ //! is picking the right mutability for the allocations in a `static` initializer: we want to make
9
+ //! as many allocations as possible immutable so LLVM can put them into read-only memory. At the
10
+ //! same time, we need to make memory that could be mutated by the program mutable to avoid
11
+ //! incorrect compilations. To achieve this, we do a type-based traversal of the final value,
12
+ //! tracking mutable and shared references and `UnsafeCell` to determine the current mutability.
13
+ //! (In principle, we could skip this type-based part for `const` and promoteds, as they need to be
14
+ //! always immutable. At least for `const` however we use this opportunity to reject any `const`
15
+ //! that contains allocations whose mutability we cannot identify.)
5
16
6
17
use super :: validity:: RefTracking ;
7
18
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
8
19
use rustc_hir as hir;
9
20
use rustc_middle:: mir:: interpret:: InterpResult ;
10
- use rustc_middle:: ty:: { self , layout:: TyAndLayout , query :: TyCtxtAt , Ty } ;
21
+ use rustc_middle:: ty:: { self , layout:: TyAndLayout , Ty } ;
11
22
use rustc_target:: abi:: Size ;
12
23
13
24
use rustc_ast:: Mutability ;
@@ -33,17 +44,13 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> {
33
44
/// A list of all encountered allocations. After type-based interning, we traverse this list to
34
45
/// also intern allocations that are only referenced by a raw pointer or inside a union.
35
46
leftover_allocations: & ' rt mut FxHashSet < AllocId > ,
36
- /// The root kind of the value that we're looking at. This field is never mutated and only used
37
- /// for sanity assertions that will ICE when `const_qualif` screws up.
47
+ /// The root kind of the value that we're looking at. This field is never mutated for a
48
+ /// particular allocation. It is primarily used to make as many allocations as possible
49
+ /// read-only so LLVM can place them in const memory.
38
50
mode: InternMode ,
39
51
/// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect
40
52
/// the intern mode of references we encounter.
41
53
inside_unsafe_cell: bool ,
42
-
43
- /// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants
44
- /// for promoteds.
45
- /// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field
46
- ignore_interior_mut_in_const: bool ,
47
54
}
48
55
49
56
#[ derive( Copy , Clone , Debug , PartialEq , Hash , Eq ) ]
@@ -52,22 +59,14 @@ enum InternMode {
52
59
/// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
53
60
/// is *mutable*.
54
61
Static ( hir:: Mutability ) ,
55
- /// The "base value" of a const, which can have `UnsafeCell` (as in `const FOO: Cell<i32>`),
56
- /// but that interior mutability is simply ignored.
57
- ConstBase ,
58
- /// The "inner values" of a const with references, where `UnsafeCell` is an error.
59
- ConstInner ,
62
+ /// A `const`.
63
+ Const ,
60
64
}
61
65
62
66
/// Signalling data structure to ensure we don't recurse
63
67
/// into the memory of other constants or statics
64
68
struct IsStaticOrFn ;
65
69
66
- fn mutable_memory_in_const ( tcx : TyCtxtAt < ' _ > , kind : & str ) {
67
- // FIXME: show this in validation instead so we can point at where in the value the error is?
68
- tcx. sess . span_err ( tcx. span , & format ! ( "mutable memory ({}) is not allowed in constant" , kind) ) ;
69
- }
70
-
71
70
/// Intern an allocation without looking at its children.
72
71
/// `mode` is the mode of the environment where we found this pointer.
73
72
/// `mutablity` is the mutability of the place to be interned; even if that says
@@ -113,8 +112,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
113
112
// For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume
114
113
// no interior mutability.
115
114
let frozen = ty. map_or ( true , |ty| ty. is_freeze ( ecx. tcx , ecx. param_env ) ) ;
116
- // For statics, allocation mutability is the combination of the place mutability and
117
- // the type mutability.
115
+ // For statics, allocation mutability is the combination of place mutability and
116
+ // type mutability.
118
117
// The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
119
118
let immutable = mutability == Mutability :: Not && frozen;
120
119
if immutable {
@@ -128,9 +127,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
128
127
// See const_eval::machine::MemoryExtra::can_access_statics for why
129
128
// immutability is so important.
130
129
131
- // There are no sensible checks we can do here; grep for `mutable_memory_in_const` to
132
- // find the checks we are doing elsewhere to avoid even getting here for memory
133
- // that "wants" to be mutable.
130
+ // Validation will ensure that there is no `UnsafeCell` on an immutable allocation.
134
131
alloc. mutability = Mutability :: Not ;
135
132
} ;
136
133
// link the alloc id to the actual allocation
@@ -166,17 +163,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
166
163
mplace : MPlaceTy < ' tcx > ,
167
164
fields : impl Iterator < Item = InterpResult < ' tcx , Self :: V > > ,
168
165
) -> InterpResult < ' tcx > {
166
+ // ZSTs cannot contain pointers, so we can skip them.
167
+ if mplace. layout . is_zst ( ) {
168
+ return Ok ( ( ) ) ;
169
+ }
170
+
169
171
if let Some ( def) = mplace. layout . ty . ty_adt_def ( ) {
170
172
if Some ( def. did ) == self . ecx . tcx . lang_items ( ) . unsafe_cell_type ( ) {
171
- if self . mode == InternMode :: ConstInner && !self . ignore_interior_mut_in_const {
172
- // We do not actually make this memory mutable. But in case the user
173
- // *expected* it to be mutable, make sure we error. This is just a
174
- // sanity check to prevent users from accidentally exploiting the UB
175
- // they caused. It also helps us to find cases where const-checking
176
- // failed to prevent an `UnsafeCell` (but as `ignore_interior_mut_in_const`
177
- // shows that part is not airtight).
178
- mutable_memory_in_const ( self . ecx . tcx , "`UnsafeCell`" ) ;
179
- }
180
173
// We are crossing over an `UnsafeCell`, we can mutate again. This means that
181
174
// References we encounter inside here are interned as pointing to mutable
182
175
// allocations.
@@ -188,11 +181,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
188
181
}
189
182
}
190
183
191
- // ZSTs do not need validation unless they're uninhabited
192
- if mplace. layout . is_zst ( ) && !mplace. layout . abi . is_uninhabited ( ) {
193
- return Ok ( ( ) ) ;
194
- }
195
-
196
184
self . walk_aggregate ( mplace, fields)
197
185
}
198
186
@@ -209,13 +197,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
209
197
if let ty:: Dynamic ( ..) =
210
198
tcx. struct_tail_erasing_lifetimes ( referenced_ty, self . ecx . param_env ) . kind ( )
211
199
{
212
- // Validation will error (with a better message) on an invalid vtable pointer
213
- // so we can safely not do anything if this is not a real pointer.
214
200
if let Scalar :: Ptr ( vtable) = mplace. meta . unwrap_meta ( ) {
215
201
// Explicitly choose const mode here, since vtables are immutable, even
216
202
// if the reference of the fat pointer is mutable.
217
- self . intern_shallow ( vtable. alloc_id , InternMode :: ConstInner , None ) ;
203
+ self . intern_shallow ( vtable. alloc_id , InternMode :: Const , None ) ;
218
204
} else {
205
+ // Validation will error (with a better message) on an invalid vtable pointer.
219
206
// Let validation show the error message, but make sure it *does* error.
220
207
tcx. sess
221
208
. delay_span_bug ( tcx. span , "vtables pointers cannot be integer pointers" ) ;
@@ -224,7 +211,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
224
211
// Check if we have encountered this pointer+layout combination before.
225
212
// Only recurse for allocation-backed pointers.
226
213
if let Scalar :: Ptr ( ptr) = mplace. ptr {
227
- // Compute the mode with which we intern this.
214
+ // Compute the mode with which we intern this. Our goal here is to make as many
215
+ // statics as we can immutable so they can be placed in read-only memory by LLVM.
228
216
let ref_mode = match self . mode {
229
217
InternMode :: Static ( mutbl) => {
230
218
// In statics, merge outer mutability with reference mutability and
@@ -243,8 +231,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
243
231
}
244
232
Mutability :: Not => {
245
233
// A shared reference, things become immutable.
246
- // We do *not* consier `freeze` here -- that is done more precisely
247
- // when traversing the referenced data (by tracking `UnsafeCell`).
234
+ // We do *not* consider `freeze` here: `intern_shallow` considers
235
+ // `freeze` for the actual mutability of this allocation; the intern
236
+ // mode for references contained in this allocation is tracked more
237
+ // precisely when traversing the referenced data (by tracking
238
+ // `UnsafeCell`). This makes sure that `&(&i32, &Cell<i32>)` still
239
+ // has the left inner reference interned into a read-only
240
+ // allocation.
248
241
InternMode :: Static ( Mutability :: Not )
249
242
}
250
243
Mutability :: Mut => {
@@ -253,27 +246,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
253
246
}
254
247
}
255
248
}
256
- InternMode :: ConstBase | InternMode :: ConstInner => {
257
- // Ignore `UnsafeCell`, everything is immutable. Do some sanity checking
258
- // for mutable references that we encounter -- they must all be ZST.
259
- // This helps to prevent users from accidentally exploiting UB that they
260
- // caused (by somehow getting a mutable reference in a `const`).
261
- if ref_mutability == Mutability :: Mut {
262
- match referenced_ty. kind ( ) {
263
- ty:: Array ( _, n) if n. eval_usize ( * tcx, self . ecx . param_env ) == 0 => { }
264
- ty:: Slice ( _)
265
- if mplace. meta . unwrap_meta ( ) . to_machine_usize ( self . ecx ) ?
266
- == 0 => { }
267
- _ => mutable_memory_in_const ( tcx, "`&mut`" ) ,
268
- }
269
- } else {
270
- // A shared reference. We cannot check `freeze` here due to references
271
- // like `&dyn Trait` that are actually immutable. We do check for
272
- // concrete `UnsafeCell` when traversing the pointee though (if it is
273
- // a new allocation, not yet interned).
274
- }
275
- // Go on with the "inner" rules.
276
- InternMode :: ConstInner
249
+ InternMode :: Const => {
250
+ // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
251
+ // checking for mutable references that we encounter -- they must all be
252
+ // ZST.
253
+ InternMode :: Const
277
254
}
278
255
} ;
279
256
match self . intern_shallow ( ptr. alloc_id , ref_mode, Some ( referenced_ty) ) {
@@ -312,7 +289,6 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
312
289
ecx : & mut InterpCx < ' mir , ' tcx , M > ,
313
290
intern_kind : InternKind ,
314
291
ret : MPlaceTy < ' tcx > ,
315
- ignore_interior_mut_in_const : bool ,
316
292
) where
317
293
' tcx : ' mir ,
318
294
{
@@ -321,7 +297,7 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
321
297
InternKind :: Static ( mutbl) => InternMode :: Static ( mutbl) ,
322
298
// `Constant` includes array lengths.
323
299
// `Promoted` includes non-`Copy` array initializers and `rustc_args_required_const` arguments.
324
- InternKind :: Constant | InternKind :: Promoted => InternMode :: ConstBase ,
300
+ InternKind :: Constant | InternKind :: Promoted => InternMode :: Const ,
325
301
} ;
326
302
327
303
// Type based interning.
@@ -351,7 +327,6 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
351
327
ecx,
352
328
mode,
353
329
leftover_allocations,
354
- ignore_interior_mut_in_const,
355
330
inside_unsafe_cell : false ,
356
331
}
357
332
. visit_value ( mplace) ;
0 commit comments