Module rustc_mir::build::scope[][src]

🔬 This is a nightly-only experimental API. (rustc_private)

this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via Cargo.toml instead?

Managing the scope stack. The scopes are tied to lexical scopes, so as we descend the HAIR, we push a scope on the stack, build its contents, and then pop it off. Every scope is named by a region::Scope.

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 region::Scope 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, there 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 building 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 building the contents, we also accumulate places 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 places 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 this 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.

Structs

BreakableScope [
Experimental
]
CachedBlock [
Experimental
]
DropData [
Experimental
]
Scope [
Experimental
]

Enums

DropKind [
Experimental
]

Functions

build_diverge_scope [
Experimental
]
build_scope_drops [
Experimental
]

Builds drops for pop_scope and exit_scope.