mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Detect infinite loops
This commit is contained in:
parent
2271d67f8f
commit
ad66fbdfa2
@ -16,7 +16,7 @@ use crate::diag::{
|
|||||||
};
|
};
|
||||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
||||||
use crate::syntax::ast::AstNode;
|
use crate::syntax::ast::AstNode;
|
||||||
use crate::syntax::{ast, Source, SourceId, Span, Spanned, Unit};
|
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, Unit};
|
||||||
use crate::util::{format_eco, EcoString, PathExt};
|
use crate::util::{format_eco, EcoString, PathExt};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -994,12 +994,25 @@ impl Eval for ast::WhileLoop {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
const MAX_ITERS: usize = 10_000;
|
||||||
|
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
let condition = self.condition();
|
let condition = self.condition();
|
||||||
while condition.eval(vm)?.cast::<bool>().at(condition.span())? {
|
|
||||||
let body = self.body();
|
let body = self.body();
|
||||||
|
|
||||||
|
while condition.eval(vm)?.cast::<bool>().at(condition.span())? {
|
||||||
|
if i == 0
|
||||||
|
&& is_invariant(condition.as_untyped())
|
||||||
|
&& !can_diverge(body.as_untyped())
|
||||||
|
{
|
||||||
|
bail!(condition.span(), "condition is always true");
|
||||||
|
} else if i >= MAX_ITERS {
|
||||||
|
bail!(self.span(), "loop seems to be infinite");
|
||||||
|
}
|
||||||
|
|
||||||
let value = body.eval(vm)?;
|
let value = body.eval(vm)?;
|
||||||
output = ops::join(output, value).at(body.span())?;
|
output = ops::join(output, value).at(body.span())?;
|
||||||
|
|
||||||
@ -1012,6 +1025,8 @@ impl Eval for ast::WhileLoop {
|
|||||||
Some(Flow::Return(..)) => break,
|
Some(Flow::Return(..)) => break,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if flow.is_some() {
|
if flow.is_some() {
|
||||||
@ -1022,6 +1037,24 @@ impl Eval for ast::WhileLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the expression always evaluates to the same value.
|
||||||
|
fn is_invariant(expr: &SyntaxNode) -> bool {
|
||||||
|
match expr.cast() {
|
||||||
|
Some(ast::Expr::Ident(_)) => false,
|
||||||
|
Some(ast::Expr::MethodCall(call)) => {
|
||||||
|
is_invariant(call.target().as_untyped())
|
||||||
|
&& is_invariant(call.args().as_untyped())
|
||||||
|
}
|
||||||
|
_ => expr.children().all(is_invariant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the expression contains a break or return.
|
||||||
|
fn can_diverge(expr: &SyntaxNode) -> bool {
|
||||||
|
matches!(expr.kind(), SyntaxKind::Break | SyntaxKind::Return)
|
||||||
|
|| expr.children().any(can_diverge)
|
||||||
|
}
|
||||||
|
|
||||||
impl Eval for ast::ForLoop {
|
impl Eval for ast::ForLoop {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
|
@ -34,11 +34,13 @@
|
|||||||
#while [nope] [nope]
|
#while [nope] [nope]
|
||||||
|
|
||||||
---
|
---
|
||||||
// Make sure that we terminate and don't complain multiple times.
|
// Error: 8-25 condition is always true
|
||||||
#while true {
|
#while 2 < "hello".len() {}
|
||||||
// Error: 3-7 unknown variable
|
|
||||||
nope
|
---
|
||||||
}
|
// Error: 2:1-2:24 loop seems to be infinite
|
||||||
|
#let i = 1
|
||||||
|
#while i > 0 { i += 1 }
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7 expected expression
|
// Error: 7 expected expression
|
||||||
|
Loading…
x
Reference in New Issue
Block a user