typeck: Unify if-else blocks, match arms and array elements by coercing where possible.

This commit is contained in:
Eduard Burtescu 2016-03-06 17:33:30 +02:00
parent d2c6bef493
commit eb926dd4b7
11 changed files with 423 additions and 172 deletions

View File

@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R,
relate_substs(relation, opt_variances, a_subst, b_subst) relate_substs(relation, opt_variances, a_subst, b_subst)
} }
fn relate_substs<'a,'tcx:'a,R>(relation: &mut R, pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
variances: Option<&ty::ItemVariances>, variances: Option<&ty::ItemVariances>,
a_subst: &Substs<'tcx>, a_subst: &Substs<'tcx>,
b_subst: &Substs<'tcx>) b_subst: &Substs<'tcx>)
-> RelateResult<'tcx, Substs<'tcx>> -> RelateResult<'tcx, Substs<'tcx>>
where R: TypeRelation<'a,'tcx> where R: TypeRelation<'a,'tcx>
{ {
let mut substs = Substs::empty(); let mut substs = Substs::empty();

View File

@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const;
use middle::subst::Substs; use middle::subst::Substs;
use middle::ty::{self, Ty, TypeFoldable, LvaluePreference}; use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; use check::{demand, FnCtxt, Expectation};
use check::{check_expr_with_lvalue_pref}; use check::{check_expr_with_lvalue_pref};
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type}; use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
use check::coercion;
use lint; use lint;
use require_same_types; use require_same_types;
use util::nodemap::FnvHashMap; use util::nodemap::FnvHashMap;
@ -492,54 +493,67 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// of execution reach it, we will panic, so bottom is an appropriate // of execution reach it, we will panic, so bottom is an appropriate
// type in that case) // type in that case)
let expected = expected.adjust_for_branches(fcx); let expected = expected.adjust_for_branches(fcx);
let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| { let mut result_ty = fcx.infcx().next_diverging_ty_var();
let bty = match expected { let coerce_first = match expected {
// We don't coerce to `()` so that if the match expression is a // We don't coerce to `()` so that if the match expression is a
// statement it's branches can have any consistent type. That allows // statement it's branches can have any consistent type. That allows
// us to give better error messages (pointing to a usually better // us to give better error messages (pointing to a usually better
// arm for inconsistent arms or to the whole match when a `()` type // arm for inconsistent arms or to the whole match when a `()` type
// is required). // is required).
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => { Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
check_expr_coercable_to_type(fcx, &arm.body, ety); ety
ety }
} _ => result_ty
_ => { };
check_expr_with_expectation(fcx, &arm.body, expected); for (i, arm) in arms.iter().enumerate() {
fcx.node_ty(arm.body.id) if let Some(ref e) = arm.guard {
check_expr_has_type(fcx, e, tcx.types.bool);
}
check_expr_with_expectation(fcx, &arm.body, expected);
let arm_ty = fcx.expr_ty(&arm.body);
if result_ty.references_error() || arm_ty.references_error() {
result_ty = tcx.types.err;
continue;
}
// Handle the fallback arm of a desugared if-let like a missing else.
let is_if_let_fallback = match match_src {
hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
i == arms.len() - 1 && arm_ty.is_nil()
} }
_ => false
}; };
if let Some(ref e) = arm.guard { let origin = if is_if_let_fallback {
check_expr_has_type(fcx, &e, tcx.types.bool); TypeOrigin::IfExpressionWithNoElse(expr.span)
}
if result_ty.references_error() || bty.references_error() {
tcx.types.err
} else { } else {
let (origin, expected, found) = match match_src { TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src)
/* if-let construct without an else block */ };
hir::MatchSource::IfLetDesugar { contains_else_clause }
if !contains_else_clause => (
TypeOrigin::IfExpressionWithNoElse(expr.span),
bty,
result_ty,
),
_ => (
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src),
result_ty,
bty,
),
};
infer::common_supertype( let result = if is_if_let_fallback {
fcx.infcx(), fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty)
origin, } else if i == 0 {
true, // Special-case the first arm, as it has no "previous expressions".
expected, coercion::try(fcx, &arm.body, coerce_first)
found, } else {
) let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
} coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body)
}); };
result_ty = match result {
Ok(ty) => ty,
Err(e) => {
let (expected, found) = if is_if_let_fallback {
(arm_ty, result_ty)
} else {
(result_ty, arm_ty)
};
fcx.infcx().report_mismatched_types(origin, expected, found, e);
fcx.tcx().types.err
}
};
}
fcx.write_ty(expr.id, result_ty); fcx.write_ty(expr.id, result_ty);
} }

