Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 34 additions & 8 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query};
use rustc_lint::builtin::METHOD_CALL_ON_DIVERGING_INFER_VAR;
use rustc_macros::Diagnostic;
use rustc_middle::middle::stability;
use rustc_middle::ty::elaborate::supertrait_def_ids;
Expand Down Expand Up @@ -394,6 +395,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[diag("type annotations needed")]
struct MissingTypeAnnot;

#[derive(Diagnostic)]
#[diag("method call on a diverging inference variable")]
#[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

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.

Gaining access to the return type seems to be quite difficult here, I might give this another try later but any suggestions are appreciated

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.

I think something can be done by calling self.probe_for_name after self.demand_eqtype, with that you can figure out which method will be called:

[compiler/rustc_hir_typeck/src/method/probe.rs:531:25] res = Pick {
    item: AssocItem {
        def_id: DefId(0:4 ~ method_on_never[69a2]::Trait::method),
...

But I can't quite figure out how to get from that to a type...

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.

I was able to get the type (at least in the simple case where the method return type doesn't depend on any generics), but I don't think this helps, as there doesn't seem to be a way to get the right spans (and in real code it's inside of anyhow! macro...).

struct MethodCallOnDivergingInferenceVariable;

let mut orig_values = OriginalQueryValues::default();
let predefined_opaques_in_body = if self.next_trait_solver() {
self.tcx.mk_predefined_opaques_in_body_from_iter(
Expand Down Expand Up @@ -459,6 +465,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If we encountered an `_` type or an error type during autoderef, this is
// ambiguous.
if let Some(bad_ty) = &steps.opt_bad_ty {
// 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.
let ty = &bad_ty.ty;
let ty = self
.probe_instantiate_query_response(span, &orig_values, ty)
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
let ty = ty.value;

if is_suggestion.0 {
// Ambiguity was encountered during a suggestion. There's really
// not much use in suggesting methods in this case.
Expand All @@ -482,15 +497,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
MissingTypeAnnot,
);
// If `ty` is an inference variable that was created by being adjusted from the never type,
// We demand the type to be equal to the never type, so we can probe the never type for methods
// (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)
&& self
.diverging_type_vars
.borrow()
.iter()
.any(|&candidate_id| self.sub_unification_table_root_var(candidate_id) == ty_id)
Comment thread
JonathanBrouwer marked this conversation as resolved.
{
self.tcx.emit_node_span_lint(
METHOD_CALL_ON_DIVERGING_INFER_VAR,
scope_expr_id,
span,
MethodCallOnDivergingInferenceVariable,
);
let root_ty = Ty::new_var(self.tcx, ty_id);
self.demand_eqtype(span, root_ty, self.tcx.types.never);
} else {
// Ended up encountering a type variable when doing autoderef,
// but it may not be a type variable after processing obligations
// in our local `FnCtxt`, so don't call `structurally_resolve_type`.
let ty = &bad_ty.ty;
let ty = self
.probe_instantiate_query_response(span, &orig_values, ty)
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
let ty = self.resolve_vars_if_possible(ty.value);
let guar = match *ty.kind() {
_ if let Some(guar) = self.tainted_by_errors() => guar,
ty::Infer(ty::TyVar(_)) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/type_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
}

/// Returns the "root" variable of `vid` in the `sub_unification_table`
/// equivalence table. All type variables that have been are related via
/// equivalence table. All type variables that have been related via
/// equality or subtyping will yield the same root variable (per the
/// union-find algorithm), so `sub_unification_table_root_var(a)
/// == sub_unification_table_root_var(b)` implies that:
Expand Down
44 changes: 44 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub mod hardwired {
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
META_VARIABLE_MISUSE,
METHOD_CALL_ON_DIVERGING_INFER_VAR,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
MISSING_ABI,
MISSING_UNSAFE_ON_EXTERN,
Expand Down Expand Up @@ -5578,3 +5579,46 @@ declare_lint! {
"usage of `unsafe` code and other potentially unsound constructs",
@eval_always = true
}

declare_lint! {
/// The `method_call_on_diverging_infer_var` 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
///
#[cfg_attr(bootstrap, doc = "```rust,compile_fail")]
#[cfg_attr(not(bootstrap), doc = "```rust,no_run")]
/// fn main() {
/// let x = panic!();
/// x.clone();
/// }
#[doc = "```"]
///
/// {{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,
Comment thread
JonathanBrouwer marked this conversation as resolved.
};
Comment thread
JonathanBrouwer marked this conversation as resolved.
}
17 changes: 14 additions & 3 deletions tests/ui/inference/question-mark-type-inference-in-chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ fn parse(_s: &str) -> std::result::Result<Version, Error> {

pub fn error1(lines: &[&str]) -> Result<Vec<Version>> {
let mut tags = lines.iter().map(|e| parse(e)).collect()?;
//~^ ERROR: type annotations needed
//~| HELP: consider giving `tags` an explicit type

tags.sort(); //~ NOTE: type must be known at this point
tags.sort();
//~^ WARN method call on a diverging inference variable
//~| WARN previously accepted
//~| NOTE for more information, see issue
//~| NOTE `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default
//~| ERROR no method named `sort` found for type `!` in the current scope [E0599]
//~| HELP consider providing a type annotation
//~| NOTE method not found in `!`

Ok(tags)
}
Expand All @@ -59,6 +64,12 @@ pub fn error3(lines: &[&str]) -> Result<Vec<Version>> {
//~| NOTE: in this expansion of desugaring of operator `?`
//~| NOTE: in this expansion of desugaring of operator `?`
tags.sort();
//~^ WARN method call on a diverging inference variable
//~| WARN previously accepted
//~| NOTE for more information, see issue
//~| ERROR no method named `sort` found for type `!` in the current scope [E0599]
//~| HELP consider providing a type annotation
//~| NOTE method not found in `!`

Ok(tags)
}
Expand Down
73 changes: 58 additions & 15 deletions tests/ui/inference/question-mark-type-inference-in-chain.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
error[E0282]: type annotations needed
--> $DIR/question-mark-type-inference-in-chain.rs:33:9
warning: method call on a diverging inference variable
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
|
LL | let mut tags = lines.iter().map(|e| parse(e)).collect()?;
| ^^^^^^^^
...
LL | tags.sort();
| ---- type must be known at this point
| ^^^^
|
help: consider giving `tags` an explicit type
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default

error[E0599]: no method named `sort` found for type `!` in the current scope
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
|
LL | let mut tags: Vec<_> = lines.iter().map(|e| parse(e)).collect()?;
| ++++++++
LL | tags.sort();
| ^^^^ method not found in `!`

error[E0283]: type annotations needed
--> $DIR/question-mark-type-inference-in-chain.rs:43:65
--> $DIR/question-mark-type-inference-in-chain.rs:48:65
|
LL | let mut tags: Vec<Version> = lines.iter().map(|e| parse(e)).collect()?;
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
Expand All @@ -27,15 +30,31 @@ LL | let mut tags: Vec<Version> = lines.iter().map(|e| parse(e)).collect::<R
| ++++++++++++++++

error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> $DIR/question-mark-type-inference-in-chain.rs:55:20
--> $DIR/question-mark-type-inference-in-chain.rs:60:20
|
LL | let mut tags = lines.iter().map(|e| parse(e)).collect::<Vec<_>>()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `Vec<std::result::Result<Version, Error>>`
|
= help: the nightly-only, unstable trait `Try` is not implemented for `Vec<std::result::Result<Version, Error>>`

warning: method call on a diverging inference variable
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
|
LL | tags.sort();
| ^^^^
|
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>

error[E0599]: no method named `sort` found for type `!` in the current scope
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
|
LL | tags.sort();
| ^^^^ method not found in `!`

error[E0277]: a value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result<Version, Error>`
--> $DIR/question-mark-type-inference-in-chain.rs:74:20
--> $DIR/question-mark-type-inference-in-chain.rs:85:20
|
LL | .collect::<Result<Vec<Version>>>()?;
| ------- ^^^^^^^^^^^^^^^^^^^^ value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from `std::iter::Iterator<Item=std::result::Result<Version, Error>>`
Expand All @@ -47,7 +66,7 @@ help: the trait `FromIterator<std::result::Result<_, Error>>` is not implemented
--> $SRC_DIR/core/src/result.rs:LL:COL
= help: for that trait implementation, expected `AnotherError`, found `Error`
note: the method call chain might not have had the expected associated types
--> $DIR/question-mark-type-inference-in-chain.rs:71:10
--> $DIR/question-mark-type-inference-in-chain.rs:82:10
|
LL | let mut tags = lines
| ----- this expression has type `&[&str]`
Expand All @@ -60,7 +79,31 @@ LL | .map(|e| parse(e))
note: required by a bound in `collect`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error: aborting due to 4 previous errors
error: aborting due to 5 previous errors; 2 warnings emitted

Some errors have detailed explanations: E0277, E0282, E0283.
Some errors have detailed explanations: E0277, E0283, E0599.
For more information about an error, try `rustc --explain E0277`.
Future incompatibility report: Future breakage diagnostic:
warning: method call on a diverging inference variable
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
|
LL | tags.sort();
| ^^^^
|
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default

Future breakage diagnostic:
warning: method call on a diverging inference variable
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
|
LL | tags.sort();
| ^^^^
|
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default

7 changes: 5 additions & 2 deletions tests/ui/never_type/basic/clone-never.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! regression test for issue #2151
//@ check-pass
// Regression test for https://github.com/rust-lang/rust/issues/143349

fn main() {
let x = panic!(); //~ ERROR type annotations needed
let x = panic!();
x.clone();
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted
}
31 changes: 20 additions & 11 deletions tests/ui/never_type/basic/clone-never.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
error[E0282]: type annotations needed
--> $DIR/clone-never.rs:4:9
warning: method call on a diverging inference variable
--> $DIR/clone-never.rs:6:7
|
LL | let x = panic!();
| ^
LL | x.clone();
| - type must be known at this point
| ^^^^^
|
help: consider giving `x` an explicit type
|
LL | let x: /* Type */ = panic!();
| ++++++++++++
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default

warning: 1 warning emitted

error: aborting due to 1 previous error
Future incompatibility report: Future breakage diagnostic:
warning: method call on a diverging inference variable
--> $DIR/clone-never.rs:6:7
|
LL | x.clone();
| ^^^^^
|
= help: consider providing a type annotation
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default

For more information about this error, try `rustc --explain E0282`.
66 changes: 66 additions & 0 deletions tests/ui/never_type/basic/method-on-never.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//@ check-pass
// Regression test for https://github.com/rust-lang/rust/issues/143349

#![feature(never_type)]

trait Trait {
fn method(&self);
}
impl Trait for ! {
fn method(&self) {
todo!()
}
}

struct Adhoc;
struct Error;

#[doc(hidden)]
trait AdhocKind: Sized {
#[inline]
fn anyhow_kind(&self) -> Adhoc {
Adhoc
}
}

impl<T> AdhocKind for &T where T: ?Sized + Send + Sync + 'static {}

impl Adhoc {
#[cold]
fn new<M>(self, message: M) -> Error
where
M: Send + Sync + 'static,
{
Error
}
}

fn temp<T>() -> Result<T, ()> { todo!() }

fn main() -> Result<(), ()> {
let x = loop {};
x.method();
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted

{ loop {} }.method();
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted

let e = match loop {} {
y => y.method(),
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted
};

let error = match loop {} {
error => (&error).anyhow_kind().new(error),
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted
};

let res = temp()?;
res.method();
//~^ WARN [method_call_on_diverging_infer_var]
//~| WARN previously accepted
}
Loading
Loading