Skip to content

Fix trait method resolution on an adjusted never type#156047

Open
JonathanBrouwer wants to merge 3 commits into
rust-lang:mainfrom
JonathanBrouwer:never-ty-method
Open

Fix trait method resolution on an adjusted never type#156047
JonathanBrouwer wants to merge 3 commits into
rust-lang:mainfrom
JonathanBrouwer:never-ty-method

Conversation

@JonathanBrouwer

@JonathanBrouwer JonathanBrouwer commented May 1, 2026

Copy link
Copy Markdown
Contributor

@rustbot rustbot added 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. labels May 1, 2026
@rustbot

rustbot commented May 1, 2026

Copy link
Copy Markdown
Collaborator

WaffleLapkin is not on the review rotation at the moment.
They may take a while to respond.

@JonathanBrouwer JonathanBrouwer May 1, 2026

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.

Note that this PR allows more code to compile on stable, for example see this test.

View changes since the review

@JonathanBrouwer JonathanBrouwer May 3, 2026

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.

If this is a problem we could delay the problem by putting this change behind the never_type feature gate

Comment thread compiler/rustc_hir_typeck/src/method/probe.rs Outdated

@lcnr lcnr 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 think this a reasonable change, I would expect us to emit a FCW here, I don't want to support this permanently or more importantly, this may overlap with other kinds of inference fallback in the future, so we generally don't want users to rely on this

View changes since this review

@WaffleLapkin WaffleLapkin added the F-never_type `#![feature(never_type)]` label May 1, 2026
@rust-log-analyzer

This comment has been minimized.

@WaffleLapkin WaffleLapkin added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. T-types Relevant to the types team, which will review and decide on the PR/issue. labels May 1, 2026
@JonathanBrouwer

JonathanBrouwer commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

This PR is now based on #156048, still need to add the FCW lint, I'll do that later.
@rustbot author

Is "Trait method call on adjusted never type" the correct terminology? I'm not super familiar with the type system yet

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 2, 2026
@rustbot

rustbot commented May 2, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 2, 2026
@rust-bors

This comment has been minimized.

@rustbot

This comment has been minimized.

}

declare_lint! {
/// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which

@JonathanBrouwer JonathanBrouwer May 3, 2026

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.

I'm really unsure of what the correct terminology is, both for the lint definition and the diagnostic, so please give feedback on this

View changes since the review

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.

IMO the proper way to say it would be "method_call_on_diverging_infer_variable" and "detects situations in which a method is called on a value resulting from a never-to-any coercion, without necessary information to infer a type for it", but I'm not sure how much we want to surface this internal language...

"detects trait method calls on an coerced never type",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #156047),
report_in_deps: true,

@JonathanBrouwer JonathanBrouwer May 3, 2026

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.

I decided on a lint with level Warn with report_in_deps: true. If something else is desired, let me know

View changes since the review

@JonathanBrouwer

JonathanBrouwer commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

@rustbot ready

I rebased on #156079 and #156048 so the diff is nicely small now

@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 May 3, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Comment thread compiler/rustc_hir_typeck/src/method/probe.rs
let e = match loop {} {
y => y.method(),
//~^ WARN [trait_method_on_coerced_never_type]
//~| WARN previously accepted

@JonathanBrouwer JonathanBrouwer Jun 19, 2026

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.

This specific test case did not pass before the change

View changes since the review

@rust-log-analyzer

This comment has been minimized.

};

let res = temp()?;
res.method();

@JonathanBrouwer JonathanBrouwer Jun 19, 2026

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.

CI found another fun case that now compiles and without using sub_unification_table_root_var doesn't. Because the expansion of ? contains a return, the type of res is unified with a never type, and you can therefore .method() on it. This also explains the changes in the question-mark-type-inference-in-chain test

View changes since the review

@rust-rfcbot rust-rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Jun 23, 2026
@rust-rfcbot

Copy link
Copy Markdown
Collaborator

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

@rust-rfcbot rust-rfcbot added the to-announce Announce this issue on triage meeting label Jun 23, 2026
@JonathanBrouwer JonathanBrouwer added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-fcp Status: PR is in FCP and is awaiting for FCP to complete. S-waiting-on-t-types Status: Awaiting decision from T-types labels Jun 24, 2026
Comment thread compiler/rustc_hir_typeck/src/method/probe.rs Outdated

@lcnr lcnr 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.

can you move the ty::Infer branch into the match (and maybe that match arm into a sub function)

after that r=me :>

View changes since this review

@JonathanBrouwer

JonathanBrouwer commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@lcnr There is no way to move the branch into the match, as the match needs to evaluate to a ErrorGuaratanteed which this branch doesn't. This if branch needs to fall through to the self.probe below. I'm also not a big fan of moving the if let into a sub function as it's only 7 lines, but if you still want that I'm happy to do it

Comment on lines +469 to +470
// but it may not be a type variable after processing obligations
// in our local `FnCtxt`, so don't call `structurally_resolve_type`.

@lcnr lcnr Jul 6, 2026

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.

It feels surprising that we would to call structurally_resolve_type here. Is this something you have tried to do?

This comment to me feels like somewhat of a false lead, where talking about not doing something makes people think about there having to be a reason we should do that thing here.

I would either remove the comment or change it to

We care about the opt_bad_ty given the inference state at the point of computing the auto deref chain, so we don't call structurally_resolve_type as it processes obligations in our local FnCtxt, potentially making inference progress.

View changes since the review

// (see https://github.com/rust-lang/rust/issues/143349)
} else if let ty::Infer(ty::TyVar(ty_id)) = *ty.kind()
&& let ty_id = self.sub_unification_table_root_var(ty_id)
&& let root_ty = Ty::new_var(self.tcx, ty_id)

