Auto merge of #134757 - RalfJung:const_swap, r=scottmcm
stabilize const_swap libs-api FCP passed in https://github.com/rust-lang/rust/issues/83163. However, I only just realized that this actually involves an intrinsic. The intrinsic could be implemented entirely with existing stable const functionality, but we choose to make it a primitive to be able to detect more UB. So nominating for `@rust-lang/lang` to make sure they are aware; I leave it up to them whether they want to FCP this. While at it I also renamed the intrinsic to make the "nonoverlapping" constraint more clear. Fixes #83163
This commit is contained in:
commit
4e5fec2f1e
28 changed files with 139 additions and 95 deletions
|
@ -75,7 +75,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// If we're swapping something that's *not* an `OperandValue::Ref`,
|
||||
// then we can do it directly and avoid the alloca.
|
||||
// Otherwise, we'll let the fallback MIR body take care of it.
|
||||
if let sym::typed_swap = name {
|
||||
if let sym::typed_swap_nonoverlapping = name {
|
||||
let pointee_ty = fn_args.type_at(0);
|
||||
let pointee_layout = bx.layout_of(pointee_ty);
|
||||
if !bx.is_backend_ref(pointee_layout)
|
||||
|
|
|
@ -382,7 +382,7 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
/// Avoids `alloca`s for Immediates and ScalarPairs.
|
||||
///
|
||||
/// FIXME: Maybe do something smarter for Ref types too?
|
||||
/// For now, the `typed_swap` intrinsic just doesn't call this for those
|
||||
/// For now, the `typed_swap_nonoverlapping` intrinsic just doesn't call this for those
|
||||
/// cases (in non-debug), preferring the fallback body instead.
|
||||
fn typed_place_swap(
|
||||
&mut self,
|
||||
|
|
|
@ -883,19 +883,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
.local_to_op(mir::RETURN_PLACE, None)
|
||||
.expect("return place should always be live");
|
||||
let dest = self.frame().return_place.clone();
|
||||
let res = if self.stack().len() == 1 {
|
||||
// The initializer of constants and statics will get validated separately
|
||||
// after the constant has been fully evaluated. While we could fall back to the default
|
||||
// code path, that will cause -Zenforce-validity to cycle on static initializers.
|
||||
// Reading from a static's memory is not allowed during its evaluation, and will always
|
||||
// trigger a cycle error. Validation must read from the memory of the current item.
|
||||
// For Miri this means we do not validate the root frame return value,
|
||||
// but Miri anyway calls `read_target_isize` on that so separate validation
|
||||
// is not needed.
|
||||
self.copy_op_no_dest_validation(&op, &dest)
|
||||
} else {
|
||||
self.copy_op_allow_transmute(&op, &dest)
|
||||
};
|
||||
let res = self.copy_op_allow_transmute(&op, &dest);
|
||||
trace!("return value: {:?}", self.dump_place(&dest.into()));
|
||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||
|
|
|
@ -424,8 +424,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
|
||||
self.write_scalar(result, dest)?;
|
||||
}
|
||||
sym::typed_swap => {
|
||||
self.typed_swap_intrinsic(&args[0], &args[1])?;
|
||||
sym::typed_swap_nonoverlapping => {
|
||||
self.typed_swap_nonoverlapping_intrinsic(&args[0], &args[1])?;
|
||||
}
|
||||
|
||||
sym::vtable_size => {
|
||||
|
@ -638,19 +638,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
/// Does a *typed* swap of `*left` and `*right`.
|
||||
fn typed_swap_intrinsic(
|
||||
fn typed_swap_nonoverlapping_intrinsic(
|
||||
&mut self,
|
||||
left: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
|
||||
right: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let left = self.deref_pointer(left)?;
|
||||
let right = self.deref_pointer(right)?;
|
||||
debug_assert_eq!(left.layout, right.layout);
|
||||
assert_eq!(left.layout, right.layout);
|
||||
assert!(left.layout.is_sized());
|
||||
let kind = MemoryKind::Stack;
|
||||
let temp = self.allocate(left.layout, kind)?;
|
||||
self.copy_op(&left, &temp)?;
|
||||
self.copy_op(&right, &left)?;
|
||||
self.copy_op(&temp, &right)?;
|
||||
self.copy_op(&left, &temp)?; // checks alignment of `left`
|
||||
|
||||
// We want to always enforce non-overlapping, even if this is a scalar type.
|
||||
// Therefore we directly use the underlying `mem_copy` here.
|
||||
self.mem_copy(right.ptr(), left.ptr(), left.layout.size, /*nonoverlapping*/ true)?;
|
||||
// This means we also need to do the validation of the value that used to be in `right`
|
||||
// ourselves. This value is now in `left.` The one that started out in `left` already got
|
||||
// validated by the copy above.
|
||||
if M::enforce_validity(self, left.layout) {
|
||||
self.validate_operand(
|
||||
&left.clone().into(),
|
||||
M::enforce_validity_recursively(self, left.layout),
|
||||
/*reset_provenance_and_padding*/ true,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.copy_op(&temp, &right)?; // checks alignment of `right`
|
||||
|
||||
self.deallocate_ptr(temp.ptr(), None, kind)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
@ -773,22 +773,6 @@ where
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
/// The layouts of the `src` and `dest` may disagree.
|
||||
/// Does not perform validation of the destination.
|
||||
/// The only known use case for this function is checking the return
|
||||
/// value of a static during stack frame popping.
|
||||
#[inline(always)]
|
||||
pub(super) fn copy_op_no_dest_validation(
|
||||
&mut self,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ true, /* validate_dest */ false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
/// The layouts of the `src` and `dest` may disagree.
|
||||
#[inline(always)]
|
||||
|
@ -797,9 +781,7 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ true, /* validate_dest */ true,
|
||||
)
|
||||
self.copy_op_inner(src, dest, /* allow_transmute */ true)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
|
@ -810,9 +792,7 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ false, /* validate_dest */ true,
|
||||
)
|
||||
self.copy_op_inner(src, dest, /* allow_transmute */ false)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
|
@ -824,22 +804,21 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
validate_dest: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// These are technically *two* typed copies: `src` is a not-yet-loaded value,
|
||||
// so we're going a typed copy at `src` type from there to some intermediate storage.
|
||||
// so we're doing a typed copy at `src` type from there to some intermediate storage.
|
||||
// And then we're doing a second typed copy from that intermediate storage to `dest`.
|
||||
// But as an optimization, we only make a single direct copy here.
|
||||
|
||||
// Do the actual copy.
|
||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||
|
||||
if validate_dest && M::enforce_validity(self, dest.layout()) {
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
let dest = dest.to_place();
|
||||
// Given that there were two typed copies, we have to ensure this is valid at both types,
|
||||
// and we have to ensure this loses provenance and padding according to both types.
|
||||
// But if the types are identical, we only do one pass.
|
||||
if allow_transmute && src.layout().ty != dest.layout().ty {
|
||||
if src.layout().ty != dest.layout().ty {
|
||||
self.validate_operand(
|
||||
&dest.transmute(src.layout(), self)?,
|
||||
M::enforce_validity_recursively(self, src.layout()),
|
||||
|
|
|
@ -501,7 +501,9 @@ pub fn check_intrinsic_type(
|
|||
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit)
|
||||
}
|
||||
|
||||
sym::typed_swap => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit),
|
||||
sym::typed_swap_nonoverlapping => {
|
||||
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit)
|
||||
}
|
||||
|
||||
sym::discriminant_value => {
|
||||
let assoc_items = tcx.associated_item_def_ids(
|
||||
|
|
|
@ -2059,7 +2059,7 @@ symbols! {
|
|||
type_macros,
|
||||
type_name,
|
||||
type_privacy_lints,
|
||||
typed_swap,
|
||||
typed_swap_nonoverlapping,
|
||||
u128,
|
||||
u128_legacy_const_max,
|
||||
u128_legacy_const_min,
|
||||
|
|
|
@ -3969,6 +3969,21 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
#[rustc_nounwind]
|
||||
#[inline]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
|
||||
#[cfg(bootstrap)]
|
||||
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
|
||||
// SAFETY: The caller provided single non-overlapping items behind
|
||||
// pointers, so swapping them with `count: 1` is fine.
|
||||
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
pub use typed_swap as typed_swap_nonoverlapping;
|
||||
|
||||
/// Non-overlapping *typed* swap of a single value.
|
||||
///
|
||||
/// The codegen backends will replace this with a better implementation when
|
||||
|
@ -3982,9 +3997,10 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
|||
#[rustc_nounwind]
|
||||
#[inline]
|
||||
#[rustc_intrinsic]
|
||||
// Const-unstable because `swap_nonoverlapping` is const-unstable.
|
||||
#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")]
|
||||
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
|
||||
#[cfg(not(bootstrap))]
|
||||
pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
|
||||
// SAFETY: The caller provided single non-overlapping items behind
|
||||
// pointers, so swapping them with `count: 1` is fine.
|
||||
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||
|
|
|
@ -112,7 +112,6 @@
|
|||
#![feature(asm_experimental_arch)]
|
||||
#![feature(const_carrying_mul_add)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_typed_swap)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(coverage_attribute)]
|
||||
#![feature(internal_impls_macro)]
|
||||
|
|
|
@ -725,12 +725,12 @@ pub unsafe fn uninitialized<T>() -> T {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "mem_swap"]
|
||||
pub const fn swap<T>(x: &mut T, y: &mut T) {
|
||||
// SAFETY: `&mut` guarantees these are typed readable and writable
|
||||
// as well as non-overlapping.
|
||||
unsafe { intrinsics::typed_swap(x, y) }
|
||||
unsafe { intrinsics::typed_swap_nonoverlapping(x, y) }
|
||||
}
|
||||
|
||||
/// Replaces `dest` with the default value of `T`, returning the previous `dest` value.
|
||||
|
|
|
@ -1009,9 +1009,8 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "ptr_swap"]
|
||||
#[rustc_const_stable_indirect]
|
||||
pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
||||
// Give ourselves some scratch space to work with.
|
||||
// We do not have to worry about drops: `MaybeUninit` does nothing when dropped.
|
||||
|
|
|
@ -1594,7 +1594,7 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// [`ptr::swap`]: crate::ptr::swap()
|
||||
#[stable(feature = "pointer_methods", since = "1.26.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline(always)]
|
||||
pub const unsafe fn swap(self, with: *mut T)
|
||||
where
|
||||
|
|
|
@ -1146,7 +1146,7 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// [`ptr::swap`]: crate::ptr::swap()
|
||||
#[inline(always)]
|
||||
#[stable(feature = "non_null_convenience", since = "1.80.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const unsafe fn swap(self, with: NonNull<T>)
|
||||
where
|
||||
T: Sized,
|
||||
|
|
|
@ -913,7 +913,7 @@ impl<T> [T] {
|
|||
/// assert!(v == ["a", "b", "e", "d", "c"]);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub const fn swap(&mut self, a: usize, b: usize) {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#![feature(clone_to_uninit)]
|
||||
#![feature(const_black_box)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_swap)]
|
||||
#![feature(const_swap_nonoverlapping)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::intrinsics::typed_swap;
|
||||
use std::intrinsics::typed_swap_nonoverlapping;
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
fn invalid_array() {
|
||||
|
@ -10,7 +10,7 @@ fn invalid_array() {
|
|||
unsafe {
|
||||
let a = addr_of_mut!(a).cast::<[bool; 100]>();
|
||||
let b = addr_of_mut!(b).cast::<[bool; 100]>();
|
||||
typed_swap(a, b); //~ERROR: constructing invalid value
|
||||
typed_swap_nonoverlapping(a, b); //~ERROR: constructing invalid value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: constructing invalid value at [0]: encountered 0x02, but expected a boolean
|
||||
--> tests/fail/intrinsics/typed-swap-invalid-array.rs:LL:CC
|
||||
|
|
||||
LL | typed_swap(a, b);
|
||||
| ^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered 0x02, but expected a boolean
|
||||
LL | typed_swap_nonoverlapping(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered 0x02, but expected a boolean
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: constructing invalid value: encountered 0x02, but expected a boolean
|
||||
--> tests/fail/intrinsics/typed-swap-invalid-scalar.rs:LL:CC
|
||||
|
|
||||
LL | typed_swap(a, b);
|
||||
| ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x02, but expected a boolean
|
||||
LL | typed_swap_nonoverlapping(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x02, but expected a boolean
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
|
@ -0,0 +1,20 @@
|
|||
error: Undefined Behavior: constructing invalid value: encountered 0x03, but expected a boolean
|
||||
--> tests/fail/intrinsics/typed-swap-invalid-scalar.rs:LL:CC
|
||||
|
|
||||
LL | typed_swap_nonoverlapping(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `invalid_scalar` at tests/fail/intrinsics/typed-swap-invalid-scalar.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail/intrinsics/typed-swap-invalid-scalar.rs:LL:CC
|
||||
|
|
||||
LL | invalid_scalar();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
//@revisions: left right
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::intrinsics::typed_swap;
|
||||
use std::intrinsics::typed_swap_nonoverlapping;
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
fn invalid_scalar() {
|
||||
let mut a = 1_u8;
|
||||
let mut b = 2_u8;
|
||||
// We run the test twice, with either the left or the right side being invalid.
|
||||
let mut a = if cfg!(left) { 2_u8 } else { 1_u8 };
|
||||
let mut b = if cfg!(right) { 3_u8 } else { 1_u8 };
|
||||
unsafe {
|
||||
let a = addr_of_mut!(a).cast::<bool>();
|
||||
let b = addr_of_mut!(b).cast::<bool>();
|
||||
typed_swap(a, b); //~ERROR: constructing invalid value
|
||||
typed_swap_nonoverlapping(a, b); //~ERROR: constructing invalid value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
13
src/tools/miri/tests/fail/intrinsics/typed-swap-overlap.rs
Normal file
13
src/tools/miri/tests/fail/intrinsics/typed-swap-overlap.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::intrinsics::typed_swap_nonoverlapping;
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
fn main() {
|
||||
let mut a = 0_u8;
|
||||
unsafe {
|
||||
let a = addr_of_mut!(a);
|
||||
typed_swap_nonoverlapping(a, a); //~ERROR: called on overlapping ranges
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges
|
||||
--> tests/fail/intrinsics/typed-swap-overlap.rs:LL:CC
|
||||
|
|
||||
LL | typed_swap_nonoverlapping(a, a);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `copy_nonoverlapping` called on overlapping ranges
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at tests/fail/intrinsics/typed-swap-overlap.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
|
@ -8,14 +8,14 @@
|
|||
#![crate_type = "lib"]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
use std::intrinsics::typed_swap;
|
||||
use std::intrinsics::typed_swap_nonoverlapping;
|
||||
|
||||
// CHECK-LABEL: @swap_unit(
|
||||
#[no_mangle]
|
||||
pub unsafe fn swap_unit(x: &mut (), y: &mut ()) {
|
||||
// CHECK: start
|
||||
// CHECK-NEXT: ret void
|
||||
typed_swap(x, y)
|
||||
typed_swap_nonoverlapping(x, y)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @swap_i32(
|
||||
|
@ -32,7 +32,7 @@ pub unsafe fn swap_i32(x: &mut i32, y: &mut i32) {
|
|||
// OPT3: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 4, i1 false)
|
||||
// CHECK: store i32 %[[TEMP]], ptr %y, align 4
|
||||
// CHECK: ret void
|
||||
typed_swap(x, y)
|
||||
typed_swap_nonoverlapping(x, y)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @swap_pair(
|
||||
|
@ -47,7 +47,7 @@ pub unsafe fn swap_pair(x: &mut (i32, u32), y: &mut (i32, u32)) {
|
|||
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 8, i1 false)
|
||||
// CHECK: store i32
|
||||
// CHECK: store i32
|
||||
typed_swap(x, y)
|
||||
typed_swap_nonoverlapping(x, y)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @swap_str(
|
||||
|
@ -63,7 +63,7 @@ pub unsafe fn swap_str<'a>(x: &mut &'a str, y: &mut &'a str) {
|
|||
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 16, i1 false)
|
||||
// CHECK: store ptr
|
||||
// CHECK: store i64
|
||||
typed_swap(x, y)
|
||||
typed_swap_nonoverlapping(x, y)
|
||||
}
|
||||
|
||||
// OPT0-LABEL: @swap_string(
|
||||
|
@ -73,5 +73,5 @@ pub unsafe fn swap_string(x: &mut String, y: &mut String) {
|
|||
// OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[TEMP]], ptr align 8 %x, i64 24, i1 false)
|
||||
// OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 24, i1 false)
|
||||
// OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 %[[TEMP]], i64 24, i1 false)
|
||||
typed_swap(x, y)
|
||||
typed_swap_nonoverlapping(x, y)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//@ check-pass
|
||||
|
||||
#![feature(const_swap)]
|
||||
|
||||
#[repr(C)]
|
||||
struct Demo(u64, bool, u64, u32, u64, u64, u64);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//@ compile-flags: --crate-type=lib
|
||||
#![feature(const_precise_live_drops)]
|
||||
#![feature(const_swap)]
|
||||
|
||||
// Mutable borrow of a field with drop impl.
|
||||
pub const fn f() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:13:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:12:9
|
||||
|
|
||||
LL | let mut x = None;
|
||||
| ^^^^^ the destructor for this type cannot be evaluated in constants
|
||||
|
@ -19,13 +19,13 @@ note: inside `std::ptr::drop_in_place::<String> - shim(Some(String))`
|
|||
note: inside `std::ptr::drop_in_place::<Option<String>> - shim(Some(Option<String>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `A1`
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:19:1
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:18:1
|
||||
|
|
||||
LL | };
|
||||
| ^
|
||||
|
||||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:29:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:28:9
|
||||
|
|
||||
LL | let _z = x;
|
||||
| ^^ the destructor for this type cannot be evaluated in constants
|
||||
|
@ -44,13 +44,13 @@ note: inside `std::ptr::drop_in_place::<String> - shim(Some(String))`
|
|||
note: inside `std::ptr::drop_in_place::<Option<String>> - shim(Some(Option<String>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `A2`
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:30:1
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:29:1
|
||||
|
|
||||
LL | };
|
||||
| ^
|
||||
|
||||
error[E0493]: destructor of `(u32, Option<String>)` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:7:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:6:9
|
||||
|
|
||||
LL | let mut a: (u32, Option<String>) = (0, None);
|
||||
| ^^^^^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -59,7 +59,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:34:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:33:9
|
||||
|
|
||||
LL | let x: Option<T> = None;
|
||||
| ^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -68,7 +68,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<T>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:42:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:41:9
|
||||
|
|
||||
LL | let _y = x;
|
||||
| ^^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -76,7 +76,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:50:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:49:9
|
||||
|
|
||||
LL | let mut y: Option<String> = None;
|
||||
| ^^^^^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -85,7 +85,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:47:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:46:9
|
||||
|
|
||||
LL | let mut x: Option<String> = None;
|
||||
| ^^^^^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -94,7 +94,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:60:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:59:9
|
||||
|
|
||||
LL | let y: Option<String> = None;
|
||||
| ^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
@ -103,7 +103,7 @@ LL | }
|
|||
| - value is dropped here
|
||||
|
||||
error[E0493]: destructor of `Option<String>` cannot be evaluated at compile-time
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:57:9
|
||||
--> $DIR/qualif-indirect-mutation-fail.rs:56:9
|
||||
|
|
||||
LL | let x: Option<String> = None;
|
||||
| ^ the destructor for this type cannot be evaluated in constant functions
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//@ edition:2018
|
||||
|
||||
#![feature(thread_local)]
|
||||
#![feature(const_swap)]
|
||||
#![allow(static_mut_refs)]
|
||||
|
||||
#[thread_local]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
|
||||
--> $DIR/thread-local-static.rs:10:28
|
||||
--> $DIR/thread-local-static.rs:9:28
|
||||
|
|
||||
LL | std::mem::swap(x, &mut STATIC_VAR_2)
|
||||
| ^^^^^^^^^^^^ use of mutable static
|
||||
|
@ -7,7 +7,7 @@ LL | std::mem::swap(x, &mut STATIC_VAR_2)
|
|||
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||
|
||||
error[E0625]: thread-local statics cannot be accessed at compile-time
|
||||
--> $DIR/thread-local-static.rs:10:28
|
||||
--> $DIR/thread-local-static.rs:9:28
|
||||
|
|
||||
LL | std::mem::swap(x, &mut STATIC_VAR_2)
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue