CFI: Support provided methods on traits
Provided methods currently don't get type erasure performed on them because they are not in an `impl` block. If we are instantiating a method that is an associated item, but *not* in an impl block, treat it as a provided method instead.
This commit is contained in:
parent
08cdc2fa1a
commit
2abdc4e98c
@ -9,10 +9,10 @@
|
|||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable,
|
self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty,
|
||||||
TypeVisitableExt, UintTy,
|
TyCtxt, TypeFoldable, TypeVisitableExt, UintTy,
|
||||||
};
|
};
|
||||||
use rustc_span::sym;
|
use rustc_span::{def_id::DefId, sym};
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
@ -360,18 +360,7 @@ pub fn transform_instance<'tcx>(
|
|||||||
if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
|
if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
|
||||||
// Perform type erasure for calls on trait objects by transforming self into a trait object
|
// Perform type erasure for calls on trait objects by transforming self into a trait object
|
||||||
// of the trait that defines the method.
|
// of the trait that defines the method.
|
||||||
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) {
|
||||||
&& 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.is_object_safe(trait_id)
|
|
||||||
{
|
|
||||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
// 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
|
// implementatation will not. We need to walk back to the more general trait method
|
||||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||||
@ -393,8 +382,7 @@ pub fn transform_instance<'tcx>(
|
|||||||
instance.def = ty::InstanceKind::Virtual(method_id, 0);
|
instance.def = ty::InstanceKind::Virtual(method_id, 0);
|
||||||
let abstract_trait_args =
|
let abstract_trait_args =
|
||||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
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);
|
instance.args = instance.args.rebase_onto(tcx, ancestor, abstract_trait_args);
|
||||||
}
|
|
||||||
} else if tcx.is_closure_like(instance.def_id()) {
|
} 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,
|
// 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.
|
// instantiate it, and take the type of its only method as our own.
|
||||||
@ -452,3 +440,36 @@ pub fn transform_instance<'tcx>(
|
|||||||
|
|
||||||
instance
|
instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn implemented_method<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
instance: Instance<'tcx>,
|
||||||
|
) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> {
|
||||||
|
let trait_ref;
|
||||||
|
let method_id;
|
||||||
|
let trait_id;
|
||||||
|
let trait_method;
|
||||||
|
let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) {
|
||||||
|
// Implementation in an `impl` block
|
||||||
|
trait_ref = tcx.impl_trait_ref(impl_id)?;
|
||||||
|
let impl_method = tcx.associated_item(instance.def_id());
|
||||||
|
method_id = impl_method.trait_item_def_id?;
|
||||||
|
trait_method = tcx.associated_item(method_id);
|
||||||
|
trait_id = trait_ref.skip_binder().def_id;
|
||||||
|
impl_id
|
||||||
|
} else if let InstanceKind::Item(def_id) = instance.def
|
||||||
|
&& let Some(trait_method_bound) = tcx.opt_associated_item(def_id)
|
||||||
|
{
|
||||||
|
// Provided method in a `trait` block
|
||||||
|
trait_method = trait_method_bound;
|
||||||
|
method_id = instance.def_id();
|
||||||
|
trait_id = tcx.trait_of_item(method_id)?;
|
||||||
|
trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args));
|
||||||
|
trait_id
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let vtable_possible =
|
||||||
|
traits::is_vtable_safe_method(tcx, trait_id, trait_method) && tcx.is_object_safe(trait_id);
|
||||||
|
vtable_possible.then_some((trait_ref, method_id, ancestor))
|
||||||
|
}
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
trait Parent1 {
|
trait Parent1 {
|
||||||
type P1;
|
type P1;
|
||||||
fn p1(&self) -> Self::P1;
|
fn p1(&self) -> Self::P1;
|
||||||
|
fn d(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Parent2 {
|
trait Parent2 {
|
||||||
@ -60,14 +63,17 @@ fn main() {
|
|||||||
x.c();
|
x.c();
|
||||||
x.p1();
|
x.p1();
|
||||||
x.p2();
|
x.p2();
|
||||||
|
x.d();
|
||||||
// Parents can be created and access their methods.
|
// Parents can be created and access their methods.
|
||||||
let y = &Foo as &dyn Parent1<P1=u16>;
|
let y = &Foo as &dyn Parent1<P1=u16>;
|
||||||
y.p1();
|
y.p1();
|
||||||
|
y.d();
|
||||||
let z = &Foo as &dyn Parent2<P2=u32>;
|
let z = &Foo as &dyn Parent2<P2=u32>;
|
||||||
z.p2();
|
z.p2();
|
||||||
// Trait upcasting works
|
// Trait upcasting works
|
||||||
let x1 = x as &dyn Parent1<P1=u16>;
|
let x1 = x as &dyn Parent1<P1=u16>;
|
||||||
x1.p1();
|
x1.p1();
|
||||||
|
x1.d();
|
||||||
let x2 = x as &dyn Parent2<P2=u32>;
|
let x2 = x as &dyn Parent2<P2=u32>;
|
||||||
x2.p2();
|
x2.p2();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user