From 012c35624ed80e409f2bbb301e2824d547f01ef1 Mon Sep 17 00:00:00 2001 From: Makro Date: Wed, 29 Jul 2026 09:00:38 +0000 Subject: [PATCH 01/13] Select cache values to verify by key fingerprint, not value fingerprint --- compiler/rustc_middle/src/dep_graph/graph.rs | 9 +++++++ compiler/rustc_query_impl/src/execution.rs | 26 +++++++++++++------- compiler/rustc_query_impl/src/plumbing.rs | 3 +-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 7892404badef3..b59fc263eec9a 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -705,6 +705,15 @@ impl DepGraphData { self.previous.value_fingerprint_for_index(prev_index) } + /// The number of incremental sessions in this graph's lineage, from + /// [`SerializedDepGraph::session_count`]. Advances by one per successful + /// session; a failed session does not commit a graph, so a re-run sees + /// the same count. + #[inline] + pub fn session_count(&self) -> u64 { + self.previous.session_count() + } + #[inline] pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> &DepNode { self.previous.index_to_node(prev_index) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index a9192d0417712..a1d68fc0dc7ab 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -1,7 +1,7 @@ use std::hash::Hash; use std::mem::ManuallyDrop; -use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::{DynSend, DynSync}; @@ -490,12 +490,21 @@ fn execute_job_incr<'tcx, C: QueryCache>( /// specified, re-hash results from the cache and make sure that they have the /// expected fingerprint. /// -/// If not, we still seek to verify a subset of fingerprints loaded from disk. -/// Re-hashing results is fairly expensive, so we can't currently afford to -/// verify every hash. This subset should still give us some coverage of -/// potential bugs. -pub(crate) fn should_verify_loaded_value(tcx: TyCtxt<'_>, prev_fingerprint: Fingerprint) -> bool { - prev_fingerprint.split().1.as_u64().is_multiple_of(32) +/// If not, we still verify a subset: re-hashing is too expensive to do for +/// every value. The subset rotates with the session count, covering the whole +/// cache every 32 sessions, and is deterministic so that a verification +/// failure reproduces on retry. +/// +/// `to_smaller_hash` mixes both fingerprint halves because neither half is +/// evenly distributed on its own (`DefPathHash` keys share the +/// `StableCrateId`, `HirId` keys contain a sequential id). +pub(crate) fn should_verify_loaded_value( + tcx: TyCtxt<'_>, + dep_graph_data: &DepGraphData, + key_fingerprint: PackedFingerprint, +) -> bool { + let hash = Fingerprint::from(key_fingerprint).to_smaller_hash().as_u64(); + hash % 32 == dep_graph_data.session_count() % 32 || tcx.sess.opts.unstable_opts.incremental_verify_ich } @@ -532,8 +541,7 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( dep_graph_data.mark_debug_loaded_from_disk(*dep_node) } - let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); - let verify = should_verify_loaded_value(tcx, prev_fingerprint); + let verify = should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint); (value, verify) } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index c53293447040b..83badcb269af6 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -179,8 +179,7 @@ pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( // Verify the fingerprints of the same subset of loaded values as // `load_from_disk_or_invoke_provider_green` does. - let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); - if should_verify_loaded_value(tcx, prev_fingerprint) { + if should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint) { incremental_verify_ich( tcx, dep_graph_data, From 833ec34ae8f7b582ea4f9202fd19b01943f89cbe Mon Sep 17 00:00:00 2001 From: jyn Date: Thu, 18 Jun 2026 10:36:08 +0200 Subject: [PATCH 02/13] [blocked] Link to proposed LLM policy in CONTRIBUTING and pull request template --- .github/pull_request_template.md | 11 +++++++++++ CONTRIBUTING.md | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 93388ddd24075..872c8a0ade1ab 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,16 @@ + +- [ ] I did not use an LLM to create a change in this PR. +- [ ] I used an LLM to create a change in this PR, and I have explained below how it was used. + $DIR/macro-determinacy-non-module-issue-160195.rs:12:22 + | +LL | include!(concat!(env!())); + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr new file mode 100644 index 0000000000000..cf8c7221c2367 --- /dev/null +++ b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr @@ -0,0 +1,8 @@ +error: `env!()` takes 1 or 2 arguments + --> $DIR/macro-determinacy-non-module-issue-160195.rs:12:22 + | +LL | include!(concat!(env!())); + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs new file mode 100644 index 0000000000000..ca08a665e907b --- /dev/null +++ b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs @@ -0,0 +1,21 @@ +//@ revisions: env_first env_second + +#[cfg(env_first)] +pub mod env { + #[derive(Default)] + pub struct BusinessData; +} + +pub mod interface { + use crate::env::{self}; + + include!(concat!(env!())); //~ ERROR `env!()` takes 1 or 2 arguments +} + +#[cfg(env_second)] +pub mod env { + #[derive(Default)] + pub struct BusinessData; +} + +fn main() {} From e0830fa2bec1da2e1ec8ba8c3d9eb4be332e8136 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 4 Aug 2026 16:01:41 +0200 Subject: [PATCH 07/13] implement unsafe speculative flag to be used by `CmRefCell::borrow`, which does tracked and untracked borrowing --- compiler/rustc_resolve/src/check_unused.rs | 2 +- .../rustc_resolve/src/diagnostics/impls.rs | 4 +- .../src/effective_visibilities.rs | 4 +- compiler/rustc_resolve/src/ident.rs | 25 +++--- compiler/rustc_resolve/src/imports.rs | 18 ++-- .../rustc_resolve/src/late/diagnostics.rs | 10 ++- compiler/rustc_resolve/src/lib.rs | 86 +++++++++++++++---- compiler/rustc_resolve/src/macros.rs | 2 +- 8 files changed, 107 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 41573749abbe7..dcbda2f96323e 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -559,7 +559,7 @@ impl Resolver<'_, '_> { let mut check_redundant_imports = FxIndexSet::default(); for module in &self.local_modules { for (_key, resolution) in self.resolutions(module.to_module()).iter() { - if let Some(decl) = resolution.borrow().best_decl() + if let Some(decl) = resolution.borrow(self).best_decl() && let DeclKind::Import { import, .. } = decl.kind && let ImportKind::Single { id, .. } = import.kind { diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index cc2c72ad59906..4e451665398e9 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1873,7 +1873,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.resolutions(parent_scope.module).iter().any(|(key, name_resolution)| { if key.ns == TypeNS && key.ident == *ident - && let Some(decl) = name_resolution.borrow().best_decl() + && let Some(decl) = name_resolution.borrow(self).best_decl() { match decl.res() { // No disambiguation needed if the identically named item we @@ -3603,7 +3603,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut res = false; let m = r.expect_module(parent_module); if m.is_local() { - for importer in m.glob_importers.borrow().iter() { + for importer in m.glob_importers.borrow(r).iter() { if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { if next_parent_module == module diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index ff976b080d40d..840a8a8682538 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -126,7 +126,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { let module = self.r.expect_module(module_id.to_def_id()); for (_, name_resolution) in self.r.resolutions(module).iter() { - let Some(decl) = name_resolution.borrow().best_decl() else { + let Some(decl) = name_resolution.borrow(self.r).best_decl() else { continue; }; self.update_decl_chain(decl, ParentId::Def(module_id)); @@ -310,7 +310,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { if self.macro_reachable.insert((module_def_id, defining_mod)) { let module = self.r.expect_module(module_def_id.to_def_id()); for (_, name_resolution) in self.r.resolutions(module).iter() { - let Some(decl) = name_resolution.borrow().best_decl() else { + let Some(decl) = name_resolution.borrow(self.r).best_decl() else { continue; }; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 3f34af1d01d83..42fc5964f5aa1 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -714,7 +714,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { Some(decl) => Ok(decl), - None => Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())), + None => { + Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations(&self))) + } }, Scope::BuiltinAttrs => match self.builtin_attr_decls.get(&ident.name) { Some(decl) => Ok(*decl), @@ -727,9 +729,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.is_some(), ) { Some(decl) => Ok(decl), - None => { - Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())) - } + None => Err(Determinacy::determined( + !self.graph_root.has_unexpanded_invocations(&self), + )), } } Scope::ExternPreludeFlags => { @@ -1158,7 +1160,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { // finalize implies that the module is fully expanded - assert!(!module.has_unexpanded_invocations()); + assert!(!module.has_unexpanded_invocations(&self)); return self.get_mut().finalize_module_binding( ident, orig_ident_span, @@ -1195,7 +1197,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Check if one of unexpanded macros can still define the name. - if module.has_unexpanded_invocations() { + if module.has_unexpanded_invocations(&self) { return Err(ControlFlow::Continue(Undetermined)); } @@ -1224,7 +1226,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { // finalize implies that the module is fully expanded - assert!(!module.has_unexpanded_invocations()); + assert!(!module.has_unexpanded_invocations(&self)); return self.get_mut().finalize_module_binding( ident, orig_ident_span, @@ -1268,7 +1270,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted // shadowing is enabled, see `macro_expanded_macro_export_errors`). if let Some(binding) = binding { - return if binding.determined() || ns == MacroNS || shadowing == Shadowing::Restricted { + return if binding.determined(&self) + || ns == MacroNS + || shadowing == Shadowing::Restricted + { let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) } } else { @@ -1283,13 +1288,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // scopes we return `Undetermined` with `ControlFlow::Continue`. // Check if one of unexpanded macros can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - if module.has_unexpanded_invocations() { + if module.has_unexpanded_invocations(&self) { return Err(ControlFlow::Continue(Undetermined)); } // Check if one of glob imports can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - for glob_import in module.globs.borrow().iter() { + for glob_import in module.globs.borrow(&self).iter() { if ignore_import == Some(*glob_import) { continue; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6e2ea9abf2de8..499f9ea297362 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -781,14 +781,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut imports_to_resolve = mem::take(&mut self.indeterminate_imports); - self.assert_speculative = true; + // SAFETY: This is a "top-level" function used by the macro expansion code, unless some + // weird thing is done, all `tracked` borrows done in the previous call of + // `resolve_imports` are dropped when that call ended. + unsafe { self.speculative_flag.set(true) }; rustc_data_structures::sync::par_for_each_slice( &mut imports_to_resolve, |(import, resolution, indeterminate_count)| { (*resolution, *indeterminate_count) = self.resolve_import(*import); }, ); - self.assert_speculative = false; + // SAFETY: All `untracked` borrows are dropped after the `par_for_each_slice` call, + // as they cannot escape since they are tied to the `CmRefCell` they borrowed from. + // + // Note: Some `CmRefCell`s are arena allocated and thus have the `'ra` lifetime, + // allowing these borrows to escape, but that does not and should not happen. + unsafe { self.speculative_flag.set(false) }; self.write_import_resolutions(&imports_to_resolve); @@ -1003,7 +1011,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet>) { for module in &self.local_modules { for (key, resolution) in self.resolutions(module.to_module()).iter() { - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self); let Some(binding) = resolution.best_decl() else { continue }; // Report "cannot reexport" errors for exotic cases involving macros 2.0 @@ -1490,7 +1498,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return None; } // `use _` is never valid - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self); if let Some(name_binding) = resolution.best_decl() { match name_binding.kind { DeclKind::Import { source_decl, .. } => { @@ -1800,7 +1808,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .resolutions(module) .iter() .filter_map(|(key, resolution)| { - let res = resolution.borrow(); + let res = resolution.borrow(self); let decl = res.determined_decl()?; let mut key = *key; let scope = match key.ident.ctxt.update_unchecked(|ctxt| { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6350f79ed007f..b126272583692 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -194,7 +194,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if key.ident.name != assoc_name { return None; } - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self.r); let binding = resolution.best_decl()?; match binding.res() { Res::Def(DefKind::AssocTy, def_id) => Some(def_id), @@ -1165,7 +1165,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| { for resolution in r.resolutions(m).values() { let Some(did) = - resolution.borrow().best_decl().and_then(|binding| binding.res().opt_def_id()) + resolution.borrow(r).best_decl().and_then(|binding| binding.res().opt_def_id()) else { continue; }; @@ -1905,7 +1905,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .resolutions(module) .iter() .filter_map(|(key, resolution)| { - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self.r); resolution.best_decl().map(|binding| binding.res()).and_then(|res| { if filter_fn(res) { Some((key.ident.name, resolution.orig_ident_span, res)) @@ -2766,7 +2766,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .resolutions(*module) .iter() - .filter_map(|(key, res)| res.borrow().best_decl().map(|binding| (key, binding.res()))) + .filter_map(|(key, res)| { + res.borrow(self.r).best_decl().map(|binding| (key, binding.res())) + }) .filter(|(_, res)| match (kind, res) { (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst { .. }, _)) => true, (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a3c804e56ee22..b7e57ad8ec37e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -21,7 +21,7 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -use std::cell::{Ref, RefMut}; +use std::cell::RefMut; use std::collections::BTreeSet; use std::ops::ControlFlow; use std::sync::{Arc, OnceLock}; @@ -81,6 +81,7 @@ use crate::diagnostics::impls::{ ImportSuggestion, LabelSuggestion, OnUnknownData, StructCtor, Suggestion, }; use crate::imports::{ImportResolution, NameResolutionRef}; +use crate::ref_mut::speculative::SpeculativeFlag; use crate::ref_mut::{CmCell, CmRef, CmRefCell}; mod build_reduced_graph; @@ -767,8 +768,8 @@ impl<'ra> ModuleData<'ra> { self.kind.is_local() } - fn has_unexpanded_invocations(&self) -> bool { - !self.unexpanded_invocations.borrow().is_empty() + fn has_unexpanded_invocations<'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> bool { + !self.unexpanded_invocations.borrow(r).is_empty() } fn res(&self) -> Option { @@ -793,7 +794,7 @@ impl<'ra> Module<'ra> { mut f: impl FnMut(&R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_ref().resolutions(self).iter() { - let name_resolution = name_resolution.borrow(); + let name_resolution = name_resolution.borrow(resolver.as_ref()); if let Some(decl) = name_resolution.best_decl() { f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } @@ -806,7 +807,7 @@ impl<'ra> Module<'ra> { mut f: impl FnMut(&mut R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_mut().resolutions(self).iter() { - let name_resolution = name_resolution.borrow(); + let name_resolution = name_resolution.borrow(resolver.as_mut()); if let Some(decl) = name_resolution.best_decl() { f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } @@ -1252,10 +1253,11 @@ impl<'ra> DeclData<'ra> { /// the declaration may not be as "determined" as we think. /// FIXME: relationship between this function and similar `NameResolution::determined_decl` /// is unclear. - fn determined(&self) -> bool { + fn determined<'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> bool { match &self.kind { DeclKind::Import { source_decl, import, .. } if import.is_glob() => { - !import.parent_scope.module.has_unexpanded_invocations() && source_decl.determined() + !import.parent_scope.module.has_unexpanded_invocations(r) + && source_decl.determined(r) } _ => true, } @@ -1336,7 +1338,7 @@ pub struct Resolver<'ra, 'tcx> { graph_root: LocalModule<'ra>, /// Assert that we are in speculative resolution mode (unsafe field). - assert_speculative: bool, + speculative_flag: SpeculativeFlag, prelude: Option> = None, extern_prelude: FxIndexMap>, @@ -1810,7 +1812,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // The outermost module has def ID 0; this is not reflected in the // AST. graph_root, - assert_speculative: false, // Only set/cleared in Resolver::resolve_imports for now + // Only set/cleared in Resolver::resolve_imports for now + speculative_flag: SpeculativeFlag::default(), extern_prelude, empty_module, @@ -2011,7 +2014,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Returns a conditionally mutable resolver that can be mutated. /// Will panic if the `assert_speculative` field is true. fn cm_mut(&mut self) -> CmResolver<'_, 'ra, 'tcx> { - assert!(!self.assert_speculative, "can't mutably borrow speculative resolver"); + assert!( + !self.speculative_flag.is_speculative(), + "can't mutably borrow speculative resolver" + ); CmResolver::Mut(self) } @@ -2127,7 +2133,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { found_traits: &mut Vec>, ) { module.ensure_traits(self); - let traits = module.traits.borrow(); + let traits = module.traits.borrow(self); for &(trait_name, trait_binding, trait_module, lint_ambiguous) in traits.as_ref().unwrap().iter() { @@ -2178,7 +2184,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolutions(&self, module: Module<'ra>) -> CmRef<'ra, ResolutionTable<'ra>> { match &module.0.0.lazy_resolutions { - Resolutions::Local(local_res) => CmRef::Tracked(local_res.borrow()), + Resolutions::Local(local_res) => local_res.borrow(self), Resolutions::Extern(extern_res) => { // It is fine to return a `CmRef::Untracked`, we never give out a `&mut` // to an external table. @@ -2206,8 +2212,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &self, module: Module<'ra>, key: BindingKey, - ) -> Option>> { - self.resolutions(module).get(&key).map(|resolution| resolution.0.borrow()) + ) -> Option>> { + self.resolutions(module).get(&key).map(|resolution| resolution.0.borrow(self)) } #[track_caller] @@ -2917,7 +2923,7 @@ mod ref_mut { } pub(crate) fn set<'ra, 'tcx>(&self, val: T, r: &Resolver<'ra, 'tcx>) { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutate a `CmCell` during speculative resolution") } self.0.set(val); @@ -2946,6 +2952,27 @@ mod ref_mut { } } + pub(crate) mod speculative { + #[derive(Debug, Clone, Copy, Default)] + pub(crate) struct SpeculativeFlag(bool); + + impl SpeculativeFlag { + /// # SAFETY + /// + /// All borrows created by `CmRefCell::borrow` must be dropped before changing + /// the speculative flag: + /// - `tracked` borrows before setting it to `true`. + /// - `untracked` borrows before setting it to `false`. + pub(crate) unsafe fn set(&mut self, value: bool) { + self.0 = value; + } + + pub(crate) fn is_speculative(&self) -> bool { + self.0 + } + } + } + /// A wrapper around a [`RefCell`] that only allows writes (mutable borrows) based on a condition in the resolver. #[derive(Default)] pub(crate) struct CmRefCell(RefCell); @@ -2965,21 +2992,42 @@ mod ref_mut { &self, r: &Resolver<'ra, 'tcx>, ) -> Result, BorrowMutError> { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutably borrow a `CmRefCell` during speculative resolution"); } self.0.try_borrow_mut() } #[track_caller] - pub(crate) fn borrow(&self) -> Ref<'_, T> { - self.0.borrow() + pub(crate) fn borrow<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> CmRef<'_, T> { + if r.speculative_flag.is_speculative() { + // `try_borrow_unguarded` is unsafe because it returns a `&T` instead + // of `Ref<'_, T>`. It does provides an extra check to make sure no live + // `RefMut`s are still alive, but the other way can not be checked, so: + // + // SAFETY: This is only safe because we know that every `Untracked` borrow + // is only created during the import resolutions phase: + // + // ```rust + // // tracked borrows + // unsafe { resolver.speculative_flag.set_true() }; + // import_resolution(); // untracked borrows + // unsafe { resolver.speculative_flag.set_true() }; + // // tracked borrows + // ``` + // + // `speculative::Flag` requires all of the borrows that happened during a + // particular phase are dropped before being set to true/false. + CmRef::Untracked(unsafe { self.0.try_borrow_unguarded().unwrap() }) + } else { + CmRef::Tracked(self.0.borrow()) + } } } impl CmRefCell { pub(crate) fn take<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> T { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutate a CmRefCell during speculative resolution"); } self.0.take() diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 1e9d60ca21551..6921d0ed595fe 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -562,7 +562,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); - if target_trait.has_unexpanded_invocations() { + if target_trait.has_unexpanded_invocations(self) { return Err(Indeterminate); } // FIXME: Instead of waiting try generating all trait methods, and pruning From 180c6379b8ed8b8ff5d9545c716a17d2225c915c Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:21:15 +0200 Subject: [PATCH 08/13] Remove rustc_middle dependency on rustc_hir_pretty There is a `impl PpAnn for TyCtxt` that is unneeded. None of the big crates (middle, trait_selection) actually do any hir pretty printing so it can be removed and can either be implemented for local structs elsewhere or done by casting to `&dyn PpAnn` instead. --- Cargo.lock | 2 +- compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_driver_impl/src/pretty.rs | 10 +- compiler/rustc_hir_typeck/src/_match.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 5 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 10 + .../src/fn_ctxt/suggestions.rs | 4 +- compiler/rustc_hir_typeck/src/lib.rs | 244 +++++++++--------- compiler/rustc_hir_typeck/src/pat.rs | 17 +- compiler/rustc_middle/Cargo.toml | 1 - compiler/rustc_middle/src/hir/map.rs | 7 - .../rustc_public_bridge/src/context/impls.rs | 14 +- src/librustdoc/json/conversions.rs | 8 +- .../src/matches/match_wild_err_arm.rs | 3 +- .../src/unnecessary_mut_passed.rs | 5 +- 16 files changed, 180 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76b17e02c2359..2190fa22b77ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3954,6 +3954,7 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", + "rustc_hir", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_index", @@ -4414,7 +4415,6 @@ dependencies = [ "rustc_graphviz", "rustc_hashes", "rustc_hir", - "rustc_hir_pretty", "rustc_index", "rustc_lint_defs", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c7d3e4fae3fc5..4871c7eb9e8b0 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -16,6 +16,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 3a0a6687dd812..4bf1a3d875866 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -7,7 +7,9 @@ use std::io; use rustc_ast as ast; use rustc_ast_pretty::pprust as pprust_ast; +use rustc_hir::intravisit; use rustc_hir_pretty as pprust_hir; +use rustc_hir_pretty::PpAnn; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; @@ -71,7 +73,8 @@ struct HirIdentifiedAnn<'tcx> { impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - self.tcx.nested(state, nested) + let this = &self.tcx as &dyn intravisit::HirTyCtxt<'_>; + this.nested(state, nested) } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { @@ -149,11 +152,12 @@ struct HirTypedAnn<'tcx> { impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + let this = &self.tcx as &dyn intravisit::HirTyCtxt<'_>; let old_maybe_typeck_results = self.maybe_typeck_results.get(); if let pprust_hir::Nested::Body(id) = nested { self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id))); } - self.tcx.nested(state, nested); + this.nested(state, nested); self.maybe_typeck_results.set(old_maybe_typeck_results); } @@ -281,7 +285,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ) }; match s { - PpHirMode::Normal => f(&tcx), + PpHirMode::Normal => f(&(&tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn), PpHirMode::Identified => { let annotation = HirIdentifiedAnn { tcx }; f(&annotation) diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index ebf9907e64e64..a1ff036574fdc 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.get_fn_decl(hir_id).map(|(_, fn_decl)| { let (ty, span) = match fn_decl.output { hir::FnRetTy::DefaultReturn(span) => ("()".to_string(), span), - hir::FnRetTy::Return(ty) => (ty_to_string(&self.tcx, ty), ty.span), + hir::FnRetTy::Return(ty) => (ty_to_string(self, ty), ty.span), }; (span, format!("expected `{ty}` because of this return type")) }); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 288a1903bf675..3074a5900773d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -903,7 +903,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); unit_variant = - Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(&self.tcx, qpath))); + Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(self, qpath))); } let callee_ty = self.resolve_vars_if_possible(callee_ty); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f89d67eced3fb..12e7f82cadd43 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -50,7 +50,7 @@ use crate::diagnostics::{ use crate::op::contains_let_in_chain; use crate::{ BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, GatherLocalsVisitor, Needs, - TupleArgumentsFlag, cast, fatally_break_rust, report_unexpected_variant_res, type_error_struct, + TupleArgumentsFlag, cast, fatally_break_rust, type_error_struct, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -589,8 +589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, Some(expr), &[], diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 287e3857087e7..7a5eeccb98260 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -220,6 +220,16 @@ impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { } } +impl<'tcx> rustc_hir_pretty::PpAnn for FnCtxt<'_, 'tcx> { + fn nested(&self, state: &mut rustc_hir_pretty::State<'_>, nested: rustc_hir_pretty::Nested) { + rustc_hir_pretty::PpAnn::nested( + &(&self.tcx as &dyn rustc_hir::intravisit::HirTyCtxt<'_>), + state, + nested, + ) + } +} + impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b28eb8ad940d9..fc99dd67289bb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -723,12 +723,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::FnDecl { inputs, output, .. } = fn_ptr_ty.decl; let inputs_str = - inputs.iter().map(|ty| rustc_hir_pretty::ty_to_string(&self.tcx, ty)).join(", "); + inputs.iter().map(|ty| rustc_hir_pretty::ty_to_string(self, ty)).join(", "); let output_str = match output { hir::FnRetTy::DefaultReturn(_) => String::new(), hir::FnRetTy::Return(ty) => { - format!(" -> {}", rustc_hir_pretty::ty_to_string(&self.tcx, ty)) + format!(" -> {}", rustc_hir_pretty::ty_to_string(self, ty)) } }; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c67b8f7cdaf5c..d20d8375fc228 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -478,134 +478,138 @@ impl<'tcx> EnclosingBreakables<'tcx> { } } } - -fn report_unexpected_variant_res( - tcx: TyCtxt<'_>, - res: Res, - expr: Option<&hir::Expr<'_>>, - sub_pats: &[hir::Pat<'_>], - qpath: &hir::QPath<'_>, - span: Span, - err_code: ErrCode, - expected: &str, -) -> ErrorGuaranteed { - let res_descr = match res { - Res::Def(DefKind::Variant, _) => "struct variant", - _ => res.descr(), - }; - let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); - let mut err = tcx - .dcx() - .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) - .with_code(err_code); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == E0164 => { - let patterns_url = "https://doc.rust-lang.org/book/ch19-00-patterns.html"; - err.with_span_label(span, "`fn` calls are not allowed in patterns") - .with_help(format!("for more information, visit {patterns_url}")) - } - Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { - err.span_label(span, format!("not a {expected}")); - let variant = tcx.expect_variant_res(res); - let sugg = if variant.fields.is_empty() { - " {}".to_string() - } else { - format!( - " {{ {} }}", - variant - .fields - .iter() - .map(|f| format!("{}: /* value */", f.name)) - .collect::>() - .join(", ") - ) - }; - let descr = "you might have meant to create a new value of the struct"; - let mut suggestion = vec![]; - match tcx.parent_hir_node(expr.hir_id) { - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(..), - span: call_span, - .. - }) => { - suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); - } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { - suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); - if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id) - && let hir::ExprKind::If(condition, block, None) = parent.kind - && condition.hir_id == *hir_id - && let hir::ExprKind::Block(block, _) = block.kind - && block.stmts.is_empty() - && let Some(expr) = block.expr - && let hir::ExprKind::Path(..) = expr.kind - { - // Special case: you can incorrectly write an equality condition: - // if foo == Struct { field } { /* if body */ } - // which should have been written - // if foo == (Struct { field }) { /* if body */ } - suggestion.push((block.span.shrink_to_hi(), ")".to_string())); - } else { - suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn report_unexpected_variant_res( + &self, + res: Res, + expr: Option<&hir::Expr<'_>>, + sub_pats: &[hir::Pat<'_>], + qpath: &hir::QPath<'_>, + span: Span, + err_code: ErrCode, + expected: &str, + ) -> ErrorGuaranteed { + let tcx = self.tcx; + let res_descr = match res { + Res::Def(DefKind::Variant, _) => "struct variant", + _ => res.descr(), + }; + let path_str = rustc_hir_pretty::qpath_to_string(self, qpath); + let mut err = tcx + .dcx() + .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) + .with_code(err_code); + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == E0164 => { + let patterns_url = "https://doc.rust-lang.org/book/ch19-00-patterns.html"; + err.with_span_label(span, "`fn` calls are not allowed in patterns") + .with_help(format!("for more information, visit {patterns_url}")) + } + Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { + err.span_label(span, format!("not a {expected}")); + let variant = tcx.expect_variant_res(res); + let sugg = if variant.fields.is_empty() { + " {}".to_string() + } else { + format!( + " {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }; + let descr = "you might have meant to create a new value of the struct"; + let mut suggestion = vec![]; + match tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(..), + span: call_span, + .. + }) => { + suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); + } + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Binary(..), hir_id, .. + }) => { + suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); + if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::If(condition, block, None) = parent.kind + && condition.hir_id == *hir_id + && let hir::ExprKind::Block(block, _) = block.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + && let hir::ExprKind::Path(..) = expr.kind + { + // Special case: you can incorrectly write an equality condition: + // if foo == Struct { field } { /* if body */ } + // which should have been written + // if foo == (Struct { field }) { /* if body */ } + suggestion.push((block.span.shrink_to_hi(), ")".to_string())); + } else { + suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); + } + } + _ => { + suggestion.push((span.shrink_to_hi(), sugg)); } } - _ => { - suggestion.push((span.shrink_to_hi(), sugg)); - } + + err.multipart_suggestion(descr, suggestion, Applicability::HasPlaceholders); + err } + Res::Def(DefKind::Variant, _) if expr.is_none() => { + err.span_label(span, format!("not a {expected}")); - err.multipart_suggestion(descr, suggestion, Applicability::HasPlaceholders); - err - } - Res::Def(DefKind::Variant, _) if expr.is_none() => { - err.span_label(span, format!("not a {expected}")); - - let fields = &tcx.expect_variant_res(res).fields.raw; - let span = qpath.span().shrink_to_hi().to(span.shrink_to_hi()); - let (msg, sugg) = if fields.is_empty() { - ("use the struct variant pattern syntax", " {}".to_string()) - } else { - let msg = if fields.is_empty() { - "use struct variant pattern syntax" + let fields = &tcx.expect_variant_res(res).fields.raw; + let span = qpath.span().shrink_to_hi().to(span.shrink_to_hi()); + let (msg, sugg) = if fields.is_empty() { + ("use the struct variant pattern syntax", " {}".to_string()) } else { - "add the names to match a struct variant's fields" + let msg = if fields.is_empty() { + "use struct variant pattern syntax" + } else { + "add the names to match a struct variant's fields" + }; + let fields_sugg = fields + .iter() + .enumerate() + .map(|(i, field)| { + let field_name = field.ident(tcx).to_string(); + + let pat_snippet = sub_pats + .get(i) + .and_then(|sub_pat| { + tcx.sess.source_map().span_to_snippet(sub_pat.span).ok() + }) + .unwrap_or_else(|| "_".to_string()); + + if field_name == pat_snippet { + field_name + } else { + format!("{field_name}: {pat_snippet}") + } + }) + .collect::>() + .join(", "); + let sugg = format!(" {{ {} }}", fields_sugg); + (msg, sugg) }; - let fields_sugg = fields - .iter() - .enumerate() - .map(|(i, field)| { - let field_name = field.ident(tcx).to_string(); - - let pat_snippet = sub_pats - .get(i) - .and_then(|sub_pat| { - tcx.sess.source_map().span_to_snippet(sub_pat.span).ok() - }) - .unwrap_or_else(|| "_".to_string()); - - if field_name == pat_snippet { - field_name - } else { - format!("{field_name}: {pat_snippet}") - } - }) - .collect::>() - .join(", "); - let sugg = format!(" {{ {} }}", fields_sugg); - (msg, sugg) - }; - - err.span_suggestion_verbose( - qpath.span().shrink_to_hi().to(span.shrink_to_hi()), - msg, - sugg, - Applicability::HasPlaceholders, - ); - err + + err.span_suggestion_verbose( + qpath.span().shrink_to_hi().to(span.shrink_to_hi()), + msg, + sugg, + Applicability::HasPlaceholders, + ); + err + } + _ => err.with_span_label(span, format!("not a {expected}")), } - _ => err.with_span_label(span, format!("not a {expected}")), + .emit() } - .emit() } /// Controls whether all arguments are tupled. This is used for the call operator only. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 52602b8041d66..01c48c0ae790c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -32,7 +32,6 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use ty::adjustment::{PatAdjust, PatAdjustment}; -use super::report_unexpected_variant_res; use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, diagnostics}; @@ -1585,8 +1584,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, None, &[], @@ -1604,8 +1602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // Ok, we allow unit struct ctors in patterns only. } else { - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, None, &[], @@ -1775,8 +1772,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::TupleStruct(_, sub_pats, _) => sub_pats, _ => &[], }; - let e = report_unexpected_variant_res( - tcx, res, None, sub_pats, qpath, pat.span, E0164, expected, + let e = self.report_unexpected_variant_res( + res, None, sub_pats, qpath, pat.span, E0164, expected, ); Err(e) }; @@ -2237,7 +2234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); if has_shorthand_field_name { - let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); + let path = rustc_hir_pretty::qpath_to_string(self, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -2422,7 +2419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we don't care to report errors for a struct if the struct itself is tainted variant.has_errors()?; - let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); + let path = rustc_hir_pretty::qpath_to_string(self, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -2472,7 +2469,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { f } } - Err(_) => rustc_hir_pretty::pat_to_string(&self.tcx, field.pat), + Err(_) => rustc_hir_pretty::pat_to_string(self, field.pat), } }) .collect::>() diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index f624fcee78f59..55608083d3751 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -22,7 +22,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } -rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index c01d9e98e9b9c..8ec27921a5787 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -15,7 +15,6 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::lints::DelayedLints; use rustc_hir::*; -use rustc_hir_pretty as pprust_hir; use rustc_span::def_id::{CRATE_MOD_ID, StableCrateId}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; @@ -1156,12 +1155,6 @@ impl<'tcx> intravisit::HirTyCtxt<'tcx> for TyCtxt<'tcx> { } } -impl<'tcx> pprust_hir::PpAnn for TyCtxt<'tcx> { - fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - pprust_hir::PpAnn::nested(&(self as &dyn intravisit::HirTyCtxt<'_>), state, nested) - } -} - pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { let krate = tcx.hir_crate_items(()); let upstream_crates = upstream_crates(tcx); diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 4a2fbb8f8b7af..f648a85249dfc 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -52,6 +52,16 @@ impl<'tcx, B: Bridge> AllocRangeHelpers<'tcx> for CompilerCtxt<'tcx, B> { } } +impl<'tcx, B: Bridge> rustc_hir_pretty::PpAnn for CompilerCtxt<'tcx, B> { + fn nested(&self, state: &mut rustc_hir_pretty::State<'_>, nested: rustc_hir_pretty::Nested) { + rustc_hir_pretty::PpAnn::nested( + &(&self.tcx as &dyn rustc_hir::intravisit::HirTyCtxt<'_>), + state, + nested, + ) + } +} + impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { pub fn lift>>(&self, value: T) -> T::Lifted { self.tcx.lift(value) @@ -295,7 +305,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { .get_attrs_by_path(def_id, &attr_name) .filter_map(|attribute| { if let Attribute::Unparsed(u) = attribute { - let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(self, attribute); Some((attr_str, u.span)) } else { None @@ -314,7 +324,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { attrs_iter .filter_map(|attribute| { if let Attribute::Unparsed(u) = attribute { - let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(self, attribute); Some((attr_str, u.span)) } else { None diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7e46b2f593e49..eb382f368905f 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -12,7 +12,8 @@ use rustc_hir::attrs::{ }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; -use rustc_hir::{HeaderSafety, Safety, find_attr}; +use rustc_hir::{HeaderSafety, Safety, find_attr, intravisit}; +use rustc_hir_pretty::PpAnn; use rustc_metadata::rendered_const; use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, ty}; @@ -1243,7 +1244,10 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) } fn other_attr(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Attribute { - let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr); + let mut s = rustc_hir_pretty::attribute_to_string( + &(&tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn, + attr, + ); assert_eq!(s.pop(), Some('\n')); Attribute::Other(s) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index e38ba801c0bf7..9fc9f9944465c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -6,6 +6,7 @@ use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; +use rustc_hir::intravisit; use super::MATCH_WILD_ERR_ARM; @@ -19,7 +20,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { - let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); + let path_str = rustc_hir_pretty::qpath_to_string(#[allow(trivial_casts)] &(&cx.tcx as &dyn intravisit::HirTyCtxt<'_>), path); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); let mut ident_bind_name = kw::Underscore; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs index 60a6688927ab5..43721fa252837 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs @@ -6,6 +6,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use std::iter; +use rustc_hir_pretty::PpAnn; +use rustc_hir::intravisit; declare_clippy_lint! { /// ### What it does @@ -51,7 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { cx, &mut arguments.iter(), cx.typeck_results().expr_ty(fn_expr), - &rustc_hir_pretty::qpath_to_string(&cx.tcx, path), + #[allow(trivial_casts)] + &rustc_hir_pretty::qpath_to_string(&(&cx.tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn, path), "function", ); } From 18e0dd9aa8993a19e332fd080904a72270d18a0d Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Tue, 4 Aug 2026 15:31:15 -0300 Subject: [PATCH 09/13] Document zero-sized autodiff slice handling Clarify that slice-tail layout checks apply to the sized prefix rather than the slice element, and cover zero-sized slice elements in the type-tree run-make test. --- compiler/rustc_middle/src/ty/typetree.rs | 6 ++++-- .../autodiff/type-trees/slice-dst-typetree/rmake.rs | 1 + .../type-trees/slice-dst-typetree/slice-dst.check | 5 +++++ .../autodiff/type-trees/slice-dst-typetree/test.rs | 13 +++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/typetree.rs b/compiler/rustc_middle/src/ty/typetree.rs index 90fb0316fe39b..100c3170e12a9 100644 --- a/compiler/rustc_middle/src/ty/typetree.rs +++ b/compiler/rustc_middle/src/ty/typetree.rs @@ -66,14 +66,16 @@ fn handle_indirection<'a>( // LLVM arguments, while its child describes the memory reached through `data`. let typing_env = ty::TypingEnv::fully_monomorphized(); if let ty::Slice(element_ty) = tcx.struct_tail_for_codegen(inner_ty, typing_env).kind() { + // `layout.size` here is the sized prefix of `inner_ty`, not the slice element size. + // Direct slices, transparent wrappers (`OsStr`), and ZST-prefixed DSTs have no byte + // offset to preserve. Nonzero prefixes (e.g. `Header<[f32]>`) keep field offsets. + // ZST elements still take this path and yield an empty child TypeTree (size 0). let child = if tcx .layout_of(typing_env.as_query_input(inner_ty)) .is_ok_and(|layout| layout.size.bytes() == 0) { - // Direct slices and transparent wrappers such as `OsStr` contain elements everywhere. typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false) } else { - // Preserve field offsets for a sized prefix before the slice tail. typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true) }; return TypeTree(vec![Type { diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs index c19202fa41fe5..e0c8c87ca9e33 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs @@ -15,4 +15,5 @@ fn main() { let ir = rfs::read("test.ll"); llvm_filecheck().patterns("slice-dst.check").check_prefix("OSSTR").stdin_buf(&ir).run(); llvm_filecheck().patterns("slice-dst.check").check_prefix("HEADER").stdin_buf(&ir).run(); + llvm_filecheck().patterns("slice-dst.check").check_prefix("ZST").stdin_buf(&ir).run(); } diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check b/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check index 6031b728213ae..4b149c9ee090c 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check @@ -7,3 +7,8 @@ OSSTR: call void @llvm.memcpy{{.*}}"enzyme_type"="{[0]:Pointer, [0,0]:Pointer, [ HEADER-LABEL: define{{.*}}@header_sum( HEADER-SAME: ptr{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float, [-1,4]:Float@float}" HEADER-SAME: i64 "enzyme_type"="{[0]:Integer}" + +; ZST elements produce no child metadata under the slice data pointer. +ZST-LABEL: define{{.*}}@zst_slice_len( +ZST-SAME: ptr{{.*}}"enzyme_type"="{[-1]:Pointer}" +ZST-SAME: i64 "enzyme_type"="{[0]:Integer}" diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs b/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs index 8cd4d7b57f270..e34a8c71f4cc5 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs @@ -36,3 +36,16 @@ pub fn header_sum(value: &Header<[f32]>) -> f32 { pub fn exercise_header_sum(value: &Header<[f32]>, derivative: &mut Header<[f32]>) -> f32 { d_header_sum(value, derivative, 1.0) } + +// ZST slice elements yield an empty child TypeTree; element size 0 is expected. +#[autodiff_reverse(d_zst_slice_len, Duplicated, Active)] +#[no_mangle] +#[inline(never)] +pub fn zst_slice_len(slice: &[()]) -> f32 { + slice.len() as f32 +} + +#[no_mangle] +pub fn exercise_zst_slice_len(slice: &[()], derivative: &mut [()]) -> f32 { + d_zst_slice_len(slice, derivative, 1.0) +} From 679481475548fc354e162809e4c64591423c66ef Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 3 Aug 2026 22:52:37 +0000 Subject: [PATCH 10/13] Add some tests for specialization. --- tests/crashes/{126268.rs => 102252-2.rs} | 5 +- tests/crashes/125014.rs | 17 --- ...associated-types-in-default-impl-bounds.rs | 18 +++ ...efault-assoc-type-recursion-issue-80700.rs | 35 +++++ ...ault-impl-coherence-overlap-issue-77026.rs | 27 ++++ ...-impl-coherence-overlap-issue-77026.stderr | 12 ++ ...efault-impl-not-a-candidate-issue-48515.rs | 64 +++++++++ ...lt-impl-not-a-candidate-issue-48515.stderr | 63 +++++++++ .../default-impl-not-an-impl.rs | 71 ++++++++++ .../default-impl-not-an-impl.stderr | 69 ++++++++++ .../default-impl-partial-and-inherits.rs | 111 +++++++++++++++ .../default-type-normalize-issue-50318.rs | 24 ++++ .../default-type-normalize-issue-50318.stderr | 18 +++ ...lf-projection-ice-issue-125014.next.stderr | 66 +++++++++ ...t-type-self-projection-ice-issue-125014.rs | 27 ++++ .../spec-influences-inference-issue-36262.rs | 128 ++++++++++++++++++ ...specialized-impl-projection-issue-32483.rs | 27 ++++ .../trait-alias-specialization-issue-74809.rs | 44 ++++++ 18 files changed, 808 insertions(+), 18 deletions(-) rename tests/crashes/{126268.rs => 102252-2.rs} (86%) delete mode 100644 tests/crashes/125014.rs create mode 100644 tests/ui/specialization/associated-types-in-default-impl-bounds.rs create mode 100644 tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs create mode 100644 tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs create mode 100644 tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr create mode 100644 tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs create mode 100644 tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr create mode 100644 tests/ui/specialization/default-impl-not-an-impl.rs create mode 100644 tests/ui/specialization/default-impl-not-an-impl.stderr create mode 100644 tests/ui/specialization/default-impl-partial-and-inherits.rs create mode 100644 tests/ui/specialization/default-type-normalize-issue-50318.rs create mode 100644 tests/ui/specialization/default-type-normalize-issue-50318.stderr create mode 100644 tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr create mode 100644 tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs create mode 100644 tests/ui/specialization/spec-influences-inference-issue-36262.rs create mode 100644 tests/ui/specialization/specialized-impl-projection-issue-32483.rs create mode 100644 tests/ui/specialization/trait-alias-specialization-issue-74809.rs diff --git a/tests/crashes/126268.rs b/tests/crashes/102252-2.rs similarity index 86% rename from tests/crashes/126268.rs rename to tests/crashes/102252-2.rs index 82e52fa115dc9..ccb15b82736e2 100644 --- a/tests/crashes/126268.rs +++ b/tests/crashes/102252-2.rs @@ -1,4 +1,5 @@ -//@ known-bug: #126268 +//@ known-bug: #102252 + #![feature(min_specialization)] trait Trait {} @@ -16,3 +17,5 @@ struct DatasetIter<'a, R: Data> { pub struct ArrayBase {} impl<'a> Trait for DatasetIter<'a, ArrayBase> {} + +fn main() {} diff --git a/tests/crashes/125014.rs b/tests/crashes/125014.rs deleted file mode 100644 index b29042ee5983a..0000000000000 --- a/tests/crashes/125014.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: rust-lang/rust#125014 -//@ compile-flags: -Znext-solver=coherence -#![feature(specialization)] - -trait Foo {} - -impl Foo for ::Output {} - -impl Foo for u32 {} - -trait Assoc { - type Output; -} -impl Output for u32 {} -impl Assoc for ::Output { - default type Output = bool; -} diff --git a/tests/ui/specialization/associated-types-in-default-impl-bounds.rs b/tests/ui/specialization/associated-types-in-default-impl-bounds.rs new file mode 100644 index 0000000000000..ea7188810db07 --- /dev/null +++ b/tests/ui/specialization/associated-types-in-default-impl-bounds.rs @@ -0,0 +1,18 @@ +//@ check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +// Tests that you can use a trait's associated types in the bounds of a default impl. +// Regression test for #52396. + +trait Foo { + type Baz; + fn bar(&self, _: Self::Baz); +} + +default impl> Foo for A { + fn bar(&self, _: isize) { } +} + +fn main() {} diff --git a/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs b/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs new file mode 100644 index 0000000000000..e013610121efe --- /dev/null +++ b/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs @@ -0,0 +1,35 @@ +//@ check-pass + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a blanket impl supplying a `default type` does not make a +// recursive trait requirement diverge. +// Regression test for #80700. + +use std::marker::PhantomData; + +struct Nil; +struct Cons(PhantomData<(Head, Tail)>); +struct Error; + +trait GetLast { + type Output; +} + +impl GetLast for T { + default type Output = Error; +} + +impl GetLast for Cons { + type Output = Nil; +} + +impl GetLast for Cons> +where + Cons: GetLast, +{ + type Output = as GetLast>::Output; +} + +fn main() {} diff --git a/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs new file mode 100644 index 0000000000000..6d610805608af --- /dev/null +++ b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs @@ -0,0 +1,27 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// `default impl` still participates in coherence. However, we shouldn't get an overflow here. +// Regresion test for #77026. + +pub enum Either { + Left(L), + Right(R), +} + +default impl From for Either { + fn from(l: L) -> Self { + Either::Left(l) + } +} + +impl From for Either { + //~^ ERROR conflicting implementations of trait `From<_>` for type `Either<_, _>` + fn from(r: R) -> Self { + Either::Right(r) + } +} + +fn main() {} diff --git a/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr new file mode 100644 index 0000000000000..c9b30bf2f6495 --- /dev/null +++ b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `From<_>` for type `Either<_, _>` + --> $DIR/default-impl-coherence-overlap-issue-77026.rs:20:1 + | +LL | default impl From for Either { + | ------------------------------------------- first implementation here +... +LL | impl From for Either { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Either<_, _>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs new file mode 100644 index 0000000000000..4e31dcf117daa --- /dev/null +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs @@ -0,0 +1,64 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that we don't overflow when using `default impl`. +// Regression test for #48515, #98478, and #117909. + +// #48515 + +trait TypeString { + fn type_string() -> &'static str; +} + +default impl TypeString for T { + fn type_string() -> &'static str { + "unknown type" + } +} + +impl TypeString for () { + fn type_string() -> &'static str { + "()" + } +} + +// #98478 + +trait Spam {} + +trait SpamMore: Spam {} + +default impl Spam for T where T: SpamMore {} + +struct A; + +impl SpamMore for A {} +//~^ ERROR the trait bound `A: Spam` is not satisfied + +fn needs_spam() {} + +// #117909 + +trait Set { + fn contains(&self, bit: T); +} + +default impl Set<&T> for S +where + S: Set, +{ + fn contains(&self, _: &T) {} +} + +fn main() { + let _ = ::type_string(); + //~^ ERROR the trait bound `usize: TypeString` is not satisfied + + needs_spam::(); + //~^ ERROR the trait bound `A: Spam` is not satisfied + + 0u32.contains(()); + //~^ ERROR no method named `contains` found for type `u32` in the current scope +} diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr new file mode 100644 index 0000000000000..91df5005d7095 --- /dev/null +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr @@ -0,0 +1,63 @@ +error[E0277]: the trait bound `A: Spam` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:37:19 + | +LL | impl SpamMore for A {} + | ^ unsatisfied trait bound + | +help: the trait `Spam` is not implemented for `A` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:35:1 + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `SpamMore` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:31:17 + | +LL | trait SpamMore: Spam {} + | ^^^^ required by this bound in `SpamMore` + +error[E0277]: the trait bound `usize: TypeString` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:56:14 + | +LL | let _ = ::type_string(); + | ^^^^^ the trait `TypeString` is not implemented for `usize` + | +help: the trait `TypeString` is implemented for `()` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:21:1 + | +LL | impl TypeString for () { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `A: Spam` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:59:18 + | +LL | needs_spam::(); + | ^ unsatisfied trait bound + | +help: the trait `Spam` is not implemented for `A` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:35:1 + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `needs_spam` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:40:18 + | +LL | fn needs_spam() {} + | ^^^^ required by this bound in `needs_spam` + +error[E0599]: no method named `contains` found for type `u32` in the current scope + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:62:10 + | +LL | 0u32.contains(()); + | ^^^^^^^^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Set` defines an item `contains`, perhaps you need to implement it + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:44:1 + | +LL | trait Set { + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/specialization/default-impl-not-an-impl.rs b/tests/ui/specialization/default-impl-not-an-impl.rs new file mode 100644 index 0000000000000..2b0902173158e --- /dev/null +++ b/tests/ui/specialization/default-impl-not-an-impl.rs @@ -0,0 +1,71 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a `default impl` does not count as an *actual* impl, so it cannot +// be used to satisfy trait bounds. + +// A `default impl` may omit trait items, but a real impl may not. + +trait Gapped { + fn a(&self) -> u32; + fn b(&self) -> u32; +} + +default impl Gapped for T { + fn a(&self) -> u32 { + 1 + } +} + +impl Gapped for u8 {} +//~^ ERROR not all trait items implemented, missing: `b` + +// A `default impl` that defines *every* trait item is still not an impl. + +trait Foo { + fn f(&self) -> u32; +} + +default impl Foo for T { + fn f(&self) -> u32 { + 1 + } +} + +fn need_foo(t: &T) -> u32 { + t.f() +} + +trait Bar { + fn b(&self) -> u32; +} + +impl Bar for T { + fn b(&self) -> u32 { + self.f() + } +} + +fn need_bar(t: &T) -> u32 { + t.b() +} + +fn main() { + // as a bound (UFCS `::f` is the same trait-selection path, omitted) + need_foo(&0u32); + //~^ ERROR the trait bound `u32: Foo` is not satisfied + + // as a method-probe candidate + 0u32.f(); + //~^ ERROR no method named `f` found for type `u32` in the current scope + + // when building a vtable + let _: &dyn Foo = &0u32; + //~^ ERROR the trait bound `u32: Foo` is not satisfied + + // transitively, as another impl's where-clause + need_bar(&0i64); + //~^ ERROR the trait bound `i64: Bar` is not satisfied +} diff --git a/tests/ui/specialization/default-impl-not-an-impl.stderr b/tests/ui/specialization/default-impl-not-an-impl.stderr new file mode 100644 index 0000000000000..cde757b3f98a7 --- /dev/null +++ b/tests/ui/specialization/default-impl-not-an-impl.stderr @@ -0,0 +1,69 @@ +error[E0046]: not all trait items implemented, missing: `b` + --> $DIR/default-impl-not-an-impl.rs:22:1 + | +LL | fn b(&self) -> u32; + | ------------------- `b` from trait +... +LL | impl Gapped for u8 {} + | ^^^^^^^^^^^^^^^^^^ missing `b` in implementation + +error[E0277]: the trait bound `u32: Foo` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:57:14 + | +LL | need_foo(&0u32); + | -------- ^^^^^ the trait `Foo` is not implemented for `u32` + | | + | required by a bound introduced by this call + | +note: required by a bound in `need_foo` + --> $DIR/default-impl-not-an-impl.rs:37:16 + | +LL | fn need_foo(t: &T) -> u32 { + | ^^^ required by this bound in `need_foo` + +error[E0599]: no method named `f` found for type `u32` in the current scope + --> $DIR/default-impl-not-an-impl.rs:61:10 + | +LL | 0u32.f(); + | ^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `f`, perhaps you need to implement it + --> $DIR/default-impl-not-an-impl.rs:27:1 + | +LL | trait Foo { + | ^^^^^^^^^ + +error[E0277]: the trait bound `u32: Foo` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:65:23 + | +LL | let _: &dyn Foo = &0u32; + | ^^^^^ the trait `Foo` is not implemented for `u32` + | + = note: required for the cast from `&u32` to `&dyn Foo` + +error[E0277]: the trait bound `i64: Bar` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:69:14 + | +LL | need_bar(&0i64); + | -------- ^^^^^ the trait `Foo` is not implemented for `i64` + | | + | required by a bound introduced by this call + | +note: required for `i64` to implement `Bar` + --> $DIR/default-impl-not-an-impl.rs:45:14 + | +LL | impl Bar for T { + | --- ^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `need_bar` + --> $DIR/default-impl-not-an-impl.rs:51:16 + | +LL | fn need_bar(t: &T) -> u32 { + | ^^^ required by this bound in `need_bar` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0277, E0599. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/specialization/default-impl-partial-and-inherits.rs b/tests/ui/specialization/default-impl-partial-and-inherits.rs new file mode 100644 index 0000000000000..a2c622daafb08 --- /dev/null +++ b/tests/ui/specialization/default-impl-partial-and-inherits.rs @@ -0,0 +1,111 @@ +//@ run-pass + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a `default impl` does not need all items, but does contribute to +// the chain of specialization. + +// A partial `default impl` at each level of a 3-level chain. + +trait Foo { + type Assoc; + const N: u32; + fn from_root(&self) -> &'static str; + fn from_mid(&self) -> &'static str; + fn from_leaf(&self) -> &'static str; + fn from_trait(&self) -> &'static str { + "trait body" + } +} + +// root: assoc type, assoc const, one method +default impl Foo for T { + type Assoc = u8; + const N: u32 = 1; + fn from_root(&self) -> &'static str { + "root" + } +} + +// middle: one method +default impl Foo for T { + fn from_mid(&self) -> &'static str { + "mid" + } +} + +// leaf: one method. Everything else must come from the two ancestors, except +// `from_trait`, which no impl in the chain defines. +impl Foo for u32 { + fn from_leaf(&self) -> &'static str { + "leaf" + } +} + +// sibling leaf: overrides every inherited item, including assoc type and const +impl Foo for i8 { + type Assoc = bool; + const N: u32 = 2; + fn from_root(&self) -> &'static str { + "i8 root" + } + fn from_mid(&self) -> &'static str { + "i8 mid" + } + fn from_leaf(&self) -> &'static str { + "i8 leaf" + } + fn from_trait(&self) -> &'static str { + "i8 trait" + } +} + +fn generic(t: &T) -> [&'static str; 4] { + [t.from_root(), t.from_mid(), t.from_leaf(), t.from_trait()] +} + +// An empty `default impl`, and an empty real impl that inherits every item. + +trait Marker { + type A; + fn m(&self) -> &'static str; +} + +// Contributes nothing at all, and is still accepted. +default impl Marker for T {} + +// Covers every item of the trait. +default impl Marker for T { + type A = u8; + fn m(&self) -> &'static str { + "from default impl" + } +} + +// Declaration of intent and nothing else. This is what the `default impl` above +// is missing, and the only thing it is missing. +impl Marker for u32 {} + +fn main() { + // inherited across the chain, via a concrete receiver... + assert_eq!(0u32.from_root(), "root"); + assert_eq!(0u32.from_mid(), "mid"); + assert_eq!(0u32.from_leaf(), "leaf"); + assert_eq!(0u32.from_trait(), "trait body"); + assert_eq!(::N, 1); + // The omitting impl finalizes the ancestor's definition, so this normalizes. + let _: ::Assoc = 0u8; + + // ...and through a generic bound + assert_eq!(generic(&0u32), ["root", "mid", "leaf", "trait body"]); + assert_eq!(generic(&0i8), ["i8 root", "i8 mid", "i8 leaf", "i8 trait"]); + assert_eq!(::N, 2); + let _: ::Assoc = true; + + // empty impl really does implement: method, projection, and vtable + assert_eq!(0u32.m(), "from default impl"); + let _: ::A = 0u8; + let _: &dyn Marker = &0u32; + +} diff --git a/tests/ui/specialization/default-type-normalize-issue-50318.rs b/tests/ui/specialization/default-type-normalize-issue-50318.rs new file mode 100644 index 0000000000000..b69acfe47a929 --- /dev/null +++ b/tests/ui/specialization/default-type-normalize-issue-50318.rs @@ -0,0 +1,24 @@ +//@ check-fail +//@ known-bug: #50318 + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that we can normalize a `default type`. + +trait Trait { + type AssocType; +} + +struct Struct {} + +impl Trait for Struct { + default type AssocType = i32; +} + +type AssocType = ::AssocType; + +fn main() { + assert_eq!(std::any::type_name::(), "i32"); + let x: AssocType = 0; +} diff --git a/tests/ui/specialization/default-type-normalize-issue-50318.stderr b/tests/ui/specialization/default-type-normalize-issue-50318.stderr new file mode 100644 index 0000000000000..b0d69287adac2 --- /dev/null +++ b/tests/ui/specialization/default-type-normalize-issue-50318.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/default-type-normalize-issue-50318.rs:23:24 + | +LL | let x: AssocType = 0; + | --------- ^ expected associated type, found integer + | | + | expected due to this + | + = note: expected associated type `::AssocType` + found type `{integer}` + = help: consider constraining the associated type `::AssocType` to `{integer}` or calling a method that returns `::AssocType` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + = note: the associated type `::AssocType` is defined as `{integer}` in the implementation, but the where-bound `Struct` shadows this definition + see issue #152409 for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr new file mode 100644 index 0000000000000..7280c8213e5dd --- /dev/null +++ b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr @@ -0,0 +1,66 @@ +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:23:22 + | +LL | default type B = (); + | ^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs new file mode 100644 index 0000000000000..9b4e3eade03b6 --- /dev/null +++ b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[current] known-bug: #125014 +//@[current] failure-status: 101 +//@[current] dont-check-compiler-stderr + +// Tests that we don't ICE when a `default type` is potentially used as a self-type in an impl. +// Regression for #125014. + +#![feature(specialization)] +#![allow(incomplete_features)] + +trait A { + type B; +} + +impl A for ::B { + //[next]~^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^^^ ERROR the trait bound `u16: A` is not satisfied + default type B = (); + //[next]~^ ERROR the trait bound `u16: A` is not satisfied +} + +fn main() {} diff --git a/tests/ui/specialization/spec-influences-inference-issue-36262.rs b/tests/ui/specialization/spec-influences-inference-issue-36262.rs new file mode 100644 index 0000000000000..1e96fd5065097 --- /dev/null +++ b/tests/ui/specialization/spec-influences-inference-issue-36262.rs @@ -0,0 +1,128 @@ +//@ edition: 2021 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass +//@[current] known-bug: #36262 +//@[current] dont-check-compiler-stderr + +// Tests that specialization does not leak into type inference. +// Regression for #36262 and duplicate issues. + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Site 1: the receiver's type parameter, observed through a return position (#36262). +mod receiver_return { + struct My(T); + + trait Conv { + fn conv(self) -> T; + } + + impl Conv for My { + default fn conv(self) -> T { + self.0 + } + } + + impl Conv for My { + fn conv(self) -> u32 { + self.0 + } + } + + fn use_it() { + // Should infer `i32`; the sole `My` impl steers it to `u32`. + let x = My(0); + let _ = x.conv() + 0i32; + } +} + +// Site 2: a method argument's trait type parameter (#91973, #38516, #67918). +mod method_arg { + struct Foo; + + trait Bar { + fn bar(&self, _: T); + } + + impl Bar for Foo { + default fn bar(&self, _: T) {} + } + + impl Bar for Foo { + fn bar(&self, _: bool) {} + } + + fn use_it() { + // Should infer `{integer}`; the sole `Bar` impl steers it to `bool`. + Foo.bar(42); + } +} + +// Site 3: an explicit `_` in a UFCS trait reference (#40718). +mod ufcs_infer { + use std::vec; + + struct Foo(T); + + impl Foo { + fn build>(it: I) -> Foo { + // The second argument should infer to `I::IntoIter`; the sole + // `vec::IntoIter` impl steers it there. + >::from_iter(it.into_iter()) + } + } + + trait SpecExtend { + fn from_iter(iter: I) -> Self; + } + + impl SpecExtend for Foo + where + I: Iterator, + { + default fn from_iter(_: I) -> Self { + panic!() + } + } + + impl SpecExtend> for Foo { + fn from_iter(_: vec::IntoIter) -> Self { + panic!() + } + } +} + +// Site 4: an operator, where the sole specialization is derive-generated (#55243). +mod derived_specializer { + use std::borrow::Borrow; + + #[derive(PartialEq)] + struct MyString(String); + + impl Borrow for MyString { + fn borrow(&self) -> &str { + &self.0 + } + } + + impl PartialEq for MyString + where + Rhs: ?Sized + Borrow, + { + default fn eq(&self, rhs: &Rhs) -> bool { + self.0 == rhs.borrow() + } + } + + fn use_it() { + // Should select `PartialEq`; the derived `PartialEq` is the + // sole specialization and inference commits `Rhs = MyString`. + let s = MyString(String::from("Hello, world!")); + let _ = s == "Hello, world!"; + } +} + +fn main() {} diff --git a/tests/ui/specialization/specialized-impl-projection-issue-32483.rs b/tests/ui/specialization/specialized-impl-projection-issue-32483.rs new file mode 100644 index 0000000000000..5b26679422a40 --- /dev/null +++ b/tests/ui/specialization/specialized-impl-projection-issue-32483.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +// Tests that we allow some projections in specialized impls. +// Regression test for issue #32483. + +pub trait Foo { + type TypeA; + type TypeB: Bar; +} + +pub trait Bar { +} + +pub struct ImplsBar; +impl Bar for ImplsBar { +} + +impl Foo for T { + type TypeA = u8; + // WF checking `TypeB` here requires us to project `Self::TypeA` + default type TypeB = ImplsBar; +} + +fn main() {} diff --git a/tests/ui/specialization/trait-alias-specialization-issue-74809.rs b/tests/ui/specialization/trait-alias-specialization-issue-74809.rs new file mode 100644 index 0000000000000..e62532e8ab033 --- /dev/null +++ b/tests/ui/specialization/trait-alias-specialization-issue-74809.rs @@ -0,0 +1,44 @@ +//@ check-pass + +#![feature(specialization)] +#![feature(trait_alias)] +#![allow(incomplete_features)] + +// Tests that we can specialize on a trait alias. +// Regression test for #74809. + +pub trait Marker1 {} +pub trait Marker2 {} + +pub trait CombinedMarker = Marker1 + Marker2; + +pub struct Container { + p: std::marker::PhantomData<(T, U)>, +} + +pub struct Struct; +impl Marker1 for Struct {} + +pub trait Trait { + fn do_thing(&self); +} + +impl> Trait for Container { + default fn do_thing(&self) { + println!("default behavior"); + } +} + +impl> Trait for Container { + default fn do_thing(&self) { + println!("partially specialized behavior"); + } +} + +impl Trait for Container { + fn do_thing(&self) { + println!("fully specialized behavior") + } +} + +fn main() {} From 228bbb36ad155d5fbd2d1f783742a4c7ecd2e0c5 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 4 Aug 2026 20:11:32 +0100 Subject: [PATCH 11/13] fix(bootstrap): Normalize the names of proc macro dependency crates --- src/bootstrap/src/utils/proc_macro_deps.rs | 56 +++++++++++----------- src/tools/tidy/src/deps.rs | 11 ++++- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 8e91f95e18f3f..02d8fe41f5869 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -6,46 +6,46 @@ pub static CRATES: &[&str] = &[ "anyhow", "askama_derive", "askama_parser", - "basic-toml", + "basic_toml", "bitflags", - "block-buffer", + "block_buffer", "bumpalo", - "cfg-if", + "cfg_if", "cpufeatures", - "crypto-common", + "crypto_common", "darling", "darling_core", "derive_builder_core", "digest", "equivalent", - "fluent-bundle", - "fluent-langneg", - "fluent-syntax", + "fluent_bundle", + "fluent_langneg", + "fluent_syntax", "fnv", "foldhash", - "generic-array", + "generic_array", "glob", "hashbrown", "heck", - "id-arena", + "id_arena", "ident_case", "indexmap", - "intl-memoizer", + "intl_memoizer", "intl_pluralrules", "itoa", "leb128fmt", "libc", "log", "memchr", - "minimal-lexical", + "minimal_lexical", "nom", "pest", "pest_generator", "pest_meta", "prettyplease", - "proc-macro2", + "proc_macro2", "quote", - "rustc-hash", + "rustc_hash", "ryu", "self_cell", "semver", @@ -61,25 +61,25 @@ pub static CRATES: &[&str] = &[ "synstructure", "thiserror", "tinystr", - "type-map", + "type_map", "typenum", - "ucd-trie", - "unic-langid", - "unic-langid-impl", - "unic-langid-macros", - "unicode-ident", - "unicode-xid", + "ucd_trie", + "unic_langid", + "unic_langid_impl", + "unic_langid_macros", + "unicode_ident", + "unicode_xid", "version_check", - "wasm-bindgen-macro-support", - "wasm-bindgen-shared", - "wasm-encoder", - "wasm-metadata", + "wasm_bindgen_macro_support", + "wasm_bindgen_shared", + "wasm_encoder", + "wasm_metadata", "wasmparser", "winnow", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", - "wit-parser", + "wit_bindgen_core", + "wit_bindgen_rust", + "wit_component", + "wit_parser", "yoke", "zerofrom", "zerovec", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 479199414d7ec..734ca79518090 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -719,8 +719,15 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut // Remove the proc-macro crates themselves proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg])); // Sort and deduplicate the crate names. - let proc_macro_deps = - proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect::>(); + // Cargo package names may contain `-`, but will normalize these to `_` before passing to rustc. + // As bootstrap parses the `--crate-name` flag, use the name of the actual lib target which has + // been normalized. + let proc_macro_deps = proc_macro_deps + .into_iter() + .filter_map(|dep| { + metadata[dep].targets.iter().find_map(|target| target.is_lib().then_some(&target.name)) + }) + .collect::>(); let expected = { use std::fmt::Write; From a6dfd0cc18614a4232d0e533539bad9981a49efd Mon Sep 17 00:00:00 2001 From: derek-homel Date: Tue, 4 Aug 2026 19:02:23 -0400 Subject: [PATCH 12/13] docs: fix typo in AllowExprMetavar comment Fixes a small typo in the documentation comment for AllowExprMetavar. Changes decrarative to `declarative`. Change in compiler/rustc_attr_parsing/src/parser.rs: Line 492 --- compiler/rustc_attr_parsing/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index c4b2a5b509051..76587ba9f0ead 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -489,7 +489,7 @@ fn expr_to_lit<'sess>( } } -/// Whether expansions of `expr` metavariables from decrarative macros +/// Whether expansions of `expr` metavariables from declarative macros /// are permitted. Used when parsing meta items; currently, only `cfg` predicates /// enable this option #[derive(Clone, Copy, PartialEq, Eq)] From eace512093ce4d96afcb7352f595bb72f1ba6bb8 Mon Sep 17 00:00:00 2001 From: YingqiDuan <141370165+YingqiDuan@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:31:06 +0000 Subject: [PATCH 13/13] Suggest cast_signed for overflowing integer literals Co-authored-by: Roland Xu --- compiler/rustc_lint/src/lints.rs | 39 +++++++++++++------ compiler/rustc_lint/src/types/literal.rs | 26 +++++++++---- .../no-inline-literals-out-of-range.stderr | 9 +++-- tests/ui/lint/type-overflow.stderr | 14 ++++--- 4 files changed, 59 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index bff3b79df8655..07279a04b0c8c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2030,18 +2030,33 @@ pub(crate) enum OverflowingBinHexSub<'a> { } #[derive(Subdiagnostic)] -#[suggestion( - "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", - code = "{lit_no_suffix}{uint_ty} as {int_ty}", - applicability = "maybe-incorrect" -)] -pub(crate) struct OverflowingBinHexSignBitSub<'a> { - #[primary_span] - pub span: Span, - pub lit_no_suffix: &'a str, - pub negative_val: String, - pub uint_ty: &'a str, - pub int_ty: &'a str, +pub(crate) enum OverflowingBinHexSignBitSub<'a> { + #[suggestion( + "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", + code = "{lit_no_suffix}{uint_ty}.cast_signed()", + applicability = "maybe-incorrect" + )] + CastSigned { + #[primary_span] + span: Span, + lit_no_suffix: &'a str, + negative_val: String, + uint_ty: &'a str, + int_ty: &'a str, + }, + #[suggestion( + "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", + code = "{lit_no_suffix}{uint_ty} as {int_ty}", + applicability = "maybe-incorrect" + )] + AsCast { + #[primary_span] + span: Span, + lit_no_suffix: &'a str, + negative_val: String, + uint_ty: &'a str, + int_ty: &'a str, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index bed26ee6f3d25..4759f087ed08b 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -205,13 +205,25 @@ fn report_bin_hex_error( &repr_str }; - Some(OverflowingBinHexSignBitSub { - span, - lit_no_suffix, - negative_val: actually, - int_ty: int_ty.name_str(), - uint_ty: Integer::fit_unsigned(val).uint_ty_str(), - }) + let uint_ty = Integer::fit_unsigned(val); + // `cast_signed` only supports equal-width integer casts. + if uint_ty.size() == size { + Some(OverflowingBinHexSignBitSub::CastSigned { + span, + lit_no_suffix, + negative_val: actually, + uint_ty: uint_ty.uint_ty_str(), + int_ty: int_ty.name_str(), + }) + } else { + Some(OverflowingBinHexSignBitSub::AsCast { + span, + lit_no_suffix, + negative_val: actually, + uint_ty: uint_ty.uint_ty_str(), + int_ty: int_ty.name_str(), + }) + } }) .flatten(); diff --git a/tests/ui/fmt/no-inline-literals-out-of-range.stderr b/tests/ui/fmt/no-inline-literals-out-of-range.stderr index 0800fb2497619..744a4e5625fef 100644 --- a/tests/ui/fmt/no-inline-literals-out-of-range.stderr +++ b/tests/ui/fmt/no-inline-literals-out-of-range.stderr @@ -13,8 +13,9 @@ LL + format_args!("{}", 0x8f_u8); // issue #115423 | help: to use as a negative number (decimal `-113`), consider using the type `u8` for the literal and cast it to `i8` | -LL | format_args!("{}", 0x8f_u8 as i8); // issue #115423 - | +++++ +LL - format_args!("{}", 0x8f_i8); // issue #115423 +LL + format_args!("{}", 0x8f_u8.cast_signed()); // issue #115423 + | error: literal out of range for `u8` --> $DIR/no-inline-literals-out-of-range.rs:6:24 @@ -50,8 +51,8 @@ LL | format_args!("{}", 0xffff_ffff); // treat unsuffixed literals as i32 = help: consider using the type `u32` instead help: to use as a negative number (decimal `-1`), consider using the type `u32` for the literal and cast it to `i32` | -LL | format_args!("{}", 0xffff_ffffu32 as i32); // treat unsuffixed literals as i32 - | ++++++++++ +LL | format_args!("{}", 0xffff_ffffu32.cast_signed()); // treat unsuffixed literals as i32 + | +++++++++++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index 065c530adcf57..66d856dac3bdf 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -26,8 +26,9 @@ LL + let fail = 0b1000_0001u8; | help: to use as a negative number (decimal `-127`), consider using the type `u8` for the literal and cast it to `i8` | -LL | let fail = 0b1000_0001u8 as i8; - | +++++ +LL - let fail = 0b1000_0001i8; +LL + let fail = 0b1000_0001u8.cast_signed(); + | warning: literal out of range for `i64` --> $DIR/type-overflow.rs:15:16 @@ -43,8 +44,9 @@ LL + let fail = 0x8000_0000_0000_0000u64; | help: to use as a negative number (decimal `-9223372036854775808`), consider using the type `u64` for the literal and cast it to `i64` | -LL | let fail = 0x8000_0000_0000_0000u64 as i64; - | ++++++ +LL - let fail = 0x8000_0000_0000_0000i64; +LL + let fail = 0x8000_0000_0000_0000u64.cast_signed(); + | warning: literal out of range for `u32` --> $DIR/type-overflow.rs:19:16 @@ -64,8 +66,8 @@ LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; = help: consider using the type `u128` instead help: to use as a negative number (decimal `-170141183460469231731687303715884105728`), consider using the type `u128` for the literal and cast it to `i128` | -LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128 as i128; - | ++++++++++++ +LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.cast_signed(); + | ++++++++++++++++++ warning: literal out of range for `i32` --> $DIR/type-overflow.rs:27:16