Skip to content

Commit 56847af

Browse files
committed
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`).
1 parent dad3140 commit 56847af

File tree

5 files changed

+95
-51
lines changed

5 files changed

+95
-51
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 39 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
1616
use rustc::traits::ObligationCauseCode;
1717
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
1818
use check::{FnCtxt, Expectation, Diverges};
19+
use check::coercion::CoerceMany;
1920
use util::nodemap::FxHashMap;
2021

2122
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -414,6 +415,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
414415
discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
415416
self.check_expr_has_type(discrim, discrim_ty);
416417
};
418+
419+
// If the discriminant diverges, the match is pointless (e.g.,
420+
// `match (return) { }`).
421+
self.warn_if_unreachable(expr.id, expr.span, "expression");
422+
423+
// If there are no arms, that is a diverging match; a special case.
424+
if arms.is_empty() {
425+
self.diverges.set(self.diverges.get() | Diverges::Always);
426+
return tcx.types.never;
427+
}
428+
429+
// Otherwise, we have to union together the types that the
430+
// arms produce and so forth.
431+
417432
let discrim_diverges = self.diverges.get();
418433
self.diverges.set(Diverges::Maybe);
419434

@@ -426,6 +441,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
426441
self.check_pat(&p, discrim_ty);
427442
all_pats_diverge &= self.diverges.get();
428443
}
444+
429445
// As discussed with @eddyb, this is for disabling unreachable_code
430446
// warnings on patterns (they're now subsumed by unreachable_patterns
431447
// warnings).
@@ -444,20 +460,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
444460
// on any empty type and is therefore unreachable; should the flow
445461
// of execution reach it, we will panic, so bottom is an appropriate
446462
// type in that case)
447-
let expected = expected.adjust_for_branches(self);
448-
let mut result_ty = self.next_diverging_ty_var(
449-
TypeVariableOrigin::DivergingBlockExpr(expr.span));
450463
let mut all_arms_diverge = Diverges::WarnedAlways;
451-
let coerce_first = match expected {
452-
// We don't coerce to `()` so that if the match expression is a
453-
// statement it's branches can have any consistent type. That allows
454-
// us to give better error messages (pointing to a usually better
455-
// arm for inconsistent arms or to the whole match when a `()` type
456-
// is required).
457-
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => {
458-
ety
459-
}
460-
_ => result_ty
464+
465+
let expected = expected.adjust_for_branches(self);
466+
467+
let mut coercion = {
468+
let coerce_first = match expected {
469+
// We don't coerce to `()` so that if the match expression is a
470+
// statement it's branches can have any consistent type. That allows
471+
// us to give better error messages (pointing to a usually better
472+
// arm for inconsistent arms or to the whole match when a `()` type
473+
// is required).
474+
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
475+
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
476+
};
477+
CoerceMany::new(coerce_first)
461478
};
462479

463480
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
@@ -470,11 +487,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
470487
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
471488
all_arms_diverge &= self.diverges.get();
472489

473-
if result_ty.references_error() || arm_ty.references_error() {
474-
result_ty = tcx.types.err;
475-
continue;
476-
}
477-
478490
// Handle the fallback arm of a desugared if-let like a missing else.
479491
let is_if_let_fallback = match match_src {
480492
hir::MatchSource::IfLetDesugar { contains_else_clause: false } => {
@@ -483,47 +495,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
483495
_ => false
484496
};
485497

486-
let cause = if is_if_let_fallback {
487-
self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse)
498+
if is_if_let_fallback {
499+
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
500+
assert!(arm_ty.is_nil());
501+
coercion.coerce_forced_unit(self, &cause);
488502
} else {
489-
self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
503+
let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
490504
arm_span: arm.body.span,
491505
source: match_src
492-
})
493-
};
494-
495-
let result = if is_if_let_fallback {
496-
self.eq_types(true, &cause, arm_ty, result_ty)
497-
.map(|infer_ok| {
498-
self.register_infer_ok_obligations(infer_ok);
499-
arm_ty
500-
})
501-
} else if i == 0 {
502-
// Special-case the first arm, as it has no "previous expressions".
503-
self.try_coerce(&arm.body, arm_ty, coerce_first)
504-
} else {
505-
let prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
506-
self.try_find_coercion_lub(&cause, prev_arms, result_ty, &arm.body, arm_ty)
507-
};
508-
509-
result_ty = match result {
510-
Ok(ty) => ty,
511-
Err(e) => {
512-
let (expected, found) = if is_if_let_fallback {
513-
(arm_ty, result_ty)
514-
} else {
515-
(result_ty, arm_ty)
516-
};
517-
self.report_mismatched_types(&cause, expected, found, e).emit();
518-
self.tcx.types.err
519-
}
520-
};
506+
});
507+
coercion.coerce(self, &cause, &arm.body, arm_ty);
508+
}
521509
}
522510

523511
// We won't diverge unless the discriminant or all arms diverge.
524512
self.diverges.set(discrim_diverges | all_arms_diverge);
525513

526-
result_ty
514+
coercion.complete(self)
527515
}
528516

529517
fn check_pat_struct(&self,

src/libsyntax/parse/obsolete.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub trait ParserObsoleteMethods {
3636
impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
3737
/// Reports an obsolete syntax non-fatal error.
3838
#[allow(unused_variables)]
39+
#[allow(unreachable_code)]
3940
fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
4041
let (kind_str, desc, error) = match kind {
4142
// Nothing here at the moment
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(warnings)]
12+
#![deny(unreachable_code)]
13+
14+
enum Void { }
15+
16+
fn foo(v: Void) {
17+
match v { }
18+
let x = 2; //~ ERROR unreachable
19+
}
20+
21+
fn main() {
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(unused_parens)]
12+
#![deny(unreachable_code)]
13+
14+
fn main() {
15+
match (return) { } //~ ERROR unreachable expression
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo<T>() -> T { panic!("Rocks for my pillow") }
12+
13+
fn main() {
14+
let x = match () { //~ ERROR type annotations needed
15+
() => foo() // T here should be unresolved
16+
};
17+
}

0 commit comments

Comments
 (0)