Switch case condition that is constant () no longer optimizes to false.

This commit is contained in:
Stephen Chung 2022-07-18 23:28:12 +08:00
parent ff6954d9d5
commit 281e94fc62
3 changed files with 105 additions and 106 deletions

View File

@ -6,6 +6,11 @@ Version 1.9.0
The minimum Rust version is now `1.60.0` in order to use the `dep:` syntax for dependencies. The minimum Rust version is now `1.60.0` in order to use the `dep:` syntax for dependencies.
Bug fixes
---------
* `switch` cases with conditions that evaluate to constant `()` no longer optimize to `false` (should raise a type error during runtime).
New features New features
------------ ------------

View File

@ -153,6 +153,27 @@ impl<B: Into<StmtBlock>> From<(Expr, B)> for ConditionalStmtBlock {
} }
} }
impl ConditionalStmtBlock {
/// Is this conditional statements block always `true`?
#[inline(always)]
#[must_use]
pub fn is_always_true(&self) -> bool {
match self.condition {
Expr::BoolConstant(true, ..) => true,
_ => false,
}
}
/// Is this conditional statements block always `false`?
#[inline(always)]
#[must_use]
pub fn is_always_false(&self) -> bool {
match self.condition {
Expr::BoolConstant(false, ..) => true,
_ => false,
}
}
}
/// _(internals)_ A type containing a range case for a `switch` statement. /// _(internals)_ A type containing a range case for a `switch` statement.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Clone, Hash)] #[derive(Clone, Hash)]

View File

