Skip to content
Merged
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
b4165a2
Make tidy::Version public
Kat-zorn Aug 11, 2026
7617fb9
merge ambiguity errors that blame the same inference variable
Albab-Hasan Jul 20, 2026
e27cb9b
point at the applicable impls in merged ambiguity errors
Albab-Hasan Aug 6, 2026
ac6fcec
Document tidy::features::Version re-export
Kat-zorn Aug 12, 2026
5b3e277
Remove target argument from get_proc_macros
bjorn3 Aug 12, 2026
846d935
only merge ambiguity errors that blame the same expression
Albab-Hasan Aug 12, 2026
a693668
Implement `core::cmp::{smallest, largest}`
bushrat011899 Aug 13, 2026
f7eb24d
Add const-compatible `cmp_splat` test
bushrat011899 Aug 13, 2026
85cdf35
Snapshot tests for suite paths
Zalathar Aug 10, 2026
aa512ce
Overhaul matching of command-line selectors to steps
Zalathar Aug 12, 2026
bc44db7
Replace the `exit!` macro with a function `helpers::exit_process`
Zalathar Aug 12, 2026
51ef2f2
replace infers and non-rigid aliases with `Ty/Const::Error`
adwinwhite Aug 10, 2026
d55b2be
mailmap: Update my default email
tgross35 Aug 13, 2026
8fa3465
Rollup merge of #159593 - Albab-Hasan:merge-ambiguity-errors, r=khyperia
jhpratt Aug 13, 2026
41c3350
Rollup merge of #160687 - bushrat011899:experiment_splat_min_max, r=n…
jhpratt Aug 13, 2026
1f0923d
Rollup merge of #160856 - adwinwhite:param-env-norm-fail-with-err, r=…
jhpratt Aug 13, 2026
3ad23c1
Rollup merge of #160961 - Zalathar:selectors, r=Kobzol
jhpratt Aug 13, 2026
08d99b8
Rollup merge of #160975 - bjorn3:proc_macro_refactors6, r=Mark-Simula…
jhpratt Aug 13, 2026
9143b0c
Rollup merge of #161023 - Zalathar:exit-process, r=jieyouxu,clubby789
jhpratt Aug 13, 2026
db1d7ae
Rollup merge of #160932 - Kat-zorn:main, r=jieyouxu
jhpratt Aug 13, 2026
5a3fcad
Rollup merge of #161029 - tgross35:mailmap, r=jieyouxu
jhpratt Aug 13, 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
5 changes: 3 additions & 2 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,9 @@ Tomas Koutsky <tomas@stepnivlk.net>
Tomasz Miąsko <tomasz.miasko@gmail.com>
Torsten Weber <TorstenWeber12@gmail.com>
Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
Trevor Gross <tg@trevorgross.com> <t.gross35@gmail.com>
Trevor Gross <tg@trevorgross.com> <tgross@intrepidcs.com>
Trevor Gross <tg@trevorgross.com> <tmgross@umich.edu>
Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
Tshepang Mbambo <hopsi@tuta.io> <tshepang@gmail.com>
Ty Overby <ty@pre-alpha.com>
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_metadata/src/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -970,17 +970,19 @@ fn get_flavor_from_path(path: &Path) -> CrateFlavor {
}
}

