@@ -30,6 +30,16 @@ mod simplify;
30
30
mod test;
31
31
mod util;
32
32
33
+ /// Injects a borrow of `place`. The region is unknown at this point; we rely on NLL
34
+ /// inference to find an appropriate one. Therefore you can only call this when NLL
35
+ /// is turned on.
36
+ fn inject_borrow < ' a , ' gcx , ' tcx > ( tcx : ty:: TyCtxt < ' a , ' gcx , ' tcx > ,
37
+ place : Place < ' tcx > )
38
+ -> Rvalue < ' tcx > {
39
+ assert ! ( tcx. use_mir_borrowck( ) ) ;
40
+ Rvalue :: Ref ( tcx. types . re_empty , BorrowKind :: Shared , place)
41
+ }
42
+
33
43
/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
34
44
/// a match arm has a guard expression attached to it.
35
45
#[ derive( Copy , Clone , Debug ) ]
@@ -43,6 +53,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
43
53
discriminant : ExprRef < ' tcx > ,
44
54
arms : Vec < Arm < ' tcx > > )
45
55
-> BlockAnd < ( ) > {
56
+ let tcx = self . hir . tcx ( ) ;
46
57
let discriminant_place = unpack ! ( block = self . as_place( block, discriminant) ) ;
47
58
48
59
// Matching on a `discriminant_place` with an uninhabited type doesn't
@@ -55,16 +66,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
55
66
// HACK(eddyb) Work around the above issue by adding a dummy inspection
56
67
// of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
57
68
// (which will work regardless of type) and storing the result in a temp.
69
+ //
70
+ // FIXME: would just the borrow into `borrowed_input_temp`
71
+ // also achieve the desired effect here? TBD.
58
72
let dummy_source_info = self . source_info ( span) ;
59
73
let dummy_access = Rvalue :: Discriminant ( discriminant_place. clone ( ) ) ;
60
- let dummy_ty = dummy_access. ty ( & self . local_decls , self . hir . tcx ( ) ) ;
74
+ let dummy_ty = dummy_access. ty ( & self . local_decls , tcx) ;
61
75
let dummy_temp = self . temp ( dummy_ty, dummy_source_info. span ) ;
62
76
self . cfg . push_assign ( block, dummy_source_info, & dummy_temp, dummy_access) ;
63
77
78
+ let source_info = self . source_info ( span) ;
79
+ let borrowed_input_temp = if tcx. generate_borrow_of_any_match_input ( ) {
80
+ let borrowed_input = inject_borrow ( tcx, discriminant_place. clone ( ) ) ;
81
+ let borrowed_input_ty = borrowed_input. ty ( & self . local_decls , tcx) ;
82
+ let borrowed_input_temp = self . temp ( borrowed_input_ty, span) ;
83
+ self . cfg . push_assign ( block, source_info, & borrowed_input_temp, borrowed_input) ;
84
+ Some ( borrowed_input_temp)
85
+ } else {
86
+ None
87
+ } ;
88
+
64
89
let mut arm_blocks = ArmBlocks {
65
90
blocks : arms. iter ( )
66
- . map ( |_| self . cfg . start_new_block ( ) )
67
- . collect ( ) ,
91
+ . map ( |_| {
92
+ let arm_block = self . cfg . start_new_block ( ) ;
93
+ arm_block
94
+ } )
95
+ . collect ( ) ,
96
+
68
97
} ;
69
98
70
99
// Get the arm bodies and their scopes, while declaring bindings.
@@ -81,7 +110,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
81
110
// create binding start block for link them by false edges
82
111
let candidate_count = arms. iter ( ) . fold ( 0 , |ac, c| ac + c. patterns . len ( ) ) ;
83
112
let pre_binding_blocks: Vec < _ > = ( 0 ..candidate_count + 1 )
84
- . map ( |_| self . cfg . start_new_block ( ) ) . collect ( ) ;
113
+ . map ( |_| self . cfg . start_new_block ( ) )
114
+
115
+ . collect ( ) ;
85
116
86
117
// assemble a list of candidates: there is one candidate per
87
118
// pattern, which means there may be more than one candidate
@@ -99,6 +130,61 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
99
130
. zip ( pre_binding_blocks. iter ( ) . zip ( pre_binding_blocks. iter ( ) . skip ( 1 ) ) )
100
131
. map ( |( ( arm_index, pattern, guard) ,
101
132
( pre_binding_block, next_candidate_pre_binding_block) ) | {
133
+
134
+ if let ( true , Some ( borrow_temp) ) = ( tcx. emit_read_for_match ( ) ,
135
+ borrowed_input_temp. clone ( ) ) {
136
+ // inject a fake read of the borrowed input at
137
+ // the start of each arm's pattern testing
138
+ // code.
139
+ //
140
+ // This should ensure that you cannot change
141
+ // the variant for an enum while you are in
142
+ // the midst of matching on it.
143
+ //
144
+ // FIXME: I originally had put this at the
145
+ // start of each elem of arm_blocks (see
146
+ // ArmBlocks construction above). But this was
147
+ // broken; for example, when you had a trivial
148
+ // match like `match "foo".to_string() { _s =>
149
+ // {} }`, it would inject a ReadForMatch
150
+ // *after* the move of the input into `_s`,
151
+ // and that then does not pass the borrow
152
+ // check.
153
+ //
154
+ // * So: I need to fine tune exactly *where*
155
+ // the ReadForMatch belongs. Should it come
156
+ // at the beginning of each pattern match,
157
+ // or the end? And, should there be one
158
+ // ReadForMatch per arm, or one per
159
+ // candidate (aka pattern)?
160
+
161
+ self . cfg . push ( * pre_binding_block, Statement {
162
+ source_info,
163
+ kind : StatementKind :: ReadForMatch ( borrow_temp. clone ( ) ) ,
164
+ } ) ;
165
+ }
166
+
167
+ // One might ask: why not build up the match pair such that it
168
+ // matches via `borrowed_input_temp.deref()` instead of
169
+ // using the `discriminant_place` directly, as it is doing here?
170
+ //
171
+ // The basic answer is that if you do that, then you end up with
172
+ // accceses to a shared borrow of the input and that conflicts with
173
+ // any arms that look like e.g.
174
+ //
175
+ // match Some(&4) {
176
+ // ref mut foo => {
177
+ // ... /* mutate `foo` in arm body */ ...
178
+ // }
179
+ // }
180
+ //
181
+ // (Perhaps we could further revise the MIR
182
+ // construction here so that it only does a
183
+ // shared borrow at the outset and delays doing
184
+ // the mutable borrow until after the pattern is
185
+ // matched *and* the guard (if any) for the arm
186
+ // has been run.)
187
+
102
188
Candidate {
103
189
span : pattern. span ,
104
190
match_pairs : vec ! [ MatchPair :: new( discriminant_place. clone( ) , pattern) ] ,
0 commit comments