Skip to content

Commit

Permalink
Account for diamond inheritance in vtable layout.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Regueiro committed May 5, 2020
1 parent b183789 commit 29de923
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 71 deletions.
9 changes: 5 additions & 4 deletions src/librustc_codegen_ssa/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::middle::cstore::{self, LinkagePreference};
use rustc_middle::middle::lang_items;
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
use rustc_middle::traits::Vtable;
use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_session::cgu_reuse_tracker::CguReuse;
Expand Down Expand Up @@ -166,7 +166,8 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>(
let target_ptr = if let Some(target_trait_ref) = target_data.principal() {
// Find the offset of the supertrait's vtable within the subtrait (parent) vtable.
let trait_ref = target_trait_ref.with_self_ty(tcx, source);
let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref));
let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref))
.unwrap_or_else(|| bug!("unsized_info: no vtable found"));
let offset = match vtable {
Vtable::VtableObject(ref data) => data.vtable_base,
// HACK(alexreg): slightly dubious solution to ICE in
Expand Down Expand Up @@ -257,7 +258,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
op: OperandRef<'tcx, Bx::Value>,
dst: TyLayout<'tcx>,
dst: TyAndLayout<'tcx>,
) -> OperandValue<Bx::Value> {
assert!(bx.cx().is_backend_scalar_pair(dst));
let src = op.layout;
Expand Down Expand Up @@ -341,7 +342,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
(&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
let src_op = bx.load_operand(src);
let dst_op = coerce_ptr_unsized(bx, src_op, dst.layout);
dst_op.store(bx, dst);s
dst_op.store(bx, dst);
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_infer/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1681,10 +1681,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
exp_found: &ty::error::ExpectedFound<DefId>,
diag: &mut DiagnosticBuilder<'tcx>,
) {
use rustc::ty::{Binder, TraitRef};
use ty::{Binder, TraitRef};

let trait_ref = Binder::bind(TraitRef::identity(self.tcx, exp_found.found));
let supertraits = crate::traits::supertraits(self.tcx, trait_ref);
let supertraits = crate::traits::util::supertraits(self.tcx, trait_ref);
if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) {
diag.note("add `#![feature(trait_upcasting)]` to the crate attributes to enable");
}
Expand Down
38 changes: 34 additions & 4 deletions src/librustc_infer/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ impl<T: AsRef<ty::Predicate<'tcx>>> Extend<T> for PredicateSet<'tcx> {
pub struct Elaborator<'tcx> {
stack: Vec<PredicateObligation<'tcx>>,
visited: PredicateSet<'tcx>,
/// `true` to allow the same predicate to appear more than once within the sequence.
allow_repetitions: bool,
}

pub fn elaborate_trait_ref<'tcx>(
Expand All @@ -108,6 +110,17 @@ pub fn elaborate_trait_refs<'tcx>(
elaborate_predicates(tcx, predicates)
}

pub fn elaborate_trait_ref_with_repetitions<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Elaborator<'tcx> {
Elaborator {
stack: vec![predicate_obligation(trait_ref.without_const().to_predicate(), None)],
visited: PredicateSet::new(tcx),
allow_repetitions: true,
}
}

pub fn elaborate_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
Expand All @@ -123,7 +136,11 @@ pub fn elaborate_obligations<'tcx>(
) -> Elaborator<'tcx> {
let mut visited = PredicateSet::new(tcx);
obligations.retain(|obligation| visited.insert(&obligation.predicate));
Elaborator { stack: obligations, visited }
Elaborator {
stack: obligations,
visited,
allow_repetitions: false,
}
}

fn predicate_obligation<'tcx>(
Expand All @@ -134,7 +151,12 @@ fn predicate_obligation<'tcx>(
if let Some(span) = span {
cause.span = span;
}
Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate }
Obligation {
cause,
param_env: ty::ParamEnv::empty(),
recursion_depth: 0,
predicate,
}
}

impl Elaborator<'tcx> {
Expand All @@ -144,6 +166,7 @@ impl Elaborator<'tcx> {

fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
let tcx = self.visited.tcx;
let allow_repetitions = self.allow_repetitions;
match obligation.predicate {
ty::Predicate::Trait(ref data, _) => {
// Get predicates declared on the trait.
Expand All @@ -162,7 +185,7 @@ impl Elaborator<'tcx> {
// cases. One common case is when people define
// `trait Sized: Sized { }` rather than `trait Sized { }`.
let visited = &mut self.visited;
let obligations = obligations.filter(|o| visited.insert(&o.predicate));
let obligations = obligations.filter(|o| allow_repetitions || visited.insert(&o.predicate));

self.stack.extend(obligations);
}
Expand Down Expand Up @@ -245,7 +268,7 @@ impl Elaborator<'tcx> {
None
}
})
.filter(|p| visited.insert(p))
.filter(|p| allow_repetitions || visited.insert(p))
.map(|p| predicate_obligation(p, None)),
);
}
Expand Down Expand Up @@ -284,6 +307,13 @@ pub fn supertraits<'tcx>(
elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
}

