Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f2828cf
windows-gnu: enable native TLS
mati865 Jul 1, 2026
26be04b
Preserve eager normalization failures
Dnreikronos Sep 19, 2026
713fef3
stop computing overflowed goal chain for crate dependencies
adwinwhite Sep 20, 2026
49577f8
GVN transmutes of Immediate::Uninit to Immediate::Uninit
saethlin Sep 26, 2026
3c0c34f
Make `AllocatorNightly` less clever
maxdexh Oct 1, 2026
38c6e95
Clarify `OnceNote`/`OnceHelp` subdiag deduplication
nnethercote Sep 28, 2026
f8df28d
Add a test demonstrating the problem
nnethercote Sep 28, 2026
0efd869
Fix `encoder_methods!` macro
nnethercote Sep 28, 2026
2599fb7
Correctly handle `rustc_allow_incoherent_impl` on primitive methods
GuillaumeGomez Sep 25, 2026
f532587
Add union pattern reference change to relnotes
theemathas Oct 1, 2026
4341575
Improve diagnostic deduplication
nnethercote Sep 28, 2026
7ede218
wfcheck: name the item that discards an unused type parameter
Dnreikronos Oct 1, 2026
128288a
Rollup merge of #163461 - nnethercote:fix-162901, r=JonathanBrouwer
JonathanBrouwer Oct 1, 2026
5574894
Rollup merge of #159021 - mati865:mingw-native-tls, r=ChrisDenton
JonathanBrouwer Oct 1, 2026
25d09b2
Rollup merge of #161467 - Dnreikronos:wfcheck/unused_param_name_disca…
JonathanBrouwer Oct 1, 2026
43bc95b
Rollup merge of #162618 - Dnreikronos:trait_solver_preserve_normaliza…
JonathanBrouwer Oct 1, 2026
36b4f40
Rollup merge of #163064 - adwinwhite:dep-no-chain, r=lcnr
JonathanBrouwer Oct 1, 2026
f8f9740
Rollup merge of #163360 - GuillaumeGomez:primitive-incoherent-methods…
JonathanBrouwer Oct 1, 2026
1d344c2
Rollup merge of #163385 - saethlin:gvn-uninit-transmutes, r=oli-obk
JonathanBrouwer Oct 1, 2026
6b68e3b
Rollup merge of #163590 - maxdexh:allocator-nightly-sad, r=clarfonthey
JonathanBrouwer Oct 1, 2026
96f3cd5
Rollup merge of #163599 - theemathas:relnotes-union-pat, r=Mark-Simul…
JonathanBrouwer Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<a id="1.99.0-Internal-Changes"></a>

Expand Down
63 changes: 63 additions & 0 deletions compiler/rustc_errors/src/dedup_hash.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
40 changes: 27 additions & 13 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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());

let mut hasher = StableHasher::new();
hashed_parts.hash(&mut hasher);
hasher.finish()
// We use `discriminant` because we want to ignore the field within `Warning`.
// `Encoder`/`SpanEncoder` can't traverse a `Discriminant<T>` 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);

e.0.finish()
}
}

Expand All @@ -324,6 +328,16 @@ 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 {
let mut e = DedupHashEncoder(StableHasher::new());
self.encode(&mut e);
e.0.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
Expand Down
17 changes: 7 additions & 10 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -70,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;
Expand Down Expand Up @@ -1321,21 +1321,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());
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,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<Span>,
#[subdiagnostic]
pub help: UnusedGenericParameterHelp,
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_middle/src/query/on_disk_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
)*
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/base/windows_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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..
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
64 changes: 40 additions & 24 deletions compiler/rustc_trait_selection/src/solve/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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<TyCtxt<'tcx>>,
{
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
Expand All @@ -36,7 +46,7 @@ fn normalize_with_universes<'tcx, T>(
at: At<'_, 'tcx>,
value: Unnormalized<'tcx, T>,
universes: Vec<Option<UniverseIndex>>,
) -> Normalized<'tcx, T>
) -> Result<Normalized<'tcx, T>, PredicateObligation<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
Expand All @@ -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 {
Expand All @@ -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> {
Expand Down Expand Up @@ -134,6 +143,8 @@ impl<'me, 'tcx> TypeFolder<TyCtxt<'tcx>> 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 {
Expand All @@ -158,6 +169,8 @@ impl<'me, 'tcx> TypeFolder<TyCtxt<'tcx>> 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 {
Expand Down Expand Up @@ -222,7 +235,10 @@ where
T: TypeFoldable<TyCtxt<'tcx>>,
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 {
Expand Down
Loading
Loading