View File

@ -240,7 +240,7 @@ impl<'tcx> CastCheck<'tcx> {
if let ty::TyFnDef(_, _, f) = self.expr_ty.sty { if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
// Attempt a coercion to a fn pointer type. // Attempt a coercion to a fn pointer type.
let res = coercion::try(fcx, self.expr, let res = coercion::try(fcx, self.expr,
self.expr_ty, fcx.tcx().mk_ty(ty::TyFnPtr(f))); fcx.tcx().mk_ty(ty::TyFnPtr(f)));
if !res.is_ok() { if !res.is_ok() {
return Err(CastError::NonScalar); return Err(CastError::NonScalar);
} }
@ -390,7 +390,7 @@ impl<'tcx> CastCheck<'tcx> {
} }
fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool { fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool {
coercion::try(fcx, self.expr, self.expr_ty, self.cast_ty).is_ok() coercion::try(fcx, self.expr, self.cast_ty).is_ok()
} }
} }

View File

@ -62,7 +62,7 @@
use check::{autoderef, FnCtxt, UnresolvedTypeAction}; use check::{autoderef, FnCtxt, UnresolvedTypeAction};
use middle::infer::{self, Coercion, TypeOrigin}; use middle::infer::{Coercion, TypeOrigin, TypeTrace};
use middle::traits::{self, ObligationCause}; use middle::traits::{self, ObligationCause};
use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::traits::{predicate_for_trait_def, report_selection_error};
use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
@ -71,7 +71,7 @@ use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt}; use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt};
use middle::ty::fold::TypeFoldable; use middle::ty::fold::TypeFoldable;
use middle::ty::error::TypeError; use middle::ty::error::TypeError;
use middle::ty::relate::RelateResult; use middle::ty::relate::{relate_substs, RelateResult, TypeRelation};
use util::common::indent; use util::common::indent;
use std::cell::RefCell; use std::cell::RefCell;
@ -80,17 +80,30 @@ use rustc_front::hir;
struct Coerce<'a, 'tcx: 'a> { struct Coerce<'a, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'tcx>, fcx: &'a FnCtxt<'a, 'tcx>,
origin: infer::TypeOrigin, origin: TypeOrigin,
use_lub: bool,
unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>, unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
} }
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>; type CoerceResult<'tcx> = RelateResult<'tcx, (Ty<'tcx>, AutoAdjustment<'tcx>)>;
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
to_mutbl: hir::Mutability)
-> RelateResult<'tcx, ()> {
match (from_mutbl, to_mutbl) {
(hir::MutMutable, hir::MutMutable) |
(hir::MutImmutable, hir::MutImmutable) |
(hir::MutMutable, hir::MutImmutable) => Ok(()),
(hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
}
}
impl<'f, 'tcx> Coerce<'f, 'tcx> { impl<'f, 'tcx> Coerce<'f, 'tcx> {
fn new(fcx: &'a FnCtxt<'a, 'tcx>, origin: TypeOrigin) -> Self { fn new(fcx: &'f FnCtxt<'f, 'tcx>, origin: TypeOrigin) -> Self {
Coerce { Coerce {
fcx: fcx, fcx: fcx,
origin: origin, origin: origin,
use_lub: false,
unsizing_obligations: RefCell::new(vec![]) unsizing_obligations: RefCell::new(vec![])
} }
} }
@ -99,9 +112,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.fcx.tcx() self.fcx.tcx()
} }
fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { /// Unify two types (using sub or lub) and produce a noop coercion.
try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b)); fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
Ok(None) // No coercion required. let infcx = self.fcx.infcx();
infcx.commit_if_ok(|_| {
let trace = TypeTrace::types(self.origin, false, a, b);
if self.use_lub {
infcx.lub(false, trace).relate(&a, &b)
} else {
infcx.sub(false, trace).relate(&a, &b)
}
}).and_then(|ty| self.identity(ty))
}
/// Synthesize an identity adjustment.
fn identity(&self, ty: Ty<'tcx>) -> CoerceResult<'tcx> {
Ok((ty, AdjustDerefRef(AutoDerefRef {
autoderefs: 0,
autoref: None,
unsize: None
})))
} }
fn coerce<'a, E, I>(&self, fn coerce<'a, E, I>(&self,
@ -118,7 +148,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// Just ignore error types. // Just ignore error types.
if a.references_error() || b.references_error() { if a.references_error() || b.references_error() {
return Ok(None); return self.identity(b);
} }
// Consider coercing the subtype to a DST // Consider coercing the subtype to a DST
@ -137,7 +167,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
} }
ty::TyRef(_, mt_b) => { ty::TyRef(_, mt_b) => {
return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl); return self.coerce_borrowed_pointer(exprs, a, b, mt_b.mutbl);
} }
_ => {} _ => {}
@ -156,8 +186,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.coerce_from_fn_pointer(a, a_f, b) self.coerce_from_fn_pointer(a, a_f, b)
} }
_ => { _ => {
// Otherwise, just use subtyping rules. // Otherwise, just use unification rules.
self.subtype(a, b) self.unify(a, b)
} }
} }
} }
@ -166,7 +196,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
/// To match `A` with `B`, autoderef will be performed, /// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary. /// calling `deref`/`deref_mut` where necessary.
fn coerce_borrowed_pointer<'a, E, I>(&self, fn coerce_borrowed_pointer<'a, E, I>(&self,
span: Span,
exprs: &E, exprs: &E,
a: Ty<'tcx>, a: Ty<'tcx>,
b: Ty<'tcx>, b: Ty<'tcx>,
@ -188,7 +217,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
ty::TyRef(_, mt_a) => { ty::TyRef(_, mt_a) => {
try!(coerce_mutbls(mt_a.mutbl, mutbl_b)); try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
} }
_ => return self.subtype(a, b) _ => return self.unify(a, b)
} }
let span = self.origin.span(); let span = self.origin.span();
@ -210,19 +239,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
} }
let ty = self.tcx().mk_ref(r_borrow, let ty = self.tcx().mk_ref(r_borrow,
TypeAndMut {ty: inner_ty, mutbl: mutbl_b}); TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
if let Err(err) = self.subtype(ty, b) { match self.unify(ty, b) {
if first_error.is_none() { Err(err) => {
first_error = Some(err); if first_error.is_none() {
first_error = Some(err);
}
None
} }
None Ok((ty, _)) => Some(ty)
} else {
Some(())
} }
}); });
match success { match success {
Some(_) => { Some(ty) => {
Ok(Some(AdjustDerefRef(AutoDerefRef { Ok((ty, AdjustDerefRef(AutoDerefRef {
autoderefs: autoderefs, autoderefs: autoderefs,
autoref: autoref, autoref: autoref,
unsize: None unsize: None
@ -341,7 +371,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
unsize: Some(target) unsize: Some(target)
}; };
debug!("Success, coerced with {:?}", adjustment); debug!("Success, coerced with {:?}", adjustment);
Ok(Some(AdjustDerefRef(adjustment))) Ok((target, AdjustDerefRef(adjustment)))
} }
fn coerce_from_fn_pointer(&self, fn coerce_from_fn_pointer(&self,
@ -362,13 +392,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
match (fn_ty_a.unsafety, fn_ty_b.unsafety) { match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
(hir::Unsafety::Normal, hir::Unsafety::Unsafe) => { (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a); let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
try!(self.subtype(unsafe_a, b)); return self.unify(unsafe_a, b).map(|(ty, _)| {
return Ok(Some(AdjustUnsafeFnPointer)); (ty, AdjustUnsafeFnPointer)
});
} }
_ => {} _ => {}
} }
} }
self.subtype(a, b) self.unify(a, b)
} }
fn coerce_from_fn_item(&self, fn coerce_from_fn_item(&self,
@ -387,10 +418,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
match b.sty { match b.sty {
ty::TyFnPtr(_) => { ty::TyFnPtr(_) => {
let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a)); let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a));
try!(self.subtype(a_fn_pointer, b)); self.unify(a_fn_pointer, b).map(|(ty, _)| {
Ok(Some(AdjustReifyFnPointer)) (ty, AdjustReifyFnPointer)
})
} }
_ => self.subtype(a, b) _ => self.unify(a, b)
} }
} }
@ -407,29 +439,29 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
ty::TyRef(_, mt) => (true, mt), ty::TyRef(_, mt) => (true, mt),
ty::TyRawPtr(mt) => (false, mt), ty::TyRawPtr(mt) => (false, mt),
_ => { _ => {
return self.subtype(a, b); return self.unify(a, b);
} }
}; };
// Check that the types which they point at are compatible. // Check that the types which they point at are compatible.
let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty }); let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty });
try!(self.subtype(a_unsafe, b)); let (ty, noop) = try!(self.unify(a_unsafe, b));
try!(coerce_mutbls(mt_a.mutbl, mutbl_b)); try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
// Although references and unsafe ptrs have the same // Although references and unsafe ptrs have the same
// representation, we still register an AutoDerefRef so that // representation, we still register an AutoDerefRef so that
// regionck knows that the region for `a` must be valid here. // regionck knows that the region for `a` must be valid here.
if is_ref { Ok((ty, if is_ref {
Ok(Some(AdjustDerefRef(AutoDerefRef { AdjustDerefRef(AutoDerefRef {
autoderefs: 1, autoderefs: 1,
autoref: Some(AutoUnsafe(mutbl_b)), autoref: Some(AutoUnsafe(mutbl_b)),
unsize: None unsize: None
}))) })
} else if mt_a.mutbl != mutbl_b { } else if mt_a.mutbl != mutbl_b {
Ok(Some(AdjustMutToConstPointer)) AdjustMutToConstPointer
} else { } else {
Ok(None) noop
} }))
} }
} }
@ -437,7 +469,7 @@ fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>,
exprs: &E, exprs: &E,
a: Ty<'tcx>, a: Ty<'tcx>,
b: Ty<'tcx>) b: Ty<'tcx>)
-> RelateResult<'tcx, Ty<'tcx>> -> CoerceResult<'tcx>
where E: Fn() -> I, where E: Fn() -> I,
I: IntoIterator<Item=&'b hir::Expr> { I: IntoIterator<Item=&'b hir::Expr> {
@ -446,44 +478,153 @@ fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>,
let fcx = coerce.fcx; let fcx = coerce.fcx;
if let AdjustDerefRef(auto) = adjustment { if let AdjustDerefRef(auto) = adjustment {
if auto.unsize.is_some() { if auto.unsize.is_some() {
for obligation in coerce.unsizing_obligations.borrow_mut().drain() { let mut obligations = coerce.unsizing_obligations.borrow_mut();
for obligation in obligations.drain(..) {
fcx.register_predicate(obligation); fcx.register_predicate(obligation);
} }
} }
} }
if !adjustment.is_identity() { Ok((ty, adjustment))
debug!("Success, coerced with {:?}", adjustment);
for expr in exprs() {
assert!(!fcx.inh.tables.borrow().adjustments.contains(&expr.id));
fcx.write_adjustment(expr.id, adjustment);
}
}
Ok(ty)
} }
/// Attempt to coerce an expression from a type (a) to another type (b). /// Attempt to coerce an expression to a type, and return the
/// Adjustments are only recorded if the coercion was successful. /// adjusted type of the expression, if successful.
/// Adjustments are only recorded if the coercion succeeded.
/// The expressions *must not* have any pre-existing adjustments. /// The expressions *must not* have any pre-existing adjustments.
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &hir::Expr, expr: &hir::Expr,
a: Ty<'tcx>, target: Ty<'tcx>)
b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-> RelateResult<'tcx, ()> { let source = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr));
debug!("coercion::try({:?} -> {:?})", a, b); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span)); let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
fcx.infcx().commit_if_ok(|_| { fcx.infcx().commit_if_ok(|_| {
apply(&mut coerce, &|| Some(expr), a, b) let (ty, adjustment) =
}).map(|_| ()) try!(apply(&mut coerce, &|| Some(expr), source, target));
if !adjustment.is_identity() {
debug!("Success, coerced with {:?}", adjustment);
assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id));
fcx.write_adjustment(expr.id, adjustment);
}
Ok(ty)
})
} }
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability, /// Given some expressions, their known unified type and another expression,
to_mutbl: hir::Mutability) /// tries to unify the types, potentially inserting coercions on any of the
-> CoerceResult<'tcx> { /// provided expressions and returns their LUB (aka "common supertype").
match (from_mutbl, to_mutbl) { pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>,
(hir::MutMutable, hir::MutMutable) | origin: TypeOrigin,
(hir::MutImmutable, hir::MutImmutable) | exprs: E,
(hir::MutMutable, hir::MutImmutable) => Ok(None), prev_ty: Ty<'tcx>,
(hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability) new: &'b hir::Expr)
-> RelateResult<'tcx, Ty<'tcx>>
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
where E: Fn() -> I,
I: IntoIterator<Item=&'b hir::Expr> {
let prev_ty = fcx.resolve_type_vars_if_possible(prev_ty);
let new_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(new));
debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
let trace = TypeTrace::types(origin, true, prev_ty, new_ty);
let mut lub = fcx.infcx().lub(true, trace);
// Special-case that coercion alone cannot handle:
// Two function item types of differing IDs or Substs.
match (&prev_ty.sty, &new_ty.sty) {
(&ty::TyFnDef(a_def_id, a_substs, a_fty),
&ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
// The signature must always match.
let fty = try!(lub.relate(a_fty, b_fty));
if a_def_id == b_def_id {
// Same function, maybe the parameters match.
let substs = fcx.infcx().commit_if_ok(|_| {
relate_substs(&mut lub, None, a_substs, b_substs)
}).map(|s| fcx.tcx().mk_substs(s));
if let Ok(substs) = substs {
// We have a LUB of prev_ty and new_ty, just return it.
return Ok(fcx.tcx().mk_fn_def(a_def_id, substs, fty));
}
}
// Reify both sides and return the reified fn pointer type.
for expr in exprs().into_iter().chain(Some(new)) {
// No adjustments can produce a fn item, so this should never trip.
assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id));
fcx.write_adjustment(expr.id, AdjustReifyFnPointer);
}
return Ok(fcx.tcx().mk_fn_ptr(fty));
}
_ => {}
}
let mut coerce = Coerce::new(fcx, origin);
coerce.use_lub = true;
// First try to coerce the new expression to the type of the previous ones,
// but only if the new expression has no coercion already applied to it.
let mut first_error = None;
if !fcx.inh.tables.borrow().adjustments.contains_key(&new.id) {
let result = fcx.infcx().commit_if_ok(|_| {
apply(&mut coerce, &|| Some(new), new_ty, prev_ty)
});
match result {
Ok((ty, adjustment)) => {
if !adjustment.is_identity() {
fcx.write_adjustment(new.id, adjustment);
}
return Ok(ty);
}
Err(e) => first_error = Some(e)
}
}
// Then try to coerce the previous expressions to the type of the new one.
// This requires ensuring there are no coercions applied to *any* of the
// previous expressions, other than noop reborrows (ignoring lifetimes).
for expr in exprs() {
let noop = match fcx.inh.tables.borrow().adjustments.get(&expr.id) {
Some(&AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(AutoPtr(_, mutbl_adj)),
unsize: None
})) => match fcx.expr_ty(expr).sty {
ty::TyRef(_, mt_orig) => {
// Reborrow that we can safely ignore.
mutbl_adj == mt_orig.mutbl
}
_ => false
},
Some(_) => false,
None => true
};
if !noop {
return fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty));
}
}
match fcx.infcx().commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
Err(_) => {
// Avoid giving strange errors on failed attempts.
if let Some(e) = first_error {
Err(e)
} else {
fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty))
}
}
Ok((ty, adjustment)) => {
if !adjustment.is_identity() {
for expr in exprs() {
fcx.write_adjustment(expr.id, adjustment);
}
}
Ok(ty)
}
} }
} }

