Auto merge of #27893 - nikomatsakis:mir, r=nrc

This PR contains a new crate, `rustc_mir`, which implements the MIR as specified in the RFC (more or less). There are no targeted unit tests at the moment, as I didn't decide what kind of infrastructure would be best and didn't take the time to implement it. 

~~NB: In packaging up this PR, I realized that MIR construction code is not triggering for methods right now, I think it's only for fixed fns. I'll push a fix for this soon. Hopefully it doesn't stop any crates from building. :)~~ Fixed. Everything still seems to work.

However, the MIR construction code (`librustc_mir/build`) is intentionally quite distinct from the code which munges the compiler's data structures (`librustc_mir/tcx`). The interface between the two is the `HIR` trait (`librustc_mir/hir`). To avoid confusion with @nrc's work, perhaps a better name for this trait is warranted, although ultimately this trait *will* be connected to the HIR, I imagine, so in a way the name is perfect. Anyway, I'm open to suggestions. The initial motivation for this split was to allow for the MIR construction code to be unit-tested. But while I didn't end up writing unit tests (yet), I did find the split made the code immensely easier to think about, since the messiness of our existing system, with its myriad hashtables, punning, and so forth, is confined to one part, which simply transforms to a more fully explicit AST-like form. I tried to separate out the commits somewhat, but since this mostly new code, it mostly winds up coming in one fell swoop in the MIR commit.

Quick guide to the MIR crate:

- `repr.rs` defines the MIR itself; each MIR instance is parameterized by some HIR `H`
- `build/` is the MIR construction code, parameterized by a particular HIR
- `hir/` is the definition of the HIR interface
- `tcx/` is the impl of the HIR interface for the tcx
- `dump.rs` is the minimal compiler pass that invokes the HIR

One open question:

- In the HIR trait, I used exclusively struct-like variants. I found I like this more, since things have names. Should I convert the repr code?
This commit is contained in:
bors 2015-09-06 21:31:26 +00:00
commit 3dd1a48f3f
52 changed files with 6161 additions and 103 deletions

View file

@ -54,7 +54,7 @@ TARGET_CRATES := libc std flate arena term \
log graphviz core rbml alloc \
rustc_unicode rustc_bitflags \
alloc_system
RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \
RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
rustc_data_structures rustc_front rustc_platform_intrinsics
HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
@ -70,11 +70,12 @@ DEPS_std := core libc rand alloc collections rustc_unicode \
DEPS_graphviz := std
DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
rustc_typeck rustc_resolve log syntax serialize rustc_llvm \
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
rustc_trans rustc_privacy rustc_lint rustc_front
DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics
DEPS_rustc_mir := rustc rustc_front syntax
DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics
DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax
DEPS_rustc_resolve := rustc rustc_front log syntax

View file

