about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2020-11-22 06:10:50 -0500
committerMark Rousskov <mark.simulacrum@gmail.com>2021-08-20 17:27:50 -0400
commit60cc00f540658cb82bccf88275f9cf409215a6b8 (patch)
tree74d6783601e316041ceacee9a47cd1c94b23a8e0 /compiler
parentfaf84263f2acd638801069beea0d4b9dd662c111 (diff)
downloadrust-60cc00f540658cb82bccf88275f9cf409215a6b8.tar.gz
rust-60cc00f540658cb82bccf88275f9cf409215a6b8.zip
move `fallback_if_possible` and friends to fallback.rs
Along the way, simplify and document the logic more clearly.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs41
-rw-r--r--compiler/rustc_middle/src/ty/error.rs6
-rw-r--r--compiler/rustc_typeck/src/check/fallback.rs111
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs84
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs10
5 files changed, 127 insertions, 125 deletions
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index b3d6e44d80e..80f1a0d3254 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -23,7 +23,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir::interpret::EvalToConstValueResult;
 use rustc_middle::traits::select;
-use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
@@ -679,6 +679,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         t.fold_with(&mut self.freshener())
     }
 
+    /// Returns whether `ty` is a diverging type variable or not.
+    /// (If `ty` is not a type variable at all, returns not diverging.)
+    ///
+    /// No attempt is made to resolve `ty`.
     pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging {
         match *ty.kind() {
             ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
@@ -686,6 +690,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
+    /// Returns the origin of the type variable identified by `vid`, or `None`
+    /// if this is not a type variable.
+    ///
+    /// No attempt is made to resolve `ty`.
+    pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> {
+        match *ty.kind() {
+            ty::Infer(ty::TyVar(vid)) => {
+                Some(*self.inner.borrow_mut().type_variables().var_origin(vid))
+            }
+            _ => None,
+        }
+    }
+
     pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
         freshen::TypeFreshener::new(self, false)
     }
@@ -695,28 +712,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         freshen::TypeFreshener::new(self, true)
     }
 
-    pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {
-        use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
-        use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
-        match *ty.kind() {
-            ty::Infer(ty::IntVar(vid)) => {
-                if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() {
-                    Neither
-                } else {
-                    UnconstrainedInt
-                }
-            }
-            ty::Infer(ty::FloatVar(vid)) => {
-                if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() {
-                    Neither
-                } else {
-                    UnconstrainedFloat
-                }
-            }
-            _ => Neither,
-        }
-    }
-
     pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
         let mut inner = self.inner.borrow_mut();
         let mut vars: Vec<Ty<'_>> = inner
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 3846d9ffdba..1aa6c84dbc4 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -71,12 +71,6 @@ pub enum TypeError<'tcx> {
     TargetFeatureCast(DefId),
 }
 
-pub enum UnconstrainedNumeric {
-    UnconstrainedFloat,
-    UnconstrainedInt,
-    Neither,
-}
-
 /// Explains the source of a type err in a short, human readable way. This is meant to be placed
 /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
 /// afterwards to present additional details, particularly when it comes to lifetime-related
diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs
index 5d884f1f546..69a8970ae09 100644
--- a/compiler/rustc_typeck/src/check/fallback.rs
+++ b/compiler/rustc_typeck/src/check/fallback.rs
@@ -1,5 +1,6 @@
-use crate::check::FallbackMode;
 use crate::check::FnCtxt;
+use rustc_infer::infer::type_variable::Diverging;
+use rustc_middle::ty::{self, Ty};
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
     pub(super) fn type_inference_fallback(&self) {
@@ -12,8 +13,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // The first time, we do *not* replace opaque types.
         for ty in &self.unsolved_variables() {
             debug!("unsolved_variable = {:?}", ty);
-            fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::NoOpaque);
+            fallback_has_occurred |= self.fallback_if_possible(ty);
         }
+
         // We now see if we can make progress. This might
         // cause us to unify inference variables for opaque types,
         // since we may have unified some other type variables
