@@ -41,13 +41,12 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
41
41
/// allocation is interned immutably; if it is `Mutability :: Mut `, then the allocation * must be*
42
42
/// already mutable (as a sanity check).
43
43
///
44
- /// `recursive_alloc` is called for all recursively encountered allocations .
44
+ /// Returns an iterator over all relocations referred to by this allocation .
45
45
fn intern_shallow < ' rt , ' mir , ' tcx , T , M : CompileTimeMachine < ' mir , ' tcx , T > > (
46
46
ecx : & ' rt mut InterpCx < ' mir , ' tcx , M > ,
47
47
alloc_id : AllocId ,
48
48
mutability : Mutability ,
49
- mut recursive_alloc : impl FnMut ( & InterpCx < ' mir , ' tcx , M > , CtfeProvenance ) ,
50
- ) -> Result < ( ) , ( ) > {
49
+ ) -> Result < impl Iterator < Item = CtfeProvenance > + ' tcx , ( ) > {
51
50
trace ! ( "intern_shallow {:?}" , alloc_id) ;
52
51
// remove allocation
53
52
let Some ( ( _kind, mut alloc) ) = ecx. memory . alloc_map . remove ( & alloc_id) else {
@@ -65,14 +64,10 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
65
64
assert_eq ! ( alloc. mutability, Mutability :: Mut ) ;
66
65
}
67
66
}
68
- // record child allocations
69
- for & ( _, prov) in alloc. provenance ( ) . ptrs ( ) . iter ( ) {
70
- recursive_alloc ( ecx, prov) ;
71
- }
72
67
// link the alloc id to the actual allocation
73
68
let alloc = ecx. tcx . mk_const_alloc ( alloc) ;
74
69
ecx. tcx . set_alloc_id_memory ( alloc_id, alloc) ;
75
- Ok ( ( ) )
70
+ Ok ( alloc . 0 . 0 . provenance ( ) . ptrs ( ) . iter ( ) . map ( | & ( _ , prov ) | prov ) )
76
71
}
77
72
78
73
/// How a constant value should be interned.
@@ -128,12 +123,16 @@ pub fn intern_const_alloc_recursive<
128
123
}
129
124
} ;
130
125
131
- // Initialize recursive interning.
126
+ // Intern the base allocation, and initialize todo list for recursive interning.
132
127
let base_alloc_id = ret. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ;
133
- let mut todo = vec ! [ ( base_alloc_id, base_mutability) ] ;
128
+ // First we intern the base allocation, as it requires a different mutability.
129
+ // This gives us the initial set of nested allocations, which will then all be processed
130
+ // recursively in the loop below.
131
+ let mut todo: Vec < _ > =
132
+ intern_shallow ( ecx, base_alloc_id, base_mutability) . unwrap ( ) . map ( |prov| prov) . collect ( ) ;
134
133
// We need to distinguish "has just been interned" from "was already in `tcx`",
135
134
// so we track this in a separate set.
136
- let mut just_interned = FxHashSet :: default ( ) ;
135
+ let mut just_interned: FxHashSet < _ > = std :: iter :: once ( base_alloc_id ) . collect ( ) ;
137
136
// Whether we encountered a bad mutable pointer.
138
137
// We want to first report "dangling" and then "mutable", so we need to delay reporting these
139
138
// errors.
@@ -147,52 +146,56 @@ pub fn intern_const_alloc_recursive<
147
146
// raw pointers, so we cannot rely on validation to catch them -- and since interning runs
148
147
// before validation, and interning doesn't know the type of anything, this means we can't show
149
148
// better errors. Maybe we should consider doing validation before interning in the future.
150
- while let Some ( ( alloc_id, mutability) ) = todo. pop ( ) {
149
+ while let Some ( prov) = todo. pop ( ) {
150
+ let alloc_id = prov. alloc_id ( ) ;
151
+ // Crucially, we check this *before* checking whether the `alloc_id`
152
+ // has already been interned. The point of this check is to ensure that when
153
+ // there are multiple pointers to the same allocation, they are *all* immutable.
154
+ // Therefore it would be bad if we only checked the first pointer to any given
155
+ // allocation.
156
+ // (It is likely not possible to actually have multiple pointers to the same allocation,
157
+ // so alternatively we could also check that and ICE if there are multiple such pointers.)
158
+ if intern_kind != InternKind :: Promoted
159
+ && inner_mutability == Mutability :: Not
160
+ && !prov. immutable ( )
161
+ {
162
+ if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( )
163
+ && !just_interned. contains ( & alloc_id)
164
+ {
165
+ // This is a pointer to some memory from another constant. We encounter mutable
166
+ // pointers to such memory since we do not always track immutability through
167
+ // these "global" pointers. Allowing them is harmless; the point of these checks
168
+ // during interning is to justify why we intern the *new* allocations immutably,
169
+ // so we can completely ignore existing allocations. We also don't need to add
170
+ // this to the todo list, since after all it is already interned.
171
+ continue ;
172
+ }
173
+ // Found a mutable pointer inside a const where inner allocations should be
174
+ // immutable. We exclude promoteds from this, since things like `&mut []` and
175
+ // `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
176
+ // on the promotion analysis not screwing up to ensure that it is sound to intern
177
+ // promoteds as immutable.
178
+ found_bad_mutable_pointer = true ;
179
+ }
151
180
if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( ) {
152
181
// Already interned.
153
182
debug_assert ! ( !ecx. memory. alloc_map. contains_key( & alloc_id) ) ;
154
183
continue ;
155
184
}
156
185
just_interned. insert ( alloc_id) ;
157
- intern_shallow ( ecx, alloc_id, mutability, |ecx, prov| {
158
- let alloc_id = prov. alloc_id ( ) ;
159
- if intern_kind != InternKind :: Promoted
160
- && inner_mutability == Mutability :: Not
161
- && !prov. immutable ( )
162
- {
163
- if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( )
164
- && !just_interned. contains ( & alloc_id)
165
- {
166
- // This is a pointer to some memory from another constant. We encounter mutable
167
- // pointers to such memory since we do not always track immutability through
168
- // these "global" pointers. Allowing them is harmless; the point of these checks
169
- // during interning is to justify why we intern the *new* allocations immutably,
170
- // so we can completely ignore existing allocations. We also don't need to add
171
- // this to the todo list, since after all it is already interned.
172
- return ;
173
- }
174
- // Found a mutable pointer inside a const where inner allocations should be
175
- // immutable. We exclude promoteds from this, since things like `&mut []` and
176
- // `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
177
- // on the promotion analysis not screwing up to ensure that it is sound to intern
178
- // promoteds as immutable.
179
- found_bad_mutable_pointer = true ;
180
- }
181
- // We always intern with `inner_mutability`, and furthermore we ensured above that if
182
- // that is "immutable", then there are *no* mutable pointers anywhere in the newly
183
- // interned memory -- justifying that we can indeed intern immutably. However this also
184
- // means we can *not* easily intern immutably here if `prov.immutable()` is true and
185
- // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
186
- // we'd have to somehow check that they are *all* immutable before deciding that this
187
- // allocation can be made immutable. In the future we could consider analyzing all
188
- // pointers before deciding which allocations can be made immutable; but for now we are
189
- // okay with losing some potential for immutability here. This can anyway only affect
190
- // `static mut`.
191
- todo. push ( ( alloc_id, inner_mutability) ) ;
192
- } )
193
- . map_err ( |( ) | {
186
+ // We always intern with `inner_mutability`, and furthermore we ensured above that if
187
+ // that is "immutable", then there are *no* mutable pointers anywhere in the newly
188
+ // interned memory -- justifying that we can indeed intern immutably. However this also
189
+ // means we can *not* easily intern immutably here if `prov.immutable()` is true and
190
+ // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
191
+ // we'd have to somehow check that they are *all* immutable before deciding that this
192
+ // allocation can be made immutable. In the future we could consider analyzing all
193
+ // pointers before deciding which allocations can be made immutable; but for now we are
194
+ // okay with losing some potential for immutability here. This can anyway only affect
195
+ // `static mut`.
196
+ todo. extend ( intern_shallow ( ecx, alloc_id, inner_mutability) . map_err ( |( ) | {
194
197
ecx. tcx . dcx ( ) . emit_err ( DanglingPtrInFinal { span : ecx. tcx . span , kind : intern_kind } )
195
- } ) ?;
198
+ } ) ?) ;
196
199
}
197
200
if found_bad_mutable_pointer {
198
201
return Err ( ecx
@@ -220,13 +223,13 @@ pub fn intern_const_alloc_for_constprop<
220
223
return Ok ( ( ) ) ;
221
224
}
222
225
// Move allocation to `tcx`.
223
- intern_shallow ( ecx, alloc_id, Mutability :: Not , |_ecx , _| {
226
+ for _ in intern_shallow ( ecx, alloc_id, Mutability :: Not ) . map_err ( | ( ) | err_ub ! ( DeadLocal ) ) ? {
224
227
// We are not doing recursive interning, so we don't currently support provenance.
225
228
// (If this assertion ever triggers, we should just implement a
226
229
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
227
230
panic ! ( "`intern_const_alloc_for_constprop` called on allocation with nested provenance" )
228
- } )
229
- . map_err ( | ( ) | err_ub ! ( DeadLocal ) . into ( ) )
231
+ }
232
+ Ok ( ( ) )
230
233
}
231
234
232
235
impl < ' mir , ' tcx : ' mir , M : super :: intern:: CompileTimeMachine < ' mir , ' tcx , !> >
@@ -247,15 +250,14 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
247
250
let dest = self . allocate ( layout, MemoryKind :: Stack ) ?;
248
251
f ( self , & dest. clone ( ) . into ( ) ) ?;
249
252
let alloc_id = dest. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ; // this was just allocated, it must have provenance
250
- intern_shallow ( self , alloc_id, Mutability :: Not , |ecx , prov| {
253
+ for prov in intern_shallow ( self , alloc_id, Mutability :: Not ) . unwrap ( ) {
251
254
// We are not doing recursive interning, so we don't currently support provenance.
252
255
// (If this assertion ever triggers, we should just implement a
253
256
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
254
- if !ecx . tcx . try_get_global_alloc ( prov. alloc_id ( ) ) . is_some ( ) {
257
+ if !self . tcx . try_get_global_alloc ( prov. alloc_id ( ) ) . is_some ( ) {
255
258
panic ! ( "`intern_with_temp_alloc` with nested allocations" ) ;
256
259
}
257
- } )
258
- . unwrap ( ) ;
260
+ }
259
261
Ok ( alloc_id)
260
262
}
261
263
}
0 commit comments