Switch case condition that is constant () no longer optimizes to false.
This commit is contained in:
parent
ff6954d9d5
commit
281e94fc62
@ -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
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -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)]
|
||||||
|
185
src/optimizer.rs
185
src/optimizer.rs
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user