2
2
//! 1/ They are only assigned-to once, either as a function parameter, or in an assign statement;
3
3
//! 2/ This single assignment dominates all uses;
4
4
//!
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.
7
8
8
9
use rustc_data_structures:: graph:: dominators:: Dominators ;
9
10
use rustc_index:: bit_set:: BitSet ;
10
11
use rustc_index:: { IndexSlice , IndexVec } ;
11
12
use rustc_middle:: middle:: resolve_bound_vars:: Set1 ;
12
13
use rustc_middle:: mir:: visit:: * ;
13
14
use rustc_middle:: mir:: * ;
15
+ use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
14
16
15
17
pub struct SsaLocals {
16
18
/// Assignments to each local. This defines whether the local is SSA.
@@ -24,6 +26,8 @@ pub struct SsaLocals {
24
26
/// Number of "direct" uses of each local, ie. uses that are not dereferences.
25
27
/// We ignore non-uses (Storage statements, debuginfo).
26
28
direct_uses : IndexVec < Local , u32 > ,
29
+ /// Set of SSA locals that are immutably borrowed.
30
+ borrowed_locals : BitSet < Local > ,
27
31
}
28
32
29
33
pub enum AssignedValue < ' a , ' tcx > {
@@ -33,15 +37,22 @@ pub enum AssignedValue<'a, 'tcx> {
33
37
}
34
38
35
39
impl 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 {
37
41
let assignment_order = Vec :: with_capacity ( body. local_decls . len ( ) ) ;
38
42
39
43
let assignments = IndexVec :: from_elem ( Set1 :: Empty , & body. local_decls ) ;
40
44
let dominators = body. basic_blocks . dominators ( ) ;
41
45
42
46
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
+ } ;
45
56
46
57
for local in body. args_iter ( ) {
47
58
visitor. assignments [ local] = Set1 :: One ( DefLocation :: Argument ) ;
@@ -58,6 +69,16 @@ impl SsaLocals {
58
69
visitor. visit_var_debug_info ( var_debug_info) ;
59
70
}
60
71
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
+
61
82
debug ! ( ?visitor. assignments) ;
62
83
debug ! ( ?visitor. direct_uses) ;
63
84
@@ -70,6 +91,7 @@ impl SsaLocals {
70
91
assignments : visitor. assignments ,
71
92
assignment_order : visitor. assignment_order ,
72
93
direct_uses : visitor. direct_uses ,
94
+ borrowed_locals : visitor. borrowed_locals ,
73
95
// This is filled by `compute_copy_classes`.
74
96
copy_classes : IndexVec :: default ( ) ,
75
97
} ;
@@ -174,6 +196,11 @@ impl SsaLocals {
174
196
& self . copy_classes
175
197
}
176
198
199
+ /// Set of SSA locals that are immutably borrowed.
200
+ pub fn borrowed_locals ( & self ) -> & BitSet < Local > {
201
+ & self . borrowed_locals
202
+ }
203
+
177
204
/// Make a property uniform on a copy equivalence class by removing elements.
178
205
pub fn meet_copy_equivalence ( & self , property : & mut BitSet < Local > ) {
179
206
// Consolidate to have a local iff all its copies are.
@@ -208,6 +235,8 @@ struct SsaVisitor<'tcx, 'a> {
208
235
assignments : IndexVec < Local , Set1 < DefLocation > > ,
209
236
assignment_order : Vec < Local > ,
210
237
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 > ,
211
240
}
212
241
213
242
impl SsaVisitor < ' _ , ' _ > {
@@ -232,16 +261,18 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
232
261
PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
233
262
| PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Projection ) => bug ! ( ) ,
234
263
// 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 )
242
265
| PlaceContext :: MutatingUse ( _) => {
243
266
self . assignments [ local] = Set1 :: Many ;
244
267
}
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
+ }
245
276
PlaceContext :: NonMutatingUse ( _) => {
246
277
self . check_dominates ( local, loc) ;
247
278
self . direct_uses [ local] += 1 ;
0 commit comments