Skip to content

Commit

Permalink
Auto merge of #33130 - eddyb:mir-const, r=nikomatsakis
Browse files Browse the repository at this point in the history
Implement constant support in MIR.

All of the intended features in `trans::consts` are now supported by `mir::constant`.
The implementation is considered a temporary measure until `miri` replaces it.

A `-Z orbit` bootstrap build will only translate LLVM IR from AST for `#[rustc_no_mir]` functions.

Furthermore, almost all checks of constant expressions have been moved to MIR.
In non-`const` functions, trees of temporaries are promoted, as per RFC 1414 (rvalue promotion).
Promotion before MIR borrowck would allow reasoning about promoted values' lifetimes.

The improved checking comes at the cost of four `[breaking-change]`s:
* repeat counts must contain a constant expression, e.g.:
`let arr = [0; { println!("foo"); 5 }];` used to be allowed (it behaved like `let arr = [0; 5];`)
* dereference of a reference to a `static` cannot be used in another `static`, e.g.:
`static X: [u8; 1] = [1]; static Y: u8 = (&X)[0];` was unintentionally allowed before
* the type of a `static` *must* be `Sync`, irrespective of the initializer, e.g.
`static FOO: *const T = &BAR;` worked as `&T` is `Sync`, but it shouldn't because `*const T` isn't
* a `static` cannot wrap `UnsafeCell` around a type that *may* need drop, e.g.
`static X: MakeSync<UnsafeCell<Option<String>>> = MakeSync(UnsafeCell::new(None));`
was previously allowed based on the fact `None` alone doesn't need drop, but in `UnsafeCell`
it can be later changed to `Some(String)` which *does* need dropping

The drop restrictions are relaxed by RFC 1440 (#33156), which is implemented, but feature-gated.
However, creating `UnsafeCell` from constants is unstable, so users can just enable the feature gate.
  • Loading branch information
bors committed May 8, 2016
2 parents d91f8ab + 3b0e27c commit 1ec2217
Show file tree
Hide file tree
Showing 76 changed files with 3,411 additions and 1,351 deletions.
2 changes: 1 addition & 1 deletion mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ DEPS_rustc_lint := rustc log syntax rustc_const_eval
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
DEPS_rustc_metadata := rustc syntax rbml rustc_const_math
DEPS_rustc_passes := syntax rustc core rustc_const_eval
DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval
DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval rustc_bitflags
DEPS_rustc_resolve := arena rustc log syntax
DEPS_rustc_platform_intrinsics := std
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub enum DepNode<D: Clone + Debug> {
IntrinsicCheck(D),
MatchCheck(D),
MirMapConstruction(D),
MirPass(D),
MirTypeck(D),
BorrowCheck(D),
RvalueCheck(D),
Expand Down Expand Up @@ -186,6 +187,7 @@ impl<D: Clone + Debug> DepNode<D> {
IntrinsicCheck(ref d) => op(d).map(IntrinsicCheck),
MatchCheck(ref d) => op(d).map(MatchCheck),
MirMapConstruction(ref d) => op(d).map(MirMapConstruction),
MirPass(ref d) => op(d).map(MirPass),
MirTypeck(ref d) => op(d).map(MirTypeck),
BorrowCheck(ref d) => op(d).map(BorrowCheck),
RvalueCheck(ref d) => op(d).map(RvalueCheck),
Expand Down
82 changes: 68 additions & 14 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ impl<'ast> DefCollector<'ast> {
f(self);
self.parent_def = parent;
}

fn visit_ast_const_integer(&mut self, expr: &'ast Expr) {
// Find the node which will be used after lowering.
if let ExprKind::Paren(ref inner) = expr.node {
return self.visit_ast_const_integer(inner);
}

// FIXME(eddyb) Closures should have separate
// function definition IDs and expression IDs.
if let ExprKind::Closure(..) = expr.node {
return;
}

self.create_def(expr.id, DefPathData::Initializer);
}

fn visit_hir_const_integer(&mut self, expr: &'ast hir::Expr) {
// FIXME(eddyb) Closures should have separate
// function definition IDs and expression IDs.
if let hir::ExprClosure(..) = expr.node {
return;
}

self.create_def(expr.id, DefPathData::Initializer);
}
}

impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
Expand Down Expand Up @@ -126,14 +151,17 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
let variant_def_index =
this.create_def(v.node.data.id(),
DefPathData::EnumVariant(v.node.name.name));

for (index, field) in v.node.data.fields().iter().enumerate() {
let name = field.ident.map(|ident| ident.name)
.unwrap_or(token::intern(&index.to_string()));
this.create_def_with_parent(Some(variant_def_index),
field.id,
DefPathData::Field(name));
}
this.with_parent(variant_def_index, |this| {
for (index, field) in v.node.data.fields().iter().enumerate() {
let name = field.ident.map(|ident| ident.name)
.unwrap_or_else(|| token::intern(&index.to_string()));
this.create_def(field.id, DefPathData::Field(name));
}

if let Some(ref expr) = v.node.disr_expr {
this.visit_ast_const_integer(expr);
}
});
}
}
ItemKind::Struct(ref struct_def, _) => {
Expand Down Expand Up @@ -221,6 +249,10 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
fn visit_expr(&mut self, expr: &'ast Expr) {
let parent_def = self.parent_def;

if let ExprKind::Repeat(_, ref count) = expr.node {
self.visit_ast_const_integer(count);
}

if let ExprKind::Closure(..) = expr.node {
let def = self.create_def(expr.id, DefPathData::ClosureExpr);
self.parent_def = Some(def);
Expand All @@ -230,6 +262,13 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
self.parent_def = parent_def;
}

fn visit_ty(&mut self, ty: &'ast Ty) {
if let TyKind::FixedLengthVec(_, ref length) = ty.node {
self.visit_ast_const_integer(length);
}
visit::walk_ty(self, ty);
}

fn visit_lifetime_def(&mut self, def: &'ast LifetimeDef) {
self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name));
}
Expand Down Expand Up @@ -276,11 +315,15 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
this.create_def(v.node.data.id(),
DefPathData::EnumVariant(v.node.name));