@@ -43,10 +45,113 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // unconstrained opaque type variables, in addition to performing
         // other kinds of fallback.
         for ty in &self.unsolved_variables() {
-            fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::All);
+            fallback_has_occurred |= self.fallback_opaque_type_vars(ty);
         }
 
         // See if we can make any more progress.
         self.select_obligations_where_possible(fallback_has_occurred, |_| {});
     }
+
+    // Tries to apply a fallback to `ty` if it is an unsolved variable.
+    //
+    // - Unconstrained ints are replaced with `i32`.
+    //
+    // - Unconstrained floats are replaced with with `f64`.
+    //
+    // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
+    //   is enabled. Otherwise, they are replaced with `()`.
+    //
+    // Fallback becomes very dubious if we have encountered type-checking errors.
+    // In that case, fallback to Error.
+    // The return value indicates whether fallback has occurred.
+    fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
+        // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
+        // is an unsolved variable, and we determine its fallback based
+        // solely on how it was created, not what other type variables
+        // it may have been unified with since then.
+        //
+        // The reason this matters is that other attempts at fallback may
+        // (in principle) conflict with this fallback, and we wish to generate
+        // a type error in that case. (However, this actually isn't true right now,
+        // because we're only using the builtin fallback rules. This would be
+        // true if we were using user-supplied fallbacks. But it's still useful
+        // to write the code to detect bugs.)
+        //
+        // (Note though that if we have a general type variable `?T` that is then unified
+        // with an integer type variable `?I` that ultimately never gets
+        // resolved to a special integral type, `?T` is not considered unsolved,
+        // but `?I` is. The same is true for float variables.)
+        let fallback = match ty.kind() {
+            _ if self.is_tainted_by_errors() => self.tcx.ty_error(),
+            ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
+            ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
+            _ => match self.type_var_diverges(ty) {
+                Diverging::Diverges => self.tcx.mk_diverging_default(),
+                Diverging::NotDiverging => return false,
+            },
+        };
+        debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
+
+        let span = self
+            .infcx
+            .type_var_origin(ty)
+            .map(|origin| origin.span)
+            .unwrap_or(rustc_span::DUMMY_SP);
+        self.demand_eqtype(span, ty, fallback);
+        true
+    }
+
+    /// Second round of fallback: Unconstrained type variables
+    /// created from the instantiation of an opaque
+    /// type fall back to the opaque type itself. This is a
+    /// somewhat incomplete attempt to manage "identity passthrough"
+    /// for `impl Trait` types.
+    ///
+    /// For example, in this code:
+    ///
+    ///```
+    /// type MyType = impl Copy;
+    /// fn defining_use() -> MyType { true }
+    /// fn other_use() -> MyType { defining_use() }
+    /// ```
+    ///
+    /// `defining_use` will constrain the instantiated inference
+    /// variable to `bool`, while `other_use` will constrain
+    /// the instantiated inference variable to `MyType`.
+    ///
+    /// When we process opaque types during writeback, we
+    /// will handle cases like `other_use`, and not count
+    /// them as defining usages
+    ///
+    /// However, we also need to handle cases like this:
+    ///
+    /// ```rust
+    /// pub type Foo = impl Copy;
+    /// fn produce() -> Option<Foo> {
+    ///     None
+    ///  }
+    ///  ```
+    ///
+    /// In the above snippet, the inference variable created by
+    /// instantiating `Option<Foo>` will be completely unconstrained.
+    /// We treat this as a non-defining use by making the inference
+    /// variable fall back to the opaque type itself.
+    fn fallback_opaque_type_vars(&self, ty: Ty<'tcx>) -> bool {
+        let span = self
+            .infcx
+            .type_var_origin(ty)
+            .map(|origin| origin.span)
+            .unwrap_or(rustc_span::DUMMY_SP);
+        let oty = self.inner.borrow().opaque_types_vars.get(ty).map(|v| *v);
+        if let Some(opaque_ty) = oty {
+            debug!(
+                "fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}",
+                ty, opaque_ty
+            );
+            self.demand_eqtype(span, ty, opaque_ty);
+            true
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index a9b1fb790ed..bb80f0879a4 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -4,7 +4,7 @@ use crate::astconv::{
 };
 use crate::check::callee::{self, DeferredCallResolution};
 use crate::check::method::{self, MethodCallee, SelfSource};
-use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy};
+use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
 
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::captures::Captures;
@@ -17,7 +17,6 @@ use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, GenericArg, Node, QPath, TyKind};
 use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
 use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::type_variable::Diverging;
 use rustc_infer::infer::{InferOk, InferResult};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::fold::TypeFoldable;
