Skip to content

Resolve types properly in const eval #45488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -139,7 +139,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> {
substs: &'tcx Substs<'tcx>) -> Option<Instance<'tcx>> {
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 {
Expand Down
98 changes: 8 additions & 90 deletions src/librustc_const_eval/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -119,6 +96,7 @@ type CastResult<'tcx> = Result<ConstVal<'tcx>, 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 });
Expand Down Expand Up @@ -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);
Copy link
Contributor

@arielb1 arielb1 Oct 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this normalization also needed in the Def::Method(id) | Def::Fn(id) arm, and also for types fetched from expr_ty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically we only need it for Def::AssociatedConst, but it's a nop for Def::Const

match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
Expand Down Expand Up @@ -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>)
Expand Down
28 changes: 28 additions & 0 deletions src/test/run-pass/ctfe/assoc-const.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

trait Nat {
const VALUE: usize;
}

struct Zero;
struct Succ<N>(N);

impl Nat for Zero {
const VALUE: usize = 0;
}

impl<N: Nat> Nat for Succ<N> {
const VALUE: usize = N::VALUE + 1;
}

fn main() {
let x: [i32; <Succ<Succ<Succ<Succ<Zero>>>>>::VALUE] = [1, 2, 3, 4];
}