Auto merge of #142911 - mejrs:unsized, r=compiler-errors

Remove support for dynamic allocas

Followup to rust-lang/rust#141811
This commit is contained in:
bors 2025-07-11 05:27:32 +00:00
commit 855e0fe46e
16 changed files with 163 additions and 94 deletions

View file

@ -926,10 +926,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
.get_address(self.location)
}
fn dynamic_alloca(&mut self, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
unimplemented!();
}
fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
let block = self.llbb();
let function = block.get_function();

View file

@ -538,16 +538,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
}
fn dynamic_alloca(&mut self, size: &'ll Value, align: Align) -> &'ll Value {
unsafe {
let alloca =
llvm::LLVMBuildArrayAlloca(self.llbuilder, self.cx().type_i8(), size, UNNAMED);
llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint);
// Cast to default addrspace if necessary
llvm::LLVMBuildPointerCast(self.llbuilder, alloca, self.cx().type_ptr(), UNNAMED)
}
}
fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value {
unsafe {
let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED);

View file

@ -1492,12 +1492,6 @@ unsafe extern "C" {
Ty: &'a Type,
Name: *const c_char,
) -> &'a Value;
pub(crate) fn LLVMBuildArrayAlloca<'a>(
B: &Builder<'a>,
Ty: &'a Type,
Val: &'a Value,
Name: *const c_char,
) -> &'a Value;
pub(crate) fn LLVMBuildLoad2<'a>(
B: &Builder<'a>,
Ty: &'a Type,

View file

