Handle break and return better.

This commit is contained in:
Stephen Chung
2020-03-17 17:33:37 +08:00
parent d2951bfb6b
commit 8efe080412
9 changed files with 242 additions and 142 deletions

View File

@@ -4,7 +4,7 @@ use crate::any::{Any, Dynamic};
use crate::engine::{
Engine, FnCallArgs, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Stmt, AST};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::scope::{Scope, ScopeEntry, VariableType};
use crate::stdlib::{
@@ -121,18 +121,40 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
Stmt::Noop(pos)
}
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(*stmt, state, false))),
expr => Stmt::While(
Box::new(optimize_expr(expr, state)),
Box::new(optimize_stmt(*stmt, state, false)),
),
expr => match optimize_stmt(*stmt, state, false) {
Stmt::Break(pos) => {
// Only a single break statement - turn into running the guard expression once
state.set_dirty();
let mut statements = vec![Stmt::Expr(Box::new(optimize_expr(expr, state)))];
if preserve_result {
statements.push(Stmt::Noop(pos))
}
Stmt::Block(statements, pos)
}
stmt => Stmt::While(Box::new(optimize_expr(expr, state)), Box::new(stmt)),
},
},
Stmt::Loop(stmt) => match optimize_stmt(*stmt, state, false) {
Stmt::Break(pos) => {
// Only a single break statement
state.set_dirty();
Stmt::Noop(pos)
}
stmt => Stmt::Loop(Box::new(stmt)),
},
Stmt::Loop(stmt) => Stmt::Loop(Box::new(optimize_stmt(*stmt, state, false))),
Stmt::For(id, expr, stmt) => Stmt::For(
id,
Box::new(optimize_expr(*expr, state)),
Box::new(optimize_stmt(*stmt, state, false)),
Box::new(match optimize_stmt(*stmt, state, false) {
Stmt::Break(pos) => {
// Only a single break statement
state.set_dirty();
Stmt::Noop(pos)
}
stmt => stmt,
}),
),
Stmt::Let(id, Some(expr), pos) => {
Stmt::Let(id, Some(Box::new(optimize_expr(*expr, state))), pos)
}
@@ -153,15 +175,12 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
optimize_stmt(stmt, state, preserve_result) // Optimize the statement
}
})
.enumerate()
.filter(|(i, stmt)| stmt.is_op() || (preserve_result && *i == orig_len - 1)) // Remove no-op's but leave the last one if we need the result
.map(|(_, stmt)| stmt)
.collect();
// Remove all raw expression statements that are pure except for the very last statement
let last_stmt = if preserve_result { result.pop() } else { None };
result.retain(|stmt| !matches!(stmt, Stmt::Expr(expr) if expr.is_pure()));
result.retain(|stmt| !stmt.is_pure());
if let Some(stmt) = last_stmt {
result.push(stmt);
@@ -197,6 +216,24 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
.collect();
}
// Remove everything following the the first return/throw
let mut dead_code = false;
result.retain(|stmt| {
if dead_code {
return false;
}
match stmt {
Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => {
dead_code = true;
}
_ => (),
}
true
});
if orig_len != result.len() {
state.set_dirty();
}
@@ -504,7 +541,15 @@ pub fn optimize_ast(
OptimizationLevel::Simple | OptimizationLevel::Full => {
let pos = fn_def.body.position();
let mut body = optimize(vec![fn_def.body], None, &Scope::new());
fn_def.body = body.pop().unwrap_or_else(|| Stmt::Noop(pos));
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => {
Stmt::Expr(val)
}
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
Stmt::Expr(Box::new(Expr::Unit(pos)))
}
stmt => stmt,
};
}
}
Arc::new(fn_def)