Skip to content
7 changes: 7 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
140 changes: 46 additions & 94 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,10 +858,21 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
fcx
};

fcx.select_all_obligations_and_apply_defaults();
fcx.closure_analyze(body);
// All type checking constraints were added, try to fallback unsolved variables.
fcx.select_obligations_where_possible();
for ty in &fcx.unsolved_variables() {
fcx.fallback_if_possible(ty);
}
fcx.select_obligations_where_possible();

// Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
fcx.check_casts();

// Closure and generater analysis may run after fallback
// because they don't constrain other type variables.
fcx.closure_analyze(body);
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
fcx.resolve_generator_interiors(def_id);
fcx.select_all_obligations_or_error();

Expand Down Expand Up @@ -2128,74 +2139,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}

/// Apply "fallbacks" to some types
/// 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) {
// Tries to apply a fallback to `ty` if it is an unsolved variable.
// Non-numerics get replaced with ! or () (depending on whether
// feature(never_type) is enabled), unconstrained ints with i32,
// unconstrained floats with f64.
// Fallback becomes very dubious if we have encountered type-checking errors.
// In that case, fallback to TyError.
fn fallback_if_possible(&self, ty: Ty<'tcx>) {
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
// varibles to TyError.
if self.is_tainted_by_errors() {
for ty in &self.unsolved_variables() {
if let ty::TyInfer(_) = self.shallow_resolve(ty).sty {
debug!("default_type_parameters: defaulting `{:?}` to error", ty);
self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx().types.err);
}
}
return;
}

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 => { }
}
}
}
}

// 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();
assert!(ty.is_ty_infer());
let fallback = match self.type_is_unconstrained_numeric(ty) {
_ if self.is_tainted_by_errors() => self.tcx().types.err,
UnconstrainedInt => self.tcx.types.i32,
UnconstrainedFloat => self.tcx.types.f64,
Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
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) {
debug!("select_all_obligations_or_error");

// upvar inference should have ensured that all deferred call
// resolutions are handled by now.
assert!(self.deferred_call_resolutions.borrow().is_empty());

self.select_all_obligations_and_apply_defaults();

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

match fulfillment_cx.select_all_or_error(self) {
Ok(()) => { }
Err(errors) => { self.report_fulfillment_errors(&errors, self.inh.body_id); }
if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
self.report_fulfillment_errors(&errors, self.inh.body_id);
}
}

Expand Down Expand Up @@ -4954,39 +4923,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
});
}

fn structurally_resolve_type_or_else<F>(&self, sp: Span, ty: Ty<'tcx>, f: F)
-> Ty<'tcx>
where F: Fn() -> Ty<'tcx>
{
let mut ty = self.resolve_type_vars_with_obligations(ty);

if ty.is_ty_var() {
let alternative = f();

// If not, error.
if alternative.is_ty_var() || alternative.references_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();
}
self.demand_suptype(sp, self.tcx.types.err, ty);
ty = self.tcx.types.err;
} else {
self.demand_suptype(sp, alternative, ty);
ty = alternative;
}
}

ty
}

// 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> {
self.structurally_resolve_type_or_else(sp, ty, || {
let ty = self.resolve_type_vars_with_obligations(ty);
if !ty.is_ty_var() {
ty
} else {
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,
Expand Down
20 changes: 20 additions & 0 deletions src/test/run-pass/cast-does-fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub fn main() {
// Test that these type check correctly.
(&42u8 >> 4) as usize;
(&42u8 << 4) as usize;

let cap = 512 * 512;
cap as u8;
// Assert `cap` did not get inferred to `u8` and overflowed.
assert_ne!(cap, 0);
}