@ -172,6 +172,18 @@ RUST_LIB_FLAGS_ST3 += -C prefer-dynamic
# by not emitting them.
RUSTFLAGS_STAGE0 += -Z no-landing-pads
# Enable MIR to "always build" for crates where this works. This is
# just temporary while MIR is being actively built up -- it's just a
# poor man's unit testing infrastructure. Anyway we only want this for
# stage1/stage2.
define ADD_MIR_FLAG
RUSTFLAGS1_$(1) += -Z always-build-mir
RUSTFLAGS2_$(1) += -Z always-build-mir
endef
$(foreach crate,$(TARGET_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
$(foreach crate,$(RUSTC_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
$(foreach crate,$(HOST_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
# platform-specific auto-configuration
include $(CFG_SRC_DIR)mk/platform.mk

View file

@ -93,6 +93,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
$$(RUSTFLAGS_$(4)) \
$$(RUSTFLAGS$(1)_$(4)) \
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
--out-dir $$(@D) \
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \

View file

@ -313,6 +313,13 @@ pub enum LabelText<'a> {
/// are also the escape sequences `\l` which left-justifies the
/// preceding line and `\r` which right-justifies it.
EscStr(Cow<'a, str>),
/// This uses a graphviz [HTML string label][html]. The string is
/// printed exactly as given, but between `<` and `>`. **No
/// escaping is performed.**
///
/// [html]: http://www.graphviz.org/content/node-shapes#html
HtmlStr(Cow<'a, str>),
}
/// The style for a node or edge.
@ -453,6 +460,14 @@ pub trait Labeller<'a,N,E> {
/// is a valid DOT identifier.
fn node_id(&'a self, n: &N) -> Id<'a>;
/// Maps `n` to one of the [graphviz `shape` names][1]. If `None`
/// is returned, no `shape` attribute is specified.
///
/// [1]: http://www.graphviz.org/content/node-shapes
fn node_shape(&'a self, _node: &N) -> Option<LabelText<'a>> {
None
}
/// Maps `n` to a label that will be used in the rendered output.
/// The label need not be unique, and may be the empty string; the
/// default is just the output from `node_id`.
@ -479,6 +494,16 @@ pub trait Labeller<'a,N,E> {
}
}
/// Escape tags in such a way that it is suitable for inclusion in a
/// Graphviz HTML label.
pub fn escape_html(s: &str) -> String {
s
.replace("&", "&amp;")
.replace("\"", "&quot;")
.replace("<", "&lt;")
.replace(">", "&gt;")
}
impl<'a> LabelText<'a> {
pub fn label<S:IntoCow<'a, str>>(s: S) -> LabelText<'a> {
LabelStr(s.into_cow())
@ -488,6 +513,10 @@ impl<'a> LabelText<'a> {
EscStr(s.into_cow())
}
pub fn html<S:IntoCow<'a, str>>(s: S) -> LabelText<'a> {
HtmlStr(s.into_cow())
}
fn escape_char<F>(c: char, mut f: F) where F: FnMut(char) {
match c {
// not escaping \\, since Graphviz escString needs to
@ -505,10 +534,12 @@ impl<'a> LabelText<'a> {
}
/// Renders text as string suitable for a label in a .dot file.
pub fn escape(&self) -> String {
/// This includes quotes or suitable delimeters.
pub fn to_dot_string(&self) -> String {
match self {
&LabelStr(ref s) => s.escape_default(),
&EscStr(ref s) => LabelText::escape_str(&s[..]),
&LabelStr(ref s) => format!("\"{}\"", s.escape_default()),
&EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s[..])),
&HtmlStr(ref s) => format!("<{}>", s),
}
}
@ -524,6 +555,7 @@ impl<'a> LabelText<'a> {
} else {
s
},
HtmlStr(s) => s,
}
}
@ -612,14 +644,15 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
try!(indent(w));
let id = g.node_id(n);
let escaped = &g.node_label(n).escape();
let escaped = &g.node_label(n).to_dot_string();
let shape;
let mut text = vec![id.as_slice()];
if !options.contains(&RenderOption::NoNodeLabels) {
text.push("[label=\"");
text.push("[label=");
text.push(escaped);
text.push("\"]");
text.push("]");
}
let style = g.node_style(n);
@ -629,12 +662,19 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
text.push("\"]");
}
if let Some(s) = g.node_shape(n) {
shape = s.to_dot_string();
text.push("[shape=");
text.push(&shape);
text.push("]");
}
text.push(";");
try!(writeln(w, &text));
}
for e in g.edges().iter() {
let escaped_label = &g.edge_label(e).escape();
let escaped_label = &g.edge_label(e).to_dot_string();
try!(indent(w));
let source = g.source(e);
let target = g.target(e);
@ -644,9 +684,9 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
let mut text = vec![source_id.as_slice(), " -> ", target_id.as_slice()];
if !options.contains(&RenderOption::NoEdgeLabels) {
text.push("[label=\"");
text.push("[label=");
text.push(escaped_label);
text.push("\"]");
text.push("]");
}
let style = g.edge_style(e);
@ -667,7 +707,7 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
mod tests {
use self::NodeLabels::*;
use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style};
use super::LabelText::{self, LabelStr, EscStr};
use super::LabelText::{self, LabelStr, EscStr, HtmlStr};
use std::io;
use std::io::prelude::*;
use std::borrow::IntoCow;
@ -805,12 +845,12 @@ mod tests {
fn node_id(&'a self, n: &Node) -> Id<'a> { self.graph.node_id(n) }
fn node_label(&'a self, n: &Node) -> LabelText<'a> {
match self.graph.node_label(n) {
LabelStr(s) | EscStr(s) => EscStr(s),
LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
}
}
fn edge_label(&'a self, e: & &'a Edge) -> LabelText<'a> {
match self.graph.edge_label(e) {
LabelStr(s) | EscStr(s) => EscStr(s),
LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
}
}
}

View file

@ -470,8 +470,8 @@ impl tr for def::Def {
def::DefPrimTy(p) => def::DefPrimTy(p),
def::DefTyParam(s, index, def_id, n) => def::DefTyParam(s, index, def_id.tr(dcx), n),
def::DefUse(did) => def::DefUse(did.tr(dcx)),
def::DefUpvar(nid1, nid2) => {
def::DefUpvar(dcx.tr_id(nid1), dcx.tr_id(nid2))
def::DefUpvar(nid1, index, nid2) => {
def::DefUpvar(dcx.tr_id(nid1), index, dcx.tr_id(nid2))
}
def::DefStruct(did) => def::DefStruct(did.tr(dcx)),
def::DefRegion(nid) => def::DefRegion(dcx.tr_id(nid)),

View file

@ -38,6 +38,7 @@ pub enum Def {
DefTyParam(ParamSpace, u32, DefId, ast::Name),
DefUse(DefId),
DefUpvar(ast::NodeId, // id of closed over local
usize, // index in the freevars list of the closure
ast::NodeId), // expr node that creates the closure
/// Note that if it's a tuple struct's definition, the node id of the DefId
@ -129,7 +130,7 @@ impl Def {
id
}
DefLocal(id) |
DefUpvar(id, _) |
DefUpvar(id, _, _) |
DefRegion(id) |
DefLabel(id) |
DefSelfTy(_, Some((_, id))) => {

View file

@ -246,7 +246,7 @@ impl OverloadedCallType {
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d+'t> {
typer: &'t infer::InferCtxt<'a, 'tcx>,
mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
delegate: &'d mut (Delegate<'tcx>+'d),
delegate: &'d mut Delegate<'tcx>,
}
// If the TYPER results in an error, it's because the type check

View file

@ -578,7 +578,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
}))
}
def::DefUpvar(var_id, fn_node_id) => {
def::DefUpvar(var_id, _, fn_node_id) => {
let ty = try!(self.node_ty(fn_node_id));
match ty.sty {
ty::TyClosure(closure_id, _) => {

View file

@ -329,6 +329,9 @@ impl RegionMaps {
pub fn item_extent(&self, n: ast::NodeId) -> CodeExtent {
self.lookup_code_extent(CodeExtentData::DestructionScope(n))
}
pub fn opt_destruction_extent(&self, n: ast::NodeId) -> Option<CodeExtent> {
self.code_extent_interner.borrow().get(&CodeExtentData::DestructionScope(n)).cloned()
}
pub fn intern_code_extent(&self,
e: CodeExtentData,
parent: CodeExtent) -> CodeExtent {

View file

@ -3475,6 +3475,13 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
.expect("variant_with_id: unknown variant")
}
pub fn variant_index_with_id(&self, vid: DefId) -> usize {
self.variants
.iter()
.position(|v| v.did == vid)
.expect("variant_index_with_id: unknown variant")
}
pub fn variant_of_def(&self, def: def::Def) -> &VariantDefData<'tcx, 'container> {
match def {
def::DefVariant(_, vid, _) => self.variant_with_id(vid),
@ -5191,28 +5198,12 @@ impl<'tcx> TyS<'tcx> {
if !adjusted_ty.references_error() {
for i in 0..adj.autoderefs {
let method_call = MethodCall::autoderef(expr_id, i as u32);
match method_type(method_call) {
Some(method_ty) => {
// Overloaded deref operators have all late-bound
// regions fully instantiated and coverge.
let fn_ret =
cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
adjusted_ty = fn_ret.unwrap();
}
None => {}
}
match adjusted_ty.builtin_deref(true, NoPreference) {
Some(mt) => { adjusted_ty = mt.ty; }
None => {
cx.sess.span_bug(
span,
&format!("the {}th autoderef failed: {}",
i,
adjusted_ty)
);
}
}
adjusted_ty =
adjusted_ty.adjust_for_autoderef(cx,
expr_id,
span,
i as u32,
&mut method_type);
}
}
@ -5228,6 +5219,36 @@ impl<'tcx> TyS<'tcx> {
};
}
pub fn adjust_for_autoderef<F>(&'tcx self,
cx: &ctxt<'tcx>,
expr_id: ast::NodeId,
expr_span: Span,
autoderef: u32, // how many autoderefs so far?
mut method_type: F)
-> Ty<'tcx> where
F: FnMut(MethodCall) -> Option<Ty<'tcx>>,
{
let method_call = MethodCall::autoderef(expr_id, autoderef);
let mut adjusted_ty = self;
if let Some(method_ty) = method_type(method_call) {
// Method calls always have all late-bound regions
// fully instantiated.
let fn_ret = cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
adjusted_ty = fn_ret.unwrap();
}
match adjusted_ty.builtin_deref(true, NoPreference) {
Some(mt) => mt.ty,
None => {
cx.sess.span_bug(
expr_span,
&format!("the {}th autoderef failed: {}",
autoderef,
adjusted_ty)
);
}
}
}
pub fn adjust_for_autoref(&'tcx self, cx: &ctxt<'tcx>,
autoref: Option<AutoRef<'tcx>>)
-> Ty<'tcx> {

View file

@ -104,6 +104,7 @@ pub struct Options {
pub parse_only: bool,
pub no_trans: bool,
pub treat_err_as_bug: bool,
pub always_build_mir: bool,
pub no_analysis: bool,
pub debugging_opts: DebuggingOptions,
/// Whether to write dependency files. It's (enabled, optional filename).
@ -216,6 +217,7 @@ pub fn basic_options() -> Options {
parse_only: false,
no_trans: false,
treat_err_as_bug: false,
always_build_mir: false,
no_analysis: false,
debugging_opts: basic_debugging_options(),
write_dependency_info: (false, None),
@ -583,6 +585,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"Run all passes except translation; no output"),
treat_err_as_bug: bool = (false, parse_bool,
"Treat all errors that occur as bugs"),
always_build_mir: bool = (false, parse_bool,
"Always build MIR for all fns, even without a #[rustc_mir] annotation"),
no_analysis: bool = (false, parse_bool,
"Parse and expand the source, but run no analysis"),
extra_plugins: Vec<String> = (Vec::new(), parse_list,
@ -894,6 +898,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let parse_only = debugging_opts.parse_only;
let no_trans = debugging_opts.no_trans;
let treat_err_as_bug = debugging_opts.treat_err_as_bug;
let always_build_mir = debugging_opts.always_build_mir;
let no_analysis = debugging_opts.no_analysis;
if debugging_opts.debug_llvm {
@ -1049,6 +1054,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
parse_only: parse_only,
no_trans: no_trans,
treat_err_as_bug: treat_err_as_bug,
always_build_mir: always_build_mir,
no_analysis: no_analysis,
debugging_opts: debugging_opts,
write_dependency_info: write_dependency_info,

View file

@ -13,14 +13,10 @@
#![allow(non_snake_case)]
use middle::def_id::DefId;
use std::collections::hash_state::DefaultState;
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::hash::{Hasher, Hash};
use syntax::ast;
pub type FnvHashMap<K, V> = HashMap<K, V, DefaultState<FnvHasher>>;
pub type FnvHashSet<V> = HashSet<V, DefaultState<FnvHasher>>;
pub use rustc_data_structures::fnv::FnvHashMap;
pub use rustc_data_structures::fnv::FnvHashSet;
pub type NodeMap<T> = FnvHashMap<ast::NodeId, T>;
pub type DefIdMap<T> = FnvHashMap<DefId, T>;
@ -28,39 +24,8 @@ pub type DefIdMap<T> = FnvHashMap<DefId, T>;
pub type NodeSet = FnvHashSet<ast::NodeId>;
pub type DefIdSet = FnvHashSet<DefId>;
pub fn FnvHashMap<K: Hash + Eq, V>() -> FnvHashMap<K, V> {
Default::default()
}
pub fn FnvHashSet<V: Hash + Eq>() -> FnvHashSet<V> {
Default::default()
}
pub fn NodeMap<T>() -> NodeMap<T> { FnvHashMap() }
pub fn DefIdMap<T>() -> DefIdMap<T> { FnvHashMap() }
pub fn NodeSet() -> NodeSet { FnvHashSet() }
pub fn DefIdSet() -> DefIdSet { FnvHashSet() }
/// A speedy hash algorithm for node ids and def ids. The hashmap in
/// libcollections by default uses SipHash which isn't quite as speedy as we
/// want. In the compiler we're not really worried about DOS attempts, so we
/// just default to a non-cryptographic hash.
///
/// This uses FNV hashing, as described here:
/// http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
pub struct FnvHasher(u64);
impl Default for FnvHasher {
fn default() -> FnvHasher { FnvHasher(0xcbf29ce484222325) }
}
impl Hasher for FnvHasher {
fn write(&mut self, bytes: &[u8]) {
let FnvHasher(mut hash) = *self;
for byte in bytes {
hash = hash ^ (*byte as u64);
hash = hash.wrapping_mul(0x100000001b3);
}
*self = FnvHasher(hash);
}
fn finish(&self) -> u64 { self.0 }
}

View file

@ -0,0 +1,52 @@
// 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.
use std::collections::{HashMap, HashSet};
use std::collections::hash_state::DefaultState;
use std::default::Default;
use std::hash::{Hasher, Hash};
pub type FnvHashMap<K, V> = HashMap<K, V, DefaultState<FnvHasher>>;
pub type FnvHashSet<V> = HashSet<V, DefaultState<FnvHasher>>;
#[allow(non_snake_case)]
pub fn FnvHashMap<K: Hash + Eq, V>() -> FnvHashMap<K, V> {
Default::default()
}
#[allow(non_snake_case)]
pub fn FnvHashSet<V: Hash + Eq>() -> FnvHashSet<V> {
Default::default()
}
/// A speedy hash algorithm for node ids and def ids. The hashmap in
/// libcollections by default uses SipHash which isn't quite as speedy as we
/// want. In the compiler we're not really worried about DOS attempts, so we
/// just default to a non-cryptographic hash.
///
/// This uses FNV hashing, as described here:
/// http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
pub struct FnvHasher(u64);
impl Default for FnvHasher {
fn default() -> FnvHasher { FnvHasher(0xcbf29ce484222325) }
}
impl Hasher for FnvHasher {
fn write(&mut self, bytes: &[u8]) {
let FnvHasher(mut hash) = *self;
for byte in bytes {
hash = hash ^ (*byte as u64);
hash = hash.wrapping_mul(0x100000001b3);
}
*self = FnvHasher(hash);
}
fn finish(&self) -> u64 { self.0 }
}

View file

@ -119,11 +119,21 @@ impl<N:Debug,E:Debug> Graph<N,E> {
&self.nodes
}
#[inline]
pub fn len_nodes(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn all_edges<'a>(&'a self) -> &'a [Edge<E>] {
&self.edges
}
#[inline]
pub fn len_edges(&self) -> usize {
self.edges.len()
}
///////////////////////////////////////////////////////////////////////////
// Node construction

View file

@ -28,6 +28,8 @@
html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(rustc_private, staged_api)]
#![feature(hashmap_hasher)]
#![cfg_attr(test, feature(test))]
#[macro_use] extern crate log;
@ -39,6 +41,7 @@ pub mod ivar;
pub mod snapshot_vec;
pub mod transitive_relation;
pub mod unify;
pub mod fnv;
// See comments in src/librustc/lib.rs
#[doc(hidden)]

View file

@ -10,6 +10,7 @@
use rustc::front;
use rustc::front::map as hir_map;
use rustc_mir as mir;
use rustc::session::Session;
use rustc::session::config::{self, Input, OutputFilenames};
use rustc::session::search_paths::PathKind;
@ -706,6 +707,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
// passes are timed inside typeck
typeck::check_crate(tcx, trait_map);
time(time_passes, "MIR dump", ||
mir::dump::dump_crate(tcx));
time(time_passes, "const checking", ||
middle::check_const::check_crate(tcx));

View file

@ -45,6 +45,7 @@ extern crate rustc_borrowck;
extern crate rustc_front;
extern crate rustc_lint;
extern crate rustc_privacy;
extern crate rustc_mir;
extern crate rustc_resolve;
extern crate rustc_trans;
extern crate rustc_typeck;

View file

@ -0,0 +1,28 @@
// 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.
use hair::*;
use repr::*;
use build::{BlockAnd, Builder};
impl<H:Hair> Builder<H> {
pub fn ast_block(&mut self,
destination: &Lvalue<H>,
mut block: BasicBlock,
ast_block: H::Block)
-> BlockAnd<()> {
let this = self;
let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
this.in_scope(extent, block, |this| {
unpack!(block = this.stmts(block, stmts));
this.into(destination, block, expr)
})
}
}

View file

@ -0,0 +1,88 @@
// 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.
//! Routines for manipulating the control-flow graph.
use build::CFG;
use hair::*;
use repr::*;
impl<H:Hair> CFG<H> {
pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<H> {
&self.basic_blocks[blk.index()]
}
pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<H> {
&mut self.basic_blocks[blk.index()]
}
pub fn end_point(&self, block: BasicBlock) -> ExecutionPoint {
ExecutionPoint {
block: block,
statement: self.block_data(block).statements.len() as u32
}
}
pub fn start_new_block(&mut self) -> BasicBlock {
let node_index = self.basic_blocks.len();
self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge));
BasicBlock::new(node_index)
}
pub fn push(&mut self, block: BasicBlock, statement: Statement<H>) {
debug!("push({:?}, {:?})", block, statement);
self.block_data_mut(block).statements.push(statement);
}
pub fn push_assign_constant(&mut self,
block: BasicBlock,
span: H::Span,
temp: &Lvalue<H>,
constant: Constant<H>) {
self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
}
pub fn push_drop(&mut self, block: BasicBlock, span: H::Span,
kind: DropKind, lvalue: &Lvalue<H>) {
self.push(block, Statement {
span: span,
kind: StatementKind::Drop(kind, lvalue.clone())
});
}
pub fn push_assign(&mut self,
block: BasicBlock,
span: H::Span,
lvalue: &Lvalue<H>,
rvalue: Rvalue<H>) {
self.push(block, Statement {
span: span,
kind: StatementKind::Assign(lvalue.clone(), rvalue)
});
}
pub fn terminate(&mut self,
block: BasicBlock,
terminator: Terminator<H>) {
// Check whether this block has already been terminated. For
// this, we rely on the fact that the initial state is to have
// a Diverge terminator and an empty list of targets (which
// is not a valid state).
debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true,
_ => false },
"terminate: block {:?} already has a terminator set", block);
self.block_data_mut(block).terminator = terminator;
}
}

View file

@ -0,0 +1,123 @@
// 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.
//! See docs in build/expr/mod.rs
use rustc_data_structures::fnv::FnvHashMap;
use build::{Builder};
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr`, yielding a compile-time constant. Assumes that
/// `expr` is a valid compile-time constant!
pub fn as_constant<M>(&mut self, expr: M) -> Constant<H>
where M: Mirror<H, Output=Expr<H>>
{
let expr = self.hir.mirror(expr);
self.expr_as_constant(expr)
}
fn expr_as_constant(&mut self, expr: Expr<H>) -> Constant<H> {
let this = self;
let Expr { ty: _, temp_lifetime: _, span, kind } = expr;
let kind = match kind {
ExprKind::Scope { extent: _, value } => {
return this.as_constant(value);
}
ExprKind::Paren { arg } => {
return this.as_constant(arg);
}
ExprKind::Literal { literal } => {
ConstantKind::Literal(literal)
}
ExprKind::Vec { fields } => {
let fields = this.as_constants(fields);
ConstantKind::Aggregate(AggregateKind::Vec, fields)
}
ExprKind::Tuple { fields } => {
let fields = this.as_constants(fields);
ConstantKind::Aggregate(AggregateKind::Tuple, fields)
}
ExprKind::Adt { adt_def, variant_index, substs, fields, base: None } => {
let field_names = this.hir.fields(adt_def, variant_index);
let fields = this.named_field_constants(field_names, fields);
ConstantKind::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), fields)
}
ExprKind::Repeat { value, count } => {
let value = Box::new(this.as_constant(value));
let count = Box::new(this.as_constant(count));
ConstantKind::Repeat(value, count)
}
ExprKind::Binary { op, lhs, rhs } => {
let lhs = Box::new(this.as_constant(lhs));
let rhs = Box::new(this.as_constant(rhs));
ConstantKind::BinaryOp(op, lhs, rhs)
}
ExprKind::Unary { op, arg } => {
let arg = Box::new(this.as_constant(arg));
ConstantKind::UnaryOp(op, arg)
}
ExprKind::Field { lhs, name } => {
let lhs = this.as_constant(lhs);
ConstantKind::Projection(
Box::new(ConstantProjection {
base: lhs,
elem: ProjectionElem::Field(name),
}))
}
ExprKind::Deref { arg } => {
let arg = this.as_constant(arg);
ConstantKind::Projection(
Box::new(ConstantProjection {
base: arg,
elem: ProjectionElem::Deref,
}))
}
ExprKind::Call { fun, args } => {
let fun = this.as_constant(fun);
let args = this.as_constants(args);
ConstantKind::Call(Box::new(fun), args)
}
_ => {
this.hir.span_bug(
span,
&format!("expression is not a valid constant {:?}", kind));
}
};
Constant { span: span, kind: kind }
}
fn as_constants(&mut self,
exprs: Vec<ExprRef<H>>)
-> Vec<Constant<H>>
{
exprs.into_iter().map(|expr| self.as_constant(expr)).collect()
}
fn named_field_constants(&mut self,
field_names: Vec<Field<H>>,
field_exprs: Vec<FieldExprRef<H>>)
-> Vec<Constant<H>>
{
let fields_map: FnvHashMap<_, _> =
field_exprs.into_iter()
.map(|f| (f.name, self.as_constant(f.expr)))
.collect();
let fields: Vec<_> =
field_names.into_iter()
.map(|n| fields_map[&n].clone())
.collect();
fields
}
}

View file

@ -0,0 +1,131 @@
// 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.
//! See docs in build/expr/mod.rs
use build::{BlockAnd, Builder};
use build::expr::category::Category;
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr`, yielding an lvalue that we can move from etc.
pub fn as_lvalue<M>(&mut self,
block: BasicBlock,
expr: M)
-> BlockAnd<Lvalue<H>>
where M: Mirror<H, Output=Expr<H>>
{
let expr = self.hir.mirror(expr);
self.expr_as_lvalue(block, expr)
}
fn expr_as_lvalue(&mut self,
mut block: BasicBlock,
expr: Expr<H>)
-> BlockAnd<Lvalue<H>>
{
debug!("expr_as_lvalue(block={:?}, expr={:?})",
block, expr);
let this = self;
let expr_span = expr.span;
match expr.kind {
ExprKind::Scope { extent, value } => {
this.in_scope(extent, block, |this| {
this.as_lvalue(block, value)
})
}
ExprKind::Paren { arg } => {
this.as_lvalue(block, arg)
}
ExprKind::Field { lhs, name } => {
let lvalue = unpack!(block = this.as_lvalue(block, lhs));
let lvalue = lvalue.field(name);
block.and(lvalue)
}
ExprKind::Deref { arg } => {
let lvalue = unpack!(block = this.as_lvalue(block, arg));
let lvalue = lvalue.deref();
block.and(lvalue)
}
ExprKind::Index { lhs, index } => {
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
let slice = unpack!(block = this.as_lvalue(block, lhs));
let idx = unpack!(block = this.as_operand(block, index));
// bounds check:
let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
this.cfg.push_assign(block, expr_span, // len = len(slice)
&len, Rvalue::Len(slice.clone()));
this.cfg.push_assign(block, expr_span, // lt = idx < len
&lt, Rvalue::BinaryOp(BinOp::Lt,
idx.clone(),
Operand::Consume(len)));
let (success, failure) = (this.cfg.start_new_block(),
this.cfg.start_new_block());
this.cfg.terminate(block,
Terminator::If {
cond: Operand::Consume(lt),
targets: [success, failure]
});
this.panic(failure);
success.and(slice.index(idx))
}
ExprKind::SelfRef => {
block.and(Lvalue::Arg(0))
}
ExprKind::VarRef { id } => {
let index = this.var_indices[&id];
block.and(Lvalue::Var(index))
}
ExprKind::StaticRef { id } => {
block.and(Lvalue::Static(id))
}
ExprKind::Vec { .. } |
ExprKind::Tuple { .. } |
ExprKind::Adt { .. } |
ExprKind::Closure { .. } |
ExprKind::Unary { .. } |
ExprKind::Binary { .. } |
ExprKind::LogicalOp { .. } |
ExprKind::Box { .. } |
ExprKind::Cast { .. } |
ExprKind::ReifyFnPointer { .. } |
ExprKind::UnsafeFnPointer { .. } |
ExprKind::Unsize { .. } |
ExprKind::Repeat { .. } |
ExprKind::Borrow { .. } |
ExprKind::If { .. } |
ExprKind::Match { .. } |
ExprKind::Loop { .. } |
ExprKind::Block { .. } |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Break { .. } |
ExprKind::Continue { .. } |
ExprKind::Return { .. } |
ExprKind::Literal { .. } |
ExprKind::InlineAsm { .. } |
ExprKind::Call { .. } => {
// these are not lvalues, so we need to make a temporary.
debug_assert!(match Category::of(&expr.kind) {
Some(Category::Lvalue) => false,
_ => true,
});
this.as_temp(block, expr)
}
}
}
}

View file

@ -0,0 +1,68 @@
// 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.
//! See docs in build/expr/mod.rs
use build::{BlockAnd, Builder};
use build::expr::category::Category;
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr` into a value that can be used as an operand.
/// If `expr` is an lvalue like `x`, this will introduce a
/// temporary `tmp = x`, so that we capture the value of `x` at
/// this time.
pub fn as_operand<M>(&mut self,
block: BasicBlock,
expr: M)
-> BlockAnd<Operand<H>>
where M: Mirror<H, Output=Expr<H>>
{
let expr = self.hir.mirror(expr);
self.expr_as_operand(block, expr)
}
fn expr_as_operand(&mut self,
mut block: BasicBlock,
expr: Expr<H>)
-> BlockAnd<Operand<H>>
{
debug!("expr_as_operand(block={:?}, expr={:?})",
block, expr);
let this = self;
match expr.kind {
ExprKind::Scope { extent, value } => {
return this.in_scope(extent, block, |this| {
this.as_operand(block, value)
});
}
ExprKind::Paren { arg } => {
return this.as_operand(block, arg);
}
_ => { }
}
let category = Category::of(&expr.kind).unwrap();
debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind);
match category {
Category::Constant => {
let constant = this.as_constant(expr);
block.and(Operand::Constant(constant))
}
Category::Lvalue |
Category::Rvalue(..) => {
let operand = unpack!(block = this.as_temp(block, expr));
block.and(Operand::Consume(operand))
}
}
}
}

View file

@ -0,0 +1,220 @@
// 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.
//! See docs in build/expr/mod.rs
use rustc_data_structures::fnv::FnvHashMap;
use build::{BlockAnd, Builder};
use build::expr::category::{Category, RvalueFunc};
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr`, yielding an rvalue.
pub fn as_rvalue<M>(&mut self,
block: BasicBlock,
expr: M)
-> BlockAnd<Rvalue<H>>
where M: Mirror<H, Output=Expr<H>>
{
let expr = self.hir.mirror(expr);
self.expr_as_rvalue(block, expr)
}
fn expr_as_rvalue(&mut self,
mut block: BasicBlock,
expr: Expr<H>)
-> BlockAnd<Rvalue<H>>
{
debug!("expr_as_rvalue(block={:?}, expr={:?})",
block, expr);
let this = self;
let expr_span = expr.span;
match expr.kind {
ExprKind::Scope { extent, value } => {
this.in_scope(extent, block, |this| {
this.as_rvalue(block, value)
})
}
ExprKind::Paren { arg } => {
this.as_rvalue(block, arg)
}
ExprKind::InlineAsm { asm } => {
block.and(Rvalue::InlineAsm(asm))
}
ExprKind::Repeat { value, count } => {
let value_operand = unpack!(block = this.as_operand(block, value));
let count_operand = unpack!(block = this.as_operand(block, count));
block.and(Rvalue::Repeat(value_operand, count_operand))
}
ExprKind::Borrow { region, borrow_kind, arg } => {
let arg_lvalue = unpack!(block = this.as_lvalue(block, arg));
block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue))
}
ExprKind::Binary { op, lhs, rhs } => {
let lhs = unpack!(block = this.as_operand(block, lhs));
let rhs = unpack!(block = this.as_operand(block, rhs));
block.and(Rvalue::BinaryOp(op, lhs, rhs))
}
ExprKind::Unary { op, arg } => {
let arg = unpack!(block = this.as_operand(block, arg));
block.and(Rvalue::UnaryOp(op, arg))
}
ExprKind::Box { place: _, value } => {
let value = this.hir.mirror(value);
let value_ty = value.ty.clone();
let result = this.temp(value_ty.clone());
// to start, malloc some memory of suitable type (thus far, uninitialized):
let rvalue = Rvalue::Box(value.ty.clone());
this.cfg.push_assign(block, expr_span, &result, rvalue);
// schedule a shallow free of that memory, lest we unwind:
let extent = this.extent_of_innermost_scope().unwrap();
this.schedule_drop(expr_span, extent, DropKind::Shallow, &result, value_ty);
// initialize the box contents:
let contents = result.clone().deref();
unpack!(block = this.into(&contents, block, value));
// now that the result is fully initialized, cancel the drop
// by "using" the result (which is linear):
block.and(Rvalue::Use(Operand::Consume(result)))
}
ExprKind::Cast { source } => {
let source = unpack!(block = this.as_operand(block, source));
block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
}
ExprKind::ReifyFnPointer { source } => {
let source = unpack!(block = this.as_operand(block, source));
block.and(Rvalue::Cast(CastKind::ReifyFnPointer, source, expr.ty))
}
ExprKind::UnsafeFnPointer { source } => {
let source = unpack!(block = this.as_operand(block, source));
block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
}
ExprKind::Unsize { source } => {
let source = unpack!(block = this.as_operand(block, source));
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
}
ExprKind::Vec { fields } => {
// (*) We would (maybe) be closer to trans if we
// handled this and other aggregate cases via
// `into()`, not `as_rvalue` -- in that case, instead
// of generating
//
// let tmp1 = ...1;
// let tmp2 = ...2;
// dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
//
// we could just generate
//
// dest.f = ...1;
// dest.g = ...2;
//
// The problem is that then we would need to:
//
// (a) have a more complex mechanism for handling
// partial cleanup;
// (b) distinguish the case where the type `Foo` has a
// destructor, in which case creating an instance
// as a whole "arms" the destructor, and you can't
// write individual fields; and,
// (c) handle the case where the type Foo has no
// fields. We don't want `let x: ();` to compile
// to the same MIR as `let x = ();`.
// first process the set of fields
let fields: Vec<_> =
fields.into_iter()
.map(|f| unpack!(block = this.as_operand(block, f)))
.collect();
block.and(Rvalue::Aggregate(AggregateKind::Vec, fields))
}
ExprKind::Tuple { fields } => { // see (*) above
// first process the set of fields
let fields: Vec<_> =
fields.into_iter()
.map(|f| unpack!(block = this.as_operand(block, f)))
.collect();
block.and(Rvalue::Aggregate(AggregateKind::Tuple, fields))
}
ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above
let upvars =
upvars.into_iter()
.map(|upvar| unpack!(block = this.as_operand(block, upvar)))
.collect();
block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
}
ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above
// first process the set of fields
let fields_map: FnvHashMap<_, _> =
fields.into_iter()
.map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
.collect();
let field_names =
this.hir.fields(adt_def, variant_index);
let base =
base.map(|base| unpack!(block = this.as_lvalue(block, base)));
// for the actual values we use, take either the
// expr the user specified or, if they didn't
// specify something for this field name, create a
// path relative to the base (which must have been
// supplied, or the IR is internally
// inconsistent).
let fields: Vec<_> =
field_names.into_iter()
.map(|n| match fields_map.get(&n) {
Some(v) => v.clone(),
None => Operand::Consume(base.clone().unwrap().field(n)),
})
.collect();
block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
fields))
}
ExprKind::Literal { .. } |
ExprKind::Block { .. } |
ExprKind::Match { .. } |
ExprKind::If { .. } |
ExprKind::Loop { .. } |
ExprKind::LogicalOp { .. } |
ExprKind::Call { .. } |
ExprKind::Field { .. } |
ExprKind::Deref { .. } |
ExprKind::Index { .. } |
ExprKind::VarRef { .. } |
ExprKind::SelfRef |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Break { .. } |
ExprKind::Continue { .. } |
ExprKind::Return { .. } |
ExprKind::StaticRef { .. } => {
// these do not have corresponding `Rvalue` variants,
// so make an operand and then return that
debug_assert!(match Category::of(&expr.kind) {
Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
_ => true,
});
let operand = unpack!(block = this.as_operand(block, expr));
block.and(Rvalue::Use(operand))
}
}
}
}

View file

@ -0,0 +1,85 @@
// 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.
//! See docs in build/expr/mod.rs
use build::{BlockAnd, Builder};
use build::expr::category::Category;
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr` into a fresh temporary. This is used when building
/// up rvalues so as to freeze the value that will be consumed.
pub fn as_temp<M>(&mut self,
block: BasicBlock,
expr: M)
-> BlockAnd<Lvalue<H>>
where M: Mirror<H, Output=Expr<H>>
{
let expr = self.hir.mirror(expr);
self.expr_as_temp(block, expr)
}
fn expr_as_temp(&mut self,
mut block: BasicBlock,
expr: Expr<H>)
-> BlockAnd<Lvalue<H>>
{
debug!("expr_as_temp(block={:?}, expr={:?})",
block, expr);
let this = self;
match expr.kind {
ExprKind::Scope { extent, value } => {
return this.in_scope(extent, block, |this| {
this.as_temp(block, value)
});
}
ExprKind::Paren { arg } => {
return this.as_temp(block, arg);
}
_ => { }
}
let expr_ty = expr.ty.clone();
let temp = this.temp(expr_ty.clone());
let temp_lifetime = match expr.temp_lifetime {
Some(t) => t,
None => {
this.hir.span_bug(
expr.span,
&format!("no temp_lifetime for expr"));
}
};
this.schedule_drop(expr.span, temp_lifetime, DropKind::Deep, &temp, expr_ty);
// Careful here not to cause an infinite cycle. If we always
// called `into`, then for lvalues like `x.f`, it would
// eventually fallback to us, and we'd loop. There's a reason
// for this: `as_temp` is the point where we bridge the "by
// reference" semantics of `as_lvalue` with the "by value"
// semantics of `into`, `as_operand`, `as_rvalue`, and (of
// course) `as_temp`.
match Category::of(&expr.kind).unwrap() {
Category::Lvalue => {
let expr_span = expr.span;
let lvalue = unpack!(block = this.as_lvalue(block, expr));
let rvalue = Rvalue::Use(Operand::Consume(lvalue));
this.cfg.push_assign(block, expr_span, &temp, rvalue);
}
_ => {
unpack!(block = this.into(&temp, block, expr));
}
}
block.and(temp)
}
}

View file

@ -0,0 +1,95 @@
// 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.
use hair::*;
#[derive(Debug, PartialEq)]
pub enum Category {
// An assignable memory location like `x`, `x.f`, `foo()[3]`, that
// sort of thing. Something that could appear on the LHS of an `=`
// sign.
Lvalue,
// A literal like `23` or `"foo"`. Does not include constant
// expressions like `3 + 5`.
Constant,
// Something that generates a new value at runtime, like `x + y`
// or `foo()`.
Rvalue(RvalueFunc),
}
// Rvalues fall into different "styles" that will determine which fn
// is best suited to generate them.
#[derive(Debug, PartialEq)]
pub enum RvalueFunc {
// Best generated by `into`. This is generally exprs that
// cause branching, like `match`, but also includes calls.
Into,
// Best generated by `as_rvalue`. This is usually the case.
AsRvalue,
}
/// Determines the category for a given expression. Note that scope
/// and paren expressions have no category.
impl Category {
pub fn of<H:Hair>(ek: &ExprKind<H>) -> Option<Category> {
match *ek {
ExprKind::Scope { .. } |
ExprKind::Paren { .. } =>
None,
ExprKind::Field { .. } |
ExprKind::Deref { .. } |
ExprKind::Index { .. } |
ExprKind::SelfRef |
ExprKind::VarRef { .. } |
ExprKind::StaticRef { .. } =>
Some(Category::Lvalue),
ExprKind::LogicalOp { .. } |
ExprKind::If { .. } |
ExprKind::Match { .. } |
ExprKind::Call { .. } =>
Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Vec { .. } |
ExprKind::Tuple { .. } |
ExprKind::Adt { .. } |
ExprKind::Closure { .. } |
ExprKind::Unary { .. } |
ExprKind::Binary { .. } |
ExprKind::Box { .. } |
ExprKind::Cast { .. } |
ExprKind::ReifyFnPointer { .. } |
ExprKind::UnsafeFnPointer { .. } |
ExprKind::Unsize { .. } |
ExprKind::Repeat { .. } |
ExprKind::Borrow { .. } |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::InlineAsm { .. } =>
Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::Literal { .. } =>
Some(Category::Constant),
ExprKind::Loop { .. } |
ExprKind::Block { .. } |
ExprKind::Break { .. } |
ExprKind::Continue { .. } |
ExprKind::Return { .. } =>
// FIXME(#27840) these probably want their own
// category, like "nonterminating"
Some(Category::Rvalue(RvalueFunc::Into)),
}
}
}

View file

@ -0,0 +1,282 @@
// 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.
//! See docs in build/expr/mod.rs
use build::{BlockAnd, Builder};
use build::expr::category::{Category, RvalueFunc};
use build::scope::LoopScope;
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized.
pub fn into_expr(&mut self,
destination: &Lvalue<H>,
mut block: BasicBlock,
expr: Expr<H>)
-> BlockAnd<()>
{
debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
destination, block, expr);
// since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to
// just use the name `this` uniformly
let this = self;
let expr_span = expr.span;
match expr.kind {
ExprKind::Scope { extent, value } => {
this.in_scope(extent, block, |this| {
this.into(destination, block, value)
})
}
ExprKind::Paren { arg } => {
this.into(destination, block, arg)
}
ExprKind::Block { body: ast_block } => {
this.ast_block(destination, block, ast_block)
}
ExprKind::Match { discriminant, arms } => {
this.match_expr(destination, expr_span, block, discriminant, arms)
}
ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
let operand = unpack!(block = this.as_operand(block, cond_expr));
let mut then_block = this.cfg.start_new_block();
let mut else_block = this.cfg.start_new_block();
this.cfg.terminate(block, Terminator::If {
cond: operand,
targets: [then_block, else_block]
});
unpack!(then_block = this.into(destination, then_block, then_expr));
unpack!(else_block = this.into(destination, else_block, else_expr));
let join_block = this.cfg.start_new_block();
this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
this.cfg.terminate(else_block, Terminator::Goto { target: join_block });
join_block.unit()
}
ExprKind::LogicalOp { op, lhs, rhs } => {
// And:
//
// [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
// | | (false)
// +----------false-----------+------------------> [false_block]
//
// Or:
//
// [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
// | | (false)
// +----------true------------+-------------------> [false_block]
let (true_block, false_block, mut else_block, join_block) =
(this.cfg.start_new_block(), this.cfg.start_new_block(),
this.cfg.start_new_block(), this.cfg.start_new_block());
let lhs = unpack!(block = this.as_operand(block, lhs));
let blocks = match op {
LogicalOp::And => [else_block, false_block],
LogicalOp::Or => [true_block, else_block],
};
this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
this.cfg.terminate(else_block, Terminator::If {
cond: rhs,
targets: [true_block, false_block]
});
this.cfg.push_assign_constant(
true_block, expr_span, destination,
Constant {
span: expr_span,
kind: ConstantKind::Literal(Literal::Bool { value: true }),
});
this.cfg.push_assign_constant(
false_block, expr_span, destination,
Constant {
span: expr_span,
kind: ConstantKind::Literal(Literal::Bool { value: false }),
});
this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
this.cfg.terminate(false_block, Terminator::Goto { target: join_block });
join_block.unit()
}
ExprKind::Loop { condition: opt_cond_expr, body } => {
// [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
// ^ |
// | 0
// | |
// | v
// [body_block_end] <~~~ [body_block]
//
// If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
//
// [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
// ^ |
// | |
// +--------------------------+
//
let loop_block = this.cfg.start_new_block();
let exit_block = this.cfg.start_new_block();
// start the loop
this.cfg.terminate(block, Terminator::Goto { target: loop_block });
this.in_loop_scope(loop_block, exit_block, |this| {
// conduct the test, if necessary
let body_block;
let opt_cond_expr = opt_cond_expr; // FIXME rustc bug
if let Some(cond_expr) = opt_cond_expr {
let loop_block_end;
let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
body_block = this.cfg.start_new_block();
this.cfg.terminate(loop_block_end,
Terminator::If {
cond: cond,
targets: [body_block, exit_block]
});
} else {
body_block = loop_block;
}
// execute the body, branching back to the test
let unit_temp = this.unit_temp.clone();
let body_block_end = unpack!(this.into(&unit_temp, body_block, body));
this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
// final point is exit_block
exit_block.unit()
})
}
ExprKind::Assign { lhs, rhs } => {
// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
// operators like x[j] = x[i].
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
this.cfg.push_drop(block, expr_span, DropKind::Deep, &lhs);
this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
block.unit()
}
ExprKind::AssignOp { op, lhs, rhs } => {
// FIXME(#28160) there is an interesting semantics
// question raised here -- should we "freeze" the
// value of the lhs here? I'm inclined to think not,
// since it seems closer to the semantics of the
// overloaded version, which takes `&mut self`. This
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
// As above, RTL.
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
// we don't have to drop prior contents or anything
// because AssignOp is only legal for Copy types
// (overloaded ops should be desugared into a call).
this.cfg.push_assign(block, expr_span, &lhs,
Rvalue::BinaryOp(op,
Operand::Consume(lhs.clone()),
rhs));
block.unit()
}
ExprKind::Continue { label } => {
this.break_or_continue(expr_span, label, block,
|loop_scope| loop_scope.continue_block)
}
ExprKind::Break { label } => {
this.break_or_continue(expr_span, label, block,
|loop_scope| loop_scope.break_block)
}
ExprKind::Return { value } => {
unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
let extent = this.extent_of_outermost_scope().unwrap();
this.exit_scope(expr_span, extent, block, END_BLOCK);
this.cfg.start_new_block().unit()
}
ExprKind::Call { fun, args } => {
let fun = unpack!(block = this.as_lvalue(block, fun));
let args: Vec<_> =
args.into_iter()
.map(|arg| unpack!(block = this.as_lvalue(block, arg)))
.collect();
let success = this.cfg.start_new_block();
let panic = this.diverge_cleanup();
this.cfg.terminate(block,
Terminator::Call {
data: CallData {
destination: destination.clone(),
func: fun,
args: args
},
targets: [success, panic]
});
success.unit()
}
// these are the cases that are more naturally handled by some other mode
ExprKind::Unary { .. } |
ExprKind::Binary { .. } |
ExprKind::Box { .. } |
ExprKind::Cast { .. } |
ExprKind::ReifyFnPointer { .. } |
ExprKind::UnsafeFnPointer { .. } |
ExprKind::Unsize { .. } |
ExprKind::Repeat { .. } |
ExprKind::Borrow { .. } |
ExprKind::VarRef { .. } |
ExprKind::SelfRef |
ExprKind::StaticRef { .. } |
ExprKind::Vec { .. } |
ExprKind::Tuple { .. } |
ExprKind::Adt { .. } |
ExprKind::Closure { .. } |
ExprKind::Index { .. } |
ExprKind::Deref { .. } |
ExprKind::Literal { .. } |
ExprKind::InlineAsm { .. } |
ExprKind::Field { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
Category::Rvalue(RvalueFunc::Into) => false,
_ => true,
});
let rvalue = unpack!(block = this.as_rvalue(block, expr));
this.cfg.push_assign(block, expr_span, destination, rvalue);
block.unit()
}
}
}
fn break_or_continue<F>(&mut self,
span: H::Span,
label: Option<H::CodeExtent>,
block: BasicBlock,
exit_selector: F)
-> BlockAnd<()>
where F: FnOnce(&LoopScope<H>) -> BasicBlock
{
let loop_scope = self.find_loop_scope(span, label);
let exit_block = exit_selector(&loop_scope);
self.exit_scope(span, loop_scope.extent, block, exit_block);
self.cfg.start_new_block().unit()
}
}

View file

@ -0,0 +1,79 @@
// 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.
//! Translates expressions into MIR. As a caller into this module, you
//! have many options, but the first thing you have to decide is
//! whether you are evaluating this expression for its *value*, its
//! *location*, or as a *constant*.
//!
//! Typically, you want the value: e.g., if you are doing `expr_a +
//! expr_b`, you want the values of those expressions. In that case,
//! you want one of the following functions. Note that if the expr has
//! a type that is not `Copy`, then using any of these functions will
//! "move" the value out of its current home (if any).
//!
//! - `into` -- writes the value into a specific location, which
//! should be uninitialized
//! - `as_operand` -- evaluates the value and yields an `Operand`,
//! suitable for use as an argument to an `Rvalue`
//! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand`
//! except it always returns a fresh lvalue, even for constants
//! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment;
//! as of this writing, never needed outside of the `expr` module itself
//!
//! Sometimes though want the expression's *location*. An example
//! would be during a match statement, or the operand of the `&`
//! operator. In that case, you want `as_lvalue`. This will create a
//! temporary if necessary.
//!
//! Finally, if it's a constant you seek, then call
//! `as_constant`. This creates a `Constant<H>`, but naturally it can
//! only be used on constant expressions and hence is needed only in
//! very limited contexts.
//!
//! ### Implementation notes
//!
//! For any given kind of expression, there is generally one way that
//! can be translated most naturally. This is specified by the
//! `Category::of` function in the `category` module. For example, a
//! struct expression (or other expression that creates a new value)
//! is typically easiest to write in terms of `as_rvalue` or `into`,
//! whereas a reference to a field is easiest to write in terms of
//! `as_lvalue`. (The exception to this is scope and paren
//! expressions, which have no category.)
//!
//! Therefore, the various functions above make use of one another in
//! a descending fashion. For any given expression, you should pick
//! the most suitable spot to implement it, and then just let the
//! other fns cycle around. The handoff works like this:
//!
//! - `into(lv)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `lv`
//! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use`
//! - `as_operand` -> either invokes `as_constant` or `as_temp`
//! - `as_constant` -> (no fallback)
//! - `as_temp` -> creates a temporary and either calls `as_lvalue` or `into`
//! - `as_lvalue` -> for rvalues, falls back to `as_temp` and returns that
//!
//! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp`
//! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
//! implemented in the category where it is supposed to be, there will be a problem.
//!
//! Of those fallbacks, the most interesting one is `as_temp`, because
//! it discriminates based on the category of the expression. This is
//! basically the point where the "by value" operations are bridged
//! over to the "by reference" mode (`as_lvalue`).
mod as_constant;
mod as_lvalue;
mod as_rvalue;
mod as_operand;
mod as_temp;
mod category;
mod into;

View file

@ -0,0 +1,70 @@
// 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.
//! In general, there are a number of things for which it's convenient
//! to just call `builder.into` and have it emit its result into a
//! given location. This is basically for expressions or things that can be
//! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
//! latter `EvalInto` trait.
use build::{BlockAnd, Builder};
use hair::*;
use repr::*;
pub trait EvalInto<H:Hair> {
fn eval_into(self, builder: &mut Builder<H>, destination: &Lvalue<H>,
block: BasicBlock) -> BlockAnd<()>;
}
impl<H:Hair> Builder<H> {
pub fn into<E>(&mut self,
destination: &Lvalue<H>,
block: BasicBlock,
expr: E)
-> BlockAnd<()>
where E: EvalInto<H>
{
expr.eval_into(self, destination, block)
}
}
impl<H:Hair> EvalInto<H> for ExprRef<H> {
fn eval_into(self,
builder: &mut Builder<H>,
destination: &Lvalue<H>,
block: BasicBlock)
-> BlockAnd<()> {
let expr = builder.hir.mirror(self);
builder.into_expr(destination, block, expr)
}
}
impl<H:Hair> EvalInto<H> for Expr<H> {
fn eval_into(self,
builder: &mut Builder<H>,
destination: &Lvalue<H>,
block: BasicBlock)
-> BlockAnd<()> {
builder.into_expr(destination, block, self)
}
}
impl<H:Hair> EvalInto<H> for Option<ExprRef<H>> {
fn eval_into(self,
builder: &mut Builder<H>,
destination: &Lvalue<H>,
block: BasicBlock)
-> BlockAnd<()> {
match self {
Some(expr) => builder.into(destination, block, expr),
None => block.unit()
}
}
}

View file

@ -0,0 +1,403 @@
// 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.
//! Code related to match expresions. These are sufficiently complex
//! to warrant their own module and submodules. :) This main module
//! includes the high-level algorithm, the submodules contain the
//! details.
use build::{BlockAnd, Builder};
use repr::*;
use hair::*;
// helper functions, broken out by category:
mod simplify;
mod test;
mod util;
impl<H:Hair> Builder<H> {
pub fn match_expr(&mut self,
destination: &Lvalue<H>,
span: H::Span,
mut block: BasicBlock,
discriminant: ExprRef<H>,
arms: Vec<Arm<H>>)
-> BlockAnd<()>
{
let discriminant_lvalue =
unpack!(block = self.as_lvalue(block, discriminant));
let arm_blocks: Vec<BasicBlock> =
arms.iter()
.map(|_| self.cfg.start_new_block())
.collect();
let arm_bodies: Vec<ExprRef<H>> =
arms.iter()
.map(|arm| arm.body.clone())
.collect();
// assemble a list of candidates: there is one candidate per
// pattern, which means there may be more than one candidate
// *per arm*. These candidates are kept sorted such that the
// highest priority candidate comes last in the list. This the
// reverse of the order in which candidates are written in the
// source.
let candidates: Vec<Candidate<H>> =
arms.into_iter()
.zip(arm_blocks.iter())
.rev() // highest priority comes last
.flat_map(|(arm, &arm_block)| {
let guard = arm.guard;
arm.patterns.into_iter()
.rev()
.map(move |pat| (arm_block, pat, guard.clone()))
})
.map(|(arm_block, pattern, guard)| {
Candidate {
match_pairs: vec![self.match_pair(discriminant_lvalue.clone(), pattern)],
bindings: vec![],
guard: guard,
arm_block: arm_block,
}
})
.collect();
// this will generate code to test discriminant_lvalue and
// branch to the appropriate arm block
let var_extent = self.extent_of_innermost_scope().unwrap();
self.match_candidates(span, var_extent, candidates, block);
// all the arm blocks will rejoin here
let end_block = self.cfg.start_new_block();
for (arm_body, &arm_block) in arm_bodies.into_iter().zip(arm_blocks.iter()) {
let mut arm_block = arm_block;
unpack!(arm_block = self.into(destination, arm_block, arm_body));
self.cfg.terminate(arm_block, Terminator::Goto { target: end_block });
}
end_block.unit()
}
pub fn expr_into_pattern(&mut self,
mut block: BasicBlock,
var_extent: H::CodeExtent, // lifetime of vars
irrefutable_pat: PatternRef<H>,
initializer: ExprRef<H>)
-> BlockAnd<()>
{
// optimize the case of `let x = ...`
let irrefutable_pat = self.hir.mirror(irrefutable_pat);
match irrefutable_pat.kind {
PatternKind::Binding { mutability,
name,
mode: BindingMode::ByValue,
var,
ty,
subpattern: None } => {
let index = self.declare_binding(var_extent, mutability, name,
var, ty, irrefutable_pat.span);
let lvalue = Lvalue::Var(index);
return self.into(&lvalue, block, initializer);
}
_ => { }
}
let lvalue = unpack!(block = self.as_lvalue(block, initializer));
self.lvalue_into_pattern(block, var_extent,
PatternRef::Mirror(Box::new(irrefutable_pat)), &lvalue)
}
pub fn lvalue_into_pattern(&mut self,
mut block: BasicBlock,
var_extent: H::CodeExtent,
irrefutable_pat: PatternRef<H>,
initializer: &Lvalue<H>)
-> BlockAnd<()>
{
// create a dummy candidate
let mut candidate = Candidate::<H> {
match_pairs: vec![self.match_pair(initializer.clone(), irrefutable_pat)],
bindings: vec![],
guard: None,
arm_block: block
};
// Simplify the candidate. Since the pattern is irrefutable, this should
// always convert all match-pairs into bindings.
unpack!(block = self.simplify_candidate(block, &mut candidate));
if !candidate.match_pairs.is_empty() {
self.hir.span_bug(
candidate.match_pairs[0].pattern.span,
&format!("match pairs {:?} remaining after simplifying irrefutable pattern",
candidate.match_pairs));
}
// now apply the bindings, which will also declare the variables
self.bind_matched_candidate(block, var_extent, candidate.bindings);
block.unit()
}
pub fn declare_uninitialized_variables(&mut self,
var_extent: H::CodeExtent,
pattern: PatternRef<H>)
{
let pattern = self.hir.mirror(pattern);
match pattern.kind {
PatternKind::Binding { mutability, name, mode: _, var, ty, subpattern } => {
self.declare_binding(var_extent, mutability, name, var, ty, pattern.span);
if let Some(subpattern) = subpattern {
self.declare_uninitialized_variables(var_extent, subpattern);
}
}
PatternKind::Array { prefix, slice, suffix } |
PatternKind::Slice { prefix, slice, suffix } => {
for subpattern in prefix.into_iter().chain(slice).chain(suffix) {
self.declare_uninitialized_variables(var_extent, subpattern);
}
}
PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
}
PatternKind::Deref { subpattern } => {
self.declare_uninitialized_variables(var_extent, subpattern);
}
PatternKind::Leaf { subpatterns } |
PatternKind::Variant { subpatterns, .. } => {
for subpattern in subpatterns {
self.declare_uninitialized_variables(var_extent, subpattern.pattern);
}
}
}
}
}
#[derive(Clone, Debug)]
struct Candidate<H:Hair> {
// all of these must be satisfied...
match_pairs: Vec<MatchPair<H>>,
// ...these bindings established...
bindings: Vec<Binding<H>>,
// ...and the guard must be evaluated...
guard: Option<ExprRef<H>>,
// ...and then we branch here.
arm_block: BasicBlock,
}
#[derive(Clone, Debug)]
struct Binding<H:Hair> {
span: H::Span,
source: Lvalue<H>,
name: H::Ident,
var_id: H::VarId,
var_ty: H::Ty,
mutability: Mutability,
binding_mode: BindingMode<H>,
}
#[derive(Clone, Debug)]
struct MatchPair<H:Hair> {
// this lvalue...
lvalue: Lvalue<H>,
// ... must match this pattern.
pattern: Pattern<H>,
}
#[derive(Clone, Debug, PartialEq)]
enum TestKind<H:Hair> {
// test the branches of enum
Switch { adt_def: H::AdtDef },
// test for equality
Eq { value: Constant<H>, ty: H::Ty },
// test whether the value falls within an inclusive range
Range { lo: Constant<H>, hi: Constant<H>, ty: H::Ty },
// test length of the slice is equal to len
Len { len: usize, op: BinOp },
}
#[derive(Debug)]
struct Test<H:Hair> {
span: H::Span,
kind: TestKind<H>,
}
///////////////////////////////////////////////////////////////////////////
// Main matching algorithm
impl<H:Hair> Builder<H> {
fn match_candidates(&mut self,
span: H::Span,
var_extent: H::CodeExtent,
mut candidates: Vec<Candidate<H>>,
mut block: BasicBlock)
{
debug!("matched_candidate(span={:?}, var_extent={:?}, block={:?}, candidates={:?})",
span, var_extent, block, candidates);
// Start by simplifying candidates. Once this process is
// complete, all the match pairs which remain require some
// form of test, whether it be a switch or pattern comparison.
for candidate in &mut candidates {
unpack!(block = self.simplify_candidate(block, candidate));
}
// The candidates are inversely sorted by priority. Check to
// see whether the candidates in the front of the queue (and
// hence back of the vec) have satisfied all their match
// pairs.
let fully_matched =
candidates.iter().rev().take_while(|c| c.match_pairs.is_empty()).count();
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
for _ in 0..fully_matched {
// If so, apply any bindings, test the guard (if any), and
// branch to the arm.
let candidate = candidates.pop().unwrap();
match self.bind_and_guard_matched_candidate(block, var_extent, candidate) {
None => { return; }
Some(b) => { block = b; }
}
}
// If there are no candidates that still need testing, we're done.
// Since all matches are exhaustive, execution should never reach this point.
if candidates.is_empty() {
return self.panic(block);
}
// otherwise, extract the next match pair and construct tests
let match_pair = &candidates.last().unwrap().match_pairs[0];
let test = self.test(match_pair);
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
for (outcome, mut target_block) in target_blocks.into_iter().enumerate() {
let applicable_candidates: Vec<Candidate<H>> =
candidates.iter()
.filter_map(|candidate| {
unpack!(target_block =
self.candidate_under_assumption(target_block,
&match_pair.lvalue,
&test.kind,
outcome,
candidate))
})
.collect();
self.match_candidates(span, var_extent, applicable_candidates, target_block);
}
}
/// Initializes each of the bindings from the candidate by
/// moving/copying/ref'ing the source as appropriate. Tests the
/// guard, if any, and then branches to the arm. Returns the block
/// for the case where the guard fails.
///
/// Note: we check earlier that if there is a guard, there cannot
/// be move bindings. This isn't really important for the
/// self-consistency of this fn, but the reason for it should be
/// clear: after we've done the assignments, if there were move
/// bindings, further tests would be a use-after-move (which would
/// in turn be detected by the borrowck code that runs on the
/// MIR).
fn bind_and_guard_matched_candidate(&mut self,
mut block: BasicBlock,
var_extent: H::CodeExtent,
candidate: Candidate<H>)
-> Option<BasicBlock> {
debug!("bind_and_guard_matched_candidate(block={:?}, var_extent={:?}, candidate={:?})",
block, var_extent, candidate);
debug_assert!(candidate.match_pairs.is_empty());
self.bind_matched_candidate(block, var_extent, candidate.bindings);
if let Some(guard) = candidate.guard {
// the block to branch to if the guard fails; if there is no
// guard, this block is simply unreachable
let cond = unpack!(block = self.as_operand(block, guard));
let otherwise = self.cfg.start_new_block();
self.cfg.terminate(block, Terminator::If { cond: cond,
targets: [candidate.arm_block, otherwise]});
Some(otherwise)
} else {
self.cfg.terminate(block, Terminator::Goto { target: candidate.arm_block });
None
}
}
fn bind_matched_candidate(&mut self,
block: BasicBlock,
var_extent: H::CodeExtent,
bindings: Vec<Binding<H>>) {
debug!("bind_matched_candidate(block={:?}, var_extent={:?}, bindings={:?})",
block, var_extent, bindings);
// Assign each of the bindings. This may trigger moves out of the candidate.
for binding in bindings {
// Create a variable for the `var_id` being bound. In the
// case where there are multiple patterns for a single
// arm, it may already exist.
let var_index = if !self.var_indices.contains_key(&binding.var_id) {
self.declare_binding(var_extent,
binding.mutability,
binding.name,
binding.var_id,
binding.var_ty,
binding.span)
} else {
self.var_indices[&binding.var_id]
};
let rvalue = match binding.binding_mode {
BindingMode::ByValue =>
Rvalue::Use(Operand::Consume(binding.source)),
BindingMode::ByRef(region, borrow_kind) =>
Rvalue::Ref(region, borrow_kind, binding.source),
};
self.cfg.push_assign(block, binding.span, &Lvalue::Var(var_index), rvalue);
}
}
fn declare_binding(&mut self,
var_extent: H::CodeExtent,
mutability: Mutability,
name: H::Ident,
var_id: H::VarId,
var_ty: H::Ty,
span: H::Span)
-> u32
{
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_extent={:?}, span={:?})",
var_id, name, var_ty, var_extent, span);
let index = self.var_decls.len();
self.var_decls.push(VarDecl::<H> {
mutability: mutability,
name: name,
ty: var_ty.clone(),
});
let index = index as u32;
self.schedule_drop(span, var_extent, DropKind::Deep, &Lvalue::Var(index), var_ty);
self.var_indices.insert(var_id, index);
debug!("declare_binding: index={:?}", index);
index
}
}

View file

@ -0,0 +1,131 @@
// 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.
//! Simplifying Candidates
//!
//! *Simplifying* a match pair `lvalue @ pattern` means breaking it down
//! into bindings or other, simpler match pairs. For example:
//!
//! - `lvalue @ (P1, P2)` can be simplified to `[lvalue.0 @ P1, lvalue.1 @ P2]`
//! - `lvalue @ x` can be simplified to `[]` by binding `x` to `lvalue`
//!
//! The `simplify_candidate` routine just repeatedly applies these
//! sort of simplifications until there is nothing left to
//! simplify. Match pairs cannot be simplified if they require some
//! sort of test: for example, testing which variant an enum is, or
//! testing a value against a constant.
use build::{BlockAnd, Builder};
use build::matches::{Binding, MatchPair, Candidate};
use hair::*;
use repr::*;
use std::mem;
impl<H:Hair> Builder<H> {
pub fn simplify_candidate(&mut self,
mut block: BasicBlock,
candidate: &mut Candidate<H>)
-> BlockAnd<()>
{
// repeatedly simplify match pairs until fixed point is reached
loop {
let match_pairs = mem::replace(&mut candidate.match_pairs, vec![]);
let mut progress = match_pairs.len(); // count how many were simplified
for match_pair in match_pairs {
match self.simplify_match_pair(block, match_pair, candidate) {
Ok(b) => { block = b; }
Err(match_pair) => {
candidate.match_pairs.push(match_pair);
progress -= 1; // this one was not simplified
}
}
}
if progress == 0 {
return block.unit(); // if we were not able to simplify any, done.
}
}
}
/// Tries to simplify `match_pair`, returning true if
/// successful. If successful, new match pairs and bindings will
/// have been pushed into the candidate. On failure (if false is
/// returned), no changes are made to candidate.
fn simplify_match_pair(&mut self,
mut block: BasicBlock,
match_pair: MatchPair<H>,
candidate: &mut Candidate<H>)
-> Result<BasicBlock, MatchPair<H>> // returns Err() if cannot simplify
{
match match_pair.pattern.kind {
PatternKind::Wild(..) => {
// nothing left to do
Ok(block)
}
PatternKind::Binding { name, mutability, mode, var, ty, subpattern } => {
candidate.bindings.push(Binding {
name: name,
mutability: mutability,
span: match_pair.pattern.span,
source: match_pair.lvalue.clone(),
var_id: var,
var_ty: ty,
binding_mode: mode,
});
if let Some(subpattern) = subpattern {
// this is the `x @ P` case; have to keep matching against `P` now
let subpattern = self.hir.mirror(subpattern);
candidate.match_pairs.push(MatchPair::new(match_pair.lvalue, subpattern));
}
Ok(block)
}
PatternKind::Constant { .. } => {
// FIXME normalize patterns when possible
Err(match_pair)
}
PatternKind::Array { prefix, slice, suffix } => {
unpack!(block = self.prefix_suffix_slice(&mut candidate.match_pairs,
block,
match_pair.lvalue.clone(),
prefix,
slice,
suffix));
Ok(block)
}
PatternKind::Slice { .. } |
PatternKind::Range { .. } |
PatternKind::Variant { .. } => {
// cannot simplify, test is required
Err(match_pair)
}
PatternKind::Leaf { subpatterns } => {
// tuple struct, match subpats (if any)
candidate.match_pairs.extend(
self.field_match_pairs(match_pair.lvalue, subpatterns));
Ok(block)
}
PatternKind::Deref { subpattern } => {
let lvalue = match_pair.lvalue.deref();
let subpattern = self.hir.mirror(subpattern);
candidate.match_pairs.push(MatchPair::new(lvalue, subpattern));
Ok(block)
}
}
}
}

View file

@ -0,0 +1,347 @@
// 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.
// Testing candidates
//
// After candidates have been simplified, the only match pairs that
// remain are those that require some sort of test. The functions here
// identify what tests are needed, perform the tests, and then filter
// the candidates based on the result.
use build::{BlockAnd, Builder};
use build::matches::{Candidate, MatchPair, Test, TestKind};
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
/// Identifies what test is needed to decide if `match_pair` is applicable.
///
/// It is a bug to call this with a simplifyable pattern.
pub fn test(&mut self, match_pair: &MatchPair<H>) -> Test<H> {
match match_pair.pattern.kind {
PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => {
Test {
span: match_pair.pattern.span,
kind: TestKind::Switch { adt_def: adt_def.clone() },
}
}
PatternKind::Constant { ref expr } => {
let expr = self.as_constant(expr.clone());
Test {
span: match_pair.pattern.span,
kind: TestKind::Eq { value: expr, ty: match_pair.pattern.ty.clone() },
}
}
PatternKind::Range { ref lo, ref hi } => {
let lo = self.as_constant(lo.clone());
let hi = self.as_constant(hi.clone());
Test {
span: match_pair.pattern.span,
kind: TestKind::Range { lo: lo, hi: hi, ty: match_pair.pattern.ty.clone() },
}
}
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
let len = prefix.len() + suffix.len();
let op = if slice.is_some() {BinOp::Ge} else {BinOp::Eq};
Test {
span: match_pair.pattern.span,
kind: TestKind::Len { len: len, op: op },
}
}
PatternKind::Array { .. } |
PatternKind::Wild |
PatternKind::Binding { .. } |
PatternKind::Leaf { .. } |
PatternKind::Deref { .. } => {
self.error_simplifyable(match_pair)
}
}
}
/// Generates the code to perform a test.
pub fn perform_test(&mut self,
block: BasicBlock,
lvalue: &Lvalue<H>,
test: &Test<H>)
-> Vec<BasicBlock> {
match test.kind.clone() {
TestKind::Switch { adt_def } => {
let num_enum_variants = self.hir.num_variants(adt_def);
let target_blocks: Vec<_> =
(0..num_enum_variants).map(|_| self.cfg.start_new_block())
.collect();
self.cfg.terminate(block, Terminator::Switch {
discr: lvalue.clone(),
targets: target_blocks.clone()
});
target_blocks
}
TestKind::Eq { value, ty } => {
// call PartialEq::eq(discrim, constant)
let constant = self.push_constant(block, test.span, ty.clone(), value);
let item_ref = self.hir.partial_eq(ty);
self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant)
}
TestKind::Range { lo, hi, ty } => {
// Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
let lo = self.push_constant(block, test.span, ty.clone(), lo);
let hi = self.push_constant(block, test.span, ty.clone(), hi);
let item_ref = self.hir.partial_le(ty);
let lo_blocks =
self.call_comparison_fn(block, test.span, item_ref.clone(), lo, lvalue.clone());
let hi_blocks =
self.call_comparison_fn(lo_blocks[0], test.span, item_ref, lvalue.clone(), hi);
let failure = self.cfg.start_new_block();
self.cfg.terminate(lo_blocks[1], Terminator::Goto { target: failure });
self.cfg.terminate(hi_blocks[1], Terminator::Goto { target: failure });
vec![hi_blocks[0], failure]
}
TestKind::Len { len, op } => {
let (usize_ty, bool_ty) = (self.hir.usize_ty(), self.hir.bool_ty());
let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
// actual = len(lvalue)
self.cfg.push_assign(
block, test.span,
&actual, Rvalue::Len(lvalue.clone()));
// expected = <N>
let expected =
self.push_usize(block, test.span, len);
// result = actual == expected OR result = actual < expected
self.cfg.push_assign(
block, test.span,
&result, Rvalue::BinaryOp(op,
Operand::Consume(actual),
Operand::Consume(expected)));
// branch based on result
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
self.cfg.start_new_block()];
self.cfg.terminate(block, Terminator::If {
cond: Operand::Consume(result),
targets: [target_blocks[0], target_blocks[1]]
});
target_blocks
}
}
}
fn call_comparison_fn(&mut self,
block: BasicBlock,
span: H::Span,
item_ref: ItemRef<H>,
lvalue1: Lvalue<H>,
lvalue2: Lvalue<H>)
-> Vec<BasicBlock> {
let target_blocks = vec![self.cfg.start_new_block(),
self.cfg.start_new_block()];
let bool_ty = self.hir.bool_ty();
let eq_result = self.temp(bool_ty);
let func = self.push_item_ref(block, span, item_ref);
let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()];
self.cfg.terminate(block,
Terminator::Call {
data: CallData {
destination: eq_result.clone(),
func: func,
args: vec![lvalue1, lvalue2],
},
targets: call_blocks,
});
// check the result
self.cfg.terminate(call_blocks[0],
Terminator::If {
cond: Operand::Consume(eq_result),
targets: [target_blocks[0], target_blocks[1]]
});
target_blocks
}
/// Given a candidate and the outcome of a test we have performed,
/// transforms the candidate into a new candidate that reflects
/// further tests still needed. Returns `None` if this candidate
/// has now been ruled out.
///
/// For example, if a candidate included the patterns `[x.0 @
/// Ok(P1), x.1 @ 22]`, and we did a switch test on `x.0` and
/// found the variant `Err` (as indicated by the `test_outcome`
/// parameter), we would return `None`. But if the test_outcome
/// were `Ok`, we would return `Some([x.0.downcast<Ok>.0 @ P1, x.1
/// @ 22])`.
pub fn candidate_under_assumption(&mut self,
mut block: BasicBlock,
test_lvalue: &Lvalue<H>,
test_kind: &TestKind<H>,
test_outcome: usize,
candidate: &Candidate<H>)
-> BlockAnd<Option<Candidate<H>>> {
let candidate = candidate.clone();
let match_pairs = candidate.match_pairs;
let result = unpack!(block = self.match_pairs_under_assumption(block,
test_lvalue,
test_kind,
test_outcome,
match_pairs));
block.and(match result {
Some(match_pairs) => Some(Candidate { match_pairs: match_pairs, ..candidate }),
None => None
})
}
/// Helper for candidate_under_assumption that does the actual
/// work of transforming the list of match pairs.
fn match_pairs_under_assumption(&mut self,
mut block: BasicBlock,
test_lvalue: &Lvalue<H>,
test_kind: &TestKind<H>,
test_outcome: usize,
match_pairs: Vec<MatchPair<H>>)
-> BlockAnd<Option<Vec<MatchPair<H>>>> {
let mut result = vec![];
for match_pair in match_pairs {
// if the match pair is testing a different lvalue, it
// is unaffected by this test.
if match_pair.lvalue != *test_lvalue {
result.push(match_pair);
continue;
}
let desired_test = self.test(&match_pair);
if *test_kind != desired_test.kind {
// if the match pair wants to (e.g.) test for
// equality against some particular constant, but
// we did a switch, then we can't say whether it
// matches or not, so we still have to include it
// as a possibility.
//
// For example, we have a constant `FOO:
// Option<i32> = Some(22)`, and `match_pair` is `x
// @ FOO`, but we did a switch on the variant
// (`Some` vs `None`). (OK, in principle this
// could tell us something, but we're not that
// smart yet to actually dig into the constant
// itself)
result.push(match_pair);
continue;
}
let opt_consequent_match_pairs =
unpack!(block = self.consequent_match_pairs_under_assumption(block,
match_pair,
test_outcome));
match opt_consequent_match_pairs {
None => {
// Right kind of test, but wrong outcome. That
// means this **entire candidate** is
// inapplicable, since the candidate is only
// applicable if all of its match-pairs apply (and
// this one doesn't).
return block.and(None);
}
Some(consequent_match_pairs) => {
// Test passed; add any new patterns we have to test to the final result.
result.extend(consequent_match_pairs)
}
}
}
block.and(Some(result))
}
/// Identifies what test is needed to decide if `match_pair` is applicable.
///
/// It is a bug to call this with a simplifyable pattern.
pub fn consequent_match_pairs_under_assumption(&mut self,
mut block: BasicBlock,
match_pair: MatchPair<H>,
test_outcome: usize)
-> BlockAnd<Option<Vec<MatchPair<H>>>> {
match match_pair.pattern.kind {
PatternKind::Variant { adt_def, variant_index, subpatterns } => {
if test_outcome != variant_index {
return block.and(None);
}
let elem = ProjectionElem::Downcast(adt_def, variant_index);
let downcast_lvalue = match_pair.lvalue.clone().elem(elem);
let consequent_match_pairs =
subpatterns.into_iter()
.map(|subpattern| {
let lvalue =
downcast_lvalue.clone().field(
subpattern.field);
self.match_pair(lvalue, subpattern.pattern)
})
.collect();
block.and(Some(consequent_match_pairs))
}
PatternKind::Constant { .. } |
PatternKind::Range { .. } => {
// these are boolean tests: if we are on the 0th
// successor, then they passed, and otherwise they
// failed, but there are never any more tests to come.
if test_outcome == 0 {
block.and(Some(vec![]))
} else {
block.and(None)
}
}
PatternKind::Slice { prefix, slice, suffix } => {
if test_outcome == 0 {
let mut consequent_match_pairs = vec![];
unpack!(block = self.prefix_suffix_slice(&mut consequent_match_pairs,
block,
match_pair.lvalue,
prefix,
slice,
suffix));
block.and(Some(consequent_match_pairs))
} else {
block.and(None)
}
}
PatternKind::Array { .. } |
PatternKind::Wild |
PatternKind::Binding { .. } |
PatternKind::Leaf { .. } |
PatternKind::Deref { .. } => {
self.error_simplifyable(&match_pair)
}
}
}
fn error_simplifyable(&mut self, match_pair: &MatchPair<H>) -> ! {
self.hir.span_bug(
match_pair.pattern.span,
&format!("simplifyable pattern found: {:?}", match_pair.pattern))
}
}

