Refactor depth checks and apply them in math (#4845)

This commit is contained in:
Laurenz 2024-08-27 10:43:12 +02:00 committed by GitHub
parent 4365e18454
commit 92f2c7b470
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 64 additions and 35 deletions

View File

@ -7,7 +7,7 @@ use comemo::{Track, Tracked, TrackedMut, Validate};
use ecow::EcoVec;
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::introspection::Introspector;
use crate::syntax::{FileId, Span};
@ -229,21 +229,6 @@ pub struct Route<'a> {
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> {
/// Create a new, empty route.
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]
impl<'a> Route<'a> {
/// Whether the given id is part of the route.

View File

@ -30,9 +30,7 @@ impl Eval for ast::FuncCall<'_> {
let args = self.args();
let trailing_comma = args.trailing_comma();
if !vm.engine.route.within(Route::MAX_CALL_DEPTH) {
bail!(span, "maximum function call depth exceeded");
}
vm.engine.route.check_call_depth().at(span)?;
// Try to evaluate as a call to an associated function or field.
let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {

View File

@ -7,7 +7,7 @@ use std::num::NonZeroUsize;
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::foundations::{
Content, NativeElement, Packed, Resolve, Smart, StyleChain, Styles,
@ -724,12 +724,7 @@ fn layout_fragment_impl(
route: Route::extend(route),
};
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",
);
}
engine.route.check_layout_depth().at(content.span())?;
// If we are in a `PageElem`, this might already be a realized flow.
let arenas = Arenas::default();

View File

@ -42,7 +42,7 @@ use self::fragment::*;
use self::row::*;
use self::spacing::*;
use crate::diag::SourceResult;
use crate::diag::{At, SourceResult};
use crate::foundations::{
category, Category, Content, Module, Resolve, Scope, SequenceElem, StyleChain,
StyledElem,
@ -239,6 +239,9 @@ impl LayoutMath for Content {
if let Some((tag, realized)) =
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 {
ctx.push(MathFragment::Tag(tag.clone()));
}
@ -246,6 +249,8 @@ impl LayoutMath for Content {
if let Some(tag) = tag {
ctx.push(MathFragment::Tag(tag.with_kind(TagKind::End)));
}
ctx.engine.route.decrease();
return Ok(());
}

View File

@ -15,8 +15,8 @@ pub use self::process::process;
use std::mem;
use crate::diag::{bail, SourceResult};
use crate::engine::{Engine, Route};
use crate::diag::{bail, At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
Content, ContextElem, NativeElement, Packed, SequenceElem, Smart, StyleChain,
StyleVec, StyledElem, Styles,
@ -139,12 +139,7 @@ impl<'a, 'v> Builder<'a, 'v> {
process(self.engine, self.locator, content, styles)?
{
self.engine.route.increase();
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"
);
}
self.engine.route.check_show_depth().at(content.span())?;
if let Some(tag) = &tag {
self.accept(self.arenas.store(TagElem::packed(tag.clone())), styles)?;

View File

@ -53,3 +53,9 @@
// Hint: 22-25 check whether the show rule matches its own output
#show math.equation: $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] $