1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Managing the scope stack. The scopes are tied to lexical scopes, so as
we descend the HAIR, we push a scope on the stack, translate ite
contents, and then pop it off. Every scope is named by a
`CodeExtent`.
### SEME Regions
When pushing a new scope, we record the current point in the graph (a
basic block); this marks the entry to the scope. We then generate more
stuff in the control-flow graph. Whenever the scope is exited, either
via a `break` or `return` or just by fallthrough, that marks an exit
from the scope. Each lexical scope thus corresponds to a single-entry,
multiple-exit (SEME) region in the control-flow graph.
For now, we keep a mapping from each `CodeExtent` to its
corresponding SEME region for later reference (see caveat in next
paragraph). This is because region scopes are tied to
them. Eventually, when we shift to non-lexical lifetimes, three should
be no need to remember this mapping.
There is one additional wrinkle, actually, that I wanted to hide from
you but duty compels me to mention. In the course of translating
matches, it sometimes happen that certain code (namely guards) gets
executed multiple times. This means that the scope lexical scope may
in fact correspond to multiple, disjoint SEME regions. So in fact our
mapping is from one scope to a vector of SEME regions.
### Drops
The primary purpose for scopes is to insert drops: while translating
the contents, we also accumulate lvalues that need to be dropped upon
exit from each scope. This is done by calling `schedule_drop`. Once a
drop is scheduled, whenever we branch out we will insert drops of all
those lvalues onto the outgoing edge. Note that we don't know the full
set of scheduled drops up front, and so whenever we exit from the
scope we only drop the values scheduled thus far. For example, consider
the scope S corresponding to this loop:
```
loop {
let x = ...;
if cond { break; }
let y = ...;
}
```
When processing the `let x`, we will add one drop to the scope for
`x`. The break will then insert a drop for `x`. When we process `let
y`, we will add another drop (in fact, to a subscope, but let's ignore
that for now); any later drops would also drop `y`.
### Early exit
There are numerous "normal" ways to early exit a scope: `break`,
`continue`, `return` (panics are handled separately). Whenever an
early exit occurs, the method `exit_scope` is called. It is given the
current point in execution where the early exit occurs, as well as the
scope you want to branch to (note that all early exits from to some
other enclosing scope). `exit_scope` will record thid exit point and
also add all drops.
Panics are handled in a similar fashion, except that a panic always
returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call
`panic(p)` with the current point `p`. Or else you can call
`diverge_cleanup`, which will produce a block that you can branch to
which does the appropriate cleanup and then diverges. `panic(p)`
simply calls `diverge_cleanup()` and adds an edge from `p` to the
result.
### Loop scopes
In addition to the normal scope stack, we track a loop scope stack
that contains only loops. It tracks where a `break` and `continue`
should go to.
*/
use build::{BlockAnd, BlockAndExtension, Builder};
use rustc::middle::region::CodeExtent;
use rustc::middle::lang_items;
use rustc::middle::subst::Substs;
use rustc::middle::ty::{self, Ty};
use rustc::mir::repr::*;
use syntax::codemap::{Span, DUMMY_SP};
use syntax::parse::token::intern_and_get_ident;
pub struct Scope<'tcx> {
extent: CodeExtent,
drops: Vec<(DropKind, Span, Lvalue<'tcx>)>,
cached_block: Option<BasicBlock>,
}
#[derive(Clone, Debug)]
pub struct LoopScope {
pub extent: CodeExtent, // extent of the loop
pub continue_block: BasicBlock, // where to go on a `loop`
pub break_block: BasicBlock, // where to go on a `break
}
impl<'a,'tcx> Builder<'a,'tcx> {
/// Start a loop scope, which tracks where `continue` and `break`
/// should branch to. See module comment for more details.
pub fn in_loop_scope<F, R>(&mut self,
loop_block: BasicBlock,
break_block: BasicBlock,
f: F)
-> BlockAnd<R>
where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
{
let extent = self.extent_of_innermost_scope();
let loop_scope = LoopScope {
extent: extent.clone(),
continue_block: loop_block,
break_block: break_block,
};
self.loop_scopes.push(loop_scope);
let r = f(self);
assert!(self.loop_scopes.pop().unwrap().extent == extent);
r
}
/// Convenience wrapper that pushes a scope and then executes `f`
/// to build its contents, popping the scope afterwards.
pub fn in_scope<F, R>(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd<R>
where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
{
debug!("in_scope(extent={:?}, block={:?})", extent, block);
self.push_scope(extent, block);
let rv = unpack!(block = f(self));
self.pop_scope(extent, block);
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
block.and(rv)
}
/// Push a scope onto the stack. You can then build code in this
/// scope and call `pop_scope` afterwards. Note that these two
/// calls must be paired; using `in_scope` as a convenience
/// wrapper maybe preferable.
pub fn push_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
debug!("push_scope({:?}, {:?})", extent, block);
// push scope, execute `f`, then pop scope again
self.scopes.push(Scope {
extent: extent.clone(),
drops: vec![],
cached_block: None,
});
}
/// Pops a scope, which should have extent `extent`, adding any
/// drops onto the end of `block` that are needed. This must
/// match 1-to-1 with `push_scope`.
pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
debug!("pop_scope({:?}, {:?})", extent, block);
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.extent, extent);
// add in any drops needed on the fallthrough path (any other
// exiting paths, such as those that arise from `break`, will
// have drops already)
for (kind, span, lvalue) in scope.drops {
self.cfg.push_drop(block, span, kind, &lvalue);
}
}
/// Finds the loop scope for a given label. This is used for
/// resolving `break` and `continue`.
pub fn find_loop_scope(&mut self,
span: Span,
label: Option<CodeExtent>)
-> LoopScope {
let loop_scope =
match label {
None => {
// no label? return the innermost loop scope
self.loop_scopes.iter()
.rev()
.next()
}
Some(label) => {
// otherwise, find the loop-scope with the correct id
self.loop_scopes.iter()
.rev()
.filter(|loop_scope| loop_scope.extent == label)
.next()
}
};
match loop_scope {
Some(loop_scope) => loop_scope.clone(),
None => self.hir.span_bug(span, "no enclosing loop scope found?"),
}
}
/// Branch out of `block` to `target`, exiting all scopes up to
/// and including `extent`. This will insert whatever drops are
/// needed, as well as tracking this exit for the SEME region. See
/// module comment for details.
pub fn exit_scope(&mut self,
span: Span,
extent: CodeExtent,
block: BasicBlock,
target: BasicBlock) {
let popped_scopes =
match self.scopes.iter().rev().position(|scope| scope.extent == extent) {
Some(p) => p + 1,
None => self.hir.span_bug(span, &format!("extent {:?} does not enclose",
extent)),
};
for scope in self.scopes.iter_mut().rev().take(popped_scopes) {
for &(kind, drop_span, ref lvalue) in &scope.drops {
self.cfg.push_drop(block, drop_span, kind, lvalue);
}
}
self.cfg.terminate(block, Terminator::Goto { target: target });
}
/// Creates a path that performs all required cleanup for unwinding.
///
/// This path terminates in Resume. Returns the start of the path.
/// See module comment for more details. None indicates there’s no
/// cleanup to do at this point.
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
if self.scopes.is_empty() {
return None;
}
let mut terminator = Terminator::Resume;
// Given an array of scopes, we generate these from the outermost scope to the innermost
// one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
// generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always
// terminate with a Resume terminator.
for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) {
if let Some(b) = scope.cached_block {
terminator = Terminator::Goto { target: b };
continue;
} else {
let new_block = self.cfg.start_new_block();
self.cfg.block_data_mut(new_block).is_cleanup = true;
self.cfg.terminate(new_block, terminator);
terminator = Terminator::Goto { target: new_block };
for &(kind, span, ref lvalue) in scope.drops.iter().rev() {
self.cfg.push_drop(new_block, span, kind, lvalue);
}
scope.cached_block = Some(new_block);
}
}
// Return the innermost cached block, most likely the one we just generated.
// Note that if there are no cleanups in scope we return None.
self.scopes.iter().rev().flat_map(|b| b.cached_block).next()
}
/// Indicates that `lvalue` should be dropped on exit from
/// `extent`.
pub fn schedule_drop(&mut self,
span: Span,
extent: CodeExtent,
kind: DropKind,
lvalue: &Lvalue<'tcx>,
lvalue_ty: Ty<'tcx>) {
if self.hir.needs_drop(lvalue_ty) {
for scope in self.scopes.iter_mut().rev() {
// We must invalidate all the cached_blocks leading up to the scope we’re looking
// for, because otherwise some/most of the blocks in the chain might become
// incorrect (i.e. they still are pointing at old cached_block).
scope.cached_block = None;
if scope.extent == extent {
scope.drops.push((kind, span, lvalue.clone()));
return;
}
}
self.hir.span_bug(span,
&format!("extent {:?} not in scope to drop {:?}", extent, lvalue));
}
}
pub fn extent_of_innermost_scope(&self) -> CodeExtent {
self.scopes.last().map(|scope| scope.extent).unwrap()
}
pub fn extent_of_outermost_scope(&self) -> CodeExtent {
self.scopes.first().map(|scope| scope.extent).unwrap()
}
pub fn panic_bounds_check(&mut self,
block: BasicBlock,
index: Operand<'tcx>,
len: Operand<'tcx>,
span: Span) {
// fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
let args = func.ty.fn_args();
let ref_ty = args.skip_binder()[0];
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
(region, tyandmut.ty)
} else {
self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty));
};
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
let (file, line) = self.span_to_fileline_args(span);
let elems = vec![Operand::Constant(file), Operand::Constant(line)];
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
// icache with cold branch code), however to achieve that we either have to rely on rvalue
// promotion or have some way, in MIR, to create constants.
self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg);
Rvalue::Aggregate(AggregateKind::Tuple, elems));
// FIXME: is this region really correct here?
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(*region, BorrowKind::Unique, tuple));
let cleanup = self.diverge_cleanup();
self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref), index, len],
kind: match cleanup {
None => CallKind::Diverging,
Some(c) => CallKind::DivergingCleanup(c)
}
});
}
/// Create diverge cleanup and branch to it from `block`.
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
let func = self.lang_function(lang_items::PanicFnLangItem);
let args = func.ty.fn_args();
let ref_ty = args.skip_binder()[0];
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
(region, tyandmut.ty)
} else {
self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty));
};
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
let (file, line) = self.span_to_fileline_args(span);
let message = Constant {
span: DUMMY_SP,
ty: self.hir.tcx().mk_static_str(),
literal: self.hir.str_literal(intern_and_get_ident(msg))
};
let elems = vec![Operand::Constant(message),
Operand::Constant(file),
Operand::Constant(line)];
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
// icache with cold branch code), however to achieve that we either have to rely on rvalue
// promotion or have some way, in MIR, to create constants.
self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg);
Rvalue::Aggregate(AggregateKind::Tuple, elems));
// FIXME: is this region really correct here?
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(*region, BorrowKind::Unique, tuple));
let cleanup = self.diverge_cleanup();
self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref)],
kind: match cleanup {
None => CallKind::Diverging,
Some(c) => CallKind::DivergingCleanup(c)
}
});
}
fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
Ok(d) => d,
Err(m) => {
self.hir.tcx().sess.fatal(&*m)
}
};
Constant {
span: DUMMY_SP,
ty: self.hir.tcx().lookup_item_type(funcdid).ty,
literal: Literal::Item {
def_id: funcdid,
kind: ItemKind::Function,
substs: self.hir.tcx().mk_substs(Substs::empty())
}
}
}
fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
(Constant {
span: DUMMY_SP,
ty: self.hir.tcx().mk_static_str(),
literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
}, Constant {
span: DUMMY_SP,
ty: self.hir.tcx().types.u32,
literal: self.hir.usize_literal(span_lines.line)
})
}
}
|