Change:
Messing around with a Haskell-like implementation of , I encountered two presumably similar ICEs (would think they're the same, but the error messages are obviously different). The other one is . I'm pretty sure this should be an error, based on the reasoning that returns an opaque type, so, while the output of might be , it also might not (and in this case, isn't), and since only accepts s, should be an error. Note that this implementation throws the ICE even without the main method, but I'm leaving it there for contrast to the other ICE. : Same ICE on nightly: <details<summary<strongBacktrace</strong</summary<p</p</details
This regressed in
I was able to "weaponize" this into a transmute, lol: So the issue here is that, at least in the case of RPITs, we are not checking for opaque type mismatches in closures correctly. The closure, in this example, is able to incorrectly constrain even though in the enclosing function, we define . During mir borrowck, we never check these two together, so we never report a type mismatch. I think I have a fix, though.
WG-prioritization assigning priority (). label -I-prioritize +P-critical

Discussion:
use rustc_infer::infer::{ InferCtxt, InferOk, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin, }; <del> use rustc_infer::traits::ObligationCause; </del> use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::AssertKind;

) .unwrap(); let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); <del> // Check that RPITs are only constrained in their outermost // function, otherwise report a mismatched types error. if let OpaqueTyOrigin::FnReturn(parent) | OpaqueTyOrigin::AsyncFn(parent) = infcx.opaque_ty_origin_unchecked(opaque_type_key.def_id, hidden_type.span) && parent.to_def_id() != body.source.def_id() { infcx .report_mismatched_types( &ObligationCause::misc( hidden_type.span, infcx.tcx.hir().local_def_id_to_hir_id( body.source.def_id().expect_local(), ), ), infcx.tcx.mk_opaque(opaque_type_key.def_id.to_def_id(), opaque_type_key.substs), hidden_type.ty, ty::error::TypeError::Mismatch, ) .emit(); } </del> trace!( "finalized opaque type {:?} to {:#?}", opaque_type_key,

} #[instrument(skip(self), level = "trace")] <del> pub fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { </del> <ins> fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { </ins> let origin = match self.tcx.hir().expect_item(def_id).kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin, ref itemkind => {