@@ -636,87 +635,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    // Tries to apply a fallback to `ty` if it is an unsolved variable.
-    //
-    // - Unconstrained ints are replaced with `i32`.
-    //
-    // - Unconstrained floats are replaced with with `f64`.
-    //
-    // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
-    //   is enabled. Otherwise, they are replaced with `()`.
-    //
-    // Fallback becomes very dubious if we have encountered type-checking errors.
-    // In that case, fallback to Error.
-    // The return value indicates whether fallback has occurred.
-    pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
-        use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
-        use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
-
-        assert!(ty.is_ty_infer());
-        let fallback = match self.type_is_unconstrained_numeric(ty) {
-            _ if self.is_tainted_by_errors() => self.tcx().ty_error(),
-            UnconstrainedInt => self.tcx.types.i32,
-            UnconstrainedFloat => self.tcx.types.f64,
-            Neither => match self.type_var_diverges(ty) {
-                Diverging::Diverges => self.tcx.mk_diverging_default(),
-
-                Diverging::NotDiverging => {
-                    // This type variable was created from the instantiation of an opaque
-                    // type. The fact that we're attempting to perform fallback for it
-                    // means that the function neither constrained it to a concrete
-                    // type, nor to the opaque type itself.
-                    //
-                    // For example, in this code:
-                    //
-                    //```
-                    // type MyType = impl Copy;
-                    // fn defining_use() -> MyType { true }
-                    // fn other_use() -> MyType { defining_use() }
-                    // ```
-                    //
-                    // `defining_use` will constrain the instantiated inference
-                    // variable to `bool`, while `other_use` will constrain
-                    // the instantiated inference variable to `MyType`.
-                    //
-                    // When we process opaque types during writeback, we
-                    // will handle cases like `other_use`, and not count
-                    // them as defining usages
-                    //
-                    // However, we also need to handle cases like this:
-                    //
-                    // ```rust
-                    // pub type Foo = impl Copy;
-                    // fn produce() -> Option<Foo> {
-                    //     None
-                    //  }
-                    //  ```
-                    //
-                    // In the above snippet, the inference variable created by
-                    // instantiating `Option<Foo>` will be completely unconstrained.
-                    // We treat this as a non-defining use by making the inference
-                    // variable fall back to the opaque type itself.
-                    if let FallbackMode::All = mode {
-                        if let Some(opaque_ty) = self.infcx.inner.borrow().opaque_types_vars.get(ty)
-                        {
-                            debug!(
-                                "fallback_if_possible: falling back opaque type var {:?} to {:?}",
-                                ty, opaque_ty
-                            );
-                            *opaque_ty
-                        } else {
-                            return false;
-                        }
-                    } else {
-                        return false;
-                    }
-                }
-            },
-        };
-        debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
-        self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
-        true
-    }
-
     pub(in super::super) fn select_all_obligations_or_error(&self) {
         debug!("select_all_obligations_or_error");
         if let Err(errors) = self
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 9c6a441d01a..ad7e96e2833 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -872,16 +872,6 @@ enum TupleArgumentsFlag {
     TupleArguments,
 }
 
-/// Controls how we perform fallback for unconstrained
-/// type variables.
-enum FallbackMode {
-    /// Do not fallback type variables to opaque types.
-    NoOpaque,
-    /// Perform all possible kinds of fallback, including
-    /// turning type variables to opaque types.
-    All,
-}
-
 /// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
 #[derive(Copy, Clone)]
 struct MaybeInProgressTables<'a, 'tcx> {