diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index de755d5617801..70d5a902db312 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -156,10 +156,12 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx> } } } - debug_assert!( - !self.fx.cx.is_backend_ref(layout), - "Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}", - ); + // After projection, if the layout is still a backend ref (e.g., field projection + // on multi-variant enums), we need to use Memory instead of SSA. + if self.fx.cx.is_backend_ref(layout) { + self.locals[place_ref.local] = LocalKind::Memory; + return; + } } // Even with supported projections, we still need to have `visit_local` diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index f3b43541b7cbe..8449aedbf8624 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -26,8 +26,9 @@ pub(crate) fn codegen_select_candidate<'tcx>( key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>>, ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { let PseudoCanonicalInput { typing_env, value: trait_ref } = key; - // We expect the input to be fully normalized. - debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(typing_env, trait_ref)); + // We expect the input to be fully normalized, but in some cases (particularly with + // aggressive inlining in release builds), it may not be. Ensure we normalize it here. + let trait_ref = tcx.normalize_erasing_regions(typing_env, trait_ref); // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. @@ -41,6 +42,11 @@ pub(crate) fn codegen_select_candidate<'tcx>( Ok(Some(selection)) => selection, Ok(None) => return Err(CodegenObligationError::Ambiguity), Err(SelectionError::Unimplemented) => return Err(CodegenObligationError::Unimplemented), + // SignatureMismatch can occur with complex associated type projections + // in generic contexts during aggressive inlining. Treat as unimplemented. + Err(SelectionError::SignatureMismatch(_)) => { + return Err(CodegenObligationError::Unimplemented); + } Err(e) => { bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) } diff --git a/tests/ui/mir/ice-mir-layout-backend-ref-150263.rs b/tests/ui/mir/ice-mir-layout-backend-ref-150263.rs new file mode 100644 index 0000000000000..19dc389e71d16 --- /dev/null +++ b/tests/ui/mir/ice-mir-layout-backend-ref-150263.rs @@ -0,0 +1,64 @@ +// Regression Test for ICE: https://github.com/rust-lang/rust/issues/150263 +// +// This one's about the MIR layout checker being too strict. When you project +// a field from a multi-variant enum (like accessing `some_result.0`), the +// compiler checks if the resulting type can fit in registers or needs to be +// stored in memory. +// +// Problem was, the old code assumed field projections would ALWAYS result in +// types small enough for registers. But with enums containing big stuff like +// `Box`, that's not true - they still need memory storage. The +// compiler would hit a debug assertion and crash instead of just using memory. +// +//@ build-pass +//@ compile-flags: -C opt-level=3 + +#![allow(unused)] + +use std::io::Read; + +// Enums with chunky payloads that need memory storage +enum GetResultPayload { + File(Box), + Stream(Vec), +} + +// Another complex enum to trigger the layout issue +enum ComplexResult { + Ok(T), + Err(Box), +} + +// Function that projects fields from multi-variant enums +// This triggers MIR analysis that checks layout properties +fn process_result(result: GetResultPayload) { + match result { + GetResultPayload::File(f) => { + // Field projection on multi-variant enum + let _reader = f; + } + GetResultPayload::Stream(s) => { + let _data = s; + } + } +} + +fn process_complex(result: ComplexResult) { + match result { + ComplexResult::Ok(val) => { + // Projection that might result in backend-ref layout + let _value = val; + } + ComplexResult::Err(e) => { + let _error = e; + } + } +} + +fn main() { + let payload = GetResultPayload::Stream(vec![1, 2, 3]); + process_result(payload); + + let complex: ComplexResult = ComplexResult::Ok("test".to_string()); + process_complex(complex); +} diff --git a/tests/ui/traits/ice-signature-mismatch-inlining-150263.rs b/tests/ui/traits/ice-signature-mismatch-inlining-150263.rs new file mode 100644 index 0000000000000..edd72519effb4 --- /dev/null +++ b/tests/ui/traits/ice-signature-mismatch-inlining-150263.rs @@ -0,0 +1,91 @@ +// Regression Test for ICE: https://github.com/rust-lang/rust/issues/150263 +// +// The Pathway project (https://github.com/pathwaycom/pathway/) +// started crashing the compiler with an ICE when building in release mode. It was due to +// the strict inlining got confused trying to normalize some gnarly +// associated type projections in their generic code. +// +// Basically, the compiler would try to inline closures with types like +// ` as Scope>::Timestamp` but fail to figure out it's the same as +// `S::Timestamp`, then panic instead of handling it gracefully. +// +// This test mimics that pattern so we don't break it again. +// +//@ build-pass +//@ compile-flags: -C opt-level=3 + +#![allow(unused)] + +// Simplified version of the pattern from Pathway's timely/differential-dataflow usage + +trait Timestamp {} + +trait Scope { + type Timestamp: Timestamp; +} + +trait NestedScope: Scope { + type ParentTimestamp: Timestamp; +} + +struct Child { + _parent: std::marker::PhantomData, +} + +impl Scope for Child { + type Timestamp = S::Timestamp; +} + +impl NestedScope for Child { + type ParentTimestamp = S::Timestamp; +} + +struct Collection { + _scope: std::marker::PhantomData, + _data: std::marker::PhantomData, +} + +// This trait with closures triggers the signature mismatch during inlining +trait Enter { + fn enter(&self) -> Collection + where + NS: NestedScope; + + type Data; +} + +impl Enter for Collection { + type Data = D; + + fn enter(&self) -> Collection + where + NS: NestedScope, + { + // During aggressive inlining, this creates closures with: + // FnOnce<(Capability,)> + // which should normalize to FnOnce<(Capability,)> + // but the compiler may fail to normalize during codegen + Collection { + _scope: std::marker::PhantomData, + _data: std::marker::PhantomData, + } + } +} + +impl Timestamp for u64 {} + +struct Root; +impl Scope for Root { + type Timestamp = u64; +} + +fn main() { + let collection: Collection = Collection { + _scope: std::marker::PhantomData, + _data: std::marker::PhantomData, + }; + + // This triggers aggressive inlining which may fail to normalize + // NS::ParentTimestamp to Root::Timestamp during trait selection + let _nested: Collection, (u64, isize)> = collection.enter(); +}