@ -547,39 +547,36 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
let mut b = mem::take(&mut case_blocks[*index]); let mut b = mem::take(&mut case_blocks[*index]);
cases.clear(); cases.clear();
match b.condition { if b.is_always_true() {
Expr::BoolConstant(true, ..) => { // Promote the matched case
// Promote the matched case let statements: StmtBlockContainer = mem::take(&mut b.statements);
let statements: StmtBlockContainer = mem::take(&mut b.statements); let statements =
let statements = optimize_stmt_block(statements, state, true, true, false);
optimize_stmt_block(statements, state, true, true, false); *stmt = (statements, b.statements.span()).into();
*stmt = (statements, b.statements.span()).into(); } else {
} // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
ref mut condition => { optimize_expr(&mut b.condition, state, false);
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(condition, state, false);
let else_stmt = if let Some(index) = def_case { let else_stmt = if let Some(index) = def_case {
let def_case = &mut case_blocks[*index].statements; let def_case = &mut case_blocks[*index].statements;
let def_span = def_case.span_or_else(*pos, Position::NONE); let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case); let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt = let def_stmt =
optimize_stmt_block(def_case, state, true, true, false); optimize_stmt_block(def_case, state, true, true, false);
StmtBlock::new_with_span(def_stmt, def_span) StmtBlock::new_with_span(def_stmt, def_span)
} else { } else {
StmtBlock::NONE StmtBlock::NONE
}; };
*stmt = Stmt::If( *stmt = Stmt::If(
( (
mem::take(condition), mem::take(&mut b.condition),
mem::take(&mut b.statements), mem::take(&mut b.statements),
else_stmt, else_stmt,
) )
.into(), .into(),
match_expr.start_position(), match_expr.start_position(),
); );
}
} }
state.set_dirty(); state.set_dirty();
@ -589,18 +586,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
for &index in case_blocks_list { for &index in case_blocks_list {
let mut b = mem::take(&mut case_blocks[index]); let mut b = mem::take(&mut case_blocks[index]);
match b.condition { if b.is_always_true() {
Expr::BoolConstant(true, ..) => { // Promote the matched case
// Promote the matched case let statements: StmtBlockContainer = mem::take(&mut b.statements);
let statements: StmtBlockContainer = let statements =
mem::take(&mut b.statements); optimize_stmt_block(statements, state, true, true, false);
let statements = *stmt = (statements, b.statements.span()).into();
optimize_stmt_block(statements, state, true, true, false); state.set_dirty();
*stmt = (statements, b.statements.span()).into(); return;
state.set_dirty();
return;
}
_ => (),
} }
} }
} }
@ -613,47 +606,43 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Only one range or all ranges without conditions // Only one range or all ranges without conditions
if ranges.len() == 1 if ranges.len() == 1
|| ranges.iter().all(|r| { || ranges
matches!( .iter()
case_blocks[r.index()].condition, .all(|r| case_blocks[r.index()].is_always_true())
Expr::BoolConstant(true, ..)
)
})
{ {
for r in ranges.iter().filter(|r| r.contains(value)) { for r in ranges.iter().filter(|r| r.contains(value)) {
let condition = mem::take(&mut case_blocks[r.index()].condition); let range_block = &mut case_blocks[r.index()];
match condition { if range_block.is_always_true() {
Expr::BoolConstant(true, ..) => { // Promote the matched case
// Promote the matched case let block = &mut case_blocks[r.index()];
let block = &mut case_blocks[r.index()]; let statements = mem::take(&mut *block.statements);
let statements = mem::take(&mut *block.statements); let statements =
let statements = optimize_stmt_block(statements, state, true, true, false);
optimize_stmt_block(statements, state, true, true, false); *stmt = (statements, block.statements.span()).into();
*stmt = (statements, block.statements.span()).into(); } else {
} let mut condition = mem::take(&mut range_block.condition);
mut condition => {
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false);
let else_stmt = if let Some(index) = def_case { // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
let def_case = &mut case_blocks[*index].statements; optimize_expr(&mut condition, state, false);
let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt =
optimize_stmt_block(def_case, state, true, true, false);
StmtBlock::new_with_span(def_stmt, def_span)
} else {
StmtBlock::NONE
};
let statements = mem::take(&mut case_blocks[r.index()].statements); let else_stmt = if let Some(index) = def_case {
let def_case = &mut case_blocks[*index].statements;
let def_span = def_case.span_or_else(*pos, Position::NONE);
let def_case: StmtBlockContainer = mem::take(def_case);
let def_stmt =
optimize_stmt_block(def_case, state, true, true, false);
StmtBlock::new_with_span(def_stmt, def_span)
} else {
StmtBlock::NONE
};
*stmt = Stmt::If( let statements = mem::take(&mut case_blocks[r.index()].statements);
(condition, statements, else_stmt).into(),
match_expr.start_position(), *stmt = Stmt::If(
); (condition, statements, else_stmt).into(),
} match_expr.start_position(),
);
} }
state.set_dirty(); state.set_dirty();
@ -681,14 +670,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
optimize_stmt_block(statements, state, preserve_result, true, false); optimize_stmt_block(statements, state, preserve_result, true, false);
optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.condition, state, false);
match b.condition {
Expr::Unit(pos) => {
b.condition = Expr::BoolConstant(true, pos);
state.set_dirty()
}
_ => (),
}
} }
return; return;
} }
@ -730,38 +711,29 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.condition, state, false);
match b.condition { if b.is_always_false() {
Expr::Unit(pos) => { if !b.statements.is_empty() {
b.condition = Expr::BoolConstant(true, pos); b.statements = StmtBlock::NONE;
state.set_dirty(); state.set_dirty();
} }
Expr::BoolConstant(false, ..) => {
if !b.statements.is_empty() {
b.statements = StmtBlock::NONE;
state.set_dirty();
}
}
_ => (),
} }
} }
// Remove false cases // Remove false cases
cases.retain(|_, list| { cases.retain(|_, list| {
// Remove all entries that have false conditions // Remove all entries that have false conditions
list.retain(|index| match case_blocks[*index].condition { list.retain(|index| {
Expr::BoolConstant(false, ..) => { if case_blocks[*index].is_always_false() {
state.set_dirty(); state.set_dirty();
false false
} else {
true
} }
_ => true,
}); });
// Remove all entries after a `true` condition // Remove all entries after a `true` condition
if let Some(n) = list if let Some(n) = list
.iter() .iter()
.find(|&&index| match case_blocks[index].condition { .find(|&&index| case_blocks[index].is_always_true())
Expr::BoolConstant(true, ..) => true,
_ => false,
})
{ {
if n + 1 < list.len() { if n + 1 < list.len() {
state.set_dirty(); state.set_dirty();
@ -779,12 +751,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
}); });
// Remove false ranges // Remove false ranges
ranges.retain(|r| match case_blocks[r.index()].condition { ranges.retain(|r| {
Expr::BoolConstant(false, ..) => { if case_blocks[r.index()].is_always_false() {
state.set_dirty(); state.set_dirty();
false false
} else {
true
} }
_ => true,
}); });
if let Some(index) = def_case { if let Some(index) = def_case {