Code change:
/// /// This is required because we may still want to run MIR passes on an item /// with type errors, but normal MIR construction can't handle that in general. <del> fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Body<'_> { let span = tcx.def_span(def); let hir_id = tcx.hir().local_def_id_to_hir_id(def); let coroutine_kind = tcx.coroutine_kind(def); let body_owner_kind = tcx.hir().body_owner_kind(def); let ty = Ty::new_error(tcx, err); let num_params = match body_owner_kind { hir::BodyOwnerKind::Fn => tcx.fn_sig(def).skip_binder().inputs().skip_binder().len(), hir::BodyOwnerKind::Closure => { let ty = tcx.type_of(def).instantiate_identity(); match ty.kind() { ty::Closure(_, args) => 1 + args.as_closure().sig().inputs().skip_binder().len(), ty::Coroutine(..) => 2, _ => bug!("expected closure or coroutine, found {ty:?}"), } </del> <ins> fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -> Body<'_> { let span = tcx.def_span(def_id); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let coroutine_kind = tcx.coroutine_kind(def_id); let (inputs, output, yield_ty) = match tcx.def_kind(def_id) { DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst | DefKind::Static(_) => (vec![], tcx.type_of(def_id).instantiate_identity(), None), DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => { let sig = tcx.liberate_late_bound_regions( def_id.to_def_id(), tcx.fn_sig(def_id).instantiate_identity(), ); (sig.inputs().to_vec(), sig.output(), None) } DefKind::Closure => { let closure_ty = tcx.type_of(def_id).instantiate_identity(); let ty::Closure(_, args) = closure_ty.kind() else { bug!() }; let args = args.as_closure(); let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig()); let self_ty = match args.kind() { ty::ClosureKind::Fn => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty), ty::ClosureKind::FnMut => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty), ty::ClosureKind::FnOnce => closure_ty, }; ([self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), sig.output(), None) } DefKind::Coroutine => { let coroutine_ty = tcx.type_of(def_id).instantiate_identity(); let ty::Coroutine(_, args, _) = coroutine_ty.kind() else { bug!() }; let args = args.as_coroutine(); let yield_ty = args.yield_ty(); let return_ty = args.return_ty(); let self_ty = Ty::new_adt( tcx, tcx.adt_def(tcx.lang_items().pin_type().unwrap()), tcx.mk_args(&[Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty).into()]), ); let coroutine_state = Ty::new_adt( tcx, tcx.adt_def(tcx.lang_items().coroutine_state().unwrap()), tcx.mk_args(&[yield_ty.into(), return_ty.into()]), ); (vec![self_ty, args.resume_ty()], coroutine_state, Some(yield_ty)) </ins> } <del> hir::BodyOwnerKind::Const { .. } => 0, hir::BodyOwnerKind::Static(_) => 0, </del> <ins> dk => bug!("{:?} is not a body: {:?}", def_id, dk), </ins> }; <ins> let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; let local_decls = IndexVec::from_iter( [output].iter().chain(&inputs).map(|ty| LocalDecl::with_source_info(*ty, source_info)), ); </ins> let mut cfg = CFG { basic_blocks: IndexVec::new() }; let mut source_scopes = IndexVec::new(); <del> let mut local_decls = IndexVec::from_elem_n(LocalDecl::new(ty, span), 1); </del> cfg.start_new_block(); source_scopes.push(SourceScopeData {

Related discussion:
text: <!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code (): Somehow in the second case we think that is unconstrained even though it doesn't seem to contain more or less type information than the first case. Note also that this does not error if I use RPIT instead.
I think in the second example mir building doesn't generate code for the trailing expression because it's statically known as dead code. But I thought we were keeping around the typeck types nowadays, and typeck should be the same for both functions
I don't think that's it, same error if I put the match in a branch ():
In fact this doesn't need an empty type at all:
I think we're generating bogus MIR or THIR because of that empty match -- Are you sure the bug doesn't exist on the THIR/MIR building side, (hacked to dump the instead of )
Oh I'm sure of nothing I just assumed an unreachable in a branch would not affect anything else
So: If there's a match error, we end up constructing a failure MIR body. This will prevent us from inferring the opaque later on. edit: that last sentence was a lie Let me see if I can cook up a quick fix.