/// A function to fetch about all macros inside a proc-macro crate.
/// A function to fetch all macros inside a proc-macro crate.
///
/// Used by rust-analyzer-proc-macro-srv.
pub fn get_proc_macros(
target: &Target,
path: &Path,
metadata_loader: &dyn MetadataLoader,
cfg_version: &'static str,
) -> IoResult<Vec<(ProcMacroClient, ProcMacroKind)>> {
let host_tuple = TargetTuple::from_tuple(config::host_tuple());
let (host, _) = Target::search(&host_tuple, Path::new(""), false).unwrap();

let metadata =
get_metadata_section(target, CrateFlavor::Dylib, path, metadata_loader, cfg_version, None)
get_metadata_section(&host, CrateFlavor::Dylib, path, metadata_loader, cfg_version, None)
.map_err(|err| io::Error::other(err.to_string()))?;
let stable_crate_id = metadata.get_root().stable_crate_id();

Expand Down
195 changes: 149 additions & 46 deletions compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use tracing::{debug, instrument};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
use crate::error_reporting::traits::{FindExprBySpan, to_pretty_impl_header};
use crate::traits::ObligationCtxt;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{FulfillmentError, ObligationCtxt};

#[derive(Debug)]
pub enum CandidateSource {
Expand Down Expand Up @@ -174,10 +174,43 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
}

impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
/// The term of an ambiguous obligation's predicate that gets blamed for the
/// missing type annotation: the first one still containing inference variables.
///
/// Besides `maybe_report_ambiguity` pointing its diagnostics at this term,
/// `report_fulfillment_errors` merges the ambiguity errors whose blamed terms
/// share an inference variable into a single diagnostic.
pub(super) fn ambiguity_term(&self, predicate: ty::Predicate<'tcx>) -> Option<ty::Term<'tcx>> {
match predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => data
.trait_ref
.args
.iter()
.filter_map(ty::GenericArg::as_term)
.find(|term| term.has_non_region_infer()),
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => data
.projection_term
.args
.iter()
.filter_map(ty::GenericArg::as_term)
.chain([data.term])
.find(|term| term.has_non_region_infer()),
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => Some(term),
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
data.walk().filter_map(ty::GenericArg::as_term).find(|term| term.is_infer())
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => Some(ct.into()),
ty::PredicateKind::Subtype(data) => Some(data.a.into()),
ty::PredicateKind::NormalizesTo(data) if data.term.is_infer() => Some(data.term),
_ => None,
}
}

#[instrument(skip(self), level = "debug")]
pub(super) fn maybe_report_ambiguity(
&self,
obligation: &PredicateObligation<'tcx>,
related: &[&FulfillmentError<'tcx>],
) -> ErrorGuaranteed {
// Unable to successfully determine, probably means
// insufficient type information, but could mean
Expand Down Expand Up @@ -255,12 +288,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Pick the first generic parameter that still contains inference variables as the one
// we're going to emit an error for. If there are none (see above), fall back to
// a more general error.
let term = data
.trait_ref
.args
.iter()
.filter_map(ty::GenericArg::as_term)
.find(|s| s.has_non_region_infer());
let term = self.ambiguity_term(predicate);

let mut err = if let Some(term) = term {
let candidates: Vec<_> = self
Expand Down Expand Up @@ -306,34 +334,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.with_long_ty_path(long_ty_path)
};

let mut ambiguities = compute_applicable_impls_for_diagnostics(
self.infcx,
&obligation.with(self.tcx, trait_pred),
false,
);
let has_non_region_infer = trait_pred
.skip_binder()
.trait_ref
.args
.types()
.any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more than a
// handful of them. If there are a lot of them, but only a few of them have no type
// params, we only show those, as they are more likely to be useful/intended.
if ambiguities.len() > 5 {
let infcx = self.infcx;
if !ambiguities.iter().all(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
}) {
// If not all are blanket impls, we filter blanked impls out.
ambiguities.retain(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
});
}
}
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
if let Some(ambiguities) = self.applicable_impls_to_mention(obligation, trait_pred)
{
if let Some(e) = self.tainted_by_errors()
&& term.is_none()
{
Expand Down Expand Up @@ -590,13 +592,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// other `Foo` impls are incoherent.
return guar;
}
let term = data
.projection_term
.args
.iter()
.filter_map(ty::GenericArg::as_term)
.chain([data.term])
.find(|g| g.has_non_region_infer());
let term = self.ambiguity_term(predicate);
let predicate = self.tcx.short_string(predicate, &mut long_ty_path);
if let Some(term) = term {
self.emit_inference_failure_err(
Expand All @@ -621,16 +617,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}

ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(_)) => {
if let Err(e) = predicate.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
let term =
data.walk().filter_map(ty::GenericArg::as_term).find(|term| term.is_infer());
if let Some(term) = term {
if let Some(term) = self.ambiguity_term(predicate) {
self.emit_inference_failure_err(
obligation.cause.body_def_id,
span,
Expand Down Expand Up @@ -713,10 +707,119 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.with_long_ty_path(long_ty_path)
}
};

// The related obligations are ambiguous because of the same inference variable,
// so they belong to this diagnostic: annotating the variable has to satisfy all
// of them at once. Mention their requirements, except for bookkeeping predicates
// (`WellFormed`, sizedness, ...) whose mention wouldn't be actionable.
let mut mentioned = vec![predicate];
let mut mentioned_strs: Vec<String> = vec![];
for &error in related {
let related_pred = self.resolve_vars_if_possible(error.obligation.predicate);
if mentioned.contains(&related_pred) {
continue;
}
let note = match related_pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data))
if !matches!(
self.tcx.as_lang_item(data.def_id()),
Some(LangItem::Sized | LangItem::MetaSized | LangItem::PointeeSized)
) =>
{
let clause = related_pred.kind().rebind(data);
if let ty::Infer(_) = clause.self_ty().skip_binder().kind() {
let tr = self.tcx.short_string(
clause.print_modifiers_and_trait_path(),
&mut err.long_ty_path(),
);
format!("the type must also implement `{tr}`")
} else {
let pred = self.tcx.short_string(related_pred, &mut err.long_ty_path());
let note = format!("cannot satisfy `{pred}`");
// The self type is known, so the `impl`s that could have applied to it are
// few and worth pointing at, like the blamed bound does. When it is still
// an inference variable the list is every `impl` of the trait, which is
// why the branch above only names the trait.
//
// `tainted_by_errors` is checked because `annotate_source_of_ambiguity`
// downgrades the whole diagnostic once an error was already emitted.
if !mentioned_strs.contains(&note)
&& self.tainted_by_errors().is_none()
&& let Some(ambiguities) =
self.applicable_impls_to_mention(&error.obligation, clause)
{
self.annotate_source_of_ambiguity(&mut err, &ambiguities, related_pred);
mentioned_strs.push(note);
mentioned.push(related_pred);
continue;
}
note
}
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
let pred = self.tcx.short_string(related_pred, &mut err.long_ty_path());
format!("cannot satisfy `{pred}`")
}
_ => {
mentioned.push(related_pred);
continue;
}
};
// Two predicates can print identically (e.g. `From<?0>` and `From<?1>` both show as
// `From<_>`); only emit each unique note string once.
if !mentioned_strs.contains(&note) {
err.note(note.clone());
mentioned_strs.push(note);
}
mentioned.push(related_pred);
}

self.note_obligation_cause(&mut err, obligation);
// The merged errors are not reported on their own anymore, so the bounds they came from
// have to be explained here too. Causes shared with the blamed obligation are already
// described by the call above.
for &error in related {
if error.obligation.cause.code() != obligation.cause.code() {
self.note_obligation_cause(&mut err, &error.obligation);
}
}
err.emit()
}

/// The `impl`s and `where` clauses that could have satisfied `trait_pred`, when listing them
/// is likely to help. `None` means the caller should describe the bound some other way.
fn applicable_impls_to_mention(
&self,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> Option<Vec<CandidateSource>> {
let mut ambiguities = compute_applicable_impls_for_diagnostics(
self.infcx,
&obligation.with(self.tcx, trait_pred),
false,
);
let has_non_region_infer =
trait_pred.skip_binder().trait_ref.args.types().any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more than a
// handful of them. If there are a lot of them, but only a few of them have no type
// params, we only show those, as they are more likely to be useful/intended.
if ambiguities.len() > 5 {
let infcx = self.infcx;
if !ambiguities.iter().all(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
}) {
// If not all are blanket impls, we filter blanked impls out.
ambiguities.retain(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
});
}
}
(ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer)
.then_some(ambiguities)
}

fn annotate_source_of_ambiguity(
&self,
err: &mut Diag<'_>,
Expand Down
Loading
Loading