From e22873d91206e11349da713017bd67d43d8d535d Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 18 Apr 2017 23:38:15 +0300 Subject: [PATCH 1/3] rustc: make the const-eval cache polymorphic. --- src/librustc/dep_graph/dep_node.rs | 4 +- src/librustc/middle/const_val.rs | 3 +- src/librustc/middle/cstore.rs | 10 +- src/librustc/ty/maps.rs | 32 ++++- src/librustc/ty/mod.rs | 6 +- src/librustc_const_eval/eval.rs | 185 ++++++++++++--------------- src/librustc_const_eval/pattern.rs | 11 +- src/librustc_metadata/cstore_impl.rs | 15 +-- src/librustc_metadata/decoder.rs | 19 ++- src/librustc_mir/hair/cx/expr.rs | 3 +- src/librustc_typeck/collect.rs | 3 +- src/librustdoc/clean/inline.rs | 2 +- src/test/compile-fail/const-call.rs | 2 - src/test/compile-fail/issue-39559.rs | 4 - 14 files changed, 150 insertions(+), 149 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 224e9751e73d0..9a6574385222a 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -97,7 +97,7 @@ pub enum DepNode { 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 DepNode { 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), diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index b4c5af9401944..50f34723d5019 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -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); diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 20ed2244e864c..3251addcb3283 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -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; 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 { bug!("item_body_nested_bodies") diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index add8db850e4f4..4247bff0f5026 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -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, pub reachable_set: reachability_dep_node(CrateNum) -> Rc, - pub mir_shims: mir_shim(ty::InstanceDef<'tcx>) -> &'tcx RefCell> + pub mir_shims: mir_shim_dep_node(ty::InstanceDef<'tcx>) -> &'tcx RefCell> } fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode { @@ -470,10 +488,14 @@ fn reachability_dep_node(_: CrateNum) -> DepNode { DepNode::Reachability } -fn mir_shim(instance: ty::InstanceDef) -> DepNode { +fn mir_shim_dep_node(instance: ty::InstanceDef) -> DepNode { instance.dep_node() } fn typeck_item_bodies_dep_node(_: CrateNum) -> DepNode { DepNode::TypeckBodiesKrate } + +fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode { + DepNode::ConstEval(def_id) +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1305b0d930378..1588773479c5e 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1701,7 +1701,8 @@ 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; } @@ -1733,7 +1734,8 @@ 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; diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 9c5a669bef0d9..37395ca78714d 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -75,83 +75,34 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// /// `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)) } } } @@ -357,21 +308,15 @@ 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) { @@ -407,14 +352,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")), + let (def_id, substs) = match cx.eval(callee)? { + Function(def_id, substs) => (def_id, substs), callee => signal!(e, CallOn(callee)), }; - 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 +392,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 +490,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 +524,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 + } + } } } _ => { @@ -796,21 +759,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) } diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index f20fa27dc2251..a470d549d05ef 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -587,11 +587,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; } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index cb1b0c4c0b78b..9e6a45e7f8b7c 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -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 { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 2d562aceb65cd..a9eae5281b241 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -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, diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index b7de50efe3442..736c076ea1544 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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") diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1f2310c49e3a0..660ce837043c1 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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 diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index cc30fdf56fc34..6016fd488f56c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -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) }; diff --git a/src/test/compile-fail/const-call.rs b/src/test/compile-fail/const-call.rs index ff83dd004a257..0745ac02d0723 100644 --- a/src/test/compile-fail/const-call.rs +++ b/src/test/compile-fail/const-call.rs @@ -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 } diff --git a/src/test/compile-fail/issue-39559.rs b/src/test/compile-fail/issue-39559.rs index 06e8406cbc0bd..b7f767f109c0c 100644 --- a/src/test/compile-fail/issue-39559.rs +++ b/src/test/compile-fail/issue-39559.rs @@ -28,10 +28,6 @@ pub struct Vector { 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 } From 0ff828baa06504c91e38dfda53ead61cc44ad171 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 19 Apr 2017 15:15:12 +0300 Subject: [PATCH 2/3] rustc_const_eval: CallOn isn't needed, typeck/const-qualif handle those cases. --- src/librustc/middle/const_val.rs | 2 -- src/librustc_const_eval/eval.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 50f34723d5019..b65dbdbbcc250 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -85,7 +85,6 @@ pub enum ErrKind<'tcx> { MissingStructField, NegateOn(ConstVal<'tcx>), NotOn(ConstVal<'tcx>), - CallOn(ConstVal<'tcx>), NonConstPath, UnimplementedConstVal(&'static str), @@ -145,7 +144,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"), diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 37395ca78714d..5c421df92c7e2 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -354,7 +354,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, hir::ExprCall(ref callee, ref args) => { let (def_id, substs) = match cx.eval(callee)? { Function(def_id, substs) => (def_id, substs), - callee => signal!(e, CallOn(callee)), + _ => signal!(e, TypeckError), }; let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { From 8054377f8f4dfaf766bcff40e7a720c90c5e33be Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 19 Apr 2017 15:16:19 +0300 Subject: [PATCH 3/3] rustc_const_eval: support all unit enum variants. --- src/librustc/ich/impls_ty.rs | 9 ++- src/librustc/middle/const_val.rs | 6 +- src/librustc/mir/mod.rs | 3 +- src/librustc/ty/mod.rs | 19 ++++- src/librustc_const_eval/eval.rs | 77 ++++++++----------- src/librustc_const_eval/pattern.rs | 8 +- src/librustc_trans/mir/constant.rs | 8 +- .../const-pattern-not-const-evaluable.rs | 11 +-- src/test/compile-fail/issue-41394.rs | 20 +++++ src/test/run-pass/auxiliary/issue-41394.rs | 26 +++++++ src/test/run-pass/const-pattern-variant.rs | 38 +++++++++ .../issue-23898.rs} | 5 +- src/test/run-pass/issue-41394.rs | 17 ++++ 13 files changed, 180 insertions(+), 67 deletions(-) create mode 100644 src/test/compile-fail/issue-41394.rs create mode 100644 src/test/run-pass/auxiliary/issue-41394.rs create mode 100644 src/test/run-pass/const-pattern-variant.rs rename src/test/{compile-fail/non-constant-enum-for-vec-repeat.rs => run-pass/issue-23898.rs} (73%) create mode 100644 src/test/run-pass/issue-41394.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index f55462fb5deb6..16af98c203548 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -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); - } } } } diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index b65dbdbbcc250..ec7b3c4dd8dff 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -38,12 +38,13 @@ pub enum ConstVal<'tcx> { Str(InternedString), ByteStr(Rc>), Bool(bool), + Char(char), + Variant(DefId), Function(DefId, &'tcx Substs<'tcx>), Struct(BTreeMap>), Tuple(Vec>), Array(Vec>), Repeat(Box>, 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", } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9ff64b295b765..bfb72b5df7b2e 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1307,10 +1307,11 @@ fn fmt_const_val(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), } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1588773479c5e..cc5eb768d9b32 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1693,6 +1693,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } + #[inline] pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator + 'a { let repr_type = self.repr.discr_type(); @@ -1706,7 +1707,13 @@ impl<'a, 'gcx, 'tcx> AdtDef { 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); @@ -1740,7 +1747,15 @@ impl<'a, 'gcx, 'tcx> AdtDef { 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; } } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 5c421df92c7e2..e9352f53c92d6 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -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,28 +48,6 @@ 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. /// @@ -289,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) => { @@ -317,27 +308,20 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, 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)) { @@ -578,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), } } @@ -622,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(_) => { diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index a470d549d05ef..0dfafeb6fb836 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -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(..) | @@ -620,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)); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index e938913a3f117..040194e63d07e 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -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()); diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index b40aa2a8e27dc..71cac3edbc188 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -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); + +const BOO: Pair = 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"), } diff --git a/src/test/compile-fail/issue-41394.rs b/src/test/compile-fail/issue-41394.rs new file mode 100644 index 0000000000000..1fb3b7c4ee120 --- /dev/null +++ b/src/test/compile-fail/issue-41394.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/auxiliary/issue-41394.rs b/src/test/run-pass/auxiliary/issue-41394.rs new file mode 100644 index 0000000000000..f06b81279ac41 --- /dev/null +++ b/src/test/run-pass/auxiliary/issue-41394.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/run-pass/const-pattern-variant.rs b/src/test/run-pass/const-pattern-variant.rs new file mode 100644 index 0000000000000..104ab6e19db67 --- /dev/null +++ b/src/test/run-pass/const-pattern-variant.rs @@ -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 or the MIT license +// , 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"), + } +} diff --git a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs b/src/test/run-pass/issue-23898.rs similarity index 73% rename from src/test/compile-fail/non-constant-enum-for-vec-repeat.rs rename to src/test/run-pass/issue-23898.rs index cadfec5a38d3d..3f5546ce83ddf 100644 --- a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs +++ b/src/test/run-pass/issue-23898.rs @@ -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 } diff --git a/src/test/run-pass/issue-41394.rs b/src/test/run-pass/issue-41394.rs new file mode 100644 index 0000000000000..798905599a852 --- /dev/null +++ b/src/test/run-pass/issue-41394.rs @@ -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 or the MIT license +// , 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); +}