Skip to content

Commit e149471

Browse files
committed
CFI: Support arbitrary receivers
Previously, we only rewrote `&self` and `&mut self` receivers. By instantiating the method from the trait definition, we can make this work work with arbitrary legal receivers instead.
1 parent c71a011 commit e149471

File tree

3 files changed

+62
-39
lines changed

3 files changed

+62
-39
lines changed

compiler/rustc_symbol_mangling/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
9191
#![doc(rust_logo)]
9292
#![feature(rustdoc_internals)]
93+
#![feature(let_chains)]
9394
#![allow(internal_features)]
9495

9596
#[macro_use]

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,51 +1115,31 @@ pub fn typeid_for_instance<'tcx>(
11151115
instance.args = strip_receiver_auto(tcx, instance.args)
11161116
}
11171117

1118+
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
1119+
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
1120+
{
1121+
// Trait methods will have a Self polymorphic parameter, where the concreteized
1122+
// implementatation will not. We need to walk back to the more general trait method
1123+
let trait_ref = trait_ref.instantiate(tcx, instance.args);
1124+
let invoke_ty = tcx.trait_object_ty(ty::Binder::dummy(trait_ref));
1125+
let method_id = tcx
1126+
.impl_item_implementor_ids(impl_id)
1127+
.items()
1128+
.filter_map(|(trait_method, impl_method)| {
1129+
(*impl_method == instance.def_id()).then_some(*trait_method)
1130+
})
1131+
.min()
1132+
.unwrap();
1133+
instance.def = ty::InstanceDef::Virtual(method_id, 0);
1134+
instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
1135+
}
1136+
11181137
let fn_abi = tcx
11191138
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
11201139
.unwrap_or_else(|instance| {
11211140
bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
11221141
});
11231142

1124-
// If this instance is a method and self is a reference, get the impl it belongs to
1125-
let impl_def_id = tcx.impl_of_method(instance.def_id());
1126-
if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() {
1127-
// If this impl is not an inherent impl, get the trait it implements
1128-
if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) {
1129-
// Transform the concrete self into a reference to a trait object
1130-
let existential_predicate = trait_ref.map_bound(|trait_ref| {
1131-
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
1132-
tcx, trait_ref,
1133-
))
1134-
});
1135-
let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(
1136-
existential_predicate.skip_binder(),
1137-
)]);
1138-
// Is the concrete self mutable?
1139-
let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
1140-
Ty::new_mut_ref(
1141-
tcx,
1142-
tcx.lifetimes.re_erased,
1143-
Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
1144-
)
1145-
} else {
1146-
Ty::new_imm_ref(
1147-
tcx,
1148-
tcx.lifetimes.re_erased,
1149-
Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
1150-
)
1151-
};
1152-
1153-
// Replace the concrete self in an fn_abi clone by the reference to a trait object
1154-
let mut fn_abi = fn_abi.clone();
1155-
// HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the
1156-
// other fields are never used.
1157-
fn_abi.args[0].layout.ty = self_ty;
1158-
1159-
return typeid_for_fnabi(tcx, &fn_abi, options);
1160-
}
1161-
}
1162-
11631143
typeid_for_fnabi(tcx, fn_abi, options)
11641144
}
11651145

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Check that more complex receivers work:
2+
// * Arc<dyn Foo> as for custom receivers
3+
// * &dyn Bar<T=Baz> for type constraints
4+
5+
//@ needs-sanitizer-cfi
6+
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
7+
//@ only-linux
8+
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
9+
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
10+
//@ run-pass
11+
12+
use std::sync::Arc;
13+
14+
trait Foo {
15+
fn foo(self: Arc<Self>);
16+
}
17+
18+
struct FooImpl;
19+
20+
impl Foo for FooImpl {
21+
fn foo(self: Arc<Self>) {}
22+
}
23+
24+
trait Bar {
25+
type T;
26+
fn bar(&self) -> Self::T;
27+
}
28+
29+
struct BarImpl;
30+
31+
impl Bar for BarImpl {
32+
type T = i32;
33+
fn bar(&self) -> Self::T { 7 }
34+
}
35+
36+
fn main() {
37+
let foo: Arc<dyn Foo> = Arc::new(FooImpl);
38+
foo.foo();
39+
40+
let bar: &dyn Bar<T=i32> = &BarImpl;
41+
assert_eq!(bar.bar(), 7);
42+
}

0 commit comments

Comments
 (0)