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. * 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 Version 1.0.0

View File

@ -1073,7 +1073,9 @@ pub enum Stmt {
Box<(BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>, StmtBlock)>, Box<(BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>, StmtBlock)>,
Position, 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), While(Expr, Box<StmtBlock>, Position),
/// `do` `{` stmt `}` `while`|`until` expr /// `do` `{` stmt `}` `while`|`until` expr
/// ///
@ -1084,7 +1086,7 @@ pub enum Stmt {
Do(Box<StmtBlock>, Expr, OptionFlags, Position), Do(Box<StmtBlock>, Expr, OptionFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Expr, Box<(Ident, Option<Ident>, StmtBlock)>, Position), For(Expr, Box<(Ident, Option<Ident>, StmtBlock)>, Position),
/// \[`export`\] `let`/`const` id `=` expr /// \[`export`\] `let`|`const` id `=` expr
/// ///
/// ### Option Flags /// ### Option Flags
/// ///
@ -1298,10 +1300,22 @@ impl Stmt {
}) })
&& (x.1).0.iter().all(Stmt::is_pure) && (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::For(iterable, x, _) => iterable.is_pure() && (x.2).0.iter().all(Stmt::is_pure),
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false, Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,

View File

@ -49,3 +49,16 @@ fn test_do() -> Result<(), Box<EvalAltResult>> {
Ok(()) 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(())
}