1
Fork 0

Auto merge of #41408 - eddyb:poly-const-eval, r=arielb1

rustc: generalize monomorphic_const_eval to polymorphic constants.

With the addition of `Substs` to the query key, we can now evaluate *and cache* polymorphic constants.

Fixes #23898 by replacing the crippled explicit-discriminant-only local-crate-only `lookup_variant_by_id` with `ConstVal::Variant` which can describe variants irrespective of their discriminant.

Fixes #41394 by fixing #23898 (for the original testcase) and by not looping past the first discriminant.
This commit is contained in:
bors 2017-04-23 08:35:00 +00:00
commit 23de823e93
23 changed files with 331 additions and 219 deletions

View file

@ -97,7 +97,7 @@ pub enum DepNode<D: Clone + Debug> {
TypeckBodiesKrate,
TypeckTables(D),
UsedTraitImports(D),
MonomorphicConstEval(D),
ConstEval(D),
// The set of impls for a given trait. Ultimately, it would be
// nice to get more fine-grained here (e.g., to include a
@ -233,7 +233,7 @@ impl<D: Clone + Debug> DepNode<D> {
InherentImpls(ref d) => op(d).map(InherentImpls),
TypeckTables(ref d) => op(d).map(TypeckTables),
UsedTraitImports(ref d) => op(d).map(UsedTraitImports),
MonomorphicConstEval(ref d) => op(d).map(MonomorphicConstEval),
ConstEval(ref d) => op(d).map(ConstEval),
TraitImpls(ref d) => op(d).map(TraitImpls),
TraitItems(ref d) => op(d).map(TraitItems),
ReprHints(ref d) => op(d).map(ReprHints),

View file

@ -273,6 +273,12 @@ for ::middle::const_val::ConstVal<'tcx> {
ConstVal::Bool(value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Char(value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Variant(def_id) => {
def_id.hash_stable(hcx, hasher);
}
ConstVal::Function(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
@ -296,9 +302,6 @@ for ::middle::const_val::ConstVal<'tcx> {
value.hash_stable(hcx, hasher);
times.hash_stable(hcx, hasher);
}
ConstVal::Char(value) => {
value.hash_stable(hcx, hasher);
}
}
}
}

View file

@ -38,12 +38,13 @@ pub enum ConstVal<'tcx> {
Str(InternedString),
ByteStr(Rc<Vec<u8>>),
Bool(bool),
Char(char),
Variant(DefId),
Function(DefId, &'tcx Substs<'tcx>),
Struct(BTreeMap<ast::Name, ConstVal<'tcx>>),
Tuple(Vec<ConstVal<'tcx>>),
Array(Vec<ConstVal<'tcx>>),
Repeat(Box<ConstVal<'tcx>>, u64),
Char(char),
}
impl<'tcx> ConstVal<'tcx> {
@ -54,12 +55,13 @@ impl<'tcx> ConstVal<'tcx> {
Str(_) => "string literal",
ByteStr(_) => "byte string literal",
Bool(_) => "boolean",
Char(..) => "char",
Variant(_) => "enum variant",
Struct(_) => "struct",
Tuple(_) => "tuple",
Function(..) => "function definition",
Array(..) => "array",
Repeat(..) => "repeat",
Char(..) => "char",
}
}
@ -85,7 +87,6 @@ pub enum ErrKind<'tcx> {
MissingStructField,
NegateOn(ConstVal<'tcx>),
NotOn(ConstVal<'tcx>),
CallOn(ConstVal<'tcx>),
NonConstPath,
UnimplementedConstVal(&'static str),
@ -145,7 +146,6 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
CannotCast => simple!("can't cast this type"),
NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
NotOn(ref const_val) => simple!("not on {}", const_val.description()),
CallOn(ref const_val) => simple!("call on {}", const_val.description()),
MissingStructField => simple!("nonexistent struct field"),
NonConstPath => simple!("non-constant path in constant expression"),
@ -227,7 +227,8 @@ pub fn eval_length(tcx: TyCtxt,
{
let count_expr = &tcx.hir.body(count).value;
let count_def_id = tcx.hir.body_owner_def_id(count);
match ty::queries::monomorphic_const_eval::get(tcx, count_expr.span, count_def_id) {
let substs = Substs::empty();
match ty::queries::const_eval::get(tcx, count_expr.span, (count_def_id, substs)) {
Ok(Integral(Usize(count))) => {
let val = count.as_u64(tcx.sess.target.uint_type);
assert_eq!(val as usize as u64, val);

View file

@ -249,8 +249,8 @@ pub trait CrateStore {
fn load_macro(&self, did: DefId, sess: &Session) -> LoadedMacro;
// misc. metadata
fn maybe_get_item_body<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
-> Option<&'tcx hir::Body>;
fn item_body<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
-> &'tcx hir::Body;
fn item_body_nested_bodies(&self, def: DefId) -> BTreeMap<hir::BodyId, hir::Body>;
fn const_is_rvalue_promotable_to_static(&self, def: DefId) -> bool;
@ -399,9 +399,9 @@ impl CrateStore for DummyCrateStore {
fn load_macro(&self, did: DefId, sess: &Session) -> LoadedMacro { bug!("load_macro") }
// misc. metadata
fn maybe_get_item_body<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
-> Option<&'tcx hir::Body> {
bug!("maybe_get_item_body")
fn item_body<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
-> &'tcx hir::Body {
bug!("item_body")
}
fn item_body_nested_bodies(&self, def: DefId) -> BTreeMap<hir::BodyId, hir::Body> {
bug!("item_body_nested_bodies")

View file

@ -1307,10 +1307,11 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
write!(fmt, "b\"{}\"", escaped)
}
Bool(b) => write!(fmt, "{:?}", b),
Char(c) => write!(fmt, "{:?}", c),
Variant(def_id) |
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
Struct(_) | Tuple(_) | Array(_) | Repeat(..) =>
bug!("ConstVal `{:?}` should not be in MIR", const_val),
Char(c) => write!(fmt, "{:?}", c),
}
}

View file

@ -16,6 +16,7 @@ use middle::privacy::AccessLevels;
use mir;
use session::CompileResult;
use ty::{self, CrateInherentImpls, Ty, TyCtxt};
use ty::subst::Substs;
use util::nodemap::NodeSet;
use rustc_data_structures::indexed_vec::IndexVec;
@ -74,6 +75,15 @@ impl Key for (CrateNum, DefId) {
}
}
impl<'tcx> Key for (DefId, &'tcx Substs<'tcx>) {
fn map_crate(&self) -> CrateNum {
self.0.krate
}
fn default_span(&self, tcx: TyCtxt) -> Span {
self.0.default_span(tcx)
}
}
trait Value<'tcx>: Sized {
fn from_cycle_error<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self;
}
@ -217,6 +227,13 @@ impl<'tcx> QueryDescription for queries::reachable_set<'tcx> {
}
}
impl<'tcx> QueryDescription for queries::const_eval<'tcx> {
fn describe(tcx: TyCtxt, (def_id, _): (DefId, &'tcx Substs<'tcx>)) -> String {
format!("const-evaluating `{}`",
tcx.item_path_str(def_id))
}
}
macro_rules! define_maps {
(<$tcx:tt>
$($(#[$attr:meta])*
@ -446,16 +463,17 @@ define_maps! { <'tcx>
/// (Defined only for LOCAL_CRATE)
pub crate_inherent_impls_overlap_check: crate_inherent_impls_dep_node(CrateNum) -> (),
/// Results of evaluating monomorphic constants embedded in
/// other items, such as enum variant explicit discriminants.
pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> const_val::EvalResult<'tcx>,
/// Results of evaluating const items or constants embedded in
/// other items (such as enum variant explicit discriminants).
pub const_eval: const_eval_dep_node((DefId, &'tcx Substs<'tcx>))
-> const_val::EvalResult<'tcx>,
/// Performs the privacy check and computes "access levels".
pub privacy_access_levels: PrivacyAccessLevels(CrateNum) -> Rc<AccessLevels>,
pub reachable_set: reachability_dep_node(CrateNum) -> Rc<NodeSet>,
pub mir_shims: mir_shim(ty::InstanceDef<'tcx>) -> &'tcx RefCell<mir::Mir<'tcx>>
pub mir_shims: mir_shim_dep_node(ty::InstanceDef<'tcx>) -> &'tcx RefCell<mir::Mir<'tcx>>
}
fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode<DefId> {
@ -470,10 +488,14 @@ fn reachability_dep_node(_: CrateNum) -> DepNode<DefId> {
DepNode::Reachability
}
fn mir_shim(instance: ty::InstanceDef) -> DepNode<DefId> {
fn mir_shim_dep_node(instance: ty::InstanceDef) -> DepNode<DefId> {
instance.dep_node()
}
fn typeck_item_bodies_dep_node(_: CrateNum) -> DepNode<DefId> {
DepNode::TypeckBodiesKrate
}
fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode<DefId> {
DepNode::ConstEval(def_id)
}

View file

@ -1693,6 +1693,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
}
}
#[inline]
pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
-> impl Iterator<Item=ConstInt> + 'a {
let repr_type = self.repr.discr_type();
@ -1701,11 +1702,18 @@ impl<'a, 'gcx, 'tcx> AdtDef {
self.variants.iter().map(move |v| {
let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr());
if let VariantDiscr::Explicit(expr_did) = v.discr {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
let substs = Substs::empty();
match queries::const_eval::get(tcx, DUMMY_SP, (expr_did, substs)) {
Ok(ConstVal::Integral(v)) => {
discr = v;
}
_ => {}
err => {
if !expr_did.is_local() {
span_bug!(tcx.def_span(expr_did),
"variant discriminant evaluation succeeded \
in its crate but failed locally: {:?}", err);
}
}
}
}
prev_discr = Some(discr);
@ -1733,12 +1741,21 @@ impl<'a, 'gcx, 'tcx> AdtDef {
explicit_index -= distance;
}
ty::VariantDiscr::Explicit(expr_did) => {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
let substs = Substs::empty();
match queries::const_eval::get(tcx, DUMMY_SP, (expr_did, substs)) {
Ok(ConstVal::Integral(v)) => {
explicit_value = v;
break;
}
_ => {
err => {
if !expr_did.is_local() {
span_bug!(tcx.def_span(expr_did),
"variant discriminant evaluation succeeded \
in its crate but failed locally: {:?}", err);
}
if explicit_index == 0 {
break;
}
explicit_index -= 1;
}
}

View file

@ -15,7 +15,7 @@ use rustc::middle::const_val::{ConstVal, ConstEvalErr, EvalResult, ErrKind};
use rustc::hir::map as hir_map;
use rustc::hir::map::blocks::FnLikeNode;
use rustc::traits;
use rustc::hir::def::Def;
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::maps::Providers;
@ -48,110 +48,39 @@ macro_rules! math {
}
}
fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
variant_def: DefId)
-> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> {
if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) {
let enum_node_id = tcx.hir.get_parent(variant_node_id);
if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) {
if let hir::ItemEnum(ref edef, _) = it.node {
for variant in &edef.variants {
if variant.node.data.id() == variant_node_id {
return variant.node.disr_expr.map(|e| {
let def_id = tcx.hir.body_owner_def_id(e);
(&tcx.hir.body(e).value,
tcx.item_tables(def_id))
});
}
}
}
}
}
None
}
/// * `def_id` is the id of the constant.
/// * `substs` is the monomorphized substitutions for the expression.
///
/// `substs` is optional and is used for associated constants.
/// This generally happens in late/trans const evaluation.
pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
-> Option<(&'tcx Expr,
&'a ty::TypeckTables<'tcx>)> {
pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
match tcx.hir.find(node_id) {
None => None,
Some(hir_map::NodeItem(&hir::Item {
node: hir::ItemConst(_, body), ..
})) |
Some(hir_map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Const(_, body), ..
})) => {
Some((&tcx.hir.body(body).value,
tcx.item_tables(def_id)))
Some(hir_map::NodeTraitItem(_)) => {
// If we have a trait item and the substitutions for it,
// `resolve_trait_associated_const` will select an impl
// or the default.
resolve_trait_associated_const(tcx, def_id, substs)
}
Some(hir_map::NodeTraitItem(ti)) => match ti.node {
hir::TraitItemKind::Const(_, default) => {
// If we have a trait item and the substitutions for it,
// `resolve_trait_associated_const` will select an impl
// or the default.
let trait_id = tcx.hir.get_parent(node_id);
let trait_id = tcx.hir.local_def_id(trait_id);
let default_value = default.map(|body| {
(&tcx.hir.body(body).value,
tcx.item_tables(def_id))
});
resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs)
}
_ => None
},
Some(_) => None
_ => Some((def_id, substs))
}
} else {
let expr_and_tables = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
(&body.value, tcx.item_tables(def_id))
});
match tcx.sess.cstore.describe_def(def_id) {
Some(Def::AssociatedConst(_)) => {
let trait_id = tcx.sess.cstore.trait_of_item(def_id);
// As mentioned in the comments above for in-crate
// constants, we only try to find the expression for a
// trait-associated const if the caller gives us the
// substitutions for the reference to it.
if let Some(trait_id) = trait_id {
resolve_trait_associated_const(tcx, def_id, expr_and_tables,
trait_id, substs)
if tcx.sess.cstore.trait_of_item(def_id).is_some() {
resolve_trait_associated_const(tcx, def_id, substs)
} else {
expr_and_tables
Some((def_id, substs))
}
},
Some(Def::Const(..)) => expr_and_tables,
_ => None
}
}
}
fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
-> Option<(&'tcx hir::Body, &'a ty::TypeckTables<'tcx>)>
{
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
FnLikeNode::from_node(tcx.hir.get(node_id)).and_then(|fn_like| {
if fn_like.constness() == hir::Constness::Const {
Some((tcx.hir.body(fn_like.body()),
tcx.item_tables(def_id)))
} else {
None
}
})
} else {
if tcx.sess.cstore.is_const_fn(def_id) {
tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
(body, tcx.item_tables(def_id))
})
} else {
None
_ => Some((def_id, substs))
}
}
}
@ -338,9 +267,22 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
}
}
hir::ExprCast(ref base, _) => {
match cast_const(tcx, cx.eval(base)?, ety) {
Ok(val) => val,
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
let base_val = cx.eval(base)?;
let base_ty = cx.tables.expr_ty(base);
// Avoid applying substitutions if they're empty, that'd ICE.
let base_ty = if cx.substs.is_empty() {
base_ty
} else {
base_ty.subst(tcx, cx.substs)
};
if ety == base_ty {
base_val
} else {
match cast_const(tcx, base_val, ety) {
Ok(val) => val,
Err(kind) => signal!(e, kind),
}
}
}
hir::ExprPath(ref qpath) => {
@ -357,42 +299,29 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
match cx.tables.qpath_def(qpath, e.id) {
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
if let Some((expr, tables)) = lookup_const_by_id(tcx, def_id, substs) {
let cx = ConstContext::with_tables(tcx, tables);
match cx.eval(expr) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
signal!(e, TypeckError);
}
Err(err) => {
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
signal!(e, ErroneousReferencedConstant(box err))
},
}
} else {
signal!(e, TypeckError);
}
match ty::queries::const_eval::get(tcx, e.span, (def_id, substs)) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
signal!(e, TypeckError);
}
Err(err) => {
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
signal!(e, ErroneousReferencedConstant(box err))
},
}
},
Def::VariantCtor(variant_def, ..) => {
if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) {
let cx = ConstContext::with_tables(tcx, tables);
match cx.eval(expr) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
signal!(e, TypeckError);
}
Err(err) => {
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
signal!(e, ErroneousReferencedConstant(box err))
},
}
} else {
signal!(e, UnimplementedConstVal("enum variants"));
}
Def::VariantCtor(variant_def, CtorKind::Const) => {
Variant(variant_def)
}
Def::StructCtor(..) => {
Def::VariantCtor(_, CtorKind::Fn) => {
signal!(e, UnimplementedConstVal("enum variants"));
}
Def::StructCtor(_, CtorKind::Const) => {
ConstVal::Struct(Default::default())
}
Def::StructCtor(_, CtorKind::Fn) => {
signal!(e, UnimplementedConstVal("tuple struct constructors"))
}
Def::Local(def_id) => {
debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args);
if let Some(val) = cx.fn_args.as_ref().and_then(|args| args.get(&def_id)) {
@ -407,14 +336,27 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
}
}
hir::ExprCall(ref callee, ref args) => {
let (did, substs) = match cx.eval(callee)? {
Function(did, substs) => (did, substs),
Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")),
callee => signal!(e, CallOn(callee)),
let (def_id, substs) = match cx.eval(callee)? {
Function(def_id, substs) => (def_id, substs),
_ => signal!(e, TypeckError),
};
let (body, tables) = match lookup_const_fn_by_id(tcx, did) {
Some(x) => x,
None => signal!(e, NonConstPath),
let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
if fn_like.constness() == hir::Constness::Const {
tcx.hir.body(fn_like.body())
} else {
signal!(e, TypeckError)
}
} else {
signal!(e, TypeckError)
}
} else {
if tcx.sess.cstore.is_const_fn(def_id) {
tcx.sess.cstore.item_body(tcx, def_id)
} else {
signal!(e, TypeckError)
}
};
let arg_defs = body.arguments.iter().map(|arg| match arg.pat.node {
@ -434,7 +376,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
debug!("const call({:?})", call_args);
let callee_cx = ConstContext {
tcx: tcx,
tables: tables,
tables: tcx.item_tables(def_id),
substs: substs,
fn_args: Some(call_args)
};
@ -532,19 +474,16 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
Ok(result)
}
fn resolve_trait_associated_const<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_item_id: DefId,
default_value: Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>,
trait_id: DefId,
rcvr_substs: &'tcx Substs<'tcx>
) -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>
{
let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, rcvr_substs));
fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
let trait_item = tcx.associated_item(def_id);
let trait_id = trait_item.container.id();
let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
debug!("resolve_trait_associated_const: trait_ref={:?}",
trait_ref);
tcx.populate_implementations_for_trait_if_necessary(trait_id);
tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
let mut selcx = traits::SelectionContext::new(&infcx);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
@ -569,12 +508,20 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(
// when constructing the inference context above.
match selection {
traits::VtableImpl(ref impl_data) => {
let name = tcx.associated_item(trait_item_id).name;
let name = trait_item.name;
let ac = tcx.associated_items(impl_data.impl_def_id)
.find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
match ac {
Some(ic) => lookup_const_by_id(tcx, ic.def_id, Substs::empty()),
None => default_value,
// FIXME(eddyb) Use proper Instance resolution to
// get the correct Substs returned from here.
Some(ic) => Some((ic.def_id, Substs::empty())),
None => {
if trait_item.defaultness.has_value() {
Some((def_id, substs))
} else {
None
}
}
}
}
_ => {
@ -615,7 +562,7 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
U8(u) => Ok(Char(u as char)),
_ => bug!(),
},
_ => bug!(),
_ => Err(CannotCast),
}
}
@ -659,6 +606,11 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
Float(f) => cast_const_float(tcx, f, ty),
Char(c) => cast_const_int(tcx, U32(c as u32), ty),
Variant(v) => {
let adt = tcx.lookup_adt_def(tcx.parent_def_id(v).unwrap());
let idx = adt.variant_index_with_id(v);
cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
}
Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
ByteStr(b) => match ty.sty {
ty::TyRawPtr(_) => {
@ -796,21 +748,35 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> {
pub fn provide(providers: &mut Providers) {
*providers = Providers {
monomorphic_const_eval,
const_eval,
..*providers
};
}
fn monomorphic_const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> EvalResult<'tcx> {
let cx = ConstContext::with_tables(tcx, tcx.item_tables(def_id));
fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
(def_id, substs): (DefId, &'tcx Substs<'tcx>))
-> EvalResult<'tcx> {
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, def_id, substs) {
resolved
} else {
return Err(ConstEvalErr {
span: tcx.def_span(def_id),
kind: TypeckError
});
};
let cx = ConstContext {
tcx,
tables: tcx.item_tables(def_id),
substs: substs,
fn_args: None
};
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id);
tcx.hir.body(tcx.hir.body_owned_by(id))
} else {
tcx.sess.cstore.maybe_get_item_body(tcx, def_id).unwrap()
tcx.sess.cstore.item_body(tcx, def_id)
};
cx.eval(&body.value)
}