View file

@ -0,0 +1,125 @@
// 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.
use build::{BlockAnd, Builder};
use build::matches::MatchPair;
use hair::*;
use repr::*;
use std::u32;
impl<H:Hair> Builder<H> {
pub fn field_match_pairs(&mut self,
lvalue: Lvalue<H>,
subpatterns: Vec<FieldPatternRef<H>>)
-> Vec<MatchPair<H>> {
subpatterns.into_iter()
.map(|fieldpat| {
let lvalue = lvalue.clone().field(fieldpat.field);
self.match_pair(lvalue, fieldpat.pattern)
})
.collect()
}
pub fn match_pair(&mut self, lvalue: Lvalue<H>, pattern: PatternRef<H>) -> MatchPair<H> {
let pattern = self.hir.mirror(pattern);
MatchPair::new(lvalue, pattern)
}
/// When processing an array/slice pattern like `lv @ [x, y, ..s, z]`,
/// this function converts the prefix (`x`, `y`) and suffix (`z`) into
/// distinct match pairs:
///
/// lv[0 of 3] @ x // see ProjectionElem::ConstantIndex (and its Debug impl)
/// lv[1 of 3] @ y // to explain the `[x of y]` notation
/// lv[-1 of 3] @ z
///
/// If a slice like `s` is present, then the function also creates
/// a temporary like:
///
/// tmp0 = lv[2..-1] // using the special Rvalue::Slice
///
/// and creates a match pair `tmp0 @ s`
pub fn prefix_suffix_slice(&mut self,
match_pairs: &mut Vec<MatchPair<H>>,
block: BasicBlock,
lvalue: Lvalue<H>,
prefix: Vec<PatternRef<H>>,
opt_slice: Option<PatternRef<H>>,
suffix: Vec<PatternRef<H>>)
-> BlockAnd<()>
{
// If there is a `..P` pattern, create a temporary `t0` for
// the slice and then a match pair `t0 @ P`:
if let Some(slice) = opt_slice {
let slice = self.hir.mirror(slice);
let prefix_len = prefix.len();
let suffix_len = suffix.len();
let rvalue = Rvalue::Slice { input: lvalue.clone(),
from_start: prefix_len,
from_end: suffix_len };
let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy
self.cfg.push_assign(block, slice.span, &temp, rvalue);
match_pairs.push(MatchPair::new(temp, slice));
}
self.prefix_suffix(match_pairs, lvalue, prefix, suffix);
block.unit()
}
/// Helper for `prefix_suffix_slice` which just processes the prefix and suffix.
fn prefix_suffix(&mut self,
match_pairs: &mut Vec<MatchPair<H>>,
lvalue: Lvalue<H>,
prefix: Vec<PatternRef<H>>,
suffix: Vec<PatternRef<H>>)
{
let min_length = prefix.len() + suffix.len();
assert!(min_length < u32::MAX as usize);
let min_length = min_length as u32;
let prefix_pairs: Vec<_> =
prefix.into_iter()
.enumerate()
.map(|(idx, subpattern)| {
let elem = ProjectionElem::ConstantIndex {
offset: idx as u32,
min_length: min_length,
from_end: false,
};
let lvalue = lvalue.clone().elem(elem);
self.match_pair(lvalue, subpattern)
})
.collect();
let suffix_pairs: Vec<_> =
suffix.into_iter()
.rev()
.enumerate()
.map(|(idx, subpattern)| {
let elem = ProjectionElem::ConstantIndex {
offset: (idx+1) as u32,
min_length: min_length,
from_end: true,
};
let lvalue = lvalue.clone().elem(elem);
self.match_pair(lvalue, subpattern)
})
.collect();
match_pairs.extend(prefix_pairs.into_iter().chain(suffix_pairs));
}
}
impl<H:Hair> MatchPair<H> {
pub fn new(lvalue: Lvalue<H>, pattern: Pattern<H>) -> MatchPair<H> {
MatchPair { lvalue: lvalue, pattern: pattern }
}
}