for field in v.node.data.fields() {
this.create_def_with_parent(Some(variant_def_index),
field.id,
DefPathData::Field(field.name));
}
this.with_parent(variant_def_index, |this| {
for field in v.node.data.fields() {
this.create_def(field.id,
DefPathData::Field(field.name));
}
if let Some(ref expr) = v.node.disr_expr {
this.visit_hir_const_integer(expr);
}
});
}
}
hir::ItemStruct(ref struct_def, _) => {
Expand Down Expand Up @@ -365,6 +408,10 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
fn visit_expr(&mut self, expr: &'ast hir::Expr) {
let parent_def = self.parent_def;

if let hir::ExprRepeat(_, ref count) = expr.node {
self.visit_hir_const_integer(count);
}

if let hir::ExprClosure(..) = expr.node {
let def = self.create_def(expr.id, DefPathData::ClosureExpr);
self.parent_def = Some(def);
Expand All @@ -374,11 +421,18 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
self.parent_def = parent_def;
}

fn visit_ty(&mut self, ty: &'ast hir::Ty) {
if let hir::TyFixedLengthVec(_, ref length) = ty.node {
self.visit_hir_const_integer(length);
}
intravisit::walk_ty(self, ty);
}

fn visit_lifetime_def(&mut self, def: &'ast hir::LifetimeDef) {
self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name));
}

fn visit_macro_def(&mut self, macro_def: &'ast hir::MacroDef) {
self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.name));
}
}
}
12 changes: 12 additions & 0 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub struct Mir<'tcx> {
/// used (eventually) for debuginfo. Indexed by a `ScopeId`.
pub scopes: Vec<ScopeData>,

/// Rvalues promoted from this function, such as borrows of constants.
/// Each of them is the Mir of a constant with the fn's type parameters
/// in scope, but no vars or args and a separate set of temps.
pub promoted: Vec<Mir<'tcx>>,

/// Return type of the function.
pub return_ty: FnOutput<'tcx>,

Expand Down Expand Up @@ -987,6 +992,10 @@ pub enum Literal<'tcx> {
Value {
value: ConstVal,
},
Promoted {
// Index into the `promoted` vector of `Mir`.
index: usize
},
}

impl<'tcx> Debug for Constant<'tcx> {
Expand All @@ -1007,6 +1016,9 @@ impl<'tcx> Debug for Literal<'tcx> {
write!(fmt, "const ")?;
fmt_const_val(fmt, value)
}
Promoted { index } => {
write!(fmt, "promoted{}", index)
}
}
}
}
Expand Down
77 changes: 74 additions & 3 deletions src/librustc/mir/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,102 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use dep_graph::DepNode;
use hir;
use hir::map::DefPathData;
use hir::def_id::DefId;
use mir::mir_map::MirMap;
use mir::repr::Mir;
use ty::TyCtxt;
use syntax::ast::NodeId;

/// Where a specific Mir comes from.
#[derive(Copy, Clone)]
pub enum MirSource {
/// Functions and methods.
Fn(NodeId),

/// Constants and associated constants.
Const(NodeId),

/// Initializer of a `static` item.
Static(NodeId, hir::Mutability),

/// Promoted rvalues within a function.
Promoted(NodeId, usize)
}

impl MirSource {
pub fn from_node(tcx: &TyCtxt, id: NodeId) -> MirSource {
use hir::*;

// Handle constants in enum discriminants, types, and repeat expressions.
let def_id = tcx.map.local_def_id(id);
let def_key = tcx.def_key(def_id);
if def_key.disambiguated_data.data == DefPathData::Initializer {
return MirSource::Const(id);
}

match tcx.map.get(id) {
map::NodeItem(&Item { node: ItemConst(..), .. }) |
map::NodeTraitItem(&TraitItem { node: ConstTraitItem(..), .. }) |
map::NodeImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) => {
MirSource::Const(id)
}
map::NodeItem(&Item { node: ItemStatic(_, m, _), .. }) => {
MirSource::Static(id, m)
}
// Default to function if it's not a constant or static.
_ => MirSource::Fn(id)
}
}

