Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_traits/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
}
Expand Down
64 changes: 64 additions & 0 deletions tests/ui/mir/ice-mir-layout-backend-ref-150263.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Read>`, 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<dyn Read>),
Stream(Vec<u8>),
}

// Another complex enum to trigger the layout issue
enum ComplexResult<T> {
Ok(T),
Err(Box<dyn std::error::Error>),
}

// 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<T>(result: ComplexResult<T>) {
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<String> = ComplexResult::Ok("test".to_string());
process_complex(complex);
}
91 changes: 91 additions & 0 deletions tests/ui/traits/ice-signature-mismatch-inlining-150263.rs
Original file line number Diff line number Diff line change
@@ -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
// `<Child<S, T> 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<S: Scope> {
_parent: std::marker::PhantomData<S>,
}

impl<S: Scope> Scope for Child<S> {
type Timestamp = S::Timestamp;
}

impl<S: Scope> NestedScope for Child<S> {
type ParentTimestamp = S::Timestamp;
}

struct Collection<S: Scope, D> {
_scope: std::marker::PhantomData<S>,
_data: std::marker::PhantomData<D>,
}

// This trait with closures triggers the signature mismatch during inlining
trait Enter<S: Scope> {
fn enter<NS>(&self) -> Collection<NS, Self::Data>
where
NS: NestedScope<ParentTimestamp = S::Timestamp>;

type Data;
}

impl<S: Scope, D> Enter<S> for Collection<S, D> {
type Data = D;

fn enter<NS>(&self) -> Collection<NS, D>
where
NS: NestedScope<ParentTimestamp = S::Timestamp>,
{
// During aggressive inlining, this creates closures with:
// FnOnce<(Capability<NS::ParentTimestamp>,)>
// which should normalize to FnOnce<(Capability<S::Timestamp>,)>
// 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<Root, (u64, isize)> = 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<Child<Root>, (u64, isize)> = collection.enter();
}
Loading