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:
parent
dad3140407
commit
56847af916
@ -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,
|
||||
|
@ -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
|
||||
|
22
src/test/compile-fail/match-no-arms-unreachable-after.rs
Normal file
22
src/test/compile-fail/match-no-arms-unreachable-after.rs
Normal 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() {
|
||||
}
|
@ -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
|
||||
}
|
17
src/test/compile-fail/match-unresolved-one-arm.rs
Normal file
17
src/test/compile-fail/match-unresolved-one-arm.rs
Normal 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
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user