@ -140,8 +140,13 @@ enum LocalRef<'tcx, V> {
Place(PlaceRef<'tcx, V>),
/// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).
/// `*p` is the wide pointer that references the actual unsized place.
/// Every time it is initialized, we have to reallocate the place
/// and update the wide pointer. That's the reason why it is indirect.
///
/// MIR only supports unsized args, not dynamically-sized locals, so
/// new unsized temps don't exist and we must reuse the referred-to place.
///
/// FIXME: Since the removal of unsized locals in <https://github.com/rust-lang/rust/pull/142911>,
/// can we maybe use `Place` here? Or refactor it in another way? There are quite a few
/// `UnsizedPlace => bug` branches now.
UnsizedPlace(PlaceRef<'tcx, V>),
/// The backend [`OperandValue`] has already been generated.
Operand(OperandRef<'tcx, V>),
@ -498,7 +503,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
}
}
// Unsized indirect qrguments
// Unsized indirect arguments
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
// As the storage for the indirect argument lives during
// the whole function call, we just copy the wide pointer.

View file

@ -16,9 +16,9 @@ use tracing::{debug, instrument};
use super::place::{PlaceRef, PlaceValue};
use super::rvalue::transmute_scalar;
use super::{FunctionCx, LocalRef};
use crate::MemFlags;
use crate::common::IntPredicate;
use crate::traits::*;
use crate::{MemFlags, size_of_val};
/// The representation of a Rust value. The enum variant is in fact
/// uniquely determined by the value's type, but is kept as a
@ -861,44 +861,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
}
}
}
pub fn store_unsized<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
self,
bx: &mut Bx,
indirect_dest: PlaceRef<'tcx, V>,
) {
debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
// `indirect_dest` must have `*mut T` type. We extract `T` out of it.
let unsized_ty = indirect_dest
.layout
.ty
.builtin_deref(true)
.unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest));
let OperandValue::Ref(PlaceValue { llval: llptr, llextra: Some(llextra), .. }) = self
else {
bug!("store_unsized called with a sized value (or with an extern type)")
};
// Allocate an appropriate region on the stack, and copy the value into it. Since alloca
// doesn't support dynamic alignment, we allocate an extra align - 1 bytes, and align the
// pointer manually.
let (size, align) = size_of_val::size_and_align_of_dst(bx, unsized_ty, Some(llextra));
let one = bx.const_usize(1);
let align_minus_1 = bx.sub(align, one);
let size_extra = bx.add(size, align_minus_1);
let min_align = Align::ONE;
let alloca = bx.dynamic_alloca(size_extra, min_align);
let address = bx.ptrtoint(alloca, bx.type_isize());
let neg_address = bx.neg(address);
let offset = bx.and(neg_address, align_minus_1);
let dst = bx.inbounds_ptradd(alloca, offset);
bx.memcpy(dst, min_align, llptr, min_align, size, MemFlags::empty());
// Store the allocated region and the extra to the indirect place.
let indirect_operand = OperandValue::Pair(dst, llextra);
indirect_operand.store(bx, indirect_dest);
}
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {

View file

@ -327,27 +327,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(imm)
}
pub(crate) fn codegen_rvalue_unsized(
&mut self,
bx: &mut Bx,
indirect_dest: PlaceRef<'tcx, Bx::Value>,
rvalue: &mir::Rvalue<'tcx>,
) {
debug!(
"codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
indirect_dest.val.llval, rvalue
);
match *rvalue {
mir::Rvalue::Use(ref operand) => {
let cg_operand = self.codegen_operand(bx, operand);
cg_operand.val.store_unsized(bx, indirect_dest);
}
_ => bug!("unsized assignment other than `Rvalue::Use`"),
}
}
pub(crate) fn codegen_rvalue_operand(
&mut self,
bx: &mut Bx,

View file

@ -15,7 +15,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match self.locals[index] {
LocalRef::Place(cg_dest) => self.codegen_rvalue(bx, cg_dest, rvalue),
LocalRef::UnsizedPlace(cg_indirect_dest) => {
self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
let ty = cg_indirect_dest.layout.ty;
span_bug!(
statement.source_info.span,
"cannot reallocate from `UnsizedPlace({ty})` \
into `{rvalue:?}`; dynamic alloca is not supported",
);
}
LocalRef::PendingOperand => {
let operand = self.codegen_rvalue_operand(bx, rvalue);

View file

@ -224,7 +224,6 @@ pub trait BuilderMethods<'a, 'tcx>:
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value;
fn alloca(&mut self, size: Size, align: Align) -> Self::Value;
fn dynamic_alloca(&mut self, size: Self::Value, align: Align) -> Self::Value;
fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value;
fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value;

View file

@ -241,6 +241,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
arg_expr.span,
ObligationCauseCode::WellFormed(None),
);
self.check_place_expr_if_unsized(fn_input_ty, arg_expr);
}
// First, let's unify the formal method signature with the expectation eagerly.
@ -543,6 +545,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// If `unsized_fn_params` is active, check that unsized values are place expressions. Since
/// the removal of `unsized_locals` in <https://github.com/rust-lang/rust/pull/142911> we can't
/// store them in MIR locals as temporaries.
///
/// If `unsized_fn_params` is inactive, this will be checked in borrowck instead.
fn check_place_expr_if_unsized(&self, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if self.tcx.features().unsized_fn_params() && !expr.is_syntactic_place_expr() {
self.require_type_is_sized(
ty,
expr.span,
ObligationCauseCode::UnsizedNonPlaceExpr(expr.span),
);
}
}
fn report_arg_errors(
&self,
compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
@ -1873,7 +1890,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}
hir::StmtKind::Semi(expr) => {
self.check_expr(expr);
let ty = self.check_expr(expr);
self.check_place_expr_if_unsized(ty, expr);
}
}

View file

@ -412,6 +412,10 @@ pub enum ObligationCauseCode<'tcx> {
/// Obligations emitted during the normalization of a free type alias.
TypeAlias(ObligationCauseCodeHandle<'tcx>, Span, DefId),
/// Only reachable if the `unsized_fn_params` feature is used. Unsized function arguments must
/// be place expressions because we can't store them in MIR locals as temporaries.
UnsizedNonPlaceExpr(Span),
}
/// Whether a value can be extracted into a const.

View file

@ -3719,6 +3719,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
suggest_remove_deref(err, &expr);
}
ObligationCauseCode::UnsizedNonPlaceExpr(span) => {
err.span_note(
span,
"unsized values must be place expressions and cannot be put in temporaries",
);
}
}
}

