@@ -243,13 +243,52 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
243
243
return ;
244
244
}
245
245
246
- match * dest {
247
- Place :: Local ( index) if ( self . mir . local_kind ( index) == LocalKind :: Var ||
248
- self . mir . local_kind ( index) == LocalKind :: Arg ) &&
249
- self . tcx . sess . features_untracked ( ) . const_let => {
250
- debug ! ( "store to var {:?}" , index) ;
251
- self . local_qualif [ index] = Some ( self . qualif ) ;
246
+ if self . tcx . features ( ) . const_let {
247
+ let mut dest = dest;
248
+ let index = loop {
249
+ match dest {
250
+ // with `const_let` active, we treat all locals equal
251
+ Place :: Local ( index) => break * index,
252
+ // projections are transparent for assignments
253
+ // we qualify the entire destination at once, even if just a field would have
254
+ // stricter qualification
255
+ Place :: Projection ( proj) => {
256
+ // Catch more errors in the destination. `visit_place` also checks various
257
+ // projection rules like union field access and raw pointer deref
258
+ self . visit_place (
259
+ dest,
260
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
261
+ location
262
+ ) ;
263
+ dest = & proj. base ;
264
+ } ,
265
+ Place :: Promoted ( ..) => bug ! ( "promoteds don't exist yet during promotion" ) ,
266
+ Place :: Static ( ..) => {
267
+ // Catch more errors in the destination. `visit_place` also checks that we
268
+ // do not try to access statics from constants or try to mutate statics
269
+ self . visit_place (
270
+ dest,
271
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
272
+ location
273
+ ) ;
274
+ return ;
275
+ }
276
+ }
277
+ } ;
278
+ debug ! ( "store to var {:?}" , index) ;
279
+ match & mut self . local_qualif [ index] {
280
+ // this is overly restrictive, because even full assignments do not clear the qualif
281
+ // While we could special case full assignments, this would be inconsistent with
282
+ // aggregates where we overwrite all fields via assignments, which would not get
283
+ // that feature.
284
+ Some ( ref mut qualif) => * qualif = * qualif | self . qualif ,
285
+ // insert new qualification
286
+ qualif @ None => * qualif = Some ( self . qualif ) ,
252
287
}
288
+ return ;
289
+ }
290
+
291
+ match * dest {
253
292
Place :: Local ( index) if self . mir . local_kind ( index) == LocalKind :: Temp ||
254
293
self . mir . local_kind ( index) == LocalKind :: ReturnPointer => {
255
294
debug ! ( "store to {:?} (temp or return pointer)" , index) ;
@@ -478,6 +517,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
478
517
479
518
// Only allow statics (not consts) to refer to other statics.
480
519
if self . mode == Mode :: Static || self . mode == Mode :: StaticMut {
520
+ if context. is_mutating_use ( ) {
521
+ // this is not strictly necessary as miri will also bail out
522
+ // For interior mutability we can't really catch this statically as that
523
+ // goes through raw pointers and intermediate temporaries, so miri has
524
+ // to catch this anyway
525
+ self . tcx . sess . span_err (
526
+ self . span ,
527
+ "cannot mutate statics in the initializer of another static" ,
528
+ ) ;
529
+ }
481
530
return ;
482
531
}
483
532
self . add ( Qualif :: NOT_CONST ) ;
0 commit comments