pub fn supertraits_with_repetitions<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Supertraits<'tcx> {
elaborate_trait_ref_with_repetitions(tcx, trait_ref).filter_to_traits()
}

pub fn transitive_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ptr_size * (
methods
.iter()
.map(|l| u64::try_from(methods.len()).unwrap().checked_add(3).unwrap())
.map(|l| u64::try_from(l.len()).unwrap().checked_add(3).unwrap())
.sum()
),
ptr_align,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_mir/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,8 @@ fn create_mono_items_for_vtable_methods<'tcx>(
// Walk all methods of the trait, including those of its supertraits.
let methods = tcx.vtable_methods(poly_trait_ref);
let methods = methods
.into_iter()
.flat_map(|s| *s)
.cloned()
.filter_map(|method| method)
.map(|(def_id, substs)| {
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_session/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,7 @@ impl Session {
self.opts.debugging_opts.asm_comments
}
pub fn verify_llvm_ir(&self) -> bool {
(self.opts.debugging_opts.verify_llvm_ir
|| cfg!(always_verify_llvm_ir))
&& !self.opts.debugging_opts.no_verify_llvm_ir
self.opts.debugging_opts.verify_llvm_ir || cfg!(always_verify_llvm_ir)
}
pub fn borrowck_stats(&self) -> bool {
self.opts.debugging_opts.borrowck_stats
Expand Down
90 changes: 46 additions & 44 deletions src/librustc_trait_selection/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ pub use self::util::{
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
};
pub use self::util::{
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,
supertraits, supertraits_with_repetitions, supertrait_def_ids, transitive_bounds, Supertraits,
SupertraitDefIds,
};

pub use rustc_infer::traits::*;
Expand Down Expand Up @@ -471,51 +472,52 @@ fn vtable_methods<'tcx>(
) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] {
debug!("vtable_methods({:?})", trait_ref);

tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).map(move |trait_ref| {
let trait_methods = tcx
.associated_items(trait_ref.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Fn);

// Now, list each method's `DefId` and `InternalSubsts` (for within its trait).
// If the method can never be called from this object, produce `None`.
&*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| {
debug!("vtable_methods: trait_method={:?}", trait_method);
let def_id = trait_method.def_id;

// Some methods cannot be called on an object; skip those.
if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) {
debug!("vtable_methods: not vtable safe");
return None;
}
tcx.arena.alloc_from_iter(supertraits_with_repetitions(tcx, trait_ref)
.map(move |trait_ref| {
let trait_methods = tcx
.associated_items(trait_ref.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Fn);

// Now, list each method's `DefId` and `InternalSubsts` (for within its trait).
// If the method can never be called from this object, produce `None`.
&*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| {
debug!("vtable_methods: trait_method={:?}", trait_method);
let def_id = trait_method.def_id;

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

// The method may have some early-bound lifetimes; add regions for those.
let substs = trait_ref.map_bound(|trait_ref| {
InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
trait_ref.substs[param.index as usize]
}
})
});

// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
let substs =
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs);

// 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 codegen it, in that case (see #23435).
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
if !normalize_and_test_predicates(tcx, predicates.predicates) {
debug!("vtable_methods: predicates do not hold");
return None;
}
// The method may have some early-bound lifetimes; add regions for those.
let substs = trait_ref.map_bound(|trait_ref| {
InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
trait_ref.substs[param.index as usize]
}
})
});

// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
let substs =
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs);

// 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 codegen it, in that case (see #23435).
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
if !normalize_and_test_predicates(tcx, predicates.predicates) {
debug!("vtable_methods: predicates do not hold");
return None;
}

Some((def_id, substs))
Some((def_id, substs))
}))
}))
}
Expand Down
27 changes: 14 additions & 13 deletions src/librustc_trait_selection/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2696,26 +2696,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// where we can unify, because otherwise select would have
// reported an ambiguity. (When we do find a match, also
// record it for later.)
let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| {
match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) {
Ok(obligations) => {
upcast_trait_ref = Some(t);
nested.extend(obligations);
false
}
Err(_) => true,
}
});
let nonmatching = util::supertraits_with_repetitions(tcx, poly_trait_ref)
.take_while(
|&trait_ref| match self.infcx.commit_if_ok(
|_| self.match_poly_trait_ref(obligation, trait_ref)
) {
Ok(obligations) => {
upcast_trait_ref = Some(trait_ref);
nested.extend(obligations);
false
}
Err(_) => true,
},
);

// Additionally, for each of the non-matching predicates that
// we pass over, we sum up the set of number of vtable
// entries, so that we can compute the offset for the selected
// trait.
vtable_base = nonmatching
// Skip 3 entries in vtable per supertrait for `(drop, size, align)` metadata.
.map(|trait_ref|
u64::try_from(tcx.count_own_vtable_entries(trait_ref)).unwrap().checked_add(3).unwrap()
)
.map(|trait_ref| util::count_own_vtable_entries(tcx, trait_ref).checked_add(3).unwrap())
.sum();
}

Expand Down

0 comments on commit 29de923

Please sign in to comment.