Auto merge of #33130 - eddyb:mir-const, r=nikomatsakis

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.
This commit is contained in:
bors 2016-05-08 00:31:40 -07:00
commit 1ec22171e6
76 changed files with 3426 additions and 1366 deletions

View File

@ -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

View File

@ -64,6 +64,7 @@ pub enum DepNode<D: Clone + Debug> {
IntrinsicCheck(D),
MatchCheck(D),
MirMapConstruction(D),
MirPass(D),
MirTypeck(D),
BorrowCheck(D),
RvalueCheck(D),
@ -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),

View File

@ -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> {
@ -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));
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));
}
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));
}
if let Some(ref expr) = v.node.disr_expr {
this.visit_ast_const_integer(expr);
}
});
}
}
ItemKind::Struct(ref struct_def, _) => {
@ -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);
@ -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));
}
@ -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, _) => {
@ -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);
@ -374,6 +421,13 @@ 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));
}
@ -381,4 +435,4 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
fn visit_macro_def(&mut self, macro_def: &'ast hir::MacroDef) {
self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.name));
}
}
}

View File

@ -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>,
@ -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> {
@ -1007,6 +1016,9 @@ impl<'tcx> Debug for Literal<'tcx> {
write!(fmt, "const ")?;
fmt_const_val(fmt, value)
}
Promoted { index } => {
write!(fmt, "promoted{}", index)
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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,
@ -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: _ } => {}
}
}

View File

@ -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};
@ -1376,6 +1376,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
}
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemConst(..) |
hir::ItemStatic(..) => {
@ -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",

View File

@ -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;
@ -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));
}
@ -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);

View File

@ -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
});

View File

@ -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" }

View File

@ -35,13 +35,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
let expr_ty = expr.ty.clone();
let temp = this.temp(expr_ty.clone());
let temp_lifetime = match expr.temp_lifetime {
Some(t) => t,
None => {
span_bug!(expr.span, "no temp_lifetime for expr");
}
};
this.schedule_drop(expr.span, temp_lifetime, &temp, expr_ty);
// In constants, temp_lifetime is None. We should not need to drop
// anything because no values with a destructor can be created in
// a constant at this time, even if the type may need dropping.
if let Some(temp_lifetime) = expr.temp_lifetime {
this.schedule_drop(expr.span, temp_lifetime, &temp, expr_ty);
}
// Careful here not to cause an infinite cycle. If we always
// called `into`, then for lvalues like `x.f`, it would

View File

