about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-13 14:05:19 +0000
committerbors <bors@rust-lang.org>2024-06-13 14:05:19 +0000
commitb6e5e3ffbbc41a7ef1f2a522fc7ded19b99918dc (patch)
tree21cb2758699310f1f86aef3317a3df84afd7a787
parent9fdbfe1441a82e96e3f99ae199ff5348d8d948ae (diff)
parentea98e42bfd7e3723a68cb2dd6f6ac4a4866a400e (diff)
downloadrust-b6e5e3ffbbc41a7ef1f2a522fc7ded19b99918dc.tar.gz
rust-b6e5e3ffbbc41a7ef1f2a522fc7ded19b99918dc.zip
Auto merge of #125289 - WaffleLapkin:never-obligations, r=compiler-errors
Implement lint for obligations broken by never type fallback change

This is the second (and probably last major?) lint required for the never type fallback change.

The idea is to check if the code errors with `fallback = ()` and if it errors with `fallback = !` and if it went from "ok" to "error", lint.

I'm not happy with the diagnostic, ideally we'd highlight what bound is the problem. But I'm really unsure how to do that  (cc `@jackh726,` iirc you had some ideas?)

r? `@compiler-errors`

Thanks `@BoxyUwU` with helping with trait solver stuff when I was implementing the initial version of this lint.

Tracking:
- https://github.com/rust-lang/rust/issues/123748
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl3
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs50
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs54
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs5
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.stderr3
-rw-r--r--tests/ui/delegation/not-supported.rs4
-rw-r--r--tests/ui/delegation/not-supported.stderr51
-rw-r--r--tests/ui/editions/never-type-fallback-breaking.e2021.stderr23
-rw-r--r--tests/ui/editions/never-type-fallback-breaking.e2024.stderr6
-rw-r--r--tests/ui/editions/never-type-fallback-breaking.rs4
-rw-r--r--tests/ui/never_type/defaulted-never-note.fallback.stderr2
-rw-r--r--tests/ui/never_type/defaulted-never-note.nofallback.stderr13
-rw-r--r--tests/ui/never_type/defaulted-never-note.rs2
-rw-r--r--tests/ui/never_type/dependency-on-fallback-to-unit.rs28
-rw-r--r--tests/ui/never_type/dependency-on-fallback-to-unit.stderr23
-rw-r--r--tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr23
-rw-r--r--tests/ui/never_type/diverging-fallback-control-flow.rs4
-rw-r--r--tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr2
-rw-r--r--tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr13
-rw-r--r--tests/ui/never_type/diverging-fallback-no-leak.rs3
-rw-r--r--tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr13
-rw-r--r--tests/ui/never_type/diverging-fallback-unconstrained-return.rs3
-rw-r--r--tests/ui/never_type/fallback-closure-ret.nofallback.stderr13
-rw-r--r--tests/ui/never_type/fallback-closure-ret.rs8
-rw-r--r--tests/ui/never_type/impl_trait_fallback.rs2
-rw-r--r--tests/ui/never_type/impl_trait_fallback.stderr13
27 files changed, 344 insertions, 29 deletions
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 6f499947d5c..3ab319a037b 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -44,6 +44,9 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `
 
 hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
 
+hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()`
+    .help = specify the types explicitly
+
 hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
 
 hir_typeck_expected_default_return_type = expected `()` because of default return type
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 3f12f252654..6dd34024fd1 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -183,6 +183,11 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe {
     Deref,
 }
 
+#[derive(LintDiagnostic)]
+#[help]
+#[diag(hir_typeck_dependency_on_unit_never_type_fallback)]
+pub struct DependencyOnUnitNeverTypeFallback {}
+
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(
     hir_typeck_add_missing_parentheses_in_range,
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 5ba67c52ef2..b7937f458b3 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::DUMMY_SP;
 use rustc_span::{def_id::LocalDefId, Span};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 
 #[derive(Copy, Clone)]
 pub enum DivergingFallbackBehavior {
@@ -344,6 +345,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // `!`.
         let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len());
         let unsafe_infer_vars = OnceCell::new();
+
+        self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids);
+
         for &diverging_vid in &diverging_vids {
             let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
             let root_vid = self.root_var(diverging_vid);
@@ -468,6 +472,52 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         }
     }
 
