@@ -27,7 +27,7 @@ use rustc_span::sym;
27
27
28
28
use super :: { AllocId , Allocation , InterpCx , MPlaceTy , Machine , MemoryKind , PlaceTy } ;
29
29
use crate :: const_eval;
30
- use crate :: errors:: { DanglingPtrInFinal , MutablePtrInFinal } ;
30
+ use crate :: errors:: MutablePtrInFinal ;
31
31
32
32
pub trait CompileTimeMachine < ' mir , ' tcx : ' mir , T > = Machine <
33
33
' mir ,
@@ -62,26 +62,13 @@ impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_, '_> {
62
62
fn intern_shallow < ' rt , ' mir , ' tcx , T , M : CompileTimeMachine < ' mir , ' tcx , T > > (
63
63
ecx : & ' rt mut InterpCx < ' mir , ' tcx , M > ,
64
64
alloc_id : AllocId ,
65
- mutability : Mutability ,
66
65
) -> Result < impl Iterator < Item = CtfeProvenance > + ' tcx , ( ) > {
67
66
trace ! ( "intern_shallow {:?}" , alloc_id) ;
68
67
// remove allocation
69
68
// FIXME(#120456) - is `swap_remove` correct?
70
- let Some ( ( _kind, mut alloc) ) = ecx. memory . alloc_map . swap_remove ( & alloc_id) else {
69
+ let Some ( ( _kind, alloc) ) = ecx. memory . alloc_map . swap_remove ( & alloc_id) else {
71
70
return Err ( ( ) ) ;
72
71
} ;
73
- // Set allocation mutability as appropriate. This is used by LLVM to put things into
74
- // read-only memory, and also by Miri when evaluating other globals that
75
- // access this one.
76
- match mutability {
77
- Mutability :: Not => {
78
- alloc. mutability = Mutability :: Not ;
79
- }
80
- Mutability :: Mut => {
81
- // This must be already mutable, we won't "un-freeze" allocations ever.
82
- assert_eq ! ( alloc. mutability, Mutability :: Mut ) ;
83
- }
84
- }
85
72
// link the alloc id to the actual allocation
86
73
let alloc = ecx. tcx . mk_const_alloc ( alloc) ;
87
74
if let Some ( static_id) = ecx. machine . static_def_id ( ) {
@@ -123,14 +110,9 @@ pub enum InternKind {
123
110
Promoted ,
124
111
}
125
112
126
- /// Intern `ret` and everything it references.
127
- ///
128
- /// This *cannot raise an interpreter error*. Doing so is left to validation, which
129
- /// tracks where in the value we are and thus can show much better error messages.
130
- ///
131
- /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
132
- #[ instrument( level = "debug" , skip( ecx) ) ]
133
- pub fn intern_const_alloc_recursive <
113
+ /// Now that evaluation is finished, and we are not going to modify allocations anymore,
114
+ /// recursively mark all allocations as immutable if the item kind calls for it (const/promoted/immut static).
115
+ pub fn patch_mutability_of_allocs <
134
116
' mir ,
135
117
' tcx : ' mir ,
136
118
M : CompileTimeMachine < ' mir , ' tcx , const_eval:: MemoryKind > ,
@@ -141,12 +123,12 @@ pub fn intern_const_alloc_recursive<
141
123
) -> Result < ( ) , ErrorGuaranteed > {
142
124
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
143
125
// that we are starting in, and all other allocations that we are encountering recursively.
144
- let ( base_mutability, inner_mutability, is_static ) = match intern_kind {
126
+ let ( base_mutability, inner_mutability) = match intern_kind {
145
127
InternKind :: Constant | InternKind :: Promoted => {
146
128
// Completely immutable. Interning anything mutably here can only lead to unsoundness,
147
129
// since all consts are conceptually independent values but share the same underlying
148
130
// memory.
149
- ( Mutability :: Not , Mutability :: Not , false )
131
+ ( Mutability :: Not , Mutability :: Not )
150
132
}
151
133
InternKind :: Static ( Mutability :: Not ) => {
152
134
(
@@ -159,30 +141,79 @@ pub fn intern_const_alloc_recursive<
159
141
// Inner allocations are never mutable. They can only arise via the "tail
160
142
// expression" / "outer scope" rule, and we treat them consistently with `const`.
161
143
Mutability :: Not ,
162
- true ,
163
144
)
164
145
}
165
146
InternKind :: Static ( Mutability :: Mut ) => {
166
147
// Just make everything mutable. We accept code like
167
148
// `static mut X = &mut [42]`, so even inner allocations need to be mutable.
168
- ( Mutability :: Mut , Mutability :: Mut , true )
149
+ ( Mutability :: Mut , Mutability :: Mut )
169
150
}
170
151
} ;
152
+ let base_alloc_id = ret. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ;
153
+ let mut todo: Vec < _ > = {
154
+ let base_alloc = & mut ecx. memory . alloc_map . get_mut ( & base_alloc_id) . unwrap ( ) . 1 ;
155
+ base_alloc. mutability = base_mutability;
156
+ base_alloc. provenance ( ) . ptrs ( ) . iter ( ) . copied ( ) . collect ( )
157
+ } ;
158
+ let mut seen = FxHashSet :: default ( ) ;
159
+ seen. insert ( base_alloc_id) ;
160
+ while let Some ( ( _, prov) ) = todo. pop ( ) {
161
+ if !seen. insert ( prov. alloc_id ( ) ) {
162
+ // Already processed
163
+ continue ;
164
+ }
165
+ let Some ( ( _, alloc) ) = & mut ecx. memory . alloc_map . get_mut ( & prov. alloc_id ( ) ) else {
166
+ continue ;
167
+ } ;
168
+ // We always intern with `inner_mutability`, and furthermore we ensured above that if
169
+ // that is "immutable", then there are *no* mutable pointers anywhere in the newly
170
+ // interned memory -- justifying that we can indeed intern immutably. However this also
171
+ // means we can *not* easily intern immutably here if `prov.immutable()` is true and
172
+ // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
173
+ // we'd have to somehow check that they are *all* immutable before deciding that this
174
+ // allocation can be made immutable. In the future we could consider analyzing all
175
+ // pointers before deciding which allocations can be made immutable; but for now we are
176
+ // okay with losing some potential for immutability here. This can anyway only affect
177
+ // `static mut`.
178
+ alloc. mutability = inner_mutability;
179
+ todo. extend ( alloc. provenance ( ) . ptrs ( ) . iter ( ) . copied ( ) ) ;
180
+ }
181
+ Ok ( ( ) )
182
+ }
183
+
184
+ /// Intern `ret` and everything it references.
185
+ ///
186
+ /// This *cannot raise an interpreter error*. Doing so is left to validation, which
187
+ /// tracks where in the value we are and thus can show much better error messages.
188
+ ///
189
+ /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
190
+ #[ instrument( level = "debug" , skip( ecx) ) ]
191
+ pub fn intern_const_alloc_recursive <
192
+ ' mir ,
193
+ ' tcx : ' mir ,
194
+ M : CompileTimeMachine < ' mir , ' tcx , const_eval:: MemoryKind > ,
195
+ > (
196
+ ecx : & mut InterpCx < ' mir , ' tcx , M > ,
197
+ intern_kind : InternKind ,
198
+ ret : & MPlaceTy < ' tcx > ,
199
+ ) -> Result < ( ) , ErrorGuaranteed > {
200
+ let ( inner_mutability, is_static) = match intern_kind {
201
+ InternKind :: Constant | InternKind :: Promoted => ( Mutability :: Not , false ) ,
202
+ InternKind :: Static ( mutability) => ( mutability, true ) ,
203
+ } ;
171
204
172
205
// Intern the base allocation, and initialize todo list for recursive interning.
173
206
let base_alloc_id = ret. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ;
174
- trace ! ( ?base_alloc_id, ?base_mutability ) ;
207
+ trace ! ( ?base_alloc_id) ;
175
208
// First we intern the base allocation, as it requires a different mutability.
176
209
// This gives us the initial set of nested allocations, which will then all be processed
177
210
// recursively in the loop below.
178
211
let mut todo: Vec < _ > = if is_static {
179
212
// Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`.
180
- // But still change its mutability to match the requested one.
181
- let alloc = ecx. memory . alloc_map . get_mut ( & base_alloc_id) . unwrap ( ) ;
182
- alloc. 1 . mutability = base_mutability;
213
+ let alloc = ecx. memory . alloc_map . get ( & base_alloc_id) . unwrap ( ) ;
183
214
alloc. 1 . provenance ( ) . ptrs ( ) . iter ( ) . map ( |& ( _, prov) | prov) . collect ( )
184
215
} else {
185
- intern_shallow ( ecx, base_alloc_id, base_mutability ) . unwrap ( ) . collect ( )
216
+ intern_shallow ( ecx, base_alloc_id) . unwrap ( ) . collect ( )
186
217
} ;
187
218
// We need to distinguish "has just been interned" from "was already in `tcx`",
188
219
// so we track this in a separate set.
@@ -248,19 +279,7 @@ pub fn intern_const_alloc_recursive<
248
279
continue ;
249
280
}
250
281
just_interned. insert ( alloc_id) ;
251
- // We always intern with `inner_mutability`, and furthermore we ensured above that if
252
- // that is "immutable", then there are *no* mutable pointers anywhere in the newly
253
- // interned memory -- justifying that we can indeed intern immutably. However this also
254
- // means we can *not* easily intern immutably here if `prov.immutable()` is true and
255
- // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
256
- // we'd have to somehow check that they are *all* immutable before deciding that this
257
- // allocation can be made immutable. In the future we could consider analyzing all
258
- // pointers before deciding which allocations can be made immutable; but for now we are
259
- // okay with losing some potential for immutability here. This can anyway only affect
260
- // `static mut`.
261
- todo. extend ( intern_shallow ( ecx, alloc_id, inner_mutability) . map_err ( |( ) | {
262
- ecx. tcx . dcx ( ) . emit_err ( DanglingPtrInFinal { span : ecx. tcx . span , kind : intern_kind } )
263
- } ) ?) ;
282
+ todo. extend ( intern_shallow ( ecx, alloc_id) . unwrap ( ) ) ;
264
283
}
265
284
if found_bad_mutable_pointer {
266
285
let err_diag = MutablePtrInFinal { span : ecx. tcx . span , kind : intern_kind } ;
@@ -291,7 +310,8 @@ pub fn intern_const_alloc_for_constprop<
291
310
return Ok ( ( ) ) ;
292
311
}
293
312
// Move allocation to `tcx`.
294
- for _ in intern_shallow ( ecx, alloc_id, Mutability :: Not ) . map_err ( |( ) | err_ub ! ( DeadLocal ) ) ? {
313
+ ecx. memory . alloc_map . get_mut ( & alloc_id) . unwrap ( ) . 1 . mutability = Mutability :: Not ;
314
+ for _ in intern_shallow ( ecx, alloc_id) . map_err ( |( ) | err_ub ! ( DeadLocal ) ) ? {
295
315
// We are not doing recursive interning, so we don't currently support provenance.
296
316
// (If this assertion ever triggers, we should just implement a
297
317
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
@@ -318,7 +338,8 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
318
338
let dest = self . allocate ( layout, MemoryKind :: Stack ) ?;
319
339
f ( self , & dest. clone ( ) . into ( ) ) ?;
320
340
let alloc_id = dest. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ; // this was just allocated, it must have provenance
321
- for prov in intern_shallow ( self , alloc_id, Mutability :: Not ) . unwrap ( ) {
341
+ self . memory . alloc_map . get_mut ( & alloc_id) . unwrap ( ) . 1 . mutability = Mutability :: Not ;
342
+ for prov in intern_shallow ( self , alloc_id) . unwrap ( ) {
322
343
// We are not doing recursive interning, so we don't currently support provenance.
323
344
// (If this assertion ever triggers, we should just implement a
324
345
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
0 commit comments