Allow duplicated switch cases.

This commit is contained in:
Stephen Chung
2022-07-18 13:40:41 +08:00
parent 4b760d1d0f
commit 7dca916c45
9 changed files with 235 additions and 145 deletions

View File

@@ -176,14 +176,14 @@ impl fmt::Debug for RangeCase {
impl From<Range<INT>> for RangeCase {
#[inline(always)]
fn from(value: Range<INT>) -> Self {
Self::ExclusiveInt(value, 0)
Self::ExclusiveInt(value, usize::MAX)
}
}
impl From<RangeInclusive<INT>> for RangeCase {
#[inline(always)]
fn from(value: RangeInclusive<INT>) -> Self {
Self::InclusiveInt(value, 0)
Self::InclusiveInt(value, usize::MAX)
}
}
@@ -256,14 +256,16 @@ impl RangeCase {
}
}
pub type CaseBlocksList = smallvec::SmallVec<[usize; 1]>;
/// _(internals)_ A type containing all cases for a `switch` statement.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct SwitchCases {
pub struct SwitchCasesCollection {
/// List of [`ConditionalStmtBlock`]'s.
pub blocks: StaticVec<ConditionalStmtBlock>,
pub case_blocks: StaticVec<ConditionalStmtBlock>,
/// Dictionary mapping value hashes to [`ConditionalStmtBlock`]'s.
pub cases: BTreeMap<u64, usize>,
pub cases: BTreeMap<u64, CaseBlocksList>,
/// Statements block for the default case (there can be no condition for the default case).
pub def_case: usize,
/// List of range cases.
@@ -512,7 +514,7 @@ pub enum Stmt {
/// 0) Hash table for (condition, block)
/// 1) Default block
/// 2) List of ranges: (start, end, inclusive, condition, statement)
Switch(Box<(Expr, SwitchCases)>, Position),
Switch(Box<(Expr, SwitchCasesCollection)>, Position),
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
///
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
@@ -755,15 +757,18 @@ impl Stmt {
Self::Switch(x, ..) => {
let (expr, sw) = &**x;
expr.is_pure()
&& sw.cases.values().all(|&c| {
let block = &sw.blocks[c];
&& sw.cases.values().flat_map(|cases| cases.iter()).all(|&c| {
let block = &sw.case_blocks[c];
block.condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
})
&& sw.ranges.iter().all(|r| {
let block = &sw.blocks[r.index()];
let block = &sw.case_blocks[r.index()];
block.condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
})
&& sw.blocks[sw.def_case].statements.iter().all(Stmt::is_pure)
&& sw.case_blocks[sw.def_case]
.statements
.iter()
.all(Stmt::is_pure)
}
// Loops that exit can be pure because it can never be infinite.
@@ -904,20 +909,22 @@ impl Stmt {
if !expr.walk(path, on_node) {
return false;
}
for (.., &b) in &sw.cases {
let block = &sw.blocks[b];
for (.., blocks) in &sw.cases {
for &b in blocks {
let block = &sw.case_blocks[b];
if !block.condition.walk(path, on_node) {
return false;
}
for s in &block.statements {
if !s.walk(path, on_node) {
if !block.condition.walk(path, on_node) {
return false;
}
for s in &block.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
}
for r in &sw.ranges {
let block = &sw.blocks[r.index()];
let block = &sw.case_blocks[r.index()];
if !block.condition.walk(path, on_node) {
return false;
@@ -928,7 +935,7 @@ impl Stmt {
}
}
}
for s in &sw.blocks[sw.def_case].statements {
for s in &sw.case_blocks[sw.def_case].statements {
if !s.walk(path, on_node) {
return false;
}