rustc_const_eval: demand that the MIR qualify_consts ran on each evaluated body.

This commit is contained in:
Eduard-Mihai Burtescu 2017-02-20 03:55:28 +02:00
parent e7a48821c0
commit d9f0a949fd
13 changed files with 148 additions and 112 deletions

View File

@ -361,6 +361,11 @@ define_maps! { <'tcx>
/// (in the `RefCell` sense) to prevent accidental mutation.
pub mir: Mir(DefId) -> &'tcx RefCell<mir::Mir<'tcx>>,
/// 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,

View File

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

View File

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

View File

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

View File

@ -411,8 +411,8 @@ impl<'a, 'tcx> MetadataBlob {
impl<'tcx> EntryKind<'tcx> {
fn to_def(&self, did: DefId) -> Option<Def> {
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) => {

View File

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

View File

@ -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<ImplData<'tcx>>),
Method(Lazy<MethodData>),
AssociatedType(AssociatedContainer),
AssociatedConst(AssociatedContainer),
AssociatedConst(AssociatedContainer, u8),
}
#[derive(RustcEncodable, RustcDecodable)]

View File

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

View File

@ -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<Qualif>,
temp_qualif: IndexVec<Local, Option<Qualif>>,
return_qualif: Option<Qualif>,
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<Qualif>,
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<Qualif>,
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<Qualif>
}
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<for<'s> 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.

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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() {}

View File

@ -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() {}

View File

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

View File

@ -27,9 +27,11 @@ pub struct Vector<T, D: Dim> {
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
}