View file

@ -0,0 +1,78 @@
// 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.
//! Miscellaneous builder routines that are not specific to building any particular
//! kind of thing.
use build::Builder;
use hair::*;
use repr::*;
use std::u32;
impl<H:Hair> Builder<H> {
/// Add a new temporary value of type `ty` storing the result of
/// evaluating `expr`.
///
/// NB: **No cleanup is scheduled for this temporary.** You should
/// call `schedule_drop` once the temporary is initialized.
pub fn temp(&mut self, ty: H::Ty) -> Lvalue<H> {
let index = self.temp_decls.len();
self.temp_decls.push(TempDecl { ty: ty });
assert!(index < (u32::MAX) as usize);
let lvalue = Lvalue::Temp(index as u32);
debug!("temp: created temp {:?} with type {:?}",
lvalue, self.temp_decls.last().unwrap().ty);
lvalue
}
pub fn push_constant(&mut self,
block: BasicBlock,
span: H::Span,
ty: H::Ty,
constant: Constant<H>)
-> Lvalue<H> {
let temp = self.temp(ty);
self.cfg.push_assign_constant(block, span, &temp, constant);
temp
}
pub fn push_usize(&mut self,
block: BasicBlock,
span: H::Span,
value: usize)
-> Lvalue<H> {
let usize_ty = self.hir.usize_ty();
let temp = self.temp(usize_ty);
self.cfg.push_assign_constant(
block, span, &temp,
Constant {
span: span,
kind: ConstantKind::Literal(Literal::Uint { bits: IntegralBits::BSize,
value: value as u64 }),
});
temp
}
pub fn push_item_ref(&mut self,
block: BasicBlock,
span: H::Span,
item_ref: ItemRef<H>)
-> Lvalue<H> {
let constant = Constant {
span: span,
kind: ConstantKind::Literal(Literal::Item {
def_id: item_ref.def_id,
substs: item_ref.substs
})
};
self.push_constant(block, span, item_ref.ty, constant)
}
}

View file

@ -0,0 +1,174 @@
// Copyright 2012-2014 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.
use hair::{self, Hair};
use rustc_data_structures::fnv::FnvHashMap;
use repr::*;
struct Builder<H:Hair> {
hir: H,
extents: FnvHashMap<H::CodeExtent, Vec<GraphExtent>>,
cfg: CFG<H>,
scopes: Vec<scope::Scope<H>>,
loop_scopes: Vec<scope::LoopScope<H>>,
unit_temp: Lvalue<H>,
var_decls: Vec<VarDecl<H>>,
var_indices: FnvHashMap<H::VarId, u32>,
temp_decls: Vec<TempDecl<H>>,
}
struct CFG<H:Hair> {
basic_blocks: Vec<BasicBlockData<H>>
}
///////////////////////////////////////////////////////////////////////////
// The `BlockAnd` "monad" packages up the new basic block along with a
// produced value (sometimes just unit, of course). The `unpack!`
// macro (and methods below) makes working with `BlockAnd` much more
// convenient.
#[must_use] // if you don't use one of these results, you're leaving a dangling edge
struct BlockAnd<T>(BasicBlock, T);
impl BasicBlock {
fn and<T>(self, v: T) -> BlockAnd<T> {
BlockAnd(self, v)
}
fn unit(self) -> BlockAnd<()> {
BlockAnd(self, ())
}
}
/// Update a block pointer and return the value.
/// Use it like `let x = unpack!(block = self.foo(block, foo))`.
macro_rules! unpack {
($x:ident = $c:expr) => {
{
let BlockAnd(b, v) = $c;
$x = b;
v
}
};
($c:expr) => {
{
let BlockAnd(b, ()) = $c;
b
}
};
}
///////////////////////////////////////////////////////////////////////////
// construct() -- the main entry point for building MIR for a function
pub fn construct<H:Hair>(mut hir: H,
_span: H::Span,
implicit_arguments: Vec<H::Ty>,
explicit_arguments: Vec<(H::Ty, H::Pattern)>,
argument_extent: H::CodeExtent,
ast_block: H::Block)
-> Mir<H> {
let cfg = CFG { basic_blocks: vec![] };
// it's handy to have a temporary of type `()` sometimes, so make
// one from the start and keep it available
let temp_decls = vec![TempDecl::<H> { ty: hir.unit_ty() }];
let unit_temp = Lvalue::Temp(0);
let mut builder = Builder {
hir: hir,
cfg: cfg,
extents: FnvHashMap(),
scopes: vec![],
loop_scopes: vec![],
temp_decls: temp_decls,
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: unit_temp,
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK);
let mut block = START_BLOCK;
let arg_decls = unpack!(block = builder.args_and_body(block,
implicit_arguments,
explicit_arguments,
argument_extent,
ast_block));
builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK });
builder.cfg.terminate(END_BLOCK, Terminator::Return);
Mir {
basic_blocks: builder.cfg.basic_blocks,
extents: builder.extents,
var_decls: builder.var_decls,
arg_decls: arg_decls,
temp_decls: builder.temp_decls,
}
}
impl<H:Hair> Builder<H> {
fn args_and_body(&mut self,
mut block: BasicBlock,
implicit_arguments: Vec<H::Ty>,
explicit_arguments: Vec<(H::Ty, H::Pattern)>,
argument_extent: H::CodeExtent,
ast_block: H::Block)
-> BlockAnd<Vec<ArgDecl<H>>>
{
self.in_scope(argument_extent, block, |this| {
let arg_decls = {
let implicit_arg_decls = implicit_arguments.into_iter()
.map(|ty| ArgDecl { ty: ty });
// to start, translate the argument patterns and collect the
// argument types.
let explicit_arg_decls =
explicit_arguments
.into_iter()
.enumerate()
.map(|(index, (ty, pattern))| {
let lvalue = Lvalue::Arg(index as u32);
unpack!(block = this.lvalue_into_pattern(block,
argument_extent,
hair::PatternRef::Hair(pattern),
&lvalue));
ArgDecl { ty: ty }
});
implicit_arg_decls.chain(explicit_arg_decls).collect()
};
// start the first basic block and translate the body
unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block));
block.and(arg_decls)
})
}
}
///////////////////////////////////////////////////////////////////////////
// Builder methods are broken up into modules, depending on what kind
// of thing is being translated. Note that they use the `unpack` macro
// above extensively.
mod block;
mod cfg;
mod expr;
mod into;
mod matches;
mod misc;
mod scope;
mod stmt;

View file

@ -0,0 +1,304 @@
// 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
`H::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 `H::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 os 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, Builder, CFG};
use hair::Hair;
use repr::*;
pub struct Scope<H:Hair> {
extent: H::CodeExtent,
exits: Vec<ExecutionPoint>,
drops: Vec<(DropKind, H::Span, Lvalue<H>)>,
cached_block: Option<BasicBlock>,
}
#[derive(Clone, Debug)]
pub struct LoopScope<H:Hair> {
pub extent: H::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<H:Hair> Builder<H> {
/// 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<H>) -> BlockAnd<R>
{
let extent = self.extent_of_innermost_scope().unwrap();
let loop_scope = LoopScope::<H> { 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
}
/// Start a scope. The closure `f` should translate the contents
/// of the scope. See module comment for more details.
pub fn in_scope<F,R>(&mut self,
extent: H::CodeExtent,
block: BasicBlock,
f: F)
-> BlockAnd<R>
where F: FnOnce(&mut Builder<H>) -> BlockAnd<R>
{
debug!("in_scope(extent={:?}, block={:?})", extent, block);
let start_point = self.cfg.end_point(block);
// push scope, execute `f`, then pop scope again
self.scopes.push(Scope {
extent: extent.clone(),
drops: vec![],
exits: vec![],
cached_block: None,
});
let BlockAnd(fallthrough_block, rv) = f(self);
let mut scope = self.scopes.pop().unwrap();
// 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(fallthrough_block, span, kind, &lvalue);
}
// add the implicit fallthrough edge
scope.exits.push(self.cfg.end_point(fallthrough_block));
// compute the extent from start to finish and store it in the graph
let graph_extent = self.graph_extent(start_point, scope.exits);
self.extents.entry(extent)
.or_insert(vec![])
.push(graph_extent);
debug!("in_scope: exiting extent={:?} fallthrough_block={:?}", extent, fallthrough_block);
fallthrough_block.and(rv)
}
/// Creates a graph extent (SEME region) from an entry point and
/// exit points.
fn graph_extent(&self, entry: ExecutionPoint, exits: Vec<ExecutionPoint>) -> GraphExtent {
if exits.len() == 1 && entry.block == exits[0].block {
GraphExtent { entry: entry, exit: GraphExtentExit::Statement(exits[0].statement) }
} else {
GraphExtent { entry: entry, exit: GraphExtentExit::Points(exits) }
}
}
/// Finds the loop scope for a given label. This is used for
/// resolving `break` and `continue`.
pub fn find_loop_scope(&mut self,
span: H::Span,
label: Option<H::CodeExtent>)
-> LoopScope<H> {
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: H::Span,
extent: H::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);
}
scope.exits.push(self.cfg.end_point(block));
}
self.cfg.terminate(block, Terminator::Goto { target: target });
}
/// Creates a path that performs all required cleanup for
/// unwinding. This path terminates in DIVERGE. Returns the start
/// of the path. See module comment for more details.
pub fn diverge_cleanup(&mut self) -> BasicBlock {
diverge_cleanup_helper(&mut self.cfg, &mut self.scopes)
}
/// Create diverge cleanup and branch to it from `block`.
pub fn panic(&mut self, block: BasicBlock) {
let cleanup = self.diverge_cleanup();
self.cfg.terminate(block, Terminator::Panic { target: cleanup });
}
/// Indicates that `lvalue` should be dropped on exit from
/// `extent`.
pub fn schedule_drop(&mut self,
span: H::Span,
extent: H::CodeExtent,
kind: DropKind,
lvalue: &Lvalue<H>,
lvalue_ty: H::Ty)
{
if self.hir.needs_drop(lvalue_ty, span) {
match self.scopes.iter_mut().rev().find(|s| s.extent == extent) {
Some(scope) => {
scope.drops.push((kind, span, lvalue.clone()));
scope.cached_block = None;
}
None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}",
extent, lvalue)),
}
}
}
pub fn extent_of_innermost_scope(&self) -> Option<H::CodeExtent> {
self.scopes.last().map(|scope| scope.extent)
}
pub fn extent_of_outermost_scope(&self) -> Option<H::CodeExtent> {
self.scopes.first().map(|scope| scope.extent)
}
}
fn diverge_cleanup_helper<H:Hair>(cfg: &mut CFG<H>,
scopes: &mut [Scope<H>])
-> BasicBlock {
let len = scopes.len();
if len == 0 {
return DIVERGE_BLOCK;
}
let (remaining, scope) = scopes.split_at_mut(len - 1);
let scope = &mut scope[0];
if let Some(b) = scope.cached_block {
return b;
}
let block = cfg.start_new_block();
for &(kind, span, ref lvalue) in &scope.drops {
cfg.push_drop(block, span, kind, lvalue);
}
scope.cached_block = Some(block);
let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining);
cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block });
block
}

