Skip to content

Commit 3d83fc9

Browse files
committed
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.
1 parent c01bfbd commit 3d83fc9

File tree

6 files changed

+87
-52
lines changed

6 files changed

+87
-52
lines changed

src/librustc/ty/sty.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
12901290
}
12911291
}
12921292

1293+
pub fn is_ty_infer(&self) -> bool {
1294+
match self.sty {
1295+
TyInfer(_) => true,
1296+
_ => false,
1297+
}
1298+
}
1299+
12931300
pub fn is_phantom_data(&self) -> bool {
12941301
if let TyAdt(def, _) = self.sty {
12951302
def.is_phantom_data()

src/librustc_typeck/check/cast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
392392
}
393393

394394
pub fn check(mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
395-
self.expr_ty = fcx.structurally_resolved_type(self.span, self.expr_ty);
396-
self.cast_ty = fcx.structurally_resolved_type(self.span, self.cast_ty);
395+
self.expr_ty = fcx.resolved_type(self.span, self.expr_ty);
396+
self.cast_ty = fcx.resolved_type(self.span, self.cast_ty);
397397

398398
debug!("check_cast({}, {:?} as {:?})",
399399
self.expr.id,

src/librustc_typeck/check/mod.rs

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -858,9 +858,8 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
858858
fcx
859859
};
860860

861-
fcx.select_all_obligations_and_apply_defaults();
862-
fcx.closure_analyze(body);
863861
fcx.select_obligations_where_possible();
862+
fcx.closure_analyze(body);
864863
fcx.check_casts();
865864
fcx.resolve_generator_interiors(def_id);
866865
fcx.select_all_obligations_or_error();
@@ -2129,13 +2128,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21292128
}
21302129

21312130
/// Apply "fallbacks" to some types
2132-
/// unconstrained types get replaced with ! or () (depending on whether
2131+
/// unconstrained types get replaced with ! or () (depending on whether
21332132
/// feature(never_type) is enabled), unconstrained ints with i32, and
21342133
/// unconstrained floats with f64.
21352134
fn default_type_parameters(&self) {
2136-
use rustc::ty::error::UnconstrainedNumeric::Neither;
2137-
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
2138-
21392135
// Defaulting inference variables becomes very dubious if we have
21402136
// encountered type-checking errors. Therefore, if we think we saw
21412137
// some errors in this function, just resolve all uninstanted type
@@ -2152,34 +2148,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21522148

21532149
for ty in &self.unsolved_variables() {
21542150
let resolved = self.resolve_type_vars_if_possible(ty);
2155-
if self.type_var_diverges(resolved) {
2156-
debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges",
2157-
resolved);
2158-
self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
2159-
self.tcx.mk_diverging_default());
2160-
} else {
2161-
match self.type_is_unconstrained_numeric(resolved) {
2162-
UnconstrainedInt => {
2163-
debug!("default_type_parameters: defaulting `{:?}` to `i32`",
2164-
resolved);
2165-
self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.i32)
2166-
},
2167-
UnconstrainedFloat => {
2168-
debug!("default_type_parameters: defaulting `{:?}` to `f32`",
2169-
resolved);
2170-
self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.f64)
2171-
}
2172-
Neither => { }
2173-
}
2151+
if resolved.is_ty_infer() {
2152+
self.apply_diverging_fallback_to_type(ty);
2153+
self.apply_numeric_fallback_to_type(ty);
21742154
}
21752155
}
21762156
}
21772157

2178-
// Implements type inference fallback algorithm
2179-
fn select_all_obligations_and_apply_defaults(&self) {
2180-
self.select_obligations_where_possible();
2181-
self.default_type_parameters();
2182-
self.select_obligations_where_possible();
2158+
fn apply_diverging_fallback_to_type(&self, ty: Ty<'tcx>) {
2159+
assert!(ty.is_ty_infer());
2160+
if self.type_var_diverges(ty) {
2161+
debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges", ty);
2162+
self.demand_eqtype(syntax_pos::DUMMY_SP, ty, self.tcx.mk_diverging_default());
2163+
}
2164+
}
2165+
2166+
fn apply_numeric_fallback_to_type(&self, ty: Ty<'tcx>) {
2167+
use rustc::ty::error::UnconstrainedNumeric::Neither;
2168+
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
2169+
2170+
assert!(ty.is_ty_infer());
2171+
let fallback = match self.type_is_unconstrained_numeric(ty) {
2172+
UnconstrainedInt => self.tcx.types.i32,
2173+
UnconstrainedFloat => self.tcx.types.f64,
2174+
Neither => return,
2175+
};
2176+
debug!("default_type_parameters: defaulting `{:?}` to `{:?}`", ty, fallback);
2177+
self.demand_eqtype(syntax_pos::DUMMY_SP, ty, fallback);
21832178
}
21842179

21852180
fn select_all_obligations_or_error(&self) {
@@ -2189,7 +2184,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21892184
// resolutions are handled by now.
21902185
assert!(self.deferred_call_resolutions.borrow().is_empty());
21912186

2192-
self.select_all_obligations_and_apply_defaults();
2187+
self.select_obligations_where_possible();
2188+
self.default_type_parameters();
21932189

21942190
let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
21952191

@@ -4954,21 +4950,51 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
49544950
});
49554951
}
49564952

