Skip to content

Commit 5ba9e4b

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 e78913b commit 5ba9e4b

File tree

7 files changed

+69
-80
lines changed

7 files changed

+69
-80
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+5-9
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, Diag};
44
use rustc_hir::def::{CtorOf, DefKind, Res};
55
use rustc_hir::def_id::LocalDefId;
@@ -30,7 +30,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3030

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

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

166162
// 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
@@ -2,7 +2,7 @@ use std::cell::RefCell;
22

33
use crate::coercion::CoerceMany;
44
use crate::gather_locals::GatherLocalsVisitor;
5-
use crate::{CoroutineTypes, Diverges, FnCtxt};
5+
use crate::{CoroutineTypes, DivergeReason, Diverges, FnCtxt};
66
use rustc_hir as hir;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::intravisit::Visitor;
@@ -76,10 +76,8 @@ pub(super) fn check_fn<'a, 'tcx>(
7676
let ty_span = ty.map(|ty| ty.span);
7777
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
7878
if param.pat.is_never_pattern() {
79-
fcx.function_diverges_because_of_empty_arguments.set(Diverges::Always {
80-
span: param.pat.span,
81-
custom_note: Some("any code following a never pattern is unreachable"),
82-
});
79+
fcx.function_diverges_because_of_empty_arguments
80+
.set(Diverges::Always(DivergeReason::NeverPattern, param.pat.span));
8381
}
8482

8583
// Check that argument is Sized.
+25-26
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,21 @@
1-
use rustc_span::{Span, DUMMY_SP};
1+
use rustc_span::Span;
2+
23
use std::{cmp, ops};
34

45
/// Tracks whether executing a node may exit normally (versus
56
/// return/break/panic, which "diverge", leaving dead code in their
67
/// wake). Tracked semi-automatically (through type variables marked
78
/// as diverging), with some manual adjustments for control-flow
89
/// primitives (approximating a CFG).
9-
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
10+
#[derive(Copy, Clone, Debug)]
1011
pub enum Diverges {
1112
/// Potentially unknown, some cases converge,
1213
/// others require a CFG to determine them.
1314
Maybe,
1415

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

3220
/// Same as `Always` but with a reachability
3321
/// warning already emitted.
@@ -39,14 +27,15 @@ pub enum Diverges {
3927
impl ops::BitAnd for Diverges {
4028
type Output = Self;
4129
fn bitand(self, other: Self) -> Self {
42-
cmp::min(self, other)
30+
cmp::min_by_key(self, other, Self::ordinal)
4331
}
4432
}
4533

4634
impl ops::BitOr for Diverges {
4735
type Output = Self;
4836
fn bitor(self, other: Self) -> Self {
49-
cmp::max(self, other)
37+
// argument order is to prefer `self` if ordinal is equal
38+
cmp::max_by_key(other, self, Self::ordinal)
5039
}
5140
}
5241

@@ -63,15 +52,25 @@ impl ops::BitOrAssign for Diverges {
6352
}
6453

6554
impl Diverges {
66-
/// Creates a `Diverges::Always` with the provided `span` and the default note message.
67-
pub(super) fn always(span: Span) -> Diverges {
68-
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+
}
6960
}
7061

71-
pub(super) fn is_always(self) -> bool {
72-
// Enum comparison ignores the
73-
// contents of fields, so we just
74-
// fill them in with garbage here.
75-
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+
}
7668
}
7769
}
70+
71+
#[derive(Clone, Copy, Debug)]
72+
pub enum DivergeReason {
73+
AllArmsDiverge,
74+
NeverPattern,
75+
Other,
76+
}

compiler/rustc_hir_typeck/src/expr.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::type_error_struct;
1818
use crate::CoroutineTypes;
1919
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
2020
use crate::{
21-
report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs,
21+
report_unexpected_variant_res, BreakableCtxt, DivergeReason, Diverges, FnCtxt, Needs,
2222
TupleArgumentsFlag::DontTupleArguments,
2323
};
2424
use rustc_ast as ast;
@@ -253,7 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
253253

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

259260
// Record the type, which applies it effects.
@@ -1432,7 +1433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14321433
})
14331434
});
14341435
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
1435-
assert_eq!(self.diverges.get(), Diverges::Maybe);
1436+
assert!(matches!(self.diverges.get(), Diverges::Maybe));
14361437
for e in args {
14371438
let e_ty = self.check_expr_with_hint(e, coerce_to);
14381439
let cause = self.misc(e.span);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+28-30
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, LoweredTy};
5+
use crate::{BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LoweredTy};
66
use rustc_data_structures::captures::Captures;
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey};
@@ -48,36 +48,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4848
/// Produces warning on the given node, if the current point in the
4949
/// function is unreachable, and there hasn't been another warning.
5050
pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
51-
// FIXME: Combine these two 'if' expressions into one once
52-
// let chains are implemented
53-
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
54-
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
55-
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
56-
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
57-
if !span.is_desugaring(DesugaringKind::CondTemporary)
58-
&& !span.is_desugaring(DesugaringKind::Async)
59-
&& !orig_span.is_desugaring(DesugaringKind::Await)
60-
{
61-
self.diverges.set(Diverges::WarnedAlways);
62-
63-
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
64-
65-
let msg = format!("unreachable {kind}");
66-
self.tcx().node_span_lint(
67-
lint::builtin::UNREACHABLE_CODE,
68-
id,
69-
span,
70-
msg.clone(),
71-
|lint| {
72-
lint.span_label(span, msg).span_label(
73-
orig_span,
74-
custom_note
75-
.unwrap_or("any code following this expression is unreachable"),
76-
);
77-
},
78-
)
79-
}
51+
let Diverges::Always(reason, orig_span) = self.diverges.get() else {
52+
return;
53+
};
54+
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
55+
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
56+
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
57+
if matches!(
58+
span.desugaring_kind(),
59+
Some(DesugaringKind::Async | DesugaringKind::Await | DesugaringKind::CondTemporary)
60+
) {
61+
return;
8062
}
63+
64+
self.diverges.set(Diverges::WarnedAlways);
65+
66+
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
67+
68+
let msg = format!("unreachable {kind}");
69+
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, msg.clone(), |lint| {
70+
let label = match reason {
71+
DivergeReason::AllArmsDiverge => {
72+
"any code following this `match` expression is unreachable, as all arms diverge"
73+
}
74+
DivergeReason::NeverPattern => "any code following a never pattern is unreachable",
75+
DivergeReason::Other => "any code following this expression is unreachable",
76+
};
77+
lint.span_label(span, msg).span_label(orig_span, label);
78+
})
8179
}
8280

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

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::method::MethodCallee;
1010
use crate::TupleArgumentsFlag::*;
1111
use crate::{errors, Expectation::*};
1212
use crate::{
13-
struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs,
14-
TupleArgumentsFlag,
13+
struct_span_code_err, BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LoweredTy,
14+
Needs, TupleArgumentsFlag,
1515
};
1616
use itertools::Itertools;
1717
use rustc_ast as ast;
@@ -1637,10 +1637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16371637
pub fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
16381638
self.check_decl(local.into());
16391639
if local.pat.is_never_pattern() {
1640-
self.diverges.set(Diverges::Always {
1641-
span: local.pat.span,
1642-
custom_note: Some("any code following a never pattern is unreachable"),
1643-
});
1640+
self.diverges.set(Diverges::Always(DivergeReason::NeverPattern, local.pat.span));
16441641
}
16451642
}
16461643

compiler/rustc_hir_typeck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub use typeck_root_ctxt::TypeckRootCtxt;
4747

4848
use crate::check::check_fn;
4949
use crate::coercion::DynamicCoerceMany;
50-
use crate::diverges::Diverges;
50+
use crate::diverges::{DivergeReason, Diverges};
5151
use crate::expectation::Expectation;
5252
use crate::fn_ctxt::LoweredTy;
5353
use crate::gather_locals::GatherLocalsVisitor;

0 commit comments

Comments
 (0)