Skip to content

Commit 3dd1a48

Browse files
committed
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?
2 parents 01b9cc5 + c8a6618 commit 3dd1a48

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+6161
-103
lines changed

mk/crates.mk

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ TARGET_CRATES := libc std flate arena term \
5454
log graphviz core rbml alloc \
5555
rustc_unicode rustc_bitflags \
5656
alloc_system
57-
RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \
57+
RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \
5858
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
5959
rustc_data_structures rustc_front rustc_platform_intrinsics
6060
HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
@@ -70,11 +70,12 @@ DEPS_std := core libc rand alloc collections rustc_unicode \
7070
DEPS_graphviz := std
7171
DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags
7272
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
73-
rustc_typeck rustc_resolve log syntax serialize rustc_llvm \
73+
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
7474
rustc_trans rustc_privacy rustc_lint rustc_front
7575

7676
DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
7777
log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics
78+
DEPS_rustc_mir := rustc rustc_front syntax
7879
DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics
7980
DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax
8081
DEPS_rustc_resolve := rustc rustc_front log syntax

mk/main.mk

+12
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,18 @@ RUST_LIB_FLAGS_ST3 += -C prefer-dynamic
172172
# by not emitting them.
173173
RUSTFLAGS_STAGE0 += -Z no-landing-pads
174174

