22//! 1/ They are only assigned-to once, either as a function parameter, or in an assign statement;
33//! 2/ This single assignment dominates all uses;
44//!
5- //! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
6- //! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
5+ //! As we do not track indirect assignments, a local that has its address taken (either by
6+ //! AddressOf or by borrowing) is considered non-SSA. However, it is UB to modify through an
7+ //! immutable borrow of a `Freeze` local. Those can still be considered to be SSA.
78
89use rustc_data_structures:: graph:: dominators:: Dominators ;
910use rustc_index:: bit_set:: BitSet ;
1011use rustc_index:: { IndexSlice , IndexVec } ;
1112use rustc_middle:: middle:: resolve_bound_vars:: Set1 ;
1213use rustc_middle:: mir:: visit:: * ;
1314use rustc_middle:: mir:: * ;
15+ use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
1416
1517pub struct SsaLocals {
1618 /// Assignments to each local. This defines whether the local is SSA.
@@ -24,6 +26,8 @@ pub struct SsaLocals {
2426 /// Number of "direct" uses of each local, ie. uses that are not dereferences.
2527 /// We ignore non-uses (Storage statements, debuginfo).
2628 direct_uses : IndexVec < Local , u32 > ,
29+ /// Set of SSA locals that are immutably borrowed.
30+ borrowed_locals : BitSet < Local > ,
2731}
2832
2933pub enum AssignedValue < ' a , ' tcx > {
@@ -33,15 +37,22 @@ pub enum AssignedValue<'a, 'tcx> {
3337}
3438
3539impl SsaLocals {
36- pub fn new < ' tcx > ( body : & Body < ' tcx > ) -> SsaLocals {
40+ pub fn new < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , param_env : ParamEnv < ' tcx > ) -> SsaLocals {
3741 let assignment_order = Vec :: with_capacity ( body. local_decls . len ( ) ) ;
3842
3943 let assignments = IndexVec :: from_elem ( Set1 :: Empty , & body. local_decls ) ;
4044 let dominators = body. basic_blocks . dominators ( ) ;
4145
4246 let direct_uses = IndexVec :: from_elem ( 0 , & body. local_decls ) ;
43- let mut visitor =
44- SsaVisitor { body, assignments, assignment_order, dominators, direct_uses } ;
47+ let borrowed_locals = BitSet :: new_empty ( body. local_decls . len ( ) ) ;
48+ let mut visitor = SsaVisitor {
49+ body,
50+ assignments,
51+ assignment_order,
52+ dominators,
53+ direct_uses,
54+ borrowed_locals,
55+ } ;
4556
4657 for local in body. args_iter ( ) {
4758 visitor. assignments [ local] = Set1 :: One ( DefLocation :: Argument ) ;
@@ -58,6 +69,16 @@ impl SsaLocals {
5869 visitor. visit_var_debug_info ( var_debug_info) ;
5970 }
6071
72+ // The immutability of shared borrows only works on `Freeze` locals. If the visitor found
73+ // borrows, we need to check the types. For raw pointers and mutable borrows, the locals
74+ // have already been marked as non-SSA.
75+ debug ! ( ?visitor. borrowed_locals) ;
76+ for local in visitor. borrowed_locals . iter ( ) {
77+ if !body. local_decls [ local] . ty . is_freeze ( tcx, param_env) {
78+ visitor. assignments [ local] = Set1 :: Many ;
79+ }
80+ }
81+
6182 debug ! ( ?visitor. assignments) ;
6283 debug ! ( ?visitor. direct_uses) ;
6384
@@ -70,6 +91,7 @@ impl SsaLocals {
7091 assignments : visitor. assignments ,
7192 assignment_order : visitor. assignment_order ,
7293 direct_uses : visitor. direct_uses ,
94+ borrowed_locals : visitor. borrowed_locals ,
7395 // This is filled by `compute_copy_classes`.
7496 copy_classes : IndexVec :: default ( ) ,
7597 } ;
@@ -174,6 +196,11 @@ impl SsaLocals {
174196 & self . copy_classes
175197 }
176198
199+ /// Set of SSA locals that are immutably borrowed.
200+ pub fn borrowed_locals ( & self ) -> & BitSet < Local > {
201+ & self . borrowed_locals
202+ }
203+
177204 /// Make a property uniform on a copy equivalence class by removing elements.
178205 pub fn meet_copy_equivalence ( & self , property : & mut BitSet < Local > ) {
179206 // Consolidate to have a local iff all its copies are.
@@ -208,6 +235,8 @@ struct SsaVisitor<'tcx, 'a> {
208235 assignments : IndexVec < Local , Set1 < DefLocation > > ,
209236 assignment_order : Vec < Local > ,
210237 direct_uses : IndexVec < Local , u32 > ,
238+ // Track locals that are immutably borrowed, so we can check their type is `Freeze` later.
239+ borrowed_locals : BitSet < Local > ,
211240}
212241
213242impl SsaVisitor < ' _ , ' _ > {
@@ -232,16 +261,18 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
232261 PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
233262 | PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Projection ) => bug ! ( ) ,
234263 // Anything can happen with raw pointers, so remove them.
235- // We do not verify that all uses of the borrow dominate the assignment to `local`,
236- // so we have to remove them too.
237- PlaceContext :: NonMutatingUse (
238- NonMutatingUseContext :: SharedBorrow
239- | NonMutatingUseContext :: FakeBorrow
240- | NonMutatingUseContext :: AddressOf ,
241- )
264+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: AddressOf )
242265 | PlaceContext :: MutatingUse ( _) => {
243266 self . assignments [ local] = Set1 :: Many ;
244267 }
268+ // Immutable borrows are ok, but we need to delay a check that the type is `Freeze`.
269+ PlaceContext :: NonMutatingUse (
270+ NonMutatingUseContext :: SharedBorrow | NonMutatingUseContext :: FakeBorrow ,
271+ ) => {
272+ self . borrowed_locals . insert ( local) ;
273+ self . check_dominates ( local, loc) ;
274+ self . direct_uses [ local] += 1 ;
275+ }
245276 PlaceContext :: NonMutatingUse ( _) => {
246277 self . check_dominates ( local, loc) ;
247278 self . direct_uses [ local] += 1 ;
0 commit comments