Skip to content

Commit eb926dd

Browse files
committed
typeck: Unify if-else blocks, match arms and array elements by coercing where possible.
1 parent d2c6bef commit eb926dd

File tree

11 files changed

+422
-171
lines changed

11 files changed

+422
-171
lines changed

src/librustc/middle/ty/relate.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R,
139139
relate_substs(relation, opt_variances, a_subst, b_subst)
140140
}
141141

142-
fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
143-
variances: Option<&ty::ItemVariances>,
144-
a_subst: &Substs<'tcx>,
145-
b_subst: &Substs<'tcx>)
146-
-> RelateResult<'tcx, Substs<'tcx>>
142+
pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
143+
variances: Option<&ty::ItemVariances>,
144+
a_subst: &Substs<'tcx>,
145+
b_subst: &Substs<'tcx>)
146+
-> RelateResult<'tcx, Substs<'tcx>>
147147
where R: TypeRelation<'a,'tcx>
148148
{
149149
let mut substs = Substs::empty();

src/librustc_typeck/check/_match.rs

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const;
1515
use middle::subst::Substs;
1616
use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
1717
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
18-
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
18+
use check::{demand, FnCtxt, Expectation};
1919
use check::{check_expr_with_lvalue_pref};
2020
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
21+
use check::coercion;
2122
use lint;
2223
use require_same_types;
2324
use util::nodemap::FnvHashMap;
@@ -492,54 +493,67 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
492493
// of execution reach it, we will panic, so bottom is an appropriate
493494
// type in that case)
494495
let expected = expected.adjust_for_branches(fcx);
495-
let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
496-
let bty = match expected {
497-
// We don't coerce to `()` so that if the match expression is a
498-
// statement it's branches can have any consistent type. That allows
499-
// us to give better error messages (pointing to a usually better
500-
// arm for inconsistent arms or to the whole match when a `()` type
501-
// is required).
502-
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
503-
check_expr_coercable_to_type(fcx, &arm.body, ety);
504-
ety
505-
}
506-
_ => {
507-
check_expr_with_expectation(fcx, &arm.body, expected);
508-
fcx.node_ty(arm.body.id)
496+
let mut result_ty = fcx.infcx().next_diverging_ty_var();
497+
let coerce_first = match expected {
498+
// We don't coerce to `()` so that if the match expression is a
499+
// statement it's branches can have any consistent type. That allows
500+
// us to give better error messages (pointing to a usually better
501+
// arm for inconsistent arms or to the whole match when a `()` type
502+
// is required).
503+
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
504+
ety
505+
}
506+
_ => result_ty
507+
};
508+
for (i, arm) in arms.iter().enumerate() {
509+
if let Some(ref e) = arm.guard {
510+
check_expr_has_type(fcx, e, tcx.types.bool);
511+
}
512+
check_expr_with_expectation(fcx, &arm.body, expected);
513+
let arm_ty = fcx.expr_ty(&arm.body);
514+
515+
if result_ty.references_error() || arm_ty.references_error() {
516+
result_ty = tcx.types.err;
517+
continue;
518+
}
519+
520+
// Handle the fallback arm of a desugared if-let like a missing else.
521+
let is_if_let_fallback = match match_src {
522+
hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
523+
i == arms.len() - 1 && arm_ty.is_nil()
509524
}
525+
_ => false
510526
};
511527

512-
if let Some(ref e) = arm.guard {
513-
check_expr_has_type(fcx, &e, tcx.types.bool);
514-
}
528+
let origin = if is_if_let_fallback {
529+
TypeOrigin::IfExpressionWithNoElse(expr.span)
530+
} else {
531+
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src)
532+
};
515533

516-
if result_ty.references_error() || bty.references_error() {
517-
tcx.types.err
534+
let result = if is_if_let_fallback {
535+
fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty)
536+
} else if i == 0 {
537+
// Special-case the first arm, as it has no "previous expressions".
538+
coercion::try(fcx, &arm.body, coerce_first)
518539
} else {
519-
let (origin, expected, found) = match match_src {
520-
/* if-let construct without an else block */
521-
hir::MatchSource::IfLetDesugar { contains_else_clause }
522-
if !contains_else_clause => (
523-
TypeOrigin::IfExpressionWithNoElse(expr.span),
524-
bty,
525-
result_ty,
526-
),
527-
_ => (
528-
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src),
529-
result_ty,
530-
bty,
531-
),
532-
};
540+
let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
541+
coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body)
542+
};
533543

534-
infer::common_supertype(
535-
fcx.infcx(),
536-
origin,
537-
true,
538-
expected,
539-
found,
540-
)
541-
}
542-
});
544+
result_ty = match result {
545+
Ok(ty) => ty,
546+
Err(e) => {
547+
let (expected, found) = if is_if_let_fallback {
548+
(arm_ty, result_ty)
549+
} else {
550+
(result_ty, arm_ty)
551+
};
552+
fcx.infcx().report_mismatched_types(origin, expected, found, e);
553+
fcx.tcx().types.err
554+
}
555+
};
556+
}
543557

544558
fcx.write_ty(expr.id, result_ty);
545559
}

src/librustc_typeck/check/cast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ impl<'tcx> CastCheck<'tcx> {
240240
if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
241241
// Attempt a coercion to a fn pointer type.
242242
let res = coercion::try(fcx, self.expr,
243-
self.expr_ty, fcx.tcx().mk_ty(ty::TyFnPtr(f)));
243+
fcx.tcx().mk_ty(ty::TyFnPtr(f)));
244244
if !res.is_ok() {
245245
return Err(CastError::NonScalar);
246246
}
@@ -390,7 +390,7 @@ impl<'tcx> CastCheck<'tcx> {
390390
}
391391

392392
fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool {
393-
coercion::try(fcx, self.expr, self.expr_ty, self.cast_ty).is_ok()
393+
coercion::try(fcx, self.expr, self.cast_ty).is_ok()
394394
}
395395

396396
}

0 commit comments

Comments
 (0)