@@ -243,13 +243,52 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
243243 return ;
244244 }
245245
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 ) ,
252287 }
288+ return ;
289+ }
290+
291+ match * dest {
253292 Place :: Local ( index) if self . mir . local_kind ( index) == LocalKind :: Temp ||
254293 self . mir . local_kind ( index) == LocalKind :: ReturnPointer => {
255294 debug ! ( "store to {:?} (temp or return pointer)" , index) ;
@@ -478,6 +517,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
478517
479518 // Only allow statics (not consts) to refer to other statics.
480519 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+ }
481530 return ;
482531 }
483532 self . add ( Qualif :: NOT_CONST ) ;
0 commit comments