Loops cannot be pure.

This commit is contained in:
Stephen Chung 2021-08-04 17:37:56 +08:00
parent 81770f00e0
commit 4807fdf1cf
3 changed files with 32 additions and 4 deletions

View File

@ -8,6 +8,7 @@ Bug fixes
---------
* Fixed bug in using indexing/dotting inside index bracket.
* `while` and `loop` statements are no longer considered _pure_ (since a loop can go on forever and this is a side effect).
Version 1.0.0

View File

@ -1073,7 +1073,9 @@ pub enum Stmt {
Box<(BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>, StmtBlock)>,
Position,
),
/// `while` expr `{` stmt `}`
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
///
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
While(Expr, Box<StmtBlock>, Position),
/// `do` `{` stmt `}` `while`|`until` expr
///
@ -1084,7 +1086,7 @@ pub enum Stmt {
Do(Box<StmtBlock>, Expr, OptionFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Expr, Box<(Ident, Option<Ident>, StmtBlock)>, Position),
/// \[`export`\] `let`/`const` id `=` expr
/// \[`export`\] `let`|`const` id `=` expr
///
/// ### Option Flags
///
@ -1298,10 +1300,22 @@ impl Stmt {
})
&& (x.1).0.iter().all(Stmt::is_pure)
}
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
condition.is_pure() && block.0.iter().all(Stmt::is_pure)
// Loops that exit can be pure because it can never be infinite.
Self::While(Expr::BoolConstant(false, _), _, _) => true,
Self::Do(body, Expr::BoolConstant(x, _), options, _)
if *x == options.contains(AST_OPTION_FLAGS::AST_OPTION_NEGATED) =>
{
body.iter().all(Stmt::is_pure)
}
// Loops are never pure since they can be infinite - and that's a side effect.
Self::While(_, _, _) | Self::Do(_, _, _, _) => false,
// For loops can be pure because if the iterable is pure, it is finite,
// so infinite loops can never occur.
Self::For(iterable, x, _) => iterable.is_pure() && (x.2).0.iter().all(Stmt::is_pure),
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,

View File

@ -49,3 +49,16 @@ fn test_do() -> Result<(), Box<EvalAltResult>> {
Ok(())
}
#[test]
fn test_infinite_loops() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
engine.set_max_operations(1024);
assert!(engine.consume("loop {}").is_err());
assert!(engine.consume("while true {}").is_err());
assert!(engine.consume("do {} while true").is_err());
Ok(())
}