Change:
This reduced testcase is getting miscompiled with . I bisected it to . Rust version:
cc
Slightly simpler reproducer fails the same way:
I may be talking nonsense (first time looking at a mir dump), but I think in : should've been initialized similar to (something like ), but it's not.
Looking at MIR dumps, before constant propagation this is how is initialized: After: So the last field disappears.
I think the bug is in these lines: In this program when visiting substs is so the rvalue needs to be not .
Confirmed that commenting out this block of code fixes the issue. Not sure how to proceed. If we want to do this optimization for scalar pairs then I think we'll have to filter out zero-sized types the value's type in . Would that make sense?
Since we are making the decision on something that came from a layout, but then use the type to get at said field, we should probably figure out a layout based scheme for this. You can ask the layout for a field count, then iterate and use the method to get a layout for each field. If it's zst, create a value from nothing, if not, fetch the next value from the scalar pair. If there are three fields, we need three entries even if one of them is zst
Thanks I have a fix, PR coming.

Discussion:
ScalarMaybeUndef::Scalar(one), ScalarMaybeUndef::Scalar(two) ) => { <ins> // Found a value represented as a pair. For now only do cont-prop if type of // Rvalue is also a pair with two scalars. The more general case is more // complicated to implement so we'll do it later. </ins> let ty = &value.layout.ty.kind; <ins> // Only do it for tuples </ins> if let ty::Tuple(substs) = ty { <del> *rval = Rvalue::Aggregate( Box::new(AggregateKind::Tuple), vec![ self.operand_from_scalar( one, substs[0].expect_ty(), source_info.span ), self.operand_from_scalar( two, substs[1].expect_ty(), source_info.span ), ], ); </del> <ins> // Only do it if tuple is also a pair with two scalars if substs.len() == 2 { let opt_ty1_ty2 = self.use_ecx(source_info, |this| { let ty1 = substs[0].expect_ty(); let ty2 = substs[1].expect_ty(); let ty_is_scalar = |ty| { this.ecx .layout_of(ty) .ok() .map(|ty| ty.details.abi.is_scalar()) == Some(true) }; if ty_is_scalar(ty1) && ty_is_scalar(ty2) { Ok(Some((ty1, ty2))) } else { Ok(None) } }); if let Some(Some((ty1, ty2))) = opt_ty1_ty2 { *rval = Rvalue::Aggregate( Box::new(AggregateKind::Tuple), vec![ self.operand_from_scalar( one, ty1, source_info.span ), self.operand_from_scalar( two, ty2, source_info.span ), ], ); } } </ins> } }, _ => { }

_ => false, } } <ins> /// Returns `true` is this is a scalar type pub fn is_scalar(&self) -> bool { match *self { Abi::Scalar(_) => true, _ => false, } } </ins> } rustc_index::newtype_index! {

<ins> // compile-flags: -Z mir-opt-level=2 // Due to a bug in propagating scalar pairs the assertion below used to fail. In the expected // outputs below, after ConstProp this is how _2 would look like with the bug: // //     _2 = (const Scalar(0x00) : (), const 0u8); // // Which has the wrong type. fn encode(this: ((), u8, u8)) { assert!(this.2 == 0); } fn main() { encode(((), 0, 0)); } // END RUST SOURCE // START rustc.main.ConstProp.before.mir //  bb0: { //      ... //      _3 = (); //      _2 = (move _3, const 0u8, const 0u8); //      ... //      _1 = const encode(move _2) -> bb1; //      ... //  } // END rustc.main.ConstProp.before.mir // START rustc.main.ConstProp.after.mir //  bb0: { //      ... //      _3 = const Scalar(<ZST>) : (); //      _2 = (move _3, const 0u8, const 0u8); //      ... //      _1 = const encode(move _2) -> bb1; //      ... //  } // END rustc.main.ConstProp.after.mir </ins>

<ins> // compile-flags: -Z mir-opt-level=2 // This used to ICE in const-prop fn test(this: ((u8, u8),)) { assert!((this.0).0 == 1); } fn main() { test(((1, 2),)); } // Important bit is parameter passing so we only check that below // END RUST SOURCE // START rustc.main.ConstProp.before.mir //  bb0: { //      ... //      _3 = (const 1u8, const 2u8); //      _2 = (move _3,); //      ... //      _1 = const test(move _2) -> bb1; //      ... //  } // END rustc.main.ConstProp.before.mir // START rustc.main.ConstProp.after.mir //  bb0: { //      ... //      _3 = (const 1u8, const 2u8); //      _2 = (move _3,); //      ... //      _1 = const test(move _2) -> bb1; //      ... //  } // END rustc.main.ConstProp.after.mir </ins>

<ins> // compile-flags: -Z mir-opt-level=2 // build-pass // This used to ICE in const-prop fn foo() { let bar = |_| { }; let _ = bar("a"); } fn main() { foo(); } </ins>