@lcnr lcnr Jul 6, 2026

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.

can move that after emitting the lint

View changes since the review

@lcnr

lcnr commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

r=me on my end, handing over to @WaffleLapkin the exact wording of the lint and lint description

r? WaffleLapkin

@rustbot rustbot unassigned lcnr Jul 6, 2026
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Jul 9, 2026
Comment on lines +5582 to +5612

declare_lint! {
/// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which
/// was coerced to any, has a trait method on it.
///
/// ### Example
///
/// ```rust,no_run
/// fn main() {
/// let x = panic!();
/// x.clone();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Calling trait methods on a coerced `!` was previously disallowed for the never type,
/// but it did work for empty enums such as `Infallible` since these don't coerce.
/// This means that changing the definition of `Infallible` to become a type alias to `!` (a long-term goal),
/// would break code that called a trait method on `Infallible`, in such a way that the `!` would coerce.
///
/// Therefore, to aid in the transition of changing `Infallible` to a type alias, this is temporarily allowed with a FCW.
pub TRAIT_METHOD_ON_COERCED_NEVER_TYPE,
Warn,
"detects trait method calls on an coerced never type",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #156047),
report_in_deps: true,
};

@WaffleLapkin WaffleLapkin Jul 9, 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.

Suggested change
declare_lint! {
/// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which
/// was coerced to any, has a trait method on it.
///
/// ### Example
///
/// ```rust,no_run
/// fn main() {
/// let x = panic!();
/// x.clone();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Calling trait methods on a coerced `!` was previously disallowed for the never type,
/// but it did work for empty enums such as `Infallible` since these don't coerce.
/// This means that changing the definition of `Infallible` to become a type alias to `!` (a long-term goal),
/// would break code that called a trait method on `Infallible`, in such a way that the `!` would coerce.
///
/// Therefore, to aid in the transition of changing `Infallible` to a type alias, this is temporarily allowed with a FCW.
pub TRAIT_METHOD_ON_COERCED_NEVER_TYPE,
Warn,
"detects trait method calls on an coerced never type",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #156047),
report_in_deps: true,
};
declare_lint! {
/// The `method_call_on_diverging_infer_variable` lint detects situations in which a method is called on a value resulting from a never-to-any coercion, without necessary information to infer a type for it
///
/// ### Example
///
/// ```rust,no_run
/// fn main() {
/// let x = panic!();
/// x.clone();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Rust does not generally allow calling methods on values which do not have a known type, such a result of a never-to-any coercion with no type specified.
///
/// To aid with transition of code calling methods on `Infallible` after changing `Infallible` to be an alias for `!`, rustc *temporarily* allows such calls.
/// This will (once again) become an error in the future.
///
/// Thanks to never-to-any coercion you can replace method calls on `!` with the use of the `!` variable, or an `as` cast to an explicit type:
///
/// ```diff
/// - x.clone()
/// + x
/// ```
/// ```diff
/// - result.map(|x| x.convert_error())?;
/// + result.map(|x| x as ErrorType)?;
/// ```
pub METHOD_CALL_ON_DIVERGING_INFER_VAR,
Warn,
"detects method calls on a result of never-to-any coercion",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #156047),
report_in_deps: true,
};

View changes since the review

struct MissingTypeAnnot;

#[derive(Diagnostic)]
#[diag("trait method call on a coerced never type")]

@WaffleLapkin WaffleLapkin Jul 9, 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.

Suggested change
#[diag("trait method call on a coerced never type")]
#[diag("method call on a diverging inference variable")]

View changes since the review


#[derive(Diagnostic)]
#[diag("trait method call on a coerced never type")]
#[help("consider providing a type annotation")]

@WaffleLapkin WaffleLapkin Jul 9, 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.

Can you make a suggestion to go from x.method() to x as ReturnTypeOfTheMethod?

View changes since the review

@rustbot rustbot 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 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. F-never_type `#![feature(never_type)]` finished-final-comment-period The final comment period is finished for this PR / Issue. I-types-nominated Nominated for discussion during a types team meeting. 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. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent method lookup on never type

9 participants