From f2828cfe4436da090440d295bbe8c866e42c37d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Wed, 1 Jul 2026 18:38:41 +0200 Subject: [PATCH 01/12] windows-gnu: enable native TLS --- compiler/rustc_target/src/spec/base/windows_gnu.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index 1a343c737ad45..c2cf50b40a478 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -101,6 +101,7 @@ pub(crate) fn opts() -> TargetOptions { emit_debug_gdb_scripts: false, requires_uwtable: true, eh_frame_header: false, + has_thread_local: true, debuginfo_kind: DebuginfoKind::Dwarf, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. From 26be04bec67f222287eaa3706cfda26d078a02bb Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sat, 19 Sep 2026 12:25:37 -0300 Subject: [PATCH 02/12] Preserve eager normalization failures Return failed projection obligations from deep normalization instead of retrying aliases through infallible fallback. Keep regular normalization recovery by rebuilding projection obligations from the original aliases. --- .../src/solve/normalize.rs | 64 ++++++++++++------- ...1.stderr => issue-111404-1.current.stderr} | 10 +-- .../issue-111404-1.next.stderr | 9 +++ .../issue-111404-1.rs | 15 +++-- ..._outlives_bounds_not_resolving_vars_ice.rs | 4 +- ...lives_bounds_not_resolving_vars_ice.stderr | 22 ++----- .../deeply-normalize-fallback-issue-160875.rs | 34 ++++++++++ ...ply-normalize-fallback-issue-160875.stderr | 16 +++++ 8 files changed, 119 insertions(+), 55 deletions(-) rename tests/ui/associated-inherent-types/{issue-111404-1.stderr => issue-111404-1.current.stderr} (88%) create mode 100644 tests/ui/associated-inherent-types/issue-111404-1.next.stderr create mode 100644 tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.rs create mode 100644 tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.stderr diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 9430b6c4b3307..ef9caca003ef2 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -2,7 +2,8 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::infer::at::At; use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{ - FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, TraitErrors, + FromSolverError, Normalized, Obligation, PredicateObligation, PredicateObligations, + TraitEngine, TraitErrors, }; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ @@ -11,23 +12,32 @@ use rustc_middle::ty::{ }; use rustc_next_trait_solver::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; -use thin_vec::ThinVec; +use thin_vec::{ThinVec, thin_vec}; use super::{FulfillmentCtxt, NextSolverError}; use crate::solve::{Certainty, SolverDelegate}; use crate::traits::{BoundVarReplacer, ScrubbedTraitError}; -/// see `normalize_with_universes`. +/// Normalize a value, deferring ambiguity and errors to fulfillment. pub fn normalize<'tcx, T>(at: At<'_, 'tcx>, value: Unnormalized<'tcx, T>) -> Normalized<'tcx, T> where T: TypeFoldable>, { - normalize_with_universes(at, value, vec![]) + match normalize_with_universes(at, value.clone(), vec![]) { + Ok(normalized) => normalized, + Err(_) => { + let mut replacer = + ReplaceAliasWithInfer { at, obligations: Default::default(), universes: vec![] }; + let value = at.infcx.deeply_resolve_ignoring_regions(value.skip_normalization()); + let value = value.fold_with(&mut replacer); + Normalized { value, obligations: replacer.obligations } + } + } } /// Like `deeply_normalize`, but we handle ambiguity and inference variables in this routine. /// The behavior should be same as the old solver. -/// For error, we return an infer var plus the failed obligation. +/// On error, return the failed obligation. /// For ambiguity, we have two cases: /// - has_escaping_bound_vars: return the original alias. /// - otherwise: return the normalized result. It can be (partially) inferred @@ -36,7 +46,7 @@ fn normalize_with_universes<'tcx, T>( at: At<'_, 'tcx>, value: Unnormalized<'tcx, T>, universes: Vec>, -) -> Normalized<'tcx, T> +) -> Result, PredicateObligation<'tcx>> where T: TypeFoldable>, { @@ -45,19 +55,25 @@ where let value = infcx.deeply_resolve_ignoring_regions(value); if !infcx.tcx.renormalize_rigid_aliases() && !value.has_non_rigid_aliases() { - return Normalized { value, obligations: Default::default() }; + return Ok(Normalized { value, obligations: Default::default() }); } - let original_value = value.clone(); let mut stalled_goals = vec![]; - let mut folder = NormalizationFolder::new(infcx, universes.clone(), |alias_term| { + let mut folder = NormalizationFolder::new(infcx, universes, |alias_term| { let delegate = <&SolverDelegate<'tcx>>::from(infcx); let infer_term = delegate.next_term_var_of_alias_kind(alias_term, at.cause.span); let predicate = ty::ProjectionClause { projection_term: alias_term, term: infer_term }; let goal = Goal::new(infcx.tcx, at.param_env, predicate); let result = match delegate.evaluate_root_goal(goal, at.cause.span, None) { Ok(result) => result, - Err(err) => return Err(err), + Err(_) => { + return Err(Obligation::new( + infcx.tcx, + at.cause.clone(), + goal.param_env, + goal.predicate, + )); + } }; let normalized = infcx.deeply_resolve_ignoring_regions(infer_term); let normalization_was_ambiguous = match result.certainty { @@ -69,19 +85,12 @@ where }; Ok((normalized, normalization_was_ambiguous)) }); - if let Ok(value) = value.try_fold_with(&mut folder) { - let obligations = stalled_goals - .into_iter() - .map(|goal| { - Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate) - }) - .collect(); - Normalized { value, obligations } - } else { - let mut replacer = ReplaceAliasWithInfer { at, obligations: Default::default(), universes }; - let value = original_value.fold_with(&mut replacer); - Normalized { value, obligations: replacer.obligations } - } + let value = value.try_fold_with(&mut folder)?; + let obligations = stalled_goals + .into_iter() + .map(|goal| Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate)) + .collect(); + Ok(Normalized { value, obligations }) } struct ReplaceAliasWithInfer<'me, 'tcx> { @@ -134,6 +143,8 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { if ty.has_escaping_bound_vars() { let (replaced, ..) = BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, alias); + // Keep the higher-ranked alias in the folded value; the fresh term is only + // used to register its projection obligation. let _ = self.term_to_infer(replaced.into()); ty } else { @@ -158,6 +169,8 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { &mut self.universes, alias_const, ); + // Keep the higher-ranked alias in the folded value; the fresh term is only + // used to register its projection obligation. let _ = self.term_to_infer(replaced.into()); ct } else { @@ -222,7 +235,10 @@ where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { - let Normalized { value, obligations } = normalize_with_universes(at, value, universes); + let Normalized { value, obligations } = normalize_with_universes(at, value, universes) + .map_err(|obligation| { + thin_vec![E::from_solver_error(at.infcx, NextSolverError::TrueError(obligation))] + })?; let mut fulfill_cx = FulfillmentCtxt::new(at.infcx); for pred in obligations { diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.current.stderr similarity index 88% rename from tests/ui/associated-inherent-types/issue-111404-1.stderr rename to tests/ui/associated-inherent-types/issue-111404-1.current.stderr index 9a5b69497c0cf..b77b3c00a69f9 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.current.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-111404-1.rs:10:11 + --> $DIR/issue-111404-1.rs:14:11 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other @@ -8,7 +8,7 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} found struct `Foo fn(&'b ())>` error[E0308]: mismatched types - --> $DIR/issue-111404-1.rs:10:11 + --> $DIR/issue-111404-1.rs:14:11 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other @@ -18,13 +18,13 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:1 + --> $DIR/issue-111404-1.rs:14:1 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:1 + --> $DIR/issue-111404-1.rs:14:1 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:8 + --> $DIR/issue-111404-1.rs:14:8 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^ diff --git a/tests/ui/associated-inherent-types/issue-111404-1.next.stderr b/tests/ui/associated-inherent-types/issue-111404-1.next.stderr new file mode 100644 index 0000000000000..812a6ee052d29 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-111404-1.next.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `Foo::Assoc == &()` + --> $DIR/issue-111404-1.rs:14:11 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index cad6d48b1c5af..a23856d0eac8d 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + #![feature(inherent_associated_types)] #![allow(incomplete_features)] @@ -8,10 +12,11 @@ impl<'a> Foo { } fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} -//~^ ERROR mismatched types [E0308] -//~| ERROR mismatched types [E0308] -//~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error +//[current]~^ ERROR mismatched types [E0308] +//[current]~| ERROR mismatched types [E0308] +//[current]~| ERROR higher-ranked subtype error +//[current]~| ERROR higher-ranked subtype error +//[current]~| ERROR higher-ranked subtype error +//[next]~^^^^^^ ERROR type mismatch resolving fn main() {} diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs index 17a81b5705dfb..6acc1baf319ef 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs @@ -11,8 +11,6 @@ impl<'a> Foo { } fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} -//~^ ERROR: higher-ranked subtype error -//~| ERROR: lifetime bound not satisfied [E0478] -//~| ERROR: lifetime bound not satisfied [E0478] +//~^ ERROR type mismatch resolving fn main() {} diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr index 47227014ec567..4a032c267e80a 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr @@ -1,23 +1,9 @@ -error[E0478]: lifetime bound not satisfied +error[E0271]: type mismatch resolving `Foo::Assoc == &()` --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:11 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ -error[E0478]: lifetime bound not satisfied - --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:11 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: higher-ranked subtype error - --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:8 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0478`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.rs b/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.rs new file mode 100644 index 0000000000000..de94cad403a3a --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.rs @@ -0,0 +1,34 @@ +//@ needs-rustc-debug-assertions +//@ compile-flags: -Znext-solver + +// Regression test for #160875. +// +// Eagerly normalizing the inner alias makes the outer alias fail the leak check: +// `Foo fn(&'b ())>` cannot be equated with the impl's `Foo`. +// Deep normalization must propagate that failure. Retrying the original aliases +// with `ReplaceAliasWithInfer` instead moved the lifetime equalities into nested +// goals, whose constraints were invisible to the parent's leak check. +// +// The fallback incorrectly succeeded and returned `fn(?3t)`. Resolving that value +// only hid the failed leak check. With debug assertions enabled, returning the +// unresolved value also triggered an assertion in `normalize_erasing_regions`. +// +// The invalid const body reaches MIR signature normalization in metadata-only +// tests. Keep debug assertions enabled to catch the original ICE as well. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Foo(T); + +impl<'a> Foo { + type Assoc = &'a (); +} + +const fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) { + //~^ ERROR type mismatch resolving + x + //~^ ERROR cannot find value `x` in this scope +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.stderr b/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.stderr new file mode 100644 index 0000000000000..680e5f702b423 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/deeply-normalize-fallback-issue-160875.stderr @@ -0,0 +1,16 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/deeply-normalize-fallback-issue-160875.rs:30:5 + | +LL | x + | ^ not found in this scope + +error[E0271]: type mismatch resolving `Foo::Assoc == &()` + --> $DIR/deeply-normalize-fallback-issue-160875.rs:28:17 + | +LL | const fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0425. +For more information about an error, try `rustc --explain E0271`. From 713fef39f2e674373353f47da592868f5f1cf68a Mon Sep 17 00:00:00 2001 From: Adwin White Date: Sun, 20 Sep 2026 17:38:00 +0800 Subject: [PATCH 03/12] stop computing overflowed goal chain for crate dependencies --- compiler/rustc_trait_selection/src/solve/delegate.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index c67a4bdd329b0..eee6714e64fcd 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -521,8 +521,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< predicates: vec![], recursion_limit: usize::min(16, tcx.recursion_limit().0), }; - let _ = self - .with_disabled_next_solver_overflow_fcw(|| self.visit_proof_tree(goal, &mut visitor)); + + // HACK: avoid computing goal chains for dependencies by relying on the fact that + // `cargo` passes `lint_cap=allow` to deps. This should mitigate some of the perf/rss + // regression when compiling crates whose deps trigger a large number of these FCWs. + if !matches!(tcx.sess.opts.lint_cap, Some(rustc_lint_defs::Level::Allow)) { + let _ = self.with_disabled_next_solver_overflow_fcw(|| { + self.visit_proof_tree(goal, &mut visitor) + }); + } + tcx.emit_node_span_lint( RECURSION_DEPTH_EXCEEDING_LIMIT, CRATE_HIR_ID, From 49577f8f4555c6e1f140958b2f6caf714266aa8f Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 26 Sep 2026 14:24:16 -0400 Subject: [PATCH 04/12] GVN transmutes of Immediate::Uninit to Immediate::Uninit --- compiler/rustc_mir_transform/src/gvn.rs | 3 ++ tests/mir-opt/const_prop/maybe_uninit.rs | 7 ++++ ...maybe_uninit.u8_array_assume_init.GVN.diff | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/mir-opt/const_prop/maybe_uninit.u8_array_assume_init.GVN.diff diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 81a41be1c62da..b5fa022a527e5 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -725,6 +725,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } CastKind::Transmute | CastKind::Subtype => { let value = self.eval_to_const(value)?; + if value.is_immediate_uninit() { + return Some(ImmTy::uninit(ty).into()); + } // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do // limited transmutes: it only works between types with the same layout, and diff --git a/tests/mir-opt/const_prop/maybe_uninit.rs b/tests/mir-opt/const_prop/maybe_uninit.rs index 649fea6252216..89c395a7be5bb 100644 --- a/tests/mir-opt/const_prop/maybe_uninit.rs +++ b/tests/mir-opt/const_prop/maybe_uninit.rs @@ -8,3 +8,10 @@ pub fn u8_array() -> [MaybeUninit; 8] { // CHECK: _0 = const ; [MaybeUninit::uninit(); 8] } + +// EMIT_MIR maybe_uninit.u8_array_assume_init.GVN.diff +pub fn u8_array_assume_init() -> [MaybeUninit; 8] { + // CHECK: fn u8_array_assume_init( + // CHECK: _0 = const ; + unsafe { MaybeUninit::uninit().assume_init() } +} diff --git a/tests/mir-opt/const_prop/maybe_uninit.u8_array_assume_init.GVN.diff b/tests/mir-opt/const_prop/maybe_uninit.u8_array_assume_init.GVN.diff new file mode 100644 index 0000000000000..e0c4af329fadc --- /dev/null +++ b/tests/mir-opt/const_prop/maybe_uninit.u8_array_assume_init.GVN.diff @@ -0,0 +1,36 @@ +- // MIR for `u8_array_assume_init` before GVN ++ // MIR for `u8_array_assume_init` after GVN + + fn u8_array_assume_init() -> [MaybeUninit; 8] { + let mut _0: [std::mem::MaybeUninit; 8]; + let mut _1: std::mem::MaybeUninit<[std::mem::MaybeUninit; 8]>; + scope 1 (inlined MaybeUninit::<[MaybeUninit; 8]>::uninit) { + } + scope 2 (inlined #[track_caller] MaybeUninit::<[MaybeUninit; 8]>::assume_init) { + scope 3 (inlined transmute_neo::; 8]>, [MaybeUninit; 8]>) { + } + } + + bb0: { + StorageLive(_1); +- _1 = MaybeUninit::<[MaybeUninit; 8]> { uninit: const () }; ++ _1 = const ; + goto -> bb1; + } + + bb1: { +- _0 = copy _1 as [std::mem::MaybeUninit; 8] (Transmute); ++ _0 = const ; + StorageDead(_1); + return; + } ++ } ++ ++ ALLOC0 (size: 8, align: 1) { ++ __ __ __ __ __ __ __ __ │ ░░░░░░░░ ++ } ++ ++ ALLOC1 (size: 8, align: 1) { ++ __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } + From 3c0c34f3acd6a29e9f12ee906e03b4aa5111d9a7 Mon Sep 17 00:00:00 2001 From: Max Dexheimer Date: Thu, 1 Oct 2026 10:56:51 +0200 Subject: [PATCH 05/12] Make `AllocatorNightly` less clever --- library/alloc/src/alloc.rs | 22 ---------------------- library/alloc/src/boxed.rs | 5 ++++- library/alloc/src/rcs/arc.rs | 9 +++++++-- library/alloc/src/rcs/rc.rs | 9 +++++++-- library/core/src/alloc/mod.rs | 26 ++++++++++++++++++++++++++ library/std/src/alloc.rs | 4 ++++ 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index de65820cf9a75..a64cbb5785a03 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -703,27 +703,5 @@ pub mod __alloc_error_handler { } } -/// Allocator marker trait that is implemented only on `Global`, except when -/// the allocator feature gate is enabled (in which case it is implemented -/// for all allocators). -/// -/// This is to prevent stable code from e.g. constructing `Arc` -/// using the `From> for Arc` impl. -/// -/// Note that this trait cannot appear in specialization impls (even if not -/// specialized on). -/// -/// This trait should be used as a bound whenever a function constructing -/// a type with an `#[unstable] A: Allocator = Global` parameter may be -/// callable for `A != Global`. -#[marker] -#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] -#[doc(hidden)] -pub trait AllocatorNightly: Allocator {} - -#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] -#[unstable_feature_bound(allocator_ext)] -impl AllocatorNightly for A {} - #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] impl AllocatorNightly for Global {} diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 7504763120532..36af491af1bab 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -206,7 +206,7 @@ use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -use crate::alloc::{AllocError, Allocator, Global, Layout, StaticAllocator}; +use crate::alloc::{AllocError, Allocator, AllocatorNightly, Global, Layout, StaticAllocator}; use crate::raw_vec::RawVec; #[cfg(not(no_global_oom_handling))] use crate::str::from_boxed_utf8_unchecked_in; @@ -2541,6 +2541,9 @@ unsafe impl Allocator for Box { unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for Box {} #[unstable(feature = "random", issue = "130703")] impl core::random::Rng for Box { diff --git a/library/alloc/src/rcs/arc.rs b/library/alloc/src/rcs/arc.rs index e61ea5193eccb..41e787ace9765 100644 --- a/library/alloc/src/rcs/arc.rs +++ b/library/alloc/src/rcs/arc.rs @@ -23,9 +23,11 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use core::sync::atomic::{self, Atomic}; use core::{borrow, fmt, hint}; -use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout, StaticAllocator}; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{AllocatorNightly, handle_alloc_error}; +use crate::alloc::handle_alloc_error; +use crate::alloc::{ + AllocError, Allocator, AllocatorClone, AllocatorNightly, Global, Layout, StaticAllocator, +}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; @@ -5199,6 +5201,9 @@ unsafe impl Allocator for Arc { unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for Arc {} #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl AllocatorClone for Arc {} diff --git a/library/alloc/src/rcs/rc.rs b/library/alloc/src/rcs/rc.rs index 8cca09d1d4a8c..e9b4044163001 100644 --- a/library/alloc/src/rcs/rc.rs +++ b/library/alloc/src/rcs/rc.rs @@ -266,9 +266,11 @@ use core::ptr::{self, NonNull, drop_in_place}; use core::slice::from_raw_parts_mut; use core::{borrow, fmt, hint}; -use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout, StaticAllocator}; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{AllocatorNightly, handle_alloc_error}; +use crate::alloc::handle_alloc_error; +use crate::alloc::{ + AllocError, Allocator, AllocatorClone, AllocatorNightly, Global, Layout, StaticAllocator, +}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] @@ -4792,6 +4794,9 @@ unsafe impl Allocator for Rc { unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for Rc {} #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl AllocatorClone for Rc {} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 11b9da3032ae7..9f671b739cb14 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -743,6 +743,13 @@ where } } +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl

