Skip to content

Commit

Permalink
typeck: Unify if-else blocks, match arms and array elements by coerci…
Browse files Browse the repository at this point in the history
…ng where possible.
  • Loading branch information
eddyb committed Mar 9, 2016
1 parent d2c6bef commit eb926dd
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 171 deletions.
10 changes: 5 additions & 5 deletions src/librustc/middle/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R,
relate_substs(relation, opt_variances, a_subst, b_subst)
}

fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
variances: Option<&ty::ItemVariances>,
a_subst: &Substs<'tcx>,
b_subst: &Substs<'tcx>)
-> RelateResult<'tcx, Substs<'tcx>>
pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
variances: Option<&ty::ItemVariances>,
a_subst: &Substs<'tcx>,
b_subst: &Substs<'tcx>)
-> RelateResult<'tcx, Substs<'tcx>>
where R: TypeRelation<'a,'tcx>
{
let mut substs = Substs::empty();
Expand Down
100 changes: 57 additions & 43 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const;
use middle::subst::Substs;
use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
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::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
use check::coercion;
use lint;
use require_same_types;
use util::nodemap::FnvHashMap;
Expand Down Expand Up @@ -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
// type in that case)
let expected = expected.adjust_for_branches(fcx);
let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
let bty = match expected {
// We don't coerce to `()` so that if the match expression is a
// statement it's branches can have any consistent type. That allows
// us to give better error messages (pointing to a usually better
// arm for inconsistent arms or to the whole match when a `()` type
// is required).
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
check_expr_coercable_to_type(fcx, &arm.body, ety);
ety
}
_ => {
check_expr_with_expectation(fcx, &arm.body, expected);
fcx.node_ty(arm.body.id)
let mut result_ty = fcx.infcx().next_diverging_ty_var();
let coerce_first = match expected {
// We don't coerce to `()` so that if the match expression is a
// statement it's branches can have any consistent type. That allows
// us to give better error messages (pointing to a usually better
// arm for inconsistent arms or to the whole match when a `()` type
// is required).
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
ety
}
_ => result_ty
};
for (i, arm) in arms.iter().enumerate() {
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 {
check_expr_has_type(fcx, &e, tcx.types.bool);
}
let origin = if is_if_let_fallback {
TypeOrigin::IfExpressionWithNoElse(expr.span)
} else {
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src)
};

if result_ty.references_error() || bty.references_error() {
tcx.types.err
let result = if is_if_let_fallback {
fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty)
} else if i == 0 {
// Special-case the first arm, as it has no "previous expressions".
coercion::try(fcx, &arm.body, coerce_first)
} else {
let (origin, expected, found) = match 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,
),
};
let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body)
};

infer::common_supertype(
fcx.infcx(),
origin,
true,
expected,
found,
)
}
});
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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<'tcx> CastCheck<'tcx> {
if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
// Attempt a coercion to a fn pointer type.
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() {
return Err(CastError::NonScalar);
}
Expand Down Expand Up @@ -390,7 +390,7 @@ impl<'tcx> CastCheck<'tcx> {
}

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()
}

}
Loading

0 comments on commit eb926dd

Please sign in to comment.