about summary refs log tree commit diff
diff options
context:
space:
mode:
authorleonardo.yvens <leoyvens@gmail.com>2017-12-13 12:35:26 -0200
committerleonardo.yvens <leoyvens@gmail.com>2018-01-27 15:42:54 -0200
commit3d83fc914a0f116ad0e4412d5ccc0be24b3b7af2 (patch)
tree89b6902ac09c683ebb0231b214eccd713032f6f7
parentc01bfbd02b57a1aa9d8a39cd907f799d23c2a230 (diff)
Lazy numeric fallback.
This refactoring tries to make numeric fallback easier to reason about.
Instead of applying all fallbacks at an arbitrary point in the middle
of inference, we apply the fallback only when necessary and only for
the variable that requires it, which for numeric fallback turns out to
be just casts.

The only visible consequence seems to be some error messages where
instead of getting `i32` we get `{integer}` because we are less eager
about fallback.

The bigger goal is to make it easier to integrate user fallbacks into
inference, if we ever figure that out.
-rw-r--r--src/librustc/ty/sty.rs7
-rw-r--r--src/librustc_typeck/check/cast.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs110
-rw-r--r--src/test/compile-fail/derived-errors/issue-31997.rs4
-rw-r--r--src/test/ui/interior-mutability/interior-mutability.stderr12
-rw-r--r--src/test/ui/mismatched_types/issue-26480.stderr2
6 files changed, 87 insertions, 52 deletions
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index db7e4fe45ef..d38ceb619e3 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1290,6 +1290,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    pub fn is_ty_infer(&self) -> bool {
+        match self.sty {
+            TyInfer(_) => true,
+            _ => false,
+        }
+    }
+
     pub fn is_phantom_data(&self) -> bool {
         if let TyAdt(def, _) = self.sty {
             def.is_phantom_data()
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 48bd7b14fc9..bc157d6feea 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -392,8 +392,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
     }
 
     pub fn check(mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
-        self.expr_ty = fcx.structurally_resolved_type(self.span, self.expr_ty);
-        self.cast_ty = fcx.structurally_resolved_type(self.span, self.cast_ty);
+        self.expr_ty = fcx.resolved_type(self.span, self.expr_ty);
+        self.cast_ty = fcx.resolved_type(self.span, self.cast_ty);
 
         debug!("check_cast({}, {:?} as {:?})",
                self.expr.id,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index cb80498f3e2..aff96e39cfc 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -858,9 +858,8 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             fcx
         };
 
-        fcx.select_all_obligations_and_apply_defaults();
-        fcx.closure_analyze(body);
         fcx.select_obligations_where_possible();
+        fcx.closure_analyze(body);
         fcx.check_casts();
         fcx.resolve_generator_interiors(def_id);
         fcx.select_all_obligations_or_error();
@@ -2129,13 +2128,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 
     /// Apply "fallbacks" to some types
-    /// unconstrained types get replaced with ! or  () (depending on whether
+    /// unconstrained types get replaced with ! or () (depending on whether
     /// feature(never_type) is enabled), unconstrained ints with i32, and
     /// unconstrained floats with f64.
     fn default_type_parameters(&self) {
-        use rustc::ty::error::UnconstrainedNumeric::Neither;
-        use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
-
         // Defaulting inference variables becomes very dubious if we have
         // encountered type-checking errors. Therefore, if we think we saw
         // some errors in this function, just resolve all uninstanted type
@@ -2152,34 +2148,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         for ty in &self.unsolved_variables() {
             let resolved = self.resolve_type_vars_if_possible(ty);
-            if self.type_var_diverges(resolved) {
-                debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges",
-                       resolved);
-                self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
-                                   self.tcx.mk_diverging_default());
-            } else {
-                match self.type_is_unconstrained_numeric(resolved) {
-                    UnconstrainedInt => {
-                        debug!("default_type_parameters: defaulting `{:?}` to `i32`",
-                               resolved);
-                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.i32)
-                    },
-                    UnconstrainedFloat => {
-                        debug!("default_type_parameters: defaulting `{:?}` to `f32`",
-                               resolved);
-                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.f64)
-                    }
-                    Neither => { }
-                }
+            if resolved.is_ty_infer() {
+                self.apply_diverging_fallback_to_type(ty);
+                self.apply_numeric_fallback_to_type(ty);
             }
         }
     }
 
-    // Implements type inference fallback algorithm
-    fn select_all_obligations_and_apply_defaults(&self) {
-        self.select_obligations_where_possible();
-        self.default_type_parameters();
-        self.select_obligations_where_possible();
+    fn apply_diverging_fallback_to_type(&self, ty: Ty<'tcx>) {
+        assert!(ty.is_ty_infer());
+        if self.type_var_diverges(ty) {
+            debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges", ty);
+            self.demand_eqtype(syntax_pos::DUMMY_SP, ty, self.tcx.mk_diverging_default());
+        }
+    }
+
+    fn apply_numeric_fallback_to_type(&self, ty: Ty<'tcx>) {
+        use rustc::ty::error::UnconstrainedNumeric::Neither;
+        use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
+
+        assert!(ty.is_ty_infer());
+        let fallback = match self.type_is_unconstrained_numeric(ty) {
+            UnconstrainedInt => self.tcx.types.i32,
+            UnconstrainedFloat => self.tcx.types.f64,
+            Neither => return,
+        };
+        debug!("default_type_parameters: defaulting `{:?}` to `{:?}`", ty, fallback);
+        self.demand_eqtype(syntax_pos::DUMMY_SP, ty, fallback);
     }
 
     fn select_all_obligations_or_error(&self) {
@@ -2189,7 +2184,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // resolutions are handled by now.
         assert!(self.deferred_call_resolutions.borrow().is_empty());
 
-        self.select_all_obligations_and_apply_defaults();
+        self.select_obligations_where_possible();
+        self.default_type_parameters();
 
         let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
 
@@ -4954,21 +4950,51 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         });
     }
 
