22//!
33//! After a const evaluation has computed a value, before we destroy the const evaluator's session
44//! 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.)
516
617use super :: validity:: RefTracking ;
718use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
819use rustc_hir as hir;
920use 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 } ;
1122use rustc_target:: abi:: Size ;
1223
1324use rustc_ast:: Mutability ;
@@ -40,11 +51,6 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> {
4051 /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect
4152 /// the intern mode of references we encounter.
4253 inside_unsafe_cell: bool ,
43-
44- /// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants
45- /// for promoteds.
46- /// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field
47- ignore_interior_mut_in_const: bool ,
4854}
4955
5056#[ derive( Copy , Clone , Debug , PartialEq , Hash , Eq ) ]
@@ -53,22 +59,14 @@ enum InternMode {
5359 /// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
5460 /// is *mutable*.
5561 Static ( hir:: Mutability ) ,
56- /// The "base value" of a const, which can have `UnsafeCell` (as in `const FOO: Cell<i32>`),
57- /// but that interior mutability is simply ignored.
58- ConstBase ,
59- /// The "inner values" of a const with references, where `UnsafeCell` is an error.
60- ConstInner ,
62+ /// A `const`.
63+ Const ,
6164}
6265
6366/// Signalling data structure to ensure we don't recurse
6467/// into the memory of other constants or statics
6568struct IsStaticOrFn ;
6669
67- fn mutable_memory_in_const ( tcx : TyCtxtAt < ' _ > , kind : & str ) {
68- // FIXME: show this in validation instead so we can point at where in the value the error is?
69- tcx. sess . span_err ( tcx. span , & format ! ( "mutable memory ({}) is not allowed in constant" , kind) ) ;
70- }
71-
7270/// Intern an allocation without looking at its children.
7371/// `mode` is the mode of the environment where we found this pointer.
7472/// `mutablity` is the mutability of the place to be interned; even if that says
@@ -129,9 +127,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
129127 // See const_eval::machine::MemoryExtra::can_access_statics for why
130128 // immutability is so important.
131129
132- // There are no sensible checks we can do here; grep for `mutable_memory_in_const` to
133- // find the checks we are doing elsewhere to avoid even getting here for memory
134- // that "wants" to be mutable.
130+ // Validation will ensure that there is no `UnsafeCell` on an immutable allocation.
135131 alloc. mutability = Mutability :: Not ;
136132 } ;
137133 // link the alloc id to the actual allocation
@@ -167,17 +163,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
167163 mplace : MPlaceTy < ' tcx > ,
168164 fields : impl Iterator < Item = InterpResult < ' tcx , Self :: V > > ,
169165 ) -> InterpResult < ' tcx > {
166+ // ZSTs cannot contain pointers, so we can skip them.
167+ if mplace. layout . is_zst ( ) {
168+ return Ok ( ( ) ) ;
169+ }
170+
170171 if let Some ( def) = mplace. layout . ty . ty_adt_def ( ) {
171172 if Some ( def. did ) == self . ecx . tcx . lang_items ( ) . unsafe_cell_type ( ) {
172- if self . mode == InternMode :: ConstInner && !self . ignore_interior_mut_in_const {
173- // We do not actually make this memory mutable. But in case the user
174- // *expected* it to be mutable, make sure we error. This is just a
175- // sanity check to prevent users from accidentally exploiting the UB
176- // they caused. It also helps us to find cases where const-checking
177- // failed to prevent an `UnsafeCell` (but as `ignore_interior_mut_in_const`
178- // shows that part is not airtight).
179- mutable_memory_in_const ( self . ecx . tcx , "`UnsafeCell`" ) ;
180- }
181173 // We are crossing over an `UnsafeCell`, we can mutate again. This means that
182174 // References we encounter inside here are interned as pointing to mutable
183175 // allocations.
@@ -189,11 +181,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
189181 }
190182 }
191183
192- // ZSTs cannot contain pointers, so we can skip them.
193- if mplace. layout . is_zst ( ) {
194- return Ok ( ( ) ) ;
195- }
196-
197184 self . walk_aggregate ( mplace, fields)
198185 }
199186
@@ -213,7 +200,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
213200 if let Scalar :: Ptr ( vtable) = mplace. meta . unwrap_meta ( ) {
214201 // Explicitly choose const mode here, since vtables are immutable, even
215202 // if the reference of the fat pointer is mutable.
216- self . intern_shallow ( vtable. alloc_id , InternMode :: ConstInner , None ) ;
203+ self . intern_shallow ( vtable. alloc_id , InternMode :: Const , None ) ;
217204 } else {
218205 // Validation will error (with a better message) on an invalid vtable pointer.
219206 // Let validation show the error message, but make sure it *does* error.
@@ -225,7 +212,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
225212 // Only recurse for allocation-backed pointers.
226213 if let Scalar :: Ptr ( ptr) = mplace. ptr {
227214 // Compute the mode with which we intern this. Our goal here is to make as many
228- // statics as we can immutable so they can be placed in const memory by LLVM.
215+ // statics as we can immutable so they can be placed in read-only memory by LLVM.
229216 let ref_mode = match self . mode {
230217 InternMode :: Static ( mutbl) => {
231218 // In statics, merge outer mutability with reference mutability and
@@ -259,27 +246,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir
259246 }
260247 }
261248 }
262- InternMode :: ConstBase | InternMode :: ConstInner => {
263- // Ignore `UnsafeCell`, everything is immutable. Do some sanity checking
264- // for mutable references that we encounter -- they must all be ZST.
265- // This helps to prevent users from accidentally exploiting UB that they
266- // caused (by somehow getting a mutable reference in a `const`).
267- if ref_mutability == Mutability :: Mut {
268- match referenced_ty. kind ( ) {
269- ty:: Array ( _, n) if n. eval_usize ( * tcx, self . ecx . param_env ) == 0 => { }
270- ty:: Slice ( _)
271- if mplace. meta . unwrap_meta ( ) . to_machine_usize ( self . ecx ) ?
272- == 0 => { }
273- _ => mutable_memory_in_const ( tcx, "`&mut`" ) ,
274- }
275- } else {
276- // A shared reference. We cannot check `freeze` here due to references
277- // like `&dyn Trait` that are actually immutable. We do check for
278- // concrete `UnsafeCell` when traversing the pointee though (if it is
279- // a new allocation, not yet interned).
280- }
281- // Go on with the "inner" rules.
282- 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
283254 }
284255 } ;
285256 match self . intern_shallow ( ptr. alloc_id , ref_mode, Some ( referenced_ty) ) {
@@ -318,7 +289,6 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
318289 ecx : & mut InterpCx < ' mir , ' tcx , M > ,
319290 intern_kind : InternKind ,
320291 ret : MPlaceTy < ' tcx > ,
321- ignore_interior_mut_in_const : bool ,
322292) where
323293 ' tcx : ' mir ,
324294{
@@ -327,7 +297,7 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
327297 InternKind :: Static ( mutbl) => InternMode :: Static ( mutbl) ,
328298 // `Constant` includes array lengths.
329299 // `Promoted` includes non-`Copy` array initializers and `rustc_args_required_const` arguments.
330- InternKind :: Constant | InternKind :: Promoted => InternMode :: ConstBase ,
300+ InternKind :: Constant | InternKind :: Promoted => InternMode :: Const ,
331301 } ;
332302
333303 // Type based interning.
@@ -357,7 +327,6 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
357327 ecx,
358328 mode,
359329 leftover_allocations,
360- ignore_interior_mut_in_const,
361330 inside_unsafe_cell : false ,
362331 }
363332 . visit_value ( mplace) ;
0 commit comments