View file

@ -0,0 +1,61 @@
// 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.
use build::{BlockAnd, Builder};
use hair::*;
use repr::*;
impl<H:Hair> Builder<H> {
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<H>>) -> BlockAnd<()> {
for stmt in stmts {
unpack!(block = self.stmt(block, stmt));
}
block.unit()
}
pub fn stmt(&mut self, mut block: BasicBlock, stmt: StmtRef<H>) -> BlockAnd<()> {
let this = self;
let Stmt { span, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Let { remainder_scope,
init_scope,
pattern,
initializer: Some(initializer),
stmts } => {
this.in_scope(remainder_scope, block, |this| {
unpack!(block = this.in_scope(init_scope, block, |this| {
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
}));
this.stmts(block, stmts)
})
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
this.in_scope(remainder_scope, block, |this| {
unpack!(block = this.in_scope(init_scope, block, |this| {
this.declare_uninitialized_variables(remainder_scope, pattern);
block.unit()
}));
this.stmts(block, stmts)
})
}
StmtKind::Expr { scope, expr } => {
this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
block.unit()
})
}
}
}
}

225
src/librustc_mir/dump.rs Normal file
View file

@ -0,0 +1,225 @@
// 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.
//! An experimental pass that scources for `#[rustc_mir]` attributes,
//! builds the resulting MIR, and dumps it out into a file for inspection.
//!
//! The attribute formats that are currently accepted are:
//!
//! - `#[rustc_mir(graphviz="file.gv")]`
//! - `#[rustc_mir(pretty="file.mir")]`
extern crate syntax;
extern crate rustc;
extern crate rustc_front;
use build;
use dot;
use repr::Mir;
use std::fs::File;
use tcx::{PatNode, Cx};
use self::rustc::middle::def_id::DefId;
use self::rustc::middle::infer;
use self::rustc::middle::region::CodeExtentData;
use self::rustc::middle::ty::{self, Ty};
use self::rustc::util::common::ErrorReported;
use self::rustc_front::hir;
use self::rustc_front::attr::{AttrMetaMethods};
use self::rustc_front::visit;
use self::syntax::ast;
use self::syntax::codemap::Span;
pub fn dump_crate(tcx: &ty::ctxt) {
let mut dump = OuterDump { tcx: tcx };
visit::walk_crate(&mut dump, tcx.map.krate());
}
///////////////////////////////////////////////////////////////////////////
// OuterDump -- walks a crate, looking for fn items and methods to build MIR from
struct OuterDump<'a,'tcx:'a> {
tcx: &'a ty::ctxt<'tcx>,
}
impl<'a, 'tcx> OuterDump<'a, 'tcx> {
fn visit_mir<OP>(&self, attributes: &'tcx [hir::Attribute], mut walk_op: OP)
where OP: FnMut(&mut InnerDump<'a,'tcx>)
{
let mut built_mir = false;
for attr in attributes {
if attr.check_name("rustc_mir") {
let mut closure_dump = InnerDump { tcx: self.tcx, attr: Some(attr) };
walk_op(&mut closure_dump);
built_mir = true;
}
}
let always_build_mir = self.tcx.sess.opts.always_build_mir;
if !built_mir && always_build_mir {
let mut closure_dump = InnerDump { tcx: self.tcx, attr: None };
walk_op(&mut closure_dump);
}
}
}
impl<'a, 'tcx> visit::Visitor<'tcx> for OuterDump<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
self.visit_mir(&item.attrs, |c| visit::walk_item(c, item));
visit::walk_item(self, item);
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
match trait_item.node {
hir::MethodTraitItem(_, Some(_)) => {
self.visit_mir(&trait_item.attrs, |c| visit::walk_trait_item(c, trait_item));
}
_ => { }
}
visit::walk_trait_item(self, trait_item);
}
}
///////////////////////////////////////////////////////////////////////////
// InnerDump -- dumps MIR for a single fn and its contained closures
struct InnerDump<'a,'tcx:'a> {
tcx: &'a ty::ctxt<'tcx>,
attr: Option<&'a hir::Attribute>,
}
impl<'a, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'tcx> {
fn visit_item(&mut self, _: &'tcx hir::Item) {
// ignore nested items; they need their own graphviz annotation
}
fn visit_fn(&mut self,
fk: visit::FnKind<'tcx>,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block,
span: Span,
id: ast::NodeId) {
let (prefix, implicit_arg_tys) = match fk {
visit::FnKind::Closure =>
(format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
_ =>
(format!(""), vec![]),
};
let param_env =
ty::ParameterEnvironment::for_item(self.tcx, id);
let infcx =
infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
Some(param_env),
true);
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mir) => {
let meta_item_list =
self.attr.iter()
.flat_map(|a| a.meta_item_list())
.flat_map(|l| l.iter());
for item in meta_item_list {
if item.check_name("graphviz") {
match item.value_str() {
Some(s) => {
match
File::create(format!("{}{}", prefix, s))
.and_then(|ref mut output| dot::render(&mir, output))
{
Ok(()) => { }
Err(e) => {
self.tcx.sess.span_fatal(
item.span,
&format!("Error writing graphviz \
results to `{}`: {}",
s, e));
}
}
}
None => {
self.tcx.sess.span_err(
item.span,
&format!("graphviz attribute requires a path"));
}
}
}
}
}
Err(ErrorReported) => { }
}
visit::walk_fn(self, fk, decl, body, span);
}
}
fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
implicit_arg_tys: Vec<Ty<'tcx>>,
fn_id: ast::NodeId,
span: Span,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block)
-> Result<Mir<Cx<'a,'tcx>>, ErrorReported> {
let arguments =
decl.inputs
.iter()
.map(|arg| {
let ty = cx.tcx.node_id_to_type(arg.id);
(ty, PatNode::irrefutable(&arg.pat))
})
.collect();
let parameter_scope =
cx.tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
Ok(build::construct(cx,
span,
implicit_arg_tys,
arguments,
parameter_scope,
body))
}
fn closure_self_ty<'a,'tcx>(tcx: &ty::ctxt<'tcx>,
closure_expr_id: ast::NodeId,
body_id: ast::NodeId)
-> Ty<'tcx>
{
let closure_ty = tcx.node_id_to_type(closure_expr_id);
// We're just hard-coding the idea that the signature will be
// &self or &mut self and hence will have a bound region with
// number 0, hokey.
let region =
ty::Region::ReFree(
ty::FreeRegion {
scope: tcx.region_maps.item_extent(body_id),
bound_region: ty::BoundRegion::BrAnon(0)
});
let region =
tcx.mk_region(region);
match tcx.closure_kind(DefId::local(closure_expr_id)) {
ty::ClosureKind::FnClosureKind =>
tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutImmutable }),
ty::ClosureKind::FnMutClosureKind =>
tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutMutable }),
ty::ClosureKind::FnOnceClosureKind =>
closure_ty
}
}

View file

@ -0,0 +1,157 @@
// Copyright 2014 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.
use dot;
use hair::Hair;
use repr::*;
use std::borrow::IntoCow;
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct EdgeIndex {
source: BasicBlock,
target: BasicBlock,
index: usize,
}
impl<'a,H:Hair> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<H> {
fn graph_id(&'a self) -> dot::Id<'a> {
dot::Id::new("Mir").unwrap()
}
fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> {
dot::Id::new(format!("BB{}", n.index())).unwrap()
}
fn node_shape(&'a self, _: &BasicBlock) -> Option<dot::LabelText<'a>> {
Some(dot::LabelText::label("none"))
}
fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> {
let mut buffer = String::new();
buffer.push_str("<TABLE ALIGN=\"LEFT\">");
buffer.push_str("<TR><TD>");
buffer.push_str(&format!("{:?}", n));
buffer.push_str("</TD></TR>");
let data = self.basic_block_data(n);
for statement in &data.statements {
buffer.push_str("<TR><TD>");
buffer.push_str(&escape(format!("{:?}", statement)));
buffer.push_str("</TD></TR>");
}
buffer.push_str("<TR><TD>");
buffer.push_str(&escape(format!("{:?}", &data.terminator)));
buffer.push_str("</TD></TR>");
buffer.push_str("</TABLE>");
dot::LabelText::html(buffer)
}
fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> {
dot::LabelText::label(format!("{}", edge.index))
}
}
impl<'a,H:Hair> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<H> {
fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> {
self.all_basic_blocks().into_cow()
}
fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> {
self.all_basic_blocks()
.into_iter()
.flat_map(|source| {
self.basic_block_data(source).terminator
.successors()
.iter()
.enumerate()
.map(move |(index, &target)| {
EdgeIndex { source: source,
target: target,
index: index }
})
})
.collect::<Vec<_>>()
.into_cow()
}
fn source(&'a self, edge: &EdgeIndex) -> BasicBlock {
edge.source
}
fn target(&'a self, edge: &EdgeIndex) -> BasicBlock {
edge.target
}
}
fn escape(text: String) -> String {
let text = dot::escape_html(&text);
let text = all_to_subscript("Temp", text);
let text = all_to_subscript("Var", text);
let text = all_to_subscript("Arg", text);
let text = all_to_subscript("BB", text);
text
}
/// A call like `all_to_subscript("Temp", "Temp(123)")` will convert
/// to `Temp₁₂₃`.
fn all_to_subscript(header: &str, mut text: String) -> String {
let mut offset = 0;
while offset < text.len() {
if let Some(text1) = to_subscript1(header, &text, &mut offset) {
text = text1;
}
}
return text;
/// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts.
/// Updates `offset` to point to the next location where we might want to search.
/// Returns an updated string if changes were made, else None.
fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option<String> {
let a = match text[*offset..].find(header) {
None => { *offset = text.len(); return None; }
Some(a) => a + *offset,
};
// Example:
//
// header: "Foo"
// text: ....Foo(123)...
// ^ ^
// a b
let b = a + header.len();
*offset = b;
let mut chars = text[b..].chars();
if Some('(') != chars.next() {
return None;
}
let mut result = String::new();
result.push_str(&text[..b]);
while let Some(c) = chars.next() {
if c == ')' { break; }
if !c.is_digit(10) { return None; }
// 0x208 is _0 in unicode, 0x209 is _1, etc
const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉";
let n = (c as usize) - ('0' as usize);
result.extend(SUBSCRIPTS.chars().skip(n).take(1));
}
result.extend(chars);
return Some(result);
}
}

382
src/librustc_mir/hair.rs Normal file
View file

@ -0,0 +1,382 @@
// 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.
//! The MIR is translated from some high-level abstract IR
//! (HAIR). This section defines the HAIR along with a trait for
//! accessing it. The intention is to allow MIR construction to be
//! unit-tested and separated from the Rust source and compiler data
//! structures.
use repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp};
use std::fmt::Debug;
use std::hash::Hash;
pub trait Hair: Sized+Debug+Clone+Eq+Hash { // (*)
// (*) the `Sized` and Debug` bounds are the only ones that really
// make sense. The rest are just there so that we can
// `#[derive(Clone)]` on things that are parameterized over
// `H:HAIR`. It's kind of lame.
type VarId: Copy+Debug+Eq+Hash; // e.g., NodeId for a variable
type DefId: Copy+Debug+Eq+Hash; // e.g., DefId
type AdtDef: Copy+Debug+Eq+Hash; // e.g., AdtDef<'tcx>
type Name: Copy+Debug+Eq+Hash; // e.g., ast::Name
type Ident: Copy+Debug+Eq+Hash; // e.g., ast::Ident
type InternedString: Clone+Debug+Eq+Hash; // e.g., InternedString
type Bytes: Clone+Debug+Eq+Hash; // e.g., Rc<Vec<u8>>
type Span: Copy+Debug+Eq; // e.g., syntax::codemap::Span
type Projection: Clone+Debug+Eq; // e.g., ty::ProjectionTy<'tcx>
type Substs: Clone+Debug+Eq; // e.g., substs::Substs<'tcx>
type ClosureSubsts: Clone+Debug+Eq; // e.g., ty::ClosureSubsts<'tcx>
type Ty: Clone+Debug+Eq; // e.g., ty::Ty<'tcx>
type Region: Copy+Debug; // e.g., ty::Region
type CodeExtent: Copy+Debug+Hash+Eq; // e.g., region::CodeExtent
type Pattern: Clone+Debug+Mirror<Self,Output=Pattern<Self>>; // e.g., &P<ast::Pat>
type Expr: Clone+Debug+Mirror<Self,Output=Expr<Self>>; // e.g., &P<ast::Expr>
type Stmt: Clone+Debug+Mirror<Self,Output=Stmt<Self>>; // e.g., &P<ast::Stmt>
type Block: Clone+Debug+Mirror<Self,Output=Block<Self>>; // e.g., &P<ast::Block>
type InlineAsm: Clone+Debug+Eq+Hash; // e.g., ast::InlineAsm
/// Normalizes `ast` into the appropriate `mirror` type.
fn mirror<M:Mirror<Self>>(&mut self, ast: M) -> M::Output {
ast.make_mirror(self)
}
/// Returns the unit type `()`
fn unit_ty(&mut self) -> Self::Ty;
/// Returns the type `usize`.
fn usize_ty(&mut self) -> Self::Ty;
/// Returns the type `bool`.
fn bool_ty(&mut self) -> Self::Ty;
/// Returns a reference to `PartialEq::<T,T>::eq`
fn partial_eq(&mut self, ty: Self::Ty) -> ItemRef<Self>;
/// Returns a reference to `PartialOrd::<T,T>::le`
fn partial_le(&mut self, ty: Self::Ty) -> ItemRef<Self>;
/// Returns the number of variants for the given enum
fn num_variants(&mut self, adt: Self::AdtDef) -> usize;
fn fields(&mut self, adt: Self::AdtDef, variant_index: usize) -> Vec<Field<Self>>;
/// true if a value of type `ty` (may) need to be dropped; this
/// may return false even for non-Copy types if there is no
/// destructor to execute. If correct result is not known, may be
/// approximated by returning `true`; this will result in more
/// drops but not incorrect code.
fn needs_drop(&mut self, ty: Self::Ty, span: Self::Span) -> bool;
/// Report an internal inconsistency.
fn span_bug(&mut self, span: Self::Span, message: &str) -> !;
}
#[derive(Clone, Debug)]
pub struct ItemRef<H:Hair> {
pub ty: H::Ty,
pub def_id: H::DefId,
pub substs: H::Substs,
}
#[derive(Clone, Debug)]
pub struct Block<H:Hair> {
pub extent: H::CodeExtent,
pub span: H::Span,
pub stmts: Vec<StmtRef<H>>,
pub expr: Option<ExprRef<H>>,
}
#[derive(Clone, Debug)]
pub enum StmtRef<H:Hair> {
Hair(H::Stmt),
Mirror(Box<Stmt<H>>),
}
#[derive(Clone, Debug)]
pub struct Stmt<H:Hair> {
pub span: H::Span,
pub kind: StmtKind<H>,
}
#[derive(Clone, Debug)]
pub enum StmtKind<H:Hair> {
Expr {
/// scope for this statement; may be used as lifetime of temporaries
scope: H::CodeExtent,
/// expression being evaluated in this statement
expr: ExprRef<H>
},
Let {
/// scope for variables bound in this let; covers this and
/// remaining statements in block
remainder_scope: H::CodeExtent,
/// scope for the initialization itself; might be used as
/// lifetime of temporaries
init_scope: H::CodeExtent,
/// let <PAT> = ...
pattern: PatternRef<H>,
/// let pat = <INIT> ...
initializer: Option<ExprRef<H>>,
/// let pat = init; <STMTS>
stmts: Vec<StmtRef<H>>
},
}
// The Hair trait implementor translates their expressions (`H::Expr`)
// into instances of this `Expr` enum. This translation can be done
// basically as lazilly or as eagerly as desired: every recursive
// reference to an expression in this enum is an `ExprRef<H>`, which
// may in turn be another instance of this enum (boxed), or else an
// untranslated `H::Expr`. Note that instances of `Expr` are very
// shortlived. They are created by `Hair::to_expr`, analyzed and
// converted into MIR, and then discarded.
//
// If you compare `Expr` to the full compiler AST, you will see it is
// a good bit simpler. In fact, a number of the more straight-forward
// MIR simplifications are already done in the impl of `Hair`. For
// example, method calls and overloaded operators are absent: they are
// expected to be converted into `Expr::Call` instances.
#[derive(Clone, Debug)]
pub struct Expr<H:Hair> {
// type of this expression
pub ty: H::Ty,
// lifetime of this expression if it should be spilled into a
// temporary; should be None only if in a constant context
pub temp_lifetime: Option<H::CodeExtent>,
// span of the expression in the source
pub span: H::Span,
// kind of expression
pub kind: ExprKind<H>,
}
#[derive(Clone, Debug)]
pub enum ExprKind<H:Hair> {
Scope { extent: H::CodeExtent, value: ExprRef<H> },
Paren { arg: ExprRef<H> }, // ugh. should be able to remove this!
Box { place: Option<ExprRef<H>>, value: ExprRef<H> },
Call { fun: ExprRef<H>, args: Vec<ExprRef<H>> },
Deref { arg: ExprRef<H> }, // NOT overloaded!
Binary { op: BinOp, lhs: ExprRef<H>, rhs: ExprRef<H> }, // NOT overloaded!
LogicalOp { op: LogicalOp, lhs: ExprRef<H>, rhs: ExprRef<H> },
Unary { op: UnOp, arg: ExprRef<H> }, // NOT overloaded!
Cast { source: ExprRef<H> },
ReifyFnPointer { source: ExprRef<H> },
UnsafeFnPointer { source: ExprRef<H> },
Unsize { source: ExprRef<H> },
If { condition: ExprRef<H>, then: ExprRef<H>, otherwise: Option<ExprRef<H>> },
Loop { condition: Option<ExprRef<H>>, body: ExprRef<H>, },
Match { discriminant: ExprRef<H>, arms: Vec<Arm<H>> },
Block { body: H::Block },
Assign { lhs: ExprRef<H>, rhs: ExprRef<H> },
AssignOp { op: BinOp, lhs: ExprRef<H>, rhs: ExprRef<H> },
Field { lhs: ExprRef<H>, name: Field<H> },
Index { lhs: ExprRef<H>, index: ExprRef<H> },
VarRef { id: H::VarId },
SelfRef, // first argument, used for self in a closure
StaticRef { id: H::DefId },
Borrow { region: H::Region, borrow_kind: BorrowKind, arg: ExprRef<H> },
Break { label: Option<H::CodeExtent> },
Continue { label: Option<H::CodeExtent> },
Return { value: Option<ExprRef<H>> },
Repeat { value: ExprRef<H>, count: ExprRef<H> },
Vec { fields: Vec<ExprRef<H>> },
Tuple { fields: Vec<ExprRef<H>> },
Adt { adt_def: H::AdtDef,
variant_index: usize,
substs: H::Substs,
fields: Vec<FieldExprRef<H>>,
base: Option<ExprRef<H>> },
Closure { closure_id: H::DefId, substs: H::ClosureSubsts,
upvars: Vec<ExprRef<H>> },
Literal { literal: Literal<H> },
InlineAsm { asm: H::InlineAsm },
}
#[derive(Clone, Debug)]
pub enum ExprRef<H:Hair> {
Hair(H::Expr),
Mirror(Box<Expr<H>>),
}
#[derive(Clone, Debug)]
pub struct FieldExprRef<H:Hair> {
pub name: Field<H>,
pub expr: ExprRef<H>,
}
#[derive(Clone, Debug)]
pub struct Arm<H:Hair> {
pub patterns: Vec<PatternRef<H>>,
pub guard: Option<ExprRef<H>>,
pub body: ExprRef<H>,
}
#[derive(Clone, Debug)]
pub struct Pattern<H:Hair> {
pub ty: H::Ty,
pub span: H::Span,
pub kind: PatternKind<H>,
}
#[derive(Copy, Clone, Debug)]
pub enum LogicalOp {
And,
Or
}
#[derive(Clone, Debug)]
pub enum PatternKind<H:Hair> {
Wild,
// x, ref x, x @ P, etc
Binding { mutability: Mutability,
name: H::Ident,
mode: BindingMode<H>,
var: H::VarId,
ty: H::Ty,
subpattern: Option<PatternRef<H>> },
// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants
Variant { adt_def: H::AdtDef, variant_index: usize, subpatterns: Vec<FieldPatternRef<H>> },
// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant
Leaf { subpatterns: Vec<FieldPatternRef<H>> },
Deref { subpattern: PatternRef<H> }, // box P, &P, &mut P, etc
Constant { expr: ExprRef<H> },
Range { lo: ExprRef<H>, hi: ExprRef<H> },
// matches against a slice, checking the length and extracting elements
Slice { prefix: Vec<PatternRef<H>>,
slice: Option<PatternRef<H>>,
suffix: Vec<PatternRef<H>> },
// fixed match against an array, irrefutable
Array { prefix: Vec<PatternRef<H>>,
slice: Option<PatternRef<H>>,
suffix: Vec<PatternRef<H>> },
}
#[derive(Copy, Clone, Debug)]
pub enum BindingMode<H:Hair> {
ByValue,
ByRef(H::Region, BorrowKind),
}
#[derive(Clone, Debug)]
pub enum PatternRef<H:Hair> {
Hair(H::Pattern),
Mirror(Box<Pattern<H>>),
}
#[derive(Clone, Debug)]
pub struct FieldPatternRef<H:Hair> {
pub field: Field<H>,
pub pattern: PatternRef<H>,
}
///////////////////////////////////////////////////////////////////////////
// The Mirror trait
/// "Mirroring" is the process of converting from a Hair type into one
/// of the types in this file. For example, the mirror of a `H::Expr`
/// is an `Expr<H>`. Mirroring is the point at which the actual IR is
/// converting into the more idealized representation described in
/// this file. Mirroring is gradual: when you mirror an outer
/// expression like `e1 + e2`, the references to the inner expressions
/// `e1` and `e2` are `ExprRef<H>` instances, and they may or may not
/// be eagerly mirrored. This allows a single AST node from the
/// compiler to expand into one or more Hair nodes, which lets the Hair
/// nodes be simpler.
pub trait Mirror<H:Hair> {
type Output;
fn make_mirror(self, hir: &mut H) -> Self::Output;
}
impl<H:Hair> Mirror<H> for Expr<H> {
type Output = Expr<H>;
fn make_mirror(self, _: &mut H) -> Expr<H> {
self
}
}
impl<H:Hair> Mirror<H> for ExprRef<H> {
type Output = Expr<H>;
fn make_mirror(self, hir: &mut H) -> Expr<H> {
match self {
ExprRef::Hair(h) => h.make_mirror(hir),
ExprRef::Mirror(m) => *m,
}
}
}
impl<H:Hair> Mirror<H> for Stmt<H> {
type Output = Stmt<H>;
fn make_mirror(self, _: &mut H) -> Stmt<H> {
self
}
}
impl<H:Hair> Mirror<H> for StmtRef<H> {
type Output = Stmt<H>;
fn make_mirror(self, hir: &mut H) -> Stmt<H> {
match self {
StmtRef::Hair(h) => h.make_mirror(hir),
StmtRef::Mirror(m) => *m,
}
}
}
impl<H:Hair> Mirror<H> for Pattern<H> {
type Output = Pattern<H>;
fn make_mirror(self, _: &mut H) -> Pattern<H> {
self
}
}
impl<H:Hair> Mirror<H> for PatternRef<H> {
type Output = Pattern<H>;
fn make_mirror(self, hir: &mut H) -> Pattern<H> {
match self {
PatternRef::Hair(h) => h.make_mirror(hir),
PatternRef::Mirror(m) => *m,
}
}
}
impl<H:Hair> Mirror<H> for Block<H> {
type Output = Block<H>;
fn make_mirror(self, _: &mut H) -> Block<H> {
self
}
}