@ -9,13 +9,14 @@
// except according to those terms.
use hair::cx::Cx;
use rustc::middle::region::{CodeExtent, CodeExtentData};
use rustc::ty::{self, FnOutput, Ty};
use rustc::middle::region::{CodeExtent, CodeExtentData, ROOT_CODE_EXTENT};
use rustc::ty::{self, Ty};
use rustc::mir::repr::*;
use rustc_data_structures::fnv::FnvHashMap;
use rustc::hir;
use rustc::hir::pat_util::pat_is_binding;
use std::ops::{Index, IndexMut};
use syntax::abi::Abi;
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token::keywords;
@ -83,7 +84,7 @@ pub struct ScopeAuxiliary {
pub postdoms: Vec<Location>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Location {
/// the location is within this block
pub block: BasicBlock,
@ -159,53 +160,31 @@ macro_rules! unpack {
///////////////////////////////////////////////////////////////////////////
/// the main entry point for building MIR for a function
pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
span: Span,
fn_id: ast::NodeId,
body_id: ast::NodeId,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
return_ty: FnOutput<'tcx>,
ast_block: &'tcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
pub fn construct_fn<'a, 'tcx, A>(hir: Cx<'a,'tcx>,
fn_id: ast::NodeId,
arguments: A,
return_ty: ty::FnOutput<'tcx>,
ast_block: &'tcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec)
where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
{
let tcx = hir.tcx();
let cfg = CFG { basic_blocks: vec![] };
let span = tcx.map.span(fn_id);
let mut builder = Builder::new(hir, span);
let mut builder = Builder {
hir: hir,
cfg: cfg,
fn_span: span,
scopes: vec![],
scope_datas: vec![],
scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
loop_scopes: vec![],
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None,
cached_resume_block: None,
cached_return_block: None
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
let mut arg_decls = None; // assigned to `Some` in closures below
let body_id = ast_block.id;
let call_site_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id });
let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| {
let mut block = START_BLOCK;
let arg_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| {
arg_decls = Some(unpack!(block = builder.args_and_body(block,
return_ty,
implicit_arguments,
explicit_arguments,
arg_scope_id,
ast_block)));
block.unit()
let arg_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
let mut block = START_BLOCK;
let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block,
|builder, call_site_scope_id| {
let arg_decls = unpack!(block = builder.in_scope(arg_extent, block,
|builder, arg_scope_id| {
builder.args_and_body(block, return_ty, arguments, arg_scope_id, ast_block)
}));
let return_block = builder.return_block();
@ -213,20 +192,19 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
TerminatorKind::Goto { target: return_block });
builder.cfg.terminate(return_block, call_site_scope_id, span,
TerminatorKind::Return);
return_block.unit()
});
return_block.and(arg_decls)
}));
assert_eq!(block, builder.return_block());
assert!(
builder.cfg.basic_blocks
.iter()
.enumerate()
.all(|(index, block)| {
if block.terminator.is_none() {
bug!("no terminator on block {:?} in fn {:?}",
index, fn_id)
}
true
}));
match tcx.node_id_to_type(fn_id).sty {
ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
// RustCall pseudo-ABI untuples the last argument.
if let Some(arg_decl) = arg_decls.last_mut() {
arg_decl.spread = true;
}
}
_ => {}
}
// Gather the upvars of a closure, if any.
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
@ -251,72 +229,126 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
}).collect()
});
(
Mir {
basic_blocks: builder.cfg.basic_blocks,
scopes: builder.scope_datas,
var_decls: builder.var_decls,
arg_decls: arg_decls.take().expect("args never built?"),
temp_decls: builder.temp_decls,
upvar_decls: upvar_decls,
return_ty: return_ty,
span: span
},
builder.scope_auxiliary,
)
builder.finish(upvar_decls, arg_decls, return_ty)
}
pub fn construct_const<'a, 'tcx>(hir: Cx<'a,'tcx>,
item_id: ast::NodeId,
ast_expr: &'tcx hir::Expr)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
let tcx = hir.tcx();
let span = tcx.map.span(item_id);
let mut builder = Builder::new(hir, span);
let extent = ROOT_CODE_EXTENT;
let mut block = START_BLOCK;
let _ = builder.in_scope(extent, block, |builder, call_site_scope_id| {
let expr = builder.hir.mirror(ast_expr);
unpack!(block = builder.into(&Lvalue::ReturnPointer, block, expr));
let return_block = builder.return_block();
builder.cfg.terminate(block, call_site_scope_id, span,
TerminatorKind::Goto { target: return_block });
builder.cfg.terminate(return_block, call_site_scope_id, span,
TerminatorKind::Return);
return_block.unit()
});
let ty = tcx.expr_ty_adjusted(ast_expr);
builder.finish(vec![], vec![], ty::FnConverging(ty))
}
impl<'a,'tcx> Builder<'a,'tcx> {
fn args_and_body(&mut self,
mut block: BasicBlock,
return_ty: FnOutput<'tcx>,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
argument_scope_id: ScopeId,
ast_block: &'tcx hir::Block)
-> BlockAnd<Vec<ArgDecl<'tcx>>>
fn new(hir: Cx<'a, 'tcx>, span: Span) -> Builder<'a, 'tcx> {
let mut builder = Builder {
hir: hir,
cfg: CFG { basic_blocks: vec![] },
fn_span: span,
scopes: vec![],
scope_datas: vec![],
scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
loop_scopes: vec![],
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None,
cached_resume_block: None,
cached_return_block: None
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
builder
}
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
arg_decls: Vec<ArgDecl<'tcx>>,
return_ty: ty::FnOutput<'tcx>)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
span_bug!(self.fn_span, "no terminator on block {:?}", index);
}
}
(Mir {
basic_blocks: self.cfg.basic_blocks,
scopes: self.scope_datas,
promoted: vec![],
var_decls: self.var_decls,
arg_decls: arg_decls,
temp_decls: self.temp_decls,
upvar_decls: upvar_decls,
return_ty: return_ty,
span: self.fn_span
}, self.scope_auxiliary)
}
fn args_and_body<A>(&mut self,
mut block: BasicBlock,
return_ty: ty::FnOutput<'tcx>,
arguments: A,
argument_scope_id: ScopeId,
ast_block: &'tcx hir::Block)
-> BlockAnd<Vec<ArgDecl<'tcx>>>
where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
{
// to start, translate the argument patterns and collect the argument types.
let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
let arg_decls =
implicits
.chain(explicits)
.enumerate()
.map(|(index, (ty, pattern))| {
let lvalue = Lvalue::Arg(index as u32);
if let Some(pattern) = pattern {
let pattern = self.hir.irrefutable_pat(pattern);
unpack!(block = self.lvalue_into_pattern(block,
argument_scope_id,
pattern,
&lvalue));
}
let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
let lvalue = Lvalue::Arg(index as u32);
if let Some(pattern) = pattern {
let pattern = self.hir.irrefutable_pat(pattern);
unpack!(block = self.lvalue_into_pattern(block,
argument_scope_id,
pattern,
&lvalue));
}
// Make sure we drop (parts of) the argument even when not matched on.
let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
// Make sure we drop (parts of) the argument even when not matched on.
let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
let mut name = keywords::Invalid.name();
if let Some(pat) = pattern {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
name = ident.node.name;
}
let mut name = keywords::Invalid.name();
if let Some(pat) = pattern {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
name = ident.node.name;
}
}
}
ArgDecl {
ty: ty,
spread: false,
debug_name: name
}
})
.collect();
ArgDecl {
ty: ty,
spread: false,
debug_name: name
}
}).collect();
// FIXME(#32959): temporary hack for the issue at hand
let return_is_unit = if let FnOutput::FnConverging(t) = return_ty {
let return_is_unit = if let ty::FnConverging(t) = return_ty {
t.is_nil()
} else {
false

View File

@ -0,0 +1,387 @@
// 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.
#![allow(non_snake_case)]
register_long_diagnostics! {
E0010: r##"
The value of statics and constants must be known at compile time, and they live
for the entire lifetime of a program. Creating a boxed value allocates memory on
the heap at runtime, and therefore cannot be done at compile time. Erroneous
code example:
```compile_fail
#![feature(box_syntax)]
const CON : Box<i32> = box 0;
```
"##,
E0013: r##"
Static and const variables can refer to other const variables. But a const
variable cannot refer to a static variable. For example, `Y` cannot refer to
`X` here:
```compile_fail
static X: i32 = 42;
const Y: i32 = X;
```
To fix this, the value can be extracted as a const and then used:
```
const A: i32 = 42;
static X: i32 = A;
const Y: i32 = A;
```
"##,
// FIXME(#24111) Change the language here when const fn stabilizes
E0015: r##"
The only functions that can be called in static or constant expressions are
`const` functions, and struct/enum constructors. `const` functions are only
available on a nightly compiler. Rust currently does not support more general
compile-time function execution.
```
const FOO: Option<u8> = Some(1); // enum constructor
struct Bar {x: u8}
const BAR: Bar = Bar {x: 1}; // struct constructor
```
See [RFC 911] for more details on the design of `const fn`s.
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0016: r##"
Blocks in constants may only contain items (such as constant, function
definition, etc...) and a tail expression. Erroneous code example:
```compile_fail
const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
```
To avoid it, you have to replace the non-item object:
```
const FOO: i32 = { const X : i32 = 0; X };
```
"##,
E0017: r##"
References in statics and constants may only refer to immutable values.
Erroneous code example:
```compile_fail
static X: i32 = 1;
const C: i32 = 2;
// these three are not allowed:
const CR: &'static mut i32 = &mut C;
static STATIC_REF: &'static mut i32 = &mut X;
static CONST_REF: &'static mut i32 = &mut C;
```
Statics are shared everywhere, and if they refer to mutable data one might
violate memory safety since holding multiple mutable references to shared data
is not allowed.
If you really want global mutable state, try using `static mut` or a global
`UnsafeCell`.
"##,
E0018: r##"
The value of static and constant integers must be known at compile time. You
can't cast a pointer to an integer because the address of a pointer can
vary.
For example, if you write:
```compile_fail
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
```
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
the address can change when the program is linked, as well as change
between different executions due to ASLR, and many linkers would
not be able to calculate the value of `WHAT`.
On the other hand, static and constant pointers can point either to
a known numeric address or to the address of a symbol.
```
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
// ... and also
static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
```
This does not pose a problem by itself because they can't be
accessed directly.
"##,
E0019: r##"
A function call isn't allowed in the const's initialization expression
because the expression's value must be known at compile-time. Erroneous code
example:
```compile_fail
enum Test {
V1
}
impl Test {
fn test(&self) -> i32 {
12
}
}
fn main() {
const FOO: Test = Test::V1;
const A: i32 = FOO.test(); // You can't call Test::func() here !
}
```
Remember: you can't use a function call inside a const's initialization
expression! However, you can totally use it anywhere else:
```
fn main() {
const FOO: Test = Test::V1;
FOO.func(); // here is good
let x = FOO.func(); // or even here!
}
```
"##,
E0022: r##"
Constant functions are not allowed to mutate anything. Thus, binding to an
argument with a mutable pattern is not allowed. For example,
```compile_fail
const fn foo(mut x: u8) {
// do stuff
}
```
Is incorrect because the function body may not mutate `x`.
Remove any mutable bindings from the argument list to fix this error. In case
you need to mutate the argument, try lazily initializing a global variable
instead of using a `const fn`, or refactoring the code to a functional style to
avoid mutation if possible.
"##,
E0394: r##"
From [RFC 246]:
> It is invalid for a static to reference another static by value. It is
> required that all references be borrowed.
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
"##,
E0395: r##"
The value assigned to a constant scalar must be known at compile time,
which is not the case when comparing raw pointers.
Erroneous code example:
```compile_fail
static FOO: i32 = 42;
static BAR: i32 = 42;
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// error: raw pointers cannot be compared in statics!
```
The address assigned by the linker to `FOO` and `BAR` may or may not
be identical, so the value of `BAZ` can't be determined.
If you want to do the comparison, please do it at run-time.
For example:
```
static FOO: i32 = 42;
static BAR: i32 = 42;
let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// baz isn't a constant expression so it's ok
```
"##,
E0396: r##"
The value behind a raw pointer can't be determined at compile-time
(or even link-time), which means it can't be used in a constant
expression. Erroneous code example:
```compile_fail
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
const VALUE: u8 = unsafe { *REG_ADDR };
// error: raw pointers cannot be dereferenced in constants
```
A possible fix is to dereference your pointer at some point in run-time.
For example:
```
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
let reg_value = unsafe { *REG_ADDR };
```
"##,
E0492: r##"
A borrow of a constant containing interior mutability was attempted. Erroneous
code example:
```compile_fail
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
const A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A;
// error: cannot borrow a constant which contains interior mutability, create a
// static instead
```
A `const` represents a constant value that should never change. If one takes
a `&` reference to the constant, then one is taking a pointer to some memory
location containing the value. Normally this is perfectly fine: most values
can't be changed via a shared `&` pointer, but interior mutability would allow
it. That is, a constant value could be mutated. On the other hand, a `static` is
explicitly a single memory location, which can be mutated at will.
So, in order to solve this error, either use statics which are `Sync`:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
static A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A; // ok!
```
You can also have this error while using a cell type:
```compile_fail
#![feature(const_fn)]
use std::cell::Cell;
const A: Cell<usize> = Cell::new(1);
const B: &'static Cell<usize> = &A;
// error: cannot borrow a constant which contains interior mutability, create
// a static instead
// or:
struct C { a: Cell<usize> }
const D: C = C { a: Cell::new(1) };
const E: &'static Cell<usize> = &D.a; // error
// or:
const F: &'static C = &D; // error
```
This is because cell types do operations that are not thread-safe. Due to this,
they don't implement Sync and thus can't be placed in statics. In this
case, `StaticMutex` would work just fine, but it isn't stable yet:
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
However, if you still wish to use these types, you can achieve this by an unsafe
wrapper:
```
#![feature(const_fn)]
use std::cell::Cell;
use std::marker::Sync;
struct NotThreadSafe<T> {
value: Cell<T>,
}
unsafe impl<T> Sync for NotThreadSafe<T> {}
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
static B: &'static NotThreadSafe<usize> = &A; // ok!
```
Remember this solution is unsafe! You will have to ensure that accesses to the
cell are synchronized.
"##,
E0493: r##"
A type with a destructor was assigned to an invalid type of variable. Erroneous
code example:
```compile_fail
struct Foo {
a: u32
}
impl Drop for Foo {
fn drop(&mut self) {}
}
const F : Foo = Foo { a : 0 };
// error: constants are not allowed to have destructors
static S : Foo = Foo { a : 0 };
// error: destructors in statics are an unstable feature
```
To solve this issue, please use a type which does allow the usage of type with
destructors.
"##,
E0494: r##"
A reference of an interior static was assigned to another const/static.
Erroneous code example:
```compile_fail
struct Foo {
a: u32
}
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a;
// error: cannot refer to the interior of another static, use a
// constant instead
```
The "base" variable has to be a const if you want another static/const variable
to refer to one of its fields. Example:
```
struct Foo {
a: u32
}
const S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; // ok!
```
"##,
}
register_diagnostics! {
E0526, // shuffle indices are not constant
}

View File

@ -351,21 +351,39 @@ fn make_mirror_unadjusted<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr
pass_args, lhs.to_ref(), vec![rhs])
} else {
// FIXME overflow
match op.node {
hir::BinOp_::BiAnd => {
match (op.node, cx.constness) {
// FIXME(eddyb) use logical ops in constants when
// they can handle that kind of control-flow.
(hir::BinOp_::BiAnd, hir::Constness::Const) => {
ExprKind::Binary {
op: BinOp::BitAnd,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
(hir::BinOp_::BiOr, hir::Constness::Const) => {
ExprKind::Binary {
op: BinOp::BitOr,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
(hir::BinOp_::BiAnd, hir::Constness::NotConst) => {
ExprKind::LogicalOp {
op: LogicalOp::And,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
hir::BinOp_::BiOr => {
(hir::BinOp_::BiOr, hir::Constness::NotConst) => {
ExprKind::LogicalOp {
op: LogicalOp::Or,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
_ => {
let op = bin_op(op.node);
ExprKind::Binary {

View File

@ -32,13 +32,17 @@ use rustc_const_math::{ConstInt, ConstUsize};
pub struct Cx<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>,
infcx: &'a InferCtxt<'a, 'tcx>,
constness: hir::Constness
}
impl<'a,'tcx> Cx<'a,'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>,
constness: hir::Constness)
-> Cx<'a, 'tcx> {
Cx {
tcx: infcx.tcx,
infcx: infcx,
constness: constness,
}
}
}

View File

@ -20,7 +20,9 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![cfg_attr(not(stage0), deny(warnings))]
#![unstable(feature = "rustc_private", issue = "27812")]
#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
#![feature(staged_api)]
#![feature(question_mark)]
@ -31,10 +33,16 @@ extern crate graphviz as dot;
extern crate rustc;
extern crate rustc_data_structures;
extern crate rustc_back;
#[macro_use]
#[no_link]
extern crate rustc_bitflags;
#[macro_use]
extern crate syntax;
extern crate rustc_const_math;
extern crate rustc_const_eval;
pub mod diagnostics;
pub mod build;
pub mod graphviz;
mod hair;

View File

@ -16,11 +16,10 @@
//! - `#[rustc_mir(graphviz="file.gv")]`
//! - `#[rustc_mir(pretty="file.mir")]`
extern crate syntax;
use build;
use rustc::dep_graph::DepNode;
use rustc::mir::repr::Mir;
use rustc::mir::transform::MirSource;
use pretty;
use hair::cx::Cx;
@ -28,13 +27,11 @@ use rustc::mir::mir_map::MirMap;
use rustc::infer;
use rustc::traits::ProjectionMode;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeMap;
use rustc::hir;
use rustc::hir::intravisit::{self, Visitor};
use syntax::abi::Abi;
use rustc::hir::intravisit::{self, FnKind, Visitor};
use rustc::hir::map::blocks::FnLikeNode;
use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
@ -42,7 +39,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
map: NodeMap(),
};
{
let mut dump = OuterDump {
let mut dump = BuildMir {
tcx: tcx,
map: &mut map,
};
@ -52,159 +49,158 @@ pub fn build_mir_for_crate<'tcx>(tcx: &TyCtxt<'tcx>) -> MirMap<'tcx> {
}
///////////////////////////////////////////////////////////////////////////
// OuterDump -- walks a crate, looking for fn items and methods to build MIR from
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
struct OuterDump<'a, 'tcx: 'a> {
struct BuildMir<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>,
map: &'a mut MirMap<'tcx>,
}
impl<'a, 'tcx> OuterDump<'a, 'tcx> {
fn visit_mir<OP>(&mut self, attributes: &'a [ast::Attribute], mut walk_op: OP)
where OP: for<'m> FnMut(&mut InnerDump<'a, 'm, 'tcx>)
impl<'a, 'tcx> BuildMir<'a, 'tcx> {
fn build<F>(&mut self, src: MirSource, f: F)
where F: for<'b> FnOnce(Cx<'b, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec)
{
let mut closure_dump = InnerDump {
tcx: self.tcx,
attr: None,
map: &mut *self.map,
};
for attr in attributes {
if attr.check_name("rustc_mir") {
closure_dump.attr = Some(attr);
let constness = match src {
MirSource::Const(_) |
MirSource::Static(..) => hir::Constness::Const,
MirSource::Fn(id) => {
let fn_like = FnLikeNode::from_node(self.tcx.map.get(id));
match fn_like.map(|f| f.kind()) {
Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
Some(FnKind::Method(_, m, _, _)) => m.constness,
_ => hir::Constness::NotConst
}
}
}
walk_op(&mut closure_dump);
}
}
impl<'a, 'tcx> Visitor<'tcx> for OuterDump<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
self.visit_mir(&item.attrs, |c| intravisit::walk_item(c, item));
intravisit::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| intravisit::walk_trait_item(c, trait_item));
}
hir::MethodTraitItem(_, None) |
hir::ConstTraitItem(..) |
hir::TypeTraitItem(..) => {}
}
intravisit::walk_trait_item(self, trait_item);
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
match impl_item.node {
hir::ImplItemKind::Method(..) => {
self.visit_mir(&impl_item.attrs, |c| intravisit::walk_impl_item(c, impl_item));
}
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(..) => {}
}
intravisit::walk_impl_item(self, impl_item);
}
}
///////////////////////////////////////////////////////////////////////////
// InnerDump -- dumps MIR for a single fn and its contained closures
struct InnerDump<'a, 'm, 'tcx: 'a + 'm> {
tcx: &'a TyCtxt<'tcx>,
map: &'m mut MirMap<'tcx>,
attr: Option<&'a ast::Attribute>,
}
impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {
// ignore methods; the outer dump will call us for them independently
}
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {
// ignore methods; the outer dump will call us for them independently
}
fn visit_fn(&mut self,
fk: intravisit::FnKind<'tcx>,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block,
span: Span,
id: ast::NodeId) {
let implicit_arg_tys = if let intravisit::FnKind::Closure(..) = fk {
vec![closure_self_ty(&self.tcx, id, body.id)]
} else {
vec![]
MirSource::Promoted(..) => bug!()
};
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id());
let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
Some(param_env),
ProjectionMode::AnyFinal);
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
Err(ErrorReported) => {}
}
let (mir, scope_auxiliary) = f(Cx::new(&infcx, constness));
intravisit::walk_fn(self, fk, decl, body, span);
pretty::dump_mir(self.tcx, "mir_map", &0, src, &mir, Some(&scope_auxiliary));
assert!(self.map.map.insert(src.item_id(), mir).is_none())
}
fn build_const_integer(&mut self, expr: &'tcx hir::Expr) {
// FIXME(eddyb) Closures should have separate
// function definition IDs and expression IDs.
// Type-checking should not let closures get
// this far in an integer constant position.
if let hir::ExprClosure(..) = expr.node {
return;
}
self.build(MirSource::Const(expr.id), |cx| {
build::construct_const(cx, expr.id, expr)
});
}
}
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<'tcx>, ErrorReported> {
// fetch the fully liberated fn signature (that is, all bound
// types/lifetimes replaced)
let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
Some(f) => f.clone(),
None => {
span_bug!(span, "no liberated fn sig for {:?}", fn_id);
}
};
let arguments =
decl.inputs
.iter()
.enumerate()
.map(|(index, arg)| {
(fn_sig.inputs[index], &*arg.pat)
})
.collect();
let (mut mir, scope_auxiliary) =
build::construct(cx,
span,
fn_id,
body.id,
implicit_arg_tys,
arguments,
fn_sig.output,
body);
match cx.tcx().node_id_to_type(fn_id).sty {
ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
// RustCall pseudo-ABI untuples the last argument.
if let Some(arg_decl) = mir.arg_decls.last_mut() {
arg_decl.spread = true;
impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
// Const and static items.
fn visit_item(&mut self, item: &'tcx hir::Item) {
match item.node {
hir::ItemConst(_, ref expr) => {
self.build(MirSource::Const(item.id), |cx| {
build::construct_const(cx, item.id, expr)
});
}
hir::ItemStatic(_, m, ref expr) => {
self.build(MirSource::Static(item.id, m), |cx| {
build::construct_const(cx, item.id, expr)
});
}
_ => {}
}
_ => {}
intravisit::walk_item(self, item);
}
pretty::dump_mir(cx.tcx(),
"mir_map",
&0,
fn_id,
&mir,
Some(&scope_auxiliary));
// Trait associated const defaults.
fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) {
if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
self.build(MirSource::Const(item.id), |cx| {
build::construct_const(cx, item.id, expr)
});
}
intravisit::walk_trait_item(self, item);
}
Ok(mir)
// Impl associated const.
fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) {
if let hir::ImplItemKind::Const(_, ref expr) = item.node {
self.build(MirSource::Const(item.id), |cx| {
build::construct_const(cx, item.id, expr)
});
}
intravisit::walk_impl_item(self, item);
}
// Repeat counts, i.e. [expr; constant].
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
if let hir::ExprRepeat(_, ref count) = expr.node {
self.build_const_integer(count);
}
intravisit::walk_expr(self, expr);
}
// Array lengths, i.e. [T; constant].
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
if let hir::TyFixedLengthVec(_, ref length) = ty.node {
self.build_const_integer(length);
}
intravisit::walk_ty(self, ty);
}
// Enum variant discriminant values.
fn visit_variant(&mut self, v: &'tcx hir::Variant,
g: &'tcx hir::Generics, item_id: ast::NodeId) {
if let Some(ref expr) = v.node.disr_expr {
self.build_const_integer(expr);
}
intravisit::walk_variant(self, v, g, item_id);
}
fn visit_fn(&mut self,
fk: FnKind<'tcx>,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block,
span: Span,
id: ast::NodeId) {
// fetch the fully liberated fn signature (that is, all bound
// types/lifetimes replaced)
let fn_sig = match self.tcx.tables.borrow().liberated_fn_sigs.get(&id) {
Some(f) => f.clone(),
None => {
span_bug!(span, "no liberated fn sig for {:?}", id);
}
};
let implicit_argument = if let FnKind::Closure(..) = fk {
Some((closure_self_ty(&self.tcx, id, body.id), None))
} else {
None
};
let explicit_arguments =
decl.inputs
.iter()
.enumerate()
.map(|(index, arg)| {
(fn_sig.inputs[index], Some(&*arg.pat))
});
self.build(MirSource::Fn(id), |cx| {
let arguments = implicit_argument.into_iter().chain(explicit_arguments);
build::construct_fn(cx, id, arguments, fn_sig.output, body)
});
intravisit::walk_fn(self, fk, decl, body, span);
}
}
fn closure_self_ty<'a, 'tcx>(tcx: &TyCtxt<'tcx>,

View File

@ -9,7 +9,9 @@
// except according to those terms.
use build::{Location, ScopeAuxiliaryVec};
use rustc::hir;
use rustc::mir::repr::*;
use rustc::mir::transform::MirSource;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::fnv::FnvHashMap;
use std::fmt::Display;
@ -37,13 +39,14 @@ const INDENT: &'static str = " ";
pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
pass_name: &str,
disambiguator: &Display,
node_id: NodeId,
src: MirSource,
mir: &Mir<'tcx>,
auxiliary: Option<&ScopeAuxiliaryVec>) {
let filters = match tcx.sess.opts.debugging_opts.dump_mir {
None => return,
Some(ref filters) => filters,
};
let node_id = src.item_id();
let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
let is_matched =
filters.split("&")
@ -64,7 +67,7 @@ pub fn dump_mir<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
try!(writeln!(file, "// pass_name = {}", pass_name));
try!(writeln!(file, "// disambiguator = {}", disambiguator));
try!(writeln!(file, ""));
try!(write_mir_fn(tcx, node_id, mir, &mut file, auxiliary));
try!(write_mir_fn(tcx, src, mir, &mut file, auxiliary));
Ok(())
});
}
@ -76,8 +79,13 @@ pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>,
-> io::Result<()>
where I: Iterator<Item=(&'a NodeId, &'a Mir<'tcx>)>, 'tcx: 'a
{
for (&node_id, mir) in iter {
write_mir_fn(tcx, node_id, mir, w, None)?;
for (&id, mir) in iter {
let src = MirSource::from_node(tcx, id);
write_mir_fn(tcx, src, mir, w, None)?;
for (i, mir) in mir.promoted.iter().enumerate() {
write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w, None)?;
}
}
Ok(())
}
@ -88,7 +96,7 @@ enum Annotation {
}
pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>,
node_id: NodeId,
src: MirSource,
mir: &Mir<'tcx>,
w: &mut Write,
auxiliary: Option<&ScopeAuxiliaryVec>)
@ -111,7 +119,7 @@ pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>,
}
}
write_mir_intro(tcx, node_id, mir, w)?;
write_mir_intro(tcx, src, mir, w)?;
for block in mir.all_basic_blocks() {
write_basic_block(tcx, block, mir, w, &annotations)?;
}
@ -214,24 +222,39 @@ fn write_scope_tree(tcx: &TyCtxt,
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write)
fn write_mir_intro(tcx: &TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
-> io::Result<()> {
write!(w, "fn {}(", tcx.node_path_str(nid))?;
// fn argument types.
for (i, arg) in mir.arg_decls.iter().enumerate() {
if i > 0 {
write!(w, ", ")?;
}
write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
match src {
MirSource::Fn(_) => write!(w, "fn")?,
MirSource::Const(_) => write!(w, "const")?,
MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
MirSource::Promoted(_, i) => write!(w, "promoted{} in", i)?
}
write!(w, ") -> ")?;
write!(w, " {}", tcx.node_path_str(src.item_id()))?;
// fn return type.
match mir.return_ty {
ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?,
ty::FnOutput::FnDiverging => write!(w, "!")?,
if let MirSource::Fn(_) = src {
write!(w, "(")?;
// fn argument types.
for (i, arg) in mir.arg_decls.iter().enumerate() {
if i > 0 {
write!(w, ", ")?;
}
write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
}
write!(w, ") -> ")?;
// fn return type.
match mir.return_ty {
ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?,
ty::FnOutput::FnDiverging => write!(w, "!")?,
}
} else {
assert!(mir.arg_decls.is_empty());
write!(w, ": {} =", mir.return_ty.unwrap())?;
}
writeln!(w, " {{")?;

View File

@ -10,8 +10,7 @@
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, Pass};
use syntax::ast::NodeId;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc_data_structures::bitvec::BitVector;
@ -43,7 +42,7 @@ pub struct BreakCriticalEdges;
*/
impl<'tcx> MirPass<'tcx> for BreakCriticalEdges {
fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
break_critical_edges(mir);
}
}

View File

@ -16,8 +16,7 @@ use rustc::ty::subst::Substs;
use rustc::ty::{Ty, TyCtxt};
use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor;
use rustc::mir::transform::{MirPass, Pass};
use syntax::ast::NodeId;
use rustc::mir::transform::{MirPass, MirSource, Pass};
struct EraseRegionsVisitor<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>,
@ -47,7 +46,7 @@ pub struct EraseRegions;
impl Pass for EraseRegions {}
impl<'tcx> MirPass<'tcx> for EraseRegions {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
EraseRegionsVisitor::new(tcx).visit_mir(mir);
}
}

View File

@ -14,3 +14,5 @@ pub mod erase_regions;
pub mod no_landing_pads;
pub mod type_check;
pub mod break_critical_edges;
pub mod promote_consts;
pub mod qualify_consts;

View File

@ -14,8 +14,7 @@
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor;
use rustc::mir::transform::{Pass, MirPass};
use syntax::ast::NodeId;
use rustc::mir::transform::{Pass, MirPass, MirSource};
pub struct NoLandingPads;
@ -42,7 +41,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
}
impl<'tcx> MirPass<'tcx> for NoLandingPads {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
if tcx.sess.no_landing_pads() {
self.visit_mir(mir);
}

View File

@ -0,0 +1,412 @@
// Copyright 2016 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.
//! A pass that promotes borrows of constant rvalues.
//!
//! The rvalues considered constant are trees of temps,
//! each with exactly one initialization, and holding
//! a constant value with no interior mutability.
//! They are placed into a new MIR constant body in
//! `promoted` and the borrow rvalue is replaced with
//! a `Literal::Promoted` using the index into `promoted`
//! of that constant MIR.
//!
//! This pass assumes that every use is dominated by an
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.
use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::ty::{self, TyCtxt};
use syntax::codemap::Span;
use build::Location;
use traversal::ReversePostorder;
use std::mem;
/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState {
/// No references to this temp.
Undefined,
/// One direct assignment and any number of direct uses.
/// A borrow of this temp is promotable if the assigned
/// value is qualified as constant.
Defined {
location: Location,
uses: usize
},
/// Any other combination of assignments/uses.
Unpromotable,
/// This temp was part of an rvalue which got extracted
/// during promotion and needs cleanup.
PromotedOut
}
impl TempState {
pub fn is_promotable(&self) -> bool {
if let TempState::Defined { uses, .. } = *self {
uses > 0
} else {
false
}
}
}
/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
pub enum Candidate {
/// Borrow of a constant temporary.
Ref(Location),
/// Array of indices found in the third argument of
/// a call to one of the simd_shuffleN intrinsics.
ShuffleIndices(BasicBlock)
}
struct TempCollector {
temps: Vec<TempState>,
location: Location,
span: Span
}
impl<'tcx> Visitor<'tcx> for TempCollector {
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
self.super_lvalue(lvalue, context);
if let Lvalue::Temp(index) = *lvalue {
// Ignore drops, if the temp gets promoted,
// then it's constant and thus drop is noop.
if let LvalueContext::Drop = context {
return;
}
let temp = &mut self.temps[index as usize];
if *temp == TempState::Undefined {
match context {
LvalueContext::Store |
LvalueContext::Call => {
*temp = TempState::Defined {
location: self.location,
uses: 0
};
return;
}
_ => { /* mark as unpromotable below */ }
}
} else if let TempState::Defined { ref mut uses, .. } = *temp {
match context {
LvalueContext::Borrow {..} |
LvalueContext::Consume |
LvalueContext::Inspect => {
*uses += 1;
return;
}
_ => { /* mark as unpromotable below */ }
}
}
*temp = TempState::Unpromotable;
}
}
fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
assert_eq!(self.location.block, bb);
self.span = statement.span;
self.super_statement(bb, statement);
self.location.statement_index += 1;
}
fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
self.span = terminator.span;
self.super_terminator(bb, terminator);
}
fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
self.location.statement_index = 0;
self.location.block = bb;
self.super_basic_block_data(bb, data);
}
}
pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
let mut collector = TempCollector {
temps: vec![TempState::Undefined; mir.temp_decls.len()],
location: Location {
block: START_BLOCK,
statement_index: 0
},
span: mir.span
};
for (bb, data) in rpo {
collector.visit_basic_block_data(bb, data);
}
collector.temps
}
struct Promoter<'a, 'tcx: 'a> {
source: &'a mut Mir<'tcx>,
promoted: Mir<'tcx>,
temps: &'a mut Vec<TempState>,
/// If true, all nested temps are also kept in the
/// source MIR, not moved to the promoted MIR.
keep_original: bool
}
impl<'a, 'tcx> Promoter<'a, 'tcx> {
fn new_block(&mut self) -> BasicBlock {
let index = self.promoted.basic_blocks.len();
self.promoted.basic_blocks.push(BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
span: self.promoted.span,
scope: ScopeId::new(0),
kind: TerminatorKind::Return
}),
is_cleanup: false
});
BasicBlock::new(index)
}
fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
let data = self.promoted.basic_blocks.last_mut().unwrap();
data.statements.push(Statement {
span: span,
scope: ScopeId::new(0),
kind: StatementKind::Assign(dest, rvalue)
});
}
/// Copy the initialization of this temp to the
/// promoted MIR, recursing through temps.
fn promote_temp(&mut self, index: u32) -> u32 {
let index = index as usize;
let old_keep_original = self.keep_original;
let (bb, stmt_idx) = match self.temps[index] {
TempState::Defined {
location: Location { block, statement_index },
uses
} if uses > 0 => {
if uses > 1 {
self.keep_original = true;
}
(block, statement_index)
}
temp => {
span_bug!(self.promoted.span, "tmp{} not promotable: {:?}",
index, temp);
}
};
if !self.keep_original {
self.temps[index] = TempState::PromotedOut;
}
let no_stmts = self.source[bb].statements.len();
// First, take the Rvalue or Call out of the source MIR,
// or duplicate it, depending on keep_original.
let (mut rvalue, mut call) = (None, None);
let span = if stmt_idx < no_stmts {
let statement = &mut self.source[bb].statements[stmt_idx];
let StatementKind::Assign(_, ref mut rhs) = statement.kind;
if self.keep_original {
rvalue = Some(rhs.clone());
} else {
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
rvalue = Some(mem::replace(rhs, unit));
}
statement.span
} else if self.keep_original {
let terminator = self.source[bb].terminator().clone();
call = Some(terminator.kind);
terminator.span
} else {
let terminator = self.source[bb].terminator_mut();
let target = match terminator.kind {
TerminatorKind::Call {
destination: ref mut dest @ Some(_),
ref mut cleanup, ..
} => {
// No cleanup necessary.
cleanup.take();
// We'll put a new destination in later.
dest.take().unwrap().1
}
ref kind => {
span_bug!(terminator.span, "{:?} not promotable", kind);
}
};
call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
target: target
}));
terminator.span
};
// Then, recurse for components in the Rvalue or Call.
if stmt_idx < no_stmts {
self.visit_rvalue(rvalue.as_mut().unwrap());
} else {
self.visit_terminator_kind(bb, call.as_mut().unwrap());
}
let new_index = self.promoted.temp_decls.len() as u32;
let new_temp = Lvalue::Temp(new_index);
self.promoted.temp_decls.push(TempDecl {
ty: self.source.temp_decls[index].ty
});
// Inject the Rvalue or Call into the promoted MIR.
if stmt_idx < no_stmts {
self.assign(new_temp, rvalue.unwrap(), span);
} else {
let last = self.promoted.basic_blocks.len() - 1;
let new_target = self.new_block();
let mut call = call.unwrap();
match call {
TerminatorKind::Call { ref mut destination, ..} => {
*destination = Some((new_temp, new_target));
}
_ => bug!()
}
let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
terminator.span = span;
terminator.kind = call;
}
// Restore the old duplication state.
self.keep_original = old_keep_original;
new_index
}
fn promote_candidate(mut self, candidate: Candidate) {
let span = self.promoted.span;
let new_operand = Operand::Constant(Constant {
span: span,
ty: self.promoted.return_ty.unwrap(),
literal: Literal::Promoted {
index: self.source.promoted.len()
}
});
let mut rvalue = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
match self.source[bb].statements[stmt_idx].kind {
StatementKind::Assign(_, ref mut rvalue) => {
mem::replace(rvalue, Rvalue::Use(new_operand))
}
}
}
Candidate::ShuffleIndices(bb) => {
match self.source[bb].terminator_mut().kind {
TerminatorKind::Call { ref mut args, .. } => {
Rvalue::Use(mem::replace(&mut args[2], new_operand))
}
_ => bug!()
}
}
};
self.visit_rvalue(&mut rvalue);
self.assign(Lvalue::ReturnPointer, rvalue, span);
self.source.promoted.push(self.promoted);
}
}
/// Replaces all temporaries with their promoted counterparts.
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) {
if let Lvalue::Temp(ref mut index) = *lvalue {
*index = self.promote_temp(*index);
}
self.super_lvalue(lvalue, context);
}
}
pub fn promote_candidates<'tcx>(mir: &mut Mir<'tcx>,
tcx: &TyCtxt<'tcx>,
mut temps: Vec<TempState>,
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
for candidate in candidates.into_iter().rev() {
let (span, ty) = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
let statement = &mir[bb].statements[stmt_idx];
let StatementKind::Assign(ref dest, _) = statement.kind;
if let Lvalue::Temp(index) = *dest {
if temps[index as usize] == TempState::PromotedOut {
// Already promoted.
continue;
}
}
(statement.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
}
Candidate::ShuffleIndices(bb) => {
let terminator = mir[bb].terminator();
let ty = match terminator.kind {
TerminatorKind::Call { ref args, .. } => {
mir.operand_ty(tcx, &args[2])
}
_ => {
span_bug!(terminator.span,
"expected simd_shuffleN call to promote");
}
};
(terminator.span, ty)
}
};
let mut promoter = Promoter {
source: mir,
promoted: Mir {
basic_blocks: vec![],
scopes: vec![ScopeData {
span: span,
parent_scope: None
}],
promoted: vec![],
return_ty: ty::FnConverging(ty),
var_decls: vec![],
arg_decls: vec![],
temp_decls: vec![],
upvar_decls: vec![],
span: span
},
temps: &mut temps,
keep_original: false
};
assert_eq!(promoter.new_block(), START_BLOCK);
promoter.promote_candidate(candidate);
}
// Eliminate assignments to, and drops of promoted temps.
let promoted = |index: u32| temps[index as usize] == TempState::PromotedOut;
for block in &mut mir.basic_blocks {
block.statements.retain(|statement| {
match statement.kind {
StatementKind::Assign(Lvalue::Temp(index), _) => {
!promoted(index)
}
_ => true
}
});
let terminator = block.terminator_mut();
match terminator.kind {
TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => {
if promoted(index) {
terminator.kind = TerminatorKind::Goto {
target: target
};
}
}
_ => {}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -35,13 +35,12 @@
use rustc_data_structures::bitvec::BitVector;
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{Pass, MirPass};
use syntax::ast::NodeId;
use rustc::mir::transform::{Pass, MirPass, MirSource};
pub struct RemoveDeadBlocks;
impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks {
fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
let mut seen = BitVector::new(mir.basic_blocks.len());
// This block is always required.
seen.insert(START_BLOCK.index());

View File

@ -11,9 +11,8 @@
use rustc::middle::const_val::ConstVal;
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, Pass};
use rustc::mir::transform::{MirPass, MirSource, Pass};
use pretty;
use syntax::ast::NodeId;
use super::remove_dead_blocks::RemoveDeadBlocks;
@ -112,15 +111,15 @@ impl SimplifyCfg {
}
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
let mut counter = 0;
let mut changed = true;
while changed {
pretty::dump_mir(tcx, "simplify_cfg", &counter, id, mir, None);
pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None);
counter += 1;
changed = self.simplify_branches(mir);
changed |= self.remove_goto_chains(mir);
RemoveDeadBlocks.run_pass(tcx, id, mir);
RemoveDeadBlocks.run_pass(tcx, src, mir);
}
// FIXME: Should probably be moved into some kind of pass manager
mir.basic_blocks.shrink_to_fit();

View File

@ -12,16 +12,16 @@
#![allow(unreachable_code)]
use rustc::dep_graph::DepNode;
use rustc::hir::def_id::DefId;
use rustc::infer::{self, InferCtxt, InferOk};
use rustc::traits::{self, ProjectionMode};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::mir::repr::*;
use rustc::mir::tcx::LvalueTy;
use rustc::mir::transform::{MirPass, Pass};
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::visit::{self, Visitor};
use std::fmt;
use syntax::ast::NodeId;
use syntax::codemap::{Span, DUMMY_SP};
macro_rules! span_mirbug {
@ -578,15 +578,13 @@ impl TypeckMir {
}
impl<'tcx> MirPass<'tcx> for TypeckMir {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
if tcx.sess.err_count() > 0 {
// compiling a broken program can obviously result in a
// broken MIR, so try not to report duplicate errors.
return;
}
let def_id = tcx.map.local_def_id(id);
let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(def_id));
let param_env = ty::ParameterEnvironment::for_item(tcx, id);
let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id());
let infcx = infer::new_infer_ctxt(tcx,
&tcx.tables,
Some(param_env),
@ -605,4 +603,8 @@ impl<'tcx> MirPass<'tcx> for TypeckMir {
}
}
impl Pass for TypeckMir {}
impl Pass for TypeckMir {
fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
DepNode::MirTypeck(def_id)
}
}

