mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refactor depth checks and apply them in math (#4845)
This commit is contained in:
parent
4365e18454
commit
92f2c7b470
@ -7,7 +7,7 @@ use comemo::{Track, Tracked, TrackedMut, Validate};
|
|||||||
use ecow::EcoVec;
|
use ecow::EcoVec;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
use crate::diag::{SourceDiagnostic, SourceResult};
|
use crate::diag::{bail, HintedStrResult, SourceDiagnostic, SourceResult, StrResult};
|
||||||
use crate::foundations::{Styles, Value};
|
use crate::foundations::{Styles, Value};
|
||||||
use crate::introspection::Introspector;
|
use crate::introspection::Introspector;
|
||||||
use crate::syntax::{FileId, Span};
|
use crate::syntax::{FileId, Span};
|
||||||
@ -229,21 +229,6 @@ pub struct Route<'a> {
|
|||||||
upper: AtomicUsize,
|
upper: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 maximum layout nesting depth.
|
|
||||||
pub const MAX_LAYOUT_DEPTH: usize = 72;
|
|
||||||
|
|
||||||
/// The maximum function call nesting depth.
|
|
||||||
pub const MAX_CALL_DEPTH: usize = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Route<'a> {
|
impl<'a> Route<'a> {
|
||||||
/// Create a new, empty route.
|
/// Create a new, empty route.
|
||||||
pub fn root() -> Self {
|
pub fn root() -> Self {
|
||||||
@ -297,6 +282,51 @@ impl<'a> Route<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The maximum nesting depths. They are different so that even if show rule and
|
||||||
|
/// call checks are interleaved, for show rule problems we always get the show
|
||||||
|
/// rule error. 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.
|
||||||
|
const MAX_SHOW_RULE_DEPTH: usize = 64;
|
||||||
|
|
||||||
|
/// The maximum layout nesting depth.
|
||||||
|
const MAX_LAYOUT_DEPTH: usize = 72;
|
||||||
|
|
||||||
|
/// The maximum function call nesting depth.
|
||||||
|
const MAX_CALL_DEPTH: usize = 80;
|
||||||
|
|
||||||
|
/// Ensures that we are within the maximum show rule depth.
|
||||||
|
pub fn check_show_depth(&self) -> HintedStrResult<()> {
|
||||||
|
if !self.within(Route::MAX_SHOW_RULE_DEPTH) {
|
||||||
|
bail!(
|
||||||
|
"maximum show rule depth exceeded";
|
||||||
|
hint: "check whether the show rule matches its own output"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that we are within the maximum layout depth.
|
||||||
|
pub fn check_layout_depth(&self) -> HintedStrResult<()> {
|
||||||
|
if !self.within(Route::MAX_LAYOUT_DEPTH) {
|
||||||
|
bail!(
|
||||||
|
"maximum layout depth exceeded";
|
||||||
|
hint: "try to reduce the amount of nesting in your layout",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that we are within the maximum function call depth.
|
||||||
|
pub fn check_call_depth(&self) -> StrResult<()> {
|
||||||
|
if !self.within(Route::MAX_CALL_DEPTH) {
|
||||||
|
bail!("maximum function call depth exceeded");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[comemo::track]
|
#[comemo::track]
|
||||||
impl<'a> Route<'a> {
|
impl<'a> Route<'a> {
|
||||||
/// Whether the given id is part of the route.
|
/// Whether the given id is part of the route.
|
||||||
|
@ -30,9 +30,7 @@ impl Eval for ast::FuncCall<'_> {
|
|||||||
let args = self.args();
|
let args = self.args();
|
||||||
let trailing_comma = args.trailing_comma();
|
let trailing_comma = args.trailing_comma();
|
||||||
|
|
||||||
if !vm.engine.route.within(Route::MAX_CALL_DEPTH) {
|
vm.engine.route.check_call_depth().at(span)?;
|
||||||
bail!(span, "maximum function call depth exceeded");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to evaluate as a call to an associated function or field.
|
// Try to evaluate as a call to an associated function or field.
|
||||||
let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {
|
let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {
|
||||||
|
@ -7,7 +7,7 @@ use std::num::NonZeroUsize;
|
|||||||
|
|
||||||
use comemo::{Track, Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, At, SourceResult};
|
||||||
use crate::engine::{Engine, Route, Sink, Traced};
|
use crate::engine::{Engine, Route, Sink, Traced};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Content, NativeElement, Packed, Resolve, Smart, StyleChain, Styles,
|
Content, NativeElement, Packed, Resolve, Smart, StyleChain, Styles,
|
||||||
@ -724,12 +724,7 @@ fn layout_fragment_impl(
|
|||||||
route: Route::extend(route),
|
route: Route::extend(route),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !engine.route.within(Route::MAX_LAYOUT_DEPTH) {
|
engine.route.check_layout_depth().at(content.span())?;
|
||||||
bail!(
|
|
||||||
content.span(), "maximum layout depth exceeded";
|
|
||||||
hint: "try to reduce the amount of nesting in your layout",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in a `PageElem`, this might already be a realized flow.
|
// If we are in a `PageElem`, this might already be a realized flow.
|
||||||
let arenas = Arenas::default();
|
let arenas = Arenas::default();
|
||||||
|
@ -42,7 +42,7 @@ use self::fragment::*;
|
|||||||
use self::row::*;
|
use self::row::*;
|
||||||
use self::spacing::*;
|
use self::spacing::*;
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::{At, SourceResult};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
category, Category, Content, Module, Resolve, Scope, SequenceElem, StyleChain,
|
category, Category, Content, Module, Resolve, Scope, SequenceElem, StyleChain,
|
||||||
StyledElem,
|
StyledElem,
|
||||||
@ -239,6 +239,9 @@ impl LayoutMath for Content {
|
|||||||
if let Some((tag, realized)) =
|
if let Some((tag, realized)) =
|
||||||
process(ctx.engine, &mut ctx.locator, self, styles)?
|
process(ctx.engine, &mut ctx.locator, self, styles)?
|
||||||
{
|
{
|
||||||
|
ctx.engine.route.increase();
|
||||||
|
ctx.engine.route.check_show_depth().at(self.span())?;
|
||||||
|
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
ctx.push(MathFragment::Tag(tag.clone()));
|
ctx.push(MathFragment::Tag(tag.clone()));
|
||||||
}
|
}
|
||||||
@ -246,6 +249,8 @@ impl LayoutMath for Content {
|
|||||||
if let Some(tag) = tag {
|
if let Some(tag) = tag {
|
||||||
ctx.push(MathFragment::Tag(tag.with_kind(TagKind::End)));
|
ctx.push(MathFragment::Tag(tag.with_kind(TagKind::End)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.engine.route.decrease();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ pub use self::process::process;
|
|||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, At, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Content, ContextElem, NativeElement, Packed, SequenceElem, Smart, StyleChain,
|
Content, ContextElem, NativeElement, Packed, SequenceElem, Smart, StyleChain,
|
||||||
StyleVec, StyledElem, Styles,
|
StyleVec, StyledElem, Styles,
|
||||||
@ -139,12 +139,7 @@ impl<'a, 'v> Builder<'a, 'v> {
|
|||||||
process(self.engine, self.locator, content, styles)?
|
process(self.engine, self.locator, content, styles)?
|
||||||
{
|
{
|
||||||
self.engine.route.increase();
|
self.engine.route.increase();
|
||||||
if !self.engine.route.within(Route::MAX_SHOW_RULE_DEPTH) {
|
self.engine.route.check_show_depth().at(content.span())?;
|
||||||
bail!(
|
|
||||||
content.span(), "maximum show rule depth exceeded";
|
|
||||||
hint: "check whether the show rule matches its own output"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
self.accept(self.arenas.store(TagElem::packed(tag.clone())), styles)?;
|
self.accept(self.arenas.store(TagElem::packed(tag.clone())), styles)?;
|
||||||
|
@ -53,3 +53,9 @@
|
|||||||
// Hint: 22-25 check whether the show rule matches its own output
|
// Hint: 22-25 check whether the show rule matches its own output
|
||||||
#show math.equation: $x$
|
#show math.equation: $x$
|
||||||
$ x $
|
$ x $
|
||||||
|
|
||||||
|
--- recursion-show-math-realize ---
|
||||||
|
// Error: 22-33 maximum show rule depth exceeded
|
||||||
|
// Hint: 22-33 check whether the show rule matches its own output
|
||||||
|
#show heading: it => heading[it]
|
||||||
|
$ #heading[hi] $
|
||||||
|
Loading…
x
Reference in New Issue
Block a user