3
3
use rustc_errors:: { struct_span_err, Applicability , Diagnostic , ErrorReported } ;
4
4
use rustc_hir:: def_id:: DefId ;
5
5
use rustc_hir:: { self as hir, HirId , LangItem } ;
6
+ use rustc_index:: bit_set:: BitSet ;
6
7
use rustc_infer:: infer:: TyCtxtInferExt ;
7
8
use rustc_infer:: traits:: { ImplSource , Obligation , ObligationCause } ;
8
9
use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
@@ -188,6 +189,9 @@ pub struct Validator<'mir, 'tcx> {
188
189
/// The span of the current statement.
189
190
span : Span ,
190
191
192
+ /// A set that stores for each local whether it has a `StorageDead` for it somewhere.
193
+ local_has_storage_dead : Option < BitSet < Local > > ,
194
+
191
195
error_emitted : Option < ErrorReported > ,
192
196
secondary_errors : Vec < Diagnostic > ,
193
197
}
@@ -206,6 +210,7 @@ impl Validator<'mir, 'tcx> {
206
210
span : ccx. body . span ,
207
211
ccx,
208
212
qualifs : Default :: default ( ) ,
213
+ local_has_storage_dead : None ,
209
214
error_emitted : None ,
210
215
secondary_errors : Vec :: new ( ) ,
211
216
}
@@ -282,6 +287,27 @@ impl Validator<'mir, 'tcx> {
282
287
}
283
288
}
284
289
290
+ fn local_has_storage_dead ( & mut self , local : Local ) -> bool {
291
+ let ccx = self . ccx ;
292
+ self . local_has_storage_dead
293
+ . get_or_insert_with ( || {
294
+ struct StorageDeads {
295
+ locals : BitSet < Local > ,
296
+ }
297
+ impl Visitor < ' tcx > for StorageDeads {
298
+ fn visit_statement ( & mut self , stmt : & Statement < ' tcx > , _: Location ) {
299
+ if let StatementKind :: StorageDead ( l) = stmt. kind {
300
+ self . locals . insert ( l) ;
301
+ }
302
+ }
303
+ }
304
+ let mut v = StorageDeads { locals : BitSet :: new_empty ( ccx. body . local_decls . len ( ) ) } ;
305
+ v. visit_body ( ccx. body ) ;
306
+ v. locals
307
+ } )
308
+ . contains ( local)
309
+ }
310
+
285
311
pub fn qualifs_in_return_place ( & mut self ) -> ConstQualifs {
286
312
self . qualifs . in_return_place ( self . ccx , self . error_emitted )
287
313
}
@@ -556,7 +582,29 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
556
582
) ;
557
583
558
584
if borrowed_place_has_mut_interior {
559
- self . check_op ( ops:: CellBorrow ) ;
585
+ match self . const_kind ( ) {
586
+ // In a const fn all borrows are transient or point to the places given via
587
+ // references in the arguments (so we already checked them with
588
+ // TransientCellBorrow/CellBorrow as appropriate).
589
+ // The borrow checker guarantees that no new non-transient borrows are created.
590
+ // NOTE: Once we have heap allocations during CTFE we need to figure out
591
+ // how to prevent `const fn` to create long-lived allocations that point
592
+ // to (interior) mutable memory.
593
+ hir:: ConstContext :: ConstFn => self . check_op ( ops:: TransientCellBorrow ) ,
594
+ _ => {
595
+ // Locals with StorageDead are definitely not part of the final constant value, and
596
+ // it is thus inherently safe to permit such locals to have their
597
+ // address taken as we can't end up with a reference to them in the
598
+ // final value.
599
+ // Note: This is only sound if every local that has a `StorageDead` has a
600
+ // `StorageDead` in every control flow path leading to a `return` terminator.
601
+ if self . local_has_storage_dead ( place. local ) {
602
+ self . check_op ( ops:: TransientCellBorrow ) ;
603
+ } else {
604
+ self . check_op ( ops:: CellBorrow ) ;
605
+ }
606
+ }
607
+ }
560
608
}
561
609
}
562
610
0 commit comments