Skip to content

Commit

Permalink
Fix cross-crate associated constant evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
flodiebold committed Nov 29, 2016
1 parent 936dbbc commit c91037b
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/librustc/middle/cstore.rs
Expand Up @@ -181,6 +181,7 @@ impl<'a> InlinedItemRef<'a> {
pub fn from_trait_item(def_id: DefId, item: &'a hir::TraitItem, _map: &hir_map::Map) -> InlinedItemRef<'a> {
let (body, kind) = match item.node {
hir::ConstTraitItem(ref ty, Some(ref body)) => (&**body, InlinedItemKindRef::Const(ty)),
hir::ConstTraitItem(_, None) => bug!("InlinedItemRef::from_trait_item called for const without body"),
_ => bug!("InlinedItemRef::from_trait_item wrong kind")
};
InlinedItemRef {
Expand Down
42 changes: 30 additions & 12 deletions src/librustc_const_eval/eval.rs
Expand Up @@ -102,14 +102,15 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
_ => None
},
Some(ast_map::NodeTraitItem(ti)) => match ti.node {
hir::ConstTraitItem(..) => {
hir::ConstTraitItem(ref ty, ref expr_option) => {
if let Some(substs) = substs {
// 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.map.get_parent(node_id);
let trait_id = tcx.map.local_def_id(trait_id);
resolve_trait_associated_const(tcx, ti, trait_id, substs)
let default_value = expr_option.as_ref().map(|expr| (&**expr, tcx.ast_ty_to_prim_ty(ty)));
resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs)
} else {
// Technically, without knowing anything about the
// expression that generates the obligation, we could
Expand Down Expand Up @@ -145,6 +146,27 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
_ => None
};
let expr_ty = 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 {
used_substs = true;

if let Some(substs) = substs {
resolve_trait_associated_const(tcx, def_id, expr_ty, trait_id, substs)
} else {
None
}
} else {
expr_ty
}
}
_ => expr_ty
};
// If we used the substitutions, particularly to choose an impl
// of a trait-associated const, don't cache that, because the next
// lookup with the same def_id may yield a different result.
Expand Down Expand Up @@ -1036,7 +1058,8 @@ fn infer<'a, 'tcx>(i: ConstInt,
}

fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ti: &'tcx hir::TraitItem,
trait_item_id: DefId,
default_value: Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)>,
trait_id: DefId,
rcvr_substs: &'tcx Substs<'tcx>)
-> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)>
Expand Down Expand Up @@ -1070,21 +1093,16 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// when constructing the inference context above.
match selection {
traits::VtableImpl(ref impl_data) => {
let name = tcx.associated_item(trait_item_id).name;
let ac = tcx.associated_items(impl_data.impl_def_id)
.find(|item| item.kind == ty::AssociatedKind::Const && item.name == ti.name);
.find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
match ac {
Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
None => match ti.node {
hir::ConstTraitItem(ref ty, Some(ref expr)) => {
Some((&*expr, tcx.ast_ty_to_prim_ty(ty)))
},
_ => None,
},
None => default_value,
}
}
_ => {
span_bug!(ti.span,
"resolve_trait_associated_const: unexpected vtable type")
bug!("resolve_trait_associated_const: unexpected vtable type")
}
}
})
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_metadata/encoder.rs
Expand Up @@ -516,7 +516,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),

ast: if trait_item.kind == ty::AssociatedKind::Const {
ast: if let hir::ConstTraitItem(_, Some(_)) = ast_item.node {
// We only save the HIR for associated consts with bodies
// (InlinedItemRef::from_trait_item panics otherwise)
let trait_def_id = trait_item.container.id();
Some(self.encode_inlined_item(InlinedItemRef::from_trait_item(trait_def_id, ast_item, &tcx.map)))
} else {
Expand Down
30 changes: 30 additions & 0 deletions src/test/run-pass/associated-const-const-eval.rs
@@ -0,0 +1,30 @@
// 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(associated_consts)]

trait Foo {
const NUM: usize;
}

impl Foo for i32 {
const NUM: usize = 1;
}

const FOO: usize = <i32 as Foo>::NUM;

fn main() {
assert_eq!(1, FOO);

match 1 {
<i32 as Foo>::NUM => {},
_ => assert!(false)
}
}
38 changes: 38 additions & 0 deletions src/test/run-pass/associated-const-cross-crate-const-eval.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 <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:associated-const-cc-lib.rs

#![feature(associated_consts)]

extern crate associated_const_cc_lib as foolib;

pub struct LocalFoo;

impl foolib::Foo for LocalFoo {
const BAR: usize = 1;
}

const FOO_1: usize = <foolib::FooNoDefault as foolib::Foo>::BAR;
const FOO_2: usize = <LocalFoo as foolib::Foo>::BAR;
const FOO_3: usize = foolib::InherentBar::BAR;

fn main() {
assert_eq!(0, FOO_1);
assert_eq!(1, FOO_2);
assert_eq!(3, FOO_3);

match 0 {
<foolib::FooNoDefault as foolib::Foo>::BAR => {},
<LocalFoo as foolib::Foo>::BAR => assert!(false),
foolib::InherentBar::BAR => assert!(false),
_ => assert!(false)
}
}

0 comments on commit c91037b

Please sign in to comment.