From d799ff3c0cb3c2e028ee53cde8c6ce8cc9646231 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 7 Aug 2026 13:56:04 +0100 Subject: [PATCH] fix: Unresolved type variables shouldn't escape impl selection find_matching_impl() called resolve_vars_if_possible(), but we rely on unresolved region inference variables not escaping. Ensure that we substitute these variables. Without this fix, we can panic when trying to resolve the variable in the wrong inference context. See the new unit test. This seems to have always been wrong, but the panic only started occurring due when libcore changed in https://github.com/rust-lang/rust/pull/136006. AI disclosure: Code partly written by GPT-5 Codex. I've bisected and confirmed the repro, and reviewed all the code, but possibly there's a nicer place to fix this logic. --- crates/hir-ty/src/method_resolution.rs | 12 ++++++++++-- crates/hir-ty/src/mir/eval/tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 07ff9ac1936c..37e29b10cd5a 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -31,7 +31,7 @@ use hir_def::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - TypeVisitableExt, VisitorResult, + TypeFoldable, TypeVisitableExt, VisitorResult, fast_reject::{TreatParams, simplify_type}, inherent::{BoundExistentialPredicates, IntoKind}, try_visit, @@ -49,6 +49,7 @@ use crate::{ SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode, Unnormalized, infer::{ BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk, + resolve::ReplaceInferWithError, select::ImplSource, traits::{Obligation, ObligationCause, PredicateObligations}, }, @@ -510,8 +511,15 @@ pub(crate) fn find_matching_impl<'db>( return None; } + // Selection may leave region inference variables unresolved; replace them before they escape + // this inference context. + // + // FIXME: decide whether inferred regions should be replaced with error or erased. match impl_source { - ImplSource::UserDefined(impl_source) => Some((impl_source.impl_def_id, impl_source.args)), + ImplSource::UserDefined(impl_source) => Some(( + impl_source.impl_def_id, + impl_source.args.fold_with(&mut ReplaceInferWithError::new(infcx.interner)), + )), ImplSource::Param(_) | ImplSource::Builtin(..) => None, } } diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 7431ac8293e9..f09ac6f20d27 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -458,6 +458,29 @@ fn main() { ); } +#[test] +fn region_infer_var_does_not_escape_impl_lookup() { + check_pass( + r#" +//- minicore: fn, sized + +trait Bound {} +trait Trait { fn f(self); } + +struct S; + +impl<'a> Bound<&'a ()> for S {} +impl<'a, T: Bound<&'a ()>> Trait for T { + fn f(self) { make::<'a>(); } +} + +fn make<'a>() -> impl Fn(&'a ()) { |_| {} } + +fn main() { S.f(); } +"#, + ); +} + #[test] fn index_of_slice_should_preserve_len() { check_pass(