Auto merge of #123106 - maurer:cfi-closures, r=compiler-errors
CFI: Abstract Closures and Coroutines This will abstract coroutines in a moment, it's just abstracting closures for now to show `@rcvalle` This uses the same principal as the methods on traits - figure out the `dyn` type representing the fn trait, instantiate it, and attach that alias set. We're essentially just computing how we would be called in a dynamic context, and attaching that.
This commit is contained in:
commit
70714e38f2
5 changed files with 241 additions and 59 deletions
|
@ -10,6 +10,7 @@
|
|||
use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{
|
||||
|
@ -641,9 +642,7 @@ fn encode_ty<'tcx>(
|
|||
}
|
||||
|
||||
// Function types
|
||||
ty::FnDef(def_id, args)
|
||||
| ty::Closure(def_id, args)
|
||||
| ty::CoroutineClosure(def_id, args) => {
|
||||
ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
let mut s = String::new();
|
||||
|
@ -654,6 +653,18 @@ fn encode_ty<'tcx>(
|
|||
typeid.push_str(&s);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
let mut s = String::new();
|
||||
let name = encode_ty_name(tcx, *def_id);
|
||||
let _ = write!(s, "u{}{}", name.len(), &name);
|
||||
let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
|
||||
s.push_str(&encode_args(tcx, parent_args, dict, options));
|
||||
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
|
||||
typeid.push_str(&s);
|
||||
}
|
||||
|
||||
ty::Coroutine(def_id, args, ..) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
|
@ -1151,43 +1162,91 @@ pub fn typeid_for_instance<'tcx>(
|
|||
};
|
||||
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
|
||||
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
|
||||
} else if let ty::InstanceDef::VTableShim(def_id) = instance.def
|
||||
&& let Some(trait_id) = tcx.trait_of_item(def_id)
|
||||
{
|
||||
// VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable,
|
||||
// as the caller will not know the concrete Self.
|
||||
let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
}
|
||||
|
||||
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE)
|
||||
&& let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
|
||||
{
|
||||
let impl_method = tcx.associated_item(instance.def_id());
|
||||
let method_id = impl_method
|
||||
.trait_item_def_id
|
||||
.expect("Part of a trait implementation, but not linked to the def_id?");
|
||||
let trait_method = tcx.associated_item(method_id);
|
||||
let trait_id = trait_ref.skip_binder().def_id;
|
||||
if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
|
||||
&& tcx.object_safety_violations(trait_id).is_empty()
|
||||
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE) {
|
||||
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
|
||||
{
|
||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
||||
// implementatation will not. We need to walk back to the more general trait method
|
||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||
instance.args,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
trait_ref,
|
||||
);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
let impl_method = tcx.associated_item(instance.def_id());
|
||||
let method_id = impl_method
|
||||
.trait_item_def_id
|
||||
.expect("Part of a trait implementation, but not linked to the def_id?");
|
||||
let trait_method = tcx.associated_item(method_id);
|
||||
let trait_id = trait_ref.skip_binder().def_id;
|
||||
if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
|
||||
&& tcx.object_safety_violations(trait_id).is_empty()
|
||||
{
|
||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
||||
// implementatation will not. We need to walk back to the more general trait method
|
||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||
instance.args,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
trait_ref,
|
||||
);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
|
||||
// At the call site, any call to this concrete function through a vtable will be
|
||||
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
|
||||
// original method id, and we've recovered the trait arguments, we can make the callee
|
||||
// instance we're computing the alias set for match the caller instance.
|
||||
//
|
||||
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
|
||||
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
|
||||
// based on which vtables we are putting this method into, as there will be more than one
|
||||
// index value when supertraits are involved.
|
||||
instance.def = ty::InstanceDef::Virtual(method_id, 0);
|
||||
let abstract_trait_args =
|
||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
|
||||
// At the call site, any call to this concrete function through a vtable will be
|
||||
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
|
||||
// original method id, and we've recovered the trait arguments, we can make the callee
|
||||
// instance we're computing the alias set for match the caller instance.
|
||||
//
|
||||
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
|
||||
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
|
||||
// based on which vtables we are putting this method into, as there will be more than one
|
||||
// index value when supertraits are involved.
|
||||
instance.def = ty::InstanceDef::Virtual(method_id, 0);
|
||||
let abstract_trait_args =
|
||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
|
||||
}
|
||||
} else if tcx.is_closure_like(instance.def_id()) {
|
||||
// We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
|
||||
// instantiate it, and take the type of its only method as our own.
|
||||
let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
|
||||
let (trait_id, inputs) = match closure_ty.kind() {
|
||||
ty::Closure(..) => {
|
||||
let closure_args = instance.args.as_closure();
|
||||
let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
|
||||
let tuple_args =
|
||||
tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
|
||||
(trait_id, tuple_args)
|
||||
}
|
||||
ty::Coroutine(..) => (
|
||||
tcx.require_lang_item(LangItem::Coroutine, None),
|
||||
instance.args.as_coroutine().resume_ty(),
|
||||
),
|
||||
ty::CoroutineClosure(..) => (
|
||||
tcx.require_lang_item(LangItem::FnOnce, None),
|
||||
tcx.instantiate_bound_regions_with_erased(
|
||||
instance.args.as_coroutine_closure().coroutine_closure_sig(),
|
||||
)
|
||||
.tupled_inputs_ty,
|
||||
),
|
||||
x => bug!("Unexpected type kind for closure-like: {x:?}"),
|
||||
};
|
||||
let trait_ref = ty::TraitRef::new(tcx, trait_id, [closure_ty, inputs]);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
// There should be exactly one method on this trait, and it should be the one we're
|
||||
// defining.
|
||||
let call = tcx
|
||||
.associated_items(trait_id)
|
||||
.in_definition_order()
|
||||
.find(|it| it.kind == ty::AssocKind::Fn)
|
||||
.expect("No call-family function on closure-like Fn trait?")
|
||||
.def_id;
|
||||
|
||||
instance.def = ty::InstanceDef::Virtual(call, 0);
|
||||
instance.args = abstract_args;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue