Skip to content

Commit 09c2193

Browse files
committed
Enforce 'cond: bool' in while-expr + improve reachability diags.
1 parent 0511729 commit 09c2193

File tree

7 files changed

+50
-41
lines changed

7 files changed

+50
-41
lines changed

src/librustc/hir/lowering.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ use syntax::ext::hygiene::{Mark, SyntaxContext};
6363
use syntax::print::pprust;
6464
use syntax::ptr::P;
6565
use syntax::source_map::{self, respan, ExpnInfo, CompilerDesugaringKind, Spanned};
66-
use syntax::source_map::CompilerDesugaringKind::IfTemporary;
66+
use syntax::source_map::CompilerDesugaringKind::CondTemporary;
6767
use syntax::std_inject;
6868
use syntax::symbol::{kw, sym, Symbol};
6969
use syntax::tokenstream::{TokenStream, TokenTree};
@@ -4406,7 +4406,7 @@ impl<'a> LoweringContext<'a> {
44064406
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
44074407
// to preserve drop semantics since `if cond { ... }`
44084408
// don't let temporaries live outside of `cond`.
4409-
let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
4409+
let span_block = self.mark_span_with_reason(CondTemporary, cond.span, None);
44104410
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
44114411
// to preserve drop semantics since `if cond { ... }` does not
44124412
// let temporaries live outside of `cond`.
@@ -4482,7 +4482,7 @@ impl<'a> LoweringContext<'a> {
44824482
// ```
44834483
// 'label: loop {
44844484
// match DropTemps($cond) {
4485-
// true => $block,
4485+
// true => $body,
44864486
// _ => break,
44874487
// }
44884488
// }
@@ -4500,11 +4500,12 @@ impl<'a> LoweringContext<'a> {
45004500
let else_arm = this.arm(hir_vec![else_pat], else_expr);
45014501

45024502
// Lower condition:
4503+
let span_block = this.mark_span_with_reason(CondTemporary, cond.span, None);
45034504
let cond = this.with_loop_condition_scope(|this| this.lower_expr(cond));
45044505
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
45054506
// to preserve drop semantics since `if cond { ... }` does not
45064507
// let temporaries live outside of `cond`.
4507-
let cond = this.expr_drop_temps(cond.span, P(cond), ThinVec::new());
4508+
let cond = this.expr_drop_temps(span_block, P(cond), ThinVec::new());
45084509

45094510
let match_expr = this.expr_match(
45104511
cond.span,

src/librustc/ich/impls_syntax.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat {
415415
});
416416

417417
impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind {
418-
IfTemporary,
418+
CondTemporary,
419419
Async,
420420
Await,
421421
QuestionMark,

src/librustc_typeck/check/_match.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
180180
// then that's equivalent to there existing a LUB.
181181
if let Some(mut err) = self.demand_suptype_diag(pat.span, expected, pat_ty) {
182182
err.emit_unless(discrim_span
183-
.filter(|&s| s.is_compiler_desugaring(CompilerDesugaringKind::IfTemporary))
183+
.filter(|&s| {
184+
// In the case of `if`- and `while`-expressions we've already checked
185+
// that `scrutinee: bool`. We know that the pattern is `true`,
186+
// so an error here would be a duplicate and from the wrong POV.
187+
s.is_compiler_desugaring(CompilerDesugaringKind::CondTemporary)
188+
})
184189
.is_some());
185190
}
186191

@@ -624,14 +629,15 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
624629
let tcx = self.tcx;
625630

626631
use hir::MatchSource::*;
627-
let (source_if, if_no_else, if_desugar) = match match_src {
632+
let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
628633
IfDesugar { contains_else_clause } => (true, !contains_else_clause, true),
629634
IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false),
635+
WhileDesugar => (false, false, true),
630636
_ => (false, false, false),
631637
};
632638

633639
// Type check the descriminant and get its type.
634-
let discrim_ty = if if_desugar {
640+
let discrim_ty = if force_scrutinee_bool {
635641
// Here we want to ensure:
636642
//
637643
// 1. That default match bindings are *not* accepted in the condition of an
@@ -651,7 +657,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
651657
return tcx.types.never;
652658
}
653659

654-
self.warn_arms_when_scrutinee_diverges(arms, source_if);
660+
self.warn_arms_when_scrutinee_diverges(arms, match_src);
655661

656662
// Otherwise, we have to union together the types that the
657663
// arms produce and so forth.
@@ -726,7 +732,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
726732
if source_if {
727733
let then_expr = &arms[0].body;
728734
match (i, if_no_else) {
729-
(0, _) => coercion.coerce(self, &self.misc(span), then_expr, arm_ty),
735+
(0, _) => coercion.coerce(self, &self.misc(span), &arm.body, arm_ty),
730736
(_, true) => self.if_fallback_coercion(span, then_expr, &mut coercion),
731737
(_, _) => {
732738
let then_ty = prior_arm_ty.unwrap();
@@ -771,9 +777,14 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
771777

772778
/// When the previously checked expression (the scrutinee) diverges,
773779
/// warn the user about the match arms being unreachable.
774-
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm], source_if: bool) {
780+
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm], source: hir::MatchSource) {
775781
if self.diverges.get().always() {
776-
let msg = if source_if { "block in `if` expression" } else { "arm" };
782+
use hir::MatchSource::*;
783+
let msg = match source {
784+
IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression",
785+
WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
786+
_ => "arm",
787+
};
777788
for arm in arms {
778789
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
779790
}

src/librustc_typeck/check/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2155,10 +2155,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21552155
/// function is unreachable, and there hasn't been another warning.
21562156
fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
21572157
if self.diverges.get() == Diverges::Always &&
2158-
// If span arose from a desugaring of `if` then it is the condition itself,
2159-
// which diverges, that we are about to lint on. This gives suboptimal diagnostics
2160-
// and so we stop here and allow the block of the `if`-expression to be linted instead.
2161-
!span.is_compiler_desugaring(CompilerDesugaringKind::IfTemporary) {
2158+
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
2159+
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
2160+
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
2161+
!span.is_compiler_desugaring(CompilerDesugaringKind::CondTemporary) {
21622162
self.diverges.set(Diverges::WarnedAlways);
21632163

21642164
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);

src/libsyntax_pos/hygiene.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,8 @@ pub enum CompilerDesugaringKind {
723723
/// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`.
724724
/// However, we do not want to blame `c` for unreachability but rather say that `i`
725725
/// is unreachable. This desugaring kind allows us to avoid blaming `c`.
726-
IfTemporary,
726+
/// This also applies to `while` loops.
727+
CondTemporary,
727728
QuestionMark,
728729
TryBlock,
729730
/// Desugaring of an `impl Trait` in return type position
@@ -738,7 +739,7 @@ pub enum CompilerDesugaringKind {
738739
impl CompilerDesugaringKind {
739740
pub fn name(self) -> Symbol {
740741
Symbol::intern(match self {
741-
CompilerDesugaringKind::IfTemporary => "if",
742+
CompilerDesugaringKind::CondTemporary => "if and while condition",
742743
CompilerDesugaringKind::Async => "async",
743744
CompilerDesugaringKind::Await => "await",
744745
CompilerDesugaringKind::QuestionMark => "?",

src/test/ui/reachable/expr_while.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
fn foo() {
77
while {return} {
8+
//~^ ERROR unreachable block in `while` expression
89
println!("Hello, world!");
9-
//~^ ERROR unreachable
1010
}
1111
}
1212

@@ -20,11 +20,10 @@ fn bar() {
2020
fn baz() {
2121
// Here, we cite the `while` loop as dead.
2222
while {return} {
23+
//~^ ERROR unreachable block in `while` expression
2324
println!("I am dead.");
24-
//~^ ERROR unreachable
2525
}
2626
println!("I am, too.");
27-
//~^ ERROR unreachable
2827
}
2928

3029
fn main() { }
+17-20
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
1-
error: unreachable statement
2-
--> $DIR/expr_while.rs:8:9
1+
error: unreachable block in `while` expression
2+
--> $DIR/expr_while.rs:7:20
33
|
4-
LL | println!("Hello, world!");
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | while {return} {
5+
| ____________________^
6+
LL | |
7+
LL | | println!("Hello, world!");
8+
LL | | }
9+
| |_____^
610
|
711
note: lint level defined here
812
--> $DIR/expr_while.rs:4:9
913
|
1014
LL | #![deny(unreachable_code)]
1115
| ^^^^^^^^^^^^^^^^
12-
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1316

14-
error: unreachable statement
15-
--> $DIR/expr_while.rs:23:9
17+
error: unreachable block in `while` expression
18+
--> $DIR/expr_while.rs:22:20
1619
|
17-
LL | println!("I am dead.");
18-
| ^^^^^^^^^^^^^^^^^^^^^^^
19-
|
20-
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
21-
22-
error: unreachable statement
23-
--> $DIR/expr_while.rs:26:5
24-
|
25-
LL | println!("I am, too.");
26-
| ^^^^^^^^^^^^^^^^^^^^^^^
27-
|
28-
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
20+
LL | while {return} {
21+
| ____________________^
22+
LL | |
23+
LL | | println!("I am dead.");
24+
LL | | }
25+
| |_____^
2926

30-
error: aborting due to 3 previous errors
27+
error: aborting due to 2 previous errors
3128

0 commit comments

Comments
 (0)