mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-21 17:52:12 +03:00
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:
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(..)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1217,6 +1217,7 @@
|
||||
maxnumf128,
|
||||
may_dangle,
|
||||
may_unwind,
|
||||
maybe_dangling,
|
||||
maybe_uninit,
|
||||
maybe_uninit_uninit,
|
||||
maybe_uninit_zeroed,
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}))
|
||||
};
|
||||
|
||||
|
||||
@@ -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]]:
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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]]}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user