Auto merge of #150447 - WaffleLapkin:maybe-dangling-semantics, r=RalfJung

Implement `MaybeDangling` compiler support



Tracking issue: https://github.com/rust-lang/rust/issues/118166



cc @RalfJung
This commit is contained in:
bors
2026-03-05 12:21:27 +00:00
17 changed files with 232 additions and 133 deletions
+6 -5
View File
@@ -2145,21 +2145,22 @@ pub enum PointerKind {
}
/// Encodes extra information we have about a pointer.
///
/// Note that this information is advisory only, and backends are free to ignore it:
/// if the information is wrong, that can cause UB, but if the information is absent,
/// that must always be okay.
#[derive(Copy, Clone, Debug)]
pub struct PointeeInfo {
/// If this is `None`, then this is a raw pointer, so size and alignment are not guaranteed to
/// be reliable.
/// If this is `None`, then this is a raw pointer.
pub safe: Option<PointerKind>,
/// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes.
/// If `size` is not zero, then the pointer is either null or dereferenceable for this many bytes
/// (independent of `safe`).
///
/// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration
/// of this function call", i.e. it is UB for the memory that this pointer points to be freed
/// while this function is still running.
/// The size can be zero if the pointer is not dereferenceable.
pub size: Size,
/// If `safe` is `Some`, then the pointer is aligned as indicated.
/// The pointer is guaranteed to be aligned this much (independent of `safe`).
pub align: Align,
}
+13 -6
View File
@@ -10,7 +10,7 @@
use rustc_index::IndexVec;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::config::OutputFilenames;
use rustc_span::Symbol;
@@ -924,19 +924,26 @@ fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
count,
}) => {
let dst = codegen_operand(fx, dst);
let pointee = dst
.layout()
.pointee_info_at(fx, rustc_abi::Size::ZERO)
.expect("Expected pointer");
let &ty::RawPtr(pointee, _) = dst.layout().ty.kind() else {
bug!("expected pointer")
};
let pointee_layout = fx
.tcx
.layout_of(fx.typing_env().as_query_input(pointee))
.expect("expected pointee to have a layout");
let elem_size: u64 = pointee_layout.layout.size().bytes();
let dst = dst.load_scalar(fx);
let src = codegen_operand(fx, src).load_scalar(fx);
let count = codegen_operand(fx, count).load_scalar(fx);
let elem_size: u64 = pointee.size.bytes();
let bytes = if elem_size != 1 {
fx.bcx.ins().imul_imm(count, elem_size as i64)
} else {
count
};
fx.bcx.call_memcpy(fx.target_config, dst, src, bytes);
}
},
+3 -1
View File
@@ -288,7 +288,9 @@ fn scalar_gcc_type_at<'gcc>(
Float(f) => cx.type_from_float(f),
Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) {
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset)
&& pointee.align > rustc_abi::Align::ONE
{
cx.type_pointee_for_align(pointee.align)
} else {
cx.type_i8()
+1 -1
View File
@@ -715,7 +715,7 @@ fn scalar_load_metadata<'a, 'll, 'tcx>(
}
if let Some(pointee) = layout.pointee_info_at(bx, offset)
&& let Some(_) = pointee.safe
&& pointee.align > Align::ONE
{
bx.align_metadata(load, pointee.align);
}
@@ -1,5 +1,5 @@
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
use rustc_middle::span_bug;
use rustc_middle::{bug, span_bug, ty};
use tracing::instrument;
use super::{FunctionCx, LocalRef};
@@ -77,15 +77,21 @@ pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Stateme
let dst_val = self.codegen_operand(bx, dst);
let src_val = self.codegen_operand(bx, src);
let count = self.codegen_operand(bx, count).immediate();
let pointee_layout = dst_val
.layout
.pointee_info_at(bx, rustc_abi::Size::ZERO)
.expect("Expected pointer");
let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes()));
let align = pointee_layout.align;
let &ty::RawPtr(pointee, _) = dst_val.layout.ty.kind() else {
bug!("expected pointer")
};
let pointee_layout = bx
.tcx()
.layout_of(bx.typing_env().as_query_input(pointee))
.expect("expected pointee to have a layout");
let elem_size = pointee_layout.layout.size().bytes();
let bytes = bx.mul(count, bx.const_usize(elem_size));
let align = pointee_layout.layout.align.abi;
let dst = dst_val.immediate();
let src = src_val.immediate();
bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None);
}
mir::StatementKind::FakeRead(..)
+2 -1
View File
@@ -341,7 +341,8 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1);
MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1);
BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0);
MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
+11
View File
@@ -62,6 +62,8 @@ impl AdtFlags: u16 {
const IS_PIN_PROJECT = 1 << 12;
/// Indicates whether the type is `FieldRepresentingType`.
const IS_FIELD_REPRESENTING_TYPE = 1 << 13;
/// Indicates whether the type is `MaybeDangling<_>`.
const IS_MAYBE_DANGLING = 1 << 14;
}
}
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -373,6 +375,9 @@ pub(super) fn new(
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
flags |= AdtFlags::IS_MANUALLY_DROP;
}
if tcx.is_lang_item(did, LangItem::MaybeDangling) {
flags |= AdtFlags::IS_MAYBE_DANGLING;
}
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
flags |= AdtFlags::IS_UNSAFE_CELL;
}
@@ -500,6 +505,12 @@ pub fn is_manually_drop(self) -> bool {
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
}
/// Returns `true` if this is `MaybeDangling<T>`.
#[inline]
pub fn is_maybe_dangling(self) -> bool {
self.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
}
/// Returns `true` if this is `Pin<T>`.
#[inline]
pub fn is_pin(self) -> bool {
+70 -49
View File
@@ -1022,41 +1022,80 @@ fn ty_and_layout_pointee_info_at(
let tcx = cx.tcx();
let typing_env = cx.typing_env();
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
let optimize = tcx.sess.opts.optimize != OptLevel::No;
let pointee_info = match *this.ty.kind() {
ty::RawPtr(p_ty, _) if offset.bytes() == 0 => {
tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
})
}
ty::FnPtr(..) if offset.bytes() == 0 => {
tcx.layout_of(typing_env.as_query_input(this.ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
})
ty::RawPtr(_, _) | ty::FnPtr(..) if offset.bytes() == 0 => {
Some(PointeeInfo { safe: None, size: Size::ZERO, align: Align::ONE })
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
let optimize = tcx.sess.opts.optimize != OptLevel::No;
let kind = match mt {
hir::Mutability::Not => {
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
}
hir::Mutability::Mut => PointerKind::MutableRef {
unpin: optimize
&& ty.is_unpin(tcx, typing_env)
&& ty.is_unsafe_unpin(tcx, typing_env),
},
};
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| {
let (size, 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 <https://github.com/rust-lang/rust/pull/98017>)
// (if we had "dereferenceable on entry", we could support this)
size = if frozen { layout.size } else { Size::ZERO };
kind = 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 <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>)
// (if we had "dereferenceable on entry", we could support this)
size = if unpin { layout.size } else { Size::ZERO };
kind = PointerKind::MutableRef { unpin };
}
};
PointeeInfo { safe: Some(kind), size, align: layout.align.abi }
})
}
ty::Adt(..)
if offset.bytes() == 0
&& let Some(pointee) = this.ty.boxed_ty() =>
{
tcx.layout_of(typing_env.as_query_input(pointee)).ok().map(|layout| PointeeInfo {
safe: Some(PointerKind::Box {
// Same logic as for mutable references above.
unpin: optimize
&& pointee.is_unpin(tcx, typing_env)
&& 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,
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
})
}
ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => {
Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset).map(|info| {
PointeeInfo {
// Mark the pointer as raw
// (thus removing noalias/readonly/etc in case of the llvm backend)
safe: None,
// Make sure we don't assert dereferenceability of the pointer.
size: Size::ZERO,
// Preserve the alignment assertion! That is required even inside `MaybeDangling`.
align: info.align,
}
})
}
@@ -1098,7 +1137,7 @@ fn ty_and_layout_pointee_info_at(
}
}
Variants::Multiple { .. } => None,
_ => Some(this),
Variants::Empty | Variants::Single { .. } => Some(this),
};
if let Some(variant) = data_variant
@@ -1135,24 +1174,6 @@ fn ty_and_layout_pointee_info_at(
}
}
// Fixup info for the first field of a `Box`. Recursive traversal will have found
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
// will still be `None`.
if let Some(ref mut pointee) = result {
if offset.bytes() == 0
&& let Some(boxed_ty) = this.ty.boxed_ty()
{
debug_assert!(pointee.safe.is_none());
let optimize = tcx.sess.opts.optimize != OptLevel::No;
pointee.safe = Some(PointerKind::Box {
unpin: optimize
&& boxed_ty.is_unpin(tcx, typing_env)
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
global: this.ty.is_box_global(tcx),
});
}
}
result
}
};
+1
View File
@@ -1217,6 +1217,7 @@
maxnumf128,
may_dangle,
may_unwind,
maybe_dangling,
maybe_uninit,
maybe_uninit_uninit,
maybe_uninit_zeroed,
+41 -35
View File
@@ -1,7 +1,7 @@
use std::iter;
use rustc_abi::Primitive::Pointer;
use rustc_abi::{BackendRepr, ExternAbi, PointerKind, Scalar, Size};
use rustc_abi::{Align, BackendRepr, ExternAbi, PointerKind, Scalar, Size};
use rustc_data_structures::assert_matches;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
@@ -340,7 +340,15 @@ fn fn_abi_of_instance_raw<'tcx>(
})
}
// Handle safe Rust thin and wide pointers.
/// Returns argument attributes for a scalar argument.
///
/// `drop_target_pointee`, if set, causes pointer-typed scalars to be treated like mutable
/// references to the given type. This is used to special-case the argument of `ptr::drop_in_place`,
/// interpreting it as `&mut T` instead of `*mut T`, for the purposes of attributes (which is valid
/// as per its safety contract). If `drop_target_pointee` is set, `offset` must be 0 and `layout.ty`
/// must be a pointer to the given type. Note that for wide pointers this function is called twice
/// -- once for the data pointer and once for the vtable pointer. `drop_target_pointee` must only
/// be set for the data pointer.
fn arg_attrs_for_rust_scalar<'tcx>(
cx: LayoutCx<'tcx>,
scalar: Scalar,
@@ -373,42 +381,31 @@ fn arg_attrs_for_rust_scalar<'tcx>(
let tcx = cx.tcx();
if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
let kind = if let Some(kind) = pointee.safe {
Some(kind)
} else if let Some(pointee) = drop_target_pointee {
assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap());
assert_eq!(offset, Size::ZERO);
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee);
let layout = cx.layout_of(mutref).unwrap();
layout.pointee_info_at(&cx, offset).and_then(|pi| pi.safe)
} else {
None
};
if let Some(kind) = kind {
let drop_target_pointee_info = drop_target_pointee.and_then(|pointee| {
assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap());
assert_eq!(offset, Size::ZERO);
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee);
let layout = cx.layout_of(mutref).unwrap();
layout.pointee_info_at(&cx, offset)
});
if let Some(pointee) = drop_target_pointee_info.or_else(|| layout.pointee_info_at(&cx, offset))
{
if pointee.align > Align::ONE {
attrs.pointee_align =
Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment()));
}
attrs.pointee_size = match kind {
// 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
// <https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem/topic/LLVM.20dereferenceable.20on.20return.20type/with/563001493>
_ if is_return => Size::ZERO,
// `Box` are not necessarily dereferenceable for the entire duration of the function as
// they can be deallocated at any time. Same for non-frozen shared references (see
// <https://github.com/rust-lang/rust/pull/98017>), and for mutable references to
// potentially self-referential types (see
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
// to say "dereferenceable on entry" we could use it here.
PointerKind::Box { .. }
| PointerKind::SharedRef { frozen: false }
| PointerKind::MutableRef { unpin: false } => Size::ZERO,
PointerKind::SharedRef { frozen: true }
| PointerKind::MutableRef { unpin: true } => pointee.size,
};
// 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
// <https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem/topic/LLVM.20dereferenceable.20on.20return.20type/with/563001493>
if !is_return {
attrs.pointee_size = pointee.size;
};
if let Some(kind) = pointee.safe {
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
// `noalias` for it. This can be turned off using an unstable flag.
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
@@ -619,7 +616,16 @@ fn fn_abi_new_uncached<'tcx>(
};
Ok(ArgAbi::new(cx, layout, |scalar, offset| {
arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, drop_target_pointee)
arg_attrs_for_rust_scalar(
*cx,
scalar,
layout,
offset,
is_return,
// Only set `drop_target_pointee` for the data part of a wide pointer.
// See `arg_attrs_for_rust_scalar` docs for more information.
drop_target_pointee.filter(|_| offset == Size::ZERO),
)
}))
};
+1 -4
View File
@@ -4,10 +4,6 @@
/// Allows wrapped [references] and [boxes] to dangle.
///
/// <section class="warning">
/// This type is not properly implemented yet, and the documentation below is thus not accurate.
/// </section>
///
/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a
/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow
/// pointer aliasing rules or be dereferenceable.
@@ -73,6 +69,7 @@
#[repr(transparent)]
#[rustc_pub_transparent]
#[derive(Debug, Copy, Clone, Default)]
#[lang = "maybe_dangling"]
pub struct MaybeDangling<P: ?Sized>(P);
impl<P: ?Sized> MaybeDangling<P> {
@@ -22,7 +22,7 @@ pub fn make_unmake_result_never(x: i32) -> i32 {
#[no_mangle]
pub fn extract_control_flow_never(x: ControlFlow<&str, Never>) -> &str {
// CHECK-LABEL: define { ptr, i64 } @extract_control_flow_never(ptr align 1 %x.0, i64 %x.1)
// CHECK-LABEL: define { ptr, i64 } @extract_control_flow_never(ptr %x.0, i64 %x.1)
// CHECK: start:
// CHECK-NEXT: br label %[[next:bb.*]]
// CHECK: [[next]]:
+11 -11
View File
@@ -185,7 +185,7 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
// With a custom allocator, it should *not* have `noalias`. (See
// <https://github.com/rust-lang/miri/issues/3341> 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 align 1{{( captures\(address, read_provenance\))?}} %x.1)
// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly{{( captures\(address, read_provenance\))?}} %x.1)
#[no_mangle]
pub fn _box_custom(x: Box<i32, &std::alloc::Global>) {
drop(x)
@@ -209,14 +209,14 @@ pub fn struct_return() -> S {
pub fn helper(_: usize) {}
// CHECK: @slice(
// CHECK-SAME: ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0,
// CHECK-SAME: ptr noalias 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 align 1 %_1.0,
// CHECK-SAME: ptr noalias 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]
@@ -234,22 +234,22 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {}
pub fn raw_slice(_: *const [u8]) {}
// CHECK: @str(
// CHECK-SAME: ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0,
// CHECK-SAME: ptr noalias 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 align 1 %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// CHECK: @trait_borrow(ptr noundef nonnull %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn trait_borrow(_: &dyn Drop) {}
// CHECK: @option_trait_borrow(ptr noundef align 1 %x.0, ptr %x.1)
// CHECK: @option_trait_borrow(ptr noundef %x.0, ptr %x.1)
#[no_mangle]
pub fn option_trait_borrow(x: Option<&dyn Drop>) {}
// CHECK: @option_trait_borrow_mut(ptr noundef align 1 %x.0, ptr %x.1)
// CHECK: @option_trait_borrow_mut(ptr noundef %x.0, ptr %x.1)
#[no_mangle]
pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {}
@@ -259,13 +259,13 @@ 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 align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
// CHECK: @trait_box(ptr noalias noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box(_: Box<dyn Drop + Unpin + UnsafeUnpin>) {}
// CHECK: @trait_box_pin1(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
// CHECK: @trait_box_pin1(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box_pin1(_: Box<dyn Drop + Unpin>) {}
// CHECK: @trait_box_pin2(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
// CHECK: @trait_box_pin2(ptr noundef nonnull{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box_pin2(_: Box<dyn Drop + UnsafeUnpin>) {}
@@ -281,7 +281,7 @@ pub fn trait_mutref_pin1(_: &mut (i32, dyn Drop + Unpin)) {}
#[no_mangle]
pub fn trait_mutref_pin2(_: &mut (i32, dyn Drop + UnsafeUnpin)) {}
// CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1)
// CHECK: { ptr, ptr } @trait_option(ptr noalias noundef %x.0, ptr %x.1)
#[no_mangle]
pub fn trait_option(
x: Option<Box<dyn Drop + Unpin + UnsafeUnpin>>,
+46
View File
@@ -0,0 +1,46 @@
// This test checks for absence of noalias and dereferenceable attributes on
// arguments wrapped in `MaybeDangling`.
//
// This also tests
//
//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled -Cno-prepopulate-passes
#![crate_type = "lib"]
#![feature(maybe_dangling)]
use std::mem::MaybeDangling;
// CHECK: define {{(dso_local )?}}noundef nonnull ptr @f(ptr noundef nonnull %x) unnamed_addr
#[no_mangle]
pub fn f(x: MaybeDangling<Box<u8>>) -> MaybeDangling<Box<u8>> {
x
}
// CHECK: define {{(dso_local )?}}noundef nonnull ptr @g(ptr noundef nonnull %x) unnamed_addr
#[no_mangle]
pub fn g(x: MaybeDangling<&u8>) -> MaybeDangling<&u8> {
x
}
// CHECK: define {{(dso_local )?}}noundef nonnull ptr @h(ptr noundef nonnull %x) unnamed_addr
#[no_mangle]
pub fn h(x: MaybeDangling<&mut u8>) -> MaybeDangling<&mut u8> {
x
}
// CHECK: define {{(dso_local )?}}noundef nonnull align 4 ptr @i(ptr noundef nonnull align 4 %x) unnamed_addr
#[no_mangle]
pub fn i(x: MaybeDangling<Box<u32>>) -> MaybeDangling<Box<u32>> {
x
}
// CHECK: define {{(dso_local )?}}noundef nonnull align 4 ptr @j(ptr noundef nonnull align 4 %x) unnamed_addr
#[no_mangle]
pub fn j(x: MaybeDangling<&u32>) -> MaybeDangling<&u32> {
x
}
// CHECK: define {{(dso_local )?}}noundef nonnull align 4 ptr @k(ptr noundef nonnull align 4 %x) unnamed_addr
#[no_mangle]
pub fn k(x: MaybeDangling<&mut u32>) -> MaybeDangling<&mut u32> {
x
}
@@ -76,7 +76,7 @@ impl<T, U, const N: usize> Trait5<U, N> for T {
pub fn foo1(a: &dyn Trait1) {
a.foo();
// CHECK-LABEL: define{{.*}}4foo1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
}
pub fn bar1() {
@@ -84,13 +84,13 @@ pub fn bar1() {
let b = &a as &dyn Trait1;
b.foo();
// CHECK-LABEL: define{{.*}}4bar1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
}
pub fn foo2<T>(a: &dyn Trait2<T>) {
a.bar();
// CHECK-LABEL: define{{.*}}4foo2{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ]
}
pub fn bar2() {
@@ -99,14 +99,14 @@ pub fn bar2() {
let b = &a as &dyn Trait2<i32>;
b.bar();
// CHECK-LABEL: define{{.*}}4bar2{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE2:[[:print:]]+]]) ]
}
pub fn foo3(a: &dyn Trait3<Type3>) {
let b = Type3;
a.baz(&b);
// CHECK-LABEL: define{{.*}}4foo3{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}, ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}, ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ]
}
pub fn bar3() {
@@ -115,14 +115,14 @@ pub fn bar3() {
let b = &a as &dyn Trait3<Type3>;
b.baz(&a);
// CHECK-LABEL: define{{.*}}4bar3{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}, ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}, ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE3:[[:print:]]+]]) ]
}
pub fn foo4<'a>(a: &dyn Trait4<'a, Type4, Output = &'a i32>) {
let b = Type4;
a.qux(&b);
// CHECK-LABEL: define{{.*}}4foo4{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call align 4 ptr %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}, ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
// CHECK: call align 4 ptr %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}, ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
}
pub fn bar4<'a>() {
@@ -131,14 +131,14 @@ pub fn bar4<'a>() {
let b = &a as &dyn Trait4<'a, Type4, Output = &'a i32>;
b.qux(&a);
// CHECK-LABEL: define{{.*}}4bar4{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call align 4 ptr %{{[0-9]}}(ptr align 1 {{%[a-z]\.0|%_[0-9]}}, ptr align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
// CHECK: call align 4 ptr %{{[0-9]}}(ptr {{%[a-z]\.0|%_[0-9]}}, ptr {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
}
pub fn foo5(a: &dyn Trait5<Type5, 32>) {
let b = &[Type5; 32];
a.quux(&b);
// CHECK-LABEL: define{{.*}}4foo5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, ptr align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z](\.0)*|%_[0-9]+]}}, ptr {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
}
pub fn bar5() {
@@ -147,7 +147,7 @@ pub fn bar5() {
let b = &a as &dyn Trait5<Type5, 32>;
b.quux(&a);
// CHECK-LABEL: define{{.*}}4bar5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}(ptr align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, ptr align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
// CHECK: call void %{{[0-9]}}(ptr {{%[a-z](\.0)*|%_[0-9]+]}}, ptr {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
}
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]}
+1 -1
View File
@@ -50,7 +50,7 @@ fn make_mu_ref_uninit<'a>() -> MU<&'a u16> {
#[no_mangle]
fn make_mu_str(x: &str) -> MU<&str> {
// CHECK-LABEL: { ptr, i64 } @make_mu_str(ptr align 1 %x.0, i64 %x.1)
// CHECK-LABEL: { ptr, i64 } @make_mu_str(ptr %x.0, i64 %x.1)
// CHECK-NEXT: start:
// CHECK-NEXT: %0 = insertvalue { ptr, i64 } poison, ptr %x.0, 0
// CHECK-NEXT: %1 = insertvalue { ptr, i64 } %0, i64 %x.1, 1
+1 -1
View File
@@ -56,7 +56,7 @@ pub fn upcast_diamond_to_a(x: &dyn Diamond) -> &dyn A {
}
// CHECK-LABEL: upcast_diamond_to_b
// CHECK-SAME: (ptr align {{[0-9]+}} [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]])
// CHECK-SAME: (ptr [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]])
#[no_mangle]
pub fn upcast_diamond_to_b(x: &dyn Diamond) -> &dyn B {
// Requires adjustment, since it's a non-first supertrait.