From 42709d421e6a677ddd1cd1e1af0e9b288ddfbe5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 11 Mar 2026 16:39:03 +0000 Subject: [PATCH 1/3] When single impl can satisfy inference error, suggest type When encountering an inference error where a return type must be known, like when calling `Iterator::sum::()` without specifying `T`, look for all `T` that would satisfy `Sum`. If only one, suggest it. ``` error[E0283]: type annotations needed --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 | LL | let sum = v | ^^^ ... LL | .sum(); // `sum::` needs `T` to be specified | --- type must be known at this point | = note: cannot satisfy `_: Sum` help: the trait `Sum` is implemented for `i32` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified | LL | let sum: i32 = v | +++++ ``` --- .../error_reporting/infer/need_type_info.rs | 64 ++++++++++++++++--- .../src/error_reporting/traits/ambiguity.rs | 27 +++++++- .../traits/fulfillment_errors.rs | 10 +++ .../const-generics/issues/issue-83249.stderr | 6 +- ...annot-infer-iterator-sum-return-type.fixed | 13 ++++ .../cannot-infer-iterator-sum-return-type.rs | 13 ++++ ...nnot-infer-iterator-sum-return-type.stderr | 26 ++++++++ 7 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.rs create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 16b0e96cde0fa..26f9998b98aeb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -158,6 +158,7 @@ impl UnderspecifiedArgKind { struct ClosureEraser<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + depth: usize, } impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { @@ -172,7 +173,8 @@ impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { + self.depth += 1; + let ty = match ty.kind() { ty::Closure(_, args) => { // For a closure type, we turn it into a function pointer so that it gets rendered // as `fn(args) -> Ret`. @@ -233,9 +235,15 @@ impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { // its type parameters. ty.super_fold_with(self) } - // We don't have an unknown type parameter anywhere, replace with `_`. + // We are in the top-level type, not one of its type parameters. Name it with its + // parameters replaced. + _ if self.depth == 1 => ty.super_fold_with(self), + // We don't have an unknown type parameter anywhere, and we are in a type parameter. + // Replace with `_`. _ => self.new_infer(), - } + }; + self.depth -= 1; + ty } fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -287,7 +295,7 @@ fn ty_to_string<'tcx>( let ty = infcx.resolve_vars_if_possible(ty); // We use `fn` ptr syntax for closures, but this only works when the closure does not capture // anything. We also remove all type parameters that are fully known to the type system. - let ty = ty.fold_with(&mut ClosureEraser { infcx }); + let ty = ty.fold_with(&mut ClosureEraser { infcx, depth: 0 }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in @@ -467,6 +475,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, + ) -> Diag<'a> { + self.emit_inference_failure_err_with_type_hint( + body_def_id, + failure_span, + term, + error_code, + should_label_span, + None, + ) + } + + pub fn emit_inference_failure_err_with_type_hint( + &self, + body_def_id: LocalDefId, + failure_span: Span, + term: Term<'tcx>, + error_code: TypeAnnotationNeeded, + should_label_span: bool, + ty: Option>, ) -> Diag<'a> { let term = self.resolve_vars_if_possible(term); let arg_data = self @@ -479,7 +506,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -779,10 +806,20 @@ impl<'tcx> InferSourceKind<'tcx> { | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty), long_ty_path) - } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) - } else { + } else if ty.is_ty_or_numeric_infer() + || ty.is_primitive() + || matches!( + ty.kind(), + ty::Adt(_, args) + if args.types().count() == 0 && args.consts().count() == 0 + ) + { + // `ty` is either `_`, a primitive type like `u32` or a type with no type or + // const parameters. We will not mention the type in the main inference error + // message. ("other", String::new(), long_ty_path) + } else { + ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) } } // FIXME: We should be able to add some additional info here. @@ -815,6 +852,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, attempt: usize, infer_source_cost: usize, @@ -826,12 +864,14 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, ) -> Self { FindInferSourceVisitor { tecx, typeck_results, target, + ty, attempt: 0, infer_source_cost: usize::MAX, @@ -1213,7 +1253,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); - if let Some(ty) = self.opt_node_type(local.hir_id) { + if let Some(mut ty) = self.opt_node_type(local.hir_id) { if self.generic_arg_contains_target(ty.into()) { fn get_did( typeck_results: &TypeckResults<'_>, @@ -1241,7 +1281,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { _ => None, } } - + if let Some(t) = self.ty + && ty.has_infer() + { + ty = t; + } if let LocalSource::Normal = local.source && local.ty.is_none() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 2afc1b040353e..2a9674f8f8e2e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -245,12 +245,37 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .find(|s| s.has_non_region_infer()); let mut err = if let Some(term) = term { - self.emit_inference_failure_err( + let candidates: Vec<_> = self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|def_id| { + let imp = self.tcx.impl_trait_header(def_id); + if imp.polarity != ty::ImplPolarity::Positive + || !self.tcx.is_user_visible_dep(def_id.krate) + { + return None; + } + let imp = imp.trait_ref.skip_binder(); + if imp + .with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + { + Some(imp.self_ty()) + } else { + None + } + }) + .collect(); + self.emit_inference_failure_err_with_type_hint( obligation.cause.body_id, span, term, TypeAnnotationNeeded::E0283, true, + match &candidates[..] { + [candidate] => Some(*candidate), + _ => None, + }, ) } else { struct_span_code_err!( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d5383fd4d0831..6bf08170bcf7e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2251,6 +2251,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if candidates.is_empty() { return false; } + let mut specific_candidates = candidates.clone(); + specific_candidates.retain(|(tr, _)| { + tr.with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + }); + if !specific_candidates.is_empty() { + // We have found a subset of impls that fully satisfy the expected trait, only + // mention those types. + candidates = specific_candidates; + } if let &[(cand, def_id)] = &candidates[..] { if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id) && !self.tcx.features().enabled(sym::try_trait_v2) diff --git a/tests/ui/const-generics/issues/issue-83249.stderr b/tests/ui/const-generics/issues/issue-83249.stderr index 2d561dbd6b1cc..926eb9bd26f6b 100644 --- a/tests/ui/const-generics/issues/issue-83249.stderr +++ b/tests/ui/const-generics/issues/issue-83249.stderr @@ -17,10 +17,10 @@ note: required by a bound in `foo` | LL | fn foo(_: [u8; T::N]) -> T { | ^^^ required by this bound in `foo` -help: consider giving this pattern a type +help: consider giving this pattern a type, where the type for type parameter `T` is specified | -LL | let _: /* Type */ = foo([0; 1]); - | ++++++++++++ +LL | let _: u8 = foo([0; 1]); + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed new file mode 100644 index 0000000000000..9a494391f0e4f --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum: i32 = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs new file mode 100644 index 0000000000000..013a2f9148586 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr new file mode 100644 index 0000000000000..a5d2f98b0e646 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr @@ -0,0 +1,26 @@ +error[E0283]: type annotations needed + --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 + | +LL | let sum = v + | ^^^ +... +LL | .sum(); // `sum::` needs `T` to be specified + | --- type must be known at this point + | + = note: cannot satisfy `_: Sum` +help: the trait `Sum` is implemented for `i32` + --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + | + = note: in this macro invocation +note: required by a bound in `std::iter::Iterator::sum` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified + | +LL | let sum: i32 = v + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. From fbd3b6d9443a4da80db1c18277e4ce61d1da662b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 17 Mar 2026 13:39:29 +1100 Subject: [PATCH 2/3] Move query-stack-frame spans into `QueryStackFrame` Code that previously used `QueryStackFrame` now uses `TaggedQueryKey` directly. Code that previously used `Spanned` now uses `QueryStackFrame`, which includes a span. --- compiler/rustc_middle/src/query/plumbing.rs | 11 ++-- compiler/rustc_middle/src/query/stack.rs | 6 ++- compiler/rustc_query_impl/src/execution.rs | 6 +-- .../rustc_query_impl/src/from_cycle_error.rs | 10 ++-- compiler/rustc_query_impl/src/job.rs | 54 ++++++++++--------- 5 files changed, 46 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 17fa9bf085ebc..e8e2c8dbd2249 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,15 +8,14 @@ use rustc_data_structures::sync::{AtomicU64, WorkerLocal}; use rustc_errors::Diag; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; -use rustc_span::{Span, Spanned}; +use rustc_span::Span; pub use sealed::IntoQueryParam; use crate::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; use crate::ich::StableHashingContext; use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables, TaggedQueryKey}; use crate::query::on_disk_cache::OnDiskCache; -use crate::query::stack::QueryStackFrame; -use crate::query::{QueryCache, QueryJob}; +use crate::query::{QueryCache, QueryJob, QueryStackFrame}; use crate::ty::TyCtxt; /// For a particular query, keeps track of "active" keys, i.e. keys whose @@ -53,10 +52,10 @@ pub enum ActiveKeyStatus<'tcx> { #[derive(Debug)] pub struct CycleError<'tcx> { /// The query and related span that uses the cycle. - pub usage: Option>>, + pub usage: Option>, /// The span here corresponds to the reason for which this query was required. - pub cycle: Vec>>, + pub cycle: Vec>, } #[derive(Debug)] @@ -505,7 +504,7 @@ macro_rules! define_callbacks { /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a number. #[allow(non_camel_case_types)] - #[derive(Clone, Debug)] + #[derive(Clone, Copy, Debug)] pub enum TaggedQueryKey<'tcx> { $( $name($name::Key<'tcx>), diff --git a/compiler/rustc_middle/src/query/stack.rs b/compiler/rustc_middle/src/query/stack.rs index 6a77ddc1eaa7f..9465fc87edf09 100644 --- a/compiler/rustc_middle/src/query/stack.rs +++ b/compiler/rustc_middle/src/query/stack.rs @@ -1,10 +1,14 @@ +use rustc_span::Span; + use crate::queries::TaggedQueryKey; /// Description of a frame in the query stack. /// /// This is mostly used in case of cycles for error reporting. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct QueryStackFrame<'tcx> { + pub span: Span, + /// The query and key of the query method call that this stack frame /// corresponds to. /// diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 6c91db16967b1..aea0bb4d35342 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -9,7 +9,7 @@ use rustc_errors::FatalError; use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex}; use rustc_middle::query::{ ActiveKeyStatus, CycleError, EnsureMode, QueryCache, QueryJob, QueryJobId, QueryKey, - QueryLatch, QueryMode, QueryStackFrame, QueryState, QueryVTable, + QueryLatch, QueryMode, QueryState, QueryVTable, }; use rustc_middle::ty::TyCtxt; use rustc_middle::verify_ich::incremental_verify_ich; @@ -75,8 +75,8 @@ fn collect_active_query_jobs_inner<'tcx, C>( if let ActiveKeyStatus::Started(job) = status { // It's fine to call `create_tagged_key` with the shard locked, // because it's just a `TaggedQueryKey` variant constructor. - let frame = QueryStackFrame { tagged_key: (query.create_tagged_key)(*key) }; - job_map.insert(job.id, QueryJobInfo { frame, job: job.clone() }); + let tagged_key = (query.create_tagged_key)(*key); + job_map.insert(job.id, QueryJobInfo { tagged_key, job: job.clone() }); } } }; diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 317b2482c05cc..0c6082e65f624 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -76,7 +76,7 @@ fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle_error: CycleError<'tcx> let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for frame in &cycle_error.cycle { - if let TaggedQueryKey::check_representability(def_id) = frame.node.tagged_key + if let TaggedQueryKey::check_representability(def_id) = frame.tagged_key && tcx.def_kind(def_id) == DefKind::Field { let field_id: LocalDefId = def_id; @@ -89,7 +89,7 @@ fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle_error: CycleError<'tcx> } } for frame in &cycle_error.cycle { - if let TaggedQueryKey::check_representability_adt_ty(key) = frame.node.tagged_key + if let TaggedQueryKey::check_representability_adt_ty(key) = frame.tagged_key && let Some(adt) = key.ty_adt_def() && let Some(def_id) = adt.did().as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) @@ -134,7 +134,7 @@ fn layout_of<'tcx>( let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if let TaggedQueryKey::layout_of(key) = cycle[0].node.tagged_key + if let TaggedQueryKey::layout_of(key) = cycle[0].tagged_key && let ty::Coroutine(def_id, _) = key.value.kind() && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) @@ -159,7 +159,7 @@ fn layout_of<'tcx>( tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); for (i, frame) in cycle.iter().enumerate() { - let TaggedQueryKey::layout_of(frame_key) = frame.node.tagged_key else { + let TaggedQueryKey::layout_of(frame_key) = frame.tagged_key else { continue; }; let &ty::Coroutine(frame_def_id, _) = frame_key.value.kind() else { @@ -169,7 +169,7 @@ fn layout_of<'tcx>( continue; }; let frame_span = - frame.node.tagged_key.default_span(tcx, cycle[(i + 1) % cycle.len()].span); + frame.tagged_key.default_span(tcx, cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 9d2d6cc54d49e..29877c8aac88f 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -11,7 +11,7 @@ use rustc_middle::query::{ CycleError, QueryJob, QueryJobId, QueryLatch, QueryStackFrame, QueryWaiter, }; use rustc_middle::ty::TyCtxt; -use rustc_span::{DUMMY_SP, Span, respan}; +use rustc_span::{DUMMY_SP, Span}; use crate::{CollectActiveJobsKind, collect_active_query_jobs}; @@ -30,8 +30,8 @@ impl<'tcx> QueryJobMap<'tcx> { self.map.insert(id, info); } - fn frame_of(&self, id: QueryJobId) -> &QueryStackFrame<'tcx> { - &self.map[&id].frame + fn tagged_key_of(&self, id: QueryJobId) -> TaggedQueryKey<'tcx> { + self.map[&id].tagged_key } fn span_of(&self, id: QueryJobId) -> Span { @@ -49,7 +49,7 @@ impl<'tcx> QueryJobMap<'tcx> { #[derive(Debug)] pub(crate) struct QueryJobInfo<'tcx> { - pub(crate) frame: QueryStackFrame<'tcx>, + pub(crate) tagged_key: TaggedQueryKey<'tcx>, pub(crate) job: QueryJob<'tcx>, } @@ -65,7 +65,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( while let Some(job) = current_job { let info = &job_map.map[&job]; - cycle.push(respan(info.job.span, info.frame.clone())); + cycle.push(QueryStackFrame { span: info.job.span, tagged_key: info.tagged_key }); if job == id { cycle.reverse(); @@ -78,7 +78,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( // Find out why the cycle itself was used let usage = try { let parent = info.job.parent?; - respan(info.job.span, job_map.frame_of(parent).clone()) + QueryStackFrame { span: info.job.span, tagged_key: job_map.tagged_key_of(parent) } }; return CycleError { usage, cycle }; } @@ -100,19 +100,19 @@ pub(crate) fn find_dep_kind_root<'tcx>( ) -> (Span, String, usize) { let mut depth = 1; let mut info = &job_map.map[&id]; - // Two query stack frames are for the same query method if they have the same + // Two query jobs are for the same query method if they have the same // `TaggedQueryKey` discriminant. - let expected_query = mem::discriminant::>(&info.frame.tagged_key); + let expected_query = mem::discriminant::>(&info.tagged_key); let mut last_info = info; while let Some(id) = info.job.parent { info = &job_map.map[&id]; - if mem::discriminant(&info.frame.tagged_key) == expected_query { + if mem::discriminant(&info.tagged_key) == expected_query { depth += 1; last_info = info; } } - (last_info.job.span, last_info.frame.tagged_key.description(tcx), depth) + (last_info.job.span, last_info.tagged_key.description(tcx), depth) } /// The locaton of a resumable waiter. The usize is the index into waiters in the query's latch. @@ -316,14 +316,17 @@ fn remove_cycle<'tcx>( let usage = entry_point .query_waiting_on_cycle - .map(|(span, job)| respan(span, job_map.frame_of(job).clone())); + .map(|(span, job)| QueryStackFrame { span, tagged_key: job_map.tagged_key_of(job) }); // Create the cycle error let error = CycleError { usage, cycle: stack .iter() - .map(|&(span, job)| respan(span, job_map.frame_of(job).clone())) + .map(|&(span, job)| QueryStackFrame { + span, + tagged_key: job_map.tagged_key_of(job), + }) .collect(), }; @@ -419,12 +422,12 @@ pub fn print_query_stack<'tcx>( let Some(query_info) = job_map.map.get(&query) else { break; }; - let description = query_info.frame.tagged_key.description(tcx); + let description = query_info.tagged_key.description(tcx); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( "#{count_printed} [{query_name}] {description}", - query_name = query_info.frame.tagged_key.query_name(), + query_name = query_info.tagged_key.query_name(), )) .with_span(query_info.job.span) .emit(); @@ -435,7 +438,7 @@ pub fn print_query_stack<'tcx>( let _ = writeln!( file, "#{count_total} [{query_name}] {description}", - query_name = query_info.frame.tagged_key.query_name(), + query_name = query_info.tagged_key.query_name(), ); } @@ -457,12 +460,12 @@ pub(crate) fn report_cycle<'tcx>( ) -> Diag<'tcx> { assert!(!stack.is_empty()); - let span = stack[0].node.tagged_key.default_span(tcx, stack[1 % stack.len()].span); + let span = stack[0].tagged_key.default_span(tcx, stack[1 % stack.len()].span); let mut cycle_stack = Vec::new(); use crate::error::StackCount; - let stack_bottom = stack[0].node.tagged_key.description(tcx); + let stack_bottom = stack[0].tagged_key.description(tcx); let stack_count = if stack.len() == 1 { StackCount::Single { stack_bottom: stack_bottom.clone() } } else { @@ -470,24 +473,23 @@ pub(crate) fn report_cycle<'tcx>( }; for i in 1..stack.len() { - let node = &stack[i].node; - let span = node.tagged_key.default_span(tcx, stack[(i + 1) % stack.len()].span); - cycle_stack.push(crate::error::CycleStack { span, desc: node.tagged_key.description(tcx) }); + let frame = &stack[i]; + let span = frame.tagged_key.default_span(tcx, stack[(i + 1) % stack.len()].span); + cycle_stack + .push(crate::error::CycleStack { span, desc: frame.tagged_key.description(tcx) }); } let cycle_usage = usage.as_ref().map(|usage| crate::error::CycleUsage { - span: usage.node.tagged_key.default_span(tcx, usage.span), - usage: usage.node.tagged_key.description(tcx), + span: usage.tagged_key.default_span(tcx, usage.span), + usage: usage.tagged_key.description(tcx), }); let alias = if stack .iter() - .all(|entry| matches!(entry.node.tagged_key.def_kind(tcx), Some(DefKind::TyAlias))) + .all(|frame| frame.tagged_key.def_kind(tcx) == Some(DefKind::TyAlias)) { Some(crate::error::Alias::Ty) - } else if stack - .iter() - .all(|entry| entry.node.tagged_key.def_kind(tcx) == Some(DefKind::TraitAlias)) + } else if stack.iter().all(|frame| frame.tagged_key.def_kind(tcx) == Some(DefKind::TraitAlias)) { Some(crate::error::Alias::Trait) } else { From fb850aebcd39f7dd7973adabf380bdc5bb22a369 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 18 Mar 2026 10:36:19 +1100 Subject: [PATCH 3/3] Remove unused types `UnusedGenericParams` and `FiniteBitSet` These types have been unused since polymorphization was removed in . --- .../src/stable_hasher.rs | 9 -- compiler/rustc_index/src/bit_set.rs | 113 +----------------- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_metadata/src/rmeta/table.rs | 10 -- compiler/rustc_middle/src/query/erase.rs | 2 - compiler/rustc_middle/src/ty/instance.rs | 50 +------- compiler/rustc_middle/src/ty/mod.rs | 2 +- 7 files changed, 4 insertions(+), 184 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 9fb4d4352c2f6..f8e72d66d07ef 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -555,15 +555,6 @@ impl HashStable for bit_set::BitMatrix { } } -impl HashStable for bit_set::FiniteBitSet -where - T: HashStable + bit_set::FiniteBitSetTy, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.0.hash_stable(hcx, hasher); - } -} - impl_stable_traits_for_trivial_type!(::std::ffi::OsStr); impl_stable_traits_for_trivial_type!(::std::path::Path); diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 68384c73ba25a..f833acf6824e4 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; #[cfg(not(feature = "nightly"))] use std::mem; -use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Bound, Not, Range, RangeBounds, Shl}; +use std::ops::{Bound, Range, RangeBounds}; use std::rc::Rc; use std::{fmt, iter, slice}; @@ -1736,114 +1736,3 @@ fn max_bit(word: Word) -> usize { fn count_ones(words: &[Word]) -> usize { words.iter().map(|word| word.count_ones() as usize).sum() } - -/// Integral type used to represent the bit set. -pub trait FiniteBitSetTy: - BitAnd - + BitAndAssign - + BitOrAssign - + Clone - + Copy - + Shl - + Not - + PartialEq - + Sized -{ - /// Size of the domain representable by this type, e.g. 64 for `u64`. - const DOMAIN_SIZE: u32; - - /// Value which represents the `FiniteBitSet` having every bit set. - const FILLED: Self; - /// Value which represents the `FiniteBitSet` having no bits set. - const EMPTY: Self; - - /// Value for one as the integral type. - const ONE: Self; - /// Value for zero as the integral type. - const ZERO: Self; - - /// Perform a checked left shift on the integral type. - fn checked_shl(self, rhs: u32) -> Option; - /// Perform a checked right shift on the integral type. - fn checked_shr(self, rhs: u32) -> Option; -} - -impl FiniteBitSetTy for u32 { - const DOMAIN_SIZE: u32 = 32; - - const FILLED: Self = Self::MAX; - const EMPTY: Self = Self::MIN; - - const ONE: Self = 1u32; - const ZERO: Self = 0u32; - - fn checked_shl(self, rhs: u32) -> Option { - self.checked_shl(rhs) - } - - fn checked_shr(self, rhs: u32) -> Option { - self.checked_shr(rhs) - } -} - -impl std::fmt::Debug for FiniteBitSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:032b}", self.0) - } -} - -/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range -/// representable by `T` are considered set. -#[cfg_attr(feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext))] -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct FiniteBitSet(pub T); - -impl FiniteBitSet { - /// Creates a new, empty bitset. - pub fn new_empty() -> Self { - Self(T::EMPTY) - } - - /// Sets the `index`th bit. - pub fn set(&mut self, index: u32) { - self.0 |= T::ONE.checked_shl(index).unwrap_or(T::ZERO); - } - - /// Unsets the `index`th bit. - pub fn clear(&mut self, index: u32) { - self.0 &= !T::ONE.checked_shl(index).unwrap_or(T::ZERO); - } - - /// Sets the `i`th to `j`th bits. - pub fn set_range(&mut self, range: Range) { - let bits = T::FILLED - .checked_shl(range.end - range.start) - .unwrap_or(T::ZERO) - .not() - .checked_shl(range.start) - .unwrap_or(T::ZERO); - self.0 |= bits; - } - - /// Is the set empty? - pub fn is_empty(&self) -> bool { - self.0 == T::EMPTY - } - - /// Returns the domain size of the bitset. - pub fn within_domain(&self, index: u32) -> bool { - index < T::DOMAIN_SIZE - } - - /// Returns if the `index`th bit is set. - pub fn contains(&self, index: u32) -> Option { - self.within_domain(index) - .then(|| ((self.0.checked_shr(index).unwrap_or(T::ONE)) & T::ONE) == T::ONE) - } -} - -impl Default for FiniteBitSet { - fn default() -> Self { - Self::new_empty() - } -} diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 80d7ae4e9cb38..9dee913e8389c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -33,7 +33,7 @@ use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir; use rustc_middle::mir::ConstValue; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, Ty, TyCtxt, UnusedGenericParams}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::util::Providers; use rustc_serialize::opaque::FileEncoder; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 5e256438ad953..3b0570dd373a8 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -44,16 +44,6 @@ impl IsDefault for LazyArray { } } -impl IsDefault for UnusedGenericParams { - fn is_default(&self) -> bool { - // UnusedGenericParams encodes the *un*usedness as a bitset. - // This means that 0 corresponds to all bits used, which is indeed the default. - let is_default = self.bits() == 0; - debug_assert_eq!(is_default, self.all_used()); - is_default - } -} - /// Helper trait, for encoding to, and decoding from, a fixed number of bytes. /// Used mainly for Lazy positions and lengths. /// diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index d15e0b2a122b9..036aa2ed05a4a 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -356,7 +356,6 @@ impl_erasable_for_simple_types! { rustc_hir::OwnerId, rustc_hir::Stability, rustc_hir::Upvar, - rustc_index::bit_set::FiniteBitSet, rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs, rustc_middle::middle::dependency_format::Linkage, rustc_middle::middle::exported_symbols::SymbolExportInfo, @@ -383,7 +382,6 @@ impl_erasable_for_simple_types! { rustc_middle::ty::Destructor, rustc_middle::ty::fast_reject::SimplifiedType, rustc_middle::ty::ImplPolarity, - rustc_middle::ty::UnusedGenericParams, rustc_middle::ty::util::AlwaysRequiresDrop, rustc_middle::ty::Visibility, rustc_middle::middle::codegen_fn_attrs::SanitizerFnAttrs, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4cea8b62f0b62..9759e376c0586 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -6,8 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; -use rustc_index::bit_set::FiniteBitSet; -use rustc_macros::{Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable}; +use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; @@ -941,50 +940,3 @@ fn needs_fn_once_adapter_shim( (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), } } - -// Set bits represent unused generic parameters. -// An empty set indicates that all parameters are used. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Decodable, Encodable, HashStable)] -pub struct UnusedGenericParams(FiniteBitSet); - -impl Default for UnusedGenericParams { - fn default() -> Self { - UnusedGenericParams::new_all_used() - } -} - -impl UnusedGenericParams { - pub fn new_all_unused(amount: u32) -> Self { - let mut bitset = FiniteBitSet::new_empty(); - bitset.set_range(0..amount); - Self(bitset) - } - - pub fn new_all_used() -> Self { - Self(FiniteBitSet::new_empty()) - } - - pub fn mark_used(&mut self, idx: u32) { - self.0.clear(idx); - } - - pub fn is_unused(&self, idx: u32) -> bool { - self.0.contains(idx).unwrap_or(false) - } - - pub fn is_used(&self, idx: u32) -> bool { - !self.is_unused(idx) - } - - pub fn all_used(&self) -> bool { - self.0.is_empty() - } - - pub fn bits(&self) -> u32 { - self.0.0 - } - - pub fn from_bits(bits: u32) -> UnusedGenericParams { - UnusedGenericParams(FiniteBitSet(bits)) - } -} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 01c351de93198..2b02d42890aa9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -85,7 +85,7 @@ pub use self::context::{ CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, }; pub use self::fold::*; -pub use self::instance::{Instance, InstanceKind, ReifyReason, UnusedGenericParams}; +pub use self::instance::{Instance, InstanceKind, ReifyReason}; pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::opaque_types::OpaqueTypeKey; pub use self::pattern::{Pattern, PatternKind};