pub fn item_id(&self) -> NodeId {
match *self {
MirSource::Fn(id) |
MirSource::Const(id) |
MirSource::Static(id, _) |
MirSource::Promoted(id, _) => id
}
}
}

/// Various information about pass.
pub trait Pass {
// fn name() for printouts of various sorts?
// fn should_run(Session) to check if pass should run?
fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
DepNode::MirPass(def_id)
}
}

/// A pass which inspects the whole MirMap.
pub trait MirMapPass<'tcx>: Pass {
fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>);
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>);
}

/// A pass which inspects Mir of functions in isolation.
pub trait MirPass<'tcx>: Pass {
fn run_pass(&mut self, cx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>);
fn run_pass_on_promoted(&mut self, tcx: &TyCtxt<'tcx>,
item_id: NodeId, index: usize,
mir: &mut Mir<'tcx>) {
self.run_pass(tcx, MirSource::Promoted(item_id, index), mir);
}
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>);
}

impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) {
for (&id, mir) in &mut map.map {
MirPass::run_pass(self, tcx, id, mir);
let def_id = tcx.map.local_def_id(id);
let _task = tcx.dep_graph.in_task(self.dep_node(def_id));

let src = MirSource::from_node(tcx, id);
MirPass::run_pass(self, tcx, src, mir);

for (i, mir) in mir.promoted.iter_mut().enumerate() {
self.run_pass_on_promoted(tcx, id, i, mir);
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ macro_rules! make_mir_visitor {
let Mir {
ref $($mutability)* basic_blocks,
ref $($mutability)* scopes,
promoted: _, // Visited by passes separately.
ref $($mutability)* return_ty,
ref $($mutability)* var_decls,
ref $($mutability)* arg_decls,
Expand Down Expand Up @@ -649,10 +650,11 @@ macro_rules! make_mir_visitor {
ref $($mutability)* substs } => {
self.visit_def_id(def_id);
self.visit_substs(substs);
},
}
Literal::Value { ref $($mutability)* value } => {
self.visit_const_val(value);
}
Literal::Promoted { index: _ } => {}
}
}

Expand Down
12 changes: 11 additions & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use middle::cstore::{self, LOCAL_CRATE};
use hir::def::{self, Def, ExportMap};
use hir::def_id::DefId;
use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
use middle::region::{CodeExtent};
use middle::region::{CodeExtent, ROOT_CODE_EXTENT};
use traits;
use ty;
use ty::subst::{Subst, Substs, VecPerParamSpace};
Expand Down Expand Up @@ -1376,6 +1376,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
}
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemConst(..) |
hir::ItemStatic(..) => {
Expand Down Expand Up @@ -1408,6 +1409,15 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
// This is a convenience to allow closures to work.
ParameterEnvironment::for_item(cx, cx.map.get_parent(id))
}
Some(ast_map::NodeForeignItem(item)) => {
let def_id = cx.map.local_def_id(id);
let scheme = cx.lookup_item_type(def_id);
let predicates = cx.lookup_predicates(def_id);
cx.construct_parameter_environment(item.span,
&scheme.generics,
&predicates,
ROOT_CODE_EXTENT)
}
_ => {
bug!("ParameterEnvironment::from_item(): \
`{}` is not an item",
Expand Down
7 changes: 2 additions & 5 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use rustc_privacy;
use rustc_plugin::registry::Registry;
use rustc_plugin as plugin;
use rustc::hir::lowering::{lower_crate, LoweringContext};
use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
use rustc_passes::{no_asm, loops, consts, rvalues, static_recursion};
use rustc_const_eval::check_match;
use super::Compilation;

Expand Down Expand Up @@ -726,10 +726,6 @@ pub fn phase_2_configure_and_expand(sess: &Session,
})
})?;

time(time_passes,
"const fn bodies and arguments",
|| const_fn::check_crate(sess, &krate))?;

if sess.opts.debugging_opts.input_stats {
println!("Post-expansion node count: {}", count_nodes(&krate));
}
Expand Down Expand Up @@ -903,6 +899,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
let mut passes = sess.mir_passes.borrow_mut();
// Push all the built-in passes.
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants);
passes.push_pass(box mir::transform::type_check::TypeckMir);
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,9 @@ pub fn maybe_get_item_mir<'tcx>(cdata: Cmd,
};

def_id_and_span_translator.visit_mir(&mut mir);
for promoted in &mut mir.promoted {
def_id_and_span_translator.visit_mir(promoted);
}

mir
});
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ rustc_back = { path = "../librustc_back" }
rustc_const_eval = { path = "../librustc_const_eval" }
rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_bitflags = { path = "../librustc_bitflags" }
syntax = { path = "../libsyntax" }
Loading

0 comments on commit 1ec2217

Please sign in to comment.