diff --git a/mk/crates.mk b/mk/crates.mk index fec9e985e04..48c1e4ad59d 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 85b4b4f59c4..8378495599c 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -64,6 +64,7 @@ pub enum DepNode { IntrinsicCheck(D), MatchCheck(D), MirMapConstruction(D), + MirPass(D), MirTypeck(D), BorrowCheck(D), RvalueCheck(D), @@ -186,6 +187,7 @@ impl DepNode { 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), diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 053d32305be..640ef48493a 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -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)); } -} \ No newline at end of file +} diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 9ec05a9b292..9f8a3dbfa8f 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -36,6 +36,11 @@ pub struct Mir<'tcx> { /// used (eventually) for debuginfo. Indexed by a `ScopeId`. pub scopes: Vec, + /// 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>, + /// 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) + } } } } diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 410e3f9d066..520bfbddf9f 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -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 { + 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); + } } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 10afd3dd953..f6a241004b3 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -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: _ } => {} } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3e3dae3b3e9..174f626498b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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", diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 2b56366f1c6..1b570933bee 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index b5b27586a7f..c911b20c2ab 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -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 }); diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 1c41ca6f416..77dccb7e0d4 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -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" } diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index a2f7d2c9d72..a4f4e44b1b1 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -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 diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 1ed5fabf274..c6765f95d33 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -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, } -#[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>, - 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, 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>, - explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, - argument_scope_id: ScopeId, - ast_block: &'tcx hir::Block) - -> BlockAnd>> + 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, + arg_decls: Vec>, + 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(&mut self, + mut block: BasicBlock, + return_ty: ty::FnOutput<'tcx>, + arguments: A, + argument_scope_id: ScopeId, + ast_block: &'tcx hir::Block) + -> BlockAnd>> + where A: Iterator, 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 diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs new file mode 100644 index 00000000000..65d51d20528 --- /dev/null +++ b/src/librustc_mir/diagnostics.rs @@ -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 or the MIT license +// , 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 = 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 = 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 = Cell::new(1); +const B: &'static Cell = &A; +// error: cannot borrow a constant which contains interior mutability, create +// a static instead + +// or: +struct C { a: Cell } + +const D: C = C { a: Cell::new(1) }; +const E: &'static Cell = &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 { + value: Cell, +} + +unsafe impl Sync for NotThreadSafe {} + +static A: NotThreadSafe = NotThreadSafe { value : Cell::new(1) }; +static B: &'static NotThreadSafe = &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 +} diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 6d527f77800..049426db2f4 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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 { diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index c3a5fbd967c..5274b5e9aba 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -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, } } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ced73f34e0d..79d11e78bde 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -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; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 40334f652ee..d771c804389 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -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(&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(&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>, - fn_id: ast::NodeId, - span: Span, - decl: &'tcx hir::FnDecl, - body: &'tcx hir::Block) - -> Result, 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>, diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 77e8e37ef74..0e082ac262e 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -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)>, '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, " {{")?; diff --git a/src/librustc_mir/transform/break_critical_edges.rs b/src/librustc_mir/transform/break_critical_edges.rs index e1fb5dfd437..ee7c9015baa 100644 --- a/src/librustc_mir/transform/break_critical_edges.rs +++ b/src/librustc_mir/transform/break_critical_edges.rs @@ -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); } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 12bfa3aebc2..678d2c4614d 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -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); } } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index a52a8edc211..51f5c3cd7f5 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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; diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index edfe75b8430..9c9f95e2e63 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -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); } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs new file mode 100644 index 00000000000..c5ebe708eb4 --- /dev/null +++ b/src/librustc_mir/transform/promote_consts.rs @@ -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 or the MIT license +// , 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, + 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 { + 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, + + /// 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, + candidates: Vec) { + // 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 + }; + } + } + _ => {} + } + } +} diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs new file mode 100644 index 00000000000..90823528973 --- /dev/null +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -0,0 +1,1037 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A pass that qualifies constness of temporaries in constants, +//! static initializers and functions and also drives promotion. +//! +//! The Qualif flags below can be used to also provide better +//! diagnostics as to why a constant rvalue wasn't promoted. + +use rustc_data_structures::bitvec::BitVector; +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit::FnKind; +use rustc::hir::map::blocks::FnLikeNode; +use rustc::infer; +use rustc::traits::{self, ProjectionMode}; +use rustc::ty::{self, TyCtxt, Ty}; +use rustc::ty::cast::CastTy; +use rustc::mir::repr::*; +use rustc::mir::mir_map::MirMap; +use rustc::mir::transform::{Pass, MirMapPass, MirSource}; +use rustc::mir::visit::{LvalueContext, Visitor}; +use rustc::util::nodemap::DefIdMap; +use syntax::abi::Abi; +use syntax::codemap::Span; +use syntax::feature_gate::UnstableFeatures; + +use std::collections::hash_map::Entry; +use std::fmt; + +use build::Location; +use traversal::{self, ReversePostorder}; + +use super::promote_consts::{self, Candidate, TempState}; + +bitflags! { + flags Qualif: u8 { + // Const item's qualification while recursing. + // Recursive consts are an error. + const RECURSIVE = 1 << 0, + + // Constant containing interior mutability (UnsafeCell). + const MUTABLE_INTERIOR = 1 << 1, + + // Constant containing an ADT that implements Drop. + const NEEDS_DROP = 1 << 2, + + // Function argument. + const FN_ARGUMENT = 1 << 3, + + // Static lvalue or move from a static. + const STATIC = 1 << 4, + + // Reference to a static. + const STATIC_REF = 1 << 5, + + // Not constant at all - non-`const fn` calls, asm!, + // pointer comparisons, ptr-to-int casts, etc. + const NOT_CONST = 1 << 6, + + // Borrows of temporaries can be promoted only + // if they have none of the above qualifications. + const UNPROMOTABLE = !0, + + // Const items can only have MUTABLE_INTERIOR + // without producing an error. + const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits + } +} + +impl Qualif { + /// Remove flags which are impossible for the given type. + fn restrict<'a, 'tcx>(&mut self, ty: Ty<'tcx>, + param_env: &ty::ParameterEnvironment<'a, 'tcx>) { + if !ty.type_contents(param_env.tcx).interior_unsafe() { + *self = *self - Qualif::MUTABLE_INTERIOR; + } + if !param_env.tcx.type_needs_drop_given_env(ty, param_env) { + *self = *self - Qualif::NEEDS_DROP; + } + } +} + +/// What kind of item we are in. +#[derive(Copy, Clone, PartialEq, Eq)] +enum Mode { + Const, + Static, + StaticMut, + ConstFn, + Fn +} + +impl fmt::Display for Mode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Mode::Const => write!(f, "constant"), + Mode::Static | Mode::StaticMut => write!(f, "static"), + Mode::ConstFn => write!(f, "constant function"), + Mode::Fn => write!(f, "function") + } + } +} + +fn is_const_fn(tcx: &TyCtxt, def_id: DefId) -> bool { + if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + let fn_like = FnLikeNode::from_node(tcx.map.get(node_id)); + match fn_like.map(|f| f.kind()) { + Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => { + c == hir::Constness::Const + } + Some(FnKind::Method(_, m, _, _)) => { + m.constness == hir::Constness::Const + } + _ => false + } + } else { + tcx.sess.cstore.is_const_fn(def_id) + } +} + +struct Qualifier<'a, 'tcx: 'a> { + mode: Mode, + span: Span, + def_id: DefId, + mir: &'a Mir<'tcx>, + rpo: ReversePostorder<'a, 'tcx>, + tcx: &'a TyCtxt<'tcx>, + param_env: ty::ParameterEnvironment<'a, 'tcx>, + qualif_map: &'a mut DefIdMap, + mir_map: Option<&'a MirMap<'tcx>>, + temp_qualif: Vec>, + return_qualif: Option, + qualif: Qualif, + const_fn_arg_vars: BitVector, + location: Location, + temp_promotion_state: Vec, + promotion_candidates: Vec +} + +impl<'a, 'tcx> Qualifier<'a, 'tcx> { + fn new(param_env: ty::ParameterEnvironment<'a, 'tcx>, + qualif_map: &'a mut DefIdMap, + mir_map: Option<&'a MirMap<'tcx>>, + def_id: DefId, + mir: &'a Mir<'tcx>, + mode: Mode) + -> Qualifier<'a, 'tcx> { + let mut rpo = traversal::reverse_postorder(mir); + let temps = promote_consts::collect_temps(mir, &mut rpo); + rpo.reset(); + Qualifier { + mode: mode, + span: mir.span, + def_id: def_id, + mir: mir, + rpo: rpo, + tcx: param_env.tcx, + param_env: param_env, + qualif_map: qualif_map, + mir_map: mir_map, + temp_qualif: vec![None; mir.temp_decls.len()], + return_qualif: None, + qualif: Qualif::empty(), + const_fn_arg_vars: BitVector::new(mir.var_decls.len()), + location: Location { + block: START_BLOCK, + statement_index: 0 + }, + temp_promotion_state: temps, + promotion_candidates: vec![] + } + } + + // FIXME(eddyb) we could split the errors into meaningful + // categories, but enabling full miri would make that + // slightly pointless (even with feature-gating). + fn not_const(&mut self) { + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0019, + "{} contains unimplemented expression type", self.mode); + } + } + + /// Error about extra statements in a constant. + fn statement_like(&mut self) { + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0016, + "blocks in {}s are limited to items and tail expressions", + self.mode); + } + } + + /// Add the given qualification to self.qualif. + fn add(&mut self, qualif: Qualif) { + self.qualif = self.qualif | qualif; + } + + /// Add the given type's qualification to self.qualif. + fn add_type(&mut self, ty: Ty<'tcx>) { + self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP); + self.qualif.restrict(ty, &self.param_env); + } + + /// Within the provided closure, self.qualif will start + /// out empty, and its value after the closure returns will + /// be combined with the value before the call to nest. + fn nest(&mut self, f: F) { + let original = self.qualif; + self.qualif = Qualif::empty(); + f(self); + self.add(original); + } + + /// Check for NEEDS_DROP (from an ADT or const fn call) and + /// error, unless we're in a function, or the feature-gate + /// for globals with destructors is enabled. + fn deny_drop(&self) { + if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) { + return; + } + + // Static and const fn's allow destructors, but they're feature-gated. + let msg = if self.mode != Mode::Const { + // Feature-gate for globals with destructors is enabled. + if self.tcx.sess.features.borrow().drop_types_in_const { + return; + } + + // This comes from a macro that has #[allow_internal_unstable]. + if self.tcx.sess.codemap().span_allows_unstable(self.span) { + return; + } + + format!("destructors in {}s are an unstable feature", + self.mode) + } else { + format!("{}s are not allowed to have destructors", + self.mode) + }; + + let mut err = + struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg); + if self.mode != Mode::Const { + help!(&mut err, + "in Nightly builds, add `#![feature(drop_types_in_const)]` \ + to the crate attributes to enable"); + } + err.emit(); + } + + /// Check if an Lvalue with the current qualifications could + /// be consumed, by either an operand or a Deref projection. + fn try_consume(&mut self) -> bool { + if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn { + let msg = if self.mode == Mode::Static || + self.mode == Mode::StaticMut { + "cannot refer to other statics by value, use the \ + address-of operator or a constant instead" + } else { + "cannot refer to statics by value, use a constant instead" + }; + span_err!(self.tcx.sess, self.span, E0394, "{}", msg); + + // Replace STATIC with NOT_CONST to avoid further errors. + self.qualif = self.qualif - Qualif::STATIC; + self.add(Qualif::NOT_CONST); + + false + } else { + true + } + } + + /// Assign the current qualification to the given destination. + fn assign(&mut self, dest: &Lvalue<'tcx>) { + let qualif = self.qualif; + let span = self.span; + let store = |slot: &mut Option| { + if slot.is_some() { + span_bug!(span, "multiple assignments to {:?}", dest); + } + *slot = Some(qualif); + }; + + // Only handle promotable temps in non-const functions. + if self.mode == Mode::Fn { + if let Lvalue::Temp(index) = *dest { + if self.temp_promotion_state[index as usize].is_promotable() { + store(&mut self.temp_qualif[index as usize]); + } + } + return; + } + + match *dest { + Lvalue::Temp(index) => store(&mut self.temp_qualif[index as usize]), + Lvalue::ReturnPointer => store(&mut self.return_qualif), + + Lvalue::Projection(box Projection { + base: Lvalue::Temp(index), + elem: ProjectionElem::Deref + }) if self.mir.temp_decls[index as usize].ty.is_unique() + && self.temp_qualif[index as usize].map_or(false, |qualif| { + qualif.intersects(Qualif::NOT_CONST) + }) => { + // Part of `box expr`, we should've errored + // already for the Box allocation Rvalue. + } + + // This must be an explicit assignment. + _ => { + // Catch more errors in the destination. + self.visit_lvalue(dest, LvalueContext::Store); + self.statement_like(); + } + } + } + + /// Returns true if the block ends in a bounds check branch, i.e.: + /// len = Len(array); + /// cond = Lt(idx, len); + /// if cond { + /// ... + /// } else { + /// loc = (...); + /// loc_ref = &loc; + /// panic_bounds_check(loc_ref, idx, len); + /// } + fn is_bounds_check(&self, bb: BasicBlock, + cond_op: &Operand<'tcx>, + if_else: BasicBlock) -> bool { + use rustc::mir::repr::Lvalue::*; + use rustc::mir::repr::Operand::Consume; + use rustc::mir::repr::Rvalue::*; + use rustc::mir::repr::StatementKind::*; + use rustc::mir::repr::TerminatorKind::*; + + let stmts = &self.mir[bb].statements; + let stmts_panic = &self.mir[if_else].statements; + if stmts.len() < 2 || stmts_panic.len() != 2 { + return false; + } + + let all = (&stmts[stmts.len() - 2].kind, + &stmts[stmts.len() - 1].kind, + cond_op, + &stmts_panic[0].kind, + &stmts_panic[1].kind, + &self.mir[if_else].terminator().kind); + match all { + (&Assign(Temp(len), Len(_)), + &Assign(Temp(cond), BinaryOp(BinOp::Lt, ref idx, Consume(Temp(len2)))), + /* if */ &Consume(Temp(cond2)), /* {...} else */ + &Assign(Temp(loc), Aggregate(..)), + &Assign(Temp(loc_ref), Ref(_, _, Temp(loc2))), + &Call { + func: Operand::Constant(Constant { + literal: Literal::Item { def_id, .. }, .. + }), + ref args, + destination: None, + .. + }) => { + len == len2 && cond == cond2 && loc == loc2 && + args[0] == Consume(Temp(loc_ref)) && + args[1] == *idx && + args[2] == Consume(Temp(len)) && + Some(def_id) == self.tcx.lang_items.panic_bounds_check_fn() + } + _ => false + } + } + + /// Qualify a whole const, static initializer or const fn. + fn qualify_const(&mut self) -> Qualif { + let mir = self.mir; + + let mut seen_blocks = BitVector::new(mir.basic_blocks.len()); + let mut bb = START_BLOCK; + loop { + seen_blocks.insert(bb.index()); + + self.visit_basic_block_data(bb, &mir[bb]); + + let target = match mir[bb].terminator().kind { + TerminatorKind::Goto { target } | + // Drops are considered noops. + TerminatorKind::Drop { target, .. } | + TerminatorKind::Call { destination: Some((_, target)), .. } => { + Some(target) + } + + // Non-terminating calls cannot produce any value. + TerminatorKind::Call { destination: None, .. } => { + return Qualif::empty(); + } + + // Need to allow bounds checking branches. + TerminatorKind::If { ref cond, targets: (if_true, if_else) } => { + if self.is_bounds_check(bb, cond, if_else) { + Some(if_true) + } else { + None + } + } + + TerminatorKind::Switch {..} | + TerminatorKind::SwitchInt {..} | + TerminatorKind::Resume => None, + + TerminatorKind::Return => { + // Check for unused values. This usually means + // there are extra statements in the AST. + for i in 0..mir.temp_decls.len() { + if self.temp_qualif[i].is_none() { + continue; + } + + let state = self.temp_promotion_state[i]; + if let TempState::Defined { location, uses: 0 } = state { + let data = &mir[location.block]; + let stmt_idx = location.statement_index; + + // Get the span for the initialization. + if stmt_idx < data.statements.len() { + self.span = data.statements[stmt_idx].span; + } else { + self.span = data.terminator().span; + } + + // Treat this as a statement in the AST. + self.statement_like(); + } + } + + // Make sure there are no extra unassigned variables. + self.qualif = Qualif::NOT_CONST; + for index in 0..mir.var_decls.len() { + if !self.const_fn_arg_vars.contains(index) { + self.assign(&Lvalue::Var(index as u32)); + } + } + + break; + } + }; + + match target { + // No loops allowed. + Some(target) if !seen_blocks.contains(target.index()) => { + bb = target; + } + _ => { + self.not_const(); + break; + } + } + } + + let return_ty = mir.return_ty.unwrap(); + self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST); + + match self.mode { + Mode::StaticMut => { + // Check for destructors in static mut. + self.add_type(return_ty); + self.deny_drop(); + } + _ => { + // Account for errors in consts by using the + // conservative type qualification instead. + if self.qualif.intersects(Qualif::CONST_ERROR) { + self.qualif = Qualif::empty(); + self.add_type(return_ty); + } + } + } + self.qualif + } +} + +/// Accumulates an Rvalue or Call's effects in self.qualif. +/// For functions (constant or not), it also records +/// candidates for promotion in promotion_candidates. +impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx> { + fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) { + match *lvalue { + Lvalue::Arg(_) => { + self.add(Qualif::FN_ARGUMENT); + } + Lvalue::Var(_) => { + self.add(Qualif::NOT_CONST); + } + Lvalue::Temp(index) => { + if let Some(qualif) = self.temp_qualif[index as usize] { + self.add(qualif); + } else { + self.not_const(); + } + } + Lvalue::Static(_) => { + self.add(Qualif::STATIC); + if self.mode == Mode::Const || self.mode == Mode::ConstFn { + span_err!(self.tcx.sess, self.span, E0013, + "{}s cannot refer to statics, use \ + a constant instead", self.mode); + } + } + Lvalue::ReturnPointer => { + self.not_const(); + } + Lvalue::Projection(ref proj) => { + self.nest(|this| { + this.super_lvalue(lvalue, context); + match proj.elem { + ProjectionElem::Deref => { + if !this.try_consume() { + return; + } + + if this.qualif.intersects(Qualif::STATIC_REF) { + this.qualif = this.qualif - Qualif::STATIC_REF; + this.add(Qualif::STATIC); + } + + let base_ty = this.mir.lvalue_ty(this.tcx, &proj.base) + .to_ty(this.tcx); + if let ty::TyRawPtr(_) = base_ty.sty { + this.add(Qualif::NOT_CONST); + if this.mode != Mode::Fn { + span_err!(this.tcx.sess, this.span, E0396, + "raw pointers cannot be dereferenced in {}s", + this.mode); + } + } + } + + ProjectionElem::Field(..) | + ProjectionElem::Index(_) => { + if this.mode != Mode::Fn && + this.qualif.intersects(Qualif::STATIC) { + span_err!(this.tcx.sess, this.span, E0494, + "cannot refer to the interior of another \ + static, use a constant instead"); + } + let ty = this.mir.lvalue_ty(this.tcx, lvalue) + .to_ty(this.tcx); + this.qualif.restrict(ty, &this.param_env); + } + + ProjectionElem::ConstantIndex {..} | + ProjectionElem::Downcast(..) => { + this.not_const() + } + } + }); + } + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>) { + match *operand { + Operand::Consume(_) => { + self.nest(|this| { + this.super_operand(operand); + this.try_consume(); + }); + } + Operand::Constant(ref constant) => { + // Only functions and methods can have these types. + if let ty::TyFnDef(..) = constant.ty.sty { + return; + } + + if let Literal::Item { def_id, substs } = constant.literal { + // Don't peek inside generic (associated) constants. + if !substs.types.is_empty() { + self.add_type(constant.ty); + } else { + let qualif = qualify_const_item_cached(self.tcx, + self.qualif_map, + self.mir_map, + def_id); + self.add(qualif); + } + + // FIXME(eddyb) check recursive constants here, + // instead of rustc_passes::static_recursion. + if self.qualif.intersects(Qualif::RECURSIVE) { + span_bug!(constant.span, + "recursive constant wasn't caught earlier"); + } + + // Let `const fn` transitively have destructors, + // but they do get stopped in `const` or `static`. + if self.mode != Mode::ConstFn { + self.deny_drop(); + } + } + } + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + // Recurse through operands and lvalues. + self.super_rvalue(rvalue); + + match *rvalue { + Rvalue::Use(_) | + Rvalue::Repeat(..) | + Rvalue::UnaryOp(..) | + Rvalue::Cast(CastKind::ReifyFnPointer, _, _) | + Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) | + Rvalue::Cast(CastKind::Unsize, _, _) => {} + + Rvalue::Len(_) => { + // Static lvalues in consts would have errored already, + // don't treat length checks as reads from statics. + self.qualif = self.qualif - Qualif::STATIC; + } + + Rvalue::Ref(_, kind, ref lvalue) => { + // Static lvalues in consts would have errored already, + // only keep track of references to them here. + if self.qualif.intersects(Qualif::STATIC) { + self.qualif = self.qualif - Qualif::STATIC; + self.add(Qualif::STATIC_REF); + } + + let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); + if kind == BorrowKind::Mut { + // In theory, any zero-sized value could be borrowed + // mutably without consequences. However, only &mut [] + // is allowed right now, and only in functions. + let allow = if let ty::TyArray(_, 0) = ty.sty { + self.mode == Mode::Fn + } else if self.mode == Mode::StaticMut { + // Inside a `static mut`, &mut [...] is also allowed. + match ty.sty { + ty::TyArray(..) | ty::TySlice(_) => { + // Mutating can expose drops, be conservative. + self.add_type(ty); + self.deny_drop(); + true + } + _ => false + } + } else { + false + }; + + if !allow { + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0017, + "references in {}s may only refer \ + to immutable values", self.mode); + } + } + } else { + // Constants cannot be borrowed if they contain interior mutability as + // it means that our "silent insertion of statics" could change + // initializer values (very bad). + if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) { + // Replace MUTABLE_INTERIOR with NOT_CONST to avoid + // duplicate errors (from reborrowing, for example). + self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR; + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0492, + "cannot borrow a constant which contains \ + interior mutability, create a static instead"); + } + } + } + + // We might have a candidate for promotion. + let candidate = Candidate::Ref(self.location); + if self.mode == Mode::Fn || self.mode == Mode::ConstFn { + if !self.qualif.intersects(Qualif::UNPROMOTABLE) { + self.promotion_candidates.push(candidate); + } + } + } + + Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { + let operand_ty = self.mir.operand_ty(self.tcx, operand); + let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); + let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); + match (cast_in, cast_out) { + (CastTy::Ptr(_), CastTy::Int(_)) | + (CastTy::FnPtr, CastTy::Int(_)) => { + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0018, + "raw pointers cannot be cast to integers in {}s", + self.mode); + } + } + _ => {} + } + } + + Rvalue::BinaryOp(op, ref lhs, _) => { + if let ty::TyRawPtr(_) = self.mir.operand_ty(self.tcx, lhs).sty { + assert!(op == BinOp::Eq || op == BinOp::Ne || + op == BinOp::Le || op == BinOp::Lt || + op == BinOp::Ge || op == BinOp::Gt); + + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0395, + "raw pointers cannot be compared in {}s", + self.mode); + } + } + } + + Rvalue::Box(_) => { + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0010, + "allocations are not allowed in {}s", self.mode); + } + } + + Rvalue::Aggregate(ref kind, _) => { + if let AggregateKind::Adt(def, _, _) = *kind { + if def.has_dtor() { + self.add(Qualif::NEEDS_DROP); + self.deny_drop(); + } + + if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() { + let ty = self.mir.rvalue_ty(self.tcx, rvalue).unwrap(); + self.add_type(ty); + assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR)); + // Even if the value inside may not need dropping, + // mutating it would change that. + if !self.qualif.intersects(Qualif::NOT_CONST) { + self.deny_drop(); + } + } + } + } + + Rvalue::Slice {..} | + Rvalue::InlineAsm {..} => { + self.not_const(); + } + } + } + + fn visit_terminator_kind(&mut self, bb: BasicBlock, kind: &TerminatorKind<'tcx>) { + if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind { + self.visit_operand(func); + + let fn_ty = self.mir.operand_ty(self.tcx, func); + let (is_shuffle, is_const_fn) = match fn_ty.sty { + ty::TyFnDef(def_id, _, f) => { + (f.abi == Abi::PlatformIntrinsic && + self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"), + is_const_fn(self.tcx, def_id)) + } + _ => (false, false) + }; + + for (i, arg) in args.iter().enumerate() { + self.nest(|this| { + this.visit_operand(arg); + if is_shuffle && i == 2 && this.mode == Mode::Fn { + let candidate = Candidate::ShuffleIndices(bb); + if !this.qualif.intersects(Qualif::UNPROMOTABLE) { + this.promotion_candidates.push(candidate); + } else { + span_err!(this.tcx.sess, this.span, E0526, + "shuffle indices are not constant"); + } + } + }); + } + + // Const fn calls. + if is_const_fn { + // We are in a const or static initializer, + if self.mode != Mode::Fn && + + // feature-gate is not enabled, + !self.tcx.sess.features.borrow().const_fn && + + // this doesn't come from a crate with the feature-gate enabled, + self.def_id.is_local() && + + // this doesn't come from a macro that has #[allow_internal_unstable] + !self.tcx.sess.codemap().span_allows_unstable(self.span) + { + let mut err = self.tcx.sess.struct_span_err(self.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(); + } + } else { + self.qualif = Qualif::NOT_CONST; + if self.mode != Mode::Fn { + // FIXME(#24111) Remove this check when const fn stabilizes + let (msg, note) = if let UnstableFeatures::Disallow = + self.tcx.sess.opts.unstable_features { + (format!("calls in {}s are limited to \ + struct and enum constructors", + self.mode), + Some("a limited form of compile-time function \ + evaluation is available on a nightly \ + compiler via `const fn`")) + } else { + (format!("calls in {}s are limited \ + to constant functions, \ + struct and enum constructors", + self.mode), + None) + }; + let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg); + if let Some(note) = note { + err.span_note(self.span, note); + } + err.emit(); + } + } + + if let Some((ref dest, _)) = *destination { + // Avoid propagating irrelevant callee/argument qualifications. + if self.qualif.intersects(Qualif::CONST_ERROR) { + self.qualif = Qualif::NOT_CONST; + } else { + // Be conservative about the returned value of a const fn. + let tcx = self.tcx; + let ty = self.mir.lvalue_ty(tcx, dest).to_ty(tcx); + self.qualif = Qualif::empty(); + self.add_type(ty); + + // Let `const fn` transitively have destructors, + // but they do get stopped in `const` or `static`. + if self.mode != Mode::ConstFn { + self.deny_drop(); + } + } + self.assign(dest); + } + } else { + // Qualify any operands inside other terminators. + self.super_terminator_kind(bb, kind); + } + } + + fn visit_assign(&mut self, _: BasicBlock, dest: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { + self.visit_rvalue(rvalue); + + // Check the allowed const fn argument forms. + if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) { + if self.const_fn_arg_vars.insert(index as usize) { + // Direct use of an argument is permitted. + if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue { + return; + } + + // Avoid a generic error for other uses of arguments. + if self.qualif.intersects(Qualif::FN_ARGUMENT) { + let decl = &self.mir.var_decls[index as usize]; + span_err!(self.tcx.sess, decl.span, E0022, + "arguments of constant functions can only \ + be immutable by-value bindings"); + return; + } + } + } + + self.assign(dest); + } + + fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) { + assert_eq!(self.location.block, bb); + self.span = statement.span; + self.nest(|this| this.super_statement(bb, statement)); + self.location.statement_index += 1; + } + + fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) { + assert_eq!(self.location.block, bb); + self.span = terminator.span; + self.nest(|this| this.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); + } +} + +fn qualify_const_item_cached<'tcx>(tcx: &TyCtxt<'tcx>, + qualif_map: &mut DefIdMap, + mir_map: Option<&MirMap<'tcx>>, + def_id: DefId) + -> Qualif { + match qualif_map.entry(def_id) { + Entry::Occupied(entry) => return *entry.get(), + Entry::Vacant(entry) => { + // Guard against `const` recursion. + entry.insert(Qualif::RECURSIVE); + } + } + + let extern_mir; + let param_env_and_mir = if def_id.is_local() { + let node_id = tcx.map.as_local_node_id(def_id).unwrap(); + mir_map.and_then(|map| map.map.get(&node_id)).map(|mir| { + (ty::ParameterEnvironment::for_item(tcx, node_id), mir) + }) + } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) { + // These should only be monomorphic constants. + extern_mir = mir; + Some((tcx.empty_parameter_environment(), &extern_mir)) + } else { + None + }; + + let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| { + bug!("missing constant MIR for {}", tcx.item_path_str(def_id)) + }); + + let mut qualifier = Qualifier::new(param_env, qualif_map, mir_map, + def_id, mir, Mode::Const); + let qualif = qualifier.qualify_const(); + qualifier.qualif_map.insert(def_id, qualif); + qualif +} + +pub struct QualifyAndPromoteConstants; + +impl Pass for QualifyAndPromoteConstants {} + +impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { + let mut qualif_map = DefIdMap(); + + // First, visit `const` items, potentially recursing, to get + // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications. + for &id in map.map.keys() { + 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); + if let MirSource::Const(_) = src { + qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id); + } + } + + // Then, handle everything else, without recursing, + // as the MIR map is not shared, since promotion + // in functions (including `const fn`) mutates it. + for (&id, mir) in &mut map.map { + 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); + let mode = match src { + MirSource::Fn(_) => { + if is_const_fn(tcx, def_id) { + Mode::ConstFn + } else { + Mode::Fn + } + } + MirSource::Const(_) => continue, + MirSource::Static(_, hir::MutImmutable) => Mode::Static, + MirSource::Static(_, hir::MutMutable) => Mode::StaticMut, + MirSource::Promoted(..) => bug!() + }; + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + + if mode == Mode::Fn || mode == Mode::ConstFn { + // This is ugly because Qualifier holds onto mir, + // which can't be mutated until its scope ends. + let (temps, candidates) = { + let mut qualifier = Qualifier::new(param_env, &mut qualif_map, + None, def_id, mir, mode); + if mode == Mode::ConstFn { + // Enforce a constant-like CFG for `const fn`. + qualifier.qualify_const(); + } else { + while let Some((bb, data)) = qualifier.rpo.next() { + qualifier.visit_basic_block_data(bb, data); + } + } + + (qualifier.temp_promotion_state, + qualifier.promotion_candidates) + }; + + // Do the actual promotion, now that we know what's viable. + promote_consts::promote_candidates(mir, tcx, temps, candidates); + } else { + let mut qualifier = Qualifier::new(param_env, &mut qualif_map, + None, def_id, mir, mode); + qualifier.qualify_const(); + } + + // Statics must be Sync. + if mode == Mode::Static { + let ty = mir.return_ty.unwrap(); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + None, + ProjectionMode::AnyFinal); + let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic); + let mut fulfillment_cx = traits::FulfillmentContext::new(); + fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); + if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { + traits::report_fulfillment_errors(&infcx, &err); + } + + if let Err(ref errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) { + traits::report_fulfillment_errors_as_warnings(&infcx, errors, id); + } + } + } + } +} diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs index 2099e9a435a..e0d05a17d43 100644 --- a/src/librustc_mir/transform/remove_dead_blocks.rs +++ b/src/librustc_mir/transform/remove_dead_blocks.rs @@ -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()); diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 00b8f5c0930..a137a812867 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -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(); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 11ac1fa8f82..18ebadf42d6 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -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 { + DepNode::MirTypeck(def_id) + } +} diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs deleted file mode 100644 index 97a4c14863d..00000000000 --- a/src/librustc_passes/const_fn.rs +++ /dev/null @@ -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 or the MIT license -// , 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"); - } -} diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 6fb9739fca4..c417ec79ff2 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -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; } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index c89e9bb1959..77f896e011b 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -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 = 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 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 = 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> = None; - -// error: mutable statics are not allowed to have destructors -static mut BAR: Option> = 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 = Cell::new(1); -const B: &'static Cell = &A; -// error: cannot borrow a constant which contains interior mutability, create -// a static instead - -// or: -struct C { a: Cell } - -const D: C = C { a: Cell::new(1) }; -const E: &'static Cell = &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 { - value: Cell, -} - -unsafe impl Sync for NotThreadSafe {} - -static A: NotThreadSafe = NotThreadSafe { value : Cell::new(1) }; -static B: &'static NotThreadSafe = &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! { diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index b235962eb9a..67a9c2fd17e 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -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; diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index e8181579911..a0311ec6066 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -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); + } } } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index b9af0bbe3d1..09d38e8946b 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -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 { +pub fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option { 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 { } } -fn const_err(cx: &CrateContext, - e: &hir::Expr, - result: Result, - trueconst: TrueConst) - -> Result<(), ConstEvalFailure> { +pub fn const_err(cx: &CrateContext, + span: Span, + result: Result, + trueconst: TrueConst) + -> Result { 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 diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 1217b2b5a1b..2ee880fa3d8 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -131,6 +131,9 @@ pub struct LocalCrateContext<'tcx> { /// Cache of external const values extern_const_values: RefCell>, + /// Mapping from static definitions to their DefId's. + statics: RefCell>, + impl_method_cache: RefCell>, /// 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> { + &self.local.statics + } + pub fn impl_method_cache<'a>(&'a self) -> &'a RefCell> { &self.local.impl_method_cache diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs index 5e4902cf3ca..d9de673db27 100644 --- a/src/librustc_trans/diagnostics.rs +++ b/src/librustc_trans/diagnostics.rs @@ -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 -``` -"##, } diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 7c5ce371ee9..8653c977362 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -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> = (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 diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index d0b47934bcf..e605ef81c58 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -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); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index cf85595c08e..93206179837 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -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>, + + /// Variable values - specifically, argument bindings of a const fn. + vars: Vec>>, + + /// Temp values. + temps: Vec>>, + + /// Value assigned to Return, which is the resulting constant. + return_value: Option> +} + + +impl<'a, 'tcx> MirConstContext<'a, 'tcx> { + fn new(ccx: &'a CrateContext<'a, 'tcx>, + mir: &'a mir::Mir<'tcx>, + substs: &'tcx Substs<'tcx>, + args: Vec>) + -> 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>) + -> Result, 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(&self, value: &T) -> T + where T : TypeFoldable<'tcx> + { + monomorphize::apply_param_substs(self.ccx.tcx(), + self.substs, + value) + } + + fn trans(&mut self) -> Result, 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::, _>>()?; + 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, 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, 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, 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::, _>>()?; + + // 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 { + let instance = Instance::mono(ccx.tcx(), def_id); + MirConstContext::trans_def(ccx, instance, vec![]).map(|c| c.llval) +} diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 695806aa82c..e57a5cbc5b8 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -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)) } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 75d9ca32a21..b98e04e51c0 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -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; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index c15d6cd5b24..fc726a3474f 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -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 + } } } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 67d7f44cbbf..1236100a4d5 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -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) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e269475d1e2..7f01821b004 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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! ( diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock index 1fa4d5398f4..9dc1899e5a0 100644 --- a/src/rustc/Cargo.lock +++ b/src/rustc/Cargo.lock @@ -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", ] diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs index 63a5cf65e36..1134dbfd1c4 100644 --- a/src/test/compile-fail/array_const_index-0.rs +++ b/src/test/compile-fail/array_const_index-0.rs @@ -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; diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs index 1f56cd8e875..e59895cda44 100644 --- a/src/test/compile-fail/array_const_index-1.rs +++ b/src/test/compile-fail/array_const_index-1.rs @@ -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; diff --git a/src/test/compile-fail/auxiliary/pub_static_array.rs b/src/test/compile-fail/auxiliary/pub_static_array.rs index 4419a5ae83c..7248d0e543b 100644 --- a/src/test/compile-fail/auxiliary/pub_static_array.rs +++ b/src/test/compile-fail/auxiliary/pub_static_array.rs @@ -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]; diff --git a/src/test/compile-fail/check-static-immutable-mut-slices.rs b/src/test/compile-fail/check-static-immutable-mut-slices.rs index 1804b9e04c2..370cfe9d550 100644 --- a/src/test/compile-fail/check-static-immutable-mut-slices.rs +++ b/src/test/compile-fail/check-static-immutable-mut-slices.rs @@ -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() { } diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs index c3a1de11752..df22e2ea4de 100644 --- a/src/test/compile-fail/check-static-values-constraints.rs +++ b/src/test/compile-fail/check-static-values-constraints.rs @@ -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 = 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] = &[ @@ -131,7 +131,7 @@ static STATIC16: (&'static Box, &'static Box) = ( ); 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 = box 3; @@ -140,4 +140,5 @@ static STATIC19: Box = pub fn main() { let y = { static x: Box = box 3; x }; //~^ ERROR allocations are not allowed in statics + //~^^ ERROR cannot move out of static item } diff --git a/src/test/compile-fail/const-block-non-item-statement.rs b/src/test/compile-fail/const-block-non-item-statement.rs index 5ccfb1ddec7..edb85023c9b 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -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 } diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 3fb9a3f236c..a25255c010c 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -19,6 +19,11 @@ fn black_box(_: 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)); } diff --git a/src/test/compile-fail/const-fn-destructuring-arg.rs b/src/test/compile-fail/const-fn-destructuring-arg.rs index 1642c041067..c3d5975fe01 100644 --- a/src/test/compile-fail/const-fn-destructuring-arg.rs +++ b/src/test/compile-fail/const-fn-destructuring-arg.rs @@ -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() {} diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs index cb6f2d0215f..45a00de48e7 100644 --- a/src/test/compile-fail/const-fn-error.rs +++ b/src/test/compile-fail/const-fn-error.rs @@ -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)] diff --git a/src/test/compile-fail/const-fn-not-safe-for-const.rs b/src/test/compile-fail/const-fn-not-safe-for-const.rs index f8381978dc7..48877a60d25 100644 --- a/src/test/compile-fail/const-fn-not-safe-for-const.rs +++ b/src/test/compile-fail/const-fn-not-safe-for-const.rs @@ -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() { } diff --git a/src/test/compile-fail/const-fn-not-safe-for-const2.rs b/src/test/compile-fail/const-fn-not-safe-for-const2.rs deleted file mode 100644 index a053847e882..00000000000 --- a/src/test/compile-fail/const-fn-not-safe-for-const2.rs +++ /dev/null @@ -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 or the MIT license -// , 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() { -} diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index 519c4917c71..b50468c33fd 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -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; diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs index 93e09c6d8d2..98cc2fc0c3e 100644 --- a/src/test/compile-fail/feature-gate-negate-unsigned.rs +++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs @@ -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 diff --git a/src/test/compile-fail/issue-17718-borrow-interior.rs b/src/test/compile-fail/issue-17718-borrow-interior.rs index d33c12668f2..31352c57f1b 100644 --- a/src/test/compile-fail/issue-17718-borrow-interior.rs +++ b/src/test/compile-fail/issue-17718-borrow-interior.rs @@ -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 diff --git a/src/test/compile-fail/issue-17718-const-bad-values.rs b/src/test/compile-fail/issue-17718-const-bad-values.rs index 6ee869d65a8..af356588ed9 100644 --- a/src/test/compile-fail/issue-17718-const-bad-values.rs +++ b/src/test/compile-fail/issue-17718-const-bad-values.rs @@ -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() {} diff --git a/src/test/compile-fail/issue-17718-references.rs b/src/test/compile-fail/issue-17718-references.rs index 9d8b116f569..c159168030b 100644 --- a/src/test/compile-fail/issue-17718-references.rs +++ b/src/test/compile-fail/issue-17718-references.rs @@ -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 diff --git a/src/test/compile-fail/issue-18118-2.rs b/src/test/compile-fail/issue-18118-2.rs index 1fbf48f5b21..6efe532b5fd 100644 --- a/src/test/compile-fail/issue-18118-2.rs +++ b/src/test/compile-fail/issue-18118-2.rs @@ -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 }; } diff --git a/src/test/compile-fail/issue-18118.rs b/src/test/compile-fail/issue-18118.rs index 9c8ed314d22..3afb34f037b 100644 --- a/src/test/compile-fail/issue-18118.rs +++ b/src/test/compile-fail/issue-18118.rs @@ -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 diff --git a/src/test/compile-fail/issue-25901.rs b/src/test/compile-fail/issue-25901.rs index 3254f0b2aa9..72fb2a682eb 100644 --- a/src/test/compile-fail/issue-25901.rs +++ b/src/test/compile-fail/issue-25901.rs @@ -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; diff --git a/src/test/compile-fail/issue-27895.rs b/src/test/compile-fail/issue-27895.rs index 959818b49c9..3b3abc94a49 100644 --- a/src/test/compile-fail/issue-27895.rs +++ b/src/test/compile-fail/issue-27895.rs @@ -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"), } } diff --git a/src/test/compile-fail/issue-28113.rs b/src/test/compile-fail/issue-28113.rs index c5c4fb07017..5c697b69c80 100644 --- a/src/test/compile-fail/issue-28113.rs +++ b/src/test/compile-fail/issue-28113.rs @@ -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() {} diff --git a/src/test/compile-fail/issue-9243.rs b/src/test/compile-fail/issue-9243.rs index 7424a45d044..58bdff6c041 100644 --- a/src/test/compile-fail/issue-9243.rs +++ b/src/test/compile-fail/issue-9243.rs @@ -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) {} diff --git a/src/test/compile-fail/non-constant-in-const-path.rs b/src/test/compile-fail/non-constant-in-const-path.rs index 124a2ffc185..ee88168515d 100644 --- a/src/test/compile-fail/non-constant-in-const-path.rs +++ b/src/test/compile-fail/non-constant-in-const-path.rs @@ -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 }; } diff --git a/src/test/compile-fail/static-array-across-crate.rs b/src/test/compile-fail/static-array-across-crate.rs index 04a731e847a..d101432f6d1 100644 --- a/src/test/compile-fail/static-array-across-crate.rs +++ b/src/test/compile-fail/static-array-across-crate.rs @@ -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() {} diff --git a/src/test/compile-fail/static-mut-not-constant.rs b/src/test/compile-fail/static-mut-not-constant.rs index e3bb01e6970..9b83c42609a 100644 --- a/src/test/compile-fail/static-mut-not-constant.rs +++ b/src/test/compile-fail/static-mut-not-constant.rs @@ -12,6 +12,6 @@ static mut a: Box = 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() {} diff --git a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs index b5234af937b..600df1c778b 100644 --- a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs +++ b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs @@ -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) } } diff --git a/src/test/run-pass/const-autoderef.rs b/src/test/run-pass/const-autoderef.rs index 69173e35e2c..6b3970e4f19 100644 --- a/src/test/run-pass/const-autoderef.rs +++ b/src/test/run-pass/const-autoderef.rs @@ -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]); diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs index 1736ab5bb82..f58bf4fc39f 100644 --- a/src/test/run-pass/const-str-ptr.rs +++ b/src/test/run-pass/const-str-ptr.rs @@ -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; diff --git a/src/test/run-pass/mir_constval_adts.rs b/src/test/run-pass/mir_constval_adts.rs index 8a1f68dbea3..4e9c0bce646 100644 --- a/src/test/run-pass/mir_constval_adts.rs +++ b/src/test/run-pass/mir_constval_adts.rs @@ -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); + +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(); } diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs index c0ba7a76dba..a632f00d9ee 100644 --- a/src/test/run-pass/mir_raw_fat_ptr.rs +++ b/src/test/run-pass/mir_raw_fat_ptr.rs @@ -121,7 +121,6 @@ impl Foo for T { struct S(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]; diff --git a/src/test/run-pass/simd-intrinsic-generic-elements.rs b/src/test/run-pass/simd-intrinsic-generic-elements.rs index ffb9e6072df..5cb57b63ada 100644 --- a/src/test/run-pass/simd-intrinsic-generic-elements.rs +++ b/src/test/run-pass/simd-intrinsic-generic-elements.rs @@ -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); diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 245c3992bee..a9e6c454ffa 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -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