Skip to content

Commit

Permalink
rustc_trans: simplify vtable and symbol handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Sep 20, 2016
1 parent 521d3ea commit ade79d7
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 355 deletions.
84 changes: 83 additions & 1 deletion src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs};
pub use self::specialize::{SpecializesCache};
pub use self::specialize::{SpecializesCache, find_method};
pub use self::util::elaborate_predicates;
pub use self::util::supertraits;
pub use self::util::Supertraits;
Expand Down Expand Up @@ -527,6 +527,88 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
Ok(resolved_value)
}

/// Normalizes the predicates and checks whether they hold. If this
/// returns false, then either normalize encountered an error or one
/// of the predicates did not hold. Used when creating vtables to
/// check for unsatisfiable methods.
pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
predicates: Vec<ty::Predicate<'tcx>>)
-> bool
{
debug!("normalize_and_test_predicates(predicates={:?})",
predicates);

tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
let mut fulfill_cx = FulfillmentContext::new();
let cause = ObligationCause::dummy();
let Normalized { value: predicates, obligations } =
normalize(&mut selcx, cause.clone(), &predicates);
for obligation in obligations {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
for predicate in predicates {
let obligation = Obligation::new(cause.clone(), predicate);
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}

fulfill_cx.select_all_or_error(&infcx).is_ok()
})
}

/// Given a trait `trait_ref`, iterates the vtable entries
/// that come from `trait_ref`, including its supertraits.
#[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait.
pub fn get_vtable_methods<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> impl Iterator<Item=Option<(DefId, &'tcx Substs<'tcx>)>> + 'a
{
debug!("get_vtable_methods({:?})", trait_ref);

supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());

let trait_item_def_ids = tcx.impl_or_trait_items(trait_ref.def_id());
let trait_methods = (0..trait_item_def_ids.len()).filter_map(move |i| {
match tcx.impl_or_trait_item(trait_item_def_ids[i]) {
ty::MethodTraitItem(m) => Some(m),
_ => None
}
});

// Now list each method's DefId and Substs (for within its trait).
// If the method can never be called from this object, produce None.
trait_methods.map(move |trait_method| {
debug!("get_vtable_methods: trait_method={:?}", trait_method);

// Some methods cannot be called on an object; skip those.
if !tcx.is_vtable_safe_method(trait_ref.def_id(), &trait_method) {
debug!("get_vtable_methods: not vtable safe");
return None;
}

// the method may have some early-bound lifetimes, add
// regions for those
let substs = Substs::for_item(tcx, trait_method.def_id,
|_, _| tcx.mk_region(ty::ReErased),
|def, _| trait_ref.substs().type_for_def(def));

// It's possible that the method relies on where clauses that
// do not hold for this particular set of type parameters.
// Note that this method could then never be called, so we
// do not want to try and trans it, in that case (see #23435).
let predicates = trait_method.predicates.instantiate_own(tcx, substs);
if !normalize_and_test_predicates(tcx, predicates.predicates) {
debug!("get_vtable_methods: predicates do not hold");
return None;
}

Some((trait_method.def_id, substs))
})
})
}

impl<'tcx,O> Obligation<'tcx,O> {
pub fn new(cause: ObligationCause<'tcx>,
trait_ref: O)
Expand Down
39 changes: 38 additions & 1 deletion src/librustc/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ use infer::{InferCtxt, TypeOrigin};
use middle::region;
use ty::subst::{Subst, Substs};
use traits::{self, Reveal, ObligationCause, Normalized};
use ty::{self, TyCtxt};
use ty::{self, TyCtxt, TypeFoldable};
use syntax_pos::DUMMY_SP;

use syntax::ast;

pub mod specialization_graph;

/// Information pertinent to an overlapping impl error.
Expand Down Expand Up @@ -103,6 +105,41 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source_substs.rebase_onto(infcx.tcx, source_impl, target_substs)
}

/// Given a selected impl described by `impl_data`, returns the
/// definition and substitions for the method with the name `name`,
/// and trait method substitutions `substs`, in that impl, a less
/// specialized impl, or the trait default, whichever applies.
pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
name: ast::Name,
substs: &'tcx Substs<'tcx>,
impl_data: &super::VtableImplData<'tcx, ()>)
-> (DefId, &'tcx Substs<'tcx>)
{
assert!(!substs.needs_infer());

let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
let trait_def = tcx.lookup_trait_def(trait_def_id);

match trait_def.ancestors(impl_data.impl_def_id).fn_defs(tcx, name).next() {
Some(node_item) => {
let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| {
let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
let substs = translate_substs(&infcx, impl_data.impl_def_id,
substs, node_item.node);
tcx.lift(&substs).unwrap_or_else(|| {
bug!("find_method: translate_substs \
returned {:?} which contains inference types/regions",
substs);
})
});
(node_item.item.def_id, substs)
}
None => {
bug!("method {:?} not found in {:?}", name, impl_data.impl_def_id)
}
}
}