-    // Resolves `typ` by a single level if `typ` is a type variable. If no
-    // resolution is possible, then an error is reported.
+    // Resolves `typ` by a single level if `typ` is a type variable.
+    // If no resolution is possible, then an error is reported.
+    // Numeric inference variables may be left unresolved.
     pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
         let mut ty = self.resolve_type_vars_with_obligations(ty);
-        if ty.is_ty_var() {
-            // If not, error.
-            if !self.is_tainted_by_errors() {
-                type_error_struct!(self.tcx.sess, sp, ty, E0619,
-                                    "the type of this value must be known in this context")
-                .emit();
+        if !ty.is_ty_var() {
+            ty
+        } else {
+            // Try divering fallback.
+            self.apply_diverging_fallback_to_type(ty);
+            ty = self.resolve_type_vars_with_obligations(ty);
+            if !ty.is_ty_var() {
+                ty
+            } else { // Fallback failed, error.
+                self.must_be_known_in_context(sp, ty)
             }
-            self.demand_suptype(sp, self.tcx.types.err, ty);
-            ty = self.tcx.types.err;
         }
-        ty
+    }
+
+    // Same as `structurally_resolved_type` but also resolves numeric vars, with fallback.
+    pub fn resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let mut ty = self.resolve_type_vars_with_obligations(ty);
+        if !ty.is_ty_infer() {
+            return ty;
+        } else {
+            // Try diverging or numeric fallback.
+            self.apply_diverging_fallback_to_type(ty);
+            self.apply_numeric_fallback_to_type(ty);
+            ty = self.resolve_type_vars_with_obligations(ty);
+            if !ty.is_ty_infer() {
+                ty
+            } else { // Fallback failed, error.
+                self.must_be_known_in_context(sp, ty)
+            }
+        }
+    }
+
+    fn must_be_known_in_context(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !self.is_tainted_by_errors() {
+            type_error_struct!(self.tcx.sess, sp, ty, E0619,
+                                "the type of this value must be known in this context")
+            .emit();
+        }
+        self.demand_suptype(sp, self.tcx.types.err, ty);
+        self.tcx.types.err
     }
 
     fn with_breakable_ctxt<F: FnOnce() -> R, R>(&self, id: ast::NodeId,
diff --git a/src/test/compile-fail/derived-errors/issue-31997.rs b/src/test/compile-fail/derived-errors/issue-31997.rs
index 2e1d3c55a8f..0385e3b8365 100644
--- a/src/test/compile-fail/derived-errors/issue-31997.rs
+++ b/src/test/compile-fail/derived-errors/issue-31997.rs
@@ -20,7 +20,9 @@ fn closure<F, T>(x: F) -> Result<T, ()>
 }
 
 fn foo() -> Result<(), ()> {
-    try!(closure(|| bar(0 as *mut _))); //~ ERROR cannot find function `bar` in this scope
+    try!(closure(|| bar(0 as *mut _)));
+    //~^ ERROR cannot find function `bar` in this scope
+    //~^^ ERROR cannot cast to a pointer of an unknown kind
     Ok(())
 }
 
diff --git a/src/test/ui/interior-mutability/interior-mutability.stderr b/src/test/ui/interior-mutability/interior-mutability.stderr
index f4beb44b82d..dca9ab1b541 100644
--- a/src/test/ui/interior-mutability/interior-mutability.stderr
+++ b/src/test/ui/interior-mutability/interior-mutability.stderr
@@ -1,13 +1,13 @@
-error[E0277]: the trait bound `std::cell::UnsafeCell<i32>: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell<i32>`
+error[E0277]: the trait bound `std::cell::UnsafeCell<{integer}>: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell<{integer}>`
   --> $DIR/interior-mutability.rs:15:5
    |
 15 |     catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound
-   |     ^^^^^^^^^^^^ the type std::cell::UnsafeCell<i32> may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
+   |     ^^^^^^^^^^^^ the type std::cell::UnsafeCell<{integer}> may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
    |
-   = help: within `std::cell::Cell<i32>`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<i32>`
-   = note: required because it appears within the type `std::cell::Cell<i32>`
-   = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell<i32>`
-   = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:15:18: 15:35 x:&std::cell::Cell<i32>]`
+   = help: within `std::cell::Cell<{integer}>`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<{integer}>`
+   = note: required because it appears within the type `std::cell::Cell<{integer}>`
+   = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell<{integer}>`
+   = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:15:18: 15:35 x:&std::cell::Cell<{integer}>]`
    = note: required by `std::panic::catch_unwind`
 
 error: aborting due to previous error
diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr
index 5d25cb2f93c..1b056486ad9 100644
--- a/src/test/ui/mismatched_types/issue-26480.stderr
+++ b/src/test/ui/mismatched_types/issue-26480.stderr
@@ -7,7 +7,7 @@ error[E0308]: mismatched types
 37 |     write!(hello);
    |     -------------- in this macro invocation
 
-error[E0605]: non-primitive cast: `{integer}` as `()`
+error[E0605]: non-primitive cast: `i32` as `()`
   --> $DIR/issue-26480.rs:32:19
    |
 32 |     ($x:expr) => ($x as ()) //~ ERROR non-primitive cast