Skip to content

Improve diagnostics for ambiguous binops with as _ - #156591

Open
qaijuang wants to merge 10 commits into
rust-lang:mainfrom
qaijuang:diagnostics-as-infer-binop
Open

Improve diagnostics for ambiguous binops with as _#156591
qaijuang wants to merge 10 commits into
rust-lang:mainfrom
qaijuang:diagnostics-as-infer-binop

Conversation

@qaijuang

@qaijuang qaijuang commented May 15, 2026

Copy link
Copy Markdown
Contributor

View all comments

Fixes #156004

as _ inside the RHS of an overloaded binary operation can leave the cast target unresolved while there is still a pending operator obligation that knows the more useful ambiguity.

This PR keeps the normal resolved-cast path unchanged, such that when the cast target is still a type variable, cast checking now looks for a pending BinOp obligation whose RHS contains the cast span and replays that obligation in a fresh diagnostic fulfillment context. If the replayed obligation is actually ambiguous, rustc reports the existing fulfillment ambiguity, including the competing impl candidates.

Best reviewed commit-by-commit.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 15, 2026
@qaijuang
qaijuang marked this pull request as ready for review May 15, 2026 10:10
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 15, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 15, 2026
@rustbot

rustbot commented May 15, 2026

Copy link
Copy Markdown
Collaborator

r? @TaKO8Ki

rustbot has assigned @TaKO8Ki.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 17 candidates

@qaijuang

Copy link
Copy Markdown
Contributor Author

cc @mqudsi

@hkBst hkBst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to my code comments, I wonder if we could not reuse the existing diagnostic code for "multiple impls satisfying type: Trait" instead, since that is ultimately the reason for the ambiguity of "as _" here.

View changes since this review

Comment thread compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs Outdated
Comment thread compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs Outdated
Comment thread tests/ui/inference/multiple-impl-apply.stderr
@qaijuang

Copy link
Copy Markdown
Contributor Author

In addition to my code comments, I wonder if we could not reuse the existing diagnostic code for "multiple impls satisfying type: Trait" instead, since that is ultimately the reason for the ambiguity of "as _" here.

View changes since this review

I not sure what you mean here, we are reusing existing

@qaijuang

Copy link
Copy Markdown
Contributor Author

github acting slow as usual -- the updates is yet to be processed

@hkBst

hkBst commented May 27, 2026

Copy link
Copy Markdown
Member

github acting slow as usual -- the updates is yet to be processed

