{"id":"49288306c628a8ab","repo":"rust-lang/rust","slug":"alt-layout-should-always-work","errorCode":null,"errorMessage":"alt layout should always work","messagePattern":"alt layout should always work","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_abi/src/layout.rs","lineNumber":300,"sourceCode":"        // run and bias niches to the right and then check which one is closer to one of the\n        // struct's edges.\n        if let Ok(layout) = &layout {\n            // Don't try to calculate an end-biased layout for unsizable structs,\n            // otherwise we could end up with different layouts for\n            // Foo<Type> and Foo<dyn Trait> which would break unsizing.\n            if !matches!(kind, StructKind::MaybeUnsized) {\n                if let Some(niche) = layout.largest_niche {\n                    let head_space = niche.offset.bytes();\n                    let niche_len = niche.value.size(dl).bytes();\n                    let tail_space = layout.size.bytes() - head_space - niche_len;\n\n                    // This may end up doing redundant work if the niche is already in the last\n                    // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial\n                    // to get the unpadded size so we try anyway.\n                    if fields.len() > 1 && head_space != 0 && tail_space > 0 {\n                        let alt_layout = self\n                            .univariant_biased(fields, repr, kind, NicheBias::End)\n                            .expect(\"alt layout should always work\");\n                        let alt_niche = alt_layout\n                            .largest_niche\n                            .expect(\"alt layout should have a niche like the regular one\");\n                        let alt_head_space = alt_niche.offset.bytes();\n                        let alt_niche_len = alt_niche.value.size(dl).bytes();\n                        let alt_tail_space =\n                            alt_layout.size.bytes() - alt_head_space - alt_niche_len;\n\n                        debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes());\n\n                        let prefer_alt_layout =\n                            alt_head_space > head_space && alt_head_space > tail_space;\n\n                        debug!(\n                            \"sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\\n\\\n                            layout: {}\\n\\\n                            alt_layout: {}\\n\",\n                            layout.size.bytes(),","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_abi/src/layout.rs#L282-L318","documentation":"During struct layout, after a niche is found in the default (head-biased) layout, the compiler tries an end-biased alternative (`univariant_biased(..., NicheBias::End)`) to see if pushing the niche toward the tail leaves less padding. The comment 'alt layout should always work' reflects that the end-biased run uses the exact same fields, repr, and constraints as the already-successful head-biased run, so it cannot legitimately fail — an `Err` here means the biased variant has a bug.","triggerScenarios":"`LayoutCalculator::univariant_biased(fields, repr, kind, NicheBias::End)` returning `Err` in the niche-optimization fallback branch of `univariant`, when the earlier `univariant_biased(..., NicheBias::Start)` for the same inputs already succeeded.","commonSituations":"A change to `univariant_biased` that introduces an asymmetry between `NicheBias::Start` and `NicheBias::End` (e.g. an offset calculation that only works for head-bias), a niche/alignment edge case in a new target's data layout, or a refactor that made the end-bias path reject inputs the start-bias path accepts.","solutions":["Compare the `Start`- and `End`-bias arms of `univariant_biased` — they must be symmetric modulo the bias direction; restore any lost symmetry.","Reproduce with the offending struct, dump both layouts via the existing `debug!` instrumentation in this function, and diff the offsets.","Add a regression test mirroring the failing struct so the symmetry is enforced.","If it reproduces on upstream rustc, file an ICE with the struct definition and `-Ztreat-err-as-bug` backtrace."],"exampleFix":"// before — End-bias arm rejects an offset/alignment Start-bias accepts\nmatch niche_bias {\n    NicheBias::Start => compute(),\n    NicheBias::End => return Err(...), // asymmetry -> panic\n}\n\n// after — both arms share one parameterized computation\ncompute(niche_bias)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// 'alt layout should always work' is a .expect() on the second niche-biased pass\n// of univariant; by construction it should succeed whenever the first pass did.\n// A panic here is a layout-calculator bug, so isolate univariant calls.\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    calc.univariant(&fields, &repr, kind)\n}));\nmatch result {\n    Ok(Ok(layout)) => { /* use layout */ }\n    Ok(Err(e))     => { /* report layout error to user */ }\n    Err(payload)   => {\n        let msg = payload\n            .downcast_ref::<String>().map(|s| s.as_str())\n            .or_else(|| payload.downcast_ref::<&'static str>().copied())\n            .unwrap_or(\"univariant alt-layout panic\");\n        log::error!(\"layout calculator invariant violated: {msg}; fields={:?}\", &fields);\n        // fall back: retry with StructKind::Aligned so the caller still gets a layout\n    }\n}","preventionTips":["This is an internal invariant of LayoutCx::univariant, not a precondition a caller controls; treat any trip as a bug and capture the field list + repr for reproduction.","When computing layouts for many types in batch (codegen, miri), wrap each univariant call in catch_unwind so a niche-biasing regression does not abort the whole pipeline.","If you hit it consistently on one type, simplify that type (remove niche-bearing trailing fields) to confirm the regression is in niche placement, then report the minimized case."],"tags":["rustc","struct-layout","niche-optimization","ice"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}