about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaybe Waffle <waffle.lapkin@gmail.com>2024-03-15 12:27:54 +0000
committerMaybe Waffle <waffle.lapkin@gmail.com>2024-03-15 17:48:26 +0000
commit19bc3370632c4f26be1cb16b90f72375c55879a3 (patch)
tree860ac19856f9e84a30145cc874fca26288b2149e
parentee03c286cfdca26fa5b2a4ee40957625d2c826ff (diff)
downloadrust-19bc3370632c4f26be1cb16b90f72375c55879a3.tar.gz
rust-19bc3370632c4f26be1cb16b90f72375c55879a3.zip
Add `rustc_never_type_mode` crate-level attribute to allow experimenting
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs119
-rw-r--r--compiler/rustc_span/src/symbol.rs4
3 files changed, 96 insertions, 34 deletions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 1f77484d65c..a5f8b0b270f 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -580,6 +580,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "`may_dangle` has unstable semantics and may be removed in the future",
     ),
 
+    rustc_attr!(
+        rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|no_fallback"), ErrorFollowing,
+        @only_local: true,
+        "`rustc_never_type_fallback` is used to experiment with never type fallback and work on \
+         never type stabilization, and will never be stable"
+    ),
+
     // ==========================================================================
     // Internal attributes: Runtime related:
     // ==========================================================================
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index aa8bbad1d12..5206e79177b 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -4,8 +4,19 @@ use rustc_data_structures::{
     graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
     unord::{UnordBag, UnordMap, UnordSet},
 };
+use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::ty::{self, Ty};
+use rustc_span::sym;
+
+enum DivergingFallbackBehavior {
+    /// Always fallback to `()` (aka "always spontaneous decay")
+    FallbackToUnit,
+    /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
+    FallbackToNiko,
+    /// Don't fallback at all
+    NoFallback,
+}
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
     /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
@@ -64,7 +75,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             return false;
         }
 
-        let diverging_fallback = self.calculate_diverging_fallback(&unresolved_variables);
+        let diverging_behavior = self.diverging_fallback_behavior();
+        let diverging_fallback =
+            self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior);
 
         // We do fallback in two passes, to try to generate
         // better error messages.
@@ -78,6 +91,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         fallback_occurred
     }
 
+    fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
+        let Some((mode, span)) = self
+            .tcx
+            .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode)
+            .map(|attr| (attr.value_str().unwrap(), attr.span))
+        else {
+            if self.tcx.features().never_type_fallback {
+                return DivergingFallbackBehavior::FallbackToNiko;
+            }
+
+            return DivergingFallbackBehavior::FallbackToUnit;
+        };
+
+        match mode {
+            sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit,
+            sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko,
+            sym::no_fallback => DivergingFallbackBehavior::NoFallback,
+            _ => {
+                self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, and `no_fallback`)"));
+
+                DivergingFallbackBehavior::FallbackToUnit
+            }
+        }
+    }
+
     fn fallback_effects(&self) -> bool {
         let unsolved_effects = self.unsolved_effects();
 
@@ -232,6 +270,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     fn calculate_diverging_fallback(
         &self,
         unresolved_variables: &[Ty<'tcx>],
+        behavior: DivergingFallbackBehavior,
     ) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
         debug!("calculate_diverging_fallback({:?})", unresolved_variables);
 
@@ -345,39 +384,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
                 output: infer_var_infos.items().any(|info| info.output),
             };
 
-            if found_infer_var_info.self_in_trait && found_infer_var_info.output {
-                // This case falls back to () to ensure that the code pattern in
-                // tests/ui/never_type/fallback-closure-ret.rs continues to
-                // compile when never_type_fallback is enabled.
-                //
-                // This rule is not readily explainable from first principles,
-                // but is rather intended as a patchwork fix to ensure code
-                // which compiles before the stabilization of never type
-                // fallback continues to work.
-                //
-                // Typically this pattern is encountered in a function taking a
-                // closure as a parameter, where the return type of that closure
-                // (checked by `relationship.output`) is expected to implement
-                // some trait (checked by `relationship.self_in_trait`). This
-                // can come up in non-closure cases too, so we do not limit this
-                // rule to specifically `FnOnce`.
-                //
-                // When the closure's body is something like `panic!()`, the
-                // return type would normally be inferred to `!`. However, it
-                // needs to fall back to `()` in order to still compile, as the
-                // trait is specifically implemented for `()` but not `!`.
-                //
-                // For details on the requirements for these relationships to be
-                // set, see the relationship finding module in
-                // compiler/rustc_trait_selection/src/traits/relationships.rs.
-                debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
-                diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
-            } else if can_reach_non_diverging {
-                debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
-                diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
-            } else {
-                debug!("fallback to ! - all diverging: {:?}", diverging_vid);
-                diverging_fallback.insert(diverging_ty, Ty::new_diverging_default(self.tcx));
+            use DivergingFallbackBehavior::*;
+            match behavior {
+                FallbackToUnit => {
+                    debug!("fallback to () - legacy: {:?}", diverging_vid);
+                    diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
+                }
+                FallbackToNiko => {
+                    if found_infer_var_info.self_in_trait && found_infer_var_info.output {
+                        // This case falls back to () to ensure that the code pattern in
+                        // tests/ui/never_type/fallback-closure-ret.rs continues to
+                        // compile when never_type_fallback is enabled.
+                        //
+                        // This rule is not readily explainable from first principles,
+                        // but is rather intended as a patchwork fix to ensure code
+                        // which compiles before the stabilization of never type
+                        // fallback continues to work.
+                        //
+                        // Typically this pattern is encountered in a function taking a
+                        // closure as a parameter, where the return type of that closure
+                        // (checked by `relationship.output`) is expected to implement
+                        // some trait (checked by `relationship.self_in_trait`). This
+                        // can come up in non-closure cases too, so we do not limit this
+                        // rule to specifically `FnOnce`.
+                        //
+                        // When the closure's body is something like `panic!()`, the
+                        // return type would normally be inferred to `!`. However, it
+                        // needs to fall back to `()` in order to still compile, as the
+                        // trait is specifically implemented for `()` but not `!`.
+                        //
+                        // For details on the requirements for these relationships to be
+                        // set, see the relationship finding module in
+                        // compiler/rustc_trait_selection/src/traits/relationships.rs.
+                        debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
+                        diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
+                    } else if can_reach_non_diverging {
+                        debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
+                        diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
+                    } else {
+                        debug!("fallback to ! - all diverging: {:?}", diverging_vid);
+                        diverging_fallback.insert(diverging_ty, self.tcx.types.never);
+                    }
+                }
+                NoFallback => {
+                    debug!("no fallback - `rustc_never_type_mode = "no_fallback"`: {:?}", diverging_vid);
+                }
             }
         }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7de0555bb22..0df5e7873c2 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -815,6 +815,8 @@ symbols! {
         fadd_algebraic,
         fadd_fast,
         fake_variadic,
+        fallback_to_niko,
+        fallback_to_unit,
         fdiv_algebraic,
         fdiv_fast,
         feature,
@@ -1233,6 +1235,7 @@ symbols! {
         no_crate_inject,
         no_debug,
         no_default_passes,
+        no_fallback,
         no_implicit_prelude,
         no_inline,
         no_link,
@@ -1551,6 +1554,7 @@ symbols! {
         rustc_mir,
         rustc_must_implement_one_of,
         rustc_never_returns_null_ptr,
+        rustc_never_type_mode,
         rustc_no_mir_inline,
         rustc_nonnull_optimization_guaranteed,
         rustc_nounwind,