AllocatorNightly for core::pin::Pin

where + P: core::ops::Deref + core::pin::PinSafePointer +{ +} + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl AllocatorClone for &A {} @@ -753,3 +760,22 @@ unsafe impl StaticAllocator for &A {} #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl StaticAllocator for &mut A {} + +/// Allocator marker trait that is only implemented stably for `Global`. +/// +/// This trait is used as a bound whenever a function constructing +/// a type with an `#[unstable] A: Allocator = Global` parameter +/// may be callable for `A != Global`. +/// +/// This is to prevent stable code from e.g. constructing `Arc` +/// using the `From> for Arc` impl. +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +pub trait AllocatorNightly: Allocator {} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for &A {} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for &mut A {} diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index c6f94944006c9..fdca3d12ba3d4 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -504,3 +504,7 @@ pub mod __default_lib_allocator { } } } + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for System {} From 38c6e9545ded714c2ec24f493a72579cae0856a4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 28 Sep 2026 11:39:45 +1000 Subject: [PATCH 06/12] Clarify `OnceNote`/`OnceHelp` subdiag deduplication - Introduce `Subdiag::dedup_hash`, which mirrors `DiagInner::dedup_hash` and is necessary for the subsequent commits. - Change the name and structure of `not_yet_emitted` to be clearer. - Change `retain_mut` to `retain`; mutability isn't required. --- compiler/rustc_errors/src/diagnostic.rs | 15 +++++++++++++++ compiler/rustc_errors/src/lib.rs | 16 ++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 362df06c404db..e8780cb8f4286 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -324,6 +324,21 @@ pub struct Subdiag { pub span: MultiSpan, } +impl Subdiag { + /// Hash used to determine if two subdiagnostics are the same. Used by + /// `DiagCtxtInner::emitted_diagnostics`. + pub(crate) fn dedup_hash(&self) -> Hash128 { + // Deconstruct to ensure all fields are considered. + let Subdiag { level, messages, span } = self; + + let hashed_parts = (level, messages, span); + + let mut hasher = StableHasher::new(); + hashed_parts.hash(&mut hasher); + hasher.finish() + } +} + /// Used for emitting structured error messages and other diagnostic information. /// Wraps a `DiagInner`, adding some useful things. /// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 4cc5d5c230d15..47473b568f58c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -44,7 +44,6 @@ use emitter::{DynEmitter, Emitter}; use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_data_structures::stable_hash::StableHasher; use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ DiagArg, DiagArgFromDisplay, DiagArgMap, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg, @@ -1321,21 +1320,18 @@ impl DiagCtxtInner { debug!(?diagnostic); debug!(?self.emitted_diagnostics); - let not_yet_emitted = |sub: &mut Subdiag| { + let show_sub = |sub: &Subdiag| { debug!(?sub); match sub.level { Sublevel::Error | Sublevel::Warning | Sublevel::Note | Sublevel::Help => { - return true; + true + } + Sublevel::OnceNote | Sublevel::OnceHelp => { + self.emitted_diagnostics.insert(sub.dedup_hash()) } - Sublevel::OnceNote | Sublevel::OnceHelp => {} } - let mut hasher = StableHasher::new(); - sub.hash(&mut hasher); - let diagnostic_hash = hasher.finish(); - debug!(?diagnostic_hash); - self.emitted_diagnostics.insert(diagnostic_hash) }; - diagnostic.children.retain_mut(not_yet_emitted); + diagnostic.children.retain(show_sub); if already_emitted { let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; diagnostic.sub(Sublevel::Note, msg, MultiSpan::new()); From f8df28d4555660e74a9e015f24804af0786b9413 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 28 Sep 2026 14:36:52 +1000 Subject: [PATCH 07/12] Add a test demonstrating the problem Currently deduplication erroneously has no effect with incremental enabled because the span parent gets included in the hash used for deduplication. --- .../deduplicate-diagnostics-incr.dedup.stderr | 15 ++++++ .../deduplicate-diagnostics-incr.dup.stderr | 54 +++++++++++++++++++ ...plicate-diagnostics-incr.incr-dedup.stderr | 51 ++++++++++++++++++ ...duplicate-diagnostics-incr.incr-dup.stderr | 51 ++++++++++++++++++ .../deduplicate-diagnostics-incr.rs | 17 ++++++ 5 files changed, 188 insertions(+) create mode 100644 tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dedup.stderr create mode 100644 tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dup.stderr create mode 100644 tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr create mode 100644 tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr create mode 100644 tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dedup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dedup.stderr new file mode 100644 index 0000000000000..e8fb5732febdc --- /dev/null +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dedup.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dup.stderr new file mode 100644 index 0000000000000..017884aff53a4 --- /dev/null +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.dup.stderr @@ -0,0 +1,54 @@ +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr new file mode 100644 index 0000000000000..8cfd815b10d5f --- /dev/null +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr @@ -0,0 +1,51 @@ +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr new file mode 100644 index 0000000000000..8cfd815b10d5f --- /dev/null +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr @@ -0,0 +1,51 @@ +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied + --> $DIR/deduplicate-diagnostics-incr.rs:11:11 + | +LL | #[global_allocator] + | ------------------- in this attribute macro expansion +LL | static A: usize = 0; + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` + | +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs new file mode 100644 index 0000000000000..daa42492686db --- /dev/null +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs @@ -0,0 +1,17 @@ +//! Test that span parents (which are incremental only) are ignored when deduplicating diagnostics. +//! Regression test for #162901. + +//@ revisions: dedup dup incr-dedup incr-dup +//@[dedup] compile-flags: -Z deduplicate-diagnostics=yes +//@[incr-dup] incremental +//@[incr-dedup] incremental +//@[incr-dedup] compile-flags: -Z deduplicate-diagnostics=yes + +#[global_allocator] +static A: usize = 0; +//[dedup,dup,incr-dedup,incr-dup]~^ ERROR E0277 +//[dup,incr-dedup,incr-dup]~| ERROR E0277 +//[dup,incr-dedup,incr-dup]~| ERROR E0277 +//[dup,incr-dedup,incr-dup]~| ERROR E0277 + +fn main() {} From 0efd8697df1eded0de98c28d2005a84a4d5bc46e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 28 Sep 2026 22:22:56 +1000 Subject: [PATCH 08/12] Fix `encoder_methods!` macro So it applies the `#[inline]` to every method, not just the first one. --- compiler/rustc_middle/src/query/on_disk_cache.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 20c1c5c16c95c..738521b2005e0 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -929,10 +929,12 @@ impl<'tcx> TyEncoder<'tcx> for CacheEncoder<'tcx> { macro_rules! encoder_methods { ($($name:ident($ty:ty);)*) => { - #[inline] - $(fn $name(&mut self, value: $ty) { - self.encoder.$name(value) - })* + $( + #[inline] + fn $name(&mut self, value: $ty) { + self.encoder.$name(value) + } + )* } } From 2599fb7e71f472b13f87c6d39d09f3df5ff55434 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 25 Sep 2026 23:49:09 +0200 Subject: [PATCH 09/12] Correctly handle `rustc_allow_incoherent_impl` on primitive methods --- src/librustdoc/html/format.rs | 16 +++++++++++- .../auxiliary/incoherent_impl_primitive.rs | 7 ++++++ .../jump-to-def/incoherent_impl-primitive.rs | 25 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs create mode 100644 tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index da88172c688f5..af7c6410a55b3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -21,7 +21,7 @@ use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_span::symbol::kw; -use rustc_span::{Ident, Symbol}; +use rustc_span::{Ident, Symbol, sym}; use tracing::{debug, trace}; use super::url_parts_builder::UrlPartsBuilder; @@ -537,11 +537,25 @@ fn generate_item_def_id_path( && !def_id.is_local() && def_id.krate != original_def_id.krate; } else { + // This hack is because primitive types are only documented in `core` and `std`. + // However, with `#[rustc_allow_incoherent_impl]`, a lot of primitive methods are + // implemented in `core`. Some of them are documented in `core` (like `[]::sort`) while + // others aren't (like `[]::to_vec`). So in case we're not documenting `core`, we link + // to `std`. + if !of_trait && crate_name != sym::core { + if ![sym::alloc, sym::std].contains(&crate_name) { + // We cannot link to this primitive's associated item as it's not part of + // `core`, `alloc` or `std` so returning early. + return Err(HrefError::UnnamableItem); + } + maybe_have_impl_not_in_def_crate = true; + } prim = PrimitiveType::from_ty(ty); } } let (shortty, fqp) = if let Some(prim) = prim { + let crate_name = if maybe_have_impl_not_in_def_crate { sym::std } else { crate_name }; (ItemType::Primitive, vec![crate_name, prim.as_sym()]) } else { ( diff --git a/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs new file mode 100644 index 0000000000000..98e9a5129417c --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs @@ -0,0 +1,7 @@ +#![feature(rustc_attrs)] +#![allow(internal_features)] + +impl [T] { + #[rustc_allow_incoherent_impl] + pub fn f(&self) {} +} diff --git a/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs b/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs new file mode 100644 index 0000000000000..9a4609953a178 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs @@ -0,0 +1,25 @@ +// This test checks that outside of `core`/`alloc`/`std`, we don't link to incoherent method impl +// because there is no page to link to since primitive are only documented in `core` and `std`. + +//@ aux-build: incoherent_impl_primitive.rs +//@ build-aux-docs +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +extern crate incoherent_impl_primitive; + +//@ has 'src/foo/incoherent_impl-primitive.rs.html' + +// It should contain only the link to "first". Sadly, because of the limit, we cannot test +// if a link doesn't have an attribute so for now, we simply ensure that the number of `` +// (including both line numbers and jump to def links) is superior by one to the number of +// line ``. If so, we know there is only one jump to def link. +//@ count - '//pre/code/a' 7 +//@ count - '//pre/code/a[@id]' 6 +//@ has - '//pre/code/a[@href="{{channel}}/core/primitive.slice.html#method.first"]' 'first' + +fn foo() { + let x = ["a"].f(); + ["a"].first(); +} From f532587cb4a3e2f52ab0107a509f9cafe7a11aa9 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Thu, 1 Oct 2026 18:23:22 +0700 Subject: [PATCH 10/12] Add union pattern reference change to relnotes --- RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASES.md b/RELEASES.md index 77b58ce2bcf25..cd1f3ca7c3800 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -116,6 +116,7 @@ Compatibility Notes - [`semicolon_in_expressions_from_macros`: Lint on non-local macros too](https://github.com/rust-lang/rust/pull/159222) - [Split non-local `semicolon_in_expressions_from_macros` into a separate lint](https://github.com/rust-lang/rust/pull/159700) - In Cargo, incremental compilation is now disabled by default when running in CI. CI is detected via the `CI` environment variable. [#17220](https://github.com/rust-lang/cargo/pull/17220) +- [The Rust Reference no longer recommends using a single pattern to match a union alongside another value, such as in a manually-written tagged union](https://github.com/rust-lang/reference/pull/2303). This was because it was found that such code would, in some circumstances, result in the compiler reading the contents of the union before checking if the rest of the scrutinee matches the rest of the pattern. This can cause undefined behavior in code similar to what the reference previously recommended. From 434157508f02ec9092907d63e220e971d6dc34ad Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 28 Sep 2026 15:42:03 +1000 Subject: [PATCH 11/12] Improve diagnostic deduplication By making it ignore the parent field of spans within the diagnostic. This is done with a new `DedupHashEncoder` type that uses the existing `Encoder`/`SpanEncoder` traits to traverse the fields within `DiagInner` and `Subdiag`. Fixes #162901. --- compiler/rustc_errors/src/dedup_hash.rs | 63 +++++++++++++++++++ compiler/rustc_errors/src/diagnostic.rs | 41 ++++++------ compiler/rustc_errors/src/lib.rs | 1 + ...plicate-diagnostics-incr.incr-dedup.stderr | 38 +---------- ...duplicate-diagnostics-incr.incr-dup.stderr | 3 + .../deduplicate-diagnostics-incr.rs | 6 +- typos.toml | 1 + 7 files changed, 92 insertions(+), 61 deletions(-) create mode 100644 compiler/rustc_errors/src/dedup_hash.rs diff --git a/compiler/rustc_errors/src/dedup_hash.rs b/compiler/rustc_errors/src/dedup_hash.rs new file mode 100644 index 0000000000000..b8b2da69bc1d3 --- /dev/null +++ b/compiler/rustc_errors/src/dedup_hash.rs @@ -0,0 +1,63 @@ +use std::hash::Hash; + +use rustc_data_structures::stable_hash::StableHasher; +use rustc_serialize::Encoder; +use rustc_span::def_id::{CrateNum, DefId, DefIndex}; +use rustc_span::{ByteSymbol, ExpnId, Span, SpanEncoder, Symbol, SyntaxContext}; + +/// Hasher used to determine if two diagnostics are the same and should be deduplicated. Used by +/// `DiagCtxtInner::emitted_diagnostics`. It's a stable hash with one special behaviour: parents of +/// spans within the diagnostics are ignored so that incremental and non-incremental compilation +/// get the same behaviour. +/// +/// Although this has nothing to do with encoding data to/from file, it is implemented on top of +/// `Encoder`/`SpanEncoder` because they provide traversals of all the relevant types used within +/// diagnostics. There is no corresponding decoder. +pub(crate) struct DedupHashEncoder(pub(crate) StableHasher); + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + $( + #[inline] + fn $name(&mut self, value: $ty) { + value.hash(&mut self.0) + } + )* + } +} + +impl Encoder for DedupHashEncoder { + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + + emit_raw_bytes(&[u8]); + } +} + +impl SpanEncoder for DedupHashEncoder { + fn encode_span(&mut self, span: Span) { + // The raison d'être of `DedupHashEncoder` is to ignore `parent` here. + span.with_parent(None).hash(&mut self.0); + } + + encoder_methods! { + encode_symbol(Symbol); + encode_byte_symbol(ByteSymbol); + encode_expn_id(ExpnId); + encode_syntax_context(SyntaxContext); + encode_crate_num(CrateNum); + encode_def_index(DefIndex); + encode_def_id(DefId); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e8780cb8f4286..65dc8042fd409 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -12,9 +12,11 @@ use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg}; use rustc_hashes::Hash128; use rustc_lint_defs::Applicability; use rustc_macros::{Decodable, Encodable}; +use rustc_serialize::Encodable; use rustc_span::{Span, Spanned, Symbol}; use tracing::debug; +use crate::dedup_hash::DedupHashEncoder; use crate::{ CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, MultiSpan, StashKey, Style, Sublevel, Substitution, SubstitutionPart, SuggestionStyle, @@ -298,20 +300,22 @@ impl DiagInner { emitted_at: _, // ignore } = self; - let hashed_parts = ( - std::mem::discriminant(level), // ignore the field within `Warning` - messages, - code, - span, - children, - suggestions, - args.as_slice(), - is_lint, - ); + let mut e = DedupHashEncoder(StableHasher::new()); + + // We use `discriminant` because we want to ignore the field within `Warning`. + // `Encoder`/`SpanEncoder` can't traverse a `Discriminant` so we hash it directly. + std::mem::discriminant(level).hash(&mut e.0); + + // All the other fields are hashed via `encode`. + messages.encode(&mut e); + code.encode(&mut e); + span.encode(&mut e); + children.encode(&mut e); + suggestions.encode(&mut e); + args.encode(&mut e); + is_lint.encode(&mut e); - let mut hasher = StableHasher::new(); - hashed_parts.hash(&mut hasher); - hasher.finish() + e.0.finish() } } @@ -328,14 +332,9 @@ impl Subdiag { /// Hash used to determine if two subdiagnostics are the same. Used by /// `DiagCtxtInner::emitted_diagnostics`. pub(crate) fn dedup_hash(&self) -> Hash128 { - // Deconstruct to ensure all fields are considered. - let Subdiag { level, messages, span } = self; - - let hashed_parts = (level, messages, span); - - let mut hasher = StableHasher::new(); - hashed_parts.hash(&mut hasher); - hasher.finish() + let mut e = DedupHashEncoder(StableHasher::new()); + self.encode(&mut e); + e.0.finish() } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 47473b568f58c..a1ccefefbcb2a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -69,6 +69,7 @@ use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; mod decorate_diag; +mod dedup_hash; mod diagnostic; mod diagnostic_impls; pub mod emitter; diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr index 8cfd815b10d5f..e8fb5732febdc 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dedup.stderr @@ -10,42 +10,6 @@ help: the trait `GlobalAllocator` is implemented for `System` --> $SRC_DIR/std/src/alloc.rs:LL:COL = note: required for `usize` to implement `GlobalAlloc` -error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied - --> $DIR/deduplicate-diagnostics-incr.rs:11:11 - | -LL | #[global_allocator] - | ------------------- in this attribute macro expansion -LL | static A: usize = 0; - | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` - | -help: the trait `GlobalAllocator` is implemented for `System` - --> $SRC_DIR/std/src/alloc.rs:LL:COL - = note: required for `usize` to implement `GlobalAlloc` - -error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied - --> $DIR/deduplicate-diagnostics-incr.rs:11:11 - | -LL | #[global_allocator] - | ------------------- in this attribute macro expansion -LL | static A: usize = 0; - | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` - | -help: the trait `GlobalAllocator` is implemented for `System` - --> $SRC_DIR/std/src/alloc.rs:LL:COL - = note: required for `usize` to implement `GlobalAlloc` - -error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied - --> $DIR/deduplicate-diagnostics-incr.rs:11:11 - | -LL | #[global_allocator] - | ------------------- in this attribute macro expansion -LL | static A: usize = 0; - | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` - | -help: the trait `GlobalAllocator` is implemented for `System` - --> $SRC_DIR/std/src/alloc.rs:LL:COL - = note: required for `usize` to implement `GlobalAlloc` - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr index 8cfd815b10d5f..017884aff53a4 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.incr-dup.stderr @@ -21,6 +21,7 @@ LL | static A: usize = 0; help: the trait `GlobalAllocator` is implemented for `System` --> $SRC_DIR/std/src/alloc.rs:LL:COL = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/deduplicate-diagnostics-incr.rs:11:11 @@ -33,6 +34,7 @@ LL | static A: usize = 0; help: the trait `GlobalAllocator` is implemented for `System` --> $SRC_DIR/std/src/alloc.rs:LL:COL = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/deduplicate-diagnostics-incr.rs:11:11 @@ -45,6 +47,7 @@ LL | static A: usize = 0; help: the trait `GlobalAllocator` is implemented for `System` --> $SRC_DIR/std/src/alloc.rs:LL:COL = note: required for `usize` to implement `GlobalAlloc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs index daa42492686db..72bed90bba4c7 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics-incr.rs @@ -10,8 +10,8 @@ #[global_allocator] static A: usize = 0; //[dedup,dup,incr-dedup,incr-dup]~^ ERROR E0277 -//[dup,incr-dedup,incr-dup]~| ERROR E0277 -//[dup,incr-dedup,incr-dup]~| ERROR E0277 -//[dup,incr-dedup,incr-dup]~| ERROR E0277 +//[dup,incr-dup]~| ERROR E0277 +//[dup,incr-dup]~| ERROR E0277 +//[dup,incr-dup]~| ERROR E0277 fn main() {} diff --git a/typos.toml b/typos.toml index 8976759b3b259..215a971527e85 100644 --- a/typos.toml +++ b/typos.toml @@ -35,6 +35,7 @@ numer = "numer" # short for numerator, not a typo for "number" old-skool = "old-skool" # variant spelling of "old-school" optin = "optin" # short for opt-in publically = "publically" +raison = "raison" # part of the French phrase "raison d'être" rplace = "rplace" # short for R-place splitted = "splitted" sythetic = "sythetic" # typo in vendored LLVM sources From 7ede2187bab13dcc8a8ed17161d95a54aef3619e Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Thu, 1 Oct 2026 13:12:17 +0000 Subject: [PATCH 12/12] wfcheck: name the item that discards an unused type parameter * Name the item that discards an unused type parameter E0392 pointed at the mention of the parameter with a label saying it "is likely unused in the containing type", which doesn't say much. It's unclear what the containing type is meant to be, and we hedge with "likely" even though we can just go look. Track the outermost item that throws the argument away while collecting the usage spans, and name it instead. For ADTs that's a bivariant parameter in `variances_of`. Type aliases have no variances of their own, so expand them and check whether the parameter reaches a position that constrains it -- an alias can mention the parameter and still discard it by handing it to something else that ignores it. Free aliases go through `expand_free_alias_tys` rather than `type_of`, since a diverging one would otherwise recurse forever. When none of that pins down a culprit, fall back to saying the mention doesn't constrain the parameter instead of guessing. * Cover free alias types in the unused type parameter error A chain of free aliases used to hide the item doing the discarding, and the same alias applied to different arguments has to be expanded twice or a use of the parameter goes missing. The overflow case is worth a test of its own: with a growing argument there's no repeated alias to spot the divergence with, so the recursion limit is the only thing that stops the walk. * Simplify the unused type parameter diagnostic Variance and associated-type constraints are checked before this diagnostic runs. Restore the span collector and use a definite label without identifying which wrapper discards the parameter. Update the diagnostic expectations and test comments accordingly. --- .../rustc_hir_analysis/src/diagnostics.rs | 2 +- tests/ui/checked-type-alias/variance-1.stderr | 2 +- .../checked-type-alias/variance-overflow.rs | 11 +++ .../variance-overflow.stderr | 29 +++++++- ...riance-unused-param-through-alias-chain.rs | 25 +++++++ ...ce-unused-param-through-alias-chain.stderr | 34 +++++++++ .../ui/variance/variance-unused-type-param.rs | 25 +++++++ .../variance-unused-type-param.stderr | 70 ++++++++++++++++++- 8 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.rs create mode 100644 tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.stderr diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 890d25f14b264..1b3d238b390bc 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -1737,7 +1737,7 @@ pub(crate) struct UnusedGenericParameter { pub span: Span, pub param_name: Ident, pub param_def_kind: &'static str, - #[label("`{$param_name}` is named here, but is likely unused in the containing type")] + #[label("`{$param_name}` is named here, but is not used in the type that wraps it")] pub usage_spans: Vec, #[subdiagnostic] pub help: UnusedGenericParameterHelp, diff --git a/tests/ui/checked-type-alias/variance-1.stderr b/tests/ui/checked-type-alias/variance-1.stderr index 5eb2f5d0673aa..40b59b43910e8 100644 --- a/tests/ui/checked-type-alias/variance-1.stderr +++ b/tests/ui/checked-type-alias/variance-1.stderr @@ -10,7 +10,7 @@ error[E0392]: type parameter `T` is never used --> $DIR/variance-1.rs:17:24 | LL | struct WrapDiscard<'a, T>(Discard<'a, T>); - | ^ - `T` is named here, but is likely unused in the containing type + | ^ - `T` is named here, but is not used in the type that wraps it | | | unused type parameter | diff --git a/tests/ui/checked-type-alias/variance-overflow.rs b/tests/ui/checked-type-alias/variance-overflow.rs index 0b5b58e083133..3da7da1ed425d 100644 --- a/tests/ui/checked-type-alias/variance-overflow.rs +++ b/tests/ui/checked-type-alias/variance-overflow.rs @@ -19,4 +19,15 @@ struct First(Second); type Second = Second; // diverging free alias type //~^ ERROR overflow normalizing the type alias `Second` +// The same applies when a diverging alias mentions a type parameter. The argument grows with +// every step, so there is no repeated `(DefId, args)` pair to detect the divergence with. +// Variance inference must stop at the recursion limit, and reporting the unused parameter +// must not restart the expansion. +type Diverging = Diverging<(T,)>; +//~^ ERROR overflow normalizing the type alias `Diverging< + +struct Wrap(Diverging); +//~^ ERROR type parameter `T` is never used +//~| ERROR overflow normalizing the type alias `Diverging< + fn main() {} diff --git a/tests/ui/checked-type-alias/variance-overflow.stderr b/tests/ui/checked-type-alias/variance-overflow.stderr index 35486ccdc169f..3ea61d9ade9f3 100644 --- a/tests/ui/checked-type-alias/variance-overflow.stderr +++ b/tests/ui/checked-type-alias/variance-overflow.stderr @@ -23,7 +23,34 @@ LL | type Second = Second; // diverging free alias type | = note: in case this is a recursive type alias, consider using a struct, enum, or union instead -error: aborting due to 3 previous errors +error[E0275]: overflow normalizing the type alias `Diverging<(((((((_,),),),),),),)>` + --> $DIR/variance-overflow.rs:26:1 + | +LL | type Diverging = Diverging<(T,)>; + | ^^^^^^^^^^^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-overflow.rs:29:13 + | +LL | struct Wrap(Diverging); + | ^ - `T` is named here, but is not used in the type that wraps it + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0275]: overflow normalizing the type alias `Diverging<(((((((_,),),),),),),)>` + --> $DIR/variance-overflow.rs:29:16 + | +LL | struct Wrap(Diverging); + | ^^^^^^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0275, E0392. For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.rs b/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.rs new file mode 100644 index 0000000000000..22542e4142fb5 --- /dev/null +++ b/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.rs @@ -0,0 +1,25 @@ +// A mention forwarded through free aliases still does not count as a use when the final type +// ignores the parameter. The diagnostic should label the original mention even though the +// intermediate aliases mention the parameter too. + +#![feature(checked_type_aliases)] + +struct Ignores {} +//~^ ERROR type parameter `T` is never used + +type Forward = Discard; +type Discard = Ignores; + +struct Wrap(Forward); +//~^ ERROR type parameter `T` is never used + +// An alias can occur with different arguments in the same type. Here `Repeat` preserves its +// parameter through `Id`, so the enclosing struct uses `T` only recursively. The unrelated +// `Id` must not hide that recursive use. +type Id = T; +type Repeat = (Id, Id); + +struct Recursive(Repeat>>); +//~^ ERROR type parameter `T` is only used recursively + +fn main() {} diff --git a/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.stderr b/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.stderr new file mode 100644 index 0000000000000..069916d57b514 --- /dev/null +++ b/tests/ui/checked-type-alias/variance-unused-param-through-alias-chain.stderr @@ -0,0 +1,34 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-param-through-alias-chain.rs:7:16 + | +LL | struct Ignores {} + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-param-through-alias-chain.rs:13:13 + | +LL | struct Wrap(Forward); + | ^ - `T` is named here, but is not used in the type that wraps it + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: type parameter `T` is only used recursively + --> $DIR/variance-unused-param-through-alias-chain.rs:22:42 + | +LL | struct Recursive(Repeat>>); + | - ^ + | | + | type parameter must be used non-recursively in the definition + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = note: all type parameters must be used in a non-recursive way in order to constrain their variance + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0392`. diff --git a/tests/ui/variance/variance-unused-type-param.rs b/tests/ui/variance/variance-unused-type-param.rs index ef3c41ca5560c..bad994b5e280d 100644 --- a/tests/ui/variance/variance-unused-type-param.rs +++ b/tests/ui/variance/variance-unused-type-param.rs @@ -33,4 +33,29 @@ struct DoubleNothing { s: SomeStruct, } +// A mention nested inside another type argument still does not count as a use. +struct NestedNothing { +//~^ ERROR parameter `T` is never used + s: SomeStruct>, +} + +type Discard = (); +//~^ ERROR parameter `T` is never used + +struct ThroughTypeAlias(Discard); +//~^ ERROR parameter `T` is never used + +// Only the unused parameter should receive the diagnostic. +struct SecondUnused { a: A } +//~^ ERROR parameter `B` is never used + +struct UsesSecondUnused(SecondUnused); +//~^ ERROR parameter `T` is never used + +// The alias mentions `T`, but only to forward it to something that throws it away. +type Forward = SomeStruct; + +struct ThroughForwardingAlias(Forward); +//~^ ERROR parameter `T` is never used + fn main() {} diff --git a/tests/ui/variance/variance-unused-type-param.stderr b/tests/ui/variance/variance-unused-type-param.stderr index c747532e62836..d02addc5a5e53 100644 --- a/tests/ui/variance/variance-unused-type-param.stderr +++ b/tests/ui/variance/variance-unused-type-param.stderr @@ -69,11 +69,75 @@ LL | struct DoubleNothing { | ^ unused type parameter LL | LL | s: SomeStruct, - | - `T` is named here, but is likely unused in the containing type + | - `T` is named here, but is not used in the type that wraps it | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -error: aborting due to 8 previous errors +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:37:22 + | +LL | struct NestedNothing { + | ^ unused type parameter +LL | +LL | s: SomeStruct>, + | - `T` is named here, but is not used in the type that wraps it + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0091]: type parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:42:14 + | +LL | type Discard = (); + | ^ unused type parameter + | + = help: consider removing `T` or referring to it in the body of the type alias + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:45:25 + | +LL | struct ThroughTypeAlias(Discard); + | ^ - `T` is named here, but is not used in the type that wraps it + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0392]: type parameter `B` is never used + --> $DIR/variance-unused-type-param.rs:49:24 + | +LL | struct SecondUnused { a: A } + | ^ unused type parameter + | + = help: consider removing `B`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `B` to be a const parameter, use `const B: /* Type */` instead + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:52:25 + | +LL | struct UsesSecondUnused(SecondUnused); + | ^ - `T` is named here, but is not used in the type that wraps it + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0392]: type parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:58:31 + | +LL | struct ThroughForwardingAlias(Forward); + | ^ - `T` is named here, but is not used in the type that wraps it + | | + | unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: aborting due to 14 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0091, E0392. +For more information about an error, try `rustc --explain E0091`.