Skip to content

Commit 50e3d62

Browse files
committed
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.
2 parents 174d07b + d301f40 commit 50e3d62

File tree

2 files changed

+87
-10
lines changed

2 files changed

+87
-10
lines changed

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -1140,8 +1140,17 @@ pub fn typeid_for_instance<'tcx>(
11401140
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
11411141
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
11421142
instance.args = tcx.mk_args_trait(self_ty, List::empty());
1143-
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
1144-
instance.args = strip_receiver_auto(tcx, instance.args);
1143+
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
1144+
let upcast_ty = match tcx.trait_of_item(def_id) {
1145+
Some(trait_id) => trait_object_ty(
1146+
tcx,
1147+
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
1148+
),
1149+
// drop_in_place won't have a defining trait, skip the upcast
1150+
None => instance.args.type_at(0),
1151+
};
1152+
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
1153+
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
11451154
}
11461155

11471156
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE)
@@ -1191,15 +1200,11 @@ pub fn typeid_for_instance<'tcx>(
11911200
typeid_for_fnabi(tcx, fn_abi, options)
11921201
}
11931202

1194-
fn strip_receiver_auto<'tcx>(
1195-
tcx: TyCtxt<'tcx>,
1196-
args: ty::GenericArgsRef<'tcx>,
1197-
) -> ty::GenericArgsRef<'tcx> {
1198-
let ty = args.type_at(0);
1203+
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
11991204
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
12001205
bug!("Tried to strip auto traits from non-dynamic type {ty}");
12011206
};
1202-
let new_rcvr = if preds.principal().is_some() {
1207+
if preds.principal().is_some() {
12031208
let filtered_preds =
12041209
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
12051210
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
@@ -1210,8 +1215,7 @@ fn strip_receiver_auto<'tcx>(
12101215
// about it. This technically discards the knowledge that it was a type that was made
12111216
// into a trait object at some point, but that's not a lot.
12121217
tcx.types.unit
1213-
};
1214-
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
1218+
}
12151219
}
12161220

12171221
#[instrument(skip(tcx), ret)]

tests/ui/sanitizer/cfi-supertraits.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#![feature(trait_upcasting)]
2+
// Check that super-traits are callable.
3+
4+
//@ revisions: cfi kcfi
5+
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
6+
//@ only-linux
7+
//@ [cfi] needs-sanitizer-cfi
8+
//@ [kcfi] needs-sanitizer-kcfi
9+
//@ compile-flags: -C target-feature=-crt-static
10+
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
11+
//@ [cfi] compile-flags: -Z sanitizer=cfi
12+
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
13+
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
14+
//@ run-pass
15+
16+
trait Parent1 {
17+
type P1;
18+
fn p1(&self) -> Self::P1;
19+
}
20+
21+
trait Parent2 {
22+
type P2;
23+
fn p2(&self) -> Self::P2;
24+
}
25+
26+
trait Child : Parent1 + Parent2 {
27+
type C;
28+
fn c(&self) -> Self::C;
29+
}
30+
31+
struct Foo;
32+
33+
impl Parent1 for Foo {
34+
type P1 = u16;
35+
fn p1(&self) -> Self::P1 {
36+
println!("p1");
37+
1
38+
}
39+
}
40+
41+
impl Parent2 for Foo {
42+
type P2 = u32;
43+
fn p2(&self) -> Self::P2 {
44+
println!("p2");
45+
2
46+
}
47+
}
48+
49+
impl Child for Foo {
50+
type C = u8;
51+
fn c(&self) -> Self::C {
52+
println!("c");
53+
0
54+
}
55+
}
56+
57+
fn main() {
58+
// Child can access its own methods and super methods.
59+
let x = &Foo as &dyn Child<C=u8,P1=u16,P2=u32>;
60+
x.c();
61+
x.p1();
62+
x.p2();
63+
// Parents can be created and access their methods.
64+
let y = &Foo as &dyn Parent1<P1=u16>;
65+
y.p1();
66+
let z = &Foo as &dyn Parent2<P2=u32>;
67+
z.p2();
68+
// Trait upcasting works
69+
let x1 = x as &dyn Parent1<P1=u16>;
70+
x1.p1();
71+
let x2 = x as &dyn Parent2<P2=u32>;
72+
x2.p2();
73+
}

0 commit comments

Comments
 (0)