diff options
author | Felix S. Klock II <pnkfelix@pnkfx.org> | 2018-05-04 12:05:10 +0200 |
---|---|---|
committer | Felix S. Klock II <pnkfelix@pnkfx.org> | 2018-05-29 23:02:39 +0200 |
commit | 173315e353a9c3c3b8ba2301eaac14fada41f84f (patch) | |
tree | ff3168b54d4ec1d39b2a99cdfbba692b8cb594c3 | |
parent | rust-lang/rust#27282: Add `StatementKind::ReadForMatch` to MIR. (diff) | |
download | grust-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.rs | 4 | ||||
-rw-r--r-- | src/librustc/ty/context.rs | 13 | ||||
-rw-r--r-- | src/librustc_mir/build/matches/mod.rs | 94 |
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; | |||
30 | mod test; | 30 | mod test; |
31 | mod util; | 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 | /// 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)], |