Loops cannot be pure.
This commit is contained in:
parent
81770f00e0
commit
4807fdf1cf
@ -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
|
||||||
|
22
src/ast.rs
22
src/ast.rs
@ -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,
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user