From afc00b05b5b0e4ab578591ce48fd1c473ecb11e2 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 20 Feb 2017 03:55:28 +0200 Subject: [PATCH] rustc_const_eval: demand that the MIR qualify_consts ran on each evaluated body. --- src/librustc/ty/maps.rs | 5 + src/librustc_const_eval/eval.rs | 3 +- src/librustc_driver/driver.rs | 5 +- src/librustc_metadata/cstore_impl.rs | 1 + src/librustc_metadata/decoder.rs | 17 +- src/librustc_metadata/encoder.rs | 13 +- src/librustc_metadata/schema.rs | 4 +- src/librustc_mir/lib.rs | 6 + src/librustc_mir/transform/qualify_consts.rs | 147 +++++++++--------- .../const-block-non-item-statement-2.rs | 26 ++++ .../const-block-non-item-statement.rs | 21 +-- src/test/compile-fail/const-call.rs | 6 +- src/test/compile-fail/issue-39559.rs | 6 +- 13 files changed, 148 insertions(+), 112 deletions(-) create mode 100644 src/test/compile-fail/const-block-non-item-statement-2.rs diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index b79ab2a26fdc9..fd1403b15bc89 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -361,6 +361,11 @@ define_maps! { <'tcx> /// (in the `RefCell` sense) to prevent accidental mutation. pub mir: Mir(DefId) -> &'tcx RefCell>, + /// Maps DefId's that have an associated Mir to the result + /// of the MIR qualify_consts pass. The actual meaning of + /// the value isn't known except to the pass itself. + pub mir_const_qualif: Mir(DefId) -> u8, + /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. pub closure_kind: ItemSignature(DefId) -> ty::ClosureKind, diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 284991a11b1b9..c5d577ce571d4 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -27,7 +27,7 @@ use rustc::util::nodemap::DefIdMap; use graphviz::IntoCow; use syntax::ast; use rustc::hir::{self, Expr}; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; use std::borrow::Cow; use std::cmp::Ordering; @@ -228,6 +228,7 @@ pub struct ConstContext<'a, 'tcx: 'a> { impl<'a, 'tcx> ConstContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, body: hir::BodyId) -> Self { let def_id = tcx.hir.body_owner_def_id(body); + ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id); ConstContext::with_tables(tcx, tcx.item_tables(def_id)) } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 96824d19b900d..ddf09f5cfe0e0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -872,7 +872,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let index = stability::Index::new(&hir_map); let mut local_providers = ty::maps::Providers::default(); - mir::mir_map::provide(&mut local_providers); + mir::provide(&mut local_providers); typeck::provide(&mut local_providers); let mut extern_providers = ty::maps::Providers::default(); @@ -958,8 +958,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // in stage 4 below. passes.push_hook(box mir::transform::dump_mir::DumpMir); passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("initial")); - passes.push_pass( - box mir::transform::qualify_consts::QualifyAndPromoteConstants::default()); + 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_branches::SimplifyBranches::new("initial")); diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 443e75e63d3dd..7b02280ef904b 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -101,6 +101,7 @@ provide! { <'tcx> tcx, def_id, cdata mir } + mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } typeck_tables => { cdata.item_body_tables(def_id.index, tcx) } closure_kind => { cdata.closure_kind(def_id.index) } closure_type => { cdata.closure_ty(def_id.index, tcx) } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 1627b22cd5fa1..b4b9966cbe47b 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -411,8 +411,8 @@ impl<'a, 'tcx> MetadataBlob { impl<'tcx> EntryKind<'tcx> { fn to_def(&self, did: DefId) -> Option { Some(match *self { - EntryKind::Const => Def::Const(did), - EntryKind::AssociatedConst(_) => Def::AssociatedConst(did), + EntryKind::Const(_) => Def::Const(did), + EntryKind::AssociatedConst(..) => Def::AssociatedConst(did), EntryKind::ImmStatic | EntryKind::ForeignImmStatic => Def::Static(did, false), EntryKind::MutStatic | @@ -825,6 +825,17 @@ impl<'a, 'tcx> CrateMetadata { } } + pub fn mir_const_qualif(&self, id: DefIndex) -> u8 { + match self.entry(id).kind { + EntryKind::Const(qualif) | + EntryKind::AssociatedConst(AssociatedContainer::ImplDefault, qualif) | + EntryKind::AssociatedConst(AssociatedContainer::ImplFinal, qualif) => { + qualif + } + _ => bug!(), + } + } + pub fn get_associated_item(&self, id: DefIndex) -> ty::AssociatedItem { let item = self.entry(id); let def_key = self.def_key(id); @@ -832,7 +843,7 @@ impl<'a, 'tcx> CrateMetadata { let name = def_key.disambiguated_data.data.get_opt_name().unwrap(); let (kind, container, has_self) = match item.kind { - EntryKind::AssociatedConst(container) => { + EntryKind::AssociatedConst(container, _) => { (ty::AssociatedKind::Const, container, false) } EntryKind::Method(data) => { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index e73700f04fa4e..af0edab7a83bd 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -457,7 +457,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; let kind = match trait_item.kind { - ty::AssociatedKind::Const => EntryKind::AssociatedConst(container), + ty::AssociatedKind::Const => { + EntryKind::AssociatedConst(container, 0) + } ty::AssociatedKind::Method => { let fn_data = if let hir::TraitItemKind::Method(_, ref m) = ast_item.node { let arg_names = match *m { @@ -533,7 +535,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; let kind = match impl_item.kind { - ty::AssociatedKind::Const => EntryKind::AssociatedConst(container), + ty::AssociatedKind::Const => { + EntryKind::AssociatedConst(container, + ty::queries::mir_const_qualif::get(self.tcx, ast_item.span, def_id)) + } ty::AssociatedKind::Method => { let fn_data = if let hir::ImplItemKind::Method(ref sig, body) = ast_item.node { FnData { @@ -637,7 +642,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let kind = match item.node { hir::ItemStatic(_, hir::MutMutable, _) => EntryKind::MutStatic, hir::ItemStatic(_, hir::MutImmutable, _) => EntryKind::ImmStatic, - hir::ItemConst(..) => EntryKind::Const, + hir::ItemConst(..) => { + EntryKind::Const(ty::queries::mir_const_qualif::get(tcx, item.span, def_id)) + } hir::ItemFn(_, _, constness, .., body) => { let data = FnData { constness: constness, diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 26d907523a886..4a20913d0b3fd 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -221,7 +221,7 @@ pub struct Entry<'tcx> { #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] pub enum EntryKind<'tcx> { - Const, + Const(u8), ImmStatic, MutStatic, ForeignImmStatic, @@ -243,7 +243,7 @@ pub enum EntryKind<'tcx> { DefaultImpl(Lazy>), Method(Lazy), AssociatedType(AssociatedContainer), - AssociatedConst(AssociatedContainer), + AssociatedConst(AssociatedContainer, u8), } #[derive(RustcEncodable, RustcDecodable)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9a8fb1099d04b..a97495a0ebcc4 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -53,3 +53,9 @@ pub mod mir_map; pub mod pretty; pub mod transform; +use rustc::ty::maps::Providers; + +pub fn provide(providers: &mut Providers) { + mir_map::provide(providers); + transform::qualify_consts::provide(providers); +} diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 9f38564d1e2f3..441a9add883dd 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -16,24 +16,24 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; use rustc::hir::map::blocks::FnLikeNode; use rustc::traits::{self, Reveal}; -use rustc::ty::{self, TyCtxt, Ty}; +use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; use rustc::ty::cast::CastTy; +use rustc::ty::maps::Providers; use rustc::mir::*; use rustc::mir::traversal::ReversePostorder; -use rustc::mir::transform::{Pass, MirPass, MirSource}; +use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource}; use rustc::mir::visit::{LvalueContext, Visitor}; -use rustc::util::nodemap::DefIdMap; use rustc::middle::lang_items; use syntax::abi::Abi; use syntax::feature_gate::UnstableFeatures; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; -use std::collections::hash_map::Entry; use std::fmt; use std::usize; @@ -41,36 +41,32 @@ 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, + const MUTABLE_INTERIOR = 1 << 0, // Constant containing an ADT that implements Drop. - const NEEDS_DROP = 1 << 2, + const NEEDS_DROP = 1 << 1, // Function argument. - const FN_ARGUMENT = 1 << 3, + const FN_ARGUMENT = 1 << 2, // Static lvalue or move from a static. - const STATIC = 1 << 4, + const STATIC = 1 << 3, // Reference to a static. - const STATIC_REF = 1 << 5, + const STATIC_REF = 1 << 4, // Not constant at all - non-`const fn` calls, asm!, // pointer comparisons, ptr-to-int casts, etc. - const NOT_CONST = 1 << 6, + const NOT_CONST = 1 << 5, // Refers to temporaries which cannot be promoted as // promote_consts decided they weren't simple enough. - const NOT_PROMOTABLE = 1 << 7, + const NOT_PROMOTABLE = 1 << 6, // Borrows of temporaries can be promoted only // if they have none of the above qualifications. - const NEVER_PROMOTE = !0, + const NEVER_PROMOTE = 0b111_1111, // Const items can only have MUTABLE_INTERIOR // and NOT_PROMOTABLE without producing an error. @@ -134,7 +130,6 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { rpo: ReversePostorder<'a, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParameterEnvironment<'tcx>, - qualif_map: &'a mut DefIdMap, temp_qualif: IndexVec>, return_qualif: Option, qualif: Qualif, @@ -146,7 +141,6 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParameterEnvironment<'tcx>, - qualif_map: &'a mut DefIdMap, def_id: DefId, mir: &'a Mir<'tcx>, mode: Mode) @@ -162,7 +156,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { rpo: rpo, tcx: tcx, param_env: param_env, - qualif_map: qualif_map, temp_qualif: IndexVec::from_elem(None, &mir.local_decls), return_qualif: None, qualif: Qualif::empty(), @@ -585,17 +578,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if substs.types().next().is_some() { self.add_type(constant.ty); } else { - let qualif = qualify_const_item_cached(self.tcx, - self.qualif_map, - def_id); - self.add(qualif); - } + let bits = ty::queries::mir_const_qualif::get(self.tcx, + constant.span, + def_id); - // 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 qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif"); + self.add(qualif); } // Let `const fn` transitively have destructors, @@ -944,41 +932,64 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } -fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - qualif_map: &mut DefIdMap, - 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); - } +pub fn provide(providers: &mut Providers) { + providers.mir_const_qualif = qualify_const_item; +} + +fn qualify_const_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> u8 { + let mir = &tcx.item_mir(def_id); + if mir.return_ty.references_error() { + return Qualif::NOT_CONST.bits(); } - let param_env = if def_id.is_local() { - let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - ty::ParameterEnvironment::for_item(tcx, node_id) - } else { - // These should only be monomorphic constants. - tcx.empty_parameter_environment() - }; + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); + let param_env = ty::ParameterEnvironment::for_item(tcx, node_id); - let mir = &tcx.item_mir(def_id); - let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, def_id, mir, Mode::Const); - let qualif = qualifier.qualify_const(); - qualifier.qualif_map.insert(def_id, qualif); - qualif + let mut qualifier = Qualifier::new(tcx, param_env, def_id, mir, Mode::Const); + qualifier.qualify_const().bits() } -#[derive(Default)] -pub struct QualifyAndPromoteConstants { - qualif_map: DefIdMap -} +pub struct QualifyAndPromoteConstants; impl Pass for QualifyAndPromoteConstants {} -impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { +impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants { + fn run_pass<'a>(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + hooks: &mut [Box MirPassHook<'s>>]) + { + let def_ids = tcx.maps.mir.borrow().keys(); + for def_id in def_ids { + if !def_id.is_local() { + continue; + } + + let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id)); + let id = tcx.hir.as_local_node_id(def_id).unwrap(); + let src = MirSource::from_node(tcx, id); + + if let MirSource::Const(_) = src { + ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id); + continue; + } + + let mir = &mut tcx.maps.mir.borrow()[&def_id].borrow_mut(); + tcx.dep_graph.write(DepNode::Mir(def_id)); + + for hook in &mut *hooks { + hook.on_mir_pass(tcx, src, mir, self, false); + } + self.run_pass(tcx, src, mir); + for hook in &mut *hooks { + hook.on_mir_pass(tcx, src, mir, self, true); + } + } + } +} + +impl<'tcx> QualifyAndPromoteConstants { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { let id = src.item_id(); @@ -991,18 +1002,9 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { Mode::Fn } } - MirSource::Const(_) => { - match self.qualif_map.entry(def_id) { - Entry::Occupied(_) => return, - Entry::Vacant(entry) => { - // Guard against `const` recursion. - entry.insert(Qualif::RECURSIVE); - Mode::Const - } - } - } MirSource::Static(_, hir::MutImmutable) => Mode::Static, MirSource::Static(_, hir::MutMutable) => Mode::StaticMut, + MirSource::Const(_) | MirSource::Promoted(..) => return }; let param_env = ty::ParameterEnvironment::for_item(tcx, id); @@ -1012,7 +1014,6 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { // which can't be mutated until its scope ends. let (temps, candidates) = { let mut qualifier = Qualifier::new(tcx, param_env, - &mut self.qualif_map, def_id, mir, mode); if mode == Mode::ConstFn { // Enforce a constant-like CFG for `const fn`. @@ -1029,14 +1030,8 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { // 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(tcx, param_env, - &mut self.qualif_map, - def_id, mir, mode); - let qualif = qualifier.qualify_const(); - - if mode == Mode::Const { - qualifier.qualif_map.insert(def_id, qualif); - } + let mut qualifier = Qualifier::new(tcx, param_env, def_id, mir, mode); + qualifier.qualify_const(); } // Statics must be Sync. diff --git a/src/test/compile-fail/const-block-non-item-statement-2.rs b/src/test/compile-fail/const-block-non-item-statement-2.rs new file mode 100644 index 0000000000000..83166c9bd4b5e --- /dev/null +++ b/src/test/compile-fail/const-block-non-item-statement-2.rs @@ -0,0 +1,26 @@ +// Copyright 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. + +const A: usize = { 1; 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +const B: usize = { { } 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +macro_rules! foo { + () => (()) //~ ERROR: blocks in constants are limited to items and tail expressions +} +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 + +pub fn main() {} 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 edb85023c9ba7..bdc69c937c637 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -8,21 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -const A: usize = { 1; 2 }; -//~^ ERROR: blocks in constants are limited to items and tail expressions - -const B: usize = { { } 2 }; -//~^ ERROR: blocks in constants are limited to items and tail expressions - -macro_rules! foo { - () => (()) //~ ERROR: blocks in constants are limited to items and tail expressions -} -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 @@ -33,8 +18,4 @@ 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 -} +pub fn main() {} diff --git a/src/test/compile-fail/const-call.rs b/src/test/compile-fail/const-call.rs index 7e2eabf412d6c..ff83dd004a257 100644 --- a/src/test/compile-fail/const-call.rs +++ b/src/test/compile-fail/const-call.rs @@ -15,6 +15,8 @@ fn f(x: usize) -> usize { } fn main() { - let _ = [0; f(2)]; //~ ERROR constant evaluation error [E0080] - //~| non-constant path in constant expression + let _ = [0; f(2)]; + //~^ ERROR calls in constants are limited to constant functions + //~| ERROR constant evaluation error [E0080] + //~| non-constant path in constant expression } diff --git a/src/test/compile-fail/issue-39559.rs b/src/test/compile-fail/issue-39559.rs index bc492806b96c1..06e8406cbc0bd 100644 --- a/src/test/compile-fail/issue-39559.rs +++ b/src/test/compile-fail/issue-39559.rs @@ -27,9 +27,11 @@ pub struct Vector { fn main() { let array: [usize; Dim3::dim()] - //~^ ERROR constant evaluation error + //~^ ERROR calls in constants are limited to constant functions + //~| ERROR constant evaluation error //~| non-constant path in constant expression = [0; Dim3::dim()]; - //~^ ERROR constant evaluation error + //~^ ERROR calls in constants are limited to constant functions + //~| ERROR constant evaluation error //~| non-constant path in constant expression }