From 83f8f9f85d6f16fde4ab0d181caa9e9f49d257a3 Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Sun, 19 May 2024 20:51:38 +0200
Subject: [PATCH 1/7] Implement lint for obligations broken by never type
 fallback change

---
 compiler/rustc_hir_typeck/messages.ftl        |  3 +
 compiler/rustc_hir_typeck/src/errors.rs       |  5 ++
 compiler/rustc_hir_typeck/src/fallback.rs     | 50 +++++++++++++++-
 compiler/rustc_lint_defs/src/builtin.rs       | 57 +++++++++++++++++++
 .../defaulted-never-note.fallback.stderr      |  2 +-
 .../defaulted-never-note.nofallback.stderr    | 13 +++++
 tests/ui/never_type/defaulted-never-note.rs   |  2 +
 .../dependency-on-fallback-to-unit.rs         | 28 +++++++++
 .../dependency-on-fallback-to-unit.stderr     | 23 ++++++++
 ...ng-fallback-control-flow.nofallback.stderr | 23 ++++++++
 .../diverging-fallback-control-flow.rs        |  4 ++
 ...diverging-fallback-no-leak.fallback.stderr |  2 +-
 ...verging-fallback-no-leak.nofallback.stderr | 13 +++++
 .../never_type/diverging-fallback-no-leak.rs  |  3 +
 ...ack-unconstrained-return.nofallback.stderr | 13 +++++
 ...diverging-fallback-unconstrained-return.rs |  3 +
 .../fallback-closure-ret.nofallback.stderr    | 13 +++++
 tests/ui/never_type/fallback-closure-ret.rs   |  8 ++-
 tests/ui/never_type/impl_trait_fallback.rs    |  2 +
 .../ui/never_type/impl_trait_fallback.stderr  | 13 +++++
 20 files changed, 274 insertions(+), 6 deletions(-)
 create mode 100644 tests/ui/never_type/defaulted-never-note.nofallback.stderr
 create mode 100644 tests/ui/never_type/dependency-on-fallback-to-unit.rs
 create mode 100644 tests/ui/never_type/dependency-on-fallback-to-unit.stderr
 create mode 100644 tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr
 create mode 100644 tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr
 create mode 100644 tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr
 create mode 100644 tests/ui/never_type/fallback-closure-ret.nofallback.stderr
 create mode 100644 tests/ui/never_type/impl_trait_fallback.stderr

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..06c5adae99e 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -8,12 +8,17 @@ use rustc_data_structures::{
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::HirId;
-use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::bug;
+use rustc_infer::{
+    infer::{DefineOpaqueTypes, InferOk},
+    traits::ObligationCause,
+};
 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::ObligationCtxt;
+use rustc_type_ir::TyVid;
 
 #[derive(Copy, Clone)]
 pub enum DivergingFallbackBehavior {
@@ -344,6 +349,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 +476,46 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         }
     }
 
+    fn lint_obligations_broken_by_never_type_fallback_change(
+        &self,
+        behavior: DivergingFallbackBehavior,
+        diverging_vids: &[TyVid],
+    ) {
+        let DivergingFallbackBehavior::FallbackToUnit = behavior else { return };
+
+        // Returns errors which happen if fallback is set to `fallback`
+        let try_out = |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);
+                }
+
+                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 = try_out(self.tcx.types.unit);
+        if unit_errors.is_empty()
+            && let never_errors = try_out(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..e6895fbfd09 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,
@@ -4199,6 +4200,62 @@ declare_lint! {
     report_in_external_macro
 }
 
+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]: prim@never#never-type-fallback
+    /// [`()`]: prim@unit
+    /// [`!`]:
+    ///
+    /// ### 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).
+    ///
+    /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html
+    /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html
+    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
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
+

From b88d03b05966ed49a8da5bd26c7fa0a9af35459d Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Mon, 20 May 2024 04:07:38 +0200
Subject: [PATCH 2/7] Add a fast exit to
 `lint_obligations_broken_by_never_type_fallback_change`