34
src/librustc_mir/lib.rs Normal file
View file

@ -0,0 +1,34 @@
// Copyright 2014 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.
/*!
Rust MIR: a lowered representation of Rust. Also: an experiment!
*/
#![crate_name = "rustc_mir"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(ref_slice)]
#![feature(rustc_private)]
#![feature(into_cow)]
#[macro_use] extern crate log;
extern crate graphviz as dot;
extern crate rustc_data_structures;
pub mod build;
pub mod dump;
pub mod hair;
pub mod repr;
mod graphviz;
mod tcx;

689
src/librustc_mir/repr.rs Normal file
View file

@ -0,0 +1,689 @@
// Copyright 2014 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.
use hair::Hair;
use rustc_data_structures::fnv::FnvHashMap;
use std::fmt::{Debug, Formatter, Error};
use std::slice;
use std::u32;
/// Lowered representation of a single function.
pub struct Mir<H:Hair> {
pub basic_blocks: Vec<BasicBlockData<H>>,
// for every node id
pub extents: FnvHashMap<H::CodeExtent, Vec<GraphExtent>>,
pub var_decls: Vec<VarDecl<H>>,
pub arg_decls: Vec<ArgDecl<H>>,
pub temp_decls: Vec<TempDecl<H>>,
}
/// where execution begins
pub const START_BLOCK: BasicBlock = BasicBlock(0);
/// where execution ends, on normal return
pub const END_BLOCK: BasicBlock = BasicBlock(1);
/// where execution ends, on panic
pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2);
impl<H:Hair> Mir<H> {
pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
(0..self.basic_blocks.len())
.map(|i| BasicBlock::new(i))
.collect()
}
pub fn basic_block_data(&self, bb: BasicBlock) -> &BasicBlockData<H> {
&self.basic_blocks[bb.index()]
}
pub fn basic_block_data_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData<H> {
&mut self.basic_blocks[bb.index()]
}
}
///////////////////////////////////////////////////////////////////////////
// Mutability and borrow kinds
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Mutability {
Mut,
Not,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
/// Data must be immutable but not aliasable. This kind of borrow
/// cannot currently be expressed by the user and is used only in
/// implicit closure bindings. It is needed when you the closure
/// is borrowing or mutating a mutable referent, e.g.:
///
/// let x: &mut isize = ...;
/// let y = || *x += 5;
///
/// If we were to try to translate this closure into a more explicit
/// form, we'd encounter an error with the code as written:
///
/// struct Env { x: & &mut isize }
/// let x: &mut isize = ...;
/// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
///
/// This is then illegal because you cannot mutate a `&mut` found
/// in an aliasable location. To solve, you'd have to translate with
/// an `&mut` borrow:
///
/// struct Env { x: & &mut isize }
/// let x: &mut isize = ...;
/// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
///
/// Now the assignment to `**env.x` is legal, but creating a
/// mutable pointer to `x` is not because `x` is not mutable. We
/// could fix this by declaring `x` as `let mut x`. This is ok in
/// user code, if awkward, but extra weird for closures, since the
/// borrow is hidden.
///
/// So we introduce a "unique imm" borrow -- the referent is
/// immutable, but not aliasable. This solves the problem. For
/// simplicity, we don't give users the way to express this
/// borrow, it's just used when translating closures.
Unique,
/// Data is mutable and not aliasable.
Mut
}
///////////////////////////////////////////////////////////////////////////
// Variables and temps
// A "variable" is a binding declared by the user as part of the fn
// decl, a let, etc.
pub struct VarDecl<H:Hair> {
pub mutability: Mutability,
pub name: H::Ident,
pub ty: H::Ty,
}
// A "temp" is a temporary that we place on the stack. They are
// anonymous, always mutable, and have only a type.
pub struct TempDecl<H:Hair> {
pub ty: H::Ty,
}
// A "arg" is one of the function's formal arguments. These are
// anonymous and distinct from the bindings that the user declares.
//
// For example, in this function:
//
// ```
// fn foo((x, y): (i32, u32)) { ... }
// ```
//
// there is only one argument, of type `(i32, u32)`, but two bindings
// (`x` and `y`).
pub struct ArgDecl<H:Hair> {
pub ty: H::Ty,
}
///////////////////////////////////////////////////////////////////////////
// Graph extents
/// A moment in the flow of execution. It corresponds to a point in
/// between two statements:
///
/// BB[block]:
/// <--- if statement == 0
/// STMT[0]
/// <--- if statement == 1
/// STMT[1]
/// ...
/// <--- if statement == n-1
/// STMT[n-1]
/// <--- if statement == n
///
/// where the block has `n` statements.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ExecutionPoint {
pub block: BasicBlock,
pub statement: u32,
}
/// A single-entry-multiple-exit region in the graph. We build one of
/// these for every node-id during MIR construction. By construction
/// we are assured that the entry dominates all points within, and
/// that, for every interior point X, it is postdominated by some exit.
pub struct GraphExtent {
pub entry: ExecutionPoint,
pub exit: GraphExtentExit,
}
pub enum GraphExtentExit {
/// `Statement(X)`: a very common special case covering a span
/// that is local to a single block. It starts at the entry point
/// and extends until the start of statement `X` (non-inclusive).
Statement(u32),
/// The more general case where the exits are a set of points.
Points(Vec<ExecutionPoint>),
}
///////////////////////////////////////////////////////////////////////////
// BasicBlock
/// The index of a particular basic block. The index is into the `basic_blocks`
/// list of the `Mir`.
///
/// (We use a `u32` internally just to save memory.)
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BasicBlock(u32);
impl BasicBlock {
pub fn new(index: usize) -> BasicBlock {
assert!(index < (u32::MAX as usize));
BasicBlock(index as u32)
}
/// Extract the index.
pub fn index(self) -> usize {
self.0 as usize
}
}
impl Debug for BasicBlock {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "BB({})", self.0)
}
}
///////////////////////////////////////////////////////////////////////////
// BasicBlock and Terminator
#[derive(Debug)]
pub struct BasicBlockData<H:Hair> {
pub statements: Vec<Statement<H>>,
pub terminator: Terminator<H>,
}
pub enum Terminator<H:Hair> {
/// block should have one successor in the graph; we jump there
Goto { target: BasicBlock },
/// block should initiate unwinding; should be one successor
/// that does cleanup and branches to DIVERGE_BLOCK
Panic { target: BasicBlock },
/// jump to branch 0 if this lvalue evaluates to true
If { cond: Operand<H>, targets: [BasicBlock; 2] },
/// lvalue evaluates to some enum; jump depending on the branch
Switch { discr: Lvalue<H>, targets: Vec<BasicBlock> },
/// Indicates that the last statement in the block panics, aborts,
/// etc. No successors. This terminator appears on exactly one
/// basic block which we create in advance. However, during
/// construction, we use this value as a sentinel for "terminator
/// not yet assigned", and assert at the end that only the
/// well-known diverging block actually diverges.
Diverge,
/// Indicates a normal return. The ReturnPointer lvalue should
/// have been filled in by now. This should only occur in the
/// `END_BLOCK`.
Return,
/// block ends with a call; it should have two successors. The
/// first successor indicates normal return. The second indicates
/// unwinding.
Call { data: CallData<H>, targets: [BasicBlock; 2] },
}
impl<H:Hair> Terminator<H> {
pub fn successors(&self) -> &[BasicBlock] {
use self::Terminator::*;
match *self {
Goto { target: ref b } => slice::ref_slice(b),
Panic { target: ref b } => slice::ref_slice(b),
If { cond: _, targets: ref b } => b,
Switch { discr: _, targets: ref b } => b,
Diverge => &[],
Return => &[],
Call { data: _, targets: ref b } => b,
}
}
}
#[derive(Debug)]
pub struct CallData<H:Hair> {
/// where the return value is written to
pub destination: Lvalue<H>,
/// the fn being called
pub func: Lvalue<H>,
/// the arguments
pub args: Vec<Lvalue<H>>,
}
impl<H:Hair> BasicBlockData<H> {
pub fn new(terminator: Terminator<H>) -> BasicBlockData<H> {
BasicBlockData {
statements: vec![],
terminator: terminator,
}
}
}
impl<H:Hair> Debug for Terminator<H> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::Terminator::*;
match *self {
Goto { target } =>
write!(fmt, "goto -> {:?}", target),
Panic { target } =>
write!(fmt, "panic -> {:?}", target),
If { cond: ref lv, ref targets } =>
write!(fmt, "if({:?}) -> {:?}", lv, targets),
Switch { discr: ref lv, ref targets } =>
write!(fmt, "switch({:?}) -> {:?}", lv, targets),
Diverge =>
write!(fmt, "diverge"),
Return =>
write!(fmt, "return"),
Call { data: ref c, targets } => {
try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func));
for (index, arg) in c.args.iter().enumerate() {
if index > 0 { try!(write!(fmt, ", ")); }
try!(write!(fmt, "{:?}", arg));
}
write!(fmt, ") -> {:?}", targets)
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Statements
pub struct Statement<H:Hair> {
pub span: H::Span,
pub kind: StatementKind<H>,
}
#[derive(Debug)]
pub enum StatementKind<H:Hair> {
Assign(Lvalue<H>, Rvalue<H>),
Drop(DropKind, Lvalue<H>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DropKind {
Shallow,
Deep
}
impl<H:Hair> Debug for Statement<H> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::StatementKind::*;
match self.kind {
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
Drop(DropKind::Shallow, ref lv) => write!(fmt, "shallow_drop {:?}", lv),
Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv),
}
}
}
///////////////////////////////////////////////////////////////////////////
// Lvalues
/// A path to a value; something that can be evaluated without
/// changing or disturbing program state.
#[derive(Clone, PartialEq)]
pub enum Lvalue<H:Hair> {
/// local variable declared by the user
Var(u32),
/// temporary introduced during lowering into MIR
Temp(u32),
/// formal parameter of the function; note that these are NOT the
/// bindings that the user declares, which are vars
Arg(u32),
/// static or static mut variable
Static(H::DefId),
/// the return pointer of the fn
ReturnPointer,
/// projection out of an lvalue (access a field, deref a pointer, etc)
Projection(Box<LvalueProjection<H>>)
}
/// The `Projection` data structure defines things of the form `B.x`
/// or `*B` or `B[index]`. Note that it is parameterized because it is
/// shared between `Constant` and `Lvalue`. See the aliases
/// `LvalueProjection` etc below.
#[derive(Clone, Debug, PartialEq)]
pub struct Projection<H:Hair,B,V> {
pub base: B,
pub elem: ProjectionElem<H,V>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ProjectionElem<H:Hair,V> {
Deref,
Field(Field<H>),
Index(V),
// These indices are generated by slice patterns. Easiest to explain
// by example:
//
// ```
// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
// ```
ConstantIndex {
offset: u32, // index or -index (in Python terms), depending on from_end
min_length: u32, // thing being indexed must be at least this long
from_end: bool, // counting backwards from end?
},
// "Downcast" to a variant of an ADT. Currently, we only introduce
// this for ADTs with more than one variant. It may be better to
// just introduce it always, or always for enums.
Downcast(H::AdtDef, usize),
}
/// Alias for projections as they appear in lvalues, where the base is an lvalue
/// and the index is an operand.
pub type LvalueProjection<H> =
Projection<H,Lvalue<H>,Operand<H>>;
/// Alias for projections as they appear in lvalues, where the base is an lvalue
/// and the index is an operand.
pub type LvalueElem<H> =
ProjectionElem<H,Operand<H>>;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Field<H:Hair> {
Named(H::Name),
Indexed(usize),
}
impl<H:Hair> Lvalue<H> {
pub fn field(self, f: Field<H>) -> Lvalue<H> {
self.elem(ProjectionElem::Field(f))
}
pub fn deref(self) -> Lvalue<H> {
self.elem(ProjectionElem::Deref)
}
pub fn index(self, index: Operand<H>) -> Lvalue<H> {
self.elem(ProjectionElem::Index(index))
}
pub fn elem(self, elem: LvalueElem<H>) -> Lvalue<H> {
Lvalue::Projection(Box::new(LvalueProjection { base: self, elem: elem }))
}
}
impl<H:Hair> Debug for Lvalue<H> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::Lvalue::*;
match *self {
Var(id) =>
write!(fmt,"Var({:?})", id),
Arg(id) =>
write!(fmt,"Arg({:?})", id),
Temp(id) =>
write!(fmt,"Temp({:?})", id),
Static(id) =>
write!(fmt,"Static({:?})", id),
ReturnPointer =>
write!(fmt,"ReturnPointer"),
Projection(ref data) =>
match data.elem {
ProjectionElem::Downcast(_, variant_index) =>
write!(fmt,"({:?} as {:?})", data.base, variant_index),
ProjectionElem::Deref =>
write!(fmt,"(*{:?})", data.base),
ProjectionElem::Field(Field::Named(name)) =>
write!(fmt,"{:?}.{:?}", data.base, name),
ProjectionElem::Field(Field::Indexed(index)) =>
write!(fmt,"{:?}.{:?}", data.base, index),
ProjectionElem::Index(ref index) =>
write!(fmt,"{:?}[{:?}]", data.base, index),
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
write!(fmt,"{:?}[{:?} of {:?}]", data.base, offset, min_length),
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
write!(fmt,"{:?}[-{:?} of {:?}]", data.base, offset, min_length),
},
}
}
}
///////////////////////////////////////////////////////////////////////////
// Operands
//
// These are values that can appear inside an rvalue (or an index
// lvalue). They are intentionally limited to prevent rvalues from
// being nested in one another.
#[derive(Clone, PartialEq)]
pub enum Operand<H:Hair> {
Consume(Lvalue<H>),
Constant(Constant<H>),
}
impl<H:Hair> Debug for Operand<H> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::Operand::*;
match *self {
Constant(ref a) => write!(fmt, "{:?}", a),
Consume(ref lv) => write!(fmt, "{:?}", lv),
}
}
}
///////////////////////////////////////////////////////////////////////////
// Rvalues
#[derive(Clone)]
pub enum Rvalue<H:Hair> {
// x (either a move or copy, depending on type of x)
Use(Operand<H>),
// [x; 32]
Repeat(Operand<H>, Operand<H>),
// &x or &mut x
Ref(H::Region, BorrowKind, Lvalue<H>),
// length of a [X] or [X;n] value
Len(Lvalue<H>),
Cast(CastKind, Operand<H>, H::Ty),
BinaryOp(BinOp, Operand<H>, Operand<H>),
UnaryOp(UnOp, Operand<H>),
// Creates an *uninitialized* Box
Box(H::Ty),
// Create an aggregate value, like a tuple or struct. This is
// only needed because we want to distinguish `dest = Foo { x:
// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case
// that `Foo` has a destructor. These rvalues can be optimized
// away after type-checking and before lowering.
Aggregate(AggregateKind<H>, Vec<Operand<H>>),
// Generates a slice of the form `&input[from_start..L-from_end]`
// where `L` is the length of the slice. This is only created by
// slice pattern matching, so e.g. a pattern of the form `[x, y,
// .., z]` might create a slice with `from_start=2` and
// `from_end=1`.
Slice {
input: Lvalue<H>,
from_start: usize,
from_end: usize,
},
InlineAsm(H::InlineAsm),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CastKind {
Misc,
/// Convert unique, zero-sized type for a fn to fn()
ReifyFnPointer,
/// Convert safe fn() to unsafe fn()
UnsafeFnPointer,
/// "Unsize" -- convert a thin-or-fat pointer to a fat pointer.
/// trans must figure out the details once full monomorphization
/// is known. For example, this could be used to cast from a
/// `&[i32;N]` to a `&[i32]`, or a `Box<T>` to a `Box<Trait>`
/// (presuming `T: Trait`).
Unsize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AggregateKind<H:Hair> {
Vec,
Tuple,
Adt(H::AdtDef, usize, H::Substs),
Closure(H::DefId, H::ClosureSubsts),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BinOp {
/// The `+` operator (addition)
Add,
/// The `-` operator (subtraction)
Sub,
/// The `*` operator (multiplication)
Mul,
/// The `/` operator (division)
Div,
/// The `%` operator (modulus)
Rem,
/// The `^` operator (bitwise xor)
BitXor,
/// The `&` operator (bitwise and)
BitAnd,
/// The `|` operator (bitwise or)
BitOr,
/// The `<<` operator (shift left)
Shl,
/// The `>>` operator (shift right)
Shr,
/// The `==` operator (equality)
Eq,
/// The `<` operator (less than)
Lt,
/// The `<=` operator (less than or equal to)
Le,
/// The `!=` operator (not equal to)
Ne,
/// The `>=` operator (greater than or equal to)
Ge,
/// The `>` operator (greater than)
Gt,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum UnOp {
/// The `!` operator for logical inversion
Not,
/// The `-` operator for negation
Neg
}
impl<H:Hair> Debug for Rvalue<H> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::Rvalue::*;
match *self {
Use(ref lvalue) => write!(fmt, "{:?}", lvalue),
Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b),
Ref(ref a, bk, ref b) => write!(fmt, "&{:?} {:?} {:?}", a, bk, b),
Len(ref a) => write!(fmt, "LEN({:?})", a),
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?}", lv, ty, kind),
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?},{:?})", op, a, b),
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
Box(ref t) => write!(fmt, "Box {:?}", t),
Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>({:?})", kind, lvs),
InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm),
Slice { ref input, from_start, from_end } => write!(fmt, "{:?}[{:?}..-{:?}]",
input, from_start, from_end),
}
}
}
///////////////////////////////////////////////////////////////////////////
// Constants
#[derive(Clone, Debug, PartialEq)]
pub struct Constant<H:Hair> {
pub span: H::Span,
pub kind: ConstantKind<H>
}
#[derive(Clone, Debug, PartialEq)]
pub enum ConstantKind<H:Hair> {
Literal(Literal<H>),
Aggregate(AggregateKind<H>, Vec<Constant<H>>),
Call(Box<Constant<H>>, Vec<Constant<H>>),
Cast(Box<Constant<H>>, H::Ty),
Repeat(Box<Constant<H>>, Box<Constant<H>>),
Ref(BorrowKind, Box<Constant<H>>),
BinaryOp(BinOp, Box<Constant<H>>, Box<Constant<H>>),
UnaryOp(UnOp, Box<Constant<H>>),
Projection(Box<ConstantProjection<H>>)
}
pub type ConstantProjection<H> =
Projection<H,Constant<H>,Constant<H>>;
#[derive(Clone, Debug, PartialEq)]
pub enum Literal<H:Hair> {
Item { def_id: H::DefId, substs: H::Substs },
Projection { projection: H::Projection },
Int { bits: IntegralBits, value: i64 },
Uint { bits: IntegralBits, value: u64 },
Float { bits: FloatBits, value: f64 },
Char { c: char },
Bool { value: bool },
Bytes { value: H::Bytes },
String { value: H::InternedString },
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub enum IntegralBits {
B8, B16, B32, B64, BSize
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub enum FloatBits {
F32, F64
}

View file

@ -0,0 +1,114 @@
// 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.
use hair::*;
use tcx::Cx;
use tcx::pattern::PatNode;
use tcx::rustc::middle::region::{BlockRemainder, CodeExtentData};
use tcx::rustc_front::hir;
use tcx::syntax::ast;
use tcx::syntax::ptr::P;
use tcx::to_ref::ToRef;
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Block {
type Output = Block<Cx<'a,'tcx>>;
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Block<Cx<'a,'tcx>> {
// We have to eagerly translate the "spine" of the statements
// in order to get the lexical scoping correctly.
let stmts = mirror_stmts(cx, self.id, self.stmts.iter().enumerate());
Block {
extent: cx.tcx.region_maps.node_extent(self.id),
span: self.span,
stmts: stmts,
expr: self.expr.to_ref()
}
}
}
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Stmt {
type Output = Stmt<Cx<'a,'tcx>>;
fn make_mirror(self, _cx: &mut Cx<'a,'tcx>) -> Stmt<Cx<'a,'tcx>> {
// In order to get the scoping correct, we eagerly mirror
// statements when we translate the enclosing block, so we
// should in fact never get to this point.
panic!("statements are eagerly mirrored");
}
}
fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
block_id: ast::NodeId,
mut stmts: STMTS)
-> Vec<StmtRef<Cx<'a,'tcx>>>
where STMTS: Iterator<Item=(usize, &'tcx P<hir::Stmt>)>
{
let mut result = vec![];
while let Some((index, stmt)) = stmts.next() {
match stmt.node {
hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) =>
result.push(
StmtRef::Mirror(
Box::new(Stmt { span: stmt.span,
kind: StmtKind::Expr {
scope: cx.tcx.region_maps.node_extent(id),
expr: expr.to_ref() } }))),
hir::StmtDecl(ref decl, id) => {
match decl.node {
hir::DeclItem(..) => { /* ignore for purposes of the MIR */ }
hir::DeclLocal(ref local) => {
let remainder_extent = CodeExtentData::Remainder(BlockRemainder {
block: block_id,
first_statement_index: index as u32
});
let remainder_extent =
cx.tcx.region_maps.lookup_code_extent(remainder_extent);
// pull in all following statements, since
// they are within the scope of this let:
let following_stmts = mirror_stmts(cx, block_id, stmts);
result.push(
StmtRef::Mirror(
Box::new(Stmt {
span: stmt.span,
kind: StmtKind::Let {
remainder_scope: remainder_extent,
init_scope: cx.tcx.region_maps.node_extent(id),
pattern: PatNode::irrefutable(&local.pat).to_ref(),
initializer: local.init.to_ref(),
stmts: following_stmts
}
})));
return result;
}
}
}
}
}
return result;
}
pub fn to_expr_ref<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
block: &'tcx hir::Block)
-> ExprRef<Cx<'a, 'tcx>> {
let block_ty = cx.tcx.node_id_to_type(block.id);
let temp_lifetime = cx.tcx.region_maps.temporary_scope(block.id);
let expr = Expr {
ty: block_ty,
temp_lifetime: temp_lifetime,
span: block.span,
kind: ExprKind::Block { body: block }
};
expr.to_ref()
}

View file

@ -0,0 +1,870 @@
// 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.
use hair::*;
use repr::*;
use rustc_data_structures::fnv::FnvHashMap;
use std::rc::Rc;
use tcx::Cx;
use tcx::block;
use tcx::pattern::PatNode;
use tcx::rustc::front::map;
use tcx::rustc::middle::def;
use tcx::rustc::middle::def_id::DefId;
use tcx::rustc::middle::region::CodeExtent;
use tcx::rustc::middle::pat_util;
use tcx::rustc::middle::ty::{self, Ty};
use tcx::rustc_front::hir;
use tcx::rustc_front::util as hir_util;
use tcx::syntax::codemap::Span;
use tcx::syntax::parse::token;
use tcx::syntax::ptr::P;
use tcx::to_ref::ToRef;
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Expr {
type Output = Expr<Cx<'a,'tcx>>;
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Expr<Cx<'a,'tcx>> {
debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)!
let kind = match self.node {
// Here comes the interesting stuff:
hir::ExprMethodCall(_, _, ref args) => {
// Rewrite a.b(c) into UFCS form like Trait::b(a, c)
let expr = method_callee(cx, self, ty::MethodCall::expr(self.id));
let args = args.iter()
.map(|e| e.to_ref())
.collect();
ExprKind::Call {
fun: expr.to_ref(),
args: args
}
}
hir::ExprAddrOf(mutbl, ref expr) => {
let region = match expr_ty.sty {
ty::TyRef(r, _) => r,
_ => cx.tcx.sess.span_bug(expr.span, "type of & not region")
};
ExprKind::Borrow { region: *region,
borrow_kind: to_borrow_kind(mutbl),
arg: expr.to_ref() }
}
hir::ExprBlock(ref blk) => {
ExprKind::Block {
body: &**blk
}
}
hir::ExprAssign(ref lhs, ref rhs) => {
ExprKind::Assign {
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
hir::ExprAssignOp(op, ref lhs, ref rhs) => {
let op = bin_op(op.node);
ExprKind::AssignOp {
op: op,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
hir::ExprLit(ref lit) => {
let literal = convert_literal(cx, self.span, expr_ty, lit);
ExprKind::Literal { literal: literal }
}
hir::ExprBinary(op, ref lhs, ref rhs) => {
if cx.tcx.is_method_call(self.id) {
let pass_args = if hir_util::is_by_value_binop(op.node) {
PassArgs::ByValue
} else {
PassArgs::ByRef
};
overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
pass_args, lhs.to_ref(), vec![rhs])
} else {
// FIXME overflow
match op.node {
hir::BinOp_::BiAnd => {
ExprKind::LogicalOp { op: LogicalOp::And,
lhs: lhs.to_ref(),
rhs: rhs.to_ref() }
}
hir::BinOp_::BiOr => {
ExprKind::LogicalOp { op: LogicalOp::Or,
lhs: lhs.to_ref(),
rhs: rhs.to_ref() }
}
_ => {
let op = bin_op(op.node);
ExprKind::Binary { op: op,
lhs: lhs.to_ref(),
rhs: rhs.to_ref() }
}
}
}
}
hir::ExprIndex(ref lhs, ref index) => {
if cx.tcx.is_method_call(self.id) {
overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id),
PassArgs::ByValue, lhs.to_ref(), vec![index])
} else {
ExprKind::Index { lhs: lhs.to_ref(),
index: index.to_ref() }
}
}
hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => {
if cx.tcx.is_method_call(self.id) {
overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id),
PassArgs::ByValue, arg.to_ref(), vec![])
} else {
ExprKind::Deref { arg: arg.to_ref() }
}
}
hir::ExprUnary(hir::UnOp::UnUniq, ref arg) => {
assert!(!cx.tcx.is_method_call(self.id));
ExprKind::Box { place: None, value: arg.to_ref() }
}
hir::ExprUnary(op, ref arg) => {
if cx.tcx.is_method_call(self.id) {
overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
PassArgs::ByValue, arg.to_ref(), vec![])
} else {
// FIXME overflow
let op = match op {
hir::UnOp::UnNot => UnOp::Not,
hir::UnOp::UnNeg => UnOp::Neg,
hir::UnOp::UnUniq | hir::UnOp::UnDeref => {
cx.tcx.sess.span_bug(
self.span,
&format!("operator should have been handled elsewhere {:?}", op));
}
};
ExprKind::Unary { op: op, arg: arg.to_ref() }
}
}
hir::ExprStruct(_, ref fields, ref base) => {
match expr_ty.sty {
ty::TyStruct(adt, substs) => {
ExprKind::Adt {
adt_def: adt,
variant_index: 0,
substs: substs,
fields: fields.to_ref(),
base: base.to_ref(),
}
}
ty::TyEnum(adt, substs) => {
match cx.tcx.def_map.borrow()[&self.id].full_def() {
def::DefVariant(enum_id, variant_id, true) => {
debug_assert!(adt.did == enum_id);
let index = adt.variant_index_with_id(variant_id);
ExprKind::Adt {
adt_def: adt,
variant_index: index,
substs: substs,
fields: fields.to_ref(),
base: base.to_ref(),
}
}
ref def => {
cx.tcx.sess.span_bug(
self.span,
&format!("unexpected def: {:?}", def));
}
}
}
_ => {
cx.tcx.sess.span_bug(
self.span,
&format!("unexpected type for struct literal: {:?}", expr_ty));
}
}
}
hir::ExprClosure(..) => {
let closure_ty = cx.tcx.expr_ty(self);
let (def_id, substs) = match closure_ty.sty {
ty::TyClosure(def_id, ref substs) => (def_id, substs),
_ => {
cx.tcx.sess.span_bug(self.span,
&format!("closure expr w/o closure type: {:?}",
closure_ty));
}
};
let upvars = cx.tcx.with_freevars(self.id, |freevars| {
freevars.iter()
.enumerate()
.map(|(i, fv)| capture_freevar(cx, self, fv, substs.upvar_tys[i]))
.collect()
});
ExprKind::Closure {
closure_id: def_id,
substs: &**substs,
upvars: upvars,
}
}
hir::ExprRange(ref start, ref end) => {
let range_ty = cx.tcx.expr_ty(self);
let (adt_def, substs) = match range_ty.sty {
ty::TyStruct(adt_def, substs) => (adt_def, substs),
_ => {
cx.tcx.sess.span_bug(
self.span,
&format!("unexpanded ast"));
}
};
let field_expr_ref = |s: &'tcx P<hir::Expr>, nm: &str| {
FieldExprRef { name: Field::Named(token::intern(nm)),
expr: s.to_ref() }
};
let start_field = start.as_ref()
.into_iter()
.map(|s| field_expr_ref(s, "start"));
let end_field = end.as_ref()
.into_iter()
.map(|e| field_expr_ref(e, "end"));
ExprKind::Adt { adt_def: adt_def,
variant_index: 0,
substs: substs,
fields: start_field.chain(end_field).collect(),
base: None }
}
hir::ExprPath(..) => {
convert_path_expr(cx, self)
}
hir::ExprInlineAsm(ref asm) => {
ExprKind::InlineAsm { asm: asm }
}
// Now comes the rote stuff:
hir::ExprParen(ref p) =>
ExprKind::Paren { arg: p.to_ref() },
hir::ExprRepeat(ref v, ref c) =>
ExprKind::Repeat { value: v.to_ref(), count: c.to_ref() },
hir::ExprRet(ref v) =>
ExprKind::Return { value: v.to_ref() },
hir::ExprBreak(label) =>
ExprKind::Break { label: label.map(|_| loop_label(cx, self)) },
hir::ExprAgain(label) =>
ExprKind::Continue { label: label.map(|_| loop_label(cx, self)) },
hir::ExprMatch(ref discr, ref arms, _) =>
ExprKind::Match { discriminant: discr.to_ref(),
arms: arms.iter().map(|a| convert_arm(cx, a)).collect() },
hir::ExprIf(ref cond, ref then, ref otherwise) =>
ExprKind::If { condition: cond.to_ref(),
then: block::to_expr_ref(cx, then),
otherwise: otherwise.to_ref() },
hir::ExprWhile(ref cond, ref body, _) =>
ExprKind::Loop { condition: Some(cond.to_ref()),
body: block::to_expr_ref(cx, body) },
hir::ExprLoop(ref body, _) =>
ExprKind::Loop { condition: None,
body: block::to_expr_ref(cx, body) },
hir::ExprField(ref source, ident) =>
ExprKind::Field { lhs: source.to_ref(),
name: Field::Named(ident.node.name) },
hir::ExprTupField(ref source, ident) =>
ExprKind::Field { lhs: source.to_ref(),
name: Field::Indexed(ident.node) },
hir::ExprCast(ref source, _) =>
ExprKind::Cast { source: source.to_ref() },
hir::ExprBox(ref place, ref value) =>
ExprKind::Box { place: place.to_ref(), value: value.to_ref() },
hir::ExprVec(ref fields) =>
ExprKind::Vec { fields: fields.to_ref() },
hir::ExprTup(ref fields) =>
ExprKind::Tuple { fields: fields.to_ref() },
hir::ExprCall(ref fun, ref args) =>
ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() },
};
let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id);
let expr_extent = cx.tcx.region_maps.node_extent(self.id);
let mut expr = Expr {
temp_lifetime: temp_lifetime,
ty: expr_ty,
span: self.span,
kind: kind,
};
// Now apply adjustments, if any.
match cx.tcx.tables.borrow().adjustments.get(&self.id) {
None => { }
Some(&ty::AdjustReifyFnPointer) => {
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
expr = Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: self.span,
kind: ExprKind::ReifyFnPointer { source: expr.to_ref() },
};
}
Some(&ty::AdjustUnsafeFnPointer) => {
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
expr = Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: self.span,
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
};
}
Some(&ty::AdjustDerefRef(ref adj)) => {
for i in 0..adj.autoderefs {
let i = i as u32;
let adjusted_ty =
expr.ty.adjust_for_autoderef(
cx.tcx,
self.id,
self.span,
i,
|mc| cx.tcx.tables.borrow().method_map.get(&mc).map(|m| m.ty));
let kind = if cx.tcx.is_overloaded_autoderef(self.id, i) {
overloaded_lvalue(cx, self, ty::MethodCall::autoderef(self.id, i),
PassArgs::ByValue, expr.to_ref(), vec![])
} else {
ExprKind::Deref { arg: expr.to_ref() }
};
expr = Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: self.span,
kind: kind
};
}
if let Some(target) = adj.unsize {
expr = Expr {
temp_lifetime: temp_lifetime,
ty: target,
span: self.span,
kind: ExprKind::Unsize { source: expr.to_ref() }
};
} else if let Some(autoref) = adj.autoref {
let adjusted_ty = expr.ty.adjust_for_autoref(cx.tcx, Some(autoref));
match autoref {
ty::AutoPtr(r, m) => {
expr = Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: self.span,
kind: ExprKind::Borrow { region: *r,
borrow_kind: to_borrow_kind(m),
arg: expr.to_ref() }
};
}
ty::AutoUnsafe(m) => {
// Convert this to a suitable `&foo` and
// then an unsafe coercion. Limit the region to be just this
// expression.
let region = ty::ReScope(expr_extent);
let region = cx.tcx.mk_region(region);
expr = Expr {
temp_lifetime: temp_lifetime,
ty: cx.tcx.mk_ref(region, ty::TypeAndMut { ty: expr.ty, mutbl: m }),
span: self.span,
kind: ExprKind::Borrow { region: *region,
borrow_kind: to_borrow_kind(m),
arg: expr.to_ref() }
};
expr = Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: self.span,
kind: ExprKind::Cast { source: expr.to_ref() }
};
}
}
}
}
}
// Next, wrap this up in the expr's scope.
expr = Expr {
temp_lifetime: temp_lifetime,
ty: expr.ty,
span: self.span,
kind: ExprKind::Scope { extent: expr_extent,
value: expr.to_ref() }
};
// Finally, create a destruction scope, if any.
if let Some(extent) = cx.tcx.region_maps.opt_destruction_extent(self.id) {
expr = Expr {
temp_lifetime: temp_lifetime,
ty: expr.ty,
span: self.span,
kind: ExprKind::Scope { extent: extent, value: expr.to_ref() }
};
}
// OK, all done!
expr
}
}
fn method_callee<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &hir::Expr,
method_call: ty::MethodCall)
-> Expr<Cx<'a,'tcx>> {
let tables = cx.tcx.tables.borrow();
let callee = &tables.method_map[&method_call];
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
Expr {
temp_lifetime: temp_lifetime,
ty: callee.ty,
span: expr.span,
kind: ExprKind::Literal {
literal: Literal::Item {
def_id: callee.def_id,
substs: callee.substs,
}
}
}
}
fn to_borrow_kind(m: hir::Mutability) -> BorrowKind {
match m {
hir::MutMutable => BorrowKind::Mut,
hir::MutImmutable => BorrowKind::Shared,
}
}
fn convert_literal<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr_span: Span,
expr_ty: Ty<'tcx>,
literal: &hir::Lit)
-> Literal<Cx<'a,'tcx>>
{
use repr::IntegralBits::*;
match (&literal.node, &expr_ty.sty) {
(&hir::LitStr(ref text, _), _) =>
Literal::String { value: text.clone() },
(&hir::LitByteStr(ref bytes), _) =>
Literal::Bytes { value: bytes.clone() },
(&hir::LitByte(c), _) =>
Literal::Uint { bits: B8, value: c as u64 },
(&hir::LitChar(c), _) =>
Literal::Char { c: c },
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU8)) =>
Literal::Uint { bits: B8, value: v },
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU16)) =>
Literal::Uint { bits: B16, value: v },
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU32)) =>
Literal::Uint { bits: B32, value: v },
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU64)) =>
Literal::Uint { bits: B64, value: v },
(&hir::LitInt(v, _), &ty::TyUint(hir::TyUs)) =>
Literal::Uint { bits: BSize, value: v },
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI8)) =>
Literal::Int { bits: B8, value: -(v as i64) },
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI16)) =>
Literal::Int { bits: B16, value: -(v as i64) },
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI32)) =>
Literal::Int { bits: B32, value: -(v as i64) },
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI64)) =>
Literal::Int { bits: B64, value: -(v as i64) },
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyIs)) =>
Literal::Int { bits: BSize, value: -(v as i64) },
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI8)) =>
Literal::Int { bits: B8, value: v as i64 },
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI16)) =>
Literal::Int { bits: B16, value: v as i64 },
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI32)) =>
Literal::Int { bits: B32, value: v as i64 },
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI64)) =>
Literal::Int { bits: B64, value: v as i64 },
(&hir::LitInt(v, _), &ty::TyInt(hir::TyIs)) =>
Literal::Int { bits: BSize, value: v as i64 },
(&hir::LitFloat(ref v, _), &ty::TyFloat(hir::TyF32)) |
(&hir::LitFloatUnsuffixed(ref v), &ty::TyFloat(hir::TyF32)) =>
Literal::Float { bits: FloatBits::F32, value: v.parse::<f64>().unwrap() },
(&hir::LitFloat(ref v, _), &ty::TyFloat(hir::TyF64)) |
(&hir::LitFloatUnsuffixed(ref v), &ty::TyFloat(hir::TyF64)) =>
Literal::Float { bits: FloatBits::F64, value: v.parse::<f64>().unwrap() },
(&hir::LitBool(v), _) =>
Literal::Bool { value: v },
(ref l, ref t) =>
cx.tcx.sess.span_bug(
expr_span,
&format!("Invalid literal/type combination: {:?},{:?}", l, t))
}
}
fn convert_arm<'a,'tcx:'a>(cx: &Cx<'a,'tcx>, arm: &'tcx hir::Arm) -> Arm<Cx<'a,'tcx>> {
let map = if arm.pats.len() == 1 {
None
} else {
let mut map = FnvHashMap();
pat_util::pat_bindings(&cx.tcx.def_map, &arm.pats[0], |_, p_id, _, path| {
map.insert(path.node, p_id);
});
Some(Rc::new(map))
};
Arm { patterns: arm.pats.iter().map(|p| PatNode::new(p, map.clone()).to_ref()).collect(),
guard: arm.guard.to_ref(),
body: arm.body.to_ref() }
}
fn convert_path_expr<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &'tcx hir::Expr)
-> ExprKind<Cx<'a,'tcx>>
{
let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs);
match cx.tcx.def_map.borrow()[&expr.id].full_def() {
def::DefVariant(_, def_id, false) |
def::DefStruct(def_id) |
def::DefFn(def_id, _) |
def::DefConst(def_id) |
def::DefMethod(def_id) |
def::DefAssociatedConst(def_id) =>
ExprKind::Literal {
literal: Literal::Item { def_id: def_id, substs: substs }
},
def::DefStatic(node_id, _) =>
ExprKind::StaticRef {
id: node_id,
},
def @ def::DefLocal(..) |
def @ def::DefUpvar(..) =>
convert_var(cx, expr, def),
def =>
cx.tcx.sess.span_bug(
expr.span,
&format!("def `{:?}` not yet implemented", def)),
}
}
fn convert_var<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &'tcx hir::Expr,
def: def::Def)
-> ExprKind<Cx<'a,'tcx>>
{
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
match def {
def::DefLocal(node_id) => {
ExprKind::VarRef {
id: node_id,
}
}
def::DefUpvar(id_var, index, closure_expr_id) => {
debug!("convert_var(upvar({:?}, {:?}, {:?}))", id_var, index, closure_expr_id);
let var_ty = cx.tcx.node_id_to_type(id_var);
let body_id = match cx.tcx.map.find(closure_expr_id) {
Some(map::NodeExpr(expr)) => {
match expr.node {
hir::ExprClosure(_, _, ref body) => body.id,
_ => {
cx.tcx.sess.span_bug(expr.span,
&format!("closure expr is not a closure expr"));
}
}
}
_ => {
cx.tcx.sess.span_bug(expr.span,
&format!("ast-map has garbage for closure expr"));
}
};
// FIXME free regions in closures are not right
let closure_ty =
cx.tcx.node_id_to_type(closure_expr_id);
// FIXME we're just hard-coding the idea that the
// signature will be &self or &mut self and hence will
// have a bound region with number 0
let region =
ty::Region::ReFree(
ty::FreeRegion {
scope: cx.tcx.region_maps.node_extent(body_id),
bound_region: ty::BoundRegion::BrAnon(0)
});
let region =
cx.tcx.mk_region(region);
let self_expr = match cx.tcx.closure_kind(DefId::local(closure_expr_id)) {
ty::ClosureKind::FnClosureKind => {
let ref_closure_ty =
cx.tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutImmutable });
Expr {
ty: closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::Deref {
arg: Expr {
ty: ref_closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef
}.to_ref()
}
}
}
ty::ClosureKind::FnMutClosureKind => {
let ref_closure_ty =
cx.tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutMutable });
Expr {
ty: closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::Deref {
arg: Expr {
ty: ref_closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef
}.to_ref()
}
}
}
ty::ClosureKind::FnOnceClosureKind => {
Expr {
ty: closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef
}
}
};
// at this point we have `self.n`, which loads up the upvar
let field_kind =
ExprKind::Field { lhs: self_expr.to_ref(),
name: Field::Indexed(index) };
// ...but the upvar might be an `&T` or `&mut T` capture, at which
// point we need an implicit deref
let upvar_id = ty::UpvarId { var_id: id_var, closure_expr_id: closure_expr_id };
let upvar_capture = match cx.tcx.upvar_capture(upvar_id) {
Some(c) => c,
None => {
cx.tcx.sess.span_bug(
expr.span,
&format!("no upvar_capture for {:?}", upvar_id));
}
};
match upvar_capture {
ty::UpvarCapture::ByValue => field_kind,
ty::UpvarCapture::ByRef(_) => {
ExprKind::Deref {
arg: Expr {
temp_lifetime: temp_lifetime,
ty: var_ty,
span: expr.span,
kind: field_kind,
}.to_ref()
}
}
}
}
_ => cx.tcx.sess.span_bug(expr.span, "type of & not region")
}
}
fn bin_op(op: hir::BinOp_) -> BinOp {
match op {
hir::BinOp_::BiAdd => BinOp::Add,
hir::BinOp_::BiSub => BinOp::Sub,
hir::BinOp_::BiMul => BinOp::Mul,
hir::BinOp_::BiDiv => BinOp::Div,
hir::BinOp_::BiRem => BinOp::Rem,
hir::BinOp_::BiBitXor => BinOp::BitXor,
hir::BinOp_::BiBitAnd => BinOp::BitAnd,
hir::BinOp_::BiBitOr => BinOp::BitOr,
hir::BinOp_::BiShl => BinOp::Shl,
hir::BinOp_::BiShr => BinOp::Shr,
hir::BinOp_::BiEq => BinOp::Eq,
hir::BinOp_::BiLt => BinOp::Lt,
hir::BinOp_::BiLe => BinOp::Le,
hir::BinOp_::BiNe => BinOp::Ne,
hir::BinOp_::BiGe => BinOp::Ge,
hir::BinOp_::BiGt => BinOp::Gt,
_ => panic!("no equivalent for ast binop {:?}", op)
}
}
enum PassArgs {
ByValue,
ByRef
}
fn overloaded_operator<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &'tcx hir::Expr,
method_call: ty::MethodCall,
pass_args: PassArgs,
receiver: ExprRef<Cx<'a,'tcx>>,
args: Vec<&'tcx P<hir::Expr>>)
-> ExprKind<Cx<'a,'tcx>>
{
// the receiver has all the adjustments that are needed, so we can
// just push a reference to it
let mut argrefs = vec![receiver];
// the arguments, unfortunately, do not, so if this is a ByRef
// operator, we have to gin up the autorefs (but by value is easy)
match pass_args {
PassArgs::ByValue => {
argrefs.extend(
args.iter()
.map(|arg| arg.to_ref()))
}
PassArgs::ByRef => {
let scope = cx.tcx.region_maps.node_extent(expr.id);
let region = cx.tcx.mk_region(ty::ReScope(scope));
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
argrefs.extend(
args.iter()
.map(|arg| {
let arg_ty = cx.tcx.expr_ty_adjusted(arg);
let adjusted_ty =
cx.tcx.mk_ref(region,
ty::TypeAndMut { ty: arg_ty,
mutbl: hir::MutImmutable });
Expr {
temp_lifetime: temp_lifetime,
ty: adjusted_ty,
span: expr.span,
kind: ExprKind::Borrow { region: *region,
borrow_kind: BorrowKind::Shared,
arg: arg.to_ref() }
}.to_ref()
}))
}
}
// now create the call itself
let fun = method_callee(cx, expr, method_call);
ExprKind::Call {
fun: fun.to_ref(),
args: argrefs,
}
}
fn overloaded_lvalue<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &'tcx hir::Expr,
method_call: ty::MethodCall,
pass_args: PassArgs,
receiver: ExprRef<Cx<'a,'tcx>>,
args: Vec<&'tcx P<hir::Expr>>)
-> ExprKind<Cx<'a,'tcx>>
{
// For an overloaded *x or x[y] expression of type T, the method
// call returns an &T and we must add the deref so that the types
// line up (this is because `*x` and `x[y]` represent lvalues):
// to find the type &T of the content returned by the method;
let tables = cx.tcx.tables.borrow();
let callee = &tables.method_map[&method_call];
let ref_ty = callee.ty.fn_ret();
let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap().unwrap();
// 1~~~~~ 2~~~~~
// (1) callees always have all late-bound regions fully instantiated,
// (2) overloaded methods don't return `!`
// construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
let ref_kind = overloaded_operator(cx, expr, method_call, pass_args, receiver, args);
let ref_expr = Expr {
temp_lifetime: temp_lifetime,
ty: ref_ty,
span: expr.span,
kind: ref_kind,
};
// construct and return a deref wrapper `*foo()`
ExprKind::Deref { arg: ref_expr.to_ref() }
}
fn capture_freevar<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
closure_expr: &'tcx hir::Expr,
freevar: &ty::Freevar,
freevar_ty: Ty<'tcx>)
-> ExprRef<Cx<'a,'tcx>> {
let id_var = freevar.def.def_id().node;
let upvar_id = ty::UpvarId { var_id: id_var, closure_expr_id: closure_expr.id };
let upvar_capture = cx.tcx.upvar_capture(upvar_id).unwrap();
let temp_lifetime = cx.tcx.region_maps.temporary_scope(closure_expr.id);
let var_ty = cx.tcx.node_id_to_type(id_var);
let captured_var = Expr { temp_lifetime: temp_lifetime,
ty: var_ty,
span: closure_expr.span,
kind: convert_var(cx, closure_expr, freevar.def) };
match upvar_capture {
ty::UpvarCapture::ByValue => {
captured_var.to_ref()
}
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow.kind {
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
ty::BorrowKind::MutBorrow => BorrowKind::Mut,
};
Expr {
temp_lifetime: temp_lifetime,
ty: freevar_ty,
span: closure_expr.span,
kind: ExprKind::Borrow { region: upvar_borrow.region,
borrow_kind: borrow_kind,
arg: captured_var.to_ref() }
}.to_ref()
}
}
}
fn loop_label<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
expr: &'tcx hir::Expr)
-> CodeExtent
{
match cx.tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
Some(def::DefLabel(loop_id)) => cx.tcx.region_maps.node_extent(loop_id),
d => {
cx.tcx.sess.span_bug(
expr.span,
&format!("loop scope resolved to {:?}", d));
}
}
}

