From 05c0f73ab78ab8142c574b1f03107443ea8f5f9b Mon Sep 17 00:00:00 2001 From: YUZHEthefool <2804776511@qq.com> Date: Sat, 22 Aug 2026 01:06:05 +0800 Subject: [PATCH] fix: avoid panic for mismatched opaque type parameters --- crates/hir-ty/src/infer/opaques.rs | 84 ++++++++++++++++++++++++++- crates/hir-ty/src/tests/regression.rs | 68 ++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs index 2d64f9567b37..e2d62a6788a3 100644 --- a/crates/hir-ty/src/infer/opaques.rs +++ b/crates/hir-ty/src/infer/opaques.rs @@ -1,13 +1,18 @@ //! Defining opaque types via inference. -use rustc_type_ir::{TypeVisitableExt, fold_regions}; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::{ + GenericArgKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, fold_regions, + inherent::{GenericArgs as _, IntoKind}, +}; use tracing::{debug, instrument}; use crate::{ Span, infer::InferenceContext, next_solver::{ - EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode, + Const, ConstKind, DbInterner, EarlyBinder, GenericArg, GenericArgs, OpaqueTypeKey, Region, + RegionKind, SolverDefId, Ty, TyKind, TypingMode, infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause}, }, }; @@ -31,7 +36,7 @@ impl<'db> InferenceContext<'db> { } } -#[expect(unused, reason = "rustc has this")] +#[expect(dead_code, reason = "payloads are retained for non-defining-use diagnostics")] #[derive(Copy, Clone, Debug)] enum UsageKind<'db> { None, @@ -63,6 +68,73 @@ impl<'db> UsageKind<'db> { } } +struct ReverseMapper<'db> { + interner: DbInterner<'db>, + map: FxHashMap, GenericArg<'db>>, +} + +impl<'db> ReverseMapper<'db> { + fn new(interner: DbInterner<'db>, opaque_type_key: OpaqueTypeKey<'db>) -> Self { + let id_args = GenericArgs::identity_for_item(interner, opaque_type_key.def_id.into()); + let map = opaque_type_key.args.iter().zip(id_args.iter()).collect(); + Self { interner, map } + } +} + +impl<'db> TypeFolder> for ReverseMapper<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + match ty.kind() { + // A parameter outside the opaque declaration is malformed input. Keep the + // recovered type foldable so the caller never passes it to an incompatible binder. + TyKind::Param(_) => match self.map.get(&ty.into()).map(|arg| arg.kind()) { + Some(GenericArgKind::Type(ty)) => ty, + Some(_) | None => self.interner.default_types().types.error, + }, + _ => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, konst: Const<'db>) -> Const<'db> { + match konst.kind() { + ConstKind::Param(_) => match self.map.get(&konst.into()).map(|arg| arg.kind()) { + Some(GenericArgKind::Const(konst)) => konst, + Some(_) | None => self.interner.default_types().consts.error, + }, + _ => konst.super_fold_with(self), + } + } + + fn fold_region(&mut self, region: Region<'db>) -> Region<'db> { + match region.kind() { + RegionKind::ReEarlyParam(_) | RegionKind::ReLateParam(_) => { + match self.map.get(®ion.into()).map(|arg| arg.kind()) { + Some(GenericArgKind::Lifetime(region)) => region, + Some(_) | None => self.interner.default_types().regions.erased, + } + } + _ => region, + } + } +} + +fn opaque_type_has_defining_use_args<'db>( + interner: DbInterner<'db>, + opaque_type_key: OpaqueTypeKey<'db>, +) -> bool { + let mut seen = FxHashSet::default(); + opaque_type_key.iter_captured_args(interner).all(|(_, arg)| match arg.kind() { + GenericArgKind::Lifetime(_) => true, + GenericArgKind::Type(ty) => matches!(ty.kind(), TyKind::Param(_)) && seen.insert(arg), + GenericArgKind::Const(konst) => { + matches!(konst.kind(), ConstKind::Param(_)) && seen.insert(arg) + } + }) +} + impl<'db> InferenceContext<'db> { fn compute_definition_site_hidden_types( &mut self, @@ -129,6 +201,10 @@ impl<'db> InferenceContext<'db> { opaque_type_key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>, ) -> UsageKind<'db> { + if !opaque_type_has_defining_use_args(self.interner(), opaque_type_key) { + return UsageKind::NonDefiningUse(opaque_type_key, hidden_type); + } + // We ignore uses of the opaque if they have any inference variables // as this can frequently happen with recursive calls. // @@ -146,6 +222,8 @@ impl<'db> InferenceContext<'db> { }; let hidden_type = fold_regions(self.interner(), hidden_type, |_, _| self.types.regions.erased); + let hidden_type = + hidden_type.fold_with(&mut ReverseMapper::new(self.interner(), opaque_type_key)); UsageKind::HasDefiningUse(hidden_type) } } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 3ed2eb2594c5..c33f40a57760 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -3193,3 +3193,71 @@ fn main() { "#, ); } + +#[test] +fn issue_23124_atpit_hidden_type_with_method_generic_does_not_panic() { + check_no_mismatches( + r#" + //- minicore: copy + trait Bar { + type E: Copy; + + fn foo() -> Self::E; + } + + impl Bar for S { + type E = impl Copy; + + fn foo() -> Self::E { + || () + } + } + + trait ValidBar { + type E: Copy; + + fn foo() -> Self::E; + } + + impl ValidBar for S { + type E = impl Copy; + + fn foo() -> Self::E { + || () + } + } + "#, + ); + + check_no_mismatches( + r#" + //- minicore: sized + fn recursive() -> impl Sized { + // A concrete recursive call is a non-defining opaque use. + recursive::(); + 1u16 + } + "#, + ); + + check_no_mismatches( + r#" + //- minicore: sized + fn only_nondefining() -> impl Sized { + // There is no defining use, but this must still recover without a panic. + only_nondefining::(); + loop {} + } + "#, + ); + + check_no_mismatches( + r#" + //- minicore: sized + fn const_nondefining() -> impl Sized { + const_nondefining::<1>(); + 1u16 + } + "#, + ); +}