@@ -20,6 +20,7 @@ use rustc_hir as hir;
20
20
use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrs ;
21
21
use rustc_middle:: mir:: interpret:: { ConstAllocation , CtfeProvenance , InterpResult } ;
22
22
use rustc_middle:: query:: TyCtxtAt ;
23
+ use rustc_middle:: span_bug;
23
24
use rustc_middle:: ty:: layout:: TyAndLayout ;
24
25
use rustc_span:: def_id:: LocalDefId ;
25
26
use rustc_span:: sym;
@@ -223,49 +224,59 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
223
224
continue ;
224
225
}
225
226
226
- // Ensure that this is is derived from a shared reference. Crucially, we check this *before*
227
+ // Ensure that this is derived from a shared reference. Crucially, we check this *before*
227
228
// checking whether the `alloc_id` has already been interned. The point of this check is to
228
229
// ensure that when there are multiple pointers to the same allocation, they are *all*
229
230
// derived from a shared reference. Therefore it would be bad if we only checked the first
230
231
// pointer to any given allocation.
231
232
// (It is likely not possible to actually have multiple pointers to the same allocation,
232
233
// so alternatively we could also check that and ICE if there are multiple such pointers.)
233
- // See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for
234
- // "shared reference" and not "immutable", i.e., for why we are allowed interior-mutable
235
- // shared references: they can actually be created in safe code while pointing to apparently
236
- // "immutable" values, via promotion of `&None::<Cell<T>>`.
234
+ // See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for "shared
235
+ // reference" and not "immutable", i.e., for why we are allowing interior-mutable shared
236
+ // references: they can actually be created in safe code while pointing to apparently
237
+ // "immutable" values, via promotion or tail expression lifetime extension of
238
+ // `&None::<Cell<T>>`.
239
+ // We also exclude promoteds from this as `&mut []` can be promoted, which is a mutable
240
+ // reference pointing to an immutable (zero-sized) allocation. We rely on the promotion
241
+ // analysis not screwing up to ensure that it is sound to intern promoteds as immutable.
237
242
if intern_kind != InternKind :: Promoted
238
243
&& inner_mutability == Mutability :: Not
239
244
&& !prov. shared_ref ( )
240
245
{
241
- if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( )
242
- && !just_interned. contains ( & alloc_id)
243
- {
246
+ let is_already_global = ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( ) ;
247
+ if is_already_global && !just_interned. contains ( & alloc_id) {
244
248
// This is a pointer to some memory from another constant. We encounter mutable
245
249
// pointers to such memory since we do not always track immutability through
246
250
// these "global" pointers. Allowing them is harmless; the point of these checks
247
251
// during interning is to justify why we intern the *new* allocations immutably,
248
- // so we can completely ignore existing allocations. We also don't need to add
249
- // this to the todo list, since after all it is already interned.
252
+ // so we can completely ignore existing allocations.
253
+ // We can also skip the rest of this loop iteration, since after all it is already
254
+ // interned.
250
255
continue ;
251
256
}
252
- // Found a mutable reference inside a const where inner allocations should be
253
- // immutable. We exclude promoteds from this, since things like `&mut []` and
254
- // `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
255
- // on the promotion analysis not screwing up to ensure that it is sound to intern
256
- // promoteds as immutable.
257
- trace ! ( "found bad mutable pointer" ) ;
258
- // Prefer dangling pointer errors over mutable pointer errors
259
- if result. is_ok ( ) {
260
- result = Err ( InternResult :: FoundBadMutablePointer ) ;
257
+ // If this is a dangling pointer, that's actually fine -- the problematic case is
258
+ // when there is memory there that someone might expect to be mutable, but we make it immutable.
259
+ let dangling = !is_already_global && !ecx. memory . alloc_map . contains_key ( & alloc_id) ;
260
+ if !dangling {
261
+ // Found a mutable reference inside a const where inner allocations should be
262
+ // immutable.
263
+ if !ecx. tcx . sess . opts . unstable_opts . unleash_the_miri_inside_of_you {
264
+ span_bug ! (
265
+ ecx. tcx. span,
266
+ "the static const safety checks accepted mutable references they should not have accepted"
267
+ ) ;
268
+ }
269
+ // Prefer dangling pointer errors over mutable pointer errors
270
+ if result. is_ok ( ) {
271
+ result = Err ( InternResult :: FoundBadMutablePointer ) ;
272
+ }
261
273
}
262
274
}
263
275
if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( ) {
264
276
// Already interned.
265
277
debug_assert ! ( !ecx. memory. alloc_map. contains_key( & alloc_id) ) ;
266
278
continue ;
267
279
}
268
- just_interned. insert ( alloc_id) ;
269
280
// We always intern with `inner_mutability`, and furthermore we ensured above that if
270
281
// that is "immutable", then there are *no* mutable pointers anywhere in the newly
271
282
// interned memory -- justifying that we can indeed intern immutably. However this also
@@ -276,6 +287,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
276
287
// pointers before deciding which allocations can be made immutable; but for now we are
277
288
// okay with losing some potential for immutability here. This can anyway only affect
278
289
// `static mut`.
290
+ just_interned. insert ( alloc_id) ;
279
291
match intern_shallow ( ecx, alloc_id, inner_mutability) {
280
292
Ok ( nested) => todo. extend ( nested) ,
281
293
Err ( ( ) ) => {
0 commit comments