Skip to content

Commit

Permalink
Auto merge of #27893 - nikomatsakis:mir, r=<try>
Browse files Browse the repository at this point in the history
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?
  • Loading branch information
bors committed Sep 6, 2015
2 parents f84d53c + 2f00086 commit ea3892f
Show file tree
Hide file tree
Showing 52 changed files with 6,161 additions and 103 deletions.
5 changes: 3 additions & 2 deletions mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
12 changes: 12 additions & 0 deletions mk/main.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions mk/target.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
64 changes: 52 additions & 12 deletions src/libgraphviz/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`.
Expand All @@ -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())
Expand All @@ -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
Expand All @@ -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),
}
}

Expand All @@ -524,6 +555,7 @@ impl<'a> LabelText<'a> {
} else {
s
},
HtmlStr(s) => s,
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -129,7 +130,7 @@ impl Def {
id
}
DefLocal(id) |
DefUpvar(id, _) |
DefUpvar(id, _, _) |
DefRegion(id) |
DefLabel(id) |
DefSelfTy(_, Some((_, id))) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, _) => {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
65 changes: 43 additions & 22 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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> {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit ea3892f

Please sign in to comment.