tcx.mk_adt(def, substs) } ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { <del> find_opaque_ty_constraints(tcx, def_id) </del> <ins> find_opaque_ty_constraints_for_tait(tcx, def_id) </ins> } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { <del> let concrete_ty = tcx .mir_borrowck(owner) .concrete_opaque_types .get(&def_id) .copied() .map(|concrete| concrete.ty) .unwrap_or_else(|| { let table = tcx.typeck(owner); if let Some(_) = table.tainted_by_errors { // Some error in the // owner fn prevented us from populating // the `concrete_opaque_types` table. tcx.ty_error() } else { table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| { // We failed to resolve the opaque type or it // resolves to itself. We interpret this as the // no values of the hidden type ever being constructed, // so we can just make the hidden type be `!`. // For backwards compatibility reasons, we fall back to // `()` until we the diverging default is changed. Some(tcx.mk_diverging_default()) }).expect("RPIT always have a hidden type from typeck") } }); debug!("concrete_ty = {:?}", concrete_ty); concrete_ty </del> <ins> find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) </ins> } ItemKind::Trait(..) | ItemKind::TraitAlias(..)

/// fn b<T>() -> Foo<T, u32> { .. } /// ``` /// <del> fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { </del> <ins> fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { </ins> use rustc_hir::{Expr, ImplItem, Item, TraitItem}; struct ConstraintLocator<'tcx> {

} } <ins> fn find_opaque_ty_constraints_for_rpit( tcx: TyCtxt<'_>, def_id: LocalDefId, owner_def_id: LocalDefId, ) -> Ty<'_> { use rustc_hir::{Expr, ImplItem, Item, TraitItem}; struct ConstraintChecker<'tcx> { tcx: TyCtxt<'tcx>, /// def_id of the opaque type whose defining uses are being checked def_id: LocalDefId, found: ty::OpaqueHiddenType<'tcx>, } impl ConstraintChecker<'_> { #[instrument(skip(self), level = "debug")] fn check(&self, def_id: LocalDefId) { // Use borrowck to get the type with unerased regions. let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; debug!(?concrete_opaque_types); for &(def_id, concrete_type) in concrete_opaque_types { if def_id != self.def_id { // Ignore constraints for other opaque types. continue; } debug!(?concrete_type, "found constraint"); if concrete_type.ty != self.found.ty && !(concrete_type, self.found).references_error() { self.found.report_mismatch(&concrete_type, self.tcx); } } } } impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure { .. } = ex.kind { let def_id = self.tcx.hir().local_def_id(ex.hir_id); self.check(def_id); } intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { trace!(?it.def_id); // The opaque type itself or its children are not within its reveal scope. if it.def_id != self.def_id { self.check(it.def_id); intravisit::walk_item(self, it); } } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { trace!(?it.def_id); // The opaque type itself or its children are not within its reveal scope. if it.def_id != self.def_id { self.check(it.def_id); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { trace!(?it.def_id); self.check(it.def_id); intravisit::walk_trait_item(self, it); } } let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); if let Some(concrete) = concrete { let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); debug!(?scope); let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete }; match tcx.hir().get(scope) { Node::Item(it) => intravisit::walk_item(&mut locator, it), Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), other => bug!("{:?} is not a valid scope for an opaque type item", other), } } concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { let table = tcx.typeck(owner_def_id); if let Some(_) = table.tainted_by_errors { // Some error in the // owner fn prevented us from populating // the `concrete_opaque_types` table. tcx.ty_error() } else { table .concrete_opaque_types .get(&def_id) .copied() .unwrap_or_else(|| { // We failed to resolve the opaque type or it // resolves to itself. We interpret this as the // no values of the hidden type ever being constructed, // so we can just make the hidden type be `!`. // For backwards compatibility reasons, we fall back to // `()` until we the diverging default is changed. Some(tcx.mk_diverging_default()) }) .expect("RPIT always have a hidden type from typeck") } }) } </ins> fn infer_placeholder_type<'a>( tcx: TyCtxt<'a>, def_id: LocalDefId,

fn test<T: Display>(t: T, recurse: bool) -> impl Display { let f = || { let i: u32 = test::<i32>(-1, false); <del> //~^ ERROR mismatched types </del> <ins> //~^ ERROR concrete type differs from previous defining opaque type use </ins> println!("{i}"); }; if recurse {

<del> error[E0308]: mismatched types </del> <ins> error: concrete type differs from previous defining opaque type use </ins> --> $DIR/issue-99073-2.rs:9:22 | <del> LL | fn test<T: Display>(t: T, recurse: bool) -> impl Display { |                                             ------------ the expected opaque type LL |     let f = || { </del> LL |         let i: u32 = test::<i32>(-1, false); <del> |                      ^^^^^^^^^^^^^^^^^^^^^^ types differ </del> <ins> |                      ^^^^^^^^^^^^^^^^^^^^^^ expected `T`, got `u32` </ins> | <del> = note: expected opaque type `impl std::fmt::Display` found type `u32` </del> <ins> note: previous use here --> $DIR/issue-99073-2.rs:16:5 | LL |     t |     ^ </ins> error: aborting due to previous error <del> For more information about this error, try `rustc --explain E0308`. </del>

fn main() { <del> let _ = fix(|_: &dyn Fn()| {}); </del> <ins> let _ = fix(|_: &dyn Fn()| {}); </ins> } fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() { <del> move || f(fix(&f)) //~^ ERROR mismatched types </del> <ins> move || f(fix(&f)) //~^ ERROR concrete type differs from previous defining opaque type use </ins> }

<del> error[E0308]: mismatched types --> $DIR/issue-99073.rs:6:13 </del> <ins> error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073.rs:6:11 </ins> | <del> LL | fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() { |                                    --------- the expected opaque type LL |     move || f(fix(&f)) |             ^^^^^^^^^^ types differ </del> <ins> LL |   move || f(fix(&f)) |           ^^^^^^^^^^ expected `[closure@$DIR/issue-99073.rs:6:3: 6:10]`, got `G` </ins> | <del> = note: expected opaque type `impl Fn()` found type parameter `G` </del> <ins> note: previous use here --> $DIR/issue-99073.rs:6:3 | LL |   move || f(fix(&f)) |   ^^^^^^^^^^^^^^^^^^ </ins> error: aborting due to previous error <del> For more information about this error, try `rustc --explain E0308`. </del>