View file

@ -116,6 +116,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
ConstVal::Bool(b) => write!(f, "{:?}", b),
ConstVal::Char(c) => write!(f, "{:?}", c),
ConstVal::Variant(_) |
ConstVal::Struct(_) |
ConstVal::Tuple(_) |
ConstVal::Function(..) |
@ -587,11 +588,16 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
let substs = self.tables.node_id_item_substs(id)
.unwrap_or_else(|| tcx.intern_substs(&[]));
match eval::lookup_const_by_id(tcx, def_id, substs) {
Some((const_expr, const_tables)) => {
Some((def_id, _substs)) => {
// Enter the inlined constant's tables temporarily.
let old_tables = self.tables;
self.tables = const_tables;
let pat = self.lower_const_expr(const_expr, pat_id, span);
self.tables = tcx.item_tables(def_id);
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
tcx.hir.body(tcx.hir.body_owned_by(id))
} else {
tcx.sess.cstore.item_body(tcx, def_id)
};
let pat = self.lower_const_expr(&body.value, pat_id, span);
self.tables = old_tables;
return pat;
}
@ -615,7 +621,12 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
let const_cx = eval::ConstContext::with_tables(self.tcx.global_tcx(), self.tables);
match const_cx.eval(expr) {
Ok(value) => {
PatternKind::Constant { value: value }
if let ConstVal::Variant(def_id) = value {
let ty = self.tables.expr_ty(expr);
self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![])
} else {
PatternKind::Constant { value: value }
}
}
Err(e) => {
self.errors.push(PatternError::ConstEval(e));

View file

@ -420,19 +420,18 @@ impl CrateStore for cstore::CStore {
})
}
fn maybe_get_item_body<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> Option<&'tcx hir::Body>
{
fn item_body<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> &'tcx hir::Body {
if let Some(cached) = tcx.hir.get_inlined_body(def_id) {
return Some(cached);
return cached;
}
self.dep_graph.read(DepNode::MetaData(def_id));
debug!("maybe_get_item_body({}): inlining item", tcx.item_path_str(def_id));
debug!("item_body({}): inlining item", tcx.item_path_str(def_id));
self.get_crate_data(def_id.krate).maybe_get_item_body(tcx, def_id.index)
self.get_crate_data(def_id.krate).item_body(tcx, def_id.index)
}
fn item_body_nested_bodies(&self, def: DefId) -> BTreeMap<hir::BodyId, hir::Body> {

View file

@ -750,16 +750,15 @@ impl<'a, 'tcx> CrateMetadata {
}
}
pub fn maybe_get_item_body(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: DefIndex)
-> Option<&'tcx hir::Body> {
if self.is_proc_macro(id) { return None; }
self.entry(id).ast.map(|ast| {
let def_id = self.local_def_id(id);
let body = ast.decode(self).body.decode(self);
tcx.hir.intern_inlined_body(def_id, body)
})
pub fn item_body(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: DefIndex)
-> &'tcx hir::Body {
assert!(!self.is_proc_macro(id));
let ast = self.entry(id).ast.unwrap();
let def_id = self.local_def_id(id);
let body = ast.decode(self).body.decode(self);
tcx.hir.intern_inlined_body(def_id, body)
}
pub fn item_body_tables(&self,

View file

@ -593,7 +593,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
hir::ExprRepeat(ref v, count) => {
let c = &cx.tcx.hir.body(count).value;
let def_id = cx.tcx.hir.body_owner_def_id(count);
let count = match ty::queries::monomorphic_const_eval::get(cx.tcx, c.span, def_id) {
let substs = Substs::empty();
let count = match ty::queries::const_eval::get(cx.tcx, c.span, (def_id, substs)) {
Ok(ConstVal::Integral(ConstInt::Usize(u))) => u,
Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other),
Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression")

View file

@ -100,15 +100,13 @@ impl<'tcx> Const<'tcx> {
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
ConstVal::Function(..) => C_null(type_of::type_of(ccx, ty)),
ConstVal::Variant(_) |
ConstVal::Struct(_) | ConstVal::Tuple(_) |
ConstVal::Array(..) | ConstVal::Repeat(..) => {
bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
}
ConstVal::Function(..) => {
let llty = type_of::type_of(ccx, ty);
return Const::new(C_null(llty), ty);
}
ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
};
assert!(!ty.has_erasable_regions());

View file

@ -555,7 +555,8 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr());
prev_discr = Some(if let Some(e) = variant.node.disr_expr {
let expr_did = tcx.hir.local_def_id(e.node_id);
let result = ty::queries::monomorphic_const_eval::get(tcx, variant.span, expr_did);
let substs = Substs::empty();
let result = ty::queries::const_eval::get(tcx, variant.span, (expr_did, substs));
// enum variant evaluation happens before the global constant check
// so we need to report the real error

View file

@ -487,7 +487,7 @@ impl hir::print::PpAnn for InlinedConst {
}
fn print_inlined_const(cx: &DocContext, did: DefId) -> String {
let body = cx.tcx.sess.cstore.maybe_get_item_body(cx.tcx, did).unwrap();
let body = cx.tcx.sess.cstore.item_body(cx.tcx, did);
let inlined = InlinedConst {
nested_bodies: cx.tcx.sess.cstore.item_body_nested_bodies(did)
};

View file

@ -17,6 +17,4 @@ fn f(x: usize) -> usize {
fn main() {
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

@ -10,21 +10,22 @@
#![feature(const_fn)]
#[derive(PartialEq, Eq)]
enum Cake {
BlackForest,
Marmor,
}
use Cake::*;
const BOO: (Cake, Cake) = (Marmor, BlackForest);
struct Pair<A, B>(A, B);
const BOO: Pair<Cake, Cake> = Pair(Marmor, BlackForest);
//~^ ERROR: constant evaluation error [E0080]
//~| unimplemented constant expression: enum variants
//~| unimplemented constant expression: tuple struct constructors
const FOO: Cake = BOO.1;
const fn foo() -> Cake {
Marmor
//~^ ERROR: constant evaluation error [E0080]
//~| unimplemented constant expression: enum variants
}
const WORKS: Cake = Marmor;
@ -34,7 +35,7 @@ const GOO: Cake = foo();
fn main() {
match BlackForest {
FOO => println!("hi"), //~ NOTE: for pattern here
GOO => println!("meh"), //~ NOTE: for pattern here
GOO => println!("meh"),
WORKS => println!("möp"),
_ => println!("bye"),
}

View file

@ -28,10 +28,6 @@ pub struct Vector<T, D: Dim> {
fn main() {
let array: [usize; Dim3::dim()]
//~^ ERROR calls in constants are limited to constant functions
//~| ERROR constant evaluation error
//~| non-constant path in constant expression
= [0; Dim3::dim()];
//~^ ERROR calls in constants are limited to constant functions
//~| ERROR constant evaluation error
//~| non-constant path in constant expression
}

View file

@ -0,0 +1,20 @@
// Copyright 2017 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.
enum Foo {
A = "" + 1
//~^ ERROR binary operation `+` cannot be applied to type `&'static str`
}
enum Bar {
A = Foo::A as isize
}
fn main() {}

View file

@ -0,0 +1,26 @@
// Copyright 2017 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.
#![crate_type = "lib"]
#[repr(u32)]
pub enum Foo {
Foo = Private::Variant as u32
}
#[repr(u8)]
enum Private {
Variant = 42
}
#[inline(always)]
pub fn foo() -> Foo {
Foo::Foo
}

View file

@ -0,0 +1,38 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(const_fn)]
#[derive(PartialEq, Eq)]
enum Cake {
BlackForest,
Marmor,
}
use Cake::*;
const BOO: (Cake, Cake) = (Marmor, BlackForest);
const FOO: Cake = BOO.1;
const fn foo() -> Cake {
Marmor
}
const WORKS: Cake = Marmor;
const GOO: Cake = foo();
fn main() {
match BlackForest {
FOO => println!("hi"),
GOO => println!("meh"),
WORKS => println!("möp"),
_ => println!("bye"),
}
}

View file

@ -8,13 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Note: This test is checking that we forbid a coding pattern that
// Issue #5873 explicitly wants to allow.
// Note: This test was used to demonstrate #5873 (now #23898).
enum State { ST_NULL, ST_WHITESPACE }
fn main() {
[State::ST_NULL; (State::ST_WHITESPACE as usize)];
//~^ ERROR constant evaluation error
//~| unimplemented constant expression: enum variants
}

View file

@ -0,0 +1,17 @@
// Copyright 2017 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.
// aux-build:issue-41394.rs
extern crate issue_41394 as lib;
fn main() {
assert_eq!(lib::foo() as u32, 42);
}