rust-lang/rust · error
unsized locals must not be `extern` types
Error message
unsized locals must not be `extern` types
What it means
Thrown by `panic!("unsized locals must not be `extern` types")` in `load_operand` (builder.rs:726) when loading a place whose layout is unsized and whose struct tail is a `ty::Foreign` (an `extern type` per RFC 1861). rustc can copy unsized locals/args only if it can compute their dynamic size; extern types have no statically derivable size, so loading one as a local is explicitly forbidden. The comment notes this check is intentional and t-opsem should be consulted before removing it.
Source
Thrown at compiler/rustc_codegen_llvm/src/builder.rs:726
unsafe {
let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED);
// Set atomic ordering
llvm::LLVMSetOrdering(load, AtomicOrdering::from_generic(order));
// LLVM requires the alignment of atomic loads to be at least the size of the type.
llvm::LLVMSetAlignment(load, size.bytes() as c_uint);
load
}
}
#[instrument(level = "trace", skip(self))]
fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {
if place.layout.is_unsized() {
let tail = self.tcx.struct_tail_for_codegen(place.layout.ty, self.typing_env());
if matches!(tail.kind(), ty::Foreign(..)) {
// Unsized locals and, at least conceptually, even unsized arguments must be copied
// around, which requires dynamically determining their size. Therefore, we cannot
// allow `extern` types here. Consult t-opsem before removing this check.
panic!("unsized locals must not be `extern` types");
}
}
assert_eq!(place.val.llextra.is_some(), place.layout.is_unsized());
if place.layout.is_zst() {
return OperandRef::zero_sized(place.layout);
}
#[instrument(level = "trace", skip(bx))]
fn scalar_load_metadata<'a, 'll, 'tcx>(
bx: &mut Builder<'a, 'll, 'tcx>,
load: &'ll Value,
scalar: abi::Scalar,
layout: TyAndLayout<'tcx>,
offset: Size,
) {
if bx.cx.sess().opts.optimize == OptLevel::No {
// Don't emit metadata we're not going to useView on GitHub (pinned to 22057b88b0)
Solutions
- Do not bind extern types as unsized locals/by-value args — keep them behind a pointer (`&`, `*const`, `Box`) instead.
- Disable `unsized_locals` if the code doesn't strictly require it, since extern types + unsized locals is unsupported.
- Refactor the extern type usage so the value is never loaded by value (operate through references).
- Update to a newer nightly in case the interaction gains defined behavior, but expect this to remain disallowed by design.
- If you believe the load is legitimate, consult the t-opsem working group / file an issue, since the check is deliberately conservative.
Example fix
// before: binding an extern type as an unsized local panics on load
#![feature(extern_types, unsized_locals)]
extern "C" { type Opaque; }
fn f(x: Box<Opaque>) {
let local: Opaque = *x; // panic: unsized locals must not be `extern` types
}
// after: keep the extern type behind a pointer, never load by value
fn f(x: Box<Opaque>) {
let _ref: &Opaque = &*x; // OK: no by-value load of the extern-tailed unsized value
} Defensive patterns
Strategy: type-guard
Type guard
fn assert_not_extern_local<T: ?Sized>() {
// reject `extern` types used as values/locals; only allow them behind a pointer.
const _: () = { /* compile-time lint hook: ensure no extern type reaches load_operand by value */ };
}
// At the source level, never write: let x: MyExternType = ...;
// instead always use: let x: &MyExternType = ...; Prevention
- Never use an `extern` type (or any unsized type whose struct tail is Foreign) as a local variable or by-value argument.
- Always place `extern` types behind a reference, Box, or pointer - access them indirectly.
- Do not enable the unsized_locals feature with functions whose unsized parameters are extern types.
- If you need a dynamically-sized opaque type, give it a sized tail (e.g. trailing `[u8]`) instead of `extern`.
When it happens
Trigger: Triggered when code uses the `unsized_locals` and/or `extern_types` nightly features such that an unsized local (or by-value unsized argument) is bound to a value whose tail type is an `extern "C"`-style foreign type, and the codegen builder tries to load it. The `load_operand` path then hits the panic because no dynamic size can be determined for the foreign-typed tail.
Common situations: Experimenting with `#![feature(extern_types)]` together with `#![feature(unsized_locals)]`; binding an FFI extern type by value into a local; passing an extern-typed value as an unsized argument. Both features are unstable and their interaction is unsupported, so this panic guards an unimplemented corner.
Related errors
- slice element type unknown
- unsupported integer: {self:?}
- unsupported float: {self:?}
- ptr_sized_integer: unknown pointer bit size {bits}
- `Self` generic param is not found while expected
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/ffade53ce7d97035.json.
Report an issue: GitHub.