184
src/librustc_mir/tcx/mod.rs Normal file
View file

@ -0,0 +1,184 @@
// 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.
use hair::*;
use repr::*;
use std::fmt::{Debug, Formatter, Error};
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use self::rustc::middle::def_id::DefId;
use self::rustc::middle::infer::InferCtxt;
use self::rustc::middle::region::CodeExtent;
use self::rustc::middle::subst::{self, Subst, Substs};
use self::rustc::middle::ty::{self, Ty};
use self::rustc_front::hir;
use self::syntax::ast;
use self::syntax::codemap::Span;
use self::syntax::parse::token::{self, special_idents, InternedString};
extern crate rustc;
extern crate rustc_front;
extern crate syntax;
#[derive(Copy, Clone)]
pub struct Cx<'a,'tcx:'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub infcx: &'a InferCtxt<'a,'tcx>,
}
impl<'a,'tcx> Cx<'a,'tcx> {
pub fn new(infcx: &'a InferCtxt<'a,'tcx>) -> Cx<'a,'tcx> {
Cx { tcx: infcx.tcx, infcx: infcx }
}
}
pub use self::pattern::PatNode;
impl<'a,'tcx:'a> Hair for Cx<'a, 'tcx> {
type VarId = ast::NodeId;
type DefId = DefId;
type AdtDef = ty::AdtDef<'tcx>;
type Name = ast::Name;
type Ident = ast::Ident;
type InternedString = InternedString;
type Bytes = Rc<Vec<u8>>;
type Span = Span;
type Projection = ty::ProjectionTy<'tcx>;
type Substs = &'tcx subst::Substs<'tcx>;
type ClosureSubsts = &'tcx ty::ClosureSubsts<'tcx>;
type Ty = Ty<'tcx>;
type Region = ty::Region;
type CodeExtent = CodeExtent;
type Pattern = PatNode<'tcx>;
type Expr = &'tcx hir::Expr;
type Stmt = &'tcx hir::Stmt;
type Block = &'tcx hir::Block;
type InlineAsm = &'tcx hir::InlineAsm;
fn unit_ty(&mut self) -> Ty<'tcx> {
self.tcx.mk_nil()
}
fn usize_ty(&mut self) -> Ty<'tcx> {
self.tcx.types.usize
}
fn bool_ty(&mut self) -> Ty<'tcx> {
self.tcx.types.bool
}
fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<Self> {
let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
self.cmp_method_ref(eq_def_id, "eq", ty)
}
fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<Self> {
let ord_def_id = self.tcx.lang_items.ord_trait().unwrap();
self.cmp_method_ref(ord_def_id, "le", ty)
}
fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
adt_def.variants.len()
}
fn fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec<Field<Self>> {
adt_def.variants[variant_index]
.fields
.iter()
.enumerate()
.map(|(index, field)| {
if field.name == special_idents::unnamed_field.name {
Field::Indexed(index)
} else {
Field::Named(field.name)
}
})
.collect()
}
fn needs_drop(&mut self, ty: Ty<'tcx>, span: Self::Span) -> bool {
if self.infcx.type_moves_by_default(ty, span) {
// FIXME(#21859) we should do an add'l check here to determine if
// any dtor will execute, but the relevant fn
// (`type_needs_drop`) is currently factored into
// `librustc_trans`, so we can't easily do so.
true
} else {
// if type implements Copy, cannot require drop
false
}
}
fn span_bug(&mut self, span: Self::Span, message: &str) -> ! {
self.tcx.sess.span_bug(span, message)
}
}
impl<'a,'tcx:'a> Cx<'a,'tcx> {
fn cmp_method_ref(&mut self,
trait_def_id: DefId,
method_name: &str,
arg_ty: Ty<'tcx>)
-> ItemRef<Cx<'a,'tcx>> {
let method_name = token::intern(method_name);
let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty);
for trait_item in self.tcx.trait_items(trait_def_id).iter() {
match *trait_item {
ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
if method.name == method_name {
let method_ty = self.tcx.lookup_item_type(method.def_id);
let method_ty = method_ty.ty.subst(self.tcx, &substs);
return ItemRef {
ty: method_ty,
def_id: method.def_id,
substs: self.tcx.mk_substs(substs),
};
}
}
ty::ImplOrTraitItem::ConstTraitItem(..) |
ty::ImplOrTraitItem::TypeTraitItem(..) => {
}
}
}
self.tcx.sess.bug(
&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
}
}
// We only need this impl so that we do deriving for things that are
// defined relative to the `Hair` trait. See `Hair` trait for more
// details.
impl<'a,'tcx> PartialEq for Cx<'a,'tcx> {
fn eq(&self, _: &Cx<'a,'tcx>) -> bool {
panic!("Cx should never ACTUALLY be compared for equality")
}
}
impl<'a,'tcx> Eq for Cx<'a,'tcx> { }
impl<'a,'tcx> Hash for Cx<'a,'tcx> {
fn hash<H: Hasher>(&self, _: &mut H) {
panic!("Cx should never ACTUALLY be hashed")
}
}
impl<'a,'tcx> Debug for Cx<'a,'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "Tcx")
}
}
mod block;
mod expr;
mod pattern;
mod to_ref;

