@@ -30,6 +30,16 @@ mod simplify;
3030mod test;
3131mod util;
3232
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+
3343/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
3444/// a match arm has a guard expression attached to it.
3545#[ derive( Copy , Clone , Debug ) ]
@@ -43,6 +53,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
4353 discriminant : ExprRef < ' tcx > ,
4454 arms : Vec < Arm < ' tcx > > )
4555 -> BlockAnd < ( ) > {
56+ let tcx = self . hir . tcx ( ) ;
4657 let discriminant_place = unpack ! ( block = self . as_place( block, discriminant) ) ;
4758
4859 // Matching on a `discriminant_place` with an uninhabited type doesn't
@@ -55,16 +66,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5566 // HACK(eddyb) Work around the above issue by adding a dummy inspection
5667 // of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
5768 // (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.
5872 let dummy_source_info = self . source_info ( span) ;
5973 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) ;
6175 let dummy_temp = self . temp ( dummy_ty, dummy_source_info. span ) ;
6276 self . cfg . push_assign ( block, dummy_source_info, & dummy_temp, dummy_access) ;
6377
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+
6489 let mut arm_blocks = ArmBlocks {
6590 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+
6897 } ;
6998
7099 // Get the arm bodies and their scopes, while declaring bindings.
@@ -81,7 +110,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
81110 // create binding start block for link them by false edges
82111 let candidate_count = arms. iter ( ) . fold ( 0 , |ac, c| ac + c. patterns . len ( ) ) ;
83112 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 ( ) ;
85116
86117 // assemble a list of candidates: there is one candidate per
87118 // pattern, which means there may be more than one candidate
@@ -99,6 +130,61 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
99130 . zip ( pre_binding_blocks. iter ( ) . zip ( pre_binding_blocks. iter ( ) . skip ( 1 ) ) )
100131 . map ( |( ( arm_index, pattern, guard) ,
101132 ( 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+
102188 Candidate {
103189 span : pattern. span ,
104190 match_pairs : vec ! [ MatchPair :: new( discriminant_place. clone( ) , pattern) ] ,
0 commit comments