Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_middle::bug;
use rustc_middle::ty::{
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy,
List, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
UintTy,
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef,
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
};
use rustc_span::def_id::DefId;
use rustc_span::{DUMMY_SP, sym};
Expand Down Expand Up @@ -459,6 +458,30 @@ pub(crate) fn transform_instance<'tcx>(
instance
}

fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
match instance.def {
ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
tcx.opt_associated_item(def_id).map(|item| item.def_id)
}
_ => None,
}
}

/// Determines if an instance represents a trait method implementation and returns the necessary
/// information for type erasure.
///
/// This function handles two main cases:
///
/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation
/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait
/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of
/// the associated item, which points to the original trait method definition.
///
/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a
/// default implementation provided in the trait definition itself or a synthetic shim, it uses
/// the instance's own `def_id` as the method ID and determines the trait ID from the associated
/// item.
///
fn implemented_method<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
Expand All @@ -476,10 +499,11 @@ fn implemented_method<'tcx>(
trait_id = trait_ref.skip_binder().def_id;
impl_id
} else if let AssocContainer::Trait = assoc.container
&& let InstanceKind::Item(def_id) = instance.def
&& let Some(trait_method_def_id) = default_or_shim(tcx, instance)
{
// Provided method in a `trait` block or a synthetic `shim`
trait_method = assoc;
method_id = def_id;
method_id = trait_method_def_id;
trait_id = tcx.parent(method_id);
trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
trait_id
Expand Down
32 changes: 32 additions & 0 deletions tests/ui/sanitizer/cfi/fn-trait-objects.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be
// called through their trait methods.
//
//@ needs-sanitizer-cfi
//@ only-linux
//@ ignore-backends: gcc
//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer --test
//@ run-pass

#![feature(fn_traits)]
#![feature(unboxed_closures)]

fn foo(_a: u32) {}

#[test]
fn test_fn_trait() {
let f: Box<dyn Fn(u32)> = Box::new(foo);
Fn::call(&f, (0,));
}

#[test]
fn test_fnmut_trait() {
let mut a = 0;
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
FnMut::call_mut(&mut f, (1,));
}

#[test]
fn test_fnonce_trait() {
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
FnOnce::call_once(f, (2,));
}
32 changes: 32 additions & 0 deletions tests/ui/sanitizer/kcfi/fn-trait-objects.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be
// called through their trait methods.
//
//@ needs-sanitizer-kcfi
//@ only-linux
//@ ignore-backends: gcc
//@ compile-flags: -Ctarget-feature=-crt-static -Zpanic_abort_tests -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi -Cunsafe-allow-abi-mismatch=sanitizer --test
//@ run-pass

#![feature(fn_traits)]
#![feature(unboxed_closures)]

fn foo(_a: u32) {}

#[test]
fn test_fn_trait() {
let f: Box<dyn Fn(u32)> = Box::new(foo);
Fn::call(&f, (0,));
}

#[test]
fn test_fnmut_trait() {
let mut a = 0;
let mut f: Box<dyn FnMut(u32)> = Box::new(|x| a += x);
FnMut::call_mut(&mut f, (1,));
}

#[test]
fn test_fnonce_trait() {
let f: Box<dyn FnOnce(u32)> = Box::new(foo);
FnOnce::call_once(f, (2,));
}
Loading