Skip to content

Make the translation item collector handle *uses* of 'const' items instead of declarations. #35534

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 1 commit into from
Aug 14, 2016
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
64 changes: 49 additions & 15 deletions src/librustc_trans/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
};

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<TransItem<'tcx>>)
fn collect_const_item_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
output: &mut Vec<TransItem<'tcx>>)
{
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);
Expand Down
93 changes: 93 additions & 0 deletions src/test/codegen-units/partitioning/vtable-through-const.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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<T> {
fn do_something(&self, x: T) -> T;
fn do_something_else(&self, x: T) -> T;
}

impl<T> Trait1Gen<T> for u32 {
fn do_something(&self, x: T) -> T { x }
fn do_something_else(&self, x: T) -> T { x }
}

fn id<T>(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<u8> = &0u32 as &Trait1Gen<u8>;
pub const ID_CHAR: fn(char) -> char = id::<char>;



pub trait Trait2 {
fn do_something(&self) {}
fn do_something_else(&self) {}
}

impl Trait2 for u32 {}

pub trait Trait2Gen<T> {
fn do_something(&self, x: T) -> T;
fn do_something_else(&self, x: T) -> T;
}

impl<T> Trait2Gen<T> 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<u8> = &0u32 as &Trait2Gen<u8>;
pub const ID_I64: fn(i64) -> i64 = id::<i64>;
}

//~ 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]<u32> @@ 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]<u32> @@ 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]<u8> @@ vtable_through_const[Internal]
//~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0]<u8> @@ vtable_through_const[Internal]
mod1::TRAIT1_GEN_REF.do_something(0u8);

//~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0]<char> @@ vtable_through_const[Internal]
mod1::ID_CHAR('x');
}

//~ TRANS_ITEM drop-glue i8