Been checking the past 2 hours, but still seems to show nothing new... :(

@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from 30e4f90 to daf9b2e Compare May 27, 2026 15:15
@qaijuang

Copy link
Copy Markdown
Contributor Author

github acting slow as usual -- the updates is yet to be processed

Been checking the past 2 hours, but still seems to show nothing new... :(

It was a git cache issue 😪

Comment thread compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs Outdated
Comment thread compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs Outdated
@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from daf9b2e to c401e16 Compare May 28, 2026 10:05
Comment thread compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs Outdated
@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from c401e16 to af30177 Compare May 28, 2026 17:07
@hkBst

hkBst commented May 29, 2026

Copy link
Copy Markdown
Member

@qaijuang Please reread my review comments (which you marked resolved) and try to implement my suggestions.

@qaijuang

Copy link
Copy Markdown
Contributor Author

Pinging @hkBst (I believe i've implemented your suggestions)

@hkBst

hkBst commented Jun 22, 2026

Copy link
Copy Markdown
Member

@qaijuang thanks, I don't have any more suggestions and I've taken this as far as I can, so I'll let @TaKO8Ki take over now.

@rust-bors

This comment has been minimized.

@qaijuang

qaijuang commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

☔ The latest upstream changes (presumably #158342) made this pull request unmergeable. Please resolve the merge conflicts by rebasing.

I'm about to rebase

@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from ee63cc4 to 1977fc0 Compare July 2, 2026 20:48
@rustbot

This comment has been minimized.

@qaijuang

qaijuang commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

It seems @TaKO8Ki is tight on schedule, safe to @rustbot reroll

@rustbot rustbot assigned mati865 and unassigned TaKO8Ki Jul 2, 2026
@mati865

mati865 commented Jul 3, 2026

Copy link
Copy Markdown
Member

I'm not familiar with that area, @rustbot reroll

@hanna-kruppe hanna-kruppe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've touched the cast type checking code before so here are two points I can comment on. Other than that, I unfortunately have to re-roll as well because I'm not otherwise familiar with type inference and especially not with the whole obligation topic that the meat of the diagnostic logic is about.

@rustbot reroll

View changes since this review


/// Prefer a pending operator ambiguity over a generic `as _` inference failure.
#[cold]
pub(crate) fn try_report_ambiguous_binop_for_infer_cast(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is specific to casts, I would expect it to live in cast.rs, not in the general FnCtxt plumbing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now moved to cast.rs

Comment thread compiler/rustc_hir_typeck/src/cast.rs Outdated
Comment on lines +758 to +760
if self.cast_ty.is_ty_var() {
self.cast_ty =
if let Some(guar) = fcx.try_report_ambiguous_binop_for_infer_cast(self.cast_span) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, both branches of the if let will end up reporting an error and setting self.cast_ty to an error type, and the rest of the check method won't do anything interesting (the cast-to-unsized branch shouldn't be taken, and then the "No sense in giving duplicate error messages" branch catches the error type).

If all of this is correct, then re-assigning self.cast_ty feels misleading to me. At first glance I thought this was a fix-up that alters the type so following logic can proceed a little further. I'd consider phrasing it like this:

self.cast_ty = fcx.resolve_vars_with_obligations();

debug!(...);

if self.cast_ty.is_ty_var() {
    /* report ambiguity error or "type must be known" */
} else /* existing if-else ladder */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems wrong to me, or at least unnecessary, that the code now leaves the self.cast_ty as a type variable instead of converting it to a type error. I don't fully understand what @hanna-kruppe was proposing, but I think we should keep the logic that sets self.cast_ty to an error type. The current version of this patch is quite bizarre as each branch of the if-else yields an ErrorGuaranteed but it's then swallowed by a semicolon.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted!

@rustbot rustbot assigned camelid and unassigned hanna-kruppe Jul 3, 2026
@camelid camelid added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 7, 2026
@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from 28c113c to da2b451 Compare July 8, 2026 11:35
@qaijuang

qaijuang commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 8, 2026
Comment thread compiler/rustc_hir_typeck/src/cast.rs Outdated
let cast_span = self.cast_span;

if let ObligationCauseCode::BinOp { rhs_span, .. } = obligation.cause.code()
&& rhs_span.contains(cast_span)

@chenyukang chenyukang Jul 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems we don't need to limit only rhs here, how about to also handle this testcase:

let _ = 42usize as _ == n;

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test added!

@qaijuang
qaijuang requested a review from camelid July 27, 2026 12:03
@qaijuang

qaijuang commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@rustbot reroll (@camelid seems busy)

@rustbot rustbot assigned chenyukang and unassigned camelid Aug 12, 2026
Comment thread compiler/rustc_hir_typeck/src/cast.rs Outdated
if (fcx.tcx.hir_span(*lhs_hir_id).contains(cast_span)
&& lhs_ty.contains(self.cast_ty))
|| (rhs_span.contains(cast_span) && rhs_ty.contains(self.cast_ty))
&& matches!(

@chenyukang chenyukang Aug 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems it's better to reject non-trait predicates before this if check, for example this code:

use std::ops::Add;

struct Rhs;

impl Add<Rhs> for u32 {
    type Output = ();

    fn add(self, _: Rhs) {}
}

impl Add<Rhs> for i32 {
    type Output = ();

    fn add(self, _: Rhs) {}
}

fn main() {
    let _: () = 42usize as _ + Rhs;
}

now we have two similar diagnostics:

error[E0284]: type annotations needed
  --> /tmp/now.rs:18:28
   |
18 |     let _: () = 42usize as _ + Rhs;
   |                            ^ cannot infer type
   |
   = note: cannot satisfy `<_ as Add<Rhs>>::Output == ()`

error[E0283]: type annotations needed
  --> /tmp/now.rs:18:28
   |
18 |     let _: () = 42usize as _ + Rhs;
   |                            ^ cannot infer type
   |
note: multiple `impl`s satisfying `_: Add<Rhs>` found
  --> /tmp/now.rs:5:1
   |
 5 | impl Add<Rhs> for u32 {
   | ^^^^^^^^^^^^^^^^^^^^^
...
11 | impl Add<Rhs> for i32 {
   | ^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test added!

@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from b897f07 to 2527f0f Compare August 13, 2026 09:35
@rustbot

This comment has been minimized.

@chenyukang

Copy link
Copy Markdown
Member

thanks!
r=me after rebasing

@qaijuang
qaijuang force-pushed the diagnostics-as-infer-binop branch from 2527f0f to 9c80dd6 Compare August 14, 2026 07:06
@rustbot

rustbot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed type inference for primitive ops doesn't provide reason

8 participants