Auto merge of #123012 - maurer:cfi-supertraits, r=compiler-errors

CFI: Support calling methods on supertraits

Automatically adjust `Virtual` calls to supertrait functions to use the supertrait's trait object type as the receiver rather than the child trait.

cc `@compiler-errors` - this is the next usage of `trait_object_ty` I intend to have, so I thought it might be relevant while reviewing the existing one.
This commit is contained in:
bors 2024-03-30 06:38:18 +00:00
commit 50e3d6296d
2 changed files with 87 additions and 10 deletions

View File

@ -1140,8 +1140,17 @@ pub fn typeid_for_instance<'tcx>(
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
instance.args = tcx.mk_args_trait(self_ty, List::empty());
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
instance.args = strip_receiver_auto(tcx, instance.args);
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
let upcast_ty = match tcx.trait_of_item(def_id) {
Some(trait_id) => trait_object_ty(
tcx,
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
),
// drop_in_place won't have a defining trait, skip the upcast
None => instance.args.type_at(0),
};
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
}
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE)
@ -1191,15 +1200,11 @@ pub fn typeid_for_instance<'tcx>(
typeid_for_fnabi(tcx, fn_abi, options)
}
fn strip_receiver_auto<'tcx>(
tcx: TyCtxt<'tcx>,
args: ty::GenericArgsRef<'tcx>,
) -> ty::GenericArgsRef<'tcx> {
let ty = args.type_at(0);
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
bug!("Tried to strip auto traits from non-dynamic type {ty}");
};
let new_rcvr = if preds.principal().is_some() {
if preds.principal().is_some() {
let filtered_preds =
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
@ -1210,8 +1215,7 @@ fn strip_receiver_auto<'tcx>(
// about it. This technically discards the knowledge that it was a type that was made
// into a trait object at some point, but that's not a lot.
tcx.types.unit
};
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
}
}
#[instrument(skip(tcx), ret)]

View File

@ -0,0 +1,73 @@
#![feature(trait_upcasting)]
// Check that super-traits are callable.
//@ revisions: cfi kcfi
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
//@ only-linux
//@ [cfi] needs-sanitizer-cfi
//@ [kcfi] needs-sanitizer-kcfi
//@ compile-flags: -C target-feature=-crt-static
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
//@ [cfi] compile-flags: -Z sanitizer=cfi
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
//@ run-pass
trait Parent1 {
type P1;
fn p1(&self) -> Self::P1;
}
trait Parent2 {
type P2;
fn p2(&self) -> Self::P2;
}
trait Child : Parent1 + Parent2 {
type C;
fn c(&self) -> Self::C;
}
struct Foo;
impl Parent1 for Foo {
type P1 = u16;
fn p1(&self) -> Self::P1 {
println!("p1");
1
}
}
impl Parent2 for Foo {
type P2 = u32;
fn p2(&self) -> Self::P2 {
println!("p2");
2
}
}
impl Child for Foo {
type C = u8;
fn c(&self) -> Self::C {
println!("c");
0
}
}
fn main() {
// Child can access its own methods and super methods.
let x = &Foo as &dyn Child<C=u8,P1=u16,P2=u32>;
x.c();
x.p1();
x.p2();
// Parents can be created and access their methods.
let y = &Foo as &dyn Parent1<P1=u16>;
y.p1();
let z = &Foo as &dyn Parent2<P2=u32>;
z.p2();
// Trait upcasting works
let x1 = x as &dyn Parent1<P1=u16>;
x1.p1();
let x2 = x as &dyn Parent2<P2=u32>;
x2.p2();
}