View File

@ -1,118 +0,0 @@
// 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.
//! Verifies that const fn arguments are immutable by value bindings
//! and the const fn body doesn't contain any statements
use rustc::session::{Session, CompileResult};
use syntax::ast::{self, PatKind};
use syntax::visit::{self, Visitor, FnKind};
use syntax::codemap::Span;
pub fn check_crate(sess: &Session, krate: &ast::Crate) -> CompileResult {
sess.track_errors(|| {
visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
})
}
struct CheckConstFn<'a> {
sess: &'a Session,
}
struct CheckBlock<'a> {
sess: &'a Session,
kind: &'static str,
}
impl<'a, 'v> Visitor<'v> for CheckBlock<'a> {
fn visit_block(&mut self, block: &'v ast::Block) {
check_block(&self.sess, block, self.kind);
CheckConstFn{ sess: self.sess}.visit_block(block);
}
fn visit_expr(&mut self, e: &'v ast::Expr) {
if let ast::ExprKind::Closure(..) = e.node {
CheckConstFn{ sess: self.sess}.visit_expr(e);
} else {
visit::walk_expr(self, e);
}
}
fn visit_item(&mut self, _i: &'v ast::Item) { bug!("should be handled in CheckConstFn") }
fn visit_fn(&mut self,
_fk: FnKind<'v>,
_fd: &'v ast::FnDecl,
_b: &'v ast::Block,
_s: Span,
_fn_id: ast::NodeId) { bug!("should be handled in CheckConstFn") }
}
fn check_block(sess: &Session, b: &ast::Block, kind: &'static str) {
// Check all statements in the block
for stmt in &b.stmts {
let span = match stmt.node {
ast::StmtKind::Decl(ref decl, _) => {
match decl.node {
ast::DeclKind::Local(_) => decl.span,
// Item statements are allowed
ast::DeclKind::Item(_) => continue,
}
}
ast::StmtKind::Expr(ref expr, _) => expr.span,
ast::StmtKind::Semi(ref semi, _) => semi.span,
ast::StmtKind::Mac(..) => bug!(),
};
span_err!(sess, span, E0016,
"blocks in {}s are limited to items and tail expressions", kind);
}
}
impl<'a, 'v> Visitor<'v> for CheckConstFn<'a> {
fn visit_item(&mut self, i: &'v ast::Item) {
visit::walk_item(self, i);
match i.node {
ast::ItemKind::Const(_, ref e) => {
CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
},
ast::ItemKind::Static(_, _, ref e) => {
CheckBlock{ sess: self.sess, kind: "static"}.visit_expr(e)
},
_ => {},
}
}
fn visit_fn(&mut self,
fk: FnKind<'v>,
fd: &'v ast::FnDecl,
b: &'v ast::Block,
s: Span,
_fn_id: ast::NodeId) {
visit::walk_fn(self, fk, fd, b, s);
match fk {
FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {},
FnKind::Method(_, m, _) if m.constness == ast::Constness::Const => {},
_ => return,
}
// Ensure the arguments are simple, not mutable/by-ref or patterns.
for arg in &fd.inputs {
match arg.pat.node {
PatKind::Wild => {}
PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable), _, None) => {}
_ => {
span_err!(self.sess, arg.pat.span, E0022,
"arguments of constant functions can only \
be immutable by-value bindings");
}
}
}
check_block(&self.sess, b, "const function");
}
}