175+
# Enable MIR to "always build" for crates where this works. This is
176+
# just temporary while MIR is being actively built up -- it's just a
177+
# poor man's unit testing infrastructure. Anyway we only want this for
178+
# stage1/stage2.
179+
define ADD_MIR_FLAG
180+
RUSTFLAGS1_$(1) += -Z always-build-mir
181+
RUSTFLAGS2_$(1) += -Z always-build-mir
182+
endef
183+
$(foreach crate,$(TARGET_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
184+
$(foreach crate,$(RUSTC_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
185+
$(foreach crate,$(HOST_CRATES),$(eval $(call ADD_MIR_FLAG,$(crate))))
186+
175187
# platform-specific auto-configuration
176188
include $(CFG_SRC_DIR)mk/platform.mk
177189

mk/target.mk

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
9393
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
9494
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
9595
$$(RUSTFLAGS_$(4)) \
96+
$$(RUSTFLAGS$(1)_$(4)) \
9697
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
9798
--out-dir $$(@D) \
9899
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \

src/libgraphviz/lib.rs

+52-12
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ pub enum LabelText<'a> {
313313
/// are also the escape sequences `\l` which left-justifies the
314314
/// preceding line and `\r` which right-justifies it.
315315
EscStr(Cow<'a, str>),
316+
317+
/// This uses a graphviz [HTML string label][html]. The string is
318+
/// printed exactly as given, but between `<` and `>`. **No
319+
/// escaping is performed.**
320+
///
321+
/// [html]: http://www.graphviz.org/content/node-shapes#html
322+
HtmlStr(Cow<'a, str>),
316323
}
317324

318325
/// The style for a node or edge.
@@ -453,6 +460,14 @@ pub trait Labeller<'a,N,E> {
453460
/// is a valid DOT identifier.
454461
fn node_id(&'a self, n: &N) -> Id<'a>;
455462

463+
/// Maps `n` to one of the [graphviz `shape` names][1]. If `None`
464+
/// is returned, no `shape` attribute is specified.
465+
///
466+
/// [1]: http://www.graphviz.org/content/node-shapes
467+
fn node_shape(&'a self, _node: &N) -> Option<LabelText<'a>> {
468+
None
469+
}
470+
456471
/// Maps `n` to a label that will be used in the rendered output.
457472
/// The label need not be unique, and may be the empty string; the
458473
/// default is just the output from `node_id`.
@@ -479,6 +494,16 @@ pub trait Labeller<'a,N,E> {
479494
}
480495
}
481496

497+
/// Escape tags in such a way that it is suitable for inclusion in a
498+
/// Graphviz HTML label.
499+
pub fn escape_html(s: &str) -> String {
500+
s
501+
.replace("&", "&amp;")
502+
.replace("\"", "&quot;")
503+
.replace("<", "&lt;")
504+
.replace(">", "&gt;")
505+
}
506+
482507
impl<'a> LabelText<'a> {
483508
pub fn label<S:IntoCow<'a, str>>(s: S) -> LabelText<'a> {
484509
LabelStr(s.into_cow())
@@ -488,6 +513,10 @@ impl<'a> LabelText<'a> {
488513
EscStr(s.into_cow())
489514
}
490515

516+
pub fn html<S:IntoCow<'a, str>>(s: S) -> LabelText<'a> {
517+
HtmlStr(s.into_cow())
518+
}
519+
491520
fn escape_char<F>(c: char, mut f: F) where F: FnMut(char) {
492521
match c {
493522
// not escaping \\, since Graphviz escString needs to
@@ -505,10 +534,12 @@ impl<'a> LabelText<'a> {
505534
}
506535

507536
/// Renders text as string suitable for a label in a .dot file.
508-
pub fn escape(&self) -> String {
537+
/// This includes quotes or suitable delimeters.
538+
pub fn to_dot_string(&self) -> String {
509539
match self {
510-
&LabelStr(ref s) => s.escape_default(),
511-
&EscStr(ref s) => LabelText::escape_str(&s[..]),
540+
&LabelStr(ref s) => format!("\"{}\"", s.escape_default()),
541+
&EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s[..])),
542+
&HtmlStr(ref s) => format!("<{}>", s),
512543
}
513544
}
514545

@@ -524,6 +555,7 @@ impl<'a> LabelText<'a> {
524555
} else {
525556
s
526557
},
558+
HtmlStr(s) => s,
527559
}
528560
}
529561

@@ -612,14 +644,15 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
612644
try!(indent(w));
613645
let id = g.node_id(n);
614646

615-
let escaped = &g.node_label(n).escape();
647+
let escaped = &g.node_label(n).to_dot_string();
648+
let shape;
616649

617650
let mut text = vec![id.as_slice()];
618651

619652
if !options.contains(&RenderOption::NoNodeLabels) {
620-
text.push("[label=\"");
653+
text.push("[label=");
621654
text.push(escaped);
622-
text.push("\"]");
655+
text.push("]");
623656
}
624657

625658
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
629662
text.push("\"]");
630663
}
631664

665+
if let Some(s) = g.node_shape(n) {
666+
shape = s.to_dot_string();
667+
text.push("[shape=");
668+
text.push(&shape);
669+
text.push("]");
670+
}
671+
632672
text.push(";");
633673
try!(writeln(w, &text));
634674
}
635675

636676
for e in g.edges().iter() {
637-
let escaped_label = &g.edge_label(e).escape();
677+
let escaped_label = &g.edge_label(e).to_dot_string();
638678
try!(indent(w));
639679
let source = g.source(e);
640680
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
644684
let mut text = vec![source_id.as_slice(), " -> ", target_id.as_slice()];
645685

646686
if !options.contains(&RenderOption::NoEdgeLabels) {
647-
text.push("[label=\"");
687+
text.push("[label=");
648688
text.push(escaped_label);
649-
text.push("\"]");
689+
text.push("]");
650690
}
651691

652692
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
667707
mod tests {
668708
use self::NodeLabels::*;
669709
use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style};
670-
use super::LabelText::{self, LabelStr, EscStr};
710+
use super::LabelText::{self, LabelStr, EscStr, HtmlStr};
671711
use std::io;
672712
use std::io::prelude::*;
673713
use std::borrow::IntoCow;
@@ -805,12 +845,12 @@ mod tests {
805845
fn node_id(&'a self, n: &Node) -> Id<'a> { self.graph.node_id(n) }
806846
fn node_label(&'a self, n: &Node) -> LabelText<'a> {
807847
match self.graph.node_label(n) {
808-
LabelStr(s) | EscStr(s) => EscStr(s),
848+
LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
809849
}
810850
}
811851
fn edge_label(&'a self, e: & &'a Edge) -> LabelText<'a> {
812852
match self.graph.edge_label(e) {
813-
LabelStr(s) | EscStr(s) => EscStr(s),
853+
LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
814854
}
815855
}
816856
}

src/librustc/middle/astencode.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,8 @@ impl tr for def::Def {
470470
def::DefPrimTy(p) => def::DefPrimTy(p),
471471
def::DefTyParam(s, index, def_id, n) => def::DefTyParam(s, index, def_id.tr(dcx), n),
472472
def::DefUse(did) => def::DefUse(did.tr(dcx)),
473-
def::DefUpvar(nid1, nid2) => {
474-
def::DefUpvar(dcx.tr_id(nid1), dcx.tr_id(nid2))
473+
def::DefUpvar(nid1, index, nid2) => {
474+
def::DefUpvar(dcx.tr_id(nid1), index, dcx.tr_id(nid2))
475475
}
476476
def::DefStruct(did) => def::DefStruct(did.tr(dcx)),
477477
def::DefRegion(nid) => def::DefRegion(dcx.tr_id(nid)),

src/librustc/middle/def.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub enum Def {
3838
DefTyParam(ParamSpace, u32, DefId, ast::Name),
3939
DefUse(DefId),
4040
DefUpvar(ast::NodeId, // id of closed over local
41+
usize, // index in the freevars list of the closure
4142
ast::NodeId), // expr node that creates the closure
4243

4344
/// Note that if it's a tuple struct's definition, the node id of the DefId
@@ -129,7 +130,7 @@ impl Def {
129130
id
130131
}
131132
DefLocal(id) |
132-
DefUpvar(id, _) |
133+
DefUpvar(id, _, _) |
133134
DefRegion(id) |
134135
DefLabel(id) |
135136
DefSelfTy(_, Some((_, id))) => {

src/librustc/middle/expr_use_visitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ impl OverloadedCallType {
246246
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d+'t> {
247247
typer: &'t infer::InferCtxt<'a, 'tcx>,
248248
mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
249-
delegate: &'d mut (Delegate<'tcx>+'d),
249+
delegate: &'d mut Delegate<'tcx>,
250250
}
251251

252252
// If the TYPER results in an error, it's because the type check

src/librustc/middle/mem_categorization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
578578
}))
579579
}
580580

581-
def::DefUpvar(var_id, fn_node_id) => {
581+
def::DefUpvar(var_id, _, fn_node_id) => {
582582
let ty = try!(self.node_ty(fn_node_id));
583583
match ty.sty {
584584
ty::TyClosure(closure_id, _) => {

src/librustc/middle/region.rs

+3
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ impl RegionMaps {
329329
pub fn item_extent(&self, n: ast::NodeId) -> CodeExtent {
330330
self.lookup_code_extent(CodeExtentData::DestructionScope(n))
331331
}
332+
pub fn opt_destruction_extent(&self, n: ast::NodeId) -> Option<CodeExtent> {
333+
self.code_extent_interner.borrow().get(&CodeExtentData::DestructionScope(n)).cloned()
334+
}
332335
pub fn intern_code_extent(&self,
333336
e: CodeExtentData,
334337
parent: CodeExtent) -> CodeExtent {

src/librustc/middle/ty.rs

+43-22
Original file line numberDiff line numberDiff line change
@@ -3475,6 +3475,13 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
34753475
.expect("variant_with_id: unknown variant")
34763476
}
34773477

3478+
pub fn variant_index_with_id(&self, vid: DefId) -> usize {
3479+
self.variants
3480+
.iter()
3481+
.position(|v| v.did == vid)
3482+
.expect("variant_index_with_id: unknown variant")
3483+
}
3484+
34783485
pub fn variant_of_def(&self, def: def::Def) -> &VariantDefData<'tcx, 'container> {
34793486
match def {
34803487
def::DefVariant(_, vid, _) => self.variant_with_id(vid),
@@ -5191,28 +5198,12 @@ impl<'tcx> TyS<'tcx> {
51915198

51925199
if !adjusted_ty.references_error() {
51935200
for i in 0..adj.autoderefs {
5194-
let method_call = MethodCall::autoderef(expr_id, i as u32);
5195-
match method_type(method_call) {
5196-
Some(method_ty) => {
5197-
// Overloaded deref operators have all late-bound
5198-
// regions fully instantiated and coverge.
5199-
let fn_ret =
5200-
cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
5201-
adjusted_ty = fn_ret.unwrap();
5202-
}
5203-
None => {}
5204-
}
5205-
match adjusted_ty.builtin_deref(true, NoPreference) {
5206-
Some(mt) => { adjusted_ty = mt.ty; }
5207-
None => {
5208-
cx.sess.span_bug(
5209-
span,
5210-
&format!("the {}th autoderef failed: {}",
5211-
i,
5212-
adjusted_ty)
5213-
);
5214-
}
5215-
}
5201+
adjusted_ty =
5202+
adjusted_ty.adjust_for_autoderef(cx,
5203+
expr_id,
5204+
span,
5205+
i as u32,
5206+
&mut method_type);
52165207
}
52175208
}
52185209

@@ -5228,6 +5219,36 @@ impl<'tcx> TyS<'tcx> {
52285219
};
52295220
}
52305221

5222+
pub fn adjust_for_autoderef<F>(&'tcx self,
5223+
cx: &ctxt<'tcx>,
5224+
expr_id: ast::NodeId,
5225+
expr_span: Span,
5226+
autoderef: u32, // how many autoderefs so far?
5227+
mut method_type: F)
5228+
-> Ty<'tcx> where
5229+
F: FnMut(MethodCall) -> Option<Ty<'tcx>>,
5230+
{
5231+
let method_call = MethodCall::autoderef(expr_id, autoderef);
5232+
let mut adjusted_ty = self;
5233+
if let Some(method_ty) = method_type(method_call) {
5234+
// Method calls always have all late-bound regions
5235+
// fully instantiated.
5236+
let fn_ret = cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
5237+
adjusted_ty = fn_ret.unwrap();
5238+
}
5239+
match adjusted_ty.builtin_deref(true, NoPreference) {
5240+
Some(mt) => mt.ty,
5241+
None => {
5242+
cx.sess.span_bug(
5243+
expr_span,
5244+
&format!("the {}th autoderef failed: {}",
5245+
autoderef,
5246+
adjusted_ty)
5247+
);
5248+
}
5249+
}
5250+
}
5251+
52315252
pub fn adjust_for_autoref(&'tcx self, cx: &ctxt<'tcx>,
52325253
autoref: Option<AutoRef<'tcx>>)
52335254
-> Ty<'tcx> {

src/librustc/session/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub struct Options {
104104
pub parse_only: bool,
105105
pub no_trans: bool,
106106
pub treat_err_as_bug: bool,
107+
pub always_build_mir: bool,
107108
pub no_analysis: bool,
108109
pub debugging_opts: DebuggingOptions,
109110
/// Whether to write dependency files. It's (enabled, optional filename).
@@ -216,6 +217,7 @@ pub fn basic_options() -> Options {
216217
parse_only: false,
217218
no_trans: false,
218219
treat_err_as_bug: false,
220+
always_build_mir: false,
219221
no_analysis: false,
220222
debugging_opts: basic_debugging_options(),
221223
write_dependency_info: (false, None),
@@ -583,6 +585,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
583585
"Run all passes except translation; no output"),
584586
treat_err_as_bug: bool = (false, parse_bool,
585587
"Treat all errors that occur as bugs"),
588+
always_build_mir: bool = (false, parse_bool,
589+
"Always build MIR for all fns, even without a #[rustc_mir] annotation"),
586590
no_analysis: bool = (false, parse_bool,
587591
"Parse and expand the source, but run no analysis"),
588592
extra_plugins: Vec<String> = (Vec::new(), parse_list,
@@ -894,6 +898,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
894898
let parse_only = debugging_opts.parse_only;
895899
let no_trans = debugging_opts.no_trans;
896900
let treat_err_as_bug = debugging_opts.treat_err_as_bug;
901+
let always_build_mir = debugging_opts.always_build_mir;
897902
let no_analysis = debugging_opts.no_analysis;
898903

899904
if debugging_opts.debug_llvm {
@@ -1049,6 +1054,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
10491054
parse_only: parse_only,
10501055
no_trans: no_trans,
10511056
treat_err_as_bug: treat_err_as_bug,
1057+
always_build_mir: always_build_mir,
10521058
no_analysis: no_analysis,
10531059
debugging_opts: debugging_opts,
10541060
write_dependency_info: write_dependency_info,

0 commit comments

Comments
 (0)