/// Is impl1 a specialization of impl2?
///
/// Specialization is determined by the sets of types to which the impls apply;
Expand Down
44 changes: 12 additions & 32 deletions src/librustc_trans/back/symbol_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
//! virtually impossible. Thus, symbol hash generation exclusively relies on
//! DefPaths which are much more robust in the face of changes to the code base.

use common::{CrateContext, SharedCrateContext, gensym_name};
use common::SharedCrateContext;
use monomorphize::Instance;
use util::sha2::{Digest, Sha256};

Expand Down Expand Up @@ -152,16 +152,17 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
let mut hash_state = scx.symbol_hasher().borrow_mut();
record_time(&tcx.sess.perf_stats.symbol_hash_time, || {
hash_state.reset();
let mut hasher = Sha256Hasher(&mut hash_state);

let mut hasher = ty::util::TypeIdHasher::new(tcx, Sha256Hasher(&mut hash_state));
// the main symbol name is not necessarily unique; hash in the
// compiler's internal def-path, guaranteeing each symbol has a
// truly unique path
hasher.hash(def_path.to_string(tcx));
def_path.deterministic_hash_to(tcx, &mut hasher);

// Include the main item-type. Note that, in this case, the
// assertions about `needs_subst` may not hold, but this item-type
// ought to be the same for every reference anyway.
let mut hasher = ty::util::TypeIdHasher::new(tcx, hasher);
assert!(!item_type.has_erasable_regions());
hasher.visit_ty(item_type);

Expand All @@ -172,18 +173,15 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
substs.visit_with(&mut hasher);
}
});
fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String {
let output = symbol_hasher.result_bytes();
// 64 bits should be enough to avoid collisions.
output[.. 8].to_hex()
}

format!("h{}", truncated_hash_result(&mut hash_state))
// 64 bits should be enough to avoid collisions.
let output = hash_state.result_bytes();
format!("h{}", output[..8].to_hex())
}

impl<'a, 'tcx> Instance<'tcx> {
pub fn symbol_name(self, scx: &SharedCrateContext<'a, 'tcx>) -> String {
let Instance { def: def_id, ref substs } = self;
let Instance { def: def_id, substs } = self;

debug!("symbol_name(def_id={:?}, substs={:?})",
def_id, substs);
Expand Down Expand Up @@ -278,7 +276,7 @@ impl<'a, 'tcx> Instance<'tcx> {
scx.tcx().push_item_path(&mut buffer, def_id);
});

mangle(buffer.names.into_iter(), Some(&hash[..]))
mangle(buffer.names.into_iter(), &hash)
}
}

Expand Down Expand Up @@ -307,23 +305,7 @@ pub fn exported_name_from_type_and_prefix<'a, 'tcx>(scx: &SharedCrateContext<'a,
};
let hash = get_symbol_hash(scx, &empty_def_path, t, None);
let path = [token::intern_and_get_ident(prefix)];
mangle(path.iter().cloned(), Some(&hash[..]))
}

/// Only symbols that are invisible outside their compilation unit should use a
/// name generated by this function.
pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
suffix: &str)
-> String {
let path = [token::intern(&t.to_string()).as_str(),
gensym_name(suffix).as_str()];
let def_path = DefPath {
data: vec![],
krate: LOCAL_CRATE,
};
let hash = get_symbol_hash(ccx.shared(), &def_path, t, None);
mangle(path.iter().cloned(), Some(&hash[..]))
mangle(path.iter().cloned(), &hash)
}

// Name sanitation. LLVM will happily accept identifiers with weird names, but
Expand Down Expand Up @@ -376,7 +358,7 @@ pub fn sanitize(s: &str) -> String {
return result;
}

