Skip to content

Commit 1450957

Browse files
committed
Refactor Diverges in typeck
Avoid deriving PartialOrd on Diverges since it includes fields which should not affect ordering.
1 parent 4095722 commit 1450957

File tree

6 files changed

+65
-76
lines changed

6 files changed

+65
-76
lines changed

compiler/rustc_hir_typeck/src/_match.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::coercion::{AsCoercionSite, CoerceMany};
2-
use crate::{Diverges, Expectation, FnCtxt, Needs};
2+
use crate::{DivergeReason, Diverges, Expectation, FnCtxt, Needs};
33
use rustc_errors::{Applicability, MultiSpan};
44
use rustc_hir::{self as hir, ExprKind};
55
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -29,7 +29,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2929

3030
// If there are no arms, that is a diverging match; a special case.
3131
if arms.is_empty() {
32-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
32+
self.diverges.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr));
3333
return tcx.types.never;
3434
}
3535

@@ -204,13 +204,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
204204
// we can emit a better note. Rather than pointing
205205
// at a diverging expression in an arbitrary arm,
206206
// we can point at the entire `match` expression
207-
if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
208-
all_arms_diverge = Diverges::Always {
209-
span: expr.span,
210-
custom_note: Some(
211-
"any code following this `match` expression is unreachable, as all arms diverge",
212-
),
213-
};
207+
if let (Diverges::Always(..), hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
208+
all_arms_diverge = Diverges::Always(DivergeReason::AllArmsDiverge, expr);
214209
}
215210

216211
// We won't diverge unless the scrutinee or all arms diverge.
Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
1-
use rustc_span::source_map::DUMMY_SP;
2-
use rustc_span::{self, Span};
1+
use rustc_hir as hir;
2+
33
use std::{cmp, ops};
44

