Skip to content

Commit b84fe77

Browse files
committed
Auto merge of #156047 - JonathanBrouwer:never-ty-method, r=<try>
Fix trait method resolution on an adjusted never type try-job: x86_64-gnu-llvm-22-3
2 parents da80ed0 + 62c7143 commit b84fe77

8 files changed

Lines changed: 354 additions & 39 deletions

File tree

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir_analysis::autoderef::{self, Autoderef};
1212
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
1313
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
1414
use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query};
15+
use rustc_lint::builtin::METHOD_CALL_ON_DIVERGING_INFER_VAR;
1516
use rustc_macros::Diagnostic;
1617
use rustc_middle::middle::stability;
1718
use rustc_middle::ty::elaborate::supertrait_def_ids;
@@ -403,6 +404,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
403404
#[diag("type annotations needed")]
404405
struct MissingTypeAnnot;
405406

407+
#[derive(Diagnostic)]
408+
#[diag("method call on a diverging inference variable")]
409+
#[help("consider providing a type annotation")]
410+
struct MethodCallOnDivergingInferenceVariable;
411+
406412
let mut orig_values = OriginalQueryValues::default();
407413
let predefined_opaques_in_body = if self.next_trait_solver() {
408414
self.tcx.mk_predefined_opaques_in_body_from_iter(
@@ -468,6 +474,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
468474
// If we encountered an `_` type or an error type during autoderef, this is
469475
// ambiguous.
470476
if let Some(bad_ty) = &steps.opt_bad_ty {
477+
// We care about the opt_bad_ty given the inference state at the point of computing the auto deref chain,
478+
// so we don't call structurally_resolve_type as it processes obligations in our local FnCtxt,
479+
// potentially making inference progress.
480+
let ty = &bad_ty.ty;
481+
let ty = self
482+
.probe_instantiate_query_response(span, &orig_values, ty)
483+
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
484+
let ty = ty.value;
485+
471486
if is_suggestion.0 {
472487
// Ambiguity was encountered during a suggestion. There's really
473488
// not much use in suggesting methods in this case.
@@ -491,15 +506,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
491506
span,
492507
MissingTypeAnnot,
493508
);
509+
// If `ty` is an inference variable that was created by being adjusted from the never type,
510+
// We demand the type to be equal to the never type, so we can probe the never type for methods
511+
// (see https://github.com/rust-lang/rust/issues/143349)
512+
} else if let ty::Infer(ty::TyVar(ty_id)) = *ty.kind()
513+
&& let ty_id = self.sub_unification_table_root_var(ty_id)
514+
&& self
515+
.diverging_type_vars
516+
.borrow()
517+
.iter()
518+
.any(|&candidate_id| self.sub_unification_table_root_var(candidate_id) == ty_id)
519+
{
520+
self.tcx.emit_node_span_lint(
521+
METHOD_CALL_ON_DIVERGING_INFER_VAR,
522+
scope_expr_id,
523+
span,
524+
MethodCallOnDivergingInferenceVariable,
525+
);
526+
let root_ty = Ty::new_var(self.tcx, ty_id);
527+
self.demand_eqtype(span, root_ty, self.tcx.types.never);
494528
} else {
495-
// Ended up encountering a type variable when doing autoderef,
496-
// but it may not be a type variable after processing obligations
497-
// in our local `FnCtxt`, so don't call `structurally_resolve_type`.
498-
let ty = &bad_ty.ty;
499-
let ty = self
500-
.probe_instantiate_query_response(span, &orig_values, ty)
501-
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
502-
let ty = self.resolve_vars_if_possible(ty.value);
503529
let guar = match *ty.kind() {
504530
_ if let Some(guar) = self.tainted_by_errors() => guar,
505531
ty::Infer(ty::TyVar(_)) => {

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub mod hardwired {
7171
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
7272
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
7373
META_VARIABLE_MISUSE,
74+
METHOD_CALL_ON_DIVERGING_INFER_VAR,
7475
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
7576
MISSING_ABI,
7677
MISSING_UNSAFE_ON_EXTERN,
@@ -5535,3 +5536,46 @@ declare_lint! {
55355536
"usage of `unsafe` code and other potentially unsound constructs",
55365537
@eval_always = true
55375538
}
5539+
5540+
declare_lint! {
5541+
/// 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,
5542+
/// without necessary information to infer a type for it.
5543+
///
5544+
/// ### Example
5545+
///
5546+
#[cfg_attr(bootstrap, doc = "```rust,compile_fail")]
5547+
#[cfg_attr(not(bootstrap), doc = "```rust,no_run")]
5548+
/// fn main() {
5549+
/// let x = panic!();
5550+
/// x.clone();
5551+
/// }
5552+
#[doc = "```"]
5553+
///
5554+
/// {{produces}}
5555+
///
5556+
/// ### Explanation
5557+
///
5558+
/// Rust does not generally allow calling methods on values which do not have a known type,
5559+
/// such a result of a never-to-any coercion with no type specified.
5560+
///
5561+
/// To aid with transition of code calling methods on `Infallible` after changing `Infallible` to be an alias for `!`, rustc *temporarily* allows such calls.
5562+
/// This will (once again) become an error in the future.
5563+
///
5564+
/// 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:
5565+
///
5566+
/// ```diff
5567+
/// - x.clone()
5568+
/// + x
5569+
/// ```
5570+
/// ```diff
5571+
/// - result.map(|x| x.convert_error())?;
5572+
/// + result.map(|x| x as ErrorType)?;
5573+
/// ```
5574+
pub METHOD_CALL_ON_DIVERGING_INFER_VAR,
5575+
Warn,
5576+
"detects method calls on a result of never-to-any coercion",
5577+
@future_incompatible = FutureIncompatibleInfo {
5578+
reason: fcw!(FutureReleaseError #156047),
5579+
report_in_deps: true,
5580+
};
5581+
}

tests/ui/inference/question-mark-type-inference-in-chain.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ fn parse(_s: &str) -> std::result::Result<Version, Error> {
3131

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

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

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

6374
Ok(tags)
6475
}
Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
error[E0282]: type annotations needed
2-
--> $DIR/question-mark-type-inference-in-chain.rs:33:9
1+
warning: method call on a diverging inference variable
2+
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
33
|
4-
LL | let mut tags = lines.iter().map(|e| parse(e)).collect()?;
5-
| ^^^^^^^^
6-
...
74
LL | tags.sort();
8-
| ---- type must be known at this point
5+
| ^^^^
96
|
10-
help: consider giving `tags` an explicit type
7+
= help: consider providing a type annotation
8+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
9+
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
10+
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default
11+
12+
error[E0599]: no method named `sort` found for type `!` in the current scope
13+
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
1114
|
12-
LL | let mut tags: Vec<_> = lines.iter().map(|e| parse(e)).collect()?;
13-
| ++++++++
15+
LL | tags.sort();
16+
| ^^^^ method not found in `!`
1417

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

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

40+
warning: method call on a diverging inference variable
41+
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
42+
|
43+
LL | tags.sort();
44+
| ^^^^
45+
|
46+
= help: consider providing a type annotation
47+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
48+
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
49+
50+
error[E0599]: no method named `sort` found for type `!` in the current scope
51+
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
52+
|
53+
LL | tags.sort();
54+
| ^^^^ method not found in `!`
55+
3756
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>`
38-
--> $DIR/question-mark-type-inference-in-chain.rs:74:20
57+
--> $DIR/question-mark-type-inference-in-chain.rs:85:20
3958
|
4059
LL | .collect::<Result<Vec<Version>>>()?;
4160
| ------- ^^^^^^^^^^^^^^^^^^^^ value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from `std::iter::Iterator<Item=std::result::Result<Version, Error>>`
@@ -47,7 +66,7 @@ help: the trait `FromIterator<Result<_, Error>>` is not implemented for `std::re
4766
--> $SRC_DIR/core/src/result.rs:LL:COL
4867
= help: for that trait implementation, expected `AnotherError`, found `Error`
4968
note: the method call chain might not have had the expected associated types
50-
--> $DIR/question-mark-type-inference-in-chain.rs:71:10
69+
--> $DIR/question-mark-type-inference-in-chain.rs:82:10
5170
|
5271
LL | let mut tags = lines
5372
| ----- this expression has type `&[&str]`
@@ -60,7 +79,31 @@ LL | .map(|e| parse(e))
6079
note: required by a bound in `collect`
6180
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
6281

63-
error: aborting due to 4 previous errors
82+
error: aborting due to 5 previous errors; 2 warnings emitted
6483

65-
Some errors have detailed explanations: E0277, E0282, E0283.
84+
Some errors have detailed explanations: E0277, E0283, E0599.
6685
For more information about an error, try `rustc --explain E0277`.
86+
Future incompatibility report: Future breakage diagnostic:
87+
warning: method call on a diverging inference variable
88+
--> $DIR/question-mark-type-inference-in-chain.rs:35:10
89+
|
90+
LL | tags.sort();
91+
| ^^^^
92+
|
93+
= help: consider providing a type annotation
94+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
95+
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
96+
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default
97+
98+
Future breakage diagnostic:
99+
warning: method call on a diverging inference variable
100+
--> $DIR/question-mark-type-inference-in-chain.rs:66:10
101+
|
102+
LL | tags.sort();
103+
| ^^^^
104+
|
105+
= help: consider providing a type annotation
106+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
107+
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
108+
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default
109+
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
//! regression test for issue #2151
1+
//@ check-pass
2+
// Regression test for https://github.com/rust-lang/rust/issues/143349
23

34
fn main() {
4-
let x = panic!(); //~ ERROR type annotations needed
5+
let x = panic!();
56
x.clone();
7+
//~^ WARN [method_call_on_diverging_infer_var]
8+
//~| WARN previously accepted
69
}

tests/ui/never_type/basic/clone-never.stderr

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
error[E0282]: type annotations needed
2-
--> $DIR/clone-never.rs:4:9
1+
warning: method call on a diverging inference variable
2+
--> $DIR/clone-never.rs:6:7
33
|
4-
LL | let x = panic!();
5-
| ^
64
LL | x.clone();
7-
| - type must be known at this point
5+
| ^^^^^
86
|
9-
help: consider giving `x` an explicit type
10-
|
11-
LL | let x: /* Type */ = panic!();
12-
| ++++++++++++
7+
= help: consider providing a type annotation
8+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
9+
= note: for more information, see issue #156047 <https://github.com/rust-lang/rust/issues/156047>
10+
= note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default
11+
12+
warning: 1 warning emitted
1313

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

16-
For more information about this error, try `rustc --explain E0282`.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//@ check-pass
2+
// Regression test for https://github.com/rust-lang/rust/issues/143349
3+
4+
#![feature(never_type)]
5+
6+
trait Trait {
7+
fn method(&self);
8+
}
9+
impl Trait for ! {
10+
fn method(&self) {
11+
todo!()
12+
}
13+
}
14+
15+
struct Adhoc;
16+
struct Error;
17+
18+
#[doc(hidden)]
19+
trait AdhocKind: Sized {
20+
#[inline]
21+
fn anyhow_kind(&self) -> Adhoc {
22+
Adhoc
23+
}
24+
}
25+
26+
impl<T> AdhocKind for &T where T: ?Sized + Send + Sync + 'static {}
27+
28+
impl Adhoc {
29+
#[cold]
30+
fn new<M>(self, message: M) -> Error
31+
where
32+
M: Send + Sync + 'static,
33+
{
34+
Error
35+
}
36+
}
37+
38+
fn temp<T>() -> Result<T, ()> { todo!() }
39+
40+
fn main() -> Result<(), ()> {
41+
let x = loop {};
42+
x.method();
43+
//~^ WARN [method_call_on_diverging_infer_var]
44+
//~| WARN previously accepted
45+
46+
{ loop {} }.method();
47+
//~^ WARN [method_call_on_diverging_infer_var]
48+
//~| WARN previously accepted
49+
50+
let e = match loop {} {
51+
y => y.method(),
52+
//~^ WARN [method_call_on_diverging_infer_var]
53+
//~| WARN previously accepted
54+
};
55+
56+
let error = match loop {} {
57+
error => (&error).anyhow_kind().new(error),
58+
//~^ WARN [method_call_on_diverging_infer_var]
59+
//~| WARN previously accepted
60+
};
61+
62+
let res = temp()?;
63+
res.method();
64+
//~^ WARN [method_call_on_diverging_infer_var]
65+
//~| WARN previously accepted
66+
}

0 commit comments

Comments
 (0)