{"id":"ffade53ce7d97035","repo":"rust-lang/rust","slug":"unsized-locals-must-not-be-extern-types","errorCode":null,"errorMessage":"unsized locals must not be `extern` types","messagePattern":"unsized locals must not be `extern` types","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_codegen_llvm/src/builder.rs","lineNumber":726,"sourceCode":"        unsafe {\n            let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED);\n            // Set atomic ordering\n            llvm::LLVMSetOrdering(load, AtomicOrdering::from_generic(order));\n            // LLVM requires the alignment of atomic loads to be at least the size of the type.\n            llvm::LLVMSetAlignment(load, size.bytes() as c_uint);\n            load\n        }\n    }\n\n    #[instrument(level = \"trace\", skip(self))]\n    fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {\n        if place.layout.is_unsized() {\n            let tail = self.tcx.struct_tail_for_codegen(place.layout.ty, self.typing_env());\n            if matches!(tail.kind(), ty::Foreign(..)) {\n                // Unsized locals and, at least conceptually, even unsized arguments must be copied\n                // around, which requires dynamically determining their size. Therefore, we cannot\n                // allow `extern` types here. Consult t-opsem before removing this check.\n                panic!(\"unsized locals must not be `extern` types\");\n            }\n        }\n        assert_eq!(place.val.llextra.is_some(), place.layout.is_unsized());\n\n        if place.layout.is_zst() {\n            return OperandRef::zero_sized(place.layout);\n        }\n\n        #[instrument(level = \"trace\", skip(bx))]\n        fn scalar_load_metadata<'a, 'll, 'tcx>(\n            bx: &mut Builder<'a, 'll, 'tcx>,\n            load: &'ll Value,\n            scalar: abi::Scalar,\n            layout: TyAndLayout<'tcx>,\n            offset: Size,\n        ) {\n            if bx.cx.sess().opts.optimize == OptLevel::No {\n                // Don't emit metadata we're not going to use","sourceCodeStart":708,"sourceCodeEnd":744,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_codegen_llvm/src/builder.rs#L708-L744","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: binding an extern type as an unsized local panics on load\n#![feature(extern_types, unsized_locals)]\nextern \"C\" { type Opaque; }\nfn f(x: Box<Opaque>) {\n    let local: Opaque = *x;   // panic: unsized locals must not be `extern` types\n}\n\n// after: keep the extern type behind a pointer, never load by value\nfn f(x: Box<Opaque>) {\n    let _ref: &Opaque = &*x;  // OK: no by-value load of the extern-tailed unsized value\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn assert_not_extern_local<T: ?Sized>() {\n    // reject `extern` types used as values/locals; only allow them behind a pointer.\n    const _: () = { /* compile-time lint hook: ensure no extern type reaches load_operand by value */ };\n}\n// At the source level, never write:   let x: MyExternType = ...;\n// instead always use:                 let x: &MyExternType = ...;","tryCatchPattern":null,"preventionTips":["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`."],"tags":["rustc","codegen","unsized-locals","extern-types","nightly"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}