Fix trait method resolution on an adjusted never type#156047
Fix trait method resolution on an adjusted never type#156047JonathanBrouwer wants to merge 3 commits into
Conversation
|
|
There was a problem hiding this comment.
Note that this PR allows more code to compile on stable, for example see this test.
There was a problem hiding this comment.
If this is a problem we could delay the problem by putting this change behind the never_type feature gate
There was a problem hiding this comment.
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
This comment has been minimized.
This comment has been minimized.
75d6b9a to
d72e38a
Compare
|
Reminder, once the PR becomes ready for a review, use |
This comment has been minimized.
This comment has been minimized.
d72e38a to
3787326
Compare
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| declare_lint! { | ||
| /// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which |
There was a problem hiding this comment.
I'm really unsure of what the correct terminology is, both for the lint definition and the diagnostic, so please give feedback on this
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
I decided on a lint with level Warn with report_in_deps: true. If something else is desired, let me know
3787326 to
a8f68d6
Compare
This comment has been minimized.
This comment has been minimized.
a8f68d6 to
2dd2577
Compare
This comment has been minimized.
This comment has been minimized.
| let e = match loop {} { | ||
| y => y.method(), | ||
| //~^ WARN [trait_method_on_coerced_never_type] | ||
| //~| WARN previously accepted |
There was a problem hiding this comment.
This specific test case did not pass before the change
This comment has been minimized.
This comment has been minimized.
8ff5c2a to
d727ef6
Compare
| }; | ||
|
|
||
| let res = temp()?; | ||
| res.method(); |
There was a problem hiding this comment.
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
|
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. |
|
@lcnr There is no way to move the branch into the match, as the match needs to evaluate to a |
d727ef6 to
e234c7f
Compare
| // but it may not be a type variable after processing obligations | ||
| // in our local `FnCtxt`, so don't call `structurally_resolve_type`. |
There was a problem hiding this comment.
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_tygiven the inference state at the point of computing the auto deref chain, so we don't callstructurally_resolve_typeas it processes obligations in our localFnCtxt, potentially making inference progress.
| // (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) |
There was a problem hiding this comment.
can move that after emitting the lint
|
r=me on my end, handing over to @WaffleLapkin the exact wording of the lint and lint description r? WaffleLapkin |
|
|
||
| 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, | ||
| }; |
There was a problem hiding this comment.
| 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, | |
| }; |
| struct MissingTypeAnnot; | ||
|
|
||
| #[derive(Diagnostic)] | ||
| #[diag("trait method call on a coerced never type")] |
There was a problem hiding this comment.
| #[diag("trait method call on a coerced never type")] | |
| #[diag("method call on a diverging inference variable")] |
|
|
||
| #[derive(Diagnostic)] | ||
| #[diag("trait method call on a coerced never type")] | ||
| #[help("consider providing a type annotation")] |
There was a problem hiding this comment.
Can you make a suggestion to go from x.method() to x as ReturnTypeOfTheMethod?
View all comments
Fixes #143349
r? @WaffleLapkin
cc @lcnr