diff --git a/crates/typst/src/engine.rs b/crates/typst/src/engine.rs index 189034cff..7d25bad30 100644 --- a/crates/typst/src/engine.rs +++ b/crates/typst/src/engine.rs @@ -8,9 +8,6 @@ use crate::introspection::{Introspector, Locator}; use crate::syntax::FileId; use crate::World; -/// The maxmium stack nesting depth. -const MAX_DEPTH: usize = 64; - /// Holds all data needed during compilation. pub struct Engine<'a> { /// The compilation environment. @@ -69,26 +66,28 @@ pub struct Route<'a> { upper: Cell, } +/// The maximum nesting depths. They are different so that even if show rule and +/// call checks are interleaved, show rule problems we always get the show rule. +/// The lower the max depth for a kind of error, the higher its precedence +/// compared to the others. +impl Route<'_> { + /// The maximum stack nesting depth. + pub const MAX_SHOW_RULE_DEPTH: usize = 64; + + /// The maxmium layout nesting depth. + pub const MAX_LAYOUT_DEPTH: usize = 72; + + /// The maxmium function call nesting depth. + pub const MAX_CALL_DEPTH: usize = 80; +} + impl<'a> Route<'a> { /// Create a new, empty route. pub fn root() -> Self { Self { id: None, outer: None, len: 0, upper: Cell::new(0) } } - /// Insert a new id into the route. - /// - /// You must guarantee that `outer` lives longer than the resulting - /// route is ever used. - pub fn insert(outer: Tracked<'a, Self>, id: FileId) -> Self { - Route { - outer: Some(outer), - id: Some(id), - len: 0, - upper: Cell::new(usize::MAX), - } - } - - /// Extend the route without another id. + /// Extend the route with another segment with a default length of 1. pub fn extend(outer: Tracked<'a, Self>) -> Self { Route { outer: Some(outer), @@ -98,6 +97,16 @@ impl<'a> Route<'a> { } } + /// Attach a file id to the route segment. + pub fn with_id(self, id: FileId) -> Self { + Self { id: Some(id), ..self } + } + + /// Set the length of the route segment to zero. + pub fn unnested(self) -> Self { + Self { len: 0, ..self } + } + /// Start tracking this route. /// /// In comparison to [`Track::track`], this method skips this chain link @@ -118,11 +127,6 @@ impl<'a> Route<'a> { pub fn decrease(&mut self) { self.len -= 1; } - - /// Check whether the nesting depth exceeds the limit. - pub fn exceeding(&self) -> bool { - !self.within(MAX_DEPTH) - } } #[comemo::track] diff --git a/crates/typst/src/eval/call.rs b/crates/typst/src/eval/call.rs index 357d5e485..daa12cff5 100644 --- a/crates/typst/src/eval/call.rs +++ b/crates/typst/src/eval/call.rs @@ -28,7 +28,7 @@ impl Eval for ast::FuncCall<'_> { let args = self.args(); let trailing_comma = args.trailing_comma(); - if vm.engine.route.exceeding() { + if !vm.engine.route.within(Route::MAX_CALL_DEPTH) { bail!(span, "maximum function call depth exceeded"); } diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs index 9de4f1b73..d16f32047 100644 --- a/crates/typst/src/eval/mod.rs +++ b/crates/typst/src/eval/mod.rs @@ -53,7 +53,7 @@ pub fn eval( let introspector = Introspector::default(); let engine = Engine { world, - route: Route::insert(route, id), + route: Route::extend(route).with_id(id), introspector: introspector.track(), locator: &mut locator, tracer, diff --git a/crates/typst/src/introspection/counter.rs b/crates/typst/src/introspection/counter.rs index ce4859976..10960a853 100644 --- a/crates/typst/src/introspection/counter.rs +++ b/crates/typst/src/introspection/counter.rs @@ -277,7 +277,7 @@ impl Counter { let mut engine = Engine { world, introspector, - route: Route::extend(route), + route: Route::extend(route).unnested(), locator: &mut locator, tracer, }; diff --git a/crates/typst/src/introspection/state.rs b/crates/typst/src/introspection/state.rs index 8479bace6..382d82225 100644 --- a/crates/typst/src/introspection/state.rs +++ b/crates/typst/src/introspection/state.rs @@ -228,7 +228,7 @@ impl State { let mut engine = Engine { world, introspector, - route: Route::extend(route), + route: Route::extend(route).unnested(), locator: &mut locator, tracer, }; diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs index ba88f74e0..6fcdd56d3 100644 --- a/crates/typst/src/layout/inline/mod.rs +++ b/crates/typst/src/layout/inline/mod.rs @@ -28,7 +28,7 @@ use crate::text::{ use crate::util::Numeric; use crate::World; -/// Layout's content inline. +/// Layouts content inline. pub(crate) fn layout_inline( children: &[Prehashed], engine: &mut Engine, diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs index dbdadbde5..1af118566 100644 --- a/crates/typst/src/layout/mod.rs +++ b/crates/typst/src/layout/mod.rs @@ -182,7 +182,7 @@ impl LayoutRoot for Content { let mut engine = Engine { world, introspector, - route: Route::extend(route), + route: Route::extend(route).unnested(), locator: &mut locator, tracer, }; @@ -237,7 +237,7 @@ impl Layout for Content { tracer, }; - if engine.route.exceeding() { + if !engine.route.within(Route::MAX_LAYOUT_DEPTH) { bail!( content.span(), "maximum layout depth exceeded"; hint: "try to reduce the amount of nesting in your layout", diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index eda43b4db..1b2047210 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -11,7 +11,7 @@ use smallvec::smallvec; use typed_arena::Arena; use crate::diag::{bail, SourceResult}; -use crate::engine::Engine; +use crate::engine::{Engine, Route}; use crate::foundations::{ Content, Finalize, Guard, NativeElement, Recipe, Selector, Show, StyleChain, StyleVecBuilder, Styles, Synthesize, @@ -307,7 +307,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { if let Some(realized) = realize(self.engine, content, styles)? { self.engine.route.increase(); - if self.engine.route.exceeding() { + if !self.engine.route.within(Route::MAX_SHOW_RULE_DEPTH) { bail!( content.span(), "maximum show rule depth exceeded"; hint: "check whether the show rule matches its own output"; diff --git a/tests/typ/compiler/recursion.typ b/tests/typ/compiler/recursion.typ index 461680abf..ef5ea7e6a 100644 --- a/tests/typ/compiler/recursion.typ +++ b/tests/typ/compiler/recursion.typ @@ -44,8 +44,8 @@ --- // Test cyclic imports during layout. -// Error: 2-38 maximum layout depth exceeded -// Hint: 2-38 try to reduce the amount of nesting in your layout +// Error: 14-37 maximum layout depth exceeded +// Hint: 14-37 try to reduce the amount of nesting in your layout #layout(_ => include "recursion.typ") ---