pub fn mangle<PI: Iterator<Item=InternedString>>(path: PI, hash: Option<&str>) -> String {
fn mangle<PI: Iterator<Item=InternedString>>(path: PI, hash: &str) -> String {
// Follow C++ namespace-mangling style, see
// http://en.wikipedia.org/wiki/Name_mangling for more info.
//
Expand All @@ -403,9 +385,7 @@ pub fn mangle<PI: Iterator<Item=InternedString>>(path: PI, hash: Option<&str>) -
push(&mut n, &data);
}

if let Some(s) = hash {
push(&mut n, s)
}
push(&mut n, hash);

n.push('E'); // End name-sequence.
n
Expand Down
29 changes: 13 additions & 16 deletions src/librustc_trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
pub use self::CalleeData::*;

use arena::TypedArena;
use back::symbol_names;
use llvm::{self, ValueRef, get_params};
use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs;
Expand Down Expand Up @@ -133,34 +132,36 @@ impl<'tcx> Callee<'tcx> {
let trait_ref = tcx.normalize_associated_type(&ty::Binder(trait_ref));
match common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref) {
traits::VtableImpl(vtable_impl) => {
let impl_did = vtable_impl.impl_def_id;
let mname = tcx.item_name(def_id);
// create a concatenated set of substitutions which includes
// those from the impl and those from the method:
let mth = meth::get_impl_method(tcx, substs, impl_did, vtable_impl.substs, mname);
let name = tcx.item_name(def_id);
let (def_id, substs) = traits::find_method(tcx, name, substs, &vtable_impl);

// Translate the function, bypassing Callee::def.
// That is because default methods have the same ID as the
// trait method used to look up the impl method that ended
// up here, so calling Callee::def would infinitely recurse.
let (llfn, ty) = get_fn(ccx, mth.method.def_id, mth.substs);
let (llfn, ty) = get_fn(ccx, def_id, substs);
Callee::ptr(llfn, ty)
}
traits::VtableClosure(vtable_closure) => {
// The substitutions should have no type parameters remaining
// after passing through fulfill_obligation
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
let instance = Instance::new(def_id, substs);
let llfn = closure::trans_closure_method(ccx,
vtable_closure.closure_def_id,
vtable_closure.substs,
instance,
trait_closure_kind);

let method_ty = def_ty(ccx.shared(), def_id, substs);
Callee::ptr(llfn, method_ty)
}
traits::VtableFnPointer(vtable_fn_pointer) => {
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, vtable_fn_pointer.fn_ty);
let instance = Instance::new(def_id, substs);
let llfn = trans_fn_pointer_shim(ccx, instance,
trait_closure_kind,
vtable_fn_pointer.fn_ty);

let method_ty = def_ty(ccx.shared(), def_id, substs);
Callee::ptr(llfn, method_ty)
Expand Down Expand Up @@ -217,9 +218,7 @@ impl<'tcx> Callee<'tcx> {
pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
match self.data {
Fn(llfn) => llfn,
Virtual(idx) => {
meth::trans_object_shim(ccx, self.ty, idx)
}
Virtual(_) => meth::trans_object_shim(ccx, self),
NamedTupleConstructor(disr) => match self.ty.sty {
ty::TyFnDef(def_id, substs, _) => {
let instance = Instance::new(def_id, substs);
Expand Down Expand Up @@ -264,8 +263,9 @@ fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>,
/// ```
///
/// but for the bare function type given.
pub fn trans_fn_pointer_shim<'a, 'tcx>(
fn trans_fn_pointer_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
method_instance: Instance<'tcx>,
closure_kind: ty::ClosureKind,
bare_fn_ty: Ty<'tcx>)
-> ValueRef
Expand Down Expand Up @@ -345,10 +345,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
debug!("tuple_fn_ty: {:?}", tuple_fn_ty);

//
let function_name =
symbol_names::internal_name_from_type_and_suffix(ccx,
bare_fn_ty,
"fn_pointer_shim");
let function_name = method_instance.symbol_name(ccx.shared());
let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
attributes::set_frame_pointer_elimination(ccx, llfn);
//
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_trans/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// except according to those terms.

use arena::TypedArena;
use back::symbol_names;
use llvm::{self, ValueRef, get_params};
use rustc::hir::def_id::DefId;
use abi::{Abi, FnType};
Expand Down Expand Up @@ -152,6 +151,7 @@ pub fn trans_closure_body_via_mir<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
closure_def_id: DefId,
substs: ty::ClosureSubsts<'tcx>,
method_instance: Instance<'tcx>,
trait_closure_kind: ty::ClosureKind)
-> ValueRef
{
Expand Down Expand Up @@ -199,7 +199,7 @@ pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
//
// These are both the same at trans time.
trans_fn_once_adapter_shim(ccx, closure_def_id, substs, llfn)
trans_fn_once_adapter_shim(ccx, closure_def_id, substs, method_instance, llfn)
}
_ => {
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
Expand All @@ -213,6 +213,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
closure_def_id: DefId,
substs: ty::ClosureSubsts<'tcx>,
method_instance: Instance<'tcx>,
llreffn: ValueRef)
-> ValueRef
{
Expand Down Expand Up @@ -255,8 +256,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
}));

// Create the by-value helper.
let function_name =
symbol_names::internal_name_from_type_and_suffix(ccx, llonce_fn_ty, "once_shim");
let function_name = method_instance.symbol_name(ccx.shared());
let lloncefn = declare::declare_fn(ccx, &function_name, llonce_fn_ty);
attributes::set_frame_pointer_elimination(ccx, lloncefn);

Expand Down
Loading

0 comments on commit ade79d7

Please sign in to comment.