Skip to content

Commit

Permalink
Introduce lint DivergeReason.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjgillot committed Aug 18, 2024
1 parent 756cb48 commit 7e37e36
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 51 deletions.
16 changes: 13 additions & 3 deletions compiler/rustc_hir_typeck/src/diverges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ pub enum Diverges {
/// others require a CFG to determine them.
Maybe,

/// This expression is uninhabited, we want to
/// emit a diagnostic but not pollute type checking.
UninhabitedExpr(HirId, Span),

/// Same as `UninhabitedExpr` but with reachability
/// warning already emitted.
Warned,

/// Definitely known to diverge and therefore
/// not reach the next sibling or its parent.
Always(DivergeReason, Span),
Expand Down Expand Up @@ -55,16 +63,18 @@ impl ops::BitOrAssign for Diverges {
impl Diverges {
pub(super) fn is_always(self) -> bool {
match self {
Self::Maybe => false,
Self::Maybe | Diverges::UninhabitedExpr(..) | Diverges::Warned => false,
Self::Always(..) | Self::WarnedAlways => true,
}
}

fn ordinal(&self) -> u8 {
match self {
Self::Maybe => 0,
Self::Always { .. } => 1,
Self::WarnedAlways => 2,
Self::UninhabitedExpr(..) => 1,
Self::Warned => 2,
Self::Always { .. } => 3,
Self::WarnedAlways => 4,
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Field(..)
if matches!(
self.diverges.get(),
Diverges::Always(DivergeReason::UninhabitedExpr(_), _)
Diverges::UninhabitedExpr(_, _)
) && self.ty_is_uninhabited(ty) => {}
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
}

if !self.diverges.get().is_always() {
let cur_diverges = self.diverges.get();
if !cur_diverges.is_always() {
if ty.is_never() {
// Any expression that produces a value of type `!` must have diverged.
self.diverges.set(Diverges::Always(DivergeReason::Other, expr.span));
self.diverges.set(cur_diverges | Diverges::Always(DivergeReason::Other, expr.span));
} else if self.ty_is_uninhabited(ty) {
// This expression produces a value of uninhabited type.
// This means it has diverged somehow.
self.diverges
.set(Diverges::Always(DivergeReason::UninhabitedExpr(expr.hir_id), expr.span));
.set(cur_diverges | Diverges::UninhabitedExpr(expr.hir_id, expr.span));
}
}

Expand Down
18 changes: 13 additions & 5 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Produces warning on the given node, if the current point in the
/// function is unreachable, and there hasn't been another warning.
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
let Diverges::Always(reason, orig_span) = self.diverges.get() else {
return;
let (reason, orig_span) = match self.diverges.get() {
Diverges::UninhabitedExpr(hir_id, orig_span) => {
(DivergeReason::UninhabitedExpr(hir_id), orig_span)
}
Diverges::Always(reason, orig_span) => (reason, orig_span),
Diverges::Maybe | Diverges::Warned | Diverges::WarnedAlways => return,
};

match span.desugaring_kind() {
Expand All @@ -74,9 +78,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => {}
}

// Don't warn twice.
self.diverges.set(Diverges::WarnedAlways);

if matches!(reason, DivergeReason::UninhabitedExpr(_)) {
if let Some(impl_of) = self.tcx.impl_of_method(self.body_id.to_def_id()) {
if self.tcx.has_attr(impl_of, sym::automatically_derived) {
Expand All @@ -87,6 +88,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

// Don't warn twice.
self.diverges.set(match self.diverges.get() {
Diverges::UninhabitedExpr(..) => Diverges::Warned,
Diverges::Always(..) => Diverges::WarnedAlways,
Diverges::Maybe | Diverges::Warned | Diverges::WarnedAlways => bug!(),
});

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

let msg = format!("unreachable {kind}");
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/consts/let-irrefutable-pattern-ice-120337.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//@ check-pass
#![feature(never_type)]
#[derive(Copy, Clone)]
pub enum E { A(!), }
pub union U { u: (), e: E, }
pub const C: () = { //~ ERROR evaluation of constant value failed
pub const C: () = {
let E::A(ref a) = unsafe { &(&U { u: () }).e};
};

Expand Down
12 changes: 0 additions & 12 deletions tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/match/match-no-arms-unreachable-after.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: unreachable statement
--> $DIR/match-no-arms-unreachable-after.rs:8:5
|
LL | match v { }
| - this expression has type `Void`, which is uninhabited
| ----------- any code following this expression is unreachable
LL | let x = 2;
| ^^^^^^^^^^ unreachable statement
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ fn never_match() -> u32 {
//~^ ERROR unreachable arm
}
println!();
//~^ ERROR unreachable statement
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,16 @@ LL | match *ptr { ! };
| |
| this expression has type `Void`, which is uninhabited

error: aborting due to 4 previous errors
error: unreachable statement
--> $DIR/diverge-causes-unreachable-code.rs:34:5
|
LL | match *ptr { ! };
| ---------------- any code following this `match` expression is unreachable, as all arms diverge
LL | }
LL | println!();
| ^^^^^^^^^^ unreachable statement
|
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 5 previous errors

3 changes: 2 additions & 1 deletion tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn wild_void(_: Void) -> u32 {}
fn wild_let() -> u32 {
let ptr: *const Void = std::ptr::null();
unsafe {
//~^ ERROR: mismatched types
let _ = *ptr;
}
}
Expand All @@ -34,8 +35,8 @@ fn binding_void(_x: Void) -> u32 {}
fn binding_let() -> u32 {
let ptr: *const Void = std::ptr::null();
unsafe {
//~^ ERROR: mismatched types
let _x = *ptr;
//~^ ERROR: cannot move
}
}

Expand Down
46 changes: 24 additions & 22 deletions tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,49 @@ LL | fn wild_void(_: Void) -> u32 {}
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:26:18
--> $DIR/diverges-not.rs:17:5
|
LL | / unsafe {
LL | |
LL | | let _ = *ptr;
LL | | }
| |_____^ expected `u32`, found `()`

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:27:18
|
LL | _ => {}
| ^^ expected `u32`, found `()`

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:31:30
--> $DIR/diverges-not.rs:32:30
|
LL | fn binding_void(_x: Void) -> u32 {}
| ------------ ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:46:19
--> $DIR/diverges-not.rs:37:5
|
LL | / unsafe {
LL | |
LL | | let _x = *ptr;
LL | | }
| |_____^ expected `u32`, found `()`

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:47:19
|
LL | _x => {}
| ^^ expected `u32`, found `()`

error[E0308]: mismatched types
--> $DIR/diverges-not.rs:53:37
--> $DIR/diverges-not.rs:54:37
|
LL | if let true = true && let ! = x {}
| ^^ expected `u32`, found `()`

error[E0507]: cannot move out of `*ptr` which is behind a raw pointer
--> $DIR/diverges-not.rs:37:18
|
LL | let _x = *ptr;
| ^^^^ move occurs because `*ptr` has type `Void`, which does not implement the `Copy` trait
|
note: if `Void` implemented `Clone`, you could clone the value
--> $DIR/diverges-not.rs:8:1
|
LL | enum Void {}
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | let _x = *ptr;
| ---- you could clone this value

error: aborting due to 6 previous errors
error: aborting due to 7 previous errors

Some errors have detailed explanations: E0308, E0507.
For more information about an error, try `rustc --explain E0308`.
For more information about this error, try `rustc --explain E0308`.
18 changes: 17 additions & 1 deletion tests/ui/uninhabited/break-diverging-value.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ LL | fn loop_break_break() -> i32 {
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/break-diverging-value.rs:33:25
|
LL | fn loop_break_void() -> i32 {
| --------------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/break-diverging-value.rs:43:34
|
LL | fn loop_break_indirect_void() -> i32 {
| ------------------------ ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/break-diverging-value.rs:55:33
|
Expand All @@ -14,6 +30,6 @@ LL | fn loop_break_private_void() -> i32 {
| |
| implicitly returns `()` as its body has no tail or `return` expression

error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit 7e37e36

Please sign in to comment.