diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7b259055d40..acd85dd9a2d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -489,6 +489,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { #[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_with_normalize(place.layout.ty, |ty| ty, || {}); + 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.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 0ab2b7ecd9c..6661f1f81e6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -414,6 +414,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { // value is through `undef`/`poison`, and the store itself is useless. } OperandValue::Ref(r, None, source_align) => { + assert!(dest.layout.is_sized(), "cannot directly store unsized values"); if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. let ty = bx.backend_type(dest.layout); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index c6ff7e2a9ef..b28e3d5c412 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -424,11 +424,23 @@ fn fn_abi_sanity_check<'tcx>( } PassMode::Indirect { meta_attrs: None, .. } => { // No metadata, must be sized. + // Conceptually, unsized arguments must be copied around, which requires dynamically + // determining their size, which we cannot do without metadata. Consult + // t-opsem before removing this check. assert!(arg.layout.is_sized()); } PassMode::Indirect { meta_attrs: Some(_), on_stack, .. } => { // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); + // Also, must not be `extern` type. + let tail = cx.tcx.struct_tail_with_normalize(arg.layout.ty, |ty| ty, || {}); + if matches!(tail.kind(), ty::Foreign(..)) { + // These types do not have metadata, so having `meta_attrs` is bogus. + // Conceptually, 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 arguments must not be `extern` types"); + } } } }