Skip to content

Commit 4600c4a

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 fc98aa5 commit 4600c4a

File tree

5 files changed

+114
-39
lines changed

5 files changed

+114
-39
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4317,6 +4317,7 @@ dependencies = [
43174317
"rustc_session",
43184318
"rustc_span",
43194319
"rustc_target",
4320+
"rustc_trait_selection",
43204321
"tracing",
43214322
"twox-hash",
43224323
]

compiler/rustc_symbol_mangling/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1919
rustc_target = { path = "../rustc_target" }
2020
rustc_data_structures = { path = "../rustc_data_structures" }
2121
rustc_session = { path = "../rustc_session" }
22+
rustc_trait_selection = { path = "../rustc_trait_selection" }
2223
rustc_macros = { path = "../rustc_macros" }
2324
rustc_errors = { path = "../rustc_errors" }

compiler/rustc_symbol_mangling/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#![allow(rustc::potential_query_instability)]
9494
#![deny(rustc::untranslatable_diagnostic)]
9595
#![deny(rustc::diagnostic_outside_of_impl)]
96+
#![feature(let_chains)]
9697

9798
#[macro_use]
9899
extern crate rustc_middle;

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_data_structures::base_n;
1111
use rustc_data_structures::fx::FxHashMap;
1212
use rustc_hir as hir;
1313
use rustc_middle::ty::layout::IntegerExt;
14+
use rustc_middle::ty::TypeVisitableExt;
1415
use rustc_middle::ty::{
1516
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
1617
TermKind, Ty, TyCtxt, UintTy,
@@ -21,7 +22,9 @@ use rustc_span::sym;
2122
use rustc_target::abi::call::{Conv, FnAbi, PassMode};
2223
use rustc_target::abi::Integer;
2324
use rustc_target::spec::abi::Abi;
25+
use rustc_trait_selection::traits;
2426
use std::fmt::Write as _;
27+
use std::iter;
2528

2629
use crate::typeid::TypeIdOptions;
2730

@@ -1110,51 +1113,45 @@ pub fn typeid_for_instance<'tcx>(
11101113
instance.args = strip_receiver_auto(tcx, instance.args)
11111114
}
11121115

1116+
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
1117+
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
1118+
{
1119+
let impl_method = tcx.associated_item(instance.def_id());
1120+
let method_id = impl_method
1121+
.trait_item_def_id
1122+
.expect("Part of a trait implementation, but not linked to the def_id?");
1123+
let trait_method = tcx.associated_item(method_id);
1124+
if traits::is_vtable_safe_method(tcx, trait_ref.skip_binder().def_id, trait_method) {
1125+
// Trait methods will have a Self polymorphic parameter, where the concreteized
1126+
// implementatation will not. We need to walk back to the more general trait method
1127+
let trait_ref = tcx.normalize_erasing_regions(
1128+
ty::ParamEnv::reveal_all(),
1129+
trait_ref.instantiate(tcx, instance.args),
1130+
);
1131+
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
1132+
1133+
// At the call site, any call to this concrete function through a vtable will be
1134+
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
1135+
// original method id, and we've recovered the trait arguments, we can make the callee
1136+
// instance we're computing the alias set for match the caller instance.
1137+
//
1138+
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
1139+
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
1140+
// based on which vtables we are putting this method into, as there will be more than one
1141+
// index value when supertraits are involved.
1142+
instance.def = ty::InstanceDef::Virtual(method_id, 0);
1143+
let abstract_trait_args =
1144+
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
1145+
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
1146+
}
1147+
}
1148+
11131149
let fn_abi = tcx
11141150
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
11151151
.unwrap_or_else(|instance| {
11161152
bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
11171153
});
11181154

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

@@ -1180,3 +1177,36 @@ fn strip_receiver_auto<'tcx>(
11801177
};
11811178
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
11821179
}
1180+
1181+
fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
1182+
assert!(!poly_trait_ref.has_non_region_param());
1183+
let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
1184+
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
1185+
});
1186+
let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
1187+
.flat_map(|super_poly_trait_ref| {
1188+
tcx.associated_items(super_poly_trait_ref.def_id())
1189+
.in_definition_order()
1190+
.filter(|item| item.kind == ty::AssocKind::Type)
1191+
.map(move |assoc_ty| {
1192+
super_poly_trait_ref.map_bound(|super_trait_ref| {
1193+
let alias_ty = tcx.mk_alias_ty(assoc_ty.def_id, super_trait_ref.args);
1194+
let resolved = tcx.normalize_erasing_regions(
1195+
ty::ParamEnv::reveal_all(),
1196+
alias_ty.to_ty(tcx),
1197+
);
1198+
ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
1199+
def_id: assoc_ty.def_id,
1200+
args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
1201+
term: resolved.into(),
1202+
})
1203+
})
1204+
})
1205+
})
1206+
.collect();
1207+
assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
1208+
let preds = tcx.mk_poly_existential_predicates_from_iter(
1209+
iter::once(principal_pred).chain(assoc_preds.into_iter()),
1210+
);
1211+
Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
1212+
}
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)