4957-
// Resolves `typ` by a single level if `typ` is a type variable. If no
4958-
// resolution is possible, then an error is reported.
4953+
// Resolves `typ` by a single level if `typ` is a type variable.
4954+
// If no resolution is possible, then an error is reported.
4955+
// Numeric inference variables may be left unresolved.
49594956
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
49604957
let mut ty = self.resolve_type_vars_with_obligations(ty);
4961-
if ty.is_ty_var() {
4962-
// If not, error.
4963-
if !self.is_tainted_by_errors() {
4964-
type_error_struct!(self.tcx.sess, sp, ty, E0619,
4965-
"the type of this value must be known in this context")
4966-
.emit();
4958+
if !ty.is_ty_var() {
4959+
ty
4960+
} else {
4961+
// Try divering fallback.
4962+
self.apply_diverging_fallback_to_type(ty);
4963+
ty = self.resolve_type_vars_with_obligations(ty);
4964+
if !ty.is_ty_var() {
4965+
ty
4966+
} else { // Fallback failed, error.
4967+
self.must_be_known_in_context(sp, ty)
49674968
}
4968-
self.demand_suptype(sp, self.tcx.types.err, ty);
4969-
ty = self.tcx.types.err;
49704969
}
4971-
ty
4970+
}
4971+
4972+
// Same as `structurally_resolved_type` but also resolves numeric vars, with fallback.
4973+
pub fn resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
4974+
let mut ty = self.resolve_type_vars_with_obligations(ty);
4975+
if !ty.is_ty_infer() {
4976+
return ty;
4977+
} else {
4978+
// Try diverging or numeric fallback.
4979+
self.apply_diverging_fallback_to_type(ty);
4980+
self.apply_numeric_fallback_to_type(ty);
4981+
ty = self.resolve_type_vars_with_obligations(ty);
4982+
if !ty.is_ty_infer() {
4983+
ty
4984+
} else { // Fallback failed, error.
4985+
self.must_be_known_in_context(sp, ty)
4986+
}
4987+
}
4988+
}
4989+
4990+
fn must_be_known_in_context(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
4991+
if !self.is_tainted_by_errors() {
4992+
type_error_struct!(self.tcx.sess, sp, ty, E0619,
4993+
"the type of this value must be known in this context")
4994+
.emit();
4995+
}
4996+
self.demand_suptype(sp, self.tcx.types.err, ty);
4997+
self.tcx.types.err
49724998
}
49734999

49745000
fn with_breakable_ctxt<F: FnOnce() -> R, R>(&self, id: ast::NodeId,

src/test/compile-fail/derived-errors/issue-31997.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ fn closure<F, T>(x: F) -> Result<T, ()>
2020
}
2121

2222
fn foo() -> Result<(), ()> {
23-
try!(closure(|| bar(0 as *mut _))); //~ ERROR cannot find function `bar` in this scope
23+
try!(closure(|| bar(0 as *mut _)));
24+
//~^ ERROR cannot find function `bar` in this scope
25+
//~^^ ERROR cannot cast to a pointer of an unknown kind
2426
Ok(())
2527
}
2628

src/test/ui/interior-mutability/interior-mutability.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error[E0277]: the trait bound `std::cell::UnsafeCell<i32>: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell<i32>`
1+
error[E0277]: the trait bound `std::cell::UnsafeCell<{integer}>: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell<{integer}>`
22
--> $DIR/interior-mutability.rs:15:5
33
|
44
15 | catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound
5-
| ^^^^^^^^^^^^ the type std::cell::UnsafeCell<i32> may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
5+
| ^^^^^^^^^^^^ the type std::cell::UnsafeCell<{integer}> may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
66
|
7-
= help: within `std::cell::Cell<i32>`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<i32>`
8-
= note: required because it appears within the type `std::cell::Cell<i32>`
9-
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell<i32>`
10-
= note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:15:18: 15:35 x:&std::cell::Cell<i32>]`
7+
= help: within `std::cell::Cell<{integer}>`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<{integer}>`
8+
= note: required because it appears within the type `std::cell::Cell<{integer}>`
9+
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell<{integer}>`
10+
= note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:15:18: 15:35 x:&std::cell::Cell<{integer}>]`
1111
= note: required by `std::panic::catch_unwind`
1212

1313
error: aborting due to previous error

src/test/ui/mismatched_types/issue-26480.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ error[E0308]: mismatched types
77
37 | write!(hello);
88
| -------------- in this macro invocation
99

10-
error[E0605]: non-primitive cast: `{integer}` as `()`
10+
error[E0605]: non-primitive cast: `i32` as `()`
1111
--> $DIR/issue-26480.rs:32:19
1212
|
1313
32 | ($x:expr) => ($x as ()) //~ ERROR non-primitive cast

0 commit comments

Comments
 (0)