port the match code to use CoerceMany

`match { }` now (correctly?) indicates divergence, which results in more
unreachable warnings. We also avoid fallback to `!` if there is just one
arm (see new test: `match-unresolved-one-arm.rs`).
This commit is contained in:
Niko Matsakis 2017-03-17 09:51:31 -04:00
parent dad3140407
commit 56847af916
5 changed files with 95 additions and 51 deletions

View File

@ -16,6 +16,7 @@
use rustc::traits::ObligationCauseCode;
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
use check::{FnCtxt, Expectation, Diverges};
use check::coercion::CoerceMany;
use util::nodemap::FxHashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
@ -414,6 +415,20 @@ pub fn check_match(&self,
discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
self.check_expr_has_type(discrim, discrim_ty);
};
// If the discriminant diverges, the match is pointless (e.g.,
// `match (return) { }`).
self.warn_if_unreachable(expr.id, expr.span, "expression");
// If there are no arms, that is a diverging match; a special case.
if arms.is_empty() {
self.diverges.set(self.diverges.get() | Diverges::Always);
return tcx.types.never;
}
// Otherwise, we have to union together the types that the
// arms produce and so forth.
let discrim_diverges = self.diverges.get();
self.diverges.set(Diverges::Maybe);
@ -426,6 +441,7 @@ pub fn check_match(&self,
self.check_pat(&p, discrim_ty);
all_pats_diverge &= self.diverges.get();
}
// As discussed with @eddyb, this is for disabling unreachable_code
// warnings on patterns (they're now subsumed by unreachable_patterns
// warnings).
@ -444,20 +460,21 @@ pub fn check_match(&self,
// on any empty type and is therefore unreachable; should the flow
// of execution reach it, we will panic, so bottom is an appropriate
// type in that case)
let expected = expected.adjust_for_branches(self);
let mut result_ty = self.next_diverging_ty_var(
TypeVariableOrigin::DivergingBlockExpr(expr.span));
let mut all_arms_diverge = Diverges::WarnedAlways;
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 != self.tcx.mk_nil() => {
ety
}
_ => result_ty
let expected = expected.adjust_for_branches(self);
let mut coercion = {
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 != self.tcx.mk_nil() => ety,
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
};
CoerceMany::new(coerce_first)
};
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
@ -470,11 +487,6 @@ pub fn check_match(&self,
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
all_arms_diverge &= self.diverges.get();
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 } => {
@ -483,47 +495,23 @@ pub fn check_match(&self,
_ => false
};
let cause = if is_if_let_fallback {
self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse)
if is_if_let_fallback {
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
assert!(arm_ty.is_nil());
coercion.coerce_forced_unit(self, &cause);
} else {
self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
arm_span: arm.body.span,
source: match_src
})
};
let result = if is_if_let_fallback {
self.eq_types(true, &cause, arm_ty, result_ty)
.map(|infer_ok| {
self.register_infer_ok_obligations(infer_ok);
arm_ty
})
} else if i == 0 {
// Special-case the first arm, as it has no "previous expressions".
self.try_coerce(&arm.body, arm_ty, coerce_first)
} else {
let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
self.try_find_coercion_lub(&cause, prev_arms, result_ty, &arm.body, arm_ty)
};
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)
};
self.report_mismatched_types(&cause, expected, found, e).emit();
self.tcx.types.err
}
};
});
coercion.coerce(self, &cause, &arm.body, arm_ty);
}
}
// We won't diverge unless the discriminant or all arms diverge.
self.diverges.set(discrim_diverges | all_arms_diverge);
result_ty
coercion.complete(self)
}
fn check_pat_struct(&self,

View File

@ -36,6 +36,7 @@ fn report(&mut self,
impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
/// Reports an obsolete syntax non-fatal error.
#[allow(unused_variables)]
#[allow(unreachable_code)]
fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
let (kind_str, desc, error) = match kind {
// Nothing here at the moment

View File

@ -0,0 +1,22 @@
// 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.
#![allow(warnings)]
#![deny(unreachable_code)]
enum Void { }
fn foo(v: Void) {
match v { }
let x = 2; //~ ERROR unreachable
}
fn main() {
}

View File

@ -0,0 +1,16 @@
// 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.
#![allow(unused_parens)]
#![deny(unreachable_code)]
fn main() {
match (return) { } //~ ERROR unreachable expression
}

View File

@ -0,0 +1,17 @@
// 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.
fn foo<T>() -> T { panic!("Rocks for my pillow") }
fn main() {
let x = match () { //~ ERROR type annotations needed
() => foo() // T here should be unresolved
};
}