+    fn lint_obligations_broken_by_never_type_fallback_change(
+        &self,
+        behavior: DivergingFallbackBehavior,
+        diverging_vids: &[ty::TyVid],
+    ) {
+        let DivergingFallbackBehavior::ToUnit = behavior else { return };
+
+        // Fallback happens if and only if there are diverging variables
+        if diverging_vids.is_empty() {
+            return;
+        }
+
+        // Returns errors which happen if fallback is set to `fallback`
+        let remaining_errors_if_fallback_to = |fallback| {
+            self.probe(|_| {
+                let obligations = self.fulfillment_cx.borrow().pending_obligations();
+                let ocx = ObligationCtxt::new(&self.infcx);
+                ocx.register_obligations(obligations.iter().cloned());
+
+                for &diverging_vid in diverging_vids {
+                    let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
+
+                    ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
+                        .expect("expected diverging var to be unconstrained");
+                }
+
+                ocx.select_where_possible()
+            })
+        };
+
+        // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
+        // then this code will be broken by the never type fallback change.qba
+        let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
+        if unit_errors.is_empty()
+            && let never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
+            && !never_errors.is_empty()
+        {
+            self.tcx.emit_node_span_lint(
+                lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
+                self.tcx.local_def_id_to_hir_id(self.body_id),
+                self.tcx.def_span(self.body_id),
+                errors::DependencyOnUnitNeverTypeFallback {},
+            )
+        }
+    }
+
     /// Returns a graph whose nodes are (unresolved) inference variables and where
     /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
     fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 45051394ffc..a12c76037e7 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -34,6 +34,7 @@ declare_lint_pass! {
         CONST_EVALUATABLE_UNCHECKED,
         CONST_ITEM_MUTATION,
         DEAD_CODE,
+        DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
         DEPRECATED,
         DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
         DEPRECATED_IN_FUTURE,
@@ -4200,6 +4201,59 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `dependency_on_unit_never_type_fallback` lint detects cases where code compiles with
+    /// [never type fallback] being [`()`], but will stop compiling with fallback being [`!`].
+    ///
+    /// [never type fallback]: https://doc.rust-lang.org/nightly/core/primitive.never.html#never-type-fallback
+    /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html
+    /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(dependency_on_unit_never_type_fallback)]
+    /// fn main() {
+    ///     if true {
+    ///         // return has type `!` which, is some cases, causes never type fallback
+    ///         return
+    ///     } else {
+    ///         // the type produced by this call is not specified explicitly,
+    ///         // so it will be inferred from the previous branch
+    ///         Default::default()
+    ///     };
+    ///     // depending on the fallback, this may compile (because `()` implements `Default`),
+    ///     // or it may not (because `!` does not implement `Default`)
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously
+    /// coerced to `()`. There are plans to change that, but they may make the code such as above
+    /// not compile. Instead of depending on the fallback, you should specify the type explicitly:
+    /// ```
+    /// if true {
+    ///     return
+    /// } else {
+    ///     // type is explicitly specified, fallback can't hurt us no more
+    ///     <() as Default>::default()
+    /// };
+    /// ```
+    ///
+    /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748).
+    pub DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
+    Warn,
+    "never type fallback affecting unsafe function calls",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
+    };
+    report_in_external_macro
+}
+
+declare_lint! {
     /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field
     /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or
     /// more built-in traits.
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index b944f531ef6..175b14d815a 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -390,9 +390,7 @@ mod issue7344 {
 
     impl<T> RetImplTraitSelf2<T> {
         // should not trigger lint
-        fn new(t: T) -> impl Trait2<(), Self> {
-            unimplemented!()
-        }
+        fn new(t: T) -> impl Trait2<(), Self> {}
     }
 
     struct RetImplTraitNoSelf2<T>(T);
@@ -401,7 +399,6 @@ mod issue7344 {
         // should trigger lint
         fn new(t: T) -> impl Trait2<(), i32> {
             //~^ ERROR: methods called `new` usually return `Self`
-            unimplemented!()
         }
     }
 
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
index d440a9f45fc..3597ad65838 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.stderr
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr
@@ -96,11 +96,10 @@ LL | |         }
    | |_________^
 
 error: methods called `new` usually return `Self`
-  --> tests/ui/new_ret_no_self.rs:402:9
+  --> tests/ui/new_ret_no_self.rs:400:9
    |
 LL | /         fn new(t: T) -> impl Trait2<(), i32> {
 LL | |
-LL | |             unimplemented!()
 LL | |         }
    | |_________^
 
diff --git a/tests/ui/delegation/not-supported.rs b/tests/ui/delegation/not-supported.rs
index 5f78de97638..25d7a4cb895 100644
--- a/tests/ui/delegation/not-supported.rs
+++ b/tests/ui/delegation/not-supported.rs
@@ -70,12 +70,16 @@ mod opaque {
 
         pub fn opaque_arg(_: impl Trait) -> i32 { 0 }
         pub fn opaque_ret() -> impl Trait { unimplemented!() }
+        //~^ warn: this function depends on never type fallback being `()`
+        //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     }
     reuse to_reuse::opaque_arg;
     //~^ ERROR delegation with early bound generics is not supported yet
 
     trait ToReuse {
         fn opaque_ret() -> impl Trait { unimplemented!() }
+        //~^ warn: this function depends on never type fallback being `()`
+        //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     }
 
     // FIXME: Inherited `impl Trait`s create query cycles when used inside trait impls.
diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/not-supported.stderr
index e2cb04f977b..339a8418b33 100644
--- a/tests/ui/delegation/not-supported.stderr
+++ b/tests/ui/delegation/not-supported.stderr
@@ -107,7 +107,7 @@ LL |         reuse Trait::foo2 { &self.0 }
    |                      ^^^^
 
 error: delegation with early bound generics is not supported yet
-  --> $DIR/not-supported.rs:74:21
+  --> $DIR/not-supported.rs:76:21
    |
 LL |         pub fn opaque_arg(_: impl Trait) -> i32 { 0 }
    |         --------------------------------------- callee defined here
@@ -115,46 +115,67 @@ LL |         pub fn opaque_arg(_: impl Trait) -> i32 { 0 }
 LL |     reuse to_reuse::opaque_arg;
    |                     ^^^^^^^^^^
 
-error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>::{synthetic#0}`
-  --> $DIR/not-supported.rs:83:25
+warning: this function depends on never type fallback being `()`
+  --> $DIR/not-supported.rs:80:9
+   |
+LL |         fn opaque_ret() -> impl Trait { unimplemented!() }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>::{synthetic#0}`
+  --> $DIR/not-supported.rs:87:25
    |
 LL |         reuse to_reuse::opaque_ret;
    |                         ^^^^^^^^^^
    |
 note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process...
-  --> $DIR/not-supported.rs:83:25
+  --> $DIR/not-supported.rs:87:25
    |
 LL |         reuse to_reuse::opaque_ret;
    |                         ^^^^^^^^^^
-   = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>::{synthetic#0}`, completing the cycle
-note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:82:5: 82:24>` is well-formed
-  --> $DIR/not-supported.rs:82:5
+   = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>::{synthetic#0}`, completing the cycle
+note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:86:5: 86:24>` is well-formed
+  --> $DIR/not-supported.rs:86:5
    |
 LL |     impl ToReuse for u8 {
    |     ^^^^^^^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
-error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>::{synthetic#0}`
-  --> $DIR/not-supported.rs:86:24
+warning: this function depends on never type fallback being `()`
+  --> $DIR/not-supported.rs:72:9
+   |
+LL |         pub fn opaque_ret() -> impl Trait { unimplemented!() }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+
+error[E0391]: cycle detected when computing type of `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>::{synthetic#0}`
+  --> $DIR/not-supported.rs:90:24
    |
 LL |         reuse ToReuse::opaque_ret;
    |                        ^^^^^^^^^^
    |
 note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process...
-  --> $DIR/not-supported.rs:86:24
+  --> $DIR/not-supported.rs:90:24
    |
 LL |         reuse ToReuse::opaque_ret;
    |                        ^^^^^^^^^^
-   = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>::{synthetic#0}`, completing the cycle
-note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:85:5: 85:25>` is well-formed
-  --> $DIR/not-supported.rs:85:5
+   = note: ...which again requires computing type of `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>::{synthetic#0}`, completing the cycle
+note: cycle used when checking that `opaque::<impl at $DIR/not-supported.rs:89:5: 89:25>` is well-formed
+  --> $DIR/not-supported.rs:89:5
    |
 LL |     impl ToReuse for u16 {
    |     ^^^^^^^^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: recursive delegation is not supported yet
-  --> $DIR/not-supported.rs:99:22
+  --> $DIR/not-supported.rs:103:22
    |
 LL |         pub reuse to_reuse2::foo;
    |                              --- callee defined here
@@ -162,7 +183,7 @@ LL |         pub reuse to_reuse2::foo;
 LL |     reuse to_reuse1::foo;
    |                      ^^^
 
-error: aborting due to 16 previous errors
+error: aborting due to 16 previous errors; 2 warnings emitted
 
 Some errors have detailed explanations: E0049, E0195, E0391.
 For more information about an error, try `rustc --explain E0049`.
diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr
new file mode 100644
index 00000000000..92c233a0d9c
--- /dev/null
+++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr
@@ -0,0 +1,23 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/never-type-fallback-breaking.rs:15:1
+   |
+LL | fn m() {
+   | ^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: this function depends on never type fallback being `()`
+  --> $DIR/never-type-fallback-breaking.rs:27:1
+   |
+LL | fn q() -> Option<()> {
+   | ^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/editions/never-type-fallback-breaking.e2024.stderr b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr
index e9a8882eb6c..461e4ae0bdf 100644
--- a/tests/ui/editions/never-type-fallback-breaking.e2024.stderr
+++ b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `!: Default` is not satisfied
-  --> $DIR/never-type-fallback-breaking.rs:17:17
+  --> $DIR/never-type-fallback-breaking.rs:19:17
    |
 LL |         true => Default::default(),
    |                 ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `!`
@@ -8,7 +8,7 @@ LL |         true => Default::default(),
    = help: did you intend to use the type `()` here instead?
 
 error[E0277]: the trait bound `!: Default` is not satisfied
-  --> $DIR/never-type-fallback-breaking.rs:30:5
+  --> $DIR/never-type-fallback-breaking.rs:34:5
    |
 LL |     deserialize()?;
    |     ^^^^^^^^^^^^^ the trait `Default` is not implemented for `!`
@@ -16,7 +16,7 @@ LL |     deserialize()?;
    = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information)
    = help: did you intend to use the type `()` here instead?
 note: required by a bound in `deserialize`
-  --> $DIR/never-type-fallback-breaking.rs:26:23
+  --> $DIR/never-type-fallback-breaking.rs:30:23
    |
 LL |     fn deserialize<T: Default>() -> Option<T> {
    |                       ^^^^^^^ required by this bound in `deserialize`
diff --git a/tests/ui/editions/never-type-fallback-breaking.rs b/tests/ui/editions/never-type-fallback-breaking.rs
index 7dfa4702807..7b4a1b1de04 100644
--- a/tests/ui/editions/never-type-fallback-breaking.rs
+++ b/tests/ui/editions/never-type-fallback-breaking.rs
@@ -13,6 +13,8 @@ fn main() {
 }
 
 fn m() {
+    //[e2021]~^ this function depends on never type fallback being `()`
+    //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     let x = match true {
         true => Default::default(),
         //[e2024]~^ error: the trait bound `!: Default` is not satisfied
@@ -23,6 +25,8 @@ fn m() {
 }
 
 fn q() -> Option<()> {
+    //[e2021]~^ this function depends on never type fallback being `()`
+    //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     fn deserialize<T: Default>() -> Option<T> {
         Some(T::default())
     }
diff --git a/tests/ui/never_type/defaulted-never-note.fallback.stderr b/tests/ui/never_type/defaulted-never-note.fallback.stderr
index 92fa9068cfd..fe9a924f64a 100644
--- a/tests/ui/never_type/defaulted-never-note.fallback.stderr
+++ b/tests/ui/never_type/defaulted-never-note.fallback.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied
-  --> $DIR/defaulted-never-note.rs:30:9
+  --> $DIR/defaulted-never-note.rs:32:9
    |
 LL |     foo(_x);
    |     --- ^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr
new file mode 100644
index 00000000000..b69b8dda8f1
--- /dev/null
+++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr
@@ -0,0 +1,13 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/defaulted-never-note.rs:28:1
+   |
+LL | fn smeg() {
+   | ^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/never_type/defaulted-never-note.rs b/tests/ui/never_type/defaulted-never-note.rs
index f4e5273b33a..40861e73b39 100644
--- a/tests/ui/never_type/defaulted-never-note.rs
+++ b/tests/ui/never_type/defaulted-never-note.rs
@@ -26,6 +26,8 @@ fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
 //[fallback]~^ NOTE required by this bound in `foo`
 //[fallback]~| NOTE required by a bound in `foo`
 fn smeg() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     let _x = return;
     foo(_x);
     //[fallback]~^ ERROR the trait bound
diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.rs b/tests/ui/never_type/dependency-on-fallback-to-unit.rs
new file mode 100644
index 00000000000..5448d0be2c6
--- /dev/null
+++ b/tests/ui/never_type/dependency-on-fallback-to-unit.rs
@@ -0,0 +1,28 @@
+//@ check-pass
+
+fn main() {
+    def();
+    _ = question_mark();
+}
+
+fn def() {
+    //~^ warn: this function depends on never type fallback being `()`
+    //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    match true {
+        false => <_>::default(),
+        true => return,
+    };
+}
+
+// <https://github.com/rust-lang/rust/issues/51125>
+// <https://github.com/rust-lang/rust/issues/39216>
+fn question_mark() -> Result<(), ()> {
+    //~^ warn: this function depends on never type fallback being `()`
+    //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    deserialize()?;
+    Ok(())
+}
+
+fn deserialize<T: Default>() -> Result<T, ()> {
+    Ok(T::default())
+}
diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr
new file mode 100644
index 00000000000..36c82b6d1bf
--- /dev/null
+++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr
@@ -0,0 +1,23 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/dependency-on-fallback-to-unit.rs:8:1
+   |
+LL | fn def() {
+   | ^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: this function depends on never type fallback being `()`
+  --> $DIR/dependency-on-fallback-to-unit.rs:19:1
+   |
+LL | fn question_mark() -> Result<(), ()> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr
new file mode 100644
index 00000000000..5fbdc04ed3b
--- /dev/null
+++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr
@@ -0,0 +1,23 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/diverging-fallback-control-flow.rs:30:1
+   |
+LL | fn assignment() {
+   | ^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: this function depends on never type fallback being `()`
+  --> $DIR/diverging-fallback-control-flow.rs:42:1
+   |
+LL | fn assignment_rev() {
+   | ^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/never_type/diverging-fallback-control-flow.rs b/tests/ui/never_type/diverging-fallback-control-flow.rs
index e209a990885..575e2e9273c 100644
--- a/tests/ui/never_type/diverging-fallback-control-flow.rs
+++ b/tests/ui/never_type/diverging-fallback-control-flow.rs
@@ -28,6 +28,8 @@ impl UnitDefault for () {
 }
 
 fn assignment() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     let x;
 
     if true {
@@ -38,6 +40,8 @@ fn assignment() {
 }
 
 fn assignment_rev() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     let x;
 
     if true {
diff --git a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr
index 01abf2e17f1..c5463814475 100644
--- a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr
+++ b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `!: Test` is not satisfied
-  --> $DIR/diverging-fallback-no-leak.rs:17:23
+  --> $DIR/diverging-fallback-no-leak.rs:20:23
    |
 LL |     unconstrained_arg(return);
    |     ----------------- ^^^^^^ the trait `Test` is not implemented for `!`
diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr
new file mode 100644
index 00000000000..d11097323b3
--- /dev/null
+++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr
@@ -0,0 +1,13 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/diverging-fallback-no-leak.rs:14:1
+   |
+LL | fn main() {
+   | ^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/never_type/diverging-fallback-no-leak.rs b/tests/ui/never_type/diverging-fallback-no-leak.rs
index 425437da207..c6d59c7f273 100644
--- a/tests/ui/never_type/diverging-fallback-no-leak.rs
+++ b/tests/ui/never_type/diverging-fallback-no-leak.rs
@@ -12,6 +12,9 @@ impl Test for () {}
 fn unconstrained_arg<T: Test>(_: T) {}
 
 fn main() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
     // Here the type variable falls back to `!`,
     // and hence we get a type error.
     unconstrained_arg(return);
diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr
new file mode 100644
index 00000000000..750bcfb7f89
--- /dev/null
+++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr
@@ -0,0 +1,13 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/diverging-fallback-unconstrained-return.rs:28:1
+   |
+LL | fn main() {
+   | ^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs
index aeb6ee6e26e..927991db513 100644
--- a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs
+++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs
@@ -26,6 +26,9 @@ fn unconstrained_return<T: UnitReturn>() -> T {
 }
 
 fn main() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
     // In Ye Olde Days, the `T` parameter of `unconstrained_return`
     // winds up "entangled" with the `!` type that results from
     // `panic!`, and hence falls back to `()`. This is kind of unfortunate
diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr
new file mode 100644
index 00000000000..9f0b9f6daea
--- /dev/null
+++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr
@@ -0,0 +1,13 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/fallback-closure-ret.rs:21:1
+   |
+LL | fn main() {
+   | ^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/never_type/fallback-closure-ret.rs b/tests/ui/never_type/fallback-closure-ret.rs
index dcf38e03a13..30f9ac54d0b 100644
--- a/tests/ui/never_type/fallback-closure-ret.rs
+++ b/tests/ui/never_type/fallback-closure-ret.rs
@@ -12,12 +12,14 @@
 
 #![cfg_attr(fallback, feature(never_type_fallback))]
 
-trait Bar { }
-impl Bar for () {  }
-impl Bar for u32 {  }
+trait Bar {}
+impl Bar for () {}
+impl Bar for u32 {}
 
 fn foo<R: Bar>(_: impl Fn() -> R) {}
 
 fn main() {
+    //[nofallback]~^ warn: this function depends on never type fallback being `()`
+    //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     foo(|| panic!());
 }
diff --git a/tests/ui/never_type/impl_trait_fallback.rs b/tests/ui/never_type/impl_trait_fallback.rs
index ce06f8f7817..fbe13dbe2ac 100644
--- a/tests/ui/never_type/impl_trait_fallback.rs
+++ b/tests/ui/never_type/impl_trait_fallback.rs
@@ -6,5 +6,7 @@ trait T {}
 impl T for () {}
 
 fn should_ret_unit() -> impl T {
+    //~^ warn: this function depends on never type fallback being `()`
+    //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
     panic!()
 }
diff --git a/tests/ui/never_type/impl_trait_fallback.stderr b/tests/ui/never_type/impl_trait_fallback.stderr
new file mode 100644
index 00000000000..87638940332
--- /dev/null
+++ b/tests/ui/never_type/impl_trait_fallback.stderr
@@ -0,0 +1,13 @@
+warning: this function depends on never type fallback being `()`
+  --> $DIR/impl_trait_fallback.rs:8:1
+   |
+LL | fn should_ret_unit() -> impl T {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #123748 <https://github.com/rust-lang/rust/issues/123748>
+   = help: specify the types explicitly
+   = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
+
+warning: 1 warning emitted
+