diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4258896deec70..82490b25faff4 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -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::TRAIT_METHOD_ON_COERCED_NEVER_TYPE; use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; @@ -394,6 +395,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[diag("type annotations needed")] struct MissingTypeAnnot; + #[derive(Diagnostic)] + #[diag("trait method call on a coerced never type")] + #[help("consider providing a type annotation")] + struct TraitMethodOnCoercedNeverType; + 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( @@ -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 { + // 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); + if is_suggestion.0 { // Ambiguity was encountered during a suggestion. There's really // not much use in suggesting methods in this case. @@ -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.root_var(ty_id) + && let root_ty = Ty::new_var(self.tcx, ty_id) + && self + .diverging_type_vars + .borrow() + .iter() + .any(|&candidate_id| self.root_var(candidate_id) == ty_id) + { + self.tcx.emit_node_span_lint( + TRAIT_METHOD_ON_COERCED_NEVER_TYPE, + scope_expr_id, + span, + TraitMethodOnCoercedNeverType, + ); + 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(_)) => { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4858e65992cd3..88ecd2fc38d92 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -114,6 +114,7 @@ declare_lint_pass! { TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + TRAIT_METHOD_ON_COERCED_NEVER_TYPE, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, TYVAR_BEHIND_RAW_POINTER, @@ -5709,3 +5710,35 @@ declare_lint! { report_in_deps: false, }; } + +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, + }; +} diff --git a/tests/ui/never_type/basic/clone-never.rs b/tests/ui/never_type/basic/clone-never.rs index 76ee3c5f5d566..3466a4c78c026 100644 --- a/tests/ui/never_type/basic/clone-never.rs +++ b/tests/ui/never_type/basic/clone-never.rs @@ -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 [trait_method_on_coerced_never_type] + //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/clone-never.stderr b/tests/ui/never_type/basic/clone-never.stderr index ea2f55f17381e..ed726bd48f11d 100644 --- a/tests/ui/never_type/basic/clone-never.stderr +++ b/tests/ui/never_type/basic/clone-never.stderr @@ -1,16 +1,25 @@ -error[E0282]: type annotations needed - --> $DIR/clone-never.rs:4:9 +warning: trait method call on a coerced never type + --> $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 + = note: `#[warn(trait_method_on_coerced_never_type)]` (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: trait method call on a coerced never type + --> $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 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs new file mode 100644 index 0000000000000..22198bc202fd6 --- /dev/null +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -0,0 +1,24 @@ +//@ 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!() + } +} + +fn main() { + let x = loop {}; + x.method(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted + + { loop {} }.method(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted +} diff --git a/tests/ui/never_type/basic/method-on-never.stderr b/tests/ui/never_type/basic/method-on-never.stderr new file mode 100644 index 0000000000000..1fa7d160e3b79 --- /dev/null +++ b/tests/ui/never_type/basic/method-on-never.stderr @@ -0,0 +1,47 @@ +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:17:7 + | +LL | x.method(); + | ^^^^^^ + | + = 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 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:21:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = 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 + +warning: 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:17:7 + | +LL | x.method(); + | ^^^^^^ + | + = 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 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:21:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = 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 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default +