From 490742e3390624bd5c1467ab741655013db1e5cf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 24 Oct 2017 15:10:23 +0200 Subject: [PATCH 1/2] Resolve types properly in const eval --- src/librustc/ty/instance.rs | 4 +- src/librustc_const_eval/eval.rs | 98 +++------------------------------ 2 files changed, 10 insertions(+), 92 deletions(-) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 600b2572f92b8..442c79393fdc7 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -100,7 +100,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { impl<'a, 'b, 'tcx> Instance<'tcx> { pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> Instance<'tcx> { - assert!(substs.is_normalized_for_trans() && !substs.has_escaping_regions(), + assert!(!substs.has_escaping_regions(), "substs of instance {:?} not normalized for trans: {:?}", def_id, substs); Instance { def: InstanceDef::Item(def_id), substs: substs } @@ -139,7 +139,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { substs: &'tcx Substs<'tcx>) -> Option> { debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { - debug!(" => associated item, attempting to find impl"); + debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); let item = tcx.associated_item(def_id); resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) } else { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 7520c6ac652ba..657156902b5c1 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -13,9 +13,7 @@ use rustc::middle::const_val::ConstAggregate::*; use rustc::middle::const_val::ErrKind::*; use rustc::middle::const_val::{ByteArray, 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, CtorKind}; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; @@ -54,33 +52,12 @@ macro_rules! math { pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> Option<(DefId, &'tcx Substs<'tcx>)> { - let (def_id, _) = key.value; - if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - match tcx.hir.find(node_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, key) - } - _ => Some(key.value) - } - } else { - match tcx.describe_def(def_id) { - Some(Def::AssociatedConst(_)) => { - // 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 tcx.trait_of_item(def_id).is_some() { - resolve_trait_associated_const(tcx, key) - } else { - Some(key.value) - } - } - _ => Some(key.value) - } - } + ty::Instance::resolve( + tcx, + key.param_env, + key.value.0, + key.value.1, + ).map(|instance| (instance.def_id(), instance.substs)) } pub struct ConstContext<'a, 'tcx: 'a> { @@ -119,6 +96,7 @@ type CastResult<'tcx> = Result, ErrKind<'tcx>>; fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, e: &'tcx Expr) -> EvalResult<'tcx> { + trace!("eval_const_expr_partial: {:?}", e); let tcx = cx.tcx; let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs); let mk_const = |val| tcx.mk_const(ty::Const { val, ty }); @@ -289,6 +267,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, match cx.tables.qpath_def(qpath, e.hir_id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { + let substs = tcx.normalize_associated_type_in_env(&substs, cx.param_env); match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) { Ok(val) => val, Err(ConstEvalErr { kind: TypeckError, .. }) => { @@ -486,67 +465,6 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, Ok(result) } -fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { - let param_env = key.param_env; - let (def_id, substs) = key.value; - 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.infer_ctxt().enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), - param_env, - trait_ref.to_poly_trait_predicate()); - let selection = match selcx.select(&obligation) { - Ok(Some(vtable)) => vtable, - // Still ambiguous, so give up and let the caller decide whether this - // expression is really needed yet. Some associated constant values - // can't be evaluated until monomorphization is done in trans. - Ok(None) => { - return None - } - Err(_) => { - return None - } - }; - - // NOTE: this code does not currently account for specialization, but when - // it does so, it should hook into the param_env.reveal to determine when the - // constant should resolve. - match selection { - traits::VtableImpl(ref impl_data) => { - 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 { - // FIXME(eddyb) Use proper Instance resolution to - // get the correct Substs returned from here. - Some(ic) => { - let substs = Substs::identity_for_item(tcx, ic.def_id); - Some((ic.def_id, substs)) - } - None => { - if trait_item.defaultness.has_value() { - Some(key.value) - } else { - None - } - } - } - } - traits::VtableParam(_) => None, - _ => { - bug!("resolve_trait_associated_const: unexpected vtable type {:?}", selection) - } - } - }) -} - fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: Ty<'tcx>) From 1ee0ff3bfed4ecf56b175b166286b7cd2163a81b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 25 Oct 2017 09:13:41 +0200 Subject: [PATCH 2/2] Add a regression test for the const eval type resolution --- src/test/run-pass/ctfe/assoc-const.rs | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/run-pass/ctfe/assoc-const.rs diff --git a/src/test/run-pass/ctfe/assoc-const.rs b/src/test/run-pass/ctfe/assoc-const.rs new file mode 100644 index 0000000000000..6a740dc1dd30b --- /dev/null +++ b/src/test/run-pass/ctfe/assoc-const.rs @@ -0,0 +1,28 @@ +// 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. + +trait Nat { + const VALUE: usize; +} + +struct Zero; +struct Succ(N); + +impl Nat for Zero { + const VALUE: usize = 0; +} + +impl Nat for Succ { + const VALUE: usize = N::VALUE + 1; +} + +fn main() { + let x: [i32; >>>>::VALUE] = [1, 2, 3, 4]; +}