diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index dcde960258a51..6f5c1d1eb2d21 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -38,12 +38,15 @@ trait ArgAttributesExt { const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; -const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [ +const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [ (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias), (ArgAttribute::NonNull, llvm::AttributeKind::NonNull), (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly), (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef), (ArgAttribute::Writable, llvm::AttributeKind::Writable), + // We can map our internal NoFree attribute (which forbids freeing the underlying allocation) + // to LLVM's weaker attribute (which forbids freeing it through this specific pointer). + (ArgAttribute::NoFree, llvm::AttributeKind::NoFree), ]; const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [ @@ -75,7 +78,9 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' // Only apply remaining attributes when optimizing if cx.sess().opts.optimize != config::OptLevel::No { let deref = this.pointee_size.bytes(); - if deref != 0 { + // dereferenceable in LLVM currently implies nofree, so only emit dereferenceable if nofree + // is also set. + if deref != 0 && regular.contains(ArgAttribute::NoFree) { if regular.contains(ArgAttribute::NonNull) { attrs.push(llvm::CreateDereferenceableAttr(cx.llcx, deref)); } else { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9ecf5afcbcb35..36d5bf8362fa0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -294,6 +294,7 @@ pub(crate) enum AttributeKind { SanitizeRealtimeNonblocking = 47, SanitizeRealtimeBlocking = 48, Convergent = 49, + NoFree = 50, } /// LLVMIntPredicate diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 91bb1c9733630..bfaa00dc0ebaa 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -383,6 +383,7 @@ enum class LLVMRustAttributeKind { SanitizeRealtimeNonblocking = 47, SanitizeRealtimeBlocking = 48, Convergent = 49, + NoFree = 50, }; static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { @@ -481,6 +482,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { return Attribute::SanitizeRealtimeBlocking; case LLVMRustAttributeKind::Convergent: return Attribute::Convergent; + case LLVMRustAttributeKind::NoFree: + return Attribute::NoFree; } report_fatal_error("bad LLVMRustAttributeKind"); } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index e38e5393d967d..4efd06a9326d0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -979,33 +979,19 @@ where } ty::Ref(_, ty, mt) if offset.bytes() == 0 => { tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| { - let (size, kind); - match mt { + let kind = match mt { hir::Mutability::Not => { let frozen = optimize && ty.is_freeze(tcx, typing_env); - - // Non-frozen shared references are not necessarily dereferenceable for the entire duration of the function - // (see ) - // (if we had "dereferenceable on entry", we could support this) - size = if frozen { layout.size } else { Size::ZERO }; - - kind = PointerKind::SharedRef { frozen }; + PointerKind::SharedRef { frozen } } hir::Mutability::Mut => { let unpin = optimize && ty.is_unpin(tcx, typing_env) && ty.is_unsafe_unpin(tcx, typing_env); - - // Mutable references to potentially self-referential types are not - // necessarily dereferenceable for the entire duration of the function - // (see ) - // (if we had "dereferenceable on entry", we could support this) - size = if unpin { layout.size } else { Size::ZERO }; - - kind = PointerKind::MutableRef { unpin }; + PointerKind::MutableRef { unpin } } }; - PointeeInfo { safe: Some(kind), size, align: layout.align.abi } + PointeeInfo { safe: Some(kind), size: layout.size, align: layout.align.abi } }) } @@ -1021,12 +1007,7 @@ where && pointee.is_unsafe_unpin(tcx, typing_env), global: this.ty.is_box_global(tcx), }), - - // `Box` are not necessarily dereferenceable for the entire duration of the function as - // they can be deallocated at any time. - // (if we had "dereferenceable on entry", we could support this) - size: Size::ZERO, - + size: layout.size, align: layout.align.abi, }) } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index e6eb68c1fe10d..27b8331fc48d4 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -122,6 +122,10 @@ mod attr_impl { const InReg = 1 << 6; const NoUndef = 1 << 7; const Writable = 1 << 8; + /// It is UB for the allocation that this pointer points to to be freed + /// while the function is executing. Only valid on arguments (including + /// return values that are passed indirectly as arguments). + const NoFree = 1 << 9; } } rustc_data_structures::external_bitflags_debug! { ArgAttribute } @@ -143,9 +147,8 @@ pub enum ArgExtension { pub struct ArgAttributes { pub regular: ArgAttribute, pub arg_ext: ArgExtension, - /// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call - /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be - /// set on a null pointer, but all non-null pointers must be dereferenceable). + /// If the pointer is not null, the minimum dereferenceable size of the pointee, at the time of + /// function entry (for arguments) or function return (for return values). pub pointee_size: Size, /// The minimum alignment of the pointee, if any. pub pointee_align: Option, @@ -408,7 +411,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> { .set(ArgAttribute::NoAlias) .set(ArgAttribute::CapturesAddress) .set(ArgAttribute::NonNull) - .set(ArgAttribute::NoUndef); + .set(ArgAttribute::NoUndef) + .set(ArgAttribute::NoFree); attrs.pointee_size = layout.size; attrs.pointee_align = Some(layout.align.abi); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 02739ef6a4135..e782557d126bf 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -356,13 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>( Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); } - // LLVM dereferenceable attribute has unclear semantics on the return type, - // they seem to be "dereferenceable until the end of the program", which is - // generally, not valid for references. See - // - if !is_return { - attrs.pointee_size = pointee.size; - }; + attrs.pointee_size = pointee.size; if let Some(kind) = pointee.safe { // The aliasing rules for `Box` are still not decided, but currently we emit @@ -407,6 +401,26 @@ fn arg_attrs_for_rust_scalar<'tcx>( } } + // NoFree is not valid on return values. If it were, it would mean something like + // "will not be freed until the end of the program", which is generally not valid for + // references. + let no_free = !is_return + && match kind { + // Non-frozen shared references are not necessarily dereferenceable for the + // entire duration of the function + // (see ). + PointerKind::SharedRef { frozen } => frozen, + // Mutable references to potentially self-referential types are not necessarily + // dereferenceable for the entire duration of the function + // (see ). + PointerKind::MutableRef { unpin } => unpin, + // Box may be deallocated during execution of the function. + PointerKind::Box { .. } => false, + }; + if no_free { + attrs.set(ArgAttribute::NoFree); + } + if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return { attrs.set(ArgAttribute::ReadOnly); attrs.set(ArgAttribute::CapturesReadOnly); @@ -423,11 +437,18 @@ fn fn_abi_sanity_check<'tcx>( fn_abi: &FnAbi<'tcx, Ty<'tcx>>, spec_abi: ExternAbi, ) { + fn fn_arg_attrs_sanity_check(attrs: &ArgAttributes, is_ret: bool) { + if attrs.regular.contains(ArgAttribute::NoFree) { + assert!(!is_ret, "NoFree not valid on return values"); + } + } + fn fn_arg_sanity_check<'tcx>( cx: &LayoutCx<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, spec_abi: ExternAbi, arg: &ArgAbi<'tcx, Ty<'tcx>>, + is_ret: bool, ) { let tcx = cx.tcx(); @@ -452,7 +473,7 @@ fn fn_abi_sanity_check<'tcx>( PassMode::Ignore => { assert!(arg.layout.is_zst()); } - PassMode::Direct(_) => { + PassMode::Direct(attrs) => { // Here the Rust type is used to determine the actual ABI, so we have to be very // careful. Scalar/Vector is fine, since backends will generally use // `layout.backend_repr` and ignore everything else. We should just reject @@ -482,8 +503,9 @@ fn fn_abi_sanity_check<'tcx>( ); } } + fn_arg_attrs_sanity_check(attrs, is_ret); } - PassMode::Pair(_, _) => { + PassMode::Pair(attrs1, attrs2) => { // Similar to `Direct`, we need to make sure that backends use `layout.backend_repr` // and ignore the rest of the layout. assert!( @@ -491,19 +513,23 @@ fn fn_abi_sanity_check<'tcx>( "PassMode::Pair for type {}", arg.layout.ty ); + fn_arg_attrs_sanity_check(attrs1, is_ret); + fn_arg_attrs_sanity_check(attrs2, is_ret); } PassMode::Cast { .. } => { // `Cast` means "transmute to `CastType`"; that only makes sense for sized types. assert!(arg.layout.is_sized()); } - PassMode::Indirect { meta_attrs: None, .. } => { + PassMode::Indirect { meta_attrs: None, attrs, .. } => { // No metadata, must be sized. // Conceptually, unsized arguments must be copied around, which requires dynamically // determining their size, which we cannot do without metadata. Consult // t-opsem before removing this check. assert!(arg.layout.is_sized()); + // Indirect returns are arguments from an ABI perspective. + fn_arg_attrs_sanity_check(attrs, false); } - PassMode::Indirect { meta_attrs: Some(_), on_stack, .. } => { + PassMode::Indirect { meta_attrs: Some(meta_attrs), attrs, on_stack } => { // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); // Also, must not be `extern` type. @@ -515,14 +541,17 @@ fn fn_abi_sanity_check<'tcx>( // t-opsem before removing this check. panic!("unsized arguments must not be `extern` types"); } + // Indirect returns are arguments from an ABI perspective. + fn_arg_attrs_sanity_check(attrs, false); + fn_arg_attrs_sanity_check(meta_attrs, false); } } } for arg in fn_abi.args.iter() { - fn_arg_sanity_check(cx, fn_abi, spec_abi, arg); + fn_arg_sanity_check(cx, fn_abi, spec_abi, arg, false); } - fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret); + fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret, true); } #[tracing::instrument( diff --git a/tests/codegen-llvm/addr-of-mutate.rs b/tests/codegen-llvm/addr-of-mutate.rs index d1939391b25de..def73bf8190d7 100644 --- a/tests/codegen-llvm/addr-of-mutate.rs +++ b/tests/codegen-llvm/addr-of-mutate.rs @@ -5,7 +5,7 @@ // Test for the absence of `readonly` on the argument when it is mutated via `&raw const`. // See . -// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias noundef align 1{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(128) %x) +// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias nofree noundef align 1{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(128) %x) #[no_mangle] pub fn foo(x: [u8; 128]) -> u8 { let ptr = core::ptr::addr_of!(x).cast_mut(); @@ -15,7 +15,7 @@ pub fn foo(x: [u8; 128]) -> u8 { x[0] } -// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias noundef align {{[0-9]+}}{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) +// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias nofree noundef align {{[0-9]+}}{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) #[no_mangle] pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool { let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1.1).cast_mut(); @@ -24,7 +24,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool { } // If going through a deref (and there are no other mutating accesses), then `readonly` is fine. -// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias noundef readonly align {{[0-9]+}}{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) +// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias nofree noundef readonly align {{[0-9]+}}{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) #[no_mangle] pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool { let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut(); diff --git a/tests/codegen-llvm/drop-in-place-noalias.rs b/tests/codegen-llvm/drop-in-place-noalias.rs index bff2f52781f23..86e0caa1b287d 100644 --- a/tests/codegen-llvm/drop-in-place-noalias.rs +++ b/tests/codegen-llvm/drop-in-place-noalias.rs @@ -7,7 +7,7 @@ use std::marker::PhantomPinned; -// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias noundef align 4 dereferenceable(12) %{{.+}}) +// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias nofree noundef align 4 dereferenceable(12) %{{.+}}) // CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructNotUnpin{{.*}}(ptr noundef nonnull align 4 %{{.+}}) diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index 6eab9a58b47ad..0832778a6ed93 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -80,7 +80,7 @@ pub fn option_nonzero_int(x: Option>) -> Option> { x } -// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) +// CHECK: @readonly_borrow(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn readonly_borrow(_: &i32) {} @@ -91,12 +91,12 @@ pub fn readonly_borrow_ret() -> &'static i32 { loop {} } -// CHECK: @static_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) +// CHECK: @static_borrow(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // static borrow may be captured #[no_mangle] pub fn static_borrow(_: &'static i32) {} -// CHECK: @named_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) +// CHECK: @named_borrow(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // borrow with named lifetime may be captured #[no_mangle] pub fn named_borrow<'r>(_: &'r i32) {} @@ -106,12 +106,12 @@ pub fn named_borrow<'r>(_: &'r i32) {} #[no_mangle] pub fn unsafe_borrow(_: &UnsafeInner) {} -// CHECK: @mutable_unsafe_borrow(ptr noalias noundef align 2 dereferenceable(2) %_1) +// CHECK: @mutable_unsafe_borrow(ptr noalias nofree noundef align 2 dereferenceable(2) %_1) // ... unless this is a mutable borrow, those never alias #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {} -// CHECK: @mutable_borrow(ptr noalias noundef align 4 dereferenceable(4) %_1) +// CHECK: @mutable_borrow(ptr noalias nofree noundef align 4 dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn mutable_borrow(_: &mut i32) {} @@ -129,25 +129,25 @@ pub fn mutable_borrow_ret() -> &'static mut i32 { // . pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {} -// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) +// CHECK: @notunpin_borrow(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // But `&NotUnpin` behaves perfectly normal. #[no_mangle] pub fn notunpin_borrow(_: &NotUnpin) {} -// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias noundef readonly align 4{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable(32) %_1) +// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias nofree noundef readonly align 4{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable(32) %_1) #[no_mangle] pub fn indirect_struct(_: S) {} -// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(32) %_1) +// CHECK: @borrowed_struct(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(32) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn borrowed_struct(_: &S) {} -// CHECK: @option_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable_or_null(4) %_x) +// CHECK: @option_borrow(ptr noalias nofree noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable_or_null(4) %_x) #[no_mangle] pub fn option_borrow(_x: Option<&i32>) {} -// CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %_x) +// CHECK: @option_borrow_mut(ptr noalias nofree noundef align 4 dereferenceable_or_null(4) %_x) #[no_mangle] pub fn option_borrow_mut(_x: Option<&mut i32>) {} @@ -170,7 +170,7 @@ pub fn _box(x: Box) -> Box { // With a custom allocator, it should *not* have `noalias`. (See // for why.) The second argument is the allocator, // which is a reference here that still carries `noalias` as usual. -// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %x.1) +// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias nofree noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %x.1) #[no_mangle] pub fn _box_custom(x: Box) { drop(x) @@ -182,7 +182,7 @@ pub fn notunpin_box(x: Box) -> Box { x } -// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias noundef{{( writable)?}} sret([32 x i8]) align 4{{( captures\(none\))?}} dereferenceable(32){{( %_0)?}}) +// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias nofree noundef{{( writable)?}} sret([32 x i8]) align 4{{( captures\(none\))?}} dereferenceable(32){{( %_0)?}}) #[no_mangle] pub fn struct_return() -> S { S { _field: [0, 0, 0, 0, 0, 0, 0, 0] } @@ -194,14 +194,14 @@ pub fn struct_return() -> S { pub fn helper(_: usize) {} // CHECK: @slice( -// CHECK-SAME: ptr noalias noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %_1.0, +// CHECK-SAME: ptr noalias nofree noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %_1.0, // CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn slice(_: &[u8]) {} // CHECK: @mutable_slice( -// CHECK-SAME: ptr noalias noundef nonnull %_1.0, +// CHECK-SAME: ptr noalias nofree noundef nonnull %_1.0, // CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] @@ -219,13 +219,13 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {} pub fn raw_slice(_: *const [u8]) {} // CHECK: @str( -// CHECK-SAME: ptr noalias noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %_1.0, +// CHECK-SAME: ptr noalias nofree noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %_1.0, // CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn str(_: &[u8]) {} -// CHECK: @trait_borrow(ptr noundef nonnull %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) +// CHECK: @trait_borrow(ptr noundef nonnull %_1.0, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn trait_borrow(_: &dyn Drop) {} @@ -238,31 +238,31 @@ pub fn option_trait_borrow(x: Option<&dyn Drop>) {} #[no_mangle] pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {} -// CHECK: @trait_raw(ptr noundef %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) +// CHECK: @trait_raw(ptr noundef %_1.0, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) #[no_mangle] pub fn trait_raw(_: *const dyn Drop) {} // Ensure that `Box` gets `noalias` when the right traits are present, but removing *either* `Unpin` // or `UnsafeUnpin` is enough to lose the attribute. -// CHECK: @trait_box(ptr noalias noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) +// CHECK: @trait_box(ptr noalias noundef nonnull{{( %0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) #[no_mangle] pub fn trait_box(_: Box) {} -// CHECK: @trait_box_pin1(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) +// CHECK: @trait_box_pin1(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) #[no_mangle] pub fn trait_box_pin1(_: Box) {} -// CHECK: @trait_box_pin2(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) +// CHECK: @trait_box_pin2(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) #[no_mangle] pub fn trait_box_pin2(_: Box) {} // Same for mutable references (with a non-zero minimal size so that we also see the // `dereferenceable` disappear). -// CHECK: @trait_mutref(ptr noalias noundef align 4 dereferenceable(4){{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) +// CHECK: @trait_mutref(ptr noalias nofree noundef align 4 dereferenceable(4){{( %_1.0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) #[no_mangle] pub fn trait_mutref(_: &mut (i32, dyn Drop + Unpin + UnsafeUnpin)) {} -// CHECK: @trait_mutref_pin1(ptr noundef nonnull align 4{{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) +// CHECK: @trait_mutref_pin1(ptr noundef nonnull align 4{{( %_1.0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) #[no_mangle] pub fn trait_mutref_pin1(_: &mut (i32, dyn Drop + Unpin)) {} -// CHECK: @trait_mutref_pin2(ptr noundef nonnull align 4{{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) +// CHECK: @trait_mutref_pin2(ptr noundef nonnull align 4{{( %_1.0)?}}, {{.+}} noalias nofree noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) #[no_mangle] pub fn trait_mutref_pin2(_: &mut (i32, dyn Drop + UnsafeUnpin)) {} @@ -275,7 +275,7 @@ pub fn trait_option( } // CHECK: { ptr, [[USIZE]] } @return_slice( -// CHECK-SAME: ptr noalias noundef nonnull readonly align 2{{( captures\(address, read_provenance\))?}} %x.0, +// CHECK-SAME: ptr noalias nofree noundef nonnull readonly align 2{{( captures\(address, read_provenance\))?}} %x.0, // CHECK-SAME: [[USIZE]] noundef range({{i32 0, 1073741824|i64 0, 4611686018427387904}}) %x.1) #[no_mangle] pub fn return_slice(x: &[u16]) -> &[u16] { diff --git a/tests/codegen-llvm/llvm-writable.rs b/tests/codegen-llvm/llvm-writable.rs index ea245fc3a6e7a..0c72fb8a1dd9f 100644 --- a/tests/codegen-llvm/llvm-writable.rs +++ b/tests/codegen-llvm/llvm-writable.rs @@ -4,15 +4,15 @@ #![crate_type = "lib"] #![feature(rustc_attrs, unsafe_pinned)] -// CHECK: @mutable_borrow(ptr noalias noundef writable align 4 dereferenceable(4) %_1) +// CHECK: @mutable_borrow(ptr noalias nofree noundef writable align 4 dereferenceable(4) %_1) #[no_mangle] pub fn mutable_borrow(_: &mut i32) {} -// CHECK: @mutable_unsafe_borrow(ptr noalias noundef writable align 2 dereferenceable(2) %_1) +// CHECK: @mutable_unsafe_borrow(ptr noalias nofree noundef writable align 2 dereferenceable(2) %_1) #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut std::cell::UnsafeCell) {} -// CHECK: @option_borrow_mut(ptr noalias noundef writable align 4 dereferenceable_or_null(4) %_1) +// CHECK: @option_borrow_mut(ptr noalias nofree noundef writable align 4 dereferenceable_or_null(4) %_1) #[no_mangle] pub fn option_borrow_mut(_: Option<&mut i32>) {} @@ -24,7 +24,7 @@ pub fn box_moved(_: Box) {} #[no_mangle] pub fn unsafe_pinned_borrow_mut(_: &mut std::pin::UnsafePinned) {} -// CHECK: @mutable_borrow_no_writable(ptr noalias noundef align 4 dereferenceable(4) %_1) +// CHECK: @mutable_borrow_no_writable(ptr noalias nofree noundef align 4 dereferenceable(4) %_1) #[no_mangle] #[rustc_no_writable] pub fn mutable_borrow_no_writable(_: &mut i32) {} diff --git a/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs b/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs index 4c61224ac6d8f..68f1267c2a444 100644 --- a/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs +++ b/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs @@ -256,11 +256,11 @@ pub struct IntDoubleInt { c: i32, } -// CHECK: define void @f_int_double_int_s_arg(ptr{{( dead_on_return)?}} noalias noundef align 8{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(24) %a) +// CHECK: define void @f_int_double_int_s_arg(ptr{{( dead_on_return)?}} noalias nofree noundef align 8{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(24) %a) #[no_mangle] pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {} -// CHECK: define void @f_ret_int_double_int_s(ptr{{( dead_on_unwind)?}} noalias noundef{{( writable)?}} sret([24 x i8]) align 8{{( captures\(address\))?}} dereferenceable(24) %_0) +// CHECK: define void @f_ret_int_double_int_s(ptr{{( dead_on_unwind)?}} noalias nofree noundef{{( writable)?}} sret([24 x i8]) align 8{{( captures\(address\))?}} dereferenceable(24) %_0) #[no_mangle] pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt { IntDoubleInt { a: 1, b: 2., c: 3 } diff --git a/tests/codegen-llvm/packed.rs b/tests/codegen-llvm/packed.rs index 6f62719282eac..84e2b0897c09c 100644 --- a/tests/codegen-llvm/packed.rs +++ b/tests/codegen-llvm/packed.rs @@ -52,7 +52,7 @@ pub struct BigPacked2 { #[no_mangle] pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 { // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] - // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) + // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} nofree noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) // check that calls whose destination is a field of a packed struct // go through an alloca rather than calling the function with an @@ -64,7 +64,7 @@ pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 { #[no_mangle] pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 { // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] - // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) + // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} nofree noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) // check that calls whose destination is a field of a packed struct // go through an alloca rather than calling the function with an diff --git a/tests/codegen-llvm/slice-iter-nonnull.rs b/tests/codegen-llvm/slice-iter-nonnull.rs index 6b8416662314f..8ef313f2d6715 100644 --- a/tests/codegen-llvm/slice-iter-nonnull.rs +++ b/tests/codegen-llvm/slice-iter-nonnull.rs @@ -51,7 +51,7 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&' // attribute is there, and confirms adding the assume back doesn't do anything. // CHECK-LABEL: @slice_iter_new -// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1) +// CHECK-SAME: (ptr noalias nofree noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1) #[no_mangle] pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { // CHECK-NOT: slice @@ -66,7 +66,7 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { } // CHECK-LABEL: @slice_iter_mut_new -// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1) +// CHECK-SAME: (ptr noalias nofree noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1) #[no_mangle] pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> { // CHECK-NOT: slice diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index f297aa984dd2e..edea2d5772280 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some( diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index f297aa984dd2e..edea2d5772280 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some( diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index f297aa984dd2e..edea2d5772280 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some( diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index f297aa984dd2e..edea2d5772280 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some( diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index b154c3fa201e5..125150f2c2e3f 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -487,7 +487,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(32 bytes), pointee_align: Some( @@ -560,7 +560,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(128 bytes), pointee_align: Some( @@ -966,7 +966,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, mode: Direct( ArgAttributes { - regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef, + regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef | NoFree, arg_ext: None, pointee_size: Size(2 bytes), pointee_align: Some( diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 68bcd736e47ce..5f05174c12760 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -487,7 +487,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(32 bytes), pointee_align: Some( @@ -560,7 +560,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(128 bytes), pointee_align: Some( @@ -966,7 +966,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, mode: Direct( ArgAttributes { - regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef, + regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef | NoFree, arg_ext: None, pointee_size: Size(2 bytes), pointee_align: Some( diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 68bcd736e47ce..5f05174c12760 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -487,7 +487,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(32 bytes), pointee_align: Some( @@ -560,7 +560,7 @@ error: ABIs are not compatible }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(128 bytes), pointee_align: Some( @@ -966,7 +966,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, mode: Direct( ArgAttributes { - regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef, + regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef | NoFree, arg_ext: None, pointee_size: Size(2 bytes), pointee_align: Some( diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index d8cc39cb2e4e3..7c4b0240e9f31 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -40,7 +40,7 @@ error: fn_abi_of(extern_c) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(1 bytes), pointee_align: Some( diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr index 584416f58f861..45edd7bc0e0ee 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(32 bytes), pointee_align: Some( diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr index b5e0e8589af16..1e203b93e66b3 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -27,7 +27,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(24 bytes), pointee_align: Some( @@ -105,7 +105,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(24 bytes), pointee_align: Some( @@ -185,7 +185,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { }, mode: Indirect { attrs: ArgAttributes { - regular: CapturesAddress | NoAlias | NonNull | NoUndef, + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, arg_ext: None, pointee_size: Size(24 bytes), pointee_align: Some(