From c91037b964c33cd344632375afad837db5512160 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 6 Nov 2016 20:45:27 +0100 Subject: [PATCH] Fix cross-crate associated constant evaluation --- src/librustc/middle/cstore.rs | 1 + src/librustc_const_eval/eval.rs | 42 +++++++++++++------ src/librustc_metadata/encoder.rs | 4 +- .../run-pass/associated-const-const-eval.rs | 30 +++++++++++++ ...associated-const-cross-crate-const-eval.rs | 38 +++++++++++++++++ 5 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/test/run-pass/associated-const-const-eval.rs create mode 100644 src/test/run-pass/associated-const-cross-crate-const-eval.rs diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 17085e31bfe5a..fd986b6bbc5de 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -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 { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 7972b9dc09873..ccdfb1e69a722 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -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 @@ -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. @@ -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>)>, trait_id: DefId, rcvr_substs: &'tcx Substs<'tcx>) -> Option<(&'tcx Expr, Option>)> @@ -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") } } }) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index afb88cf3ddfd0..878d06ea92709 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -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 { diff --git a/src/test/run-pass/associated-const-const-eval.rs b/src/test/run-pass/associated-const-const-eval.rs new file mode 100644 index 0000000000000..0b230df41469f --- /dev/null +++ b/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 or the MIT license +// , 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 = ::NUM; + +fn main() { + assert_eq!(1, FOO); + + match 1 { + ::NUM => {}, + _ => assert!(false) + } +} diff --git a/src/test/run-pass/associated-const-cross-crate-const-eval.rs b/src/test/run-pass/associated-const-cross-crate-const-eval.rs new file mode 100644 index 0000000000000..7d31bb5b1a5e2 --- /dev/null +++ b/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 or the MIT license +// , 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 = ::BAR; +const FOO_2: usize = ::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 { + ::BAR => {}, + ::BAR => assert!(false), + foolib::InherentBar::BAR => assert!(false), + _ => assert!(false) + } +}