diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 4a6dbb2bdae..794da0d1473 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -202,10 +202,11 @@ use rustc::mir::repr as mir; use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; +use rustc_const_eval as const_eval; + use syntax::abi::Abi; use errors; use syntax_pos::DUMMY_SP; -use syntax::ast::NodeId; use base::custom_coerce_unsize_info; use context::SharedCrateContext; use common::{fulfill_obligation, normalize_and_test_predicates, type_is_sized}; @@ -543,9 +544,46 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { debug!("visiting operand {:?}", *operand); let callee = match *operand { - mir::Operand::Constant(mir::Constant { ty: &ty::TyS { - sty: ty::TyFnDef(def_id, substs, _), .. - }, .. }) => Some((def_id, substs)), + mir::Operand::Constant(ref constant) => { + if let ty::TyFnDef(def_id, substs, _) = constant.ty.sty { + // This is something that can act as a callee, proceed + Some((def_id, substs)) + } else { + // This is not a callee, but we still have to look for + // references to `const` items + if let mir::Literal::Item { def_id, substs } = constant.literal { + let tcx = self.scx.tcx(); + let substs = monomorphize::apply_param_substs(tcx, + self.param_substs, + &substs); + + // If the constant referred to here is an associated + // item of a trait, we need to resolve it to the actual + // constant in the corresponding impl. Luckily + // const_eval::lookup_const_by_id() does that for us. + if let Some((expr, _)) = const_eval::lookup_const_by_id(tcx, + def_id, + Some(substs)) { + // The hir::Expr we get here is the initializer of + // the constant, what we really want is the item + // DefId. + let const_node_id = tcx.map.get_parent(expr.id); + let def_id = if tcx.map.is_inlined_node_id(const_node_id) { + tcx.sess.cstore.defid_for_inlined_node(const_node_id).unwrap() + } else { + tcx.map.local_def_id(const_node_id) + }; + + collect_const_item_neighbours(self.scx, + def_id, + substs, + self.output); + } + } + + None + } + } _ => None }; @@ -1117,10 +1155,8 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> { self.output.push(TransItem::Static(item.id)); } hir::ItemConst(..) => { - debug!("RootCollector: ItemConst({})", - def_id_to_string(self.scx.tcx(), - self.scx.tcx().map.local_def_id(item.id))); - add_roots_for_const_item(self.scx, item.id, self.output); + // const items only generate translation items if they are + // actually used somewhere. Just declaring them is insufficient. } hir::ItemFn(_, _, _, _, ref generics, _) => { if !generics.is_type_parameterized() { @@ -1244,23 +1280,21 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // There are no translation items for constants themselves but their // initializers might still contain something that produces translation items, // such as cast that introduce a new vtable. -fn add_roots_for_const_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - const_item_node_id: NodeId, - output: &mut Vec>) +fn collect_const_item_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + output: &mut Vec>) { - let def_id = scx.tcx().map.local_def_id(const_item_node_id); - // Scan the MIR in order to find function calls, closures, and // drop-glue let mir = errors::expect(scx.sess().diagnostic(), scx.get_mir(def_id), || format!("Could not find MIR for const: {:?}", def_id)); - let empty_substs = scx.empty_substs_for_def_id(def_id); let visitor = MirNeighborCollector { scx: scx, mir: &mir, output: output, - param_substs: empty_substs + param_substs: substs }; visit_mir_and_promoted(visitor, &mir); diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs new file mode 100644 index 00000000000..b40bb7f6097 --- /dev/null +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -0,0 +1,93 @@ +// Copyright 2016 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. + +// ignore-tidy-linelength + +// We specify -Z incremental here because we want to test the partitioning for +// incremental compilation +// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const + +// This test case makes sure, that references made through constants are +// recorded properly in the InliningMap. + +mod mod1 { + pub trait Trait1 { + fn do_something(&self) {} + fn do_something_else(&self) {} + } + + impl Trait1 for u32 {} + + pub trait Trait1Gen { + fn do_something(&self, x: T) -> T; + fn do_something_else(&self, x: T) -> T; + } + + impl Trait1Gen for u32 { + fn do_something(&self, x: T) -> T { x } + fn do_something_else(&self, x: T) -> T { x } + } + + fn id(x: T) -> T { x } + + // These are referenced, so they produce trans-items (see main()) + pub const TRAIT1_REF: &'static Trait1 = &0u32 as &Trait1; + pub const TRAIT1_GEN_REF: &'static Trait1Gen = &0u32 as &Trait1Gen; + pub const ID_CHAR: fn(char) -> char = id::; + + + + pub trait Trait2 { + fn do_something(&self) {} + fn do_something_else(&self) {} + } + + impl Trait2 for u32 {} + + pub trait Trait2Gen { + fn do_something(&self, x: T) -> T; + fn do_something_else(&self, x: T) -> T; + } + + impl Trait2Gen for u32 { + fn do_something(&self, x: T) -> T { x } + fn do_something_else(&self, x: T) -> T { x } + } + + // These are not referenced, so they do not produce trans-items + pub const TRAIT2_REF: &'static Trait2 = &0u32 as &Trait2; + pub const TRAIT2_GEN_REF: &'static Trait2Gen = &0u32 as &Trait2Gen; + pub const ID_I64: fn(i64) -> i64 = id::; +} + +//~ TRANS_ITEM fn vtable_through_const::main[0] @@ vtable_through_const[External] +fn main() { + + // Since Trait1::do_something() is instantiated via its default implementation, + // it is considered a generic and is instantiated here only because it is + // referenced in this module. + //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const[Internal] + + // Although it is never used, Trait1::do_something_else() has to be + // instantiated locally here too, otherwise the <&u32 as &Trait1> vtable + // could not be fully constructed. + //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const[Internal] + mod1::TRAIT1_REF.do_something(); + + // Same as above + //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const[Internal] + //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const[Internal] + mod1::TRAIT1_GEN_REF.do_something(0u8); + + //~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const[Internal] + mod1::ID_CHAR('x'); +} + +//~ TRANS_ITEM drop-glue i8