View File

@ -40,7 +40,7 @@ use rustc::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::traits::{self, ProjectionMode};
use rustc::traits::ProjectionMode;
use rustc::util::nodemap::NodeMap;
use rustc::middle::const_qualif::ConstQualif;
use rustc::lint::builtin::CONST_ERR;
@ -48,7 +48,6 @@ use rustc::lint::builtin::CONST_ERR;
use rustc::hir::{self, PatKind};
use syntax::ast;
use syntax::codemap::Span;
use syntax::feature_gate::UnstableFeatures;
use rustc::hir::intravisit::{self, FnKind, Visitor};
use std::collections::hash_map::Entry;
@ -180,31 +179,11 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
/// Returns true if the call is to a const fn or method.
fn handle_const_fn_call(&mut self,
expr: &hir::Expr,
_expr: &hir::Expr,
def_id: DefId,
ret_ty: Ty<'tcx>)
-> bool {
if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
if
// we are in a static/const initializer
self.mode != Mode::Var &&
// feature-gate is not enabled
!self.tcx.sess.features.borrow().const_fn &&
// this doesn't come from a macro that has #[allow_internal_unstable]
!self.tcx.sess.codemap().span_allows_unstable(expr.span)
{
let mut err = self.tcx.sess.struct_span_err(
expr.span,
"const fns are an unstable feature");
help!(
&mut err,
"in Nightly builds, add `#![feature(const_fn)]` to the crate \
attributes to enable");
err.emit();
}
let qualif = self.fn_like(fn_like.kind(),
fn_like.decl(),
fn_like.body(),
@ -245,42 +224,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
Mode::Var => bug!(),
}
}
fn check_static_mut_type(&self, e: &hir::Expr) {
let node_ty = self.tcx.node_id_to_type(e.id);
let tcontents = node_ty.type_contents(self.tcx);
let suffix = if tcontents.has_dtor() {
"destructors"
} else if tcontents.owns_owned() {
"boxes"
} else {
return
};
span_err!(self.tcx.sess, e.span, E0397,
"mutable statics are not allowed to have {}", suffix);
}
fn check_static_type(&self, e: &hir::Expr) {
let ty = self.tcx.node_id_to_type(e.id);
let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
None,
ProjectionMode::AnyFinal);
let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
let mut fulfillment_cx = traits::FulfillmentContext::new();
fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
match fulfillment_cx.select_all_or_error(&infcx) {
Ok(()) => { },
Err(ref errors) => {
traits::report_fulfillment_errors(&infcx, errors);
}
}
if let Err(ref errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
traits::report_fulfillment_errors_as_warnings(&infcx, errors, e.id);
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
@ -289,11 +232,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
assert_eq!(self.mode, Mode::Var);
match i.node {
hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
self.check_static_type(&expr);
self.global_expr(Mode::Static, &expr);
}
hir::ItemStatic(_, hir::MutMutable, ref expr) => {
self.check_static_mut_type(&expr);
self.global_expr(Mode::StaticMut, &expr);
}
hir::ItemConst(_, ref expr) => {
@ -360,8 +301,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
"lower range bound must be less than or equal to upper");
}
None => {
self.tcx.sess.delay_span_bug(start.span,
"non-constant path in constant expr");
span_err!(self.tcx.sess, p.span, E0014,
"paths in {}s may only refer to constants",
self.msg());
}
}
}
@ -384,8 +326,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
hir::StmtSemi(_, _) => {},
}
self.add_qualif(ConstQualif::NOT_CONST);
// anything else should have been caught by check_const_fn
assert_eq!(self.mode, Mode::Var);
}
intravisit::walk_block(self, block);
}
@ -455,11 +395,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
let tc = node_ty.type_contents(self.tcx);
if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
outer = outer | ConstQualif::NOT_CONST;
if self.mode != Mode::Var {
span_err!(self.tcx.sess, ex.span, E0492,
"cannot borrow a constant which contains \
interior mutability, create a static instead");
}
}
// If the reference has to be 'static, avoid in-place initialization
// as that will end up pointing to the stack instead.
@ -474,10 +409,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
if self.mode == Mode::Var {
outer = outer | ConstQualif::NOT_CONST;
self.add_qualif(ConstQualif::MUTABLE_MEM);
} else {
span_err!(self.tcx.sess, ex.span, E0017,
"references in {}s may only refer \
to immutable values", self.msg())
}
}
if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
@ -525,11 +456,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
ty::TyStruct(def, _) |
ty::TyEnum(def, _) if def.has_dtor() => {
v.add_qualif(ConstQualif::NEEDS_DROP);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0493,
"{}s are not allowed to have destructors",
v.msg());
}
}
_ => {}
}
@ -540,17 +466,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
hir::ExprBinary(..) |
hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0011,
"user-defined operators are not allowed in {}s", v.msg());
}
}
hir::ExprBox(_) => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0010,
"allocations are not allowed in {}s", v.msg());
}
}
hir::ExprUnary(op, ref inner) => {
match v.tcx.node_id_to_type(inner.id).sty {
@ -558,10 +476,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
assert!(op == hir::UnDeref);
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0396,
"raw pointers cannot be dereferenced in {}s", v.msg());
}
}
_ => {}
}
@ -574,10 +488,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
op.node == hir::BiGe || op.node == hir::BiGt);
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0395,
"raw pointers cannot be compared in {}s", v.msg());
}
}
_ => {}
}
@ -588,10 +498,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
None => span_bug!(e.span, "no kind for cast"),
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0018,
"raw pointers cannot be cast to integers in {}s", v.msg());
}
}
_ => {}
}
@ -616,11 +522,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
Some(Def::Static(..)) => {
match v.mode {
Mode::Static | Mode::StaticMut => {}
Mode::Const | Mode::ConstFn => {
span_err!(v.tcx.sess, e.span, E0013,
"{}s cannot refer to other statics, insert \
an intermediate constant instead", v.msg());
}
Mode::Const | Mode::ConstFn => {}
Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
}
}
@ -636,14 +538,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
// Sadly, we can't determine whether the types are zero-sized.
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
}
def => {
_ => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
debug!("(checking const) found bad def: {:?}", def);
span_err!(v.tcx.sess, e.span, E0014,
"paths in {}s may only refer to constants \
or functions", v.msg());
}
}
}
}
@ -681,29 +577,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
};
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
// FIXME(#24111) Remove this check when const fn stabilizes
let (msg, note) =
if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
(format!("function calls in {}s are limited to \
struct and enum constructors",
v.msg()),
Some("a limited form of compile-time function \
evaluation is available on a nightly \
compiler via `const fn`"))
} else {
(format!("function calls in {}s are limited \
to constant functions, \
struct and enum constructors",
v.msg()),
None)
};
let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
if let Some(note) = note {
err.span_note(e.span, note);
}
err.emit();
}
}
}
hir::ExprMethodCall(..) => {
@ -714,11 +587,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
};
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0378,
"method calls in {}s are limited to \
constant inherent methods", v.msg());
}
}
}
hir::ExprStruct(..) => {
@ -773,10 +641,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
hir::ExprAssignOp(..) |
hir::ExprInlineAsm(..) => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0019,
"{} contains unimplemented expression type", v.msg());
}
}
}
}
@ -796,11 +660,6 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
v.tcx.is_overloaded_autoderef(e.id, autoderef)
}) {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0400,
"user-defined dereference operators are not allowed in {}s",
v.msg());
}
}
}
}
@ -819,21 +678,13 @@ pub fn check_crate(tcx: &TyCtxt) {
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
fn consume(&mut self,
_consume_id: ast::NodeId,
consume_span: Span,
_consume_span: Span,
cmt: mc::cmt,
_mode: euv::ConsumeMode) {
let mut cur = &cmt;
loop {
match cur.cat {
Categorization::StaticItem => {
if self.mode != Mode::Var {
// statics cannot be consumed by value at any time, that would imply
// that they're an initializer (what a const is for) or kept in sync
// over time (not feasible), so deny it outright.
span_err!(self.tcx.sess, consume_span, E0394,
"cannot refer to other statics by value, use the \
address-of operator or a constant instead");
}
break;
}
Categorization::Deref(ref cmt, _, _) |
@ -848,7 +699,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
}
fn borrow(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
_borrow_span: Span,
cmt: mc::cmt<'tcx>,
_loan_region: ty::Region,
bk: ty::BorrowKind,
@ -866,7 +717,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
}
let mut cur = &cmt;
let mut is_interior = false;
loop {
match cur.cat {
Categorization::Rvalue(..) => {
@ -891,20 +741,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
break;
}
Categorization::StaticItem => {
if is_interior && self.mode != Mode::Var {
// Borrowed statics can specifically *only* have their address taken,
// not any number of other borrows such as borrowing fields, reading
// elements of an array, etc.
span_err!(self.tcx.sess, borrow_span, E0494,
"cannot refer to the interior of another \
static, use a constant instead");
}
break;
}
Categorization::Deref(ref cmt, _, _) |
Categorization::Downcast(ref cmt, _) |
Categorization::Interior(ref cmt, _) => {
is_interior = true;
cur = cmt;
}

View File

@ -12,70 +12,6 @@
register_long_diagnostics! {
E0010: r##"
The value of statics and constants must be known at compile time, and they live
for the entire lifetime of a program. Creating a boxed value allocates memory on
the heap at runtime, and therefore cannot be done at compile time. Erroneous
code example:
```compile_fail
#![feature(box_syntax)]
const CON : Box<i32> = box 0;
```
"##,
E0011: r##"
Initializers for constants and statics are evaluated at compile time.
User-defined operators rely on user-defined functions, which cannot be evaluated
at compile time.
Erroneous code example:
```compile_fail
use std::ops::Index;
struct Foo { a: u8 }
impl Index<u8> for Foo {
type Output = u8;
fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
}
const a: Foo = Foo { a: 0u8 };
const b: u8 = a[0]; // Index trait is defined by the user, bad!
```
Only operators on builtin types are allowed.
Example:
```
const a: &'static [i32] = &[1, 2, 3];
const b: i32 = a[0]; // Ok!
```
"##,
E0013: r##"
Static and const variables can refer to other const variables. But a const
variable cannot refer to a static variable. For example, `Y` cannot refer to
`X` here:
```compile_fail
static X: i32 = 42;
const Y: i32 = X;
```
To fix this, the value can be extracted as a const and then used:
```
const A: i32 = 42;
static X: i32 = A;
const Y: i32 = A;
```
"##,
E0014: r##"
Constants can only be initialized by a constant value or, in a future
version of Rust, a call to a const function. This error indicates the use
@ -95,149 +31,6 @@ const FOO2: i32 = { 0 }; // but brackets are useless here
```
"##,
// FIXME(#24111) Change the language here when const fn stabilizes
E0015: r##"
The only functions that can be called in static or constant expressions are
`const` functions, and struct/enum constructors. `const` functions are only
available on a nightly compiler. Rust currently does not support more general
compile-time function execution.
```
const FOO: Option<u8> = Some(1); // enum constructor
struct Bar {x: u8}
const BAR: Bar = Bar {x: 1}; // struct constructor
```
See [RFC 911] for more details on the design of `const fn`s.
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0016: r##"
Blocks in constants may only contain items (such as constant, function
definition, etc...) and a tail expression. Erroneous code example:
```compile_fail
const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
```
To avoid it, you have to replace the non-item object:
```
const FOO: i32 = { const X : i32 = 0; X };
```
"##,
E0017: r##"
References in statics and constants may only refer to immutable values.
Erroneous code example:
```compile_fail
static X: i32 = 1;
const C: i32 = 2;
// these three are not allowed:
const CR: &'static mut i32 = &mut C;
static STATIC_REF: &'static mut i32 = &mut X;
static CONST_REF: &'static mut i32 = &mut C;
```
Statics are shared everywhere, and if they refer to mutable data one might
violate memory safety since holding multiple mutable references to shared data
is not allowed.
If you really want global mutable state, try using `static mut` or a global
`UnsafeCell`.
"##,
E0018: r##"
The value of static and constant integers must be known at compile time. You
can't cast a pointer to an integer because the address of a pointer can
vary.
For example, if you write:
```compile_fail
static MY_STATIC: u32 = 42;
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
```
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
the address can change when the program is linked, as well as change
between different executions due to ASLR, and many linkers would
not be able to calculate the value of `WHAT`.
On the other hand, static and constant pointers can point either to
a known numeric address or to the address of a symbol.
```
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
// ... and also
static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
```
This does not pose a problem by itself because they can't be
accessed directly.
"##,
E0019: r##"
A function call isn't allowed in the const's initialization expression
because the expression's value must be known at compile-time. Erroneous code
example:
```compile_fail
enum Test {
V1
}
impl Test {
fn test(&self) -> i32 {
12
}
}
fn main() {
const FOO: Test = Test::V1;
const A: i32 = FOO.test(); // You can't call Test::func() here !
}
```
Remember: you can't use a function call inside a const's initialization
expression! However, you can totally use it anywhere else:
```
fn main() {
const FOO: Test = Test::V1;
FOO.func(); // here is good
let x = FOO.func(); // or even here!
}
```
"##,
E0022: r##"
Constant functions are not allowed to mutate anything. Thus, binding to an
argument with a mutable pattern is not allowed. For example,
```compile_fail
const fn foo(mut x: u8) {
// do stuff
}
```
Is incorrect because the function body may not mutate `x`.
Remove any mutable bindings from the argument list to fix this error. In case
you need to mutate the argument, try lazily initializing a global variable
instead of using a `const fn`, or refactoring the code to a functional style to
avoid mutation if possible.
"##,
E0030: r##"
When matching against a range, the compiler verifies that the range is
non-empty. Range patterns include both end-points, so this is equivalent to
@ -325,281 +118,6 @@ fn some_func() {
```
"##,
E0378: r##"
Method calls that aren't calls to inherent `const` methods are disallowed
in statics, constants, and constant functions.
For example:
```compile_fail
const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
struct Foo(i32);
impl Foo {
const fn foo(&self) -> i32 {
self.bar() // error, `bar` isn't `const`
}
fn bar(&self) -> i32 { self.0 }
}
```
For more information about `const fn`'s, see [RFC 911].
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0394: r##"
From [RFC 246]:
> It is invalid for a static to reference another static by value. It is
> required that all references be borrowed.
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
"##,
E0395: r##"
The value assigned to a constant scalar must be known at compile time,
which is not the case when comparing raw pointers.
Erroneous code example:
```compile_fail
static FOO: i32 = 42;
static BAR: i32 = 42;
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// error: raw pointers cannot be compared in statics!
```
The address assigned by the linker to `FOO` and `BAR` may or may not
be identical, so the value of `BAZ` can't be determined.
If you want to do the comparison, please do it at run-time.
For example:
```
static FOO: i32 = 42;
static BAR: i32 = 42;
let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
// baz isn't a constant expression so it's ok
```
"##,
E0396: r##"
The value behind a raw pointer can't be determined at compile-time
(or even link-time), which means it can't be used in a constant
expression. Erroneous code example:
```compile_fail
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
const VALUE: u8 = unsafe { *REG_ADDR };
// error: raw pointers cannot be dereferenced in constants
```
A possible fix is to dereference your pointer at some point in run-time.
For example:
```
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
let reg_value = unsafe { *REG_ADDR };
```
"##,
E0397: r##"
It is not allowed for a mutable static to allocate or have destructors. For
example:
```compile_fail
// error: mutable statics are not allowed to have boxes
static mut FOO: Option<Box<usize>> = None;
// error: mutable statics are not allowed to have destructors
static mut BAR: Option<Vec<i32>> = None;
```
"##,
E0400: r##"
A user-defined dereference was attempted in an invalid context. Erroneous
code example:
```compile_fail
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
const S: &'static str = &A;
// error: user-defined dereference operators are not allowed in constants
fn main() {
let foo = S;
}
```
You cannot directly use a dereference operation whilst initializing a constant
or a static. To fix this error, restructure your code to avoid this dereference,
perhaps moving it inline:
```
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
fn main() {
let foo : &str = &A;
}
```
"##,
E0492: r##"
A borrow of a constant containing interior mutability was attempted. Erroneous
code example:
```compile_fail
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
const A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A;
// error: cannot borrow a constant which contains interior mutability, create a
// static instead
```
A `const` represents a constant value that should never change. If one takes
a `&` reference to the constant, then one is taking a pointer to some memory
location containing the value. Normally this is perfectly fine: most values
can't be changed via a shared `&` pointer, but interior mutability would allow
it. That is, a constant value could be mutated. On the other hand, a `static` is
explicitly a single memory location, which can be mutated at will.
So, in order to solve this error, either use statics which are `Sync`:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
static A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A; // ok!
```
You can also have this error while using a cell type:
```compile_fail
#![feature(const_fn)]
use std::cell::Cell;
const A: Cell<usize> = Cell::new(1);
const B: &'static Cell<usize> = &A;
// error: cannot borrow a constant which contains interior mutability, create
// a static instead
// or:
struct C { a: Cell<usize> }
const D: C = C { a: Cell::new(1) };
const E: &'static Cell<usize> = &D.a; // error
// or:
const F: &'static C = &D; // error
```
This is because cell types do operations that are not thread-safe. Due to this,
they don't implement Sync and thus can't be placed in statics. In this
case, `StaticMutex` would work just fine, but it isn't stable yet:
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
However, if you still wish to use these types, you can achieve this by an unsafe
wrapper:
```
#![feature(const_fn)]
use std::cell::Cell;
use std::marker::Sync;
struct NotThreadSafe<T> {
value: Cell<T>,
}
unsafe impl<T> Sync for NotThreadSafe<T> {}
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
static B: &'static NotThreadSafe<usize> = &A; // ok!
```
Remember this solution is unsafe! You will have to ensure that accesses to the
cell are synchronized.
"##,
E0493: r##"
A type with a destructor was assigned to an invalid type of variable. Erroneous
code example:
```compile_fail
struct Foo {
a: u32
}
impl Drop for Foo {
fn drop(&mut self) {}
}
const F : Foo = Foo { a : 0 };
// error: constants are not allowed to have destructors
static S : Foo = Foo { a : 0 };
// error: statics are not allowed to have destructors
```
To solve this issue, please use a type which does allow the usage of type with
destructors.
"##,
E0494: r##"
A reference of an interior static was assigned to another const/static.
Erroneous code example:
```compile_fail
struct Foo {
a: u32
}
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a;
// error: cannot refer to the interior of another static, use a
// constant instead
```
The "base" variable has to be a const if you want another static/const variable
to refer to one of its fields. Example:
```
struct Foo {
a: u32
}
const S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; // ok!
```
"##,
}
register_diagnostics! {

View File

@ -37,7 +37,6 @@ extern crate rustc_const_math;
pub mod diagnostics;
pub mod const_fn;
pub mod consts;
pub mod loops;
pub mod no_asm;

View File

@ -414,6 +414,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
};
visitor.visit_mir(&mir);
for promoted in &mir.promoted {
visitor.visit_mir(promoted);
}
}
}

View File

@ -39,7 +39,7 @@ use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::cast::{CastTy,IntTy};
use util::nodemap::NodeMap;
use rustc_const_math::{ConstInt, ConstMathErr, ConstUsize, ConstIsize};
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
use rustc::hir;
@ -48,6 +48,7 @@ use std::borrow::Cow;
use libc::c_uint;
use syntax::ast::{self, LitKind};
use syntax::attr::{self, AttrMetaMethods};
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ptr::P;
@ -110,11 +111,11 @@ pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef {
}
}
fn addr_of_mut(ccx: &CrateContext,
cv: ValueRef,
align: machine::llalign,
kind: &str)
-> ValueRef {
pub fn addr_of_mut(ccx: &CrateContext,
cv: ValueRef,
align: machine::llalign,
kind: &str)
-> ValueRef {
unsafe {
// FIXME: this totally needs a better name generation scheme, perhaps a simple global
// counter? Also most other uses of gensym in trans.
@ -158,13 +159,13 @@ pub fn addr_of(ccx: &CrateContext,
}
/// Deref a constant pointer
fn load_const(cx: &CrateContext, v: ValueRef, t: Ty) -> ValueRef {
pub fn load_const(cx: &CrateContext, v: ValueRef, t: Ty) -> ValueRef {
let v = match cx.const_unsized().borrow().get(&v) {
Some(&v) => v,
None => v
};
let d = unsafe { llvm::LLVMGetInitializer(v) };
if t.is_bool() {
if !d.is_null() && t.is_bool() {
unsafe { llvm::LLVMConstTrunc(d, Type::i1(cx).to_ref()) }
} else {
d
@ -466,16 +467,12 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
Some(v) => v,
None => return Ok(()),
};
match -cval {
Ok(_) => return Ok(()),
Err(err) => const_err(cx, e, Err(err), trueconst),
}
} else {
Ok(())
const_err(cx, e.span, (-cval).map_err(ErrKind::Math), trueconst)?;
}
Ok(())
}
fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
pub fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
match t.sty {
ty::TyInt(int_type) => const_to_opt_int(value).and_then(|input| match int_type {
ast::IntTy::I8 => {
@ -523,24 +520,21 @@ fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
}
}
fn const_err(cx: &CrateContext,
e: &hir::Expr,
result: Result<ConstInt, ConstMathErr>,
trueconst: TrueConst)
-> Result<(), ConstEvalFailure> {
pub fn const_err<T>(cx: &CrateContext,
span: Span,
result: Result<T, ErrKind>,
trueconst: TrueConst)
-> Result<T, ConstEvalFailure> {
match (result, trueconst) {
(Ok(_), _) => {
// We do not actually care about a successful result.
Ok(())
},
(Ok(x), _) => Ok(x),
(Err(err), TrueConst::Yes) => {
let err = ConstEvalErr{ span: e.span, kind: ErrKind::Math(err) };
cx.tcx().sess.span_err(e.span, &err.description());
let err = ConstEvalErr{ span: span, kind: err };
cx.tcx().sess.span_err(span, &err.description());
Err(Compiletime(err))
},
(Err(err), TrueConst::No) => {
let err = ConstEvalErr{ span: e.span, kind: ErrKind::Math(err) };
cx.tcx().sess.span_warn(e.span, &err.description());
let err = ConstEvalErr{ span: span, kind: err };
cx.tcx().sess.span_warn(span, &err.description());
Err(Runtime(err))
},
}
@ -564,7 +558,8 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
hir::BiShr => lhs >> rhs,
_ => return Ok(()),
};
const_err(cx, e, result, trueconst)
const_err(cx, e.span, result.map_err(ErrKind::Math), trueconst)?;
Ok(())
}
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
@ -719,8 +714,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
if iv >= len {
// FIXME #3170: report this earlier on in the const-eval
// pass. Reporting here is a bit late.
span_err!(cx.sess(), e.span, E0515,
"const index-expr is out of bounds");
const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds), trueconst)?;
C_undef(val_ty(arr).element_type())
} else {
const_get_elt(arr, &[iv as c_uint])
@ -1128,6 +1122,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
};
ccx.instances().borrow_mut().insert(instance, g);
ccx.statics().borrow_mut().insert(g, def_id);
Datum::new(g, ty, Lvalue::new("static"))
}
@ -1147,14 +1142,20 @@ pub fn trans_static(ccx: &CrateContext,
let def_id = ccx.tcx().map.local_def_id(id);
let datum = get_static(ccx, def_id);
let empty_substs = ccx.tcx().mk_substs(Substs::empty());
let (v, _) = const_expr(
ccx,
expr,
empty_substs,
None,
TrueConst::Yes,
).map_err(|e| e.into_inner())?;
let check_attrs = |attrs: &[ast::Attribute]| {
let default_to_mir = ccx.sess().opts.debugging_opts.orbit;
let invert = if default_to_mir { "rustc_no_mir" } else { "rustc_mir" };
default_to_mir ^ attrs.iter().any(|item| item.check_name(invert))
};
let use_mir = check_attrs(ccx.tcx().map.attrs(id));
let v = if use_mir {
::mir::trans_static_initializer(ccx, def_id)
} else {
let empty_substs = ccx.tcx().mk_substs(Substs::empty());
const_expr(ccx, expr, empty_substs, None, TrueConst::Yes)
.map(|(v, _)| v)
}.map_err(|e| e.into_inner())?;
// boolean SSA values are i1, but they have to be stored in i8 slots,
// otherwise some LLVM optimization passes don't work as expected

View File

@ -131,6 +131,9 @@ pub struct LocalCrateContext<'tcx> {
/// Cache of external const values
extern_const_values: RefCell<DefIdMap<ValueRef>>,
/// Mapping from static definitions to their DefId's.
statics: RefCell<FnvHashMap<ValueRef, DefId>>,
impl_method_cache: RefCell<FnvHashMap<(DefId, ast::Name), DefId>>,
/// Cache of closure wrappers for bare fn's.
@ -495,6 +498,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
const_globals: RefCell::new(FnvHashMap()),
const_values: RefCell::new(FnvHashMap()),
extern_const_values: RefCell::new(DefIdMap()),
statics: RefCell::new(FnvHashMap()),
impl_method_cache: RefCell::new(FnvHashMap()),
closure_bare_wrapper_cache: RefCell::new(FnvHashMap()),
statics_to_rauw: RefCell::new(Vec::new()),
@ -699,6 +703,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local.extern_const_values
}
pub fn statics<'a>(&'a self) -> &'a RefCell<FnvHashMap<ValueRef, DefId>> {
&self.local.statics
}
pub fn impl_method_cache<'a>(&'a self)
-> &'a RefCell<FnvHashMap<(DefId, ast::Name), DefId>> {
&self.local.impl_method_cache

View File

@ -82,19 +82,4 @@ extern "platform-intrinsic" {
unsafe { simd_add(i32x1(0), i32x1(1)); } // ok!
```
"##,
E0515: r##"
A constant index expression was out of bounds. Erroneous code example:
```compile_fail
let x = &[0, 1, 2][7]; // error: const index-expr is out of bounds
```
Please specify a valid index (not inferior to 0 or superior to array length).
Example:
```
let x = &[0, 1, 2][2]; // ok
```
"##,
}

View File

@ -1482,28 +1482,23 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
let total_len = in_len as u64 * 2;
let (vector, indirect) = match args {
let vector = match args {
Some(args) => {
match consts::const_expr(bcx.ccx(), &args[2], substs, None,
// this should probably help simd error reporting
consts::TrueConst::Yes) {
Ok((vector, _)) => (vector, false),
Ok((vector, _)) => vector,
Err(err) => bcx.sess().span_fatal(span, &err.description()),
}
}
None => (llargs[2], !type_is_immediate(bcx.ccx(), arg_tys[2]))
None => llargs[2]
};
let indices: Option<Vec<_>> = (0..n)
.map(|i| {
let arg_idx = i;
let val = if indirect {
Load(bcx, StructGEP(bcx, vector, i))
} else {
const_get_elt(vector, &[i as libc::c_uint])
};
let c = const_to_opt_uint(val);
match c {
let val = const_get_elt(vector, &[i as libc::c_uint]);
match const_to_opt_uint(val) {
None => {
emit_error!("shuffle index #{} is not a constant", arg_idx);
None

View File

@ -26,6 +26,7 @@ use glue;
use type_::Type;
use super::{MirContext, TempRef, drop};
use super::constant::Const;
use super::lvalue::{LvalueRef, load_fat_ptr};
use super::operand::OperandRef;
use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
@ -114,9 +115,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
let switch = bcx.switch(discr, self.llblock(*otherwise), values.len());
for (value, target) in values.iter().zip(targets) {
let llval = self.trans_constval(&bcx, value, switch_ty).immediate();
let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty);
let llbb = self.llblock(*target);
build::AddCase(switch, llval, llbb)
build::AddCase(switch, val.llval, llbb)
}
}
@ -240,8 +241,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
(&args[..], None)
};
let is_shuffle = intrinsic.map_or(false, |name| {
name.starts_with("simd_shuffle")
});
let mut idx = 0;
for arg in first_args {
// The indices passed to simd_shuffle* in the
// third argument must be constant. This is
// checked by const-qualification, which also
// promotes any complex rvalues to constants.
if is_shuffle && idx == 2 {
match *arg {
mir::Operand::Consume(_) => {
span_bug!(terminator.span,
"shuffle indices must be constant");
}
mir::Operand::Constant(ref constant) => {
let val = self.trans_constant(&bcx, constant);
llargs.push(val.llval);
idx += 1;
continue;
}
}
}
let val = self.trans_operand(&bcx, arg).val;
self.trans_argument(&bcx, val, &mut llargs, &fn_ty,
&mut idx, &mut callee.data);

View File

@ -8,62 +8,59 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use llvm::ValueRef;
use rustc::ty::{Ty, TypeFoldable};
use llvm::{self, ValueRef};
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::ErrKind;
use rustc_const_math::ConstInt::*;
use rustc_const_eval::lookup_const_by_id;
use rustc::hir::def_id::DefId;
use rustc::mir::repr as mir;
use abi;
use common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral,
C_str_slice, C_undef};
use consts;
use datum;
use expr;
use rustc::mir::tcx::LvalueTy;
use rustc::traits;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use {abi, adt, base, Disr};
use callee::Callee;
use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
use consts::{self, ConstEvalFailure, TrueConst, to_const_int};
use monomorphize::{self, Instance};
use type_of;
use type_::Type;
use value::Value;
use syntax::codemap::{Span, DUMMY_SP};
use std::ptr;
use super::operand::{OperandRef, OperandValue};
use super::MirContext;
/// A sized constant rvalue.
/// The LLVM type might not be the same for a single Rust type,
/// e.g. each enum variant would have its own LLVM struct type.
#[derive(Copy, Clone)]
pub struct Const<'tcx> {
pub llval: ValueRef,
pub ty: Ty<'tcx>
}
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_constval(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
cv: &ConstVal,
ty: Ty<'tcx>)
-> OperandRef<'tcx>
{
let ccx = bcx.ccx();
let val = self.trans_constval_inner(bcx, cv, ty);
let val = if common::type_is_immediate(ccx, ty) {
OperandValue::Immediate(val)
} else if common::type_is_fat_ptr(bcx.tcx(), ty) {
let data = common::const_get_elt(val, &[abi::FAT_PTR_ADDR as u32]);
let extra = common::const_get_elt(val, &[abi::FAT_PTR_EXTRA as u32]);
OperandValue::FatPtr(data, extra)
} else {
OperandValue::Ref(val)
};
assert!(!ty.has_erasable_regions());
OperandRef {
ty: ty,
val: val
impl<'tcx> Const<'tcx> {
pub fn new(llval: ValueRef, ty: Ty<'tcx>) -> Const<'tcx> {
Const {
llval: llval,
ty: ty
}
}
/// Translate ConstVal into a bare LLVM ValueRef.
fn trans_constval_inner(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
cv: &ConstVal,
ty: Ty<'tcx>)
-> ValueRef
{
let ccx = bcx.ccx();
/// Translate ConstVal into a LLVM constant value.
pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
cv: ConstVal,
ty: Ty<'tcx>)
-> Const<'tcx> {
let llty = type_of::type_of(ccx, ty);
match *cv {
let val = match cv {
ConstVal::Float(v) => C_floating_f64(v, llty),
ConstVal::Bool(v) => C_bool(ccx, v),
ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
@ -93,51 +90,769 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
ConstVal::Dummy => bug!(),
};
assert!(!ty.has_erasable_regions());
Const::new(val, ty)
}
fn get_fat_ptr(&self) -> (ValueRef, ValueRef) {
(const_get_elt(self.llval, &[abi::FAT_PTR_ADDR as u32]),
const_get_elt(self.llval, &[abi::FAT_PTR_EXTRA as u32]))
}
fn as_lvalue(&self) -> ConstLvalue<'tcx> {
ConstLvalue {
base: Base::Value(self.llval),
llextra: ptr::null_mut(),
ty: self.ty
}
}
pub fn to_operand<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> OperandRef<'tcx> {
let llty = type_of::immediate_type_of(ccx, self.ty);
let llvalty = val_ty(self.llval);
let val = if common::type_is_fat_ptr(ccx.tcx(), self.ty) {
let (data, extra) = self.get_fat_ptr();
OperandValue::FatPtr(data, extra)
} else if common::type_is_immediate(ccx, self.ty) && llty == llvalty {
// If the types match, we can use the value directly.
OperandValue::Immediate(self.llval)
} else {
// Otherwise, or if the value is not immediate, we create
// a constant LLVM global and cast its address if necessary.
let align = type_of::align_of(ccx, self.ty);
let ptr = consts::addr_of(ccx, self.llval, align, "const");
OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()))
};
OperandRef {
val: val,
ty: self.ty
}
}
}
#[derive(Copy, Clone)]
enum Base {
/// A constant value without an unique address.
Value(ValueRef),
/// String literal base pointer (cast from array).
Str(ValueRef),
/// The address of a static.
Static(ValueRef)
}
/// An lvalue as seen from a constant.
#[derive(Copy, Clone)]
struct ConstLvalue<'tcx> {
base: Base,
llextra: ValueRef,
ty: Ty<'tcx>
}
impl<'tcx> ConstLvalue<'tcx> {
fn to_const(&self, span: Span) -> Const<'tcx> {
match self.base {
Base::Value(val) => Const::new(val, self.ty),
Base::Str(ptr) => {
span_bug!(span, "loading from `str` ({:?}) in constant",
Value(ptr))
}
Base::Static(val) => {
span_bug!(span, "loading from `static` ({:?}) in constant",
Value(val))
}
}
}
pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
match self.ty.sty {
ty::TyArray(_, n) => C_uint(ccx, n),
ty::TySlice(_) | ty::TyStr => {
assert!(self.llextra != ptr::null_mut());
self.llextra
}
_ => bug!("unexpected type `{}` in ConstLvalue::len", self.ty)
}
}
}
/// Machinery for translating a constant's MIR to LLVM values.
/// FIXME(eddyb) use miri and lower its allocations to LLVM.
struct MirConstContext<'a, 'tcx: 'a> {
ccx: &'a CrateContext<'a, 'tcx>,
mir: &'a mir::Mir<'tcx>,
/// Type parameters for const fn and associated constants.
substs: &'tcx Substs<'tcx>,
/// Arguments passed to a const fn.
args: Vec<Const<'tcx>>,
/// Variable values - specifically, argument bindings of a const fn.
vars: Vec<Option<Const<'tcx>>>,
/// Temp values.
temps: Vec<Option<Const<'tcx>>>,
/// Value assigned to Return, which is the resulting constant.
return_value: Option<Const<'tcx>>
}
impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
fn new(ccx: &'a CrateContext<'a, 'tcx>,
mir: &'a mir::Mir<'tcx>,
substs: &'tcx Substs<'tcx>,
args: Vec<Const<'tcx>>)
-> MirConstContext<'a, 'tcx> {
MirConstContext {
ccx: ccx,
mir: mir,
substs: substs,
args: args,
vars: vec![None; mir.var_decls.len()],
temps: vec![None; mir.temp_decls.len()],
return_value: None
}
}
fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
mut instance: Instance<'tcx>,
args: Vec<Const<'tcx>>)
-> Result<Const<'tcx>, ConstEvalFailure> {
// Try to resolve associated constants.
if instance.substs.self_ty().is_some() {
// Only trait items can have a Self parameter.
let trait_item = ccx.tcx().impl_or_trait_item(instance.def);
let trait_id = trait_item.container().id();
let substs = instance.substs;
let trait_ref = ty::Binder(substs.to_trait_ref(ccx.tcx(), trait_id));
let vtable = common::fulfill_obligation(ccx, DUMMY_SP, trait_ref);
if let traits::VtableImpl(vtable_impl) = vtable {
let name = ccx.tcx().item_name(instance.def);
for ac in ccx.tcx().associated_consts(vtable_impl.impl_def_id) {
if ac.name == name {
instance = Instance::new(ac.def_id, vtable_impl.substs);
break;
}
}
}
}
let mir = ccx.get_mir(instance.def).unwrap_or_else(|| {
bug!("missing constant MIR for {}", instance)
});
MirConstContext::new(ccx, &mir, instance.substs, args).trans()
}
fn monomorphize<T>(&self, value: &T) -> T
where T : TypeFoldable<'tcx>
{
monomorphize::apply_param_substs(self.ccx.tcx(),
self.substs,
value)
}
fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalFailure> {
let tcx = self.ccx.tcx();
let mut bb = mir::START_BLOCK;
loop {
let data = self.mir.basic_block_data(bb);
for statement in &data.statements {
match statement.kind {
mir::StatementKind::Assign(ref dest, ref rvalue) => {
let ty = self.mir.lvalue_ty(tcx, dest);
let ty = self.monomorphize(&ty).to_ty(tcx);
let value = self.const_rvalue(rvalue, ty, statement.span)?;
self.store(dest, value, statement.span);
}
}
}
let terminator = data.terminator();
let span = terminator.span;
bb = match terminator.kind {
mir::TerminatorKind::Drop { target, .. } | // No dropping.
mir::TerminatorKind::Goto { target } => target,
mir::TerminatorKind::Return => {
return Ok(self.return_value.unwrap_or_else(|| {
span_bug!(span, "no returned value in constant");
}))
}
// This is only supported to make bounds checking work.
mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
let cond = self.const_operand(cond, span)?;
if common::const_to_uint(cond.llval) != 0 {
true_bb
} else {
false_bb
}
}
mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
let fn_ty = self.mir.operand_ty(tcx, func);
let fn_ty = self.monomorphize(&fn_ty);
let instance = match fn_ty.sty {
ty::TyFnDef(def_id, substs, _) => {
Instance::new(def_id, substs)
}
_ => span_bug!(span, "calling {:?} (of type {}) in constant",
func, fn_ty)
};
// Indexing OOB doesn't call a const fn, handle it.
if Some(instance.def) == tcx.lang_items.panic_bounds_check_fn() {
consts::const_err(self.ccx, span,
Err(ErrKind::IndexOutOfBounds),
TrueConst::Yes)?;
}
let args = args.iter().map(|arg| {
self.const_operand(arg, span)
}).collect::<Result<Vec<_>, _>>()?;
let value = MirConstContext::trans_def(self.ccx, instance, args)?;
if let Some((ref dest, target)) = *destination {
self.store(dest, value, span);
target
} else {
span_bug!(span, "diverging {:?} in constant", terminator.kind)
}
}
_ => span_bug!(span, "{:?} in constant", terminator.kind)
};
}
}
fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
let dest = match *dest {
mir::Lvalue::Var(index) => &mut self.vars[index as usize],
mir::Lvalue::Temp(index) => &mut self.temps[index as usize],
mir::Lvalue::ReturnPointer => &mut self.return_value,
_ => span_bug!(span, "assignment to {:?} in constant", dest)
};
*dest = Some(value);
}
fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span)
-> Result<ConstLvalue<'tcx>, ConstEvalFailure> {
let tcx = self.ccx.tcx();
let lvalue = match *lvalue {
mir::Lvalue::Var(index) => {
self.vars[index as usize].unwrap_or_else(|| {
span_bug!(span, "var{} not initialized", index)
}).as_lvalue()
}
mir::Lvalue::Temp(index) => {
self.temps[index as usize].unwrap_or_else(|| {
span_bug!(span, "tmp{} not initialized", index)
}).as_lvalue()
}
mir::Lvalue::Arg(index) => self.args[index as usize].as_lvalue(),
mir::Lvalue::Static(def_id) => {
ConstLvalue {
base: Base::Static(consts::get_static(self.ccx, def_id).val),
llextra: ptr::null_mut(),
ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx)
}
}
mir::Lvalue::ReturnPointer => {
span_bug!(span, "accessing Lvalue::ReturnPointer in constant")
}
mir::Lvalue::Projection(ref projection) => {
let tr_base = self.const_lvalue(&projection.base, span)?;
let projected_ty = LvalueTy::Ty { ty: tr_base.ty }
.projection_ty(tcx, &projection.elem);
let base = tr_base.to_const(span);
let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx);
let is_sized = common::type_is_sized(tcx, projected_ty);
let (projected, llextra) = match projection.elem {
mir::ProjectionElem::Deref => {
let (base, extra) = if is_sized {
(base.llval, ptr::null_mut())
} else {
base.get_fat_ptr()
};
if self.ccx.statics().borrow().contains_key(&base) {
(Base::Static(base), extra)
} else if let ty::TyStr = projected_ty.sty {
(Base::Str(base), extra)
} else {
let val = consts::load_const(self.ccx, base, projected_ty);
if val.is_null() {
span_bug!(span, "dereference of non-constant pointer `{:?}`",
Value(base));
}
(Base::Value(val), extra)
}
}
mir::ProjectionElem::Field(ref field, _) => {
let base_repr = adt::represent_type(self.ccx, tr_base.ty);
let llprojected = adt::const_get_field(&base_repr, base.llval,
Disr(0), field.index());
let llextra = if is_sized {
ptr::null_mut()
} else {
tr_base.llextra
};
(Base::Value(llprojected), llextra)
}
mir::ProjectionElem::Index(ref index) => {
let llindex = self.const_operand(index, span)?.llval;
let iv = if let Some(iv) = common::const_to_opt_uint(llindex) {
iv
} else {
span_bug!(span, "index is not an integer-constant expression")
};
(Base::Value(const_get_elt(base.llval, &[iv as u32])),
ptr::null_mut())
}
_ => span_bug!(span, "{:?} in constant", projection.elem)
};
ConstLvalue {
base: projected,
llextra: llextra,
ty: projected_ty
}
}
};
Ok(lvalue)
}
fn const_operand(&self, operand: &mir::Operand<'tcx>, span: Span)
-> Result<Const<'tcx>, ConstEvalFailure> {
match *operand {
mir::Operand::Consume(ref lvalue) => {
Ok(self.const_lvalue(lvalue, span)?.to_const(span))
}
mir::Operand::Constant(ref constant) => {
let ty = self.monomorphize(&constant.ty);
match constant.literal.clone() {
mir::Literal::Item { def_id, substs } => {
// Shortcut for zero-sized types, including function item
// types, which would not work with MirConstContext.
if common::type_is_zero_size(self.ccx, ty) {
let llty = type_of::type_of(self.ccx, ty);
return Ok(Const::new(C_null(llty), ty));
}
let substs = self.ccx.tcx().mk_substs(self.monomorphize(substs));
let instance = Instance::new(def_id, substs);
MirConstContext::trans_def(self.ccx, instance, vec![])
}
mir::Literal::Promoted { index } => {
let mir = &self.mir.promoted[index];
MirConstContext::new(self.ccx, mir, self.substs, vec![]).trans()
}
mir::Literal::Value { value } => {
Ok(Const::from_constval(self.ccx, value, ty))
}
}
}
}
}
fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>,
dest_ty: Ty<'tcx>, span: Span)
-> Result<Const<'tcx>, ConstEvalFailure> {
let tcx = self.ccx.tcx();
let val = match *rvalue {
mir::Rvalue::Use(ref operand) => self.const_operand(operand, span)?,
mir::Rvalue::Repeat(ref elem, ref count) => {
let elem = self.const_operand(elem, span)?;
let size = count.value.as_u64(tcx.sess.target.uint_type);
let fields = vec![elem.llval; size as usize];
let llunitty = type_of::type_of(self.ccx, elem.ty);
// If the array contains enums, an LLVM array won't work.
let val = if val_ty(elem.llval) == llunitty {
C_array(llunitty, &fields)
} else {
C_struct(self.ccx, &fields, false)
};
Const::new(val, dest_ty)
}
mir::Rvalue::Aggregate(ref kind, ref operands) => {
let fields = operands.iter().map(|operand| {
Ok(self.const_operand(operand, span)?.llval)
}).collect::<Result<Vec<_>, _>>()?;
// FIXME Shouldn't need to manually trigger closure instantiations.
if let mir::AggregateKind::Closure(def_id, substs) = *kind {
use rustc::hir;
use syntax::ast::DUMMY_NODE_ID;
use syntax::ptr::P;
use closure;
closure::trans_closure_expr(closure::Dest::Ignore(self.ccx),
&hir::FnDecl {
inputs: P::new(),
output: hir::NoReturn(DUMMY_SP),
variadic: false
},
&hir::Block {
stmts: P::new(),
expr: None,
id: DUMMY_NODE_ID,
rules: hir::DefaultBlock,
span: DUMMY_SP
},
DUMMY_NODE_ID, def_id,
&self.monomorphize(substs));
}
let val = if let mir::AggregateKind::Adt(adt_def, index, _) = *kind {
let repr = adt::represent_type(self.ccx, dest_ty);
let disr = Disr::from(adt_def.variants[index].disr_val);
adt::trans_const(self.ccx, &repr, disr, &fields)
} else if let ty::TyArray(elem_ty, _) = dest_ty.sty {
let llunitty = type_of::type_of(self.ccx, elem_ty);
// If the array contains enums, an LLVM array won't work.
if fields.iter().all(|&f| val_ty(f) == llunitty) {
C_array(llunitty, &fields)
} else {
C_struct(self.ccx, &fields, false)
}
} else {
C_struct(self.ccx, &fields, false)
};
Const::new(val, dest_ty)
}
mir::Rvalue::Cast(ref kind, ref source, cast_ty) => {
let operand = self.const_operand(source, span)?;
let cast_ty = self.monomorphize(&cast_ty);
let val = match *kind {
mir::CastKind::ReifyFnPointer => {
match operand.ty.sty {
ty::TyFnDef(def_id, substs, _) => {
Callee::def(self.ccx, def_id, substs)
.reify(self.ccx).val
}
_ => {
span_bug!(span, "{} cannot be reified to a fn ptr",
operand.ty)
}
}
}
mir::CastKind::UnsafeFnPointer => {
// this is a no-op at the LLVM level
operand.llval
}
mir::CastKind::Unsize => {
// unsize targets other than to a fat pointer currently
// can't be in constants.
assert!(common::type_is_fat_ptr(tcx, cast_ty));
let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference)
.expect("consts: unsizing got non-pointer type").ty;
let (base, old_info) = if !common::type_is_sized(tcx, pointee_ty) {
// Normally, the source is a thin pointer and we are
// adding extra info to make a fat pointer. The exception
// is when we are upcasting an existing object fat pointer
// to use a different vtable. In that case, we want to
// load out the original data pointer so we can repackage
// it.
let (base, extra) = operand.get_fat_ptr();
(base, Some(extra))
} else {
(operand.llval, None)
};
let unsized_ty = cast_ty.builtin_deref(true, ty::NoPreference)
.expect("consts: unsizing got non-pointer target type").ty;
let ptr_ty = type_of::in_memory_type_of(self.ccx, unsized_ty).ptr_to();
let base = consts::ptrcast(base, ptr_ty);
let info = base::unsized_info(self.ccx, pointee_ty,
unsized_ty, old_info);
if old_info.is_none() {
let prev_const = self.ccx.const_unsized().borrow_mut()
.insert(base, operand.llval);
assert!(prev_const.is_none() || prev_const == Some(operand.llval));
}
assert_eq!(abi::FAT_PTR_ADDR, 0);
assert_eq!(abi::FAT_PTR_EXTRA, 1);
C_struct(self.ccx, &[base, info], false)
}
mir::CastKind::Misc if common::type_is_immediate(self.ccx, operand.ty) => {
debug_assert!(common::type_is_immediate(self.ccx, cast_ty));
let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
let ll_t_out = type_of::immediate_type_of(self.ccx, cast_ty);
let llval = operand.llval;
let signed = if let CastTy::Int(IntTy::CEnum) = r_t_in {
let repr = adt::represent_type(self.ccx, operand.ty);
adt::is_discr_signed(&repr)
} else {
operand.ty.is_signed()
};
unsafe {
match (r_t_in, r_t_out) {
(CastTy::Int(_), CastTy::Int(_)) => {
let s = signed as llvm::Bool;
llvm::LLVMConstIntCast(llval, ll_t_out.to_ref(), s)
}
(CastTy::Int(_), CastTy::Float) => {
if signed {
llvm::LLVMConstSIToFP(llval, ll_t_out.to_ref())
} else {
llvm::LLVMConstUIToFP(llval, ll_t_out.to_ref())
}
}
(CastTy::Float, CastTy::Float) => {
llvm::LLVMConstFPCast(llval, ll_t_out.to_ref())
}
(CastTy::Float, CastTy::Int(IntTy::I)) => {
llvm::LLVMConstFPToSI(llval, ll_t_out.to_ref())
}
(CastTy::Float, CastTy::Int(_)) => {
llvm::LLVMConstFPToUI(llval, ll_t_out.to_ref())
}
(CastTy::Ptr(_), CastTy::Ptr(_)) |
(CastTy::FnPtr, CastTy::Ptr(_)) |
(CastTy::RPtr(_), CastTy::Ptr(_)) => {
consts::ptrcast(llval, ll_t_out)
}
(CastTy::Int(_), CastTy::Ptr(_)) => {
llvm::LLVMConstIntToPtr(llval, ll_t_out.to_ref())
}
(CastTy::Ptr(_), CastTy::Int(_)) |
(CastTy::FnPtr, CastTy::Int(_)) => {
llvm::LLVMConstPtrToInt(llval, ll_t_out.to_ref())
}
_ => bug!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty)
}
}
}
mir::CastKind::Misc => { // Casts from a fat-ptr.
let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty);
let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty);
if common::type_is_fat_ptr(tcx, operand.ty) {
let (data_ptr, meta_ptr) = operand.get_fat_ptr();
if common::type_is_fat_ptr(tcx, cast_ty) {
let ll_cft = ll_cast_ty.field_types();
let ll_fft = ll_from_ty.field_types();
let data_cast = consts::ptrcast(data_ptr, ll_cft[0]);
assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
C_struct(self.ccx, &[data_cast, meta_ptr], false)
} else { // cast to thin-ptr
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
// pointer-cast of that pointer to desired pointer type.
consts::ptrcast(data_ptr, ll_cast_ty)
}
} else {
bug!("Unexpected non-FatPtr operand")
}
}
};
Const::new(val, cast_ty)
}
mir::Rvalue::Ref(_, bk, ref lvalue) => {
let tr_lvalue = self.const_lvalue(lvalue, span)?;
let ty = tr_lvalue.ty;
let ref_ty = tcx.mk_ref(tcx.mk_region(ty::ReStatic),
ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() });
let base = match tr_lvalue.base {
Base::Value(llval) => {
let align = type_of::align_of(self.ccx, ty);
if bk == mir::BorrowKind::Mut {
consts::addr_of_mut(self.ccx, llval, align, "ref_mut")
} else {
consts::addr_of(self.ccx, llval, align, "ref")
}
}
Base::Str(llval) |
Base::Static(llval) => llval
};
let ptr = if common::type_is_sized(tcx, ty) {
base
} else {
C_struct(self.ccx, &[base, tr_lvalue.llextra], false)
};
Const::new(ptr, ref_ty)
}
mir::Rvalue::Len(ref lvalue) => {
let tr_lvalue = self.const_lvalue(lvalue, span)?;
Const::new(tr_lvalue.len(self.ccx), tcx.types.usize)
}
mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
let lhs = self.const_operand(lhs, span)?;
let rhs = self.const_operand(rhs, span)?;
let ty = lhs.ty;
let binop_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
let (lhs, rhs) = (lhs.llval, rhs.llval);
assert!(!ty.is_simd());
let is_float = ty.is_fp();
let signed = ty.is_signed();
if let (Some(lhs), Some(rhs)) = (to_const_int(lhs, ty, tcx),
to_const_int(rhs, ty, tcx)) {
let result = match op {
mir::BinOp::Add => lhs + rhs,
mir::BinOp::Sub => lhs - rhs,
mir::BinOp::Mul => lhs * rhs,
mir::BinOp::Div => lhs / rhs,
mir::BinOp::Rem => lhs % rhs,
mir::BinOp::Shl => lhs << rhs,
mir::BinOp::Shr => lhs >> rhs,
_ => Ok(lhs)
};
consts::const_err(self.ccx, span,
result.map_err(ErrKind::Math),
TrueConst::Yes)?;
}
let llval = unsafe {
match op {
mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs),
mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
mir::BinOp::Sub => llvm::LLVMConstSub(lhs, rhs),
mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
mir::BinOp::Mul => llvm::LLVMConstMul(lhs, rhs),
mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
mir::BinOp::Div if signed => llvm::LLVMConstSDiv(lhs, rhs),
mir::BinOp::Div => llvm::LLVMConstUDiv(lhs, rhs),
mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
mir::BinOp::Rem if signed => llvm::LLVMConstSRem(lhs, rhs),
mir::BinOp::Rem => llvm::LLVMConstURem(lhs, rhs),
mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
mir::BinOp::BitOr => llvm::LLVMConstOr(lhs, rhs),
mir::BinOp::Shl => {
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
llvm::LLVMConstShl(lhs, rhs)
}
mir::BinOp::Shr => {
let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
if signed { llvm::LLVMConstAShr(lhs, rhs) }
else { llvm::LLVMConstLShr(lhs, rhs) }
}
mir::BinOp::Eq | mir::BinOp::Ne |
mir::BinOp::Lt | mir::BinOp::Le |
mir::BinOp::Gt | mir::BinOp::Ge => {
if is_float {
let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
llvm::ConstFCmp(cmp, lhs, rhs)
} else {
let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
signed);
llvm::ConstICmp(cmp, lhs, rhs)
}
}
}
};
Const::new(llval, binop_ty)
}
mir::Rvalue::UnaryOp(op, ref operand) => {
let operand = self.const_operand(operand, span)?;
let lloperand = operand.llval;
let llval = match op {
mir::UnOp::Not => {
unsafe {
llvm::LLVMConstNot(lloperand)
}
}
mir::UnOp::Neg => {
if let Some(cval) = to_const_int(lloperand, operand.ty, tcx) {
consts::const_err(self.ccx, span,
(-cval).map_err(ErrKind::Math),
TrueConst::Yes)?;
}
let is_float = operand.ty.is_fp();
unsafe {
if is_float {
llvm::LLVMConstFNeg(lloperand)
} else {
llvm::LLVMConstNeg(lloperand)
}
}
}
};
Const::new(llval, operand.ty)
}
_ => span_bug!(span, "{:?} in constant", rvalue)
};
Ok(val)
}
}
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_constant(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
constant: &mir::Constant<'tcx>)
-> OperandRef<'tcx>
-> Const<'tcx>
{
let ty = bcx.monomorphize(&constant.ty);
match constant.literal {
let result = match constant.literal.clone() {
mir::Literal::Item { def_id, substs } => {
// Shortcut for zero-sized types, including function item
// types, which would not work with lookup_const_by_id.
// types, which would not work with MirConstContext.
if common::type_is_zero_size(bcx.ccx(), ty) {
let llty = type_of::type_of(bcx.ccx(), ty);
return OperandRef {
val: OperandValue::Immediate(C_undef(llty)),
ty: ty
};
return Const::new(C_null(llty), ty);
}
let substs = Some(bcx.monomorphize(substs));
let expr = lookup_const_by_id(bcx.tcx(), def_id, substs)
.expect("def was const, but lookup_const_by_id failed").0;
// FIXME: this is falling back to translating from HIR. This is not easy to fix,
// because we would have somehow adapt const_eval to work on MIR rather than HIR.
let d = bcx.with_block(|bcx| {
expr::trans(bcx, expr)
});
let datum = d.datum.to_rvalue_datum(d.bcx, "").datum;
match datum.kind.mode {
datum::RvalueMode::ByValue => {
OperandRef {
ty: datum.ty,
val: OperandValue::Immediate(datum.val)
}
}
datum::RvalueMode::ByRef => self.trans_load(bcx, datum.val, datum.ty)
}
let substs = bcx.tcx().mk_substs(bcx.monomorphize(substs));
let instance = Instance::new(def_id, substs);
MirConstContext::trans_def(bcx.ccx(), instance, vec![])
}
mir::Literal::Value { ref value } => {
self.trans_constval(bcx, value, ty)
mir::Literal::Promoted { index } => {
let mir = &self.mir.promoted[index];
MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, vec![]).trans()
}
mir::Literal::Value { value } => {
Ok(Const::from_constval(bcx.ccx(), value, ty))
}
};
match result {
Ok(v) => v,
Err(ConstEvalFailure::Compiletime(_)) => {
// We've errored, so we don't have to produce working code.
let llty = type_of::type_of(bcx.ccx(), ty);
Const::new(C_undef(llty), ty)
}
Err(ConstEvalFailure::Runtime(err)) => {
span_bug!(constant.span,
"MIR constant {:?} results in runtime panic: {}",
constant, err.description())
}
}
}
}
pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId)
-> Result<ValueRef, ConstEvalFailure> {
let instance = Instance::mono(ccx.tcx(), def_id);
MirConstContext::trans_def(ccx, instance, vec![]).map(|c| c.llval)
}