View file

@ -151,7 +151,7 @@ pub const fn forget<T>(t: T) {
///
/// While Rust does not permit unsized locals since its removal in [#111942] it is
/// still possible to call functions with unsized values from a function argument
/// or in-place construction.
/// or place expression.
///
/// ```rust
/// #![feature(unsized_fn_params, forget_unsized)]

View file

@ -18,11 +18,6 @@ impl std::ops::Add<i32> for A<[u8]> {
}
fn main() {
udrop::<[u8]>(loop {
break *foo();
});
udrop::<[u8]>(if true { *foo() } else { *foo() });
udrop::<[u8]>({ *foo() });
udrop::<[u8]>((*foo()));
*afoo() + 42;
udrop as fn([u8]);

View file

@ -10,7 +10,11 @@ note: required because it appears within the type `A<[u8]>`
|
LL | struct A<X: ?Sized>(X);
| ^
= note: structs must have a statically known size to be initialized
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-exprs.rs:19:22
|
LL | udrop::<A<[u8]>>(A { 0: *foo() });
| ^^^^^^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-exprs.rs:21:22
@ -24,7 +28,11 @@ note: required because it appears within the type `A<[u8]>`
|
LL | struct A<X: ?Sized>(X);
| ^
= note: the return type of a function must have a statically known size
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-exprs.rs:21:22
|
LL | udrop::<A<[u8]>>(A(*foo()));
| ^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,27 @@
//! `#![feature(unsized_fn_params)]` lets you use unsized function parameters. In particular this
//! is load bearing for `Box<dyn FnOnce()>: FnOnce()`. To do that, borrowck relaxes the requirement
//! that certain places must be `Sized`. But in #142911 we removed alloca support, so these
//! arguments cannot be put in temporaries (or ICE at codegen) That means when `unsized_fn_params`
//! is enabled, we must explicitly check that unsized function arguments are place expressions.
//!
//! Also see tests/ui/unsized_locals/unsized-exprs-rpass.rs
#![feature(unsized_fn_params)]
fn foo() -> Box<[u8]> {
Box::new(*b"foo")
}
fn udrop<T: ?Sized>(_x: T) {}
fn main(){
// NB The ordering of the following operations matters, otherwise errors get swallowed somehow.
udrop::<[u8]>(if true { *foo() } else { *foo() }); //~ERROR the size for values of type `[u8]` cannot be known at compilation time
udrop::<[u8]>({ *foo() }); //~ERROR the size for values of type `[u8]` cannot be known at compilation time
udrop(match foo() { x => *x }); //~ERROR the size for values of type `[u8]` cannot be known at compilation time
udrop::<[u8]>({ loop { break *foo(); } }); //~ERROR the size for values of type `[u8]` cannot be known at compilation time
{ *foo() }; //~ERROR the size for values of type `[u8]` cannot be known at compilation time
{ loop { break *foo(); } }; //~ERROR the size for values of type `[u8]` cannot be known at compilation time
}

View file

@ -0,0 +1,81 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:20:19
|
LL | udrop::<[u8]>(if true { *foo() } else { *foo() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:20:19
|
LL | udrop::<[u8]>(if true { *foo() } else { *foo() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:21:19
|
LL | udrop::<[u8]>({ *foo() });
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:21:19
|
LL | udrop::<[u8]>({ *foo() });
| ^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:22:11
|
LL | udrop(match foo() { x => *x });
| ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:22:11
|
LL | udrop(match foo() { x => *x });
| ^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:23:19
|
LL | udrop::<[u8]>({ loop { break *foo(); } });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:23:19
|
LL | udrop::<[u8]>({ loop { break *foo(); } });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:25:5
|
LL | { *foo() };
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:25:5
|
LL | { *foo() };
| ^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-non-place-exprs.rs:26:5
|
LL | { loop { break *foo(); } };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: unsized values must be place expressions and cannot be put in temporaries
--> $DIR/unsized-non-place-exprs.rs:26:5
|
LL | { loop { break *foo(); } };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0277`.