CFI: Support self_cell-like recursion
Current `transform_ty` attempts to avoid cycles when normalizing `#[repr(transparent)]` types to their interior, but runs afoul of this pattern used in `self_cell`: ``` struct X<T> { x: u8, p: PhantomData<T>, } #[repr(transparent)] struct Y(X<Y>); ``` When attempting to normalize Y, it will still cycle indefinitely. By using a types-visited list, this will instead get expanded exactly one layer deep to X<Y>, and then stop, not attempting to normalize `Y` any further.
This commit is contained in:
parent
85e449a323
commit
dec36c3d6e
@ -178,14 +178,14 @@ fn encode_fnsig<'tcx>(
|
|||||||
// Encode the return type
|
// Encode the return type
|
||||||
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
|
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
|
||||||
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
|
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
|
||||||
let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
|
let ty = transform_ty(tcx, fn_sig.output(), &mut Vec::new(), transform_ty_options);
|
||||||
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
|
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
|
||||||
|
|
||||||
// Encode the parameter types
|
// Encode the parameter types
|
||||||
let tys = fn_sig.inputs();
|
let tys = fn_sig.inputs();
|
||||||
if !tys.is_empty() {
|
if !tys.is_empty() {
|
||||||
for ty in tys {
|
for ty in tys {
|
||||||
let ty = transform_ty(tcx, *ty, transform_ty_options);
|
let ty = transform_ty(tcx, *ty, &mut Vec::new(), transform_ty_options);
|
||||||
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
|
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,11 +767,12 @@ fn transform_predicates<'tcx>(
|
|||||||
fn transform_args<'tcx>(
|
fn transform_args<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
args: GenericArgsRef<'tcx>,
|
args: GenericArgsRef<'tcx>,
|
||||||
|
parents: &mut Vec<Ty<'tcx>>,
|
||||||
options: TransformTyOptions,
|
options: TransformTyOptions,
|
||||||
) -> GenericArgsRef<'tcx> {
|
) -> GenericArgsRef<'tcx> {
|
||||||
let args = args.iter().map(|arg| match arg.unpack() {
|
let args = args.iter().map(|arg| match arg.unpack() {
|
||||||
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
|
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
|
||||||
GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
|
GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(),
|
||||||
_ => arg,
|
_ => arg,
|
||||||
});
|
});
|
||||||
tcx.mk_args_from_iter(args)
|
tcx.mk_args_from_iter(args)
|
||||||
@ -781,9 +782,12 @@ fn transform_args<'tcx>(
|
|||||||
// c_void types into unit types unconditionally, generalizes pointers if
|
// c_void types into unit types unconditionally, generalizes pointers if
|
||||||
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
|
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
|
||||||
// TransformTyOptions::NORMALIZE_INTEGERS option is set.
|
// TransformTyOptions::NORMALIZE_INTEGERS option is set.
|
||||||
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
|
fn transform_ty<'tcx>(
|
||||||
let mut ty = ty;
|
tcx: TyCtxt<'tcx>,
|
||||||
|
mut ty: Ty<'tcx>,
|
||||||
|
parents: &mut Vec<Ty<'tcx>>,
|
||||||
|
options: TransformTyOptions,
|
||||||
|
) -> Ty<'tcx> {
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {}
|
ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {}
|
||||||
|
|
||||||
@ -843,17 +847,20 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
_ if ty.is_unit() => {}
|
_ if ty.is_unit() => {}
|
||||||
|
|
||||||
ty::Tuple(tys) => {
|
ty::Tuple(tys) => {
|
||||||
ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options)));
|
ty = Ty::new_tup_from_iter(
|
||||||
|
tcx,
|
||||||
|
tys.iter().map(|ty| transform_ty(tcx, ty, parents, options)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Array(ty0, len) => {
|
ty::Array(ty0, len) => {
|
||||||
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
|
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
|
||||||
|
|
||||||
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
|
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, parents, options), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Slice(ty0) => {
|
ty::Slice(ty0) => {
|
||||||
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options));
|
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, parents, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Adt(adt_def, args) => {
|
ty::Adt(adt_def, args) => {
|
||||||
@ -862,7 +869,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
|
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
|
||||||
{
|
{
|
||||||
ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
|
ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
|
||||||
} else if adt_def.repr().transparent() && adt_def.is_struct() {
|
} else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty)
|
||||||
|
{
|
||||||
// Don't transform repr(transparent) types with an user-defined CFI encoding to
|
// Don't transform repr(transparent) types with an user-defined CFI encoding to
|
||||||
// preserve the user-defined CFI encoding.
|
// preserve the user-defined CFI encoding.
|
||||||
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
|
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
|
||||||
@ -881,38 +889,48 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
// Generalize any repr(transparent) user-defined type that is either a pointer
|
// Generalize any repr(transparent) user-defined type that is either a pointer
|
||||||
// or reference, and either references itself or any other type that contains or
|
// or reference, and either references itself or any other type that contains or
|
||||||
// references itself, to avoid a reference cycle.
|
// references itself, to avoid a reference cycle.
|
||||||
|
|
||||||
|
// If the self reference is not through a pointer, for example, due
|
||||||
|
// to using `PhantomData`, need to skip normalizing it if we hit it again.
|
||||||
|
parents.push(ty);
|
||||||
if ty0.is_any_ptr() && ty0.contains(ty) {
|
if ty0.is_any_ptr() && ty0.contains(ty) {
|
||||||
ty = transform_ty(
|
ty = transform_ty(
|
||||||
tcx,
|
tcx,
|
||||||
ty0,
|
ty0,
|
||||||
|
parents,
|
||||||
options | TransformTyOptions::GENERALIZE_POINTERS,
|
options | TransformTyOptions::GENERALIZE_POINTERS,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ty = transform_ty(tcx, ty0, options);
|
ty = transform_ty(tcx, ty0, parents, options);
|
||||||
}
|
}
|
||||||
|
parents.pop();
|
||||||
} else {
|
} else {
|
||||||
// Transform repr(transparent) types without non-ZST field into ()
|
// Transform repr(transparent) types without non-ZST field into ()
|
||||||
ty = Ty::new_unit(tcx);
|
ty = Ty::new_unit(tcx);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, options));
|
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, parents, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::FnDef(def_id, args) => {
|
ty::FnDef(def_id, args) => {
|
||||||
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options));
|
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, parents, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Closure(def_id, args) => {
|
ty::Closure(def_id, args) => {
|
||||||
ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options));
|
ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, parents, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::CoroutineClosure(def_id, args) => {
|
ty::CoroutineClosure(def_id, args) => {
|
||||||
ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options));
|
ty = Ty::new_coroutine_closure(
|
||||||
|
tcx,
|
||||||
|
*def_id,
|
||||||
|
transform_args(tcx, args, parents, options),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Coroutine(def_id, args) => {
|
ty::Coroutine(def_id, args) => {
|
||||||
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options));
|
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, parents, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Ref(region, ty0, ..) => {
|
ty::Ref(region, ty0, ..) => {
|
||||||
@ -924,9 +942,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ty.is_mutable_ptr() {
|
if ty.is_mutable_ptr() {
|
||||||
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options));
|
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
|
||||||
} else {
|
} else {
|
||||||
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options));
|
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -940,9 +958,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ty.is_mutable_ptr() {
|
if ty.is_mutable_ptr() {
|
||||||
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, options));
|
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options));
|
||||||
} else {
|
} else {
|
||||||
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, options));
|
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -955,9 +973,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
.skip_binder()
|
.skip_binder()
|
||||||
.inputs()
|
.inputs()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| transform_ty(tcx, *ty, options))
|
.map(|ty| transform_ty(tcx, *ty, parents, options))
|
||||||
.collect();
|
.collect();
|
||||||
let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
|
let output = transform_ty(tcx, fn_sig.skip_binder().output(), parents, options);
|
||||||
ty = Ty::new_fn_ptr(
|
ty = Ty::new_fn_ptr(
|
||||||
tcx,
|
tcx,
|
||||||
ty::Binder::bind_with_vars(
|
ty::Binder::bind_with_vars(
|
||||||
@ -987,6 +1005,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
|||||||
ty = transform_ty(
|
ty = transform_ty(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
|
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
|
||||||
|
parents,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1037,7 +1056,7 @@ pub fn typeid_for_fnabi<'tcx>(
|
|||||||
// Encode the return type
|
// Encode the return type
|
||||||
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
|
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
|
||||||
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
|
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
|
||||||
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
|
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &mut Vec::new(), transform_ty_options);
|
||||||
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
||||||
|
|
||||||
// Encode the parameter types
|
// Encode the parameter types
|
||||||
@ -1049,7 +1068,7 @@ pub fn typeid_for_fnabi<'tcx>(
|
|||||||
let mut pushed_arg = false;
|
let mut pushed_arg = false;
|
||||||
for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
|
for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
|
||||||
pushed_arg = true;
|
pushed_arg = true;
|
||||||
let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
|
let ty = transform_ty(tcx, arg.layout.ty, &mut Vec::new(), transform_ty_options);
|
||||||
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
||||||
}
|
}
|
||||||
if !pushed_arg {
|
if !pushed_arg {
|
||||||
@ -1062,7 +1081,8 @@ pub fn typeid_for_fnabi<'tcx>(
|
|||||||
if fn_abi.args[n].mode == PassMode::Ignore {
|
if fn_abi.args[n].mode == PassMode::Ignore {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
|
let ty =
|
||||||
|
transform_ty(tcx, fn_abi.args[n].layout.ty, &mut Vec::new(), transform_ty_options);
|
||||||
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,12 @@ pub struct Bar(i32);
|
|||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Type3<T>(T);
|
pub struct Type3<T>(T);
|
||||||
|
|
||||||
|
// repr(transparent) wrapper which engages in self-reference
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Type4(Type4Helper<Type4>);
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Type4Helper<T>(*mut T);
|
||||||
|
|
||||||
pub fn foo1(_: Type1) { }
|
pub fn foo1(_: Type1) { }
|
||||||
// CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
// CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
pub fn foo2(_: Type1, _: Type1) { }
|
pub fn foo2(_: Type1, _: Type1) { }
|
||||||
@ -52,6 +58,13 @@ pub fn foo8(_: Type3<Bar>, _: Type3<Bar>) { }
|
|||||||
// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
pub fn foo9(_: Type3<Bar>, _: Type3<Bar>, _: Type3<Bar>) { }
|
pub fn foo9(_: Type3<Bar>, _: Type3<Bar>, _: Type3<Bar>) { }
|
||||||
// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
|
pub fn foo10(_: Type4) { }
|
||||||
|
// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
|
pub fn foo11(_: Type4, _: Type4) { }
|
||||||
|
// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
|
pub fn foo12(_: Type4, _: Type4, _: Type4) { }
|
||||||
|
// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||||
|
|
||||||
|
|
||||||
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooE"}
|
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooE"}
|
||||||
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooS_E"}
|
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooS_E"}
|
||||||
@ -62,3 +75,6 @@ pub fn foo9(_: Type3<Bar>, _: Type3<Bar>, _: Type3<Bar>) { }
|
|||||||
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarE"}
|
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarE"}
|
||||||
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_E"}
|
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_E"}
|
||||||
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_S_E"}
|
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_S_E"}
|
||||||
|
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4E"}
|
||||||
|
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4S0_E"}
|
||||||
|
// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4S0_S0_E"}
|
||||||
|
33
tests/ui/sanitizer/cfi-self-ref.rs
Normal file
33
tests/ui/sanitizer/cfi-self-ref.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Check that encoding self-referential types works with #[repr(transparent)]
|
||||||
|
|
||||||
|
//@ needs-sanitizer-cfi
|
||||||
|
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||||
|
//@ only-linux
|
||||||
|
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
|
||||||
|
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
|
||||||
|
//@ run-pass
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct X<T> {
|
||||||
|
_x: u8,
|
||||||
|
p: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Y(X<Y>);
|
||||||
|
|
||||||
|
trait Fooable {
|
||||||
|
fn foo(&self, y: Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
impl Fooable for Bar {
|
||||||
|
fn foo(&self, _: Y) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = &Bar as &dyn Fooable;
|
||||||
|
x.foo(Y(X {_x: 0, p: PhantomData}));
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user