about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs19
-rw-r--r--compiler/rustc_typeck/src/check/fallback.rs57
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs9
-rw-r--r--compiler/rustc_typeck/src/check/inherited.rs7
4 files changed, 53 insertions, 39 deletions
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index 013aecae586..746f273e69f 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -159,24 +159,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Coercing from `!` to any type is allowed:
         if a.is_never() {
-            // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
-            // type variable, we want `?T` to fallback to `!` if not
-            // otherwise constrained. An example where this arises:
-            //
-            //     let _: Option<?T> = Some({ return; });
-            //
-            // here, we would coerce from `!` to `?T`.
-            return if b.is_ty_var() {
-                // Micro-optimization: no need for this if `b` is
-                // already resolved in some way.
-                let diverging_ty = self.next_diverging_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::AdjustmentType,
-                    span: self.cause.span,
-                });
-                self.coerce_from_inference_variable(diverging_ty, b, simple(Adjust::NeverToAny))
-            } else {
-                success(simple(Adjust::NeverToAny)(b), b, vec![])
-            };
+            return success(simple(Adjust::NeverToAny)(b), b, vec![]);
         }
 
         // Coercing *from* an unresolved inference variable means that
diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs
index 2866cd98758..fff771663e9 100644
--- a/compiler/rustc_typeck/src/check/fallback.rs
+++ b/compiler/rustc_typeck/src/check/fallback.rs
@@ -2,7 +2,6 @@ use crate::check::FnCtxt;
 use rustc_data_structures::{
     fx::FxHashMap, graph::vec_graph::VecGraph, graph::WithSuccessors, stable_set::FxHashSet,
 };
-use rustc_infer::infer::type_variable::Diverging;
 use rustc_middle::ty::{self, Ty};
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
@@ -255,8 +254,27 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
 
         // Extract the unsolved type inference variable vids; note that some
         // unsolved variables are integer/float variables and are excluded.
-        let unsolved_vids: Vec<_> =
-            unsolved_variables.iter().filter_map(|ty| ty.ty_vid()).collect();
+        let unsolved_vids = unsolved_variables.iter().filter_map(|ty| ty.ty_vid());
+
+        // Compute the diverging root vids D -- that is, the root vid of
+        // those type variables that (a) are the target of a coercion from
+        // a `!` type and (b) have not yet been solved.
+        //
+        // These variables are the ones that are targets for fallback to
+        // either `!` or `()`.
+        let diverging_roots: FxHashSet<ty::TyVid> = self
+            .diverging_type_vars
+            .borrow()
+            .iter()
+            .map(|&ty| self.infcx.shallow_resolve(ty))
+            .filter_map(|ty| ty.ty_vid())
+            .map(|vid| self.infcx.root_var(vid))
+            .collect();
+        debug!(
+            "calculate_diverging_fallback: diverging_type_vars={:?}",
+            self.diverging_type_vars.borrow()
+        );
+        debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
 
         // Find all type variables that are reachable from a diverging
         // type variable. These will typically default to `!`, unless
@@ -265,27 +283,24 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         let mut roots_reachable_from_diverging = FxHashSet::default();
         let mut diverging_vids = vec![];
         let mut non_diverging_vids = vec![];
-        for &unsolved_vid in &unsolved_vids {
+        for unsolved_vid in unsolved_vids {
+            let root_vid = self.infcx.root_var(unsolved_vid);
             debug!(
-                "calculate_diverging_fallback: unsolved_vid={:?} diverges={:?}",
+                "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
                 unsolved_vid,
-                self.infcx.ty_vid_diverges(unsolved_vid)
+                root_vid,
+                diverging_roots.contains(&root_vid),
             );
-            match self.infcx.ty_vid_diverges(unsolved_vid) {
-                Diverging::Diverges => {
-                    diverging_vids.push(unsolved_vid);
-                    let root_vid = self.infcx.root_var(unsolved_vid);
-                    debug!(
-                        "calculate_diverging_fallback: root_vid={:?} reaches {:?}",
-                        root_vid,
-                        coercion_graph.depth_first_search(root_vid).collect::<Vec<_>>()
-                    );
-                    roots_reachable_from_diverging
-                        .extend(coercion_graph.depth_first_search(root_vid));
-                }
-                Diverging::NotDiverging => {
-                    non_diverging_vids.push(unsolved_vid);
-                }
+            if diverging_roots.contains(&root_vid) {
+                diverging_vids.push(unsolved_vid);
+                debug!(
+                    "calculate_diverging_fallback: root_vid={:?} reaches {:?}",
+                    root_vid,
+                    coercion_graph.depth_first_search(root_vid).collect::<Vec<_>>()
+                );
+                roots_reachable_from_diverging.extend(coercion_graph.depth_first_search(root_vid));
+            } else {
+                non_diverging_vids.push(unsolved_vid);
             }
         }
         debug!(
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index ed01dae59f6..562d05d3ef9 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -286,6 +286,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
+        for a in &adj {
+            if let Adjust::NeverToAny = a.kind {
+                if a.target.is_ty_var() {
+                    self.diverging_type_vars.borrow_mut().insert(a.target);
+                    debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
+                }
+            }
+        }
+
         let autoborrow_mut = adj.iter().any(|adj| {
             matches!(
                 adj,
diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs
index 6006c8f7513..f7552c1f4eb 100644
--- a/compiler/rustc_typeck/src/check/inherited.rs
+++ b/compiler/rustc_typeck/src/check/inherited.rs
@@ -1,6 +1,7 @@
 use super::callee::DeferredCallResolution;
 use super::MaybeInProgressTables;
 
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefIdMap, LocalDefId};
 use rustc_hir::HirIdMap;
@@ -56,6 +57,11 @@ pub struct Inherited<'a, 'tcx> {
     pub(super) constness: hir::Constness,
 
     pub(super) body_id: Option<hir::BodyId>,
+
+    /// Whenever we introduce an adjustment from `!` into a type variable,
+    /// we record that type variable here. This is later used to inform
+    /// fallback. See the `fallback` module for details.
+    pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
 }
 
 impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
@@ -121,6 +127,7 @@ impl Inherited<'a, 'tcx> {
             deferred_call_resolutions: RefCell::new(Default::default()),
             deferred_cast_checks: RefCell::new(Vec::new()),
             deferred_generator_interiors: RefCell::new(Vec::new()),
+            diverging_type_vars: RefCell::new(Default::default()),
             constness,
             body_id,
         }