Skip to content

Commit f6e7f7c

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 298c746 commit f6e7f7c

File tree

7 files changed

+55
-58
lines changed

7 files changed

+55
-58
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_trait_selection::traits::{
1111
use tracing::{debug, instrument};
1212

1313
use crate::coercion::{AsCoercionSite, CoerceMany};
14-
use crate::{Diverges, Expectation, FnCtxt, Needs};
14+
use crate::{DivergeReason, Diverges, Expectation, FnCtxt, Needs};
1515

1616
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1717
#[instrument(skip(self), level = "debug", ret)]
@@ -31,7 +31,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3131

3232
// If there are no arms, that is a diverging match; a special case.
3333
if arms.is_empty() {
34-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
34+
self.diverges
35+
.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr.span));
3536
return tcx.types.never;
3637
}
3738

@@ -150,13 +151,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
150151
// we can emit a better note. Rather than pointing
151152
// at a diverging expression in an arbitrary arm,
152153
// we can point at the entire `match` expression
153-
if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
154-
all_arms_diverge = Diverges::Always {
155-
span: expr.span,
156-
custom_note: Some(
157-
"any code following this `match` expression is unreachable, as all arms diverge",
158-
),
159-
};
154+
if let (Diverges::Always(..), hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
155+
all_arms_diverge = Diverges::Always(DivergeReason::AllArmsDiverge, expr.span);
160156
}
161157

162158
// We won't diverge unless the scrutinee or all arms diverge.

compiler/rustc_hir_typeck/src/check.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tracing::{debug, instrument};
1616

1717
use crate::coercion::CoerceMany;
1818
use crate::gather_locals::GatherLocalsVisitor;
19-
use crate::{CoroutineTypes, Diverges, FnCtxt};
19+
use crate::{CoroutineTypes, DivergeReason, Diverges, FnCtxt};
2020

2121
/// Helper used for fns and closures. Does the grungy work of checking a function
2222
/// body and returns the function context used for that purpose, since in the case of a fn item
@@ -89,10 +89,8 @@ pub(super) fn check_fn<'a, 'tcx>(
8989
let ty_span = ty.map(|ty| ty.span);
9090
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
9191
if param.pat.is_never_pattern() {
92-
fcx.function_diverges_because_of_empty_arguments.set(Diverges::Always {
93-
span: param.pat.span,
94-
custom_note: Some("any code following a never pattern is unreachable"),
95-
});
92+
fcx.function_diverges_because_of_empty_arguments
93+
.set(Diverges::Always(DivergeReason::NeverPattern, param.pat.span));
9694
}
9795

9896
// Check that argument is Sized.
+24-26
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
11
use std::{cmp, ops};
22

3-
use rustc_span::{DUMMY_SP, Span};
3+
use rustc_span::Span;
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)]
10+
#[derive(Copy, Clone, Debug)]
1111
pub(crate) enum Diverges {
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, Span),
3219

3320
/// Same as `Always` but with a reachability
3421
/// warning already emitted.
@@ -40,14 +27,15 @@ pub(crate) enum Diverges {
4027
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

4734
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

@@ -64,15 +52,25 @@ impl ops::BitOrAssign for Diverges {
6452
}
6553

6654
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 }
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(crate) enum DivergeReason {
73+
AllArmsDiverge,
74+
NeverPattern,
75+
Other,
76+
}

compiler/rustc_hir_typeck/src/expr.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ use crate::errors::{
5050
YieldExprOutsideOfCoroutine,
5151
};
5252
use crate::{
53-
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust,
54-
report_unexpected_variant_res, type_error_struct,
53+
BreakableCtxt, CoroutineTypes, DivergeReason, Diverges, FnCtxt, Needs, cast,
54+
fatally_break_rust, report_unexpected_variant_res, type_error_struct,
5555
};
5656

5757
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -246,7 +246,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
246246
// diverging would be unsound since we may never actually read the `!`.
247247
// e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
248248
if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) {
249-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
249+
self.diverges
250+
.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr.span));
250251
}
251252

252253
// Record the type, which applies it effects.
@@ -1507,7 +1508,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15071508
// of a `break` or an outer `break` or `return`.
15081509
self.diverges.set(Diverges::Maybe);
15091510
} else {
1510-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
1511+
self.diverges
1512+
.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr.span));
15111513
}
15121514

15131515
// If we permit break with a value, then result type is
@@ -1610,7 +1612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16101612
})
16111613
.unwrap_or_else(|| self.next_ty_var(expr.span));
16121614
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
1613-
assert_eq!(self.diverges.get(), Diverges::Maybe);
1615+
assert!(matches!(self.diverges.get(), Diverges::Maybe));
16141616
for e in args {
16151617
let e_ty = self.check_expr_with_hint(e, coerce_to);
16161618
let cause = self.misc(e.span);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ use tracing::{debug, instrument};
4242
use crate::callee::{self, DeferredCallResolution};
4343
use crate::errors::{self, CtorIsPrivate};
4444
use crate::method::{self, MethodCallee};
45-
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, rvalue_scopes};
45+
use crate::{
46+
BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LoweredTy, rvalue_scopes,
47+
};
4648

4749
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4850
/// Produces warning on the given node, if the current point in the
4951
/// function is unreachable, and there hasn't been another warning.
5052
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
51-
let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else {
53+
let Diverges::Always(reason, orig_span) = self.diverges.get() else {
5254
return;
5355
};
5456

@@ -79,10 +81,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7981
let msg = format!("unreachable {kind}");
8082
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
8183
lint.primary_message(msg.clone());
82-
lint.span_label(span, msg).span_label(
83-
orig_span,
84-
custom_note.unwrap_or("any code following this expression is unreachable"),
85-
);
84+
let custom_note = match reason {
85+
DivergeReason::AllArmsDiverge => {
86+
"any code following this `match` expression is unreachable, as all arms diverge"
87+
}
88+
DivergeReason::NeverPattern => "any code following a never pattern is unreachable",
89+
DivergeReason::Other => "any code following this expression is unreachable",
90+
};
91+
lint.span_label(span, msg).span_label(orig_span, custom_note);
8692
})
8793
}
8894

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ use crate::method::probe::IsSuggestion;
4242
use crate::method::probe::Mode::MethodCall;
4343
use crate::method::probe::ProbeScope::TraitsInScope;
4444
use crate::{
45-
BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs, TupleArgumentsFlag, errors,
46-
struct_span_code_err,
45+
BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LoweredTy, Needs,
46+
TupleArgumentsFlag, errors, struct_span_code_err,
4747
};
4848

4949
#[derive(Clone, Copy, Default)]
@@ -1761,10 +1761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17611761
fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
17621762
self.check_decl(local.into());
17631763
if local.pat.is_never_pattern() {
1764-
self.diverges.set(Diverges::Always {
1765-
span: local.pat.span,
1766-
custom_note: Some("any code following a never pattern is unreachable"),
1767-
});
1764+
self.diverges.set(Diverges::Always(DivergeReason::NeverPattern, local.pat.span));
17681765
}
17691766
}
17701767

compiler/rustc_hir_typeck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ use typeck_root_ctxt::TypeckRootCtxt;
6161

6262
use crate::check::check_fn;
6363
use crate::coercion::DynamicCoerceMany;
64-
use crate::diverges::Diverges;
64+
use crate::diverges::{DivergeReason, Diverges};
6565
use crate::expectation::Expectation;
6666
use crate::fn_ctxt::LoweredTy;
6767
use crate::gather_locals::GatherLocalsVisitor;

0 commit comments

Comments
 (0)