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;
use syntax::ast;
use rustc::hir::{self, Expr};
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::Span;
use std::cmp::Ordering;
}
}
-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) {
+ match tcx.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.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))
}
}
}
}
}
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) => {
- let substs = cx.tables.node_id_item_substs(e.id)
- .unwrap_or_else(|| tcx.intern_substs(&[]));
+ let substs = cx.tables.node_substs(e.id);
// Avoid applying substitutions if they're empty, that'd ICE.
let substs = if cx.substs.is_empty() {
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 tcx.at(e.span).const_eval((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::VariantCtor(_, CtorKind::Fn) => {
+ signal!(e, UnimplementedConstVal("enum variants"));
}
- Def::StructCtor(..) => {
+ 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)) {
}
}
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 {
debug!("const call({:?})", call_args);
let callee_cx = ConstContext {
tcx: tcx,
- tables: tables,
+ tables: tcx.typeck_tables_of(def_id),
substs: substs,
fn_args: Some(call_args)
};
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| {
+ tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
let mut selcx = traits::SelectionContext::new(&infcx);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
trait_ref.to_poly_trait_predicate());
// 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
+ }
+ }
}
}
_ => {
U8(u) => Ok(Char(u as char)),
_ => bug!(),
},
- _ => bug!(),
+ _ => Err(CannotCast),
}
}
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.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(_) => {
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.typeck_tables_of(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.mir_const_qualif(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)
}