summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2018-05-04 12:05:10 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2018-05-29 23:02:39 +0200
commit173315e353a9c3c3b8ba2301eaac14fada41f84f (patch)
treeff3168b54d4ec1d39b2a99cdfbba692b8cb594c3
parentrust-lang/rust#27282: Add `StatementKind::ReadForMatch` to MIR. (diff)
downloadgrust-173315e353a9c3c3b8ba2301eaac14fada41f84f.tar.gz
grust-173315e353a9c3c3b8ba2301eaac14fada41f84f.tar.bz2
grust-173315e353a9c3c3b8ba2301eaac14fada41f84f.tar.xz
rust-lang/rust#27282: emit `ReadForMatch` on each match arm.
Also, turn on ReadForMatch emission by default (when using NLL).
-rw-r--r--src/librustc/session/config.rs4
-rw-r--r--src/librustc/ty/context.rs13
-rw-r--r--src/librustc_mir/build/matches/mod.rs94
3 files changed, 107 insertions, 4 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 45af66815f..35538e5d02 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1298,10 +1298,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
1298 "dump facts from NLL analysis into side files"), 1298 "dump facts from NLL analysis into side files"),
1299 disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED], 1299 disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED],
1300 "disable user provided type assertion in NLL"), 1300 "disable user provided type assertion in NLL"),
1301 nll_dont_emit_read_for_match: bool = (false, parse_bool, [UNTRACKED],
1302 "in match codegen, do not include ReadForMatch statements (used by mir-borrowck)"),
1301 polonius: bool = (false, parse_bool, [UNTRACKED], 1303 polonius: bool = (false, parse_bool, [UNTRACKED],
1302 "enable polonius-based borrow-checker"), 1304 "enable polonius-based borrow-checker"),
1303 codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], 1305 codegen_time_graph: bool = (false, parse_bool, [UNTRACKED],
1304 "generate a graphical HTML report of time spent in codegen and LLVM"), 1306 "generate a graphical HTML report of time spent in codegen and LLVM"),
1307 trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
1308 "generate a graphical HTML report of time spent in trans and LLVM"),
1305 thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], 1309 thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
1306 "enable ThinLTO when possible"), 1310 "enable ThinLTO when possible"),
1307 inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED], 1311 inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 1ee28a6542..68f55b4993 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1356,6 +1356,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
1356 self.borrowck_mode().use_mir() 1356 self.borrowck_mode().use_mir()
1357 } 1357 }
1358 1358
1359 /// If true, make MIR codegen for `match` emit a temp that holds a
1360 /// borrow of the input to the match expression.
1361 pub fn generate_borrow_of_any_match_input(&self) -> bool {
1362 self.emit_read_for_match()
1363 }
1364
1365 /// If true, make MIR codegen for `match` emit ReadForMatch
1366 /// statements (which simulate the maximal effect of executing the
1367 /// patterns in a match arm).
1368 pub fn emit_read_for_match(&self) -> bool {
1369 self.use_mir_borrowck() && !self.sess.opts.debugging_opts.nll_dont_emit_read_for_match
1370 }
1371
1359 /// If true, pattern variables for use in guards on match arms 1372 /// If true, pattern variables for use in guards on match arms
1360 /// will be bound as references to the data, and occurrences of 1373 /// will be bound as references to the data, and occurrences of
1361 /// those variables in the guard expression will implicitly 1374 /// those variables in the guard expression will implicitly
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index f3953d0877..67c9db1bf9 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -30,6 +30,16 @@ mod simplify;
30mod test; 30mod test;
31mod util; 31mod 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.
36fn 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/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether 43/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
34/// a match arm has a guard expression attached to it. 44/// a match arm has a guard expression attached to it.
35#[derive(Copy, Clone, Debug)] 45#[derive(Copy, Clone, Debug)]
@@ -43,6 +53,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
43 discriminant: ExprRef<'tcx>, 53 discriminant: ExprRef<'tcx>,
44 arms: Vec<Arm<'tcx>>) 54 arms: Vec<Arm<'tcx>>)
45 -> BlockAnd<()> { 55 -> BlockAnd<()> {
56 let tcx = self.hir.tcx();
46 let discriminant_place = unpack!(block = self.as_place(block, discriminant)); 57 let discriminant_place = unpack!(block = self.as_place(block, discriminant));
47 58
48 // Matching on a `discriminant_place` with an uninhabited type doesn't 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 // HACK(eddyb) Work around the above issue by adding a dummy inspection 66 // HACK(eddyb) Work around the above issue by adding a dummy inspection
56 // of `discriminant_place`, specifically by applying `Rvalue::Discriminant` 67 // of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
57 // (which will work regardless of type) and storing the result in a temp. 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 let dummy_source_info = self.source_info(span); 72 let dummy_source_info = self.source_info(span);
59 let dummy_access = Rvalue::Discriminant(discriminant_place.clone()); 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 let dummy_temp = self.temp(dummy_ty, dummy_source_info.span); 75 let dummy_temp = self.temp(dummy_ty, dummy_source_info.span);
62 self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access); 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 let mut arm_blocks = ArmBlocks { 89 let mut arm_blocks = ArmBlocks {
65 blocks: arms.iter() 90 blocks: arms.iter()
66 .map(|_| self.cfg.start_new_block()) 91 .map(|_| {
67 .collect(), 92 let arm_block = self.cfg.start_new_block();
93 arm_block
94 })
95 .collect(),
96
68 }; 97 };
69 98
70 // Get the arm bodies and their scopes, while declaring bindings. 99 // Get the arm bodies and their scopes, while declaring bindings.
@@ -81,7 +110,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
81 // create binding start block for link them by false edges 110 // create binding start block for link them by false edges
82 let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len()); 111 let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len());
83 let pre_binding_blocks: Vec<_> = (0..candidate_count + 1) 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 // assemble a list of candidates: there is one candidate per 117 // assemble a list of candidates: there is one candidate per
87 // pattern, which means there may be more than one candidate 118 // pattern, which means there may be more than one candidate
@@ -99,6 +130,61 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
99 .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1))) 130 .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
100 .map(|((arm_index, pattern, guard), 131 .map(|((arm_index, pattern, guard),
101 (pre_binding_block, next_candidate_pre_binding_block))| { 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 Candidate { 188 Candidate {
103 span: pattern.span, 189 span: pattern.span,
104 match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)], 190 match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)],