Skip to content

Commit

Permalink
Rollup merge of #135409 - Shunpoco:issue-133117-ICE-never-false-edge-…
Browse files Browse the repository at this point in the history
…start-block, r=Nadrieril

Fix ICE-133117: multiple never-pattern arm doesn't have false_edge_start_block

Fixes #133117 , and close fixes #133063 , fixes #130779

In order to fix ICE-133117, at first I needed to tackle to ICE-133063 (this fixed 130779 as well).

### ICE-133063 and ICE-130779
This ICE is caused by those steps:
1. An arm has or-pattern, and all of the sub-candidates are never-pattern
2. In that case, all sub-candidates are removed in remove_never_subcandidates(). So the arm (candidate) has no sub-candidate.
3. In the current implementation, if there is no sub-candidate, the function assigns `pre_binding_block` into the candidate ([here](https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/builder/matches/mod.rs#L2002-L2004)). However, otherwise_block should be assigned to the candidate as well, because the otherwise_block is unwrapped in multiple place (like in lower_match_tree()). As a result, it causes the panic.

I simply added the same block as pre_binding_block into otherwise_block, but I'm wondering if there is a better block to assign to otherwise_block (is it ok to assign the same block into pre_binding and otherwise?)

### ICE-133117
This is caused by those steps:
1. There are two arms, both are or-pattern and each has one match-pair (in the test code, both are `(!|!)`), and the second arm has a guard.
2. In match_candidate() for the first arm, it expands the second arm’s sub-candidates as well ([here](https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/builder/matches/mod.rs#L1800-L1805)). As a result, the root candidate of the second arm is not evaluated/modified in match_candidate(). So a false_edge_start_block is not assigned to the candidate.
3. merge_trivial_subcandidates() is called against the candidate for the second arm. It just returns immediately because the candidate has a guard. So a flase_edge_start_block is not assigned to the candidate also in this function.
4. remove_never_subcandidates() is called against the candidate. Since all sub-candidates are never-pattern. they are removed.
5. In lower_match_tree(), since there is no sub-candidate for the candidate, the candidate itself is evaluated in visit_leave_rev ([here](https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/builder/matches/mod.rs#L1532)). Because the candidate has no false_edge_start_block, it causes the panic.

So I modified the order of if blocks in merge_trivial_subcandidates() to assign a false_edge_start_block if the candidate doesn't have.
  • Loading branch information
matthiaskrgr authored Jan 22, 2025
2 parents 9206ba5 + 7275bdf commit f875983
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 29 deletions.
11 changes: 9 additions & 2 deletions compiler/rustc_mir_build/src/builder/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,6 +1986,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
return;
}

let false_edge_start_block = candidate.subcandidates[0].false_edge_start_block;
candidate.subcandidates.retain_mut(|candidate| {
if candidate.extra_data.is_never {
candidate.visit_leaves(|subcandidate| {
Expand All @@ -2000,8 +2001,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
});
if candidate.subcandidates.is_empty() {
// If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`.
candidate.pre_binding_block = Some(self.cfg.start_new_block());
// If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block` and `otherwise_block`.
let next_block = self.cfg.start_new_block();
candidate.pre_binding_block = Some(next_block);
candidate.otherwise_block = Some(next_block);
// In addition, if `candidate` doesn't have `false_edge_start_block`, it should be assigned here.
if candidate.false_edge_start_block.is_none() {
candidate.false_edge_start_block = false_edge_start_block;
}
}
}

Expand Down
11 changes: 0 additions & 11 deletions tests/crashes/130779.rs

This file was deleted.

8 changes: 0 additions & 8 deletions tests/crashes/133063.rs

This file was deleted.

8 changes: 0 additions & 8 deletions tests/crashes/133117.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(never_patterns)]
#![allow(incomplete_features)]

enum E { A }

fn main() {
match E::A {
! | //~ ERROR: a trailing `|` is not allowed in an or-pattern
//~^ ERROR: mismatched types
if true => {} //~ ERROR: a never pattern is always unreachable
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error: a trailing `|` is not allowed in an or-pattern
--> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:8:11
|
LL | ! |
| - ^
| |
| while parsing this or-pattern starting here
|
help: remove the `|`
|
LL - ! |
LL + !
|

error: a never pattern is always unreachable
--> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20
|
LL | if true => {}
| ^^
| |
| this will never be executed
| help: remove this expression

error: mismatched types
--> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:8:9
|
LL | ! |
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `E`

error: aborting due to 3 previous errors

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(never_type)]
#![feature(never_patterns)]
#![allow(incomplete_features)]

enum Void {}

fn foo(x: Void) {
loop {
match x {
(!|!) if false => {} //~ ERROR a never pattern is always unreachable
_ => {}
}
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: a never pattern is always unreachable
--> $DIR/ICE-133063-never-arm-no-otherwise-block.rs:10:31
|
LL | (!|!) if false => {}
| ^^
| |
| this will never be executed
| help: remove this expression

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![feature(never_type)]
#![feature(never_patterns)]
#![allow(incomplete_features)]

enum Void {}

fn foo(x: Void) {
match x {
(!|!) if true => {} //~ ERROR a never pattern is always unreachable
(!|!) if true => {} //~ ERROR a never pattern is always unreachable
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error: a never pattern is always unreachable
--> $DIR/ICE-133117-duplicate-never-arm.rs:9:26
|
LL | (!|!) if true => {}
| ^^
| |
| this will never be executed
| help: remove this expression

error: a never pattern is always unreachable
--> $DIR/ICE-133117-duplicate-never-arm.rs:10:26
|
LL | (!|!) if true => {}
| ^^
| |
| this will never be executed
| help: remove this expression

error: aborting due to 2 previous errors

0 comments on commit f875983

Please sign in to comment.