33use rustc_errors:: { struct_span_err, Applicability , Diagnostic , ErrorReported } ;
44use rustc_hir:: def_id:: DefId ;
55use rustc_hir:: { self as hir, HirId , LangItem } ;
6+ use rustc_index:: bit_set:: BitSet ;
67use rustc_infer:: infer:: TyCtxtInferExt ;
78use rustc_infer:: traits:: { ImplSource , Obligation , ObligationCause } ;
89use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
@@ -188,6 +189,9 @@ pub struct Validator<'mir, 'tcx> {
188189 /// The span of the current statement.
189190 span : Span ,
190191
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+
191195 error_emitted : Option < ErrorReported > ,
192196 secondary_errors : Vec < Diagnostic > ,
193197}
@@ -206,6 +210,7 @@ impl Validator<'mir, 'tcx> {
206210 span : ccx. body . span ,
207211 ccx,
208212 qualifs : Default :: default ( ) ,
213+ local_has_storage_dead : None ,
209214 error_emitted : None ,
210215 secondary_errors : Vec :: new ( ) ,
211216 }
@@ -282,6 +287,27 @@ impl Validator<'mir, 'tcx> {
282287 }
283288 }
284289
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+
285311 pub fn qualifs_in_return_place ( & mut self ) -> ConstQualifs {
286312 self . qualifs . in_return_place ( self . ccx , self . error_emitted )
287313 }
@@ -556,7 +582,29 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
556582 ) ;
557583
558584 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+ }
560608 }
561609 }
562610
0 commit comments