From 2b4dc911b413bfb5216fc6d97b12ec367cd08e70 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 9 Jul 2026 15:17:50 +0200 Subject: [PATCH 1/5] split fallback of different infer var kinds --- compiler/rustc_hir_typeck/src/fallback.rs | 159 ++++++++++++---------- compiler/rustc_infer/src/infer/mod.rs | 33 ++--- 2 files changed, 101 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 0e34a6120b609..ff2bd9734ced8 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -45,90 +45,102 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } + /// Tries to apply a fallback to all unresolved variables. + /// + /// - Unconstrained ints are replaced with `i32`. + /// + /// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate + /// `f32: From<{float}>`, in which case `f32` is used as the fallback instead. + /// + /// - Non-numerics may get replaced with `()` or `!`, depending on how they + /// were categorized by [`Self::calculate_diverging_fallback`], crate's + /// edition, and the setting of `#![rustc_never_type_options(fallback = ...)]`. + /// + /// Fallback becomes very dubious if we have encountered type-checking errors. + /// In that case, all variables fallback to Error. + /// + /// Sets [`FnCtxt::diverging_fallback_has_occurred`] if never type fallback + /// is performed during this call. + /// + /// Returns `true` if *any* kind of fallback has occurred during this call. fn fallback_types(&self) -> bool { - // Check if we have any unresolved variables. If not, no need for fallback. - let unresolved_variables = self.unresolved_variables(); + let (unresolved_ty, unresolved_int, unresolved_float) = self.unresolved_variables(); - if unresolved_variables.is_empty() { + // Check if we have any unresolved variables. If not, no need for fallback. + if unresolved_ty.is_empty() && unresolved_int.is_empty() && unresolved_float.is_empty() { return false; } let (diverging_fallback, diverging_fallback_ty) = - self.calculate_diverging_fallback(&unresolved_variables); - let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_variables); + self.calculate_diverging_fallback(&unresolved_ty); + let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_float); // We do fallback in two passes, to try to generate // better error messages. // The first time, we do *not* replace opaque types. let mut fallback_occurred = false; - for ty in unresolved_variables { - debug!("unsolved_variable = {:?}", ty); + + for vid in unresolved_ty { fallback_occurred |= self.fallback_if_possible( - ty, - &diverging_fallback, - diverging_fallback_ty, - &fallback_to_f32, + vid, + || { + diverging_fallback.contains(&vid).then(|| { + self.diverging_fallback_has_occurred.set(true); + diverging_fallback_ty + }) + }, + |vid| (Ty::new_var(self.tcx, vid), self.type_var_origin(vid).span), + ); + } + + for vid in unresolved_int { + fallback_occurred |= self.fallback_if_possible( + vid, + || Some(self.tcx.types.i32), + // Int variables have no origin?.. + |vid| (Ty::new_int_var(self.tcx, vid), DUMMY_SP), + ); + } + + for vid in unresolved_float { + fallback_occurred |= self.fallback_if_possible( + vid, + || { + Some(if fallback_to_f32.contains(&vid) { + self.tcx.types.f32 + } else { + self.tcx.types.f64 + }) + }, + |vid| (Ty::new_float_var(self.tcx, vid), self.float_var_origin(vid).span), ); } fallback_occurred } - /// Tries to apply a fallback to `ty` if it is an unsolved variable. - /// - /// - Unconstrained ints are replaced with `i32`. + /// Applies fallback to `vid`, if possible. /// - /// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate - /// `f32: From<{float}>`, in which case `f32` is used as the fallback instead. - /// - /// - Non-numerics may get replaced with `()` or `!`, depending on how they - /// were categorized by [`Self::calculate_diverging_fallback`], crate's - /// edition, and the setting of `#![rustc_never_type_options(fallback = ...)]`. + /// - If `self.tainted_by_errors()` unifies the type represented by `vid` with error + /// - Otherwise, if `fallback` returns `Some`, unifies it with the output of `fallback` + /// - Otherwise, does nothing /// - /// Fallback becomes very dubious if we have encountered - /// type-checking errors. In that case, fallback to Error. - /// - /// Sets [`FnCtxt::diverging_fallback_has_occurred`] if never type fallback - /// is performed during this call. - fn fallback_if_possible( + /// Returns whatever fallback has been applied. + fn fallback_if_possible( &self, - ty: Ty<'tcx>, - diverging_fallback: &UnordSet>, - diverging_fallback_ty: Ty<'tcx>, - fallback_to_f32: &UnordSet, + vid: V, + fallback: impl FnOnce() -> Option>, + vid_to_ty_and_span: impl FnOnce(V) -> (Ty<'tcx>, Span), ) -> bool { - // Careful: we do NOT shallow-resolve `ty`. We know that `ty` - // is an unsolved variable, and we determine its fallback - // based solely on how it was created, not what other type - // variables it may have been unified with since then. - // - // The reason this matters is that other attempts at fallback - // may (in principle) conflict with this fallback, and we wish - // to generate a type error in that case. (However, this - // actually isn't true right now, because we're only using the - // builtin fallback rules. This would be true if we were using - // user-supplied fallbacks. But it's still useful to write the - // code to detect bugs.) - // - // (Note though that if we have a general type variable `?T` - // that is then unified with an integer type variable `?I` - // that ultimately never gets resolved to a special integral - // type, `?T` is not considered unsolved, but `?I` is. The - // same is true for float variables.) - let fallback = match ty.kind() { - _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e), - ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, - ty::Infer(ty::FloatVar(vid)) if fallback_to_f32.contains(vid) => self.tcx.types.f32, - ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, - _ if diverging_fallback.contains(&ty) => { - self.diverging_fallback_has_occurred.set(true); - diverging_fallback_ty - } - _ => return false, + let fallback = if let Some(e) = self.tainted_by_errors() { + Ty::new_error(self.tcx, e) + } else if let Some(fallback) = fallback() { + fallback + } else { + return false; }; - debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); - let span = ty.ty_vid().map_or(DUMMY_SP, |vid| self.infcx.type_var_origin(vid).span); + let (ty, span) = vid_to_ty_and_span(vid); self.demand_eqtype(span, ty, fallback); true } @@ -146,15 +158,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// /// foo(1.0); /// ``` - fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordSet { + fn calculate_fallback_to_f32( + &self, + unresolved_variables: &[ty::FloatVid], + ) -> UnordSet { // Short-circuit: if no unresolved variable is a float, no f32 fallback can apply, // so we can skip the (potentially very expensive) work in `from_float_for_f32_root_vids`. // Under the new solver, that function walks `visit_proof_tree` for every pending // obligation, which is O(N × proof_tree_size) and can dominate type-checking on crates // with many large pending obligations and no f32 involvement. - if unresolved_variables.iter().all(|ty| ty.float_vid().is_none()) { + if unresolved_variables.is_empty() { return UnordSet::new(); } + let roots: UnordSet = self.from_float_for_f32_root_vids(); if roots.is_empty() { // Most functions have no `f32: From<{float}>` predicates, so short-circuit and return @@ -166,10 +182,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // top of that function for details. let fallback_to_f32 = unresolved_variables .iter() - .flat_map(|ty| ty.float_vid()) - .filter(|vid| roots.contains(&self.root_float_var(*vid))) - .inspect(|vid| { - let origin = self.float_var_origin(*vid); + .copied() + .filter(|&vid| roots.contains(&self.root_float_var(vid))) + .inspect(|&vid| { + let origin = self.float_var_origin(vid); // Show the entire literal in the suggestion to make it clearer. let mut literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); // A `.` at the end of the literal is no longer necessary if `f32` is explicitly specified @@ -195,8 +211,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fn calculate_diverging_fallback( &self, - unresolved_variables: &[Ty<'tcx>], - ) -> (UnordSet>, Ty<'tcx>) { + unresolved_variables: &[ty::TyVid], + ) -> (UnordSet, Ty<'tcx>) { debug!("calculate_diverging_fallback({:?})", unresolved_variables); let diverging_fallback_ty = match self.diverging_fallback_behavior { @@ -214,7 +230,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Extract the unsolved type inference variable vids; note that some // unsolved variables are integer/float variables and are excluded. - let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid()); + let unsolved_vids = unresolved_variables.iter(); // Compute the diverging root vids D -- that is, the root vid of // those type variables that (a) are the target of a coercion from @@ -241,7 +257,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // we find later that they are *also* reachable from some // other type variable outside this set. let mut diverging_vids = vec![]; - for unsolved_vid in unsolved_vids { + for &unsolved_vid in unsolved_vids { let root_vid = self.root_var(unsolved_vid); debug!( "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", @@ -271,7 +287,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ); for &diverging_vid in &diverging_vids { - let diverging_ty = Ty::new_var(self.tcx, diverging_vid); let root_vid = self.root_var(diverging_vid); self.lint_never_type_fallback_flowing_into_unsafe_code( @@ -280,7 +295,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { root_vid, ); - diverging_fallback.insert(diverging_ty); + diverging_fallback.insert(diverging_vid); } (diverging_fallback, diverging_fallback_ty) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index aa2fe2ee21dfa..93a1efea216ac 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -752,27 +752,22 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn unresolved_variables(&self) -> Vec> { + pub fn unresolved_variables(&self) -> (Vec, Vec, Vec) { let mut inner = self.inner.borrow_mut(); - let mut vars: Vec> = inner - .type_variables() - .unresolved_variables() - .into_iter() - .map(|t| Ty::new_var(self.tcx, t)) + let ty = inner.type_variables().unresolved_variables().into_iter().collect(); + let int = (0..inner.int_unification_table().len()) + .map(|i| ty::IntVid::from_usize(i)) + .filter(|&vid| { + inner.int_unification_table().probe_value(vid).is_unknown() + }) .collect(); - vars.extend( - (0..inner.int_unification_table().len()) - .map(|i| ty::IntVid::from_usize(i)) - .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown()) - .map(|v| Ty::new_int_var(self.tcx, v)), - ); - vars.extend( - (0..inner.float_unification_table().len()) - .map(|i| ty::FloatVid::from_usize(i)) - .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown()) - .map(|v| Ty::new_float_var(self.tcx, v)), - ); - vars + let float = (0..inner.float_unification_table().len()) + .map(|i| ty::FloatVid::from_usize(i)) + .filter(|&vid| { + inner.float_unification_table().probe_value(vid).is_unknown() + }) + .collect(); + (ty, int, float) } #[instrument(skip(self), level = "debug")] From 9dcdb29d87183cf4b9abb67657f5186e046eb57e Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 9 Jul 2026 15:17:50 +0200 Subject: [PATCH 2/5] only do fallback on root vars --- compiler/rustc_hir_typeck/src/fallback.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 7 +++++-- compiler/rustc_infer/src/infer/type_variable.rs | 8 +++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ff2bd9734ced8..8488c9d0226f1 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -64,7 +64,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// /// Returns `true` if *any* kind of fallback has occurred during this call. fn fallback_types(&self) -> bool { - let (unresolved_ty, unresolved_int, unresolved_float) = self.unresolved_variables(); + let (unresolved_ty, unresolved_int, unresolved_float) = self.unresolved_root_variables(); // Check if we have any unresolved variables. If not, no need for fallback. if unresolved_ty.is_empty() && unresolved_int.is_empty() && unresolved_float.is_empty() { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 93a1efea216ac..2bc35dd3dd617 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -752,21 +752,24 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn unresolved_variables(&self) -> (Vec, Vec, Vec) { + pub fn unresolved_root_variables(&self) -> (Vec, Vec, Vec) { let mut inner = self.inner.borrow_mut(); - let ty = inner.type_variables().unresolved_variables().into_iter().collect(); + let ty = inner.type_variables().unresolved_root_variables().into_iter().collect(); let int = (0..inner.int_unification_table().len()) .map(|i| ty::IntVid::from_usize(i)) .filter(|&vid| { inner.int_unification_table().probe_value(vid).is_unknown() + && inner.int_unification_table().find(vid) == vid }) .collect(); let float = (0..inner.float_unification_table().len()) .map(|i| ty::FloatVid::from_usize(i)) .filter(|&vid| { inner.float_unification_table().probe_value(vid).is_unknown() + && inner.float_unification_table().find(vid) == vid }) .collect(); + (ty, int, float) } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 21ba07428d9ec..9fe1ae227f551 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -286,12 +286,14 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { (range.clone(), range.map(|index| self.var_origin(index)).collect()) } - /// Returns indices of all variables that are not yet - /// instantiated. - pub(crate) fn unresolved_variables(&mut self) -> Vec { + /// Returns indices of all root variables that are not yet instantiated. + pub(crate) fn unresolved_root_variables(&mut self) -> Vec { (0..self.num_vars()) .filter_map(|i| { let vid = ty::TyVid::from_usize(i); + if self.root_var(vid) != vid { + return None; + } match self.probe(vid) { TypeVariableValue::Unknown { .. } => Some(vid), TypeVariableValue::Known { .. } => None, From c1686ccbd3a2ba54e39da2afa46cd675b2dc9f41 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 9 Jul 2026 15:17:50 +0200 Subject: [PATCH 3/5] simplify never type fallback --- compiler/rustc_hir_typeck/src/fallback.rs | 79 ++++++----------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 8488c9d0226f1..f023c525db6c3 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -71,8 +71,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - let (diverging_fallback, diverging_fallback_ty) = - self.calculate_diverging_fallback(&unresolved_ty); + let (diverging_fallback, diverging_fallback_ty) = self.calculate_diverging_fallback(); let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_float); // We do fallback in two passes, to try to generate @@ -209,12 +208,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_to_f32 } - fn calculate_diverging_fallback( - &self, - unresolved_variables: &[ty::TyVid], - ) -> (UnordSet, Ty<'tcx>) { - debug!("calculate_diverging_fallback({:?})", unresolved_variables); - + fn calculate_diverging_fallback(&self) -> (UnordSet, Ty<'tcx>) { let diverging_fallback_ty = match self.diverging_fallback_behavior { DivergingFallbackBehavior::ToUnit => self.tcx.types.unit, DivergingFallbackBehavior::ToNever => self.tcx.types.never, @@ -224,21 +218,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } }; - // Construct a coercion graph where an edge `A -> B` indicates - // a type variable is that is coerced - let coercion_graph = self.create_coercion_graph(); - - // Extract the unsolved type inference variable vids; note that some - // unsolved variables are integer/float variables and are excluded. - let unsolved_vids = unresolved_variables.iter(); - // Compute the diverging root vids D -- that is, the root vid of // those type variables that (a) are the target of a coercion from // a `!` type and (b) have not yet been solved. // // These variables are the ones that are targets for fallback to // either `!` or `()`. - let diverging_roots: UnordSet = self + let diverging_root_vids: Vec = self .diverging_type_vars .borrow() .iter() @@ -246,57 +232,28 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .filter_map(|ty| ty.ty_vid()) .map(|vid| self.root_var(vid)) .collect(); - debug!( - "calculate_diverging_fallback: diverging_type_vars={:?}", - self.diverging_type_vars.borrow() - ); - debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots); - - // Find all type variables that are reachable from a diverging - // type variable. These will typically default to `!`, unless - // we find later that they are *also* reachable from some - // other type variable outside this set. - let mut diverging_vids = vec![]; - for &unsolved_vid in unsolved_vids { - let root_vid = self.root_var(unsolved_vid); - debug!( - "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", - unsolved_vid, - root_vid, - diverging_roots.contains(&root_vid), + + { + // Construct a coercion graph where an edge `A -> B` indicates + // a type variable is that is coerced + let coercion_graph = self.create_coercion_graph(); + + self.lint_obligations_broken_by_never_type_fallback_change( + &diverging_root_vids, + &coercion_graph, ); - if diverging_roots.contains(&root_vid) { - diverging_vids.push(unsolved_vid); - debug!( - "calculate_diverging_fallback: root_vid={:?} reaches {:?}", + let unsafe_infer_vars = OnceCell::new(); + for &root_vid in &diverging_root_vids { + self.lint_never_type_fallback_flowing_into_unsafe_code( + &unsafe_infer_vars, + &coercion_graph, root_vid, - graph::depth_first_search(&coercion_graph, root_vid).collect::>() ); } } - debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations()); - - let mut diverging_fallback = UnordSet::with_capacity(diverging_vids.len()); - let unsafe_infer_vars = OnceCell::new(); - - self.lint_obligations_broken_by_never_type_fallback_change( - &diverging_vids, - &coercion_graph, - ); - - for &diverging_vid in &diverging_vids { - let root_vid = self.root_var(diverging_vid); - - self.lint_never_type_fallback_flowing_into_unsafe_code( - &unsafe_infer_vars, - &coercion_graph, - root_vid, - ); - - diverging_fallback.insert(diverging_vid); - } + let diverging_fallback = diverging_root_vids.into_iter().collect::>(); (diverging_fallback, diverging_fallback_ty) } From 619c9f1158e0a0ef595c99d2d0bda8a490f140bb Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 9 Jul 2026 15:17:50 +0200 Subject: [PATCH 4/5] tiny refactor to f32 type fallback --- compiler/rustc_hir_typeck/src/fallback.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index f023c525db6c3..db47945e9dbec 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -159,14 +159,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// ``` fn calculate_fallback_to_f32( &self, - unresolved_variables: &[ty::FloatVid], + unresolved_root_variables: &[ty::FloatVid], ) -> UnordSet { // Short-circuit: if no unresolved variable is a float, no f32 fallback can apply, // so we can skip the (potentially very expensive) work in `from_float_for_f32_root_vids`. // Under the new solver, that function walks `visit_proof_tree` for every pending // obligation, which is O(N × proof_tree_size) and can dominate type-checking on crates // with many large pending obligations and no f32 involvement. - if unresolved_variables.is_empty() { + if unresolved_root_variables.is_empty() { return UnordSet::new(); } @@ -179,10 +179,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Calculate all the unresolved variables that need to fallback to `f32` here. This ensures // we don't need to find root variables in `fallback_if_possible`: see the comment at the // top of that function for details. - let fallback_to_f32 = unresolved_variables + let fallback_to_f32 = unresolved_root_variables .iter() .copied() - .filter(|&vid| roots.contains(&self.root_float_var(vid))) + .filter(|&vid| roots.contains(&vid)) .inspect(|&vid| { let origin = self.float_var_origin(vid); // Show the entire literal in the suggestion to make it clearer. From 8992f0e12d306e475cd303ab34b6a04dd3bfdf03 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 14 Jul 2026 13:39:32 +0200 Subject: [PATCH 5/5] use a helper for getting unresolved root vars --- compiler/rustc_infer/src/infer/mod.rs | 51 +++++++++++++------ .../rustc_infer/src/infer/type_variable.rs | 12 +---- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2bc35dd3dd617..5400fbe78c428 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -13,8 +13,9 @@ use region_constraints::{ pub use relate::StructurallyRelateAliases; pub use relate::combine::PredicateEmittingRelation; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; -use rustc_data_structures::unify as ut; +use rustc_data_structures::unify::{self as ut, UnifyKey, UnifyValue}; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId}; @@ -754,21 +755,18 @@ impl<'tcx> InferCtxt<'tcx> { pub fn unresolved_root_variables(&self) -> (Vec, Vec, Vec) { let mut inner = self.inner.borrow_mut(); - let ty = inner.type_variables().unresolved_root_variables().into_iter().collect(); - let int = (0..inner.int_unification_table().len()) - .map(|i| ty::IntVid::from_usize(i)) - .filter(|&vid| { - inner.int_unification_table().probe_value(vid).is_unknown() - && inner.int_unification_table().find(vid) == vid - }) - .collect(); - let float = (0..inner.float_unification_table().len()) - .map(|i| ty::FloatVid::from_usize(i)) - .filter(|&vid| { - inner.float_unification_table().probe_value(vid).is_unknown() - && inner.float_unification_table().find(vid) == vid - }) - .collect(); + + let ty = inner.type_variables().unresolved_root_variables(); + + let int = unresolved_root_variables_of( + inner.int_unification_table(), + ty::IntVarValue::is_unknown, + ); + + let float = unresolved_root_variables_of( + inner.float_unification_table(), + ty::FloatVarValue::is_unknown, + ); (ty, int, float) } @@ -1872,3 +1870,24 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> { } } } + +/// Returns unresolved root variables from `table`, according to `is_unresolved`. +fn unresolved_root_variables_of( + mut table: UnificationTable<'_, '_, V>, + is_unresolved: impl Fn(V::Value) -> bool, +) -> Vec +where + V: Eq, + V::Value: UnifyValue, + for<'a> UndoLog<'a>: From>>, +{ + (0..table.len() as u32) + .map(V::from_index) + .filter(|&vid| { + // vid is a root variable + table.find(vid) == vid + // ...and it's currently unresolved + && is_unresolved(table.try_probe_value(vid).unwrap().clone()) + }) + .collect() +} diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 9fe1ae227f551..8829e8bf4083a 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -289,16 +289,8 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Returns indices of all root variables that are not yet instantiated. pub(crate) fn unresolved_root_variables(&mut self) -> Vec { (0..self.num_vars()) - .filter_map(|i| { - let vid = ty::TyVid::from_usize(i); - if self.root_var(vid) != vid { - return None; - } - match self.probe(vid) { - TypeVariableValue::Unknown { .. } => Some(vid), - TypeVariableValue::Known { .. } => None, - } - }) + .map(ty::TyVid::from_usize) + .filter(|&vid| self.root_var(vid) == vid && self.probe(vid).is_unknown()) .collect() } }