---
 compiler/rustc_hir_typeck/src/fallback.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 06c5adae99e..56c962fb681 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -8,11 +8,11 @@ use rustc_data_structures::{
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::HirId;
-use rustc_middle::bug;
 use rustc_infer::{
     infer::{DefineOpaqueTypes, InferOk},
     traits::ObligationCause,
 };
+use rustc_middle::bug;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::DUMMY_SP;
@@ -483,6 +483,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     ) {
         let DivergingFallbackBehavior::FallbackToUnit = 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 try_out = |fallback| {
             self.probe(|_| {

From 0f41d26f419dc7cdaa01941604faf188123d3ea0 Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Mon, 20 May 2024 04:19:22 +0200
Subject: [PATCH 3/7] Apply review nits

---
 compiler/rustc_hir_typeck/src/fallback.rs | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 56c962fb681..dbc329a12bf 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -8,17 +8,13 @@ use rustc_data_structures::{
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::HirId;
-use rustc_infer::{
-    infer::{DefineOpaqueTypes, InferOk},
-    traits::ObligationCause,
-};
+use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::bug;
 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::ObligationCtxt;
-use rustc_type_ir::TyVid;
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 
 #[derive(Copy, Clone)]
 pub enum DivergingFallbackBehavior {
@@ -479,7 +475,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     fn lint_obligations_broken_by_never_type_fallback_change(
         &self,
         behavior: DivergingFallbackBehavior,
-        diverging_vids: &[TyVid],
+        diverging_vids: &[ty::TyVid],
     ) {
         let DivergingFallbackBehavior::FallbackToUnit = behavior else { return };
 
@@ -489,7 +485,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         }
 
         // Returns errors which happen if fallback is set to `fallback`
-        let try_out = |fallback| {
+        let remaining_errors_if_fallback_to = |fallback| {
             self.probe(|_| {
                 let obligations = self.fulfillment_cx.borrow().pending_obligations();
                 let ocx = ObligationCtxt::new(&self.infcx);
@@ -498,7 +494,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
                 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);
+                    ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
+                        .expect("expected diverging var to be unconstrained");
                 }
 
                 ocx.select_where_possible()
@@ -507,9 +504,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
 
         // 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 = try_out(self.tcx.types.unit);
+        let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
         if unit_errors.is_empty()
-            && let never_errors = try_out(self.tcx.types.never)
+            && let never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
             && !never_errors.is_empty()
         {
             self.tcx.emit_node_span_lint(

From 268b55639344f783c722cc6e605a3d04cddaab2d Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Mon, 20 May 2024 04:24:23 +0200
Subject: [PATCH 4/7] Bless a test

---
 tests/ui/delegation/not-supported.rs     |  4 ++
 tests/ui/delegation/not-supported.stderr | 51 +++++++++++++++++-------
 2 files changed, 40 insertions(+), 15 deletions(-)

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`.

From a22f8aa17abcd2f0dfb83aa8ee41509f922be6ab Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Thu, 23 May 2024 18:02:13 +0200
Subject: [PATCH 5/7] Fixup clippy tests

Don't depend on the fact that `!` falls back to `()` and so panic-ish things
can be used in `-> impl ImplementedForUnit` functions
---
 src/tools/clippy/tests/ui/new_ret_no_self.rs     | 5 +----
 src/tools/clippy/tests/ui/new_ret_no_self.stderr | 3 +--
 2 files changed, 2 insertions(+), 6 deletions(-)

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 | |         }
    | |_________^
 

From 8400cd0b34bff8f013cd00064fc650fef652fc1d Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Thu, 23 May 2024 01:02:37 +0200
Subject: [PATCH 6/7] Fixup links in lint docs

looks like prim@ stuff does not work here (is it possibly not handled by rustdoc at all?)
---
 compiler/rustc_lint_defs/src/builtin.rs | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index e6895fbfd09..a12c76037e7 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -4204,9 +4204,9 @@ 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]: prim@never#never-type-fallback
-    /// [`()`]: prim@unit
-    /// [`!`]:
+    /// [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
     ///
@@ -4243,9 +4243,6 @@ declare_lint! {
     /// ```
     ///
     /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748).
-    ///
-    /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html
-    /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html
     pub DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
     Warn,
     "never type fallback affecting unsafe function calls",

From ea98e42bfd7e3723a68cb2dd6f6ac4a4866a400e Mon Sep 17 00:00:00 2001
From: Waffle Lapkin <waffle.lapkin@gmail.com>
Date: Thu, 13 Jun 2024 14:43:16 +0200
Subject: [PATCH 7/7] rebase blessing

---
 compiler/rustc_hir_typeck/src/fallback.rs     |  2 +-
 .../never-type-fallback-breaking.e2021.stderr | 23 +++++++++++++++++++
 .../never-type-fallback-breaking.e2024.stderr |  6 ++---
 .../editions/never-type-fallback-breaking.rs  |  4 ++++
 4 files changed, 31 insertions(+), 4 deletions(-)
 create mode 100644 tests/ui/editions/never-type-fallback-breaking.e2021.stderr

diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index dbc329a12bf..b7937f458b3 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -477,7 +477,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         behavior: DivergingFallbackBehavior,
         diverging_vids: &[ty::TyVid],
     ) {
-        let DivergingFallbackBehavior::FallbackToUnit = behavior else { return };
+        let DivergingFallbackBehavior::ToUnit = behavior else { return };
 
         // Fallback happens if and only if there are diverging variables
         if diverging_vids.is_empty() {
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())
     }