View File

@ -16,7 +16,7 @@ use abi;
use adt;
use base;
use builder::Builder;
use common::{self, BlockAndBuilder, C_uint};
use common::{self, BlockAndBuilder, CrateContext, C_uint};
use consts;
use machine;
use mir::drop;
@ -56,6 +56,18 @@ impl<'tcx> LvalueRef<'tcx> {
}
LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
}
pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
let ty = self.ty.to_ty(ccx.tcx());
match ty.sty {
ty::TyArray(_, n) => common::C_uint(ccx, n),
ty::TySlice(_) | ty::TyStr => {
assert!(self.llextra != ptr::null_mut());
self.llextra
}
_ => bug!("unexpected type `{}` in LvalueRef::len", ty)
}
}
}
pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
@ -71,20 +83,6 @@ pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
}
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn lvalue_len(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
lvalue: LvalueRef<'tcx>)
-> ValueRef {
match lvalue.ty.to_ty(bcx.tcx()).sty {
ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n),
ty::TySlice(_) | ty::TyStr => {
assert!(lvalue.llextra != ptr::null_mut());
lvalue.llextra
}
_ => bug!("unexpected type in lvalue_len"),
}
}
pub fn trans_lvalue(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
lvalue: &mir::Lvalue<'tcx>)
@ -190,7 +188,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
from_end: true,
min_length: _ } => {
let lloffset = C_uint(bcx.ccx(), offset);
let lllen = self.lvalue_len(bcx, tr_base);
let lllen = tr_base.len(bcx.ccx());
let llindex = bcx.sub(lllen, lloffset);
project_index(self.prepare_index(bcx, llindex))
}