View file

@ -0,0 +1,291 @@
// 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.
use hair::*;
use repr::*;
use rustc_data_structures::fnv::FnvHashMap;
use std::rc::Rc;
use tcx::Cx;
use tcx::rustc::middle::const_eval::lookup_const_by_id;
use tcx::rustc::middle::def;
use tcx::rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding};
use tcx::rustc::middle::ty::{self, Ty};
use tcx::rustc_front::hir;
use tcx::syntax::ast;
use tcx::syntax::ptr::P;
use tcx::to_ref::ToRef;
/// When there are multiple patterns in a single arm, each one has its
/// own node-ids for the bindings. References to the variables always
/// use the node-ids from the first pattern in the arm, so we just
/// remap the ids for all subsequent bindings to the first one.
///
/// Example:
/// ```
/// match foo {
/// Test1(flavor /* def 1 */) |
/// Test2(flavor /* def 2 */) if flavor /* ref 1 */.is_tasty() => { ... }
/// _ => { ... }
/// }
/// ```
#[derive(Clone, Debug)]
pub struct PatNode<'tcx> {
pat: &'tcx hir::Pat,
binding_map: Option<Rc<FnvHashMap<ast::Ident, ast::NodeId>>>
}
impl<'tcx> PatNode<'tcx> {
pub fn new(pat: &'tcx hir::Pat,
binding_map: Option<Rc<FnvHashMap<ast::Ident, ast::NodeId>>>)
-> PatNode<'tcx> {
PatNode {
pat: pat,
binding_map: binding_map,
}
}
pub fn irrefutable(pat: &'tcx hir::Pat)
-> PatNode<'tcx> {
PatNode::new(pat, None)
}
fn pat_ref<'a>(&self, pat: &'tcx hir::Pat) -> PatternRef<Cx<'a,'tcx>> {
PatNode::new(pat, self.binding_map.clone()).to_ref()
}
fn pat_refs<'a>(&self, pats: &'tcx Vec<P<hir::Pat>>) -> Vec<PatternRef<Cx<'a,'tcx>>> {
pats.iter().map(|p| self.pat_ref(p)).collect()
}
fn opt_pat_ref<'a>(&self, pat: &'tcx Option<P<hir::Pat>>) -> Option<PatternRef<Cx<'a,'tcx>>> {
pat.as_ref().map(|p| self.pat_ref(p))
}
fn slice_or_array_pattern<'a>(&self,
cx: &mut Cx<'a, 'tcx>,
ty: Ty<'tcx>,
prefix: &'tcx Vec<P<hir::Pat>>,
slice: &'tcx Option<P<hir::Pat>>,
suffix: &'tcx Vec<P<hir::Pat>>)
-> PatternKind<Cx<'a,'tcx>>
{
match ty.sty {
ty::TySlice(..) =>
// matching a slice or fixed-length array
PatternKind::Slice {
prefix: self.pat_refs(prefix),
slice: self.opt_pat_ref(slice),
suffix: self.pat_refs(suffix),
},
ty::TyArray(_, len) => {
// fixed-length array
assert!(len >= prefix.len() + suffix.len());
PatternKind::Array {
prefix: self.pat_refs(prefix),
slice: self.opt_pat_ref(slice),
suffix: self.pat_refs(suffix),
}
}
_ => {
cx.tcx.sess.span_bug(
self.pat.span,
"unexpanded macro or bad constant etc");
}
}
}
fn variant_or_leaf<'a>(&self,
cx: &mut Cx<'a, 'tcx>,
subpatterns: Vec<FieldPatternRef<Cx<'a,'tcx>>>)
-> PatternKind<Cx<'a,'tcx>>
{
let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
match def {
def::DefVariant(enum_id, variant_id, _) => {
let adt_def = cx.tcx.lookup_adt_def(enum_id);
if adt_def.variants.len() > 1 {
PatternKind::Variant { adt_def: adt_def,
variant_index: adt_def.variant_index_with_id(variant_id),
subpatterns: subpatterns }
} else {
PatternKind::Leaf { subpatterns: subpatterns }
}
}
// NB: resolving to DefStruct means the struct *constructor*,
// not the struct as a type.
def::DefStruct(..) | def::DefTy(..) => {
PatternKind::Leaf { subpatterns: subpatterns }
}
_ => {
cx.tcx.sess.span_bug(
self.pat.span,
&format!("inappropriate def for pattern: {:?}", def));
}
}
}
}
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for PatNode<'tcx> {
type Output = Pattern<Cx<'a,'tcx>>;
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Pattern<Cx<'a,'tcx>> {
let kind = match self.pat.node {
hir::PatWild(..) =>
PatternKind::Wild,
hir::PatLit(ref lt) =>
PatternKind::Constant { expr: lt.to_ref() },
hir::PatRange(ref begin, ref end) =>
PatternKind::Range { lo: begin.to_ref(),
hi: end.to_ref() },
hir::PatEnum(..) | hir::PatIdent(..) | hir::PatQPath(..)
if pat_is_resolved_const(&cx.tcx.def_map, self.pat) =>
{
let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
match def {
def::DefConst(def_id) | def::DefAssociatedConst(def_id) =>
match lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) {
Some(const_expr) =>
PatternKind::Constant { expr: const_expr.to_ref() },
None =>
cx.tcx.sess.span_bug(
self.pat.span,
&format!("cannot eval constant: {:?}", def_id)),
},
_ =>
cx.tcx.sess.span_bug(
self.pat.span,
&format!("def not a constant: {:?}", def)),
}
}
hir::PatRegion(ref subpattern, _) |
hir::PatBox(ref subpattern) => {
PatternKind::Deref { subpattern: self.pat_ref(subpattern) }
}
hir::PatVec(ref prefix, ref slice, ref suffix) => {
let ty = cx.tcx.node_id_to_type(self.pat.id);
match ty.sty {
ty::TyRef(_, mt) =>
PatternKind::Deref {
subpattern: Pattern {
ty: mt.ty,
span: self.pat.span,
kind: self.slice_or_array_pattern(cx, mt.ty, prefix,
slice, suffix),
}.to_ref()
},
ty::TySlice(..) |
ty::TyArray(..) =>
self.slice_or_array_pattern(cx, ty, prefix, slice, suffix),
ref sty =>
cx.tcx.sess.span_bug(
self.pat.span,
&format!("unexpanded type for vector pattern: {:?}", sty)),
}
}
hir::PatTup(ref subpatterns) => {
let subpatterns =
subpatterns.iter()
.enumerate()
.map(|(i, subpattern)| FieldPatternRef {
field: Field::Indexed(i),
pattern: self.pat_ref(subpattern),
})
.collect();
PatternKind::Leaf { subpatterns: subpatterns }
}
hir::PatIdent(bm, ref ident, ref sub)
if pat_is_binding(&cx.tcx.def_map, self.pat) =>
{
let id = match self.binding_map {
None => self.pat.id,
Some(ref map) => map[&ident.node],
};
let var_ty = cx.tcx.node_id_to_type(self.pat.id);
let region = match var_ty.sty {
ty::TyRef(&r, _) => Some(r),
_ => None,
};
let (mutability, mode) = match bm {
hir::BindByValue(hir::MutMutable) =>
(Mutability::Mut, BindingMode::ByValue),
hir::BindByValue(hir::MutImmutable) =>
(Mutability::Not, BindingMode::ByValue),
hir::BindByRef(hir::MutMutable) =>
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Mut)),
hir::BindByRef(hir::MutImmutable) =>
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
};
PatternKind::Binding {
mutability: mutability,
mode: mode,
name: ident.node,
var: id,
ty: var_ty,
subpattern: self.opt_pat_ref(sub),
}
}
hir::PatIdent(..) => {
self.variant_or_leaf(cx, vec![])
}
hir::PatEnum(_, ref opt_subpatterns) => {
let subpatterns =
opt_subpatterns.iter()
.flat_map(|v| v.iter())
.enumerate()
.map(|(i, field)| FieldPatternRef {
field: Field::Indexed(i),
pattern: self.pat_ref(field),
})
.collect();
self.variant_or_leaf(cx, subpatterns)
}
hir::PatStruct(_, ref fields, _) => {
let subpatterns =
fields.iter()
.map(|field| FieldPatternRef {
field: Field::Named(field.node.ident.name),
pattern: self.pat_ref(&field.node.pat),
})
.collect();
self.variant_or_leaf(cx, subpatterns)
}
hir::PatQPath(..) => {
cx.tcx.sess.span_bug(
self.pat.span,
"unexpanded macro or bad constant etc");
}
};
let ty = cx.tcx.node_id_to_type(self.pat.id);
Pattern { span: self.pat.span,
ty: ty,
kind: kind }
}
}

View file

@ -0,0 +1,94 @@
// 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.
use hair::*;
use repr::*;
use tcx::Cx;
use tcx::pattern::PatNode;
use tcx::rustc_front::hir;
use tcx::syntax::ptr::P;
pub trait ToRef<H> {
type Output;
fn to_ref(self) -> Self::Output;
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx hir::Expr {
type Output = ExprRef<Cx<'a,'tcx>>;
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
ExprRef::Hair(self)
}
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx P<hir::Expr> {
type Output = ExprRef<Cx<'a,'tcx>>;
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
ExprRef::Hair(&**self)
}
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for Expr<Cx<'a,'tcx>> {
type Output = ExprRef<Cx<'a,'tcx>>;
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
ExprRef::Mirror(Box::new(self))
}
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for PatNode<'tcx> {
type Output = PatternRef<Cx<'a,'tcx>>;
fn to_ref(self) -> PatternRef<Cx<'a,'tcx>> {
PatternRef::Hair(self)
}
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for Pattern<Cx<'a,'tcx>> {
type Output = PatternRef<Cx<'a,'tcx>>;
fn to_ref(self) -> PatternRef<Cx<'a,'tcx>> {
PatternRef::Mirror(Box::new(self))
}
}
impl<'a,'tcx:'a,T,U> ToRef<Cx<'a,'tcx>> for &'tcx Option<T>
where &'tcx T: ToRef<Cx<'a,'tcx>, Output=U>
{
type Output = Option<U>;
fn to_ref(self) -> Option<U> {
self.as_ref().map(|expr| expr.to_ref())
}
}
impl<'a,'tcx:'a,T,U> ToRef<Cx<'a,'tcx>> for &'tcx Vec<T>
where &'tcx T: ToRef<Cx<'a,'tcx>, Output=U>
{
type Output = Vec<U>;
fn to_ref(self) -> Vec<U> {
self.iter().map(|expr| expr.to_ref()).collect()
}
}
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx hir::Field {
type Output = FieldExprRef<Cx<'a,'tcx>>;
fn to_ref(self) -> FieldExprRef<Cx<'a,'tcx>> {
FieldExprRef {
name: Field::Named(self.ident.node.name),
expr: self.expr.to_ref()
}
}
}

View file

@ -62,7 +62,7 @@ use rustc::middle::pat_util::pat_bindings;
use rustc::middle::privacy::*;
use rustc::middle::subst::{ParamSpace, FnSpace, TypeSpace};
use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap};
use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap};
use rustc::util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
use rustc::util::lev_distance::lev_distance;
use syntax::ast;
@ -95,7 +95,6 @@ use rustc_front::hir::TypeImplItem;
use rustc_front::util::walk_pat;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::cell::{Cell, RefCell};
use std::fmt;
use std::mem::replace;
@ -1152,7 +1151,7 @@ pub struct Resolver<'a, 'tcx:'a> {
def_map: DefMap,
freevars: RefCell<FreevarMap>,
freevars_seen: RefCell<NodeMap<NodeSet>>,
freevars_seen: RefCell<NodeMap<NodeMap<usize>>>,
export_map: ExportMap,
trait_map: TraitMap,
external_exports: ExternalExports,
@ -1992,21 +1991,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
ClosureRibKind(function_id) => {
let prev_def = def;
def = DefUpvar(node_id, function_id);
let mut seen = self.freevars_seen.borrow_mut();
let seen = match seen.entry(function_id) {
Occupied(v) => v.into_mut(),
Vacant(v) => v.insert(NodeSet()),
};
if seen.contains(&node_id) {
let seen = seen.entry(function_id).or_insert_with(|| NodeMap());
if let Some(&index) = seen.get(&node_id) {
def = DefUpvar(node_id, index, function_id);
continue;
}
match self.freevars.borrow_mut().entry(function_id) {
Occupied(v) => v.into_mut(),
Vacant(v) => v.insert(vec![]),
}.push(Freevar { def: prev_def, span: span });
seen.insert(node_id);
let mut freevars = self.freevars.borrow_mut();
let vec = freevars.entry(function_id)
.or_insert_with(|| vec![]);
let depth = vec.len();
vec.push(Freevar { def: prev_def, span: span });
def = DefUpvar(node_id, depth, function_id);
seen.insert(node_id, depth);
}
ItemRibKind | MethodRibKind => {
// This was an attempt to access an upvar inside a

View file

@ -1428,19 +1428,19 @@ pub fn trans_match<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
fn is_discr_reassigned(bcx: Block, discr: &hir::Expr, body: &hir::Expr) -> bool {
let (vid, field) = match discr.node {
hir::ExprPath(..) => match bcx.def(discr.id) {
def::DefLocal(vid) | def::DefUpvar(vid, _) => (vid, None),
def::DefLocal(vid) | def::DefUpvar(vid, _, _) => (vid, None),
_ => return false
},
hir::ExprField(ref base, field) => {
let vid = match bcx.tcx().def_map.borrow().get(&base.id).map(|d| d.full_def()) {
Some(def::DefLocal(vid)) | Some(def::DefUpvar(vid, _)) => vid,
Some(def::DefLocal(vid)) | Some(def::DefUpvar(vid, _, _)) => vid,
_ => return false
};
(vid, Some(mc::NamedField(field.node.name)))
},
hir::ExprTupField(ref base, field) => {
let vid = match bcx.tcx().def_map.borrow().get(&base.id).map(|d| d.full_def()) {
Some(def::DefLocal(vid)) | Some(def::DefUpvar(vid, _)) => vid,
Some(def::DefLocal(vid)) | Some(def::DefUpvar(vid, _, _)) => vid,
_ => return false
};
(vid, Some(mc::PositionalField(field.node)))

View file

@ -1319,7 +1319,7 @@ pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let _icx = push_ctxt("trans_local_var");
match def {
def::DefUpvar(nid, _) => {
def::DefUpvar(nid, _, _) => {
// Can't move upvars, so this is never a ZeroMemLastUse.
let local_ty = node_id_type(bcx, nid);
let lval = Lvalue::new_with_hint("expr::trans_local_var (upvar)",

View file

@ -4318,7 +4318,7 @@ fn type_scheme_and_predicates_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
defn: def::Def)
-> (TypeScheme<'tcx>, GenericPredicates<'tcx>) {
match defn {
def::DefLocal(nid) | def::DefUpvar(nid, _) => {
def::DefLocal(nid) | def::DefUpvar(nid, _, _) => {
let typ = fcx.local_ty(sp, nid);
(ty::TypeScheme { generics: ty::Generics::empty(), ty: typ },
ty::GenericPredicates::empty())

View file

@ -272,13 +272,20 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
feature")),
("rustc_variance", Normal, Gated("rustc_attrs",
"the `#[rustc_variance]` attribute \
is an experimental feature")),
is just used for rustc unit tests \
and will never be stable")),
("rustc_error", Whitelisted, Gated("rustc_attrs",
"the `#[rustc_error]` attribute \
is an experimental feature")),
is just used for rustc unit tests \
and will never be stable")),
("rustc_move_fragments", Normal, Gated("rustc_attrs",
"the `#[rustc_move_fragments]` attribute \
is an experimental feature")),
is just used for rustc unit tests \
and will never be stable")),
("rustc_mir", Normal, Gated("rustc_attrs",
"the `#[rustc_mir]` attribute \
is just used for rustc unit tests \
and will never be stable")),
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),

View file

@ -12,9 +12,9 @@
// Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate.
#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is an experimental feature
#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is an experimental feature
#[rustc_move_fragments] //~ ERROR the `#[rustc_move_fragments]` attribute is an experimental feature
#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable
#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable
#[rustc_move_fragments] //~ ERROR the `#[rustc_move_fragments]` attribute is just used for rustc unit tests and will never be stable
#[rustc_foo]
//~^ ERROR unless otherwise specified, attributes with the prefix `rustc_` are reserved for internal compiler diagnostics