From 3d83fc914a0f116ad0e4412d5ccc0be24b3b7af2 Mon Sep 17 00:00:00 2001 From: "leonardo.yvens" Date: Wed, 13 Dec 2017 12:35:26 -0200 Subject: [PATCH] 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. --- src/librustc/ty/sty.rs | 7 ++ src/librustc_typeck/check/cast.rs | 4 +- src/librustc_typeck/check/mod.rs | 110 +++++++++++------- .../derived-errors/issue-31997.rs | 4 +- .../interior-mutability.stderr | 12 +- .../ui/mismatched_types/issue-26480.stderr | 2 +- 6 files changed, 87 insertions(+), 52 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index db7e4fe45ef7..d38ceb619e3c 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 48bd7b14fc96..bc157d6feeaf 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 cb80498f3e20..aff96e39cfc7 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 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 2e1d3c55a8f4..0385e3b8365d 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(x: F) -> Result } 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 f4beb44b82dc..dca9ab1b5415 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: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell` +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 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`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell` - = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:15:18: 15:35 x:&std::cell::Cell]` + = 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 5d25cb2f93c1..1b056486ad95 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