View File

@ -31,6 +31,8 @@ use basic_block::BasicBlock;
use rustc_data_structures::bitvec::BitVector;
pub use self::constant::trans_static_initializer;
use self::lvalue::{LvalueRef, get_dataptr, get_meta};
use rustc_mir::traversal;

View File

@ -140,7 +140,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
mir::Operand::Constant(ref constant) => {
self.trans_constant(bcx, constant)
let val = self.trans_constant(bcx, constant);
let operand = val.to_operand(bcx.ccx());
if let OperandValue::Ref(ptr) = operand.val {
// If this is a OperandValue::Ref to an immediate constant, load it.
self.trans_load(bcx, ptr, operand.ty)
} else {
operand
}
}
}
}

View File

@ -11,8 +11,6 @@
use llvm::ValueRef;
use rustc::ty::{self, Ty};
use rustc::ty::cast::{CastTy, IntTy};
use middle::const_val::ConstVal;
use rustc_const_math::ConstInt;
use rustc::mir::repr as mir;
use asm;
@ -100,8 +98,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Rvalue::Repeat(ref elem, ref count) => {
let tr_elem = self.trans_operand(&bcx, elem);
let count = ConstVal::Integral(ConstInt::Usize(count.value));
let size = self.trans_constval(&bcx, &count, bcx.tcx().types.usize).immediate();
let size = count.value.as_u64(bcx.tcx().sess.target.uint_type);
let size = C_uint(bcx.ccx(), size);
let base = get_dataptr(&bcx, dest.llval);
let bcx = bcx.map_block(|block| {
tvec::iter_vec_raw(block, base, tr_elem.ty, size, |block, llslot, _| {
@ -405,7 +403,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Rvalue::Len(ref lvalue) => {
let tr_lvalue = self.trans_lvalue(&bcx, lvalue);
let operand = OperandRef {
val: OperandValue::Immediate(self.lvalue_len(&bcx, tr_lvalue)),
val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx())),
ty: bcx.tcx().types.usize,
};
(bcx, operand)

View File

@ -266,7 +266,10 @@ declare_features! (
(active, specialization, "1.7.0", Some(31844)),
// pub(restricted) visibilities (RFC 1422)
(active, pub_restricted, "1.9.0", Some(32409))
(active, pub_restricted, "1.9.0", Some(32409)),
// Allow Drop types in statics/const functions (RFC 1440)
(active, drop_types_in_const, "1.9.0", Some(33156))
);
declare_features! (

3
src/rustc/Cargo.lock generated
View File

@ -220,6 +220,7 @@ dependencies = [
"log 0.0.0",
"rustc 0.0.0",
"rustc_back 0.0.0",
"rustc_bitflags 0.0.0",
"rustc_const_eval 0.0.0",
"rustc_const_math 0.0.0",
"rustc_data_structures 0.0.0",
@ -233,6 +234,7 @@ dependencies = [
"log 0.0.0",
"rustc 0.0.0",
"rustc_const_eval 0.0.0",
"rustc_const_math 0.0.0",
"syntax 0.0.0",
]
@ -278,6 +280,7 @@ version = "0.0.0"
dependencies = [
"log 0.0.0",
"rustc 0.0.0",
"serialize 0.0.0",
"syntax 0.0.0",
]

View File

@ -8,8 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
static A: &'static [i32] = &[];
static B: i32 = (&A)[1]; //~ ERROR: const index-expr is out of bounds
const A: &'static [i32] = &[];
const B: i32 = (&A)[1];
//~^ ERROR: array index out of bounds
fn main() {
let _ = B;

View File

@ -9,7 +9,7 @@
// except according to those terms.
const A: [i32; 0] = [];
const B: i32 = A[1]; //~ ERROR: const index-expr is out of bounds
const B: i32 = A[1]; //~ ERROR: array index out of bounds
fn main() {
let _ = B;

View File

@ -8,4 +8,4 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub static ARRAY: &'static [u8] = &[1];
pub static ARRAY: [u8; 1] = [1];

View File

@ -12,5 +12,6 @@
static TEST: &'static mut [isize] = &mut [];
//~^ ERROR references in statics may only refer to immutable values
//~^^ ERROR references in statics may only refer to immutable values
pub fn main() { }

View File

@ -37,7 +37,7 @@ static STATIC2: SafeEnum = SafeEnum::Variant2(0);
// This one should fail
static STATIC3: SafeEnum = SafeEnum::Variant3(WithDtor);
//~^ ERROR statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
// This enum will be used to test that variants
@ -54,9 +54,9 @@ impl Drop for UnsafeEnum {
static STATIC4: UnsafeEnum = UnsafeEnum::Variant5;
//~^ ERROR statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
static STATIC5: UnsafeEnum = UnsafeEnum::Variant6(0);
//~^ ERROR statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
struct SafeStruct {
@ -71,7 +71,7 @@ static STATIC6: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, field2: Safe
// field2 has an unsafe value, hence this should fail
static STATIC7: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
field2: SafeEnum::Variant3(WithDtor)};
//~^ ERROR statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
// Test variadic constructor for structs. The base struct should be examined
// as well as every field present in the constructor.
@ -84,7 +84,7 @@ static STATIC8: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
static STATIC9: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
..SafeStruct{field1: SafeEnum::Variant3(WithDtor),
field2: SafeEnum::Variant1}};
//~^^ ERROR statics are not allowed to have destructors
//~^^ ERROR destructors in statics are an unstable feature
struct UnsafeStruct;
@ -94,7 +94,7 @@ impl Drop for UnsafeStruct {
// Types with destructors are not allowed for statics
static STATIC10: UnsafeStruct = UnsafeStruct;
//~^ ERROR statics are not allowed to have destructor
//~^ ERROR destructors in statics are an unstable feature
struct MyOwned;
@ -105,19 +105,19 @@ static STATIC11: Box<MyOwned> = box MyOwned;
// to have types with destructors
// These should fail
static mut STATIC12: UnsafeStruct = UnsafeStruct;
//~^ ERROR mutable statics are not allowed to have destructors
//~^^ ERROR statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
//~^^ ERROR destructors in statics are an unstable feature
static mut STATIC13: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
//~^ ERROR mutable statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
field2: SafeEnum::Variant3(WithDtor)};
//~^ ERROR: statics are not allowed to have destructors
//~^ ERROR: destructors in statics are an unstable feature
static mut STATIC14: SafeStruct = SafeStruct {
//~^ ERROR mutable statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
field1: SafeEnum::Variant1,
field2: SafeEnum::Variant4("str".to_string())
//~^ ERROR method calls in statics are limited to constant inherent methods
//~^ ERROR calls in statics are limited to constant functions
};
static STATIC15: &'static [Box<MyOwned>] = &[
@ -131,7 +131,7 @@ static STATIC16: (&'static Box<MyOwned>, &'static Box<MyOwned>) = (
);
static mut STATIC17: SafeEnum = SafeEnum::Variant1;
//~^ ERROR mutable statics are not allowed to have destructors
//~^ ERROR destructors in statics are an unstable feature
static STATIC19: Box<isize> =
box 3;
@ -140,4 +140,5 @@ static STATIC19: Box<isize> =
pub fn main() {
let y = { static x: Box<isize> = box 3; x };
//~^ ERROR allocations are not allowed in statics
//~^^ ERROR cannot move out of static item
}

View File

@ -21,6 +21,20 @@ const C: usize = { foo!(); 2 };
const D: usize = { let x = 4; 2 };
//~^ ERROR: blocks in constants are limited to items and tail expressions
//~^^ ERROR: blocks in constants are limited to items and tail expressions
enum Foo {
Bar = { let x = 1; 3 }
//~^ ERROR: blocks in constants are limited to items and tail expressions
//~^^ ERROR: blocks in constants are limited to items and tail expressions
}
type Array = [u32; { let x = 2; 5 }];
//~^ ERROR: blocks in constants are limited to items and tail expressions
//~^^ ERROR: blocks in constants are limited to items and tail expressions
pub fn main() {
let _: Array = [0; { let x = 3; 5 }];
//~^ ERROR: blocks in constants are limited to items and tail expressions
//~^^ ERROR: blocks in constants are limited to items and tail expressions
}

View File

@ -19,6 +19,11 @@ fn black_box<T>(_: T) {
unimplemented!()
}
// Make sure that the two uses get two errors.
const FOO: u8 = [5u8][1];
//~^ ERROR array index out of bounds
//~^^ ERROR array index out of bounds
#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
fn main() {
let a = -std::i8::MIN;
@ -31,9 +36,11 @@ fn main() {
let d = 42u8 - (42u8 + 1);
//~^ WARN attempted to subtract with overflow
let _e = [5u8][1];
//~^ ERROR const index-expr is out of bounds
//~^ WARN array index out of bounds
black_box(a);
black_box(b);
black_box(c);
black_box(d);
black_box((FOO, FOO));
}

View File

@ -13,6 +13,11 @@
#![feature(const_fn)]
// no destructuring
const fn i((a, b): (u32, u32)) -> u32 { a + b } //~ ERROR: E0022
const fn i((
a, //~ ERROR: E0022
b //~ ERROR: E0022
): (u32, u32)) -> u32 {
a + b
}
fn main() {}

View File

@ -8,19 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// test that const fn signature and body errors are checked
// even in array lengths, which are evaluated before check_const
#![feature(const_fn)]
const X : usize = 2;
const fn f(x: usize) -> usize {
let mut sum = 0; //~ ERROR: E0016
for i in 0..x { //~ ERROR: E0016
let mut sum = 0;
for i in 0..x {
sum += i;
}
sum
sum //~ ERROR: E0250
}
#[allow(unused_variables)]

View File

@ -29,7 +29,7 @@ static Y: u32 = 0;
const fn get_Y() -> u32 {
Y
//~^ ERROR E0013
//~| ERROR cannot refer to other statics by value
//~| ERROR cannot refer to statics by value
}
const fn get_Y_addr() -> &'static u32 {
@ -37,5 +37,11 @@ const fn get_Y_addr() -> &'static u32 {
//~^ ERROR E0013
}
const fn get() -> u32 {
let x = 22; //~ ERROR E0016
let y = 44; //~ ERROR E0016
x + y
}
fn main() {
}

View File

@ -1,44 +0,0 @@
// 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.
// Test that we can't call random fns in a const fn or do other bad things.
#![feature(const_fn)]
use std::mem::transmute;
fn random() -> u32 { 0 }
const fn sub(x: &u32) -> usize {
unsafe { transmute(x) }
}
const fn sub1() -> u32 {
random()
}
static Y: u32 = 0;
const fn get_Y() -> u32 {
Y
}
const fn get_Y_addr() -> &'static u32 {
&Y
}
const fn get() -> u32 {
let x = 22; //~ ERROR E0016
let y = 44; //~ ERROR E0016
x + y
}
fn main() {
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
const FOO: &'static[u32] = &[1, 2, 3];
const BAR: u32 = FOO[5]; //~ ERROR const index-expr is out of bounds
const BAR: u32 = FOO[5]; //~ ERROR array index out of bounds
fn main() {
let _ = BAR;

View File

@ -16,7 +16,9 @@ impl std::ops::Neg for S {
fn neg(self) -> u32 { 0 }
}
const _MAX: usize = -1;
// FIXME(eddyb) move this back to a `-1` literal when
// MIR building stops eagerly erroring in that case.
const _MAX: usize = -(2 - 1);
//~^ WARN unary negation of unsigned integer
//~| ERROR unary negation of unsigned integer
//~| HELP use a cast or the `!` operator

View File

@ -17,7 +17,8 @@ static C: &'static usize = &(A.a);
static D: [usize; 1] = [1];
static E: usize = D[0];
//~^ ERROR: cannot refer to other statics by value
//~^ ERROR: cannot refer to the interior of another static
//~^^ ERROR: cannot refer to other statics by value
static F: &'static usize = &D[0];
//~^ ERROR: cannot refer to the interior of another static

View File

@ -10,10 +10,13 @@
const C1: &'static mut [usize] = &mut [];
//~^ ERROR: references in constants may only refer to immutable values
//~| ERROR: references in constants may only refer to immutable values
static mut S: usize = 3;
const C2: &'static mut usize = &mut S;
//~^ ERROR: constants cannot refer to other statics
//~^^ ERROR: references in constants may only refer to immutable values
const C2: &'static mut usize = unsafe { &mut S };
//~^ ERROR: constants cannot refer to statics
//~| ERROR: references in constants may only refer to immutable values
//~| ERROR: references in constants may only refer to immutable values
//~| ERROR: references in constants may only refer to immutable values
fn main() {}

View File

@ -14,19 +14,19 @@ const C: usize = 1;
static S: usize = 1;
const T1: &'static usize = &C;
const T2: &'static usize = &S; //~ ERROR: constants cannot refer to other statics
const T2: &'static usize = &S; //~ ERROR: constants cannot refer to statics
static T3: &'static usize = &C;
static T4: &'static usize = &S;
const T5: usize = C;
const T6: usize = S; //~ ERROR: constants cannot refer to other statics
//~^ cannot refer to other statics
const T6: usize = S; //~ ERROR: constants cannot refer to statics
//~^ cannot refer to statics
static T7: usize = C;
static T8: usize = S; //~ ERROR: cannot refer to other statics by value
const T9: Struct = Struct { a: C };
const T10: Struct = Struct { a: S }; //~ ERROR: cannot refer to other statics by value
//~^ ERROR: constants cannot refer to other statics
const T10: Struct = Struct { a: S }; //~ ERROR: cannot refer to statics by value
//~^ ERROR: constants cannot refer to statics
static T11: Struct = Struct { a: C };
static T12: Struct = Struct { a: S }; //~ ERROR: cannot refer to other statics by value

View File

@ -12,6 +12,6 @@ pub fn main() {
const z: &'static isize = {
static p: isize = 3;
&p
//~^ ERROR constants cannot refer to other statics, insert an intermediate constant instead
//~^ ERROR constants cannot refer to statics, use a constant instead
};
}

View File

@ -10,6 +10,7 @@
pub fn main() {
const z: &'static isize = {
//~^ ERROR blocks in constants are limited to items and tail expressions
let p = 3;
//~^ ERROR blocks in constants are limited to items and tail expressions
&p

View File

@ -11,7 +11,8 @@
struct A;
struct B;
static S: &'static B = &A; //~ ERROR user-defined dereference operators
static S: &'static B = &A;
//~^ ERROR calls in statics are limited to constant functions
use std::ops::Deref;

View File

@ -14,8 +14,7 @@ fn main() {
match i {
0...index => println!("winner"),
//~^ ERROR paths in constants may only refer to constants or functions
//~| ERROR non-constant path in constant expression
//~^ ERROR non-constant path in constant expression
_ => println!("hello"),
}
}

View File

@ -9,7 +9,8 @@
// except according to those terms.
const X: u8 =
|| -> u8 { 5 }() //~ ERROR function calls in constants are limited
|| -> u8 { 5 }()
//~^ ERROR calls in constants are limited to constant functions
;
fn main() {}

View File

@ -10,11 +10,12 @@
// Regression test for issue 9243
struct Test {
pub struct Test {
mem: isize,
}
pub static g_test: Test = Test {mem: 0}; //~ ERROR statics are not allowed to have destructors
pub static g_test: Test = Test {mem: 0};
//~^ ERROR destructors in statics are an unstable feature
impl Drop for Test {
fn drop(&mut self) {}

View File

@ -12,7 +12,6 @@ fn main() {
let x = 0;
match 1 {
0 ... x => {}
//~^ ERROR non-constant path in constant expr
//~| ERROR paths in constants may only refer to constants or functions
//~^ ERROR non-constant path in constant expression
};
}

View File

@ -17,4 +17,11 @@ use array::ARRAY;
static X: &'static u8 = &ARRAY[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
static Y: &'static u8 = &(&ARRAY)[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
static Z: u8 = (&ARRAY)[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
//~^^ ERROR: cannot refer to other statics by value
pub fn main() {}

View File

@ -12,6 +12,6 @@
static mut a: Box<isize> = box 3;
//~^ ERROR allocations are not allowed in statics
//~^^ ERROR mutable statics are not allowed to have boxes
//~^^ ERROR destructors in statics are an unstable feature
fn main() {}

View File

@ -18,7 +18,7 @@ extern crate rustc_plugin;
extern crate rustc_const_math;
extern crate syntax;
use rustc::mir::transform::{self, MirPass};
use rustc::mir::transform::{self, MirPass, MirSource};
use rustc::mir::repr::{Mir, Literal};
use rustc::mir::visit::MutVisitor;
use rustc::ty;
@ -26,13 +26,11 @@ use rustc::middle::const_val::ConstVal;
use rustc_const_math::ConstInt;
use rustc_plugin::Registry;
use syntax::ast::NodeId;
struct Pass;
impl transform::Pass for Pass {}
impl<'tcx> MirPass<'tcx> for Pass {
fn run_pass(&mut self, _: &ty::TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
fn run_pass(&mut self, _: &ty::TyCtxt<'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
Visitor.visit_mir(mir)
}
}

View File

@ -9,10 +9,10 @@
// except according to those terms.
static A: [u8; 1] = ['h' as u8];
static B: u8 = (&A)[0];
static C: &'static &'static &'static &'static [u8; 1] = & & & &A;
static D: u8 = (&C)[0];
const A: [u8; 1] = ['h' as u8];
const B: u8 = (&A)[0];
const C: &'static &'static &'static &'static [u8; 1] = & & & &A;
const D: u8 = (&C)[0];
pub fn main() {
assert_eq!(B, A[0]);

View File

@ -8,17 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_attrs)]
// ignore-pretty : (#23623) problems when ending with // comments
use std::{str, string};
const A: [u8; 2] = ['h' as u8, 'i' as u8];
const B: &'static [u8; 2] = &A;
const C: *const u8 = B as *const u8;
#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
pub fn main() {
unsafe {
let foo = &A as *const u8;

View File

@ -14,6 +14,7 @@ struct Point {
_x: i32,
_y: i32,
}
const STRUCT: Point = Point { _x: 42, _y: 42 };
const TUPLE1: (i32, i32) = (42, 42);
const TUPLE2: (&'static str, &'static str) = ("hello","world");
@ -26,7 +27,19 @@ fn mir() -> (Point, (i32, i32), (&'static str, &'static str)){
(struct1, tuple1, tuple2)
}
fn main(){
assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
#[derive(PartialEq, Eq, Debug)]
struct Newtype<T>(T);
const NEWTYPE: Newtype<&'static str> = Newtype("foobar");
#[rustc_mir]
fn test_promoted_newtype_str_ref() {
let x = &NEWTYPE;
assert_eq!(x, &Newtype("foobar"));
}
fn main(){
assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
test_promoted_newtype_str_ref();
}

View File

@ -121,7 +121,6 @@ impl<T> Foo for T {
struct S<T:?Sized>(u32, T);
#[rustc_no_mir] // FIXME #27840 MIR can't do rvalue promotion yet.
fn main() {
let array = [0,1,2,3,4];
let array2 = [5,6,7,8,9];

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(repr_simd, rustc_attrs, platform_intrinsics)]
#![feature(repr_simd, platform_intrinsics)]
// ignore-pretty : (#23623) problems when ending with // comments
@ -52,7 +52,6 @@ macro_rules! all_eq {
}}
}
#[rustc_no_mir] // FIXME #27840 MIR doesn't handle shuffle constants.
fn main() {
let x2 = i32x2(20, 21);
let x3 = i32x3(30, 31, 32);

View File

@ -261,6 +261,11 @@ pub fn run_tests(config: &Config) {
_ => { /* proceed */ }
}
// FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
if let Mode::CodegenUnits = config.mode {
let _ = fs::remove_dir_all("tmp/partitioning-tests");
}
let opts = test_opts(config);
let tests = make_tests(config);
// sadly osx needs some file descriptor limits raised for running tests in