diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 9b679019e96ce..949df2a36fe71 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -541,7 +541,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; - self.call(self.type_void(), None, None, builtin_unreachable, &[], None); + self.call(self.type_void(), None, None, builtin_unreachable, &[], None, false); } // Write results to outputs. diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f5cda81f6ab86..7eca0e1bf94ff 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -592,12 +592,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { let try_block = self.current_func().new_block("try"); let current_block = self.block.clone(); self.block = try_block; - let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here? + let call = self.call(typ, fn_attrs, None, func, args, None, false); // TODO(antoyo): use funclet here? self.block = current_block; let return_value = @@ -629,8 +630,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { - let call_site = self.call(typ, fn_attrs, None, func, args, None); + let call_site = self.call(typ, fn_attrs, None, func, args, None, false); let condition = self.context.new_rvalue_from_int(self.bool_type, 1); self.llbb().end_with_conditional(self.location, condition, then, catch); if let Some(_fn_abi) = fn_abi { @@ -1667,6 +1669,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { // FIXME(antoyo): remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a6c8b72e851b2..bef7d271cda46 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -133,6 +133,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { func, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false ) } sym::likely => self.expect(args[0].immediate(), true), @@ -401,7 +402,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn abort(&mut self) { let func = self.context.get_builtin_function("abort"); let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(self.type_void(), None, None, func, &[], None); + self.call(self.type_void(), None, None, func, &[], None, false); } fn assume(&mut self, value: Self::Value) { @@ -1103,7 +1104,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: RValue<'gcc>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - bx.call(bx.type_void(), None, None, try_func, &[data], None); + bx.call(bx.type_void(), None, None, try_func, &[data], None, false); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx.data_layout.i32_align.abi; @@ -1177,21 +1178,21 @@ fn codegen_gnu_try<'gcc>( let zero = bx.cx.context.new_rvalue_zero(bx.int_type); let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]); let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, false); bx.ret(bx.const_i32(1)); // NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not // generate a try/catch. // FIXME(antoyo): add a check in the libgccjit API to prevent this. bx.switch_to_block(current_block); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); }); let func = unsafe { std::mem::transmute(func) }; // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 74539d4d49570..681fb37e0855f 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -466,11 +466,11 @@ pub(crate) fn inline_asm_call<'ll>( let call = if !labels.is_empty() { assert!(catch_funclet.is_none()); - bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, false) } else if let Some((catch, funclet)) = catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, false) } else { - bx.call(fty, None, None, v, inputs, None) + bx.call(fty, None, None, v, inputs, None, false) }; // Store mark in a metadata node so we can map LLVM errors diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 63e59ea13fc35..7efa8319e0fae 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -21,7 +21,10 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; +use rustc_symbol_mangling::typeid::{ + kcfi_typeid_for_drop_fnabi, kcfi_typeid_for_fnabi, typeid_for_drop_fnabi, typeid_for_fnabi, + TypeIdOptions, +}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -221,6 +224,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { then: &'ll BasicBlock, catch: &'ll BasicBlock, funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -233,10 +237,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1231,6 +1235,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { llfn: &'ll Value, args: &[&'ll Value], funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("call {:?} with args ({:?})", llfn, args); @@ -1243,10 +1248,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1468,7 +1473,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); - self.call(ty, None, None, f, args, None) + self.call(ty, None, None, f, args, None, false) } fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { @@ -1526,7 +1531,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { format!("llvm.{instr}.sat.i{int_width}.f{float_width}") }; let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); - self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None) + self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, false) } pub(crate) fn landing_pad( @@ -1554,6 +1559,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { default_dest: &'ll BasicBlock, indirect_dest: &[&'ll BasicBlock], funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -1566,10 +1572,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1602,6 +1608,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: &'ll Value, + is_drop: bool, ) { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; if self.tcx.sess.is_sanitizer_cfi_enabled() @@ -1622,7 +1629,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = if is_drop { + typeid_for_drop_fnabi(self.tcx, fn_abi, options) + } else { + typeid_for_fnabi(self.tcx, fn_abi, options) + }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); // Test whether the function pointer is associated with the type identifier. @@ -1645,6 +1656,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: &'ll Value, + is_drop: bool, ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled() @@ -1665,7 +1677,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = if is_drop { + kcfi_typeid_for_drop_fnabi(self.tcx, fn_abi, options) + } else { + kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + }; Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { None diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 78c0725a63784..9d9ddddf2fadb 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -141,20 +141,30 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { + let mut typeids = Vec::new(); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty()); + typeids.push(typeid.clone()); self.set_type_metadata(llfn, typeid); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); let typeid = typeid_for_instance( self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, ); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::NO_TYPE_ERASURE); + if !typeids.contains(&typeid) { + self.add_type_metadata(llfn, typeid); + } } else { let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); self.set_type_metadata(llfn, typeid); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2409b2e78d736..660d78a0b784a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -181,6 +181,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { simple_fn, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false, ) } sym::likely => { @@ -539,7 +540,7 @@ fn catch_unwind_intrinsic<'ll>( ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.call(try_func_ty, None, None, try_func, &[data], None); + bx.call(try_func_ty, None, None, try_func, &[data], None, false); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx().data_layout.i32_align.abi; @@ -640,7 +641,7 @@ fn codegen_msvc_try<'ll>( let ptr_align = bx.tcx().data_layout.pointer_align.abi; let slot = bx.alloca(bx.type_ptr(), ptr_align); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, false); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -684,7 +685,7 @@ fn codegen_msvc_try<'ll>( let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]); let ptr = bx.load(bx.type_ptr(), slot, ptr_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), false); bx.catch_ret(&funclet, caught); // The flag value of 64 indicates a "catch-all". @@ -692,7 +693,7 @@ fn codegen_msvc_try<'ll>( let flags = bx.const_i32(64); let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null, flags, null]); - bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), false); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -701,7 +702,7 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -750,7 +751,7 @@ fn codegen_wasm_try<'ll>( // } // let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, false); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -766,7 +767,7 @@ fn codegen_wasm_try<'ll>( let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), false); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -775,7 +776,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -818,7 +819,7 @@ fn codegen_gnu_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -836,13 +837,13 @@ fn codegen_gnu_try<'ll>( bx.add_clause(vals, tydesc); let ptr = bx.extract_value(vals, 0); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, false); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -882,7 +883,7 @@ fn codegen_emcc_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -920,13 +921,13 @@ fn codegen_emcc_try<'ll>( bx.store(is_rust_panic, catch_data_1, i8_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None); + bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, false); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -1439,6 +1440,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false, ); Ok(c) } @@ -1607,6 +1609,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None, + false, ); return Ok(v); } @@ -1706,6 +1709,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[2].immediate()], None, + false, ); return Ok(v); } @@ -1799,6 +1803,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[2].immediate(), args[1].immediate(), alignment, mask], None, + false, ); return Ok(v); } @@ -1904,6 +1909,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None, + false, ); return Ok(v); } @@ -2352,11 +2358,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], None, + false, )) } else { let fn_ty = bx.type_func(&[vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None)) + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, false)) }; } @@ -2409,7 +2416,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None); + let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, false); return Ok(v); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 13809ef72ec7b..a4dc8494df100 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -482,7 +482,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (rust_main, start_ty, vec![arg_argc, arg_argv]) }; - let result = bx.call(start_ty, None, None, start_fn, &args, None); + let result = bx.call(start_ty, None, None, start_fn, &args, None, false); if cx.sess().target.os.contains("uefi") { bx.ret(result); } else { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcc27a4f0e568..adf6eac17bed2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -160,6 +160,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, ::Value>], instance: Option>, + is_drop: bool, mergeable_succ: bool, ) -> MergingSucc { let tcx = bx.tcx(); @@ -232,6 +233,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ret_llbb, unwind_block, self.funclet(fx), + is_drop, ); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(invokeret); @@ -247,7 +249,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } MergingSucc::False } else { - let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx)); + let llret = + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx), false); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(llret); } @@ -606,6 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &[], Some(instance), + true, mergeable_succ, ) } @@ -685,8 +689,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = - helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); + let merging_succ = helper.do_call( + self, + bx, + fn_abi, + llfn, + &args, + None, + unwind, + &[], + Some(instance), + false, + false, + ); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -716,6 +731,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &[], Some(instance), false, + false, ); assert_eq!(merging_succ, MergingSucc::False); } @@ -778,6 +794,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &[], Some(instance), + false, mergeable_succ, ) } else { @@ -1135,6 +1152,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &copied_constant_arguments, instance, + false, mergeable_succ, ) } @@ -1699,7 +1717,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref(), false); bx.apply_attrs_to_cleanup_callsite(llret); } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 15f2e0e56d86d..779380d01b176 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -710,7 +710,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None) + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, false) } else { bx.get_static(def_id) }; diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index e2e95cede60a5..890818a478403 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -70,7 +70,15 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // (But we are in good company, this code is duplicated plenty of times.) let fn_ty = bx.fn_decl_backend_type(fn_abi); - bx.call(fn_ty, /* fn_attrs */ None, Some(fn_abi), llfn, &[msg.0, msg.1], None); + bx.call( + fn_ty, + /* fn_attrs */ None, + Some(fn_abi), + llfn, + &[msg.0, msg.1], + None, + false, + ); // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 36f37e3791bc5..2f140ea363f02 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -80,6 +80,7 @@ pub trait BuilderMethods<'a, 'tcx>: then: Self::BasicBlock, catch: Self::BasicBlock, funclet: Option<&Self::Funclet>, + is_drop: bool, ) -> Self::Value; fn unreachable(&mut self); @@ -339,6 +340,7 @@ pub trait BuilderMethods<'a, 'tcx>: llfn: Self::Value, args: &[Self::Value], funclet: Option<&Self::Funclet>, + is_drop: bool, ) -> Self::Value; fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8a87538e78850..5f92b5ae8e39e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1841,7 +1841,7 @@ impl<'tcx> TyCtxt<'tcx> { /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used /// to identify which traits may define a given associated type to help avoid cycle errors. /// Returns a `DefId` iterator. - fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { + pub fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { let mut set = FxHashSet::default(); let mut stack = vec![trait_def_id]; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c85ee140fa4ec..f5e048027cb0f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1836,6 +1836,11 @@ impl<'tcx> Ty<'tcx> { self.0.0.flags } + #[inline] + pub fn is_tuple(self) -> bool { + matches!(self.kind(), Tuple(..)) + } + #[inline] pub fn is_unit(self) -> bool { match self.kind() { @@ -2208,6 +2213,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), FnDef(..) | FnPtr(_)) } + #[inline] + pub fn is_fn_def(self) -> bool { + matches!(self.kind(), FnDef(..)) + } + #[inline] pub fn is_fn_ptr(self) -> bool { matches!(self.kind(), FnPtr(_)) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 02bb1fde75c1b..1373563f4b133 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -91,6 +91,8 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![allow(internal_features)] +#![feature(iter_order_by)] +#![feature(let_chains)] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index e8763e49e624b..a0132957946ba 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, List, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -16,18 +16,32 @@ bitflags! { const GENERALIZE_POINTERS = 1; const GENERALIZE_REPR_C = 2; const NORMALIZE_INTEGERS = 4; + const NO_TYPE_ERASURE = 8; } } mod typeid_itanium_cxx_abi; +/// Returns a type metadata identifier for the specified drop FnAbi. +pub fn typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + typeid_itanium_cxx_abi::typeid_for_drop_fnabi(tcx, fn_abi, options) +} + /// Returns a type metadata identifier for the specified FnAbi. pub fn typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, None), + options, + ) } /// Returns a type metadata identifier for the specified Instance. @@ -36,7 +50,29 @@ pub fn typeid_for_instance<'tcx>( instance: &Instance<'tcx>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, Some(instance)), + options, + ) +} + +/// Returns a KCFI type metadata identifier for the specified drop FnAbi. +pub fn kcfi_typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(typeid_itanium_cxx_abi::typeid_for_drop_fnabi(tcx, fn_abi, options).as_bytes()); + hash.finish() as u32 } /// Returns a KCFI type metadata identifier for the specified FnAbi. @@ -48,7 +84,14 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.write( + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, None), + options, + ) + .as_bytes(), + ); hash.finish() as u32 } @@ -58,9 +101,21 @@ pub fn kcfi_typeid_for_instance<'tcx>( instance: &Instance<'tcx>, options: TypeIdOptions, ) -> u32 { + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write( + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, Some(instance)), + options, + ) + .as_bytes(), + ); hash.finish() as u32 } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 07a382d161d74..eef1717bfa76c 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::DefId; use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi, PassMode}; +use rustc_target::abi::call::{Conv, FnAbi}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; @@ -277,7 +277,9 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, s.push('E'); compress(dict, DictKey::Region(region), &mut s); } - // FIXME(@lcnr): Why is `ReEarlyParam` reachable here. + // This is possible becasuse trait objects created by new_dynamic_trait and + // new_dynamic_fn_trait are created with tcx.lifetimes.re_erased, and non Fn traits are + // transformed into their identities in transform_predicate. RegionKind::ReEarlyParam(..) | RegionKind::ReErased => { s.push_str("u6region"); compress(dict, DictKey::Region(region), &mut s); @@ -638,19 +640,6 @@ fn encode_ty<'tcx>( } // Function types - ty::FnDef(def_id, args) - | ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args) => { - // u[IE], where is , - // as vendor extended type. - let mut s = String::new(); - let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - ty::Coroutine(def_id, args, ..) => { // u[IE], where is , // as vendor extended type. @@ -720,6 +709,20 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + // Associated type projections + ty::Alias(_, alias_ty) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, alias_ty.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, alias_ty.args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // This is possible because non Fn traits are transformed into their identities in + // transform_predicate. // Type parameters ty::Param(..) => { // u5param as vendor extended type @@ -729,9 +732,11 @@ fn encode_ty<'tcx>( } // Unexpected types - ty::Alias(..) - | ty::Bound(..) + ty::Bound(..) + | ty::Closure(..) | ty::Error(..) + | ty::FnDef(..) + | ty::CoroutineClosure(..) | ty::CoroutineWitness(..) | ty::Infer(..) | ty::Placeholder(..) => { @@ -742,24 +747,440 @@ fn encode_ty<'tcx>( typeid } -/// Transforms predicates for being encoded and used in the substitution dictionary. -fn transform_predicates<'tcx>( +/// Returns the list of arguments if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_args<'tcx>( tcx: TyCtxt<'tcx>, predicates: &List>, -) -> &'tcx List> { - let predicates: Vec> = predicates - .iter() - .filter_map(|predicate| match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { +) -> Option<&'tcx List>> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + if let Some(args) = principal_args(predicates) + && !args.is_empty() + && args[0].expect_ty().is_tuple() + { + return Some(args[0].expect_ty().tuple_fields()); + } + if is_fn_subtrait(tcx, predicates) { + return Some(List::empty()); + } else { + bug!("fn_trait_args: unexpected non-tuple arg `{:?}`", principal_args(predicates)); + } + } else { + None + } +} + +/// Returns the output if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_output<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> Option> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + if let Some(projection) = predicates.projection_bounds().next() + && let Some(ty) = projection.skip_binder().term.ty() + { + return Some(ty); + } + if is_fn_subtrait(tcx, predicates) { + return Some(Ty::new_unit(tcx)); + } else { + bug!( + "fn_trait_output: unexpected non-term projection `{:?}`", + predicates.projection_bounds().next() + ); + } + } else { + None + } +} + +/// Returns the signature if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_sig<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> Option> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + Some(tcx.mk_fn_sig( + fn_trait_args(tcx, predicates).unwrap(), + fn_trait_output(tcx, predicates).unwrap(), + false, + hir::Unsafety::Normal, + Abi::Rust, + )) + } else { + None + } +} + +/// Returns true if the FnAbi is an unpacked "rust-call" FnAbi. +#[inline] +fn is_closure_call_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> bool { + if fn_abi.conv == Conv::Rust && !fn_abi.c_variadic && !fn_abi.args.is_empty() { + match fn_abi.args[0].layout.ty.kind() { + ty::RawPtr(ty, ..) | ty::Ref(_, ty, _) => { + if let ty::Closure(_, args) = ty.kind() { + let fn_sig = + tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return fn_sig + .skip_binder() + .inputs() + .iter() + .eq_by(&fn_abi.args[1..], |ty, arg| *ty == arg.layout.ty) + && fn_sig.output().skip_binder() == fn_abi.ret.layout.ty; + } + } + _ => {} + } + } + false +} + +/// Returns true if the FnAbi is a dynamic Fn trait FnAbi. +#[inline] +fn is_dynamic_fn_call_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> bool { + if fn_abi.conv == Conv::Rust + && !fn_abi.c_variadic + && !fn_abi.args.is_empty() + && let ty::Ref(_, ty0, _) = fn_abi.args[0].layout.ty.kind() + && let ty::Dynamic(predicates, ..) = ty0.kind() + { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + return fn_trait_args(tcx, predicates) + .unwrap() + .iter() + .eq_by(&fn_abi.args[1..], |ty, arg| ty == arg.layout.ty) + && fn_trait_output(tcx, predicates).unwrap() == fn_abi.ret.layout.ty; + } + } + false +} + +/// Returns true if the trait object is an Fn subtrait object. +#[inline] +fn is_dynamic_fn_subtrait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Dynamic(predicates, ..) => is_fn_subtrait(tcx, predicates), + _ => false, + } +} + +/// Returns true if the trait object is an Fn trait object. +#[inline] +fn is_dynamic_fn_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Dynamic(predicates, ..) => is_fn_trait(tcx, predicates), + _ => false, + } +} + +/// Returns true if principal is an Fn subtrait. +#[inline] +fn is_fn_subtrait<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> bool { + if let Some(principal) = predicates.principal() { + for def_id in tcx.super_traits_of(principal.skip_binder().def_id) { + if tcx.is_fn_trait(def_id) { + return true; + } + } + } + false +} + +/// Returns true if principal is an Fn trait. +#[inline] +fn is_fn_trait<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> bool { + if let Some(principal) = predicates.principal() + && tcx.is_fn_trait(principal.skip_binder().def_id) + { + true + } else { + false + } +} + +/// Creates a trait object. +#[inline] +fn new_dynamic_trait<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + args: GenericArgsRef<'tcx>, +) -> Ty<'tcx> { + let predicate = + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id: def_id, args: args }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn) +} + +/// Returns the principal list of arguments. +#[inline] +fn principal_args<'tcx>( + predicates: &List>, +) -> Option<&'tcx List>> { + predicates.principal().map(|trait_ref| trait_ref.skip_binder().args) +} + +/// Transforms a closure FnAbi for being encoded and used in the substitution dictionary. +fn transform_closure_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + _options: EncodeTyOptions, +) -> FnAbi<'tcx, Ty<'tcx>> { + if !fn_abi.args.is_empty() + && let ty::Closure(_, args) = fn_abi.args[0].layout.ty.kind() + { + let fn_sig = tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!("transform_fnabi: couldn't get fn_abi of fn_sig {:?}", fn_abi_error) + }) + .clone(); + } else if !fn_abi.args.is_empty() { + match fn_abi.args[0].layout.ty.kind() { + ty::RawPtr(ty, ..) | ty::Ref(_, ty, _) => { + if let ty::Closure(_, args) = ty.kind() { + let fn_sig = + tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!( + "transform_fnabi: couldn't get fn_abi of fn_sig {:?}", + fn_abi_error + ) + }) + .clone(); + } + } + _ => {} + } + } + + return fn_abi.clone(); +} + +/// Transforms a Fn trait FnAbi for being encoded and used in the substitution dictionary. +fn transform_fn_trait_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + _options: EncodeTyOptions, +) -> FnAbi<'tcx, Ty<'tcx>> { + if !fn_abi.args.is_empty() { + let fn_sig = ty::Binder::bind_with_vars( + tcx.mk_fn_sig( + fn_abi.args[1..].iter().map(|arg| arg.layout.ty), + fn_abi.ret.layout.ty, + false, + hir::Unsafety::Normal, + Abi::Rust, + ), + List::empty(), + ); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!("transform_fnabi: couldn't get fn_abi of fn_sig {:?}", fn_abi_error) + }) + .clone(); + } + return fn_abi.clone(); +} + +/// Transforms an FnAbi for being encoded and used in the substitution dictionary. +/// +/// typeid_for_fnabi is called at two locations, initially when declaring/defining functions and +/// methods, which it has a lot more context/information (i.e., FnAbi and Instance), and later +/// during code generation at call sites, which it has a lot less contex/information (i.e., an FnAbi +/// only, and after type erasure). +/// +/// In the first call (i.e., when declaring/defining functions and methods), it encodes a given +/// FnAbi and Instance and assigns one or more type ids to a function or method (these type ids are +/// used later used by the LowerTypeTests LLVM pass to aggregate functions/function pointers in +/// groups derived by these type ids). +/// +/// In the second call (i.e., during code generation), it encodes a given FnAbi (i.e., without an +/// Instance, and after type erasure), and uses this type id to test if the given function/function +/// pointer is member of the group derived from this type id. It means that in the first call +/// typeid_for_fnabi (where it assign type ids), it can only include at most as much information +/// that would be available during code generation at call sites, otherwise the type ids will not +/// match. +/// +/// For this, it: +/// +/// * Replaces the concrete self of trait default implementations and trait implementations by a +/// reference to a trait object (i.e., performs type erasure), similarly to what the compiler does +/// during code generation at call sites, but uses the indentity of the trait that implements the +/// method being declared/defined or called. +/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the entry in +/// the vtable (i.e., the signature of the closure passed as an argument to the shim). +/// * Adjusts the type ids of ClosureOnceShims to the type id expected in the call sites for that +/// entry in the vtable (i.e., the signature of the closure passed as an argument to the shim). +/// * Adjusts the type ids of DropGlues to the type id expected in the call sites for that +/// entry in the vtable (i.e., a call on a synthesized Drop trait object). +/// +/// For compatibility between types, it uses their least common denominator and: +/// +/// * Transforms closure FnAbis into funtion pointer FnAbis. +/// * Transforms Fn trait and Fn subtrait FnAbis into function pointer FnAbis. +/// +pub fn transform_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: EncodeTyOptions, + instance: Option<&Instance<'tcx>>, +) -> FnAbi<'tcx, Ty<'tcx>> { + if let Some(instance) = instance { + match instance.def { + ty::InstanceDef::Item(..) => { + if let Some(impl_def_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) + && !fn_abi.args.is_empty() + && fn_abi.args[0].layout.ty.is_ref() + { + let self_ty = if options.contains(EncodeTyOptions::NO_TYPE_ERASURE) { + // Do not perform type erasure for assigning a secondary type id to methods + // with their concrete self so they can be used as function pointers. + return fn_abi.clone(); + } else { + // Replace the concrete self by a reference to a trait object (i.e., perform + // type erasure). + if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + } + }; + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here + // because the other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + return fn_abi; + } else if tcx.is_closure_like(instance.def_id()) { + // Transform closure declaration/definition FnAbis into function pointer FnAbis + return transform_closure_fnabi(tcx, fn_abi, options); + } + } + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ClosureOnceShim { .. } => { + // Adjust the type ids of VTableShims and ClosureOnceShims + return transform_closure_fnabi(tcx, fn_abi, options); + } + ty::InstanceDef::DropGlue(..) => { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. + // However, Rust allows for types to be erased to any trait object and retains the + // drop function for the original type, which means at the indirect call sites in + // DropGlues, when typeid_for_fnabi is called a second time, it only has an FnAbi + // (i.e., without an Instance, and after type erasure), and it could be an FnAbi to + // a call on any arbitrary trait object, so normalize them to a synthesized Drop + // trait object, both on declaration/definition, and during code generation at call + // sites so they have the same type id and match. + if !fn_abi.args.is_empty() { + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + tcx.lang_items().drop_trait().unwrap(), + List::empty(), + ), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + tcx.lang_items().drop_trait().unwrap(), + List::empty(), + ), + ) + }; + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here + // because the other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + return fn_abi; + } + return fn_abi.clone(); + } + _ => {} + } + } else { + if is_closure_call_fnabi(tcx, fn_abi) { + // Transform closure call FnAbis into function pointer FnAbis + return transform_closure_fnabi(tcx, fn_abi, options); + } else if is_dynamic_fn_call_fnabi(tcx, fn_abi) { + // Transform Fn trait call FnAbis into function pointer FnAbis + return transform_fn_trait_fnabi(tcx, fn_abi, options); + } + } + return fn_abi.clone(); +} + +/// Transforms a predicate for being encoded and used in the substitution dictionary. +fn transform_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: ty::PolyExistentialPredicate<'tcx>, + _options: EncodeTyOptions, +) -> Option> { + match predicate.as_ref().skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + if tcx.is_fn_trait(trait_ref.def_id) { + // Fn trait objects are transformed into function pointers in transform_ty + bug!("transform_predicate: unexpected Fn trait `{}`", trait_ref) + } else { + // Transform non Fn trait objects into their identities let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), ))) } - ty::ExistentialPredicate::Projection(..) => None, - ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - }) + } + // Remove projections + ty::ExistentialPredicate::Projection(..) => None, + ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), + } +} + +/// Transforms predicates for being encoded and used in the substitution dictionary. +fn transform_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, + options: EncodeTyOptions, +) -> &'tcx List> { + let mut predicates: Vec> = predicates + .iter() + .filter_map(|predicate| transform_predicate(tcx, predicate, options)) .collect(); + + // Enforce predicates ordering when encoding + predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); tcx.mk_poly_existential_predicates(&predicates) } @@ -777,15 +1198,31 @@ fn transform_args<'tcx>( tcx.mk_args_from_iter(args) } -// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all -// c_void types into unit types unconditionally, generalizes pointers if -// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if -// TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// Transforms a ty:Ty for being encoded and used in the substitution dictionary. +/// +/// * Transforms all c_void types into unit types. +/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set. +/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and +/// either references itself or any other type that contains or references itself, to avoid a +/// reference cycle. +/// * Transforms repr(transparent) types without non-ZST field into (). +/// * Transforms function items into function pointers. +/// * Transforms closures into function pointers. +/// * Transforms Fn trait objects into function pointers. +/// * Transforms non Fn trait objects into their identities (in transform_predicate). +/// fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { let mut ty = ty; match ty.kind() { - ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} + ty::Alias(..) + | ty::Coroutine(..) + | ty::Float(..) + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::Param(..) => {} ty::Bool => { if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { @@ -899,34 +1336,50 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } - ty::FnDef(def_id, args) => { - ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options)); + ty::FnDef(def_id, ..) => { + // Transform function items into function pointers + let fn_sig = tcx.fn_sig(def_id).skip_binder(); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); } - ty::Closure(def_id, args) => { - ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options)); + ty::Closure(_, args) => { + // Transform closures into function pointers + let fn_sig = tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); } - ty::CoroutineClosure(def_id, args) => { - ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::Coroutine(def_id, args) => { - ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options)); + ty::CoroutineClosure(_, args) => { + // Transform async closures into function pointers + let fn_ptr = args.as_coroutine_closure().signature_parts_ty(); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); } ty::Ref(region, ty0, ..) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } else { - ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } + // Remove references from function items, closures, Fn trait and Fn subtrait objects + if ty0.is_fn_def() + || ty0.is_closure() + || is_dynamic_fn_trait(tcx, *ty0) + || is_dynamic_fn_subtrait(tcx, *ty0) + { + ty = transform_ty(tcx, *ty0, options); } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); + } else { + ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); + } } else { - ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + if ty.is_mutable_ptr() { + ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + } else { + ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + } } } } @@ -975,23 +1428,30 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } ty::Dynamic(predicates, _region, kind) => { - ty = Ty::new_dynamic( - tcx, - transform_predicates(tcx, predicates), - tcx.lifetimes.re_erased, - *kind, - ); - } - - ty::Alias(..) => { - ty = transform_ty( - tcx, - tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), - options, - ); + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + // Transform Fn trait and subtrait objects into function pointers + let fn_sig = ty::Binder::bind_with_vars( + fn_trait_sig(tcx, predicates).unwrap(), + List::empty(), + ); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); + } else { + ty = Ty::new_dynamic( + tcx, + transform_predicates(tcx, predicates, options), + tcx.lifetimes.re_erased, + *kind, + ); + } } - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + ty::Bound(..) + | ty::Error(..) + | ty::CoroutineWitness(..) + | ty::Infer(..) + | ty::Placeholder(..) => { bug!("transform_ty: unexpected `{:?}`", ty.kind()); } } @@ -999,6 +1459,45 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty } +/// Returns a type metadata identifier for the specified drop FnAbi using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // Transform self into a Drop trait object to match what the drop method's self is transformed + // into in transform_fnabi. + // + // DropGlues may have indirect calls to one or more given types drop function. However, Rust + // allows for types to be erased to any trait object and retains the drop function for the + // original type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has an FnAbi (i.e., without an Instance, and after type + // erasure), and it could be an FnAbi to a call on any arbitrary trait object, so normalize them + // to a synthesized Drop trait object, both on declaration/definition, and during code + // generation at call sites so they have the same type id and match. + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()), + ) + }; + + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the other + // fields are never used. + fn_abi.args[0].layout.ty = self_ty; + + return typeid_for_fnabi(tcx, &fn_abi, options); +} + /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. #[instrument(level = "trace", skip(tcx))] @@ -1041,27 +1540,19 @@ pub fn typeid_for_fnabi<'tcx>( typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types - - // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how - // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR - // interpretation today will allow skipped arguments to simply not be passed at a call-site. if !fn_abi.c_variadic { - let mut pushed_arg = false; - for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { - pushed_arg = true; - let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - if !pushed_arg { + if !fn_abi.args.is_empty() { + for arg in fn_abi.args.iter() { + let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + } else { // Empty parameter lists, whether declared as () or conventionally as (void), are // encoded with a void parameter specifier "v". typeid.push('v'); } } else { for n in 0..fn_abi.fixed_count as usize { - if fn_abi.args[n].mode == PassMode::Ignore { - continue; - } let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } @@ -1083,58 +1574,3 @@ pub fn typeid_for_fnabi<'tcx>( typeid } - -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - options: TypeIdOptions, -) -> String { - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) - .unwrap_or_else(|instance| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) - }); - - // If this instance is a method and self is a reference, get the impl it belongs to - let impl_def_id = tcx.impl_of_method(instance.def_id()); - if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { - // If this impl is not an inherent impl, get the trait it implements - if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { - // Transform the concrete self into a reference to a trait object - let existential_predicate = trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( - existential_predicate.skip_binder(), - )]); - // Is the concrete self mutable? - let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { - Ty::new_mut_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - } else { - Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - }; - - // Replace the concrete self in an fn_abi clone by the reference to a trait object - let mut fn_abi = fn_abi.clone(); - // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the - // other fields are never used. - fn_abi.args[0].layout.ty = self_ty; - - return typeid_for_fnabi(tcx, &fn_abi, options); - } - } - - typeid_for_fnabi(tcx, fn_abi, options) -} diff --git a/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs b/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs new file mode 100644 index 0000000000000..56d99b166a0de --- /dev/null +++ b/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs @@ -0,0 +1,55 @@ +// Verifies that type metadata identifiers for closures are emitted correctly. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 + +#![crate_type="lib"] +pub fn foo1(a: fn(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B:_ZTSFu3i32S_E]]") + a(1); +} + +pub fn bar1() { + foo1(|a| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar1{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo1(a); + // CHECK-LABEL: define{{.*}}4bar11a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +pub fn foo2(a: &dyn Fn(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo2{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B]]") + a(1); +} + +pub fn bar2() { + foo2(&|a: i32| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar2{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo2(&a); + // CHECK-LABEL: define{{.*}}4bar21a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +pub fn foo3(a: &mut dyn FnMut(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo3{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B]]") + a(1); +} + +pub fn bar3() { + foo3(&mut |a: i32| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar3{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo3(&mut a); + // CHECK-LABEL: define{{.*}}4bar31a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +// CHECK: ![[TYPE1A]] = !{i64 0, !"[[TYPE1B]]"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs new file mode 100644 index 0000000000000..24a985e847503 --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs @@ -0,0 +1,17 @@ +// Verifies that type metadata identifiers for closure declaration/definition FnAbis are +// disambiguated from closure call FnAbis. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +pub fn foo(f: fn()) { + // CHECK-LABEL: define{{.*}}3foo + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"_ZTSFvvE") + f(); +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs new file mode 100644 index 0000000000000..0eff39d8e4281 --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs @@ -0,0 +1,17 @@ +// Verifies that type metadata identifiers for dynamic Fn trait declaration/definition FnAbis are +// disambiguated from dynamic Fn trait call FnAbis. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +pub fn foo(f: &dyn Fn()) { + // CHECK-LABEL: define{{.*}}3foo + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"_ZTSFvvE") + f(); +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs index ab3d339989b38..240e9959e3c8b 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs @@ -6,6 +6,8 @@ #![crate_type="lib"] +trait FnSubtrait: Fn() {} + pub fn foo1(_: fn(i32) -> i32) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) { } @@ -13,33 +15,33 @@ pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) { } pub fn foo3(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { } // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo4(_: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo6(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo7(_: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo8(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo9(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo10(_: &dyn FnOnce(i32) -> i32) { } -// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo11(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { } -// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo12(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {} -// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo13(_: &dyn FnSubtrait) { } +// CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo14(_: &dyn FnSubtrait, _: &dyn FnSubtrait) { } +// CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo15(_: &dyn FnSubtrait, _: &dyn FnSubtrait, _: &dyn FnSubtrait) {} +// CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFu3i32S_EE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_S0_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_S3_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_S3_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_S3_E"} +// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvPFvvEE"} +// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvPFvvES_E"} +// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvPFvvES_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs new file mode 100644 index 0000000000000..4555c2bfe9ecd --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs @@ -0,0 +1,22 @@ +// Verifies that a secondary type metadata identifier is assigned to methods with their concrete +// self so they can be used as function pointers. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) {} + // CHECK: define{{.*}}3foo{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type ![[TYPE2:[0-9]+]] +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type1EE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index ca781a99296be..7807aaa6cdb95 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -47,42 +47,40 @@ pub fn foo() where let _: Type4 = ::bar; } -// Force arguments to be passed by using a reference. Otherwise, they may end up PassMode::Ignore - -pub fn foo1(_: &Type1) { } +pub fn foo1(_: Type1) { } // 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) { } // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &Type1, _: &Type1, _: &Type1) { } +pub fn foo3(_: Type1, _: Type1, _: Type1) { } // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &Type2) { } +pub fn foo4(_: Type2) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &Type2, _: &Type2) { } +pub fn foo5(_: Type2, _: Type2) { } // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &Type2, _: &Type2, _: &Type2) { } +pub fn foo6(_: Type2, _: Type2, _: Type2) { } // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &Type3) { } +pub fn foo7(_: Type3) { } // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &Type3, _: &Type3) { } +pub fn foo8(_: Type3, _: Type3) { } // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &Type3, _: &Type3, _: &Type3) { } +pub fn foo9(_: Type3, _: Type3, _: Type3) { } // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: &Type4) { } +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) { } +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) { } +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, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barEE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_S0_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFvvES_E"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFvvES_S_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_S_E"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_S_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_S1_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 38f507856bdee..3a1a09150eae9 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -12,9 +12,9 @@ use core::ffi::*; pub fn foo1(_: ()) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: (), _: c_void) { } -// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo3(_: (), _: c_void, _: c_void) { } -// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo4(_: *mut ()) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: *mut (), _: *mut c_void) { } @@ -131,6 +131,8 @@ pub fn foo60(_: &str, _: &str, _: &str) { } // CHECK: define{{.*}}5foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvvvE"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvvvvE"} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_E"} // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPvS_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 6f47f5e335577..0deda029c4b09 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -28,7 +28,7 @@ pub struct Type2<'a> { member3: &'a Type2<'a>, } -pub struct Bar(i32); +pub struct Bar; // repr(transparent) user-defined generic type #[repr(transparent)] diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs index cc7178e41c716..c3ff5062e5c5a 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs @@ -136,16 +136,15 @@ pub fn foo27(_: &dyn Trait5, _: &dyn Trait5, _: &dyn Trait // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_S2_E"} -// FIXME(rcvalle): Enforce autotraits ordering when encoding (e.g., alphabetical order) -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES3_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES3_S3_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_S3_E"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_E"} // CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_S3_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES4_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES4_S4_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES4_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES4_S4_E"} // CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEES2_E"} // CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEES2_S2_E"} // CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait2Iu5paramEu6regionEES3_E"} diff --git a/tests/codegen/sanitizer/cfi/normalize-integers.rs b/tests/codegen/sanitizer/cfi/normalize-integers.rs index 801ed312be5b1..210814eb9ae1f 100644 --- a/tests/codegen/sanitizer/cfi/normalize-integers.rs +++ b/tests/codegen/sanitizer/cfi/normalize-integers.rs @@ -41,6 +41,6 @@ pub fn foo11(_: (), _: usize, _: usize, _: usize) { } // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}E.normalized"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_E.normalized"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_S_E.normalized"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} diff --git a/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs b/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs new file mode 100644 index 0000000000000..2f86d2e9fe61c --- /dev/null +++ b/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs @@ -0,0 +1,15 @@ +// Verifies that using async closure works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 --edition=2021 +//@ run-pass + +#![feature(async_closure)] + +#[inline(never)] +fn foo(_: T) {} + +fn main() { + let a = async move |_: i32, _: i32| {}; + foo(a); +} diff --git a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs index a46a3afd7343a..caad3094d5a47 100644 --- a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs +++ b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs @@ -1,18 +1,10 @@ -// Tests that converting a closure to a function pointer works -// The notable thing being tested here is that when the closure does not capture anything, -// the call method from its Fn trait takes a ZST representing its environment. The compiler then -// uses the assumption that the ZST is non-passed to reify this into a function pointer. +// Verifies that casting a closure to a function pointer works. // -// This checks that the reified function pointer will have the expected alias set at its call-site. - //@ 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 +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 //@ run-pass -pub fn main() { +fn main() { let f: &fn() = &((|| ()) as _); f(); } diff --git a/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs new file mode 100644 index 0000000000000..f0ac80c773687 --- /dev/null +++ b/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs @@ -0,0 +1,11 @@ +// Verifies that casting a closure to a Fn trait object works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +#![feature(fn_traits)] +fn main() { + let f: &(dyn Fn()) = &(|| {}) as _; + f.call(()); +} diff --git a/tests/ui/sanitizer/cfi-drop-in-place.rs b/tests/ui/sanitizer/cfi-drop-in-place.rs new file mode 100644 index 0000000000000..3c8428cfaeb35 --- /dev/null +++ b/tests/ui/sanitizer/cfi-drop-in-place.rs @@ -0,0 +1,14 @@ +// Verifies that custom drops can be called on arbitraty trait objects. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +struct CustomDrop; +impl Drop for CustomDrop { + fn drop(&mut self) {} +} + +fn main() { + let _ = Box::new(CustomDrop) as Box; +} diff --git a/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs b/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs new file mode 100644 index 0000000000000..02e54278ceee8 --- /dev/null +++ b/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs @@ -0,0 +1,16 @@ +// Verifies that calling a dynamic Fn subtrait object works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +trait FnSubtrait: Fn() {} +impl FnSubtrait for T {} + +fn call_dynamic_fn_subtrait(f: &dyn FnSubtrait) { + f(); +} + +fn main() { + call_dynamic_fn_subtrait(&|| {}); +} diff --git a/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs new file mode 100644 index 0000000000000..27e5a62816fcb --- /dev/null +++ b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs @@ -0,0 +1,21 @@ +// Verifies that casting a method to a function pointer works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) {} +} + +fn main() { + let type1 = Type1 {}; + let f = ::foo; + f(&type1); +}