diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 153ee2ad..d214f95c 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -266,10 +266,10 @@ pub struct SwitchCasesCollection { pub case_blocks: StaticVec, /// Dictionary mapping value hashes to [`ConditionalStmtBlock`]'s. pub cases: BTreeMap, - /// Statements block for the default case (there can be no condition for the default case). - pub def_case: usize, /// List of range cases. pub ranges: StaticVec, + /// Statements block for the default case (there can be no condition for the default case). + pub def_case: Option, } /// _(internals)_ A `try-catch` block. @@ -765,7 +765,8 @@ impl Stmt { let block = &sw.case_blocks[r.index()]; block.condition.is_pure() && block.statements.iter().all(Stmt::is_pure) }) - && sw.case_blocks[sw.def_case] + && sw.def_case.is_some() + && sw.case_blocks[sw.def_case.unwrap()] .statements .iter() .all(Stmt::is_pure) @@ -935,9 +936,11 @@ impl Stmt { } } } - for s in &sw.case_blocks[sw.def_case].statements { - if !s.walk(path, on_node) { - return false; + if let Some(index) = sw.def_case { + for s in &sw.case_blocks[index].statements { + if !s.walk(path, on_node) { + return false; + } } } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 03d36097..44da194b 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -498,12 +498,16 @@ impl Engine { } } else if let Ok(None) = stmt_block_result { // Default match clause - let def_case = &case_blocks[*def_case].statements; + if let Some(index) = def_case { + let def_case = &case_blocks[*index].statements; - if !def_case.is_empty() { - self.eval_stmt_block( - scope, global, caches, lib, this_ptr, def_case, true, level, - ) + if !def_case.is_empty() { + self.eval_stmt_block( + scope, global, caches, lib, this_ptr, def_case, true, level, + ) + } else { + Ok(Dynamic::UNIT) + } } else { Ok(Dynamic::UNIT) } diff --git a/src/optimizer.rs b/src/optimizer.rs index 794cc124..1ed3b0b1 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -559,16 +559,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(condition, state, false); - let def_case = &mut case_blocks[*def_case].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); + 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( ( mem::take(condition), mem::take(&mut b.statements), - StmtBlock::new_with_span(def_stmt, def_span), + else_stmt, ) .into(), match_expr.start_position(), @@ -630,21 +636,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } optimize_expr(&mut condition, state, false); - let def_case = &mut case_blocks[*def_case].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); + 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 + }; let statements = mem::take(&mut case_blocks[r.index()].statements); *stmt = Stmt::If( - ( - condition, - statements, - StmtBlock::new_with_span(def_stmt, def_span), - ) - .into(), + (condition, statements, else_stmt).into(), match_expr.start_position(), ); } @@ -690,11 +696,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b // Promote the default case state.set_dirty(); - let def_case = &mut case_blocks[*def_case].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); - *stmt = (def_stmt, def_span).into(); + + 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); + *stmt = (def_stmt, def_span).into(); + } else { + *stmt = StmtBlock::empty(*pos).into(); + } } // switch Stmt::Switch(x, ..) => { @@ -735,11 +746,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b } // Remove false cases - let cases_len = cases.len(); cases.retain(|_, list| { // Remove all entries that have false conditions list.retain(|index| match case_blocks[*index].condition { - Expr::BoolConstant(false, ..) => false, + Expr::BoolConstant(false, ..) => { + state.set_dirty(); + false + } _ => true, }); // Remove all entries after a `true` condition @@ -750,14 +763,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b _ => false, }) { - list.truncate(n + 1); + if n + 1 < list.len() { + state.set_dirty(); + list.truncate(n + 1); + } } // Remove if no entry left - !list.is_empty() + match list.is_empty() { + true => { + state.set_dirty(); + false + } + false => true, + } }); - if cases.len() != cases_len { - state.set_dirty(); - } + // Remove false ranges ranges.retain(|r| match case_blocks[r.index()].condition { Expr::BoolConstant(false, ..) => { @@ -767,13 +787,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b _ => true, }); - let def_stmt_block = &mut case_blocks[*def_case].statements; - let def_block = mem::take(&mut **def_stmt_block); - **def_stmt_block = optimize_stmt_block(def_block, state, preserve_result, true, false); + if let Some(index) = def_case { + let def_stmt_block = &mut case_blocks[*index].statements; + let def_block = mem::take(&mut **def_stmt_block); + **def_stmt_block = + optimize_stmt_block(def_block, state, preserve_result, true, false); + } // Remove unused block statements for index in 0..case_blocks.len() { - if *def_case == index + if *def_case == Some(index) || cases.values().flat_map(|c| c.iter()).any(|&n| n == index) || ranges.iter().any(|r| r.index() == index) { diff --git a/src/parser.rs b/src/parser.rs index 0f36dd2b..7805deeb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1058,8 +1058,8 @@ impl Engine { let mut case_blocks = StaticVec::::new(); let mut cases = BTreeMap::::new(); let mut ranges = StaticVec::::new(); - let mut def_stmt_pos = Position::NONE; - let mut def_stmt_index = None; + let mut def_case = None; + let mut def_case_pos = Position::NONE; loop { const MISSING_RBRACE: &str = "to end this switch block"; @@ -1075,8 +1075,8 @@ impl Engine { .into_err(*pos), ) } - (Token::Underscore, pos) if def_stmt_index.is_none() => { - def_stmt_pos = *pos; + (Token::Underscore, pos) if def_case.is_none() => { + def_case_pos = *pos; eat_token(input, Token::Underscore); let (if_clause, if_pos) = match_token(input, Token::If); @@ -1087,8 +1087,8 @@ impl Engine { (Default::default(), Expr::BoolConstant(true, Position::NONE)) } - _ if def_stmt_index.is_some() => { - return Err(PERR::WrongSwitchDefaultCase.into_err(def_stmt_pos)) + _ if def_case.is_some() => { + return Err(PERR::WrongSwitchDefaultCase.into_err(def_case_pos)) } _ => { @@ -1197,7 +1197,7 @@ impl Engine { .or_insert_with(|| [index].into()); } } else { - def_stmt_index = Some(index); + def_case = Some(index); } match input.peek().expect(NEVER_ENDS) { @@ -1223,11 +1223,6 @@ impl Engine { } } - let def_case = def_stmt_index.unwrap_or_else(|| { - case_blocks.push(Default::default()); - case_blocks.len() - 1 - }); - let cases = SwitchCasesCollection { case_blocks, cases,