Skip to content

Commit 6e3f9e7

Browse files
camsteffencjgillot
authored andcommitted
Refactor Diverges in typeck
Avoid deriving PartialOrd on Diverges since it includes fields which should not affect ordering.
1 parent e4cd161 commit 6e3f9e7

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, Diagnostic, 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

@@ -162,13 +162,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
162162
// we can emit a better note. Rather than pointing
163163
// at a diverging expression in an arbitrary arm,
164164
// we can point at the entire `match` expression
165-
if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
166-
all_arms_diverge = Diverges::Always {
167-
span: expr.span,
168-
custom_note: Some(
169-
"any code following this `match` expression is unreachable, as all arms diverge",
170-
),
171-
};
165+
if let (Diverges::Always(..), hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
166+
all_arms_diverge = Diverges::Always(DivergeReason::AllArmsDiverge, expr);
172167
}
173168

174169
// 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
@@ -17,7 +17,7 @@ use crate::method::SelfSource;
1717
use crate::type_error_struct;
1818
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
1919
use crate::{
20-
report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs,
20+
report_unexpected_variant_res, BreakableCtxt, DivergeReason, Diverges, FnCtxt, Needs,
2121
TupleArgumentsFlag::DontTupleArguments,
2222
};
2323
use rustc_ast as ast;
@@ -262,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
262262

263263
// Any expression that produces a value of type `!` must have diverged
264264
if ty.is_never() {
265-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
265+
self.diverges.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr));
266266
}
267267

268268
// Record the type, which applies it effects.
@@ -1362,7 +1362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13621362
})
13631363
});
13641364
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
1365-
assert_eq!(self.diverges.get(), Diverges::Maybe);
1365+
assert!(matches!(self.diverges.get(), Diverges::Maybe));
13661366
for e in args {
13671367
let e_ty = self.check_expr_with_hint(e, coerce_to);
13681368
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
@@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution};
22
use crate::errors::CtorIsPrivate;
33
use crate::method::{self, MethodCallee, SelfSource};
44
use crate::rvalue_scopes;
5-
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy};
5+
use crate::{BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, RawTy};
66
use rustc_data_structures::captures::Captures;
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
@@ -47,36 +47,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4747
/// Produces warning on the given node, if the current point in the
4848
/// function is unreachable, and there hasn't been another warning.
4949
pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
50-
// FIXME: Combine these two 'if' expressions into one once
51-
// let chains are implemented
52-
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
53-
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
54-
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
55-
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
56-
if !span.is_desugaring(DesugaringKind::CondTemporary)
57-
&& !span.is_desugaring(DesugaringKind::Async)
58-
&& !orig_span.is_desugaring(DesugaringKind::Await)
59-
{
60-
self.diverges.set(Diverges::WarnedAlways);
50+
let Diverges::Always(reason, diverging_expr) = self.diverges.get() else {
51+
return;
52+
};
53+
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
54+
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
55+
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
56+
if matches!(
57+
span.desugaring_kind(),
58+
Some(DesugaringKind::Async | DesugaringKind::Await | DesugaringKind::CondTemporary)
59+
) {
60+
return;
61+
}
6162

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

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

8279
/// 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
@@ -104,7 +104,7 @@ pub struct FnCtxt<'a, 'tcx> {
104104
///
105105
/// An expression represents dead code if, after checking it,
106106
/// the diverges flag is set to something other than `Maybe`.
107-
pub(super) diverges: Cell<Diverges>,
107+
pub(super) diverges: Cell<Diverges<'tcx>>,
108108

109109
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
110110

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub use inherited::Inherited;
5050

5151
use crate::check::check_fn;
5252
use crate::coercion::DynamicCoerceMany;
53-
use crate::diverges::Diverges;
53+
use crate::diverges::{DivergeReason, Diverges};
5454
use crate::expectation::Expectation;
5555
use crate::fn_ctxt::RawTy;
5656
use crate::gather_locals::GatherLocalsVisitor;

0 commit comments

Comments
 (0)