55
/// Tracks whether executing a node may exit normally (versus
66
/// return/break/panic, which "diverge", leaving dead code in their
77
/// wake). Tracked semi-automatically (through type variables marked
88
/// as diverging), with some manual adjustments for control-flow
99
/// primitives (approximating a CFG).
10-
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
11-
pub enum Diverges {
10+
#[derive(Copy, Clone, Debug)]
11+
pub enum Diverges<'a> {
1212
/// Potentially unknown, some cases converge,
1313
/// others require a CFG to determine them.
1414
Maybe,
1515

1616
/// Definitely known to diverge and therefore
1717
/// not reach the next sibling or its parent.
18-
Always {
19-
/// The `Span` points to the expression
20-
/// that caused us to diverge
21-
/// (e.g. `return`, `break`, etc).
22-
span: Span,
23-
/// In some cases (e.g. a `match` expression
24-
/// where all arms diverge), we may be
25-
/// able to provide a more informative
26-
/// message to the user.
27-
/// If this is `None`, a default message
28-
/// will be generated, which is suitable
29-
/// for most cases.
30-
custom_note: Option<&'static str>,
31-
},
18+
Always(DivergeReason, &'a hir::Expr<'a>),
3219

3320
/// Same as `Always` but with a reachability
3421
/// warning already emitted.
@@ -37,42 +24,52 @@ pub enum Diverges {
3724

3825
// Convenience impls for combining `Diverges`.
3926

40-
impl ops::BitAnd for Diverges {
27+
impl ops::BitAnd for Diverges<'_> {
4128
type Output = Self;
4229
fn bitand(self, other: Self) -> Self {
43-
cmp::min(self, other)
30+
cmp::min_by_key(self, other, Self::ordinal)
4431
}
4532
}
4633

47-
impl ops::BitOr for Diverges {
34+
impl ops::BitOr for Diverges<'_> {
4835
type Output = Self;
4936
fn bitor(self, other: Self) -> Self {
50-
cmp::max(self, other)
37+
// argument order is to prefer `self` if ordinal is equal
38+
cmp::max_by_key(other, self, Self::ordinal)
5139
}
5240
}
5341

54-
impl ops::BitAndAssign for Diverges {
42+
impl ops::BitAndAssign for Diverges<'_> {
5543
fn bitand_assign(&mut self, other: Self) {
5644
*self = *self & other;
5745
}
5846
}
5947

60-
impl ops::BitOrAssign for Diverges {
48+
impl ops::BitOrAssign for Diverges<'_> {
6149
fn bitor_assign(&mut self, other: Self) {
6250
*self = *self | other;
6351
}
6452
}
6553

66-
impl Diverges {
67-
/// Creates a `Diverges::Always` with the provided `span` and the default note message.
68-
pub(super) fn always(span: Span) -> Diverges {
69-
Diverges::Always { span, custom_note: None }
54+
impl Diverges<'_> {
55+
pub(super) fn is_always(self) -> bool {
56+
match self {
57+
Self::Maybe => false,
58+
Self::Always(..) | Self::WarnedAlways => true,
59+
}
7060
}
7161

72-
pub(super) fn is_always(self) -> bool {
73-
// Enum comparison ignores the
74-
// contents of fields, so we just
75-
// fill them in with garbage here.
76-
self >= Diverges::Always { span: DUMMY_SP, custom_note: None }
62+
fn ordinal(&self) -> u8 {
63+
match self {
64+
Self::Maybe => 0,
65+
Self::Always { .. } => 1,
66+
Self::WarnedAlways => 2,
67+
}
7768
}
7869
}
70+
71+
#[derive(Clone, Copy, Debug)]
72+
pub enum DivergeReason {
73+
AllArmsDiverge,
74+
Other,
75+
}

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::method::SelfSource;
1515
use crate::type_error_struct;
1616
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
1717
use crate::{
18-
report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs,
18+
report_unexpected_variant_res, BreakableCtxt, DivergeReason, Diverges, FnCtxt, Needs,
1919
TupleArgumentsFlag::DontTupleArguments,
2020
};
2121
use rustc_ast as ast;
@@ -249,7 +249,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
249249

250250
// Any expression that produces a value of type `!` must have diverged
251251
if ty.is_never() {
252-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
252+
self.diverges.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr));
253253
}
254254

255255
// Record the type, which applies it effects.
@@ -1306,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13061306
})
13071307
});
13081308
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
1309-
assert_eq!(self.diverges.get(), Diverges::Maybe);
1309+
assert!(matches!(self.diverges.get(), Diverges::Maybe));
13101310
for e in args {
13111311
let e_ty = self.check_expr_with_hint(e, coerce_to);
13121312
let cause = self.misc(e.span);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::callee::{self, DeferredCallResolution};
22
use crate::method::{self, MethodCallee, SelfSource};
33
use crate::rvalue_scopes;
4-
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
4+
use crate::{BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LocalTy};
55
use rustc_data_structures::captures::Captures;
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -43,36 +43,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4343
/// Produces warning on the given node, if the current point in the
4444
/// function is unreachable, and there hasn't been another warning.
4545
pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
46-
// FIXME: Combine these two 'if' expressions into one once
47-
// let chains are implemented
48-
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
49-
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
50-
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
51-
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
52-
if !span.is_desugaring(DesugaringKind::CondTemporary)
53-
&& !span.is_desugaring(DesugaringKind::Async)
54-
&& !orig_span.is_desugaring(DesugaringKind::Await)
55-
{
56-
self.diverges.set(Diverges::WarnedAlways);
46+
let Diverges::Always(reason, diverging_expr) = self.diverges.get() else {
47+
return;
48+
};
49+
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
50+
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
51+
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
52+
if matches!(
53+
span.desugaring_kind(),
54+
Some(DesugaringKind::Async | DesugaringKind::Await | DesugaringKind::CondTemporary)
55+
) {
56+
return;
57+
}
5758

58-
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
59+
self.diverges.set(Diverges::WarnedAlways);
5960

60-
let msg = format!("unreachable {}", kind);
61-
self.tcx().struct_span_lint_hir(
62-
lint::builtin::UNREACHABLE_CODE,
63-
id,
64-
span,
65-
&msg,
66-
|lint| {
67-
lint.span_label(span, &msg).span_label(
68-
orig_span,
69-
custom_note
70-
.unwrap_or("any code following this expression is unreachable"),
71-
)
72-
},
73-
)
74-
}
75-
}
61+
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
62+
63+
let msg = format!("unreachable {}", kind);
64+
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg, |lint| {
65+
let label = match reason {
66+
DivergeReason::AllArmsDiverge => {
67+
"any code following this `match` expression is unreachable, as all arms diverge"
68+
}
69+
DivergeReason::Other => "any code following this expression is unreachable",
70+
};
71+
lint.span_label(span, &msg).span_label(diverging_expr.span, label)
72+
});
7673
}
7774

7875
/// Resolves type and const variables in `ty` if possible. Unlike the infcx

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub struct FnCtxt<'a, 'tcx> {
110110
///
111111
/// An expression represents dead code if, after checking it,
112112
/// the diverges flag is set to something other than `Maybe`.
113-
pub(super) diverges: Cell<Diverges>,
113+
pub(super) diverges: Cell<Diverges<'tcx>>,
114114

115115
/// Whether any child nodes have any type errors.
116116
pub(super) has_errors: Cell<bool>,

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ mod rvalue_scopes;
4444
mod upvar;
4545
mod writeback;
4646

47-
pub use diverges::Diverges;
47+
pub use diverges::{DivergeReason, Diverges};
4848
pub use expectation::Expectation;
4949
pub use fn_ctxt::*;
5050
pub use inherited::{Inherited, InheritedBuilder};

0 commit comments

Comments
 (0)