View File

@ -39,14 +39,10 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span, sp: Span,
expected: Ty<'tcx>, expected: Ty<'tcx>,
expr: &hir::Expr) { expr: &hir::Expr) {
let expr_ty = fcx.expr_ty(expr);
debug!("demand::coerce(expected = {:?}, expr_ty = {:?})",
expected,
expr_ty);
let expr_ty = fcx.resolve_type_vars_if_possible(expr_ty);
let expected = fcx.resolve_type_vars_if_possible(expected); let expected = fcx.resolve_type_vars_if_possible(expected);
let origin = TypeOrigin::Misc(sp); if let Err(e) = coercion::try(fcx, expr, expected) {
if let Err(e) = coercion::try(fcx, expr, expr_ty, expected) { let origin = TypeOrigin::Misc(sp);
let expr_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr));
fcx.infcx().report_mismatched_types(origin, expected, expr_ty, e); fcx.infcx().report_mismatched_types(origin, expected, expr_ty, e);
} }
} }

View File

@ -89,7 +89,7 @@ use middle::cstore::LOCAL_CRATE;
use middle::def::{self, Def}; use middle::def::{self, Def};
use middle::def_id::DefId; use middle::def_id::DefId;
use middle::infer; use middle::infer;
use middle::infer::{TypeOrigin, type_variable}; use middle::infer::{TypeOrigin, TypeTrace, type_variable};
use middle::pat_util::{self, pat_id_map}; use middle::pat_util::{self, pat_id_map};
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
use middle::traits::{self, report_fulfillment_errors}; use middle::traits::{self, report_fulfillment_errors};
@ -101,6 +101,7 @@ use middle::ty::{MethodCall, MethodCallee};
use middle::ty::adjustment; use middle::ty::adjustment;
use middle::ty::error::TypeError; use middle::ty::error::TypeError;
use middle::ty::fold::{TypeFolder, TypeFoldable}; use middle::ty::fold::{TypeFolder, TypeFoldable};
use middle::ty::relate::TypeRelation;
use middle::ty::util::Representability; use middle::ty::util::Representability;
use require_c_abi_if_variadic; use require_c_abi_if_variadic;
use rscope::{ElisionFailureInfo, RegionScope}; use rscope::{ElisionFailureInfo, RegionScope};
@ -2080,7 +2081,7 @@ pub fn autoderef<'a, 'b, 'tcx, E, I, T, F>(fcx: &FnCtxt<'a, 'tcx>,
// (i.e. it is an inference variable) because `Ty::builtin_deref` // (i.e. it is an inference variable) because `Ty::builtin_deref`
// and `try_overloaded_deref` both simply return `None` // and `try_overloaded_deref` both simply return `None`
// in such a case without producing spurious errors. // in such a case without producing spurious errors.
fcx.resolve_type_vars_if_possible(t) fcx.infcx().resolve_type_vars_if_possible(&t)
} }
}; };
if resolved_t.references_error() { if resolved_t.references_error() {
@ -2164,7 +2165,7 @@ fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the /// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the
/// actual type we assign to the *expression* is `T`. So this function just peels off the return /// actual type we assign to the *expression* is `T`. So this function just peels off the return
/// type by one layer to yield `T`. /// type by one layer to yield `T`.
fn make_overloaded_lvalue_return_type<'tcx>(tcx: &ty::ctxt<'tcx>, fn make_overloaded_lvalue_return_type<'tcx>(tcx: &TyCtxt<'tcx>,
method: MethodCallee<'tcx>) method: MethodCallee<'tcx>)
-> ty::TypeAndMut<'tcx> -> ty::TypeAndMut<'tcx>
{ {
@ -2834,30 +2835,52 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
check_block_with_expected(fcx, then_blk, expected); check_block_with_expected(fcx, then_blk, expected);
let then_ty = fcx.node_ty(then_blk.id); let then_ty = fcx.node_ty(then_blk.id);
let branches_ty = match opt_else_expr { let unit = fcx.tcx().mk_nil();
Some(ref else_expr) => { let (origin, expected, found, result) =
check_expr_with_expectation(fcx, &else_expr, expected); if let Some(else_expr) = opt_else_expr {
let else_ty = fcx.expr_ty(&else_expr); check_expr_with_expectation(fcx, else_expr, expected);
infer::common_supertype(fcx.infcx(), let else_ty = fcx.expr_ty(else_expr);
TypeOrigin::IfExpression(sp), let origin = TypeOrigin::IfExpression(sp);
true,
then_ty, // Only try to coerce-unify if we have a then expression
else_ty) // to assign coercions to, otherwise it's () or diverging.
} let result = if let Some(ref then) = then_blk.expr {
None => { let res = coercion::try_find_lub(fcx, origin, || Some(&**then),
infer::common_supertype(fcx.infcx(), then_ty, else_expr);
TypeOrigin::IfExpressionWithNoElse(sp),
false, // In case we did perform an adjustment, we have to update
then_ty, // the type of the block, because old trans still uses it.
fcx.tcx().mk_nil()) let adj = fcx.inh.tables.borrow().adjustments.get(&then.id).cloned();
} if res.is_ok() && adj.is_some() {
fcx.write_ty(then_blk.id, fcx.adjust_expr_ty(then, adj.as_ref()));
}
res
} else {
fcx.infcx().commit_if_ok(|_| {
let trace = TypeTrace::types(origin, true, then_ty, else_ty);
fcx.infcx().lub(true, trace).relate(&then_ty, &else_ty)
})
};
(origin, then_ty, else_ty, result)
} else {
let origin = TypeOrigin::IfExpressionWithNoElse(sp);
(origin, unit, then_ty,
fcx.infcx().eq_types(true, origin, unit, then_ty).map(|_| unit))
}; };
let cond_ty = fcx.expr_ty(cond_expr); let if_ty = match result {
let if_ty = if cond_ty.references_error() { Ok(ty) => {
fcx.tcx().types.err if fcx.expr_ty(cond_expr).references_error() {
} else { fcx.tcx().types.err
branches_ty } else {
ty
}
}
Err(e) => {
fcx.infcx().report_mismatched_types(origin, expected, found, e);
fcx.tcx().types.err
}
}; };
fcx.write_ty(id, if_ty); fcx.write_ty(id, if_ty);
@ -3497,23 +3520,30 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
} }
}); });
let typ = match uty { let mut unified = fcx.infcx().next_ty_var();
Some(uty) => { let coerce_to = uty.unwrap_or(unified);
for e in args {
check_expr_coercable_to_type(fcx, &e, uty); for (i, e) in args.iter().enumerate() {
check_expr_with_hint(fcx, e, coerce_to);
let e_ty = fcx.expr_ty(e);
let origin = TypeOrigin::Misc(e.span);
// Special-case the first element, as it has no "previous expressions".
let result = if i == 0 {
coercion::try(fcx, e, coerce_to)
} else {
let prev_elems = || args[..i].iter().map(|e| &**e);
coercion::try_find_lub(fcx, origin, prev_elems, unified, e)
};
match result {
Ok(ty) => unified = ty,
Err(e) => {
fcx.infcx().report_mismatched_types(origin, unified, e_ty, e);
} }
uty
} }
None => { }
let t: Ty = fcx.infcx().next_ty_var(); fcx.write_ty(id, tcx.mk_array(unified, args.len()));
for e in args {
check_expr_has_type(fcx, &e, t);
}
t
}
};
let typ = tcx.mk_array(typ, args.len());
fcx.write_ty(id, typ);
} }
hir::ExprRepeat(ref element, ref count_expr) => { hir::ExprRepeat(ref element, ref count_expr) => {
check_expr_has_type(fcx, &count_expr, tcx.types.usize); check_expr_has_type(fcx, &count_expr, tcx.types.usize);

View File

@ -20,13 +20,6 @@ trait Foo { fn foo() { /* this is a default fn */ } }
impl<T> Foo for T { /* `foo` is still default here */ } impl<T> Foo for T { /* `foo` is still default here */ }
fn main() { fn main() {
let f = if true { foo::<u8> } else { bar::<u8> };
//~^ ERROR if and else have incompatible types
//~| expected `fn(isize) -> isize {foo::<u8>}`
//~| found `fn(isize) -> isize {bar::<u8>}`
//~| expected fn item,
//~| found a different fn item
eq(foo::<u8>, bar::<u8>); eq(foo::<u8>, bar::<u8>);
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {foo::<u8>}` //~| expected `fn(isize) -> isize {foo::<u8>}`

View File

@ -17,7 +17,7 @@ fn main() {
let y = match x { let y = match x {
[] => None, [] => None,
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| expected `[_#0i; 2]` //~| expected `[_#1i; 2]`
//~| found `[_#7t; 0]` //~| found `[_#7t; 0]`
//~| expected an array with a fixed size of 2 elements //~| expected an array with a fixed size of 2 elements
//~| found one with 0 elements //~| found one with 0 elements

View File

@ -107,7 +107,7 @@ impl Debug for Player {
} }
fn str_to_direction(to_parse: &str) -> RoomDirection { fn str_to_direction(to_parse: &str) -> RoomDirection {
match to_parse { match to_parse { //~ ERROR match arms have incompatible types
"w" | "west" => RoomDirection::West, "w" | "west" => RoomDirection::West,
"e" | "east" => RoomDirection::East, "e" | "east" => RoomDirection::East,
"n" | "north" => RoomDirection::North, "n" | "north" => RoomDirection::North,
@ -116,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
"out" => RoomDirection::Out, "out" => RoomDirection::Out,
"up" => RoomDirection::Up, "up" => RoomDirection::Up,
"down" => RoomDirection::Down, "down" => RoomDirection::Down,
_ => None //~ ERROR mismatched types _ => None //~ NOTE match arm with an incompatible type
} }
} }

View File

@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
} }
fn main() { fn main() {
["hi"].bind(|x| [x] ); ["hi"].bind(|x| [x] );
//~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope //~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope
} }

View File

@ -0,0 +1,77 @@
// Copyright 2016 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.
// Check that coercions can unify if-else, match arms and array elements.
// Try to construct if-else chains, matches and arrays out of given expressions.
macro_rules! check {
($last:expr $(, $rest:expr)+) => {
// Last expression comes first because of whacky ifs and matches.
let _ = $(if false { $rest })else+ else { $last };
let _ = match 0 { $(_ if false => $rest,)+ _ => $last };
let _ = [$($rest,)+ $last];
}
}
// Check all non-uniform cases of 2 and 3 expressions of 2 types.
macro_rules! check2 {
($a:expr, $b:expr) => {
check!($a, $b);
check!($b, $a);
check!($a, $a, $b);
check!($a, $b, $a);
check!($a, $b, $b);
check!($b, $a, $a);
check!($b, $a, $b);
check!($b, $b, $a);
}
}
// Check all non-uniform cases of 2 and 3 expressions of 3 types.
macro_rules! check3 {
($a:expr, $b:expr, $c:expr) => {
// Delegate to check2 for cases where a type repeats.
check2!($a, $b);
check2!($b, $c);
check2!($a, $c);
// Check the remaining cases, i.e. permutations of ($a, $b, $c).
check!($a, $b, $c);
check!($a, $c, $b);
check!($b, $a, $c);
check!($b, $c, $a);
check!($c, $a, $b);
check!($c, $b, $a);
}
}
use std::mem::size_of;
fn foo() {}
fn bar() {}
pub fn main() {
check3!(foo, bar, foo as fn());
check3!(size_of::<u8>, size_of::<u16>, size_of::<usize> as fn() -> usize);
let s = String::from("bar");
check2!("foo", &s);
let a = [1, 2, 3];
let v = vec![1, 2, 3];
check2!(&a[..], &v);
// Make sure in-array coercion still works.
let _ = [("a", Default::default()), (Default::default(), "b"), (&s, &s)];
}