Skip to content

Commit 4bda070

Browse files
committed
crazy hacks to try and do vec! without box_new
1 parent deea7a9 commit 4bda070

File tree

7 files changed

+56
-17
lines changed

7 files changed

+56
-17
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
215215
| sym::wrapping_add
216216
| sym::wrapping_mul
217217
| sym::wrapping_sub
218+
| sym::write_via_move
218219
// tidy-alphabetical-end
219220
=> hir::Safety::Safe,
220221
_ => hir::Safety::Unsafe,
@@ -551,7 +552,19 @@ pub(crate) fn check_intrinsic_type(
551552

552553
sym::read_via_copy => (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
553554
sym::write_via_move => {
554-
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit)
555+
// The lifetime for argument and return type.
556+
let lft = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon };
557+
let lft = ty::Region::new_bound(tcx, ty::INNERMOST, lft);
558+
559+
let t = param(0);
560+
let mut_ref_t = Ty::new_mut_ref(tcx, lft, t);
561+
// MaybeUninit<T>
562+
// let maybe_uninit = tcx.require_lang_item(LangItem::MaybeUninit, span);
563+
// let maybe_uninit = tcx.adt_def(maybe_uninit);
564+
// let args = tcx.mk_args_from_iter([GenericArg::from(t)].into_iter());
565+
// let maybe_uninit_t = Ty::new_adt(tcx, maybe_uninit, args);
566+
567+
(1, 0, vec![mut_ref_t, t], mut_ref_t)
555568
}
556569

557570
sym::typed_swap_nonoverlapping => {

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
210210
terminator.source_info,
211211
StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))),
212212
));
213+
// The intrinsic returns the argument pointer.
214+
block.statements.push(Statement::new(
215+
terminator.source_info,
216+
StatementKind::Assign(Box::new((*destination, Rvalue::Use(ptr.node)))),
217+
));
218+
213219
terminator.kind = TerminatorKind::Goto { target };
214220
}
215221
sym::discriminant_value => {

library/alloc/src/boxed.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,14 @@ pub struct Box<
233233
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
234234
>(Unique<T>, A);
235235

236-
/// Constructs a `Box<T>` by calling the `exchange_malloc` lang item and moving the argument into
237-
/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies.
238-
///
239-
/// This is the surface syntax for `box <expr>` expressions.
236+
/// This function is not actually safe, it has the preconditions of `Box::from_raw`.
237+
/// It is meant for the `vec!` macro where we can't use an unsafe block as that would also
238+
/// wrap the user-defined `$x`.
240239
#[doc(hidden)]
241-
#[rustc_intrinsic]
242240
#[unstable(feature = "liballoc_internals", issue = "none")]
243-
pub fn box_new<T>(x: T) -> Box<T>;
241+
pub fn vec_macro_slice_helper_unsafe<T, const N: usize>(b: &mut [T; N]) -> Box<[T]> {
242+
unsafe { Box::from_raw(b) }
243+
}
244244

245245
impl<T> Box<T> {
246246
/// Allocates memory on the heap and then places `x` into it.
@@ -261,8 +261,10 @@ impl<T> Box<T> {
261261
pub fn new(x: T) -> Self {
262262
let mut b = Box::new_uninit();
263263
let ptr = mem::MaybeUninit::as_mut_ptr(&mut *b);
264-
// SAFETY: we just allocated the box to store `x`.
265-
unsafe { core::intrinsics::write_via_move(ptr, x) };
264+
// SAFETY: We rely on the unstable property of the Rust semantics that references don't
265+
// require the pointee to be valid (if the pointee is inhabited, which we know it is).
266+
let dest = unsafe { &mut *ptr };
267+
core::intrinsics::write_via_move(dest, x);
266268
// SAFETY: we just initialized `b`.
267269
unsafe { b.assume_init() }
268270
}

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,5 @@ pub mod wtf8;
235235
pub mod __export {
236236
pub use core::format_args;
237237
pub use core::hint::must_use;
238+
pub use core::intrinsics::write_via_move;
238239
}

library/alloc/src/macros.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#[macro_export]
3939
#[stable(feature = "rust1", since = "1.0.0")]
4040
#[rustc_diagnostic_item = "vec_macro"]
41-
#[allow_internal_unstable(rustc_attrs, liballoc_internals)]
41+
#[allow_internal_unstable(rustc_attrs, liballoc_internals, core_intrinsics)]
4242
macro_rules! vec {
4343
() => (
4444
$crate::vec::Vec::new()
@@ -48,9 +48,19 @@ macro_rules! vec {
4848
);
4949
($($x:expr),+ $(,)?) => (
5050
<[_]>::into_vec(
51-
// Using the intrinsic produces a dramatic improvement in stack usage for
52-
// unoptimized programs using this code path to construct large Vecs.
53-
$crate::boxed::box_new([$($x),+])
51+
// Using the `write_via_move` intrinsic produces a dramatic improvement in stack usage
52+
// for unoptimized programs using this code path to construct large Vecs.
53+
$crate::boxed::vec_macro_slice_helper_unsafe($crate::__export::write_via_move(
54+
// SAFETY: We rely on the unstable property of the Rust semantics that references
55+
// don't require the pointee to be valid (if the pointee is inhabited, which we know
56+
// it is).
57+
// We also can't let-bind this as that would change teh temporary scope behavior of
58+
// the macro.
59+
unsafe {
60+
$crate::boxed::Box::leak($crate::boxed::Box::new_uninit()).assume_init_mut()
61+
},
62+
[$($x),+],
63+
))
5464
)
5565
);
5666
}

library/core/src/intrinsics/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,16 +2168,20 @@ pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
21682168
#[rustc_intrinsic]
21692169
pub const unsafe fn read_via_copy<T>(ptr: *const T) -> T;
21702170

2171-
/// This is an implementation detail of [`crate::ptr::write`] and should
2172-
/// not be used anywhere else. See its comments for why this exists.
2171+
/// This is an implementation detail of [`crate::ptr::write`] and the `vec!` macro and should not be
2172+
/// used anywhere else. See its comments for why this exists.
2173+
///
2174+
/// The signature is very carefully chosen to make the intrinsic safe, and to allow it to be used
2175+
/// in `vec!` in tail position. `ptr` does not *actually* have to point to something already
2176+
/// initializes.
21732177
///
21742178
/// This intrinsic can *only* be called where the pointer is a local without
21752179
/// projections (`write_via_move(ptr, x)`, not `write_via_move(*ptr, x)`) so
21762180
/// that it trivially obeys runtime-MIR rules about derefs in operands.
21772181
#[rustc_intrinsic_const_stable_indirect]
21782182
#[rustc_nounwind]
21792183
#[rustc_intrinsic]
2180-
pub const unsafe fn write_via_move<T>(ptr: *mut T, value: T);
2184+
pub const fn write_via_move<T>(ptr: &mut T, value: T) -> &mut T;
21812185

21822186
/// Returns the value of the discriminant for the variant in 'v';
21832187
/// if `T` has no discriminant, returns `0`.

library/core/src/ptr/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1941,7 +1941,10 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
19411941
is_zst: bool = T::IS_ZST,
19421942
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
19431943
);
1944-
intrinsics::write_via_move(dst, src)
1944+
// We rely on the unstable property of the Rust semantics that references don't
1945+
// require the pointee to be valid (if the pointee is inhabited, which we know it is).
1946+
let ptr = &mut *dst;
1947+
intrinsics::write_via_move(ptr, src);
19451948
}
19461949
}
19471950

0 commit comments

Comments
 (0)