Delayed errors

Fixes #785. Thanks to @Dherse for the idea!
This commit is contained in:
Laurenz 2023-06-12 15:40:43 +02:00
parent 19bf1f5894
commit 378ebe5f56
14 changed files with 315 additions and 204 deletions

View File

@ -50,6 +50,7 @@ use std::mem;
use typed_arena::Arena;
use typst::diag::SourceResult;
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use typst::model::{applicable, realize, StyleVecBuilder};
use crate::math::{EquationElem, LayoutMath};
@ -116,13 +117,20 @@ impl LayoutRoot for Content {
fn cached(
content: &Content,
world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
) -> SourceResult<Document> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, tracer, locator: &mut locator, introspector };
let mut vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
let scratch = Scratch::default();
let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?;
realized
@ -132,13 +140,13 @@ impl LayoutRoot for Content {
}
tracing::info!("Starting layout");
cached(
self,
vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles,
)
}
@ -168,9 +176,10 @@ pub trait Layout {
let mut locator = Locator::chained(vt.locator.track());
let mut vt = Vt {
world: vt.world,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
locator: &mut locator,
introspector: vt.introspector,
locator: &mut locator,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
delayed: TrackedMut::reborrow_mut(&mut vt.delayed),
};
self.layout(&mut vt, styles, regions)
}
@ -184,18 +193,26 @@ impl Layout for Content {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
#[allow(clippy::too_many_arguments)]
#[comemo::memoize]
fn cached(
content: &Content,
world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, tracer, locator: &mut locator, introspector };
let mut vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
let scratch = Scratch::default();
let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?;
realized
@ -209,9 +226,10 @@ impl Layout for Content {
let fragment = cached(
self,
vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles,
regions,
)?;

View File

@ -5,6 +5,7 @@ use icu_provider_blob::BlobDataProvider;
use icu_segmenter::{LineBreakIteratorUtf8, LineSegmenter};
use once_cell::sync::Lazy;
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript};
@ -148,16 +149,23 @@ impl ParElem {
fn cached(
par: &ParElem,
world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
consecutive: bool,
region: Size,
expand: bool,
) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, tracer, locator: &mut locator, introspector };
let mut vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
let children = par.children();
// Collect all text into one string for BiDi analysis.
@ -178,9 +186,10 @@ impl ParElem {
let fragment = cached(
self,
vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles,
consecutive,
region,

View File

@ -96,11 +96,11 @@ impl BibliographyElem {
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
let mut iter = introspector.query(&Self::func().select()).into_iter();
let Some(elem) = iter.next() else {
return Err("the document does not contain a bibliography".into());
bail!("the document does not contain a bibliography");
};
if iter.next().is_some() {
Err("multiple bibliographies are not supported")?;
bail!("multiple bibliographies are not supported");
}
Ok(elem.to::<Self>().unwrap().clone())
@ -162,10 +162,7 @@ impl Show for BibliographyElem {
seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack());
}
if !vt.introspector.init() {
return Ok(Content::sequence(seq));
}
Ok(vt.delayed(|vt| {
let works = Works::new(vt).at(self.span())?;
let row_gutter = BlockElem::below_in(styles).amount();
@ -198,6 +195,7 @@ impl Show for BibliographyElem {
}
Ok(Content::sequence(seq))
}))
}
}
@ -357,10 +355,7 @@ impl Synthesize for CiteElem {
impl Show for CiteElem {
#[tracing::instrument(name = "CiteElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
Ok(vt.delayed(|vt| {
let works = Works::new(vt).at(self.span())?;
let location = self.0.location().unwrap();
works
@ -370,6 +365,7 @@ impl Show for CiteElem {
.flatten()
.ok_or("bibliography does not contain this key")
.at(self.span())
}))
}
}

View File

@ -73,12 +73,10 @@ struct LocateElem {
impl Show for LocateElem {
#[tracing::instrument(name = "LocateElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
Ok(vt.delayed(|vt| {
let location = self.0.location().unwrap();
Ok(self.func().call_vt(vt, [location])?.display())
}))
}
}

View File

@ -4,6 +4,7 @@ use std::str::FromStr;
use ecow::{eco_vec, EcoVec};
use smallvec::{smallvec, SmallVec};
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use super::{FigureElem, HeadingElem, Numbering, NumberingPattern};
use crate::layout::PageElem;
@ -397,9 +398,10 @@ impl Counter {
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
self.sequence_impl(
vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
)
}
@ -408,12 +410,19 @@ impl Counter {
fn sequence_impl(
&self,
world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, tracer, locator: &mut locator, introspector };
let mut vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
let mut state = CounterState(match &self.0 {
// special case, because pages always start at one.
CounterKey::Page => smallvec![1],
@ -618,10 +627,7 @@ struct DisplayElem {
impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
Ok(vt.delayed(|vt| {
let location = self.0.location().unwrap();
let counter = self.counter();
let numbering = self
@ -648,7 +654,9 @@ impl Show for DisplayElem {
} else {
counter.at(vt, location)?
};
state.display(vt, &numbering)
}))
}
}

View File

@ -91,21 +91,18 @@ impl Show for LinkElem {
#[tracing::instrument(name = "LinkElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
let body = self.body();
let dest = match self.dest() {
LinkTarget::Dest(dest) => dest,
LinkTarget::Label(label) => {
if !vt.introspector.init() {
return Ok(body);
}
let linked = match self.dest() {
LinkTarget::Dest(dest) => body.linked(dest),
LinkTarget::Label(label) => vt
.delayed(|vt| {
let elem = vt.introspector.query_label(&label).at(self.span())?;
Destination::Location(elem.location().unwrap())
}
let dest = Destination::Location(elem.location().unwrap());
Ok(Some(body.clone().linked(dest)))
})
.unwrap_or(body),
};
Ok(body
.linked(dest)
.styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false)))))
Ok(linked.styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false)))))
}
}

View File

@ -132,7 +132,7 @@ impl Synthesize for RefElem {
self.push_element(None);
let target = self.target();
if vt.introspector.init() && !BibliographyElem::has(vt, &target.0) {
if !BibliographyElem::has(vt, &target.0) {
if let Ok(elem) = vt.introspector.query_label(&target) {
self.push_element(Some(elem.into_inner()));
return Ok(());
@ -146,10 +146,7 @@ impl Synthesize for RefElem {
impl Show for RefElem {
#[tracing::instrument(name = "RefElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
Ok(vt.delayed(|vt| {
let target = self.target();
let elem = vt.introspector.query_label(&self.target());
let span = self.span();
@ -180,7 +177,11 @@ impl Show for RefElem {
let numbering = refable
.numbering()
.ok_or_else(|| {
eco_format!("cannot reference {0} without numbering - did you mean to use `#set {0}(numbering: \"1.\")`?", elem.func().name())
eco_format!(
"cannot reference {0} without numbering \
- did you mean to use `#set {0}(numbering: \"1.\")`?",
elem.func().name()
)
})
.at(span)?;
@ -203,6 +204,7 @@ impl Show for RefElem {
}
Ok(content.linked(Destination::Location(elem.location().unwrap())))
}))
}
}

View File

@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use ecow::{eco_vec, EcoVec};
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use crate::prelude::*;
@ -306,9 +307,10 @@ impl State {
fn sequence(&self, vt: &mut Vt) -> SourceResult<EcoVec<Value>> {
self.sequence_impl(
vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
)
}
@ -317,12 +319,19 @@ impl State {
fn sequence_impl(
&self,
world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<Value>> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, tracer, locator: &mut locator, introspector };
let mut vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
let mut state = self.init.clone();
let mut stops = eco_vec![state.clone()];
@ -397,16 +406,14 @@ struct DisplayElem {
impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
Ok(vt.delayed(|vt| {
let location = self.0.location().unwrap();
let value = self.state().at(vt, location)?;
Ok(match self.func() {
Some(func) => func.call_vt(vt, [value])?.display(),
None => value.display(),
})
}))
}
}

View File

@ -11,7 +11,7 @@ use super::{
Value, Vm,
};
use crate::diag::{bail, SourceResult, StrResult};
use crate::model::{ElemFunc, Introspector, Locator, Vt};
use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt};
use crate::syntax::ast::{self, AstNode, Expr, Ident};
use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::World;
@ -102,9 +102,10 @@ impl Func {
self,
vm.world(),
route,
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
vm.vt.locator.track(),
vm.vt.introspector,
vm.vt.locator.track(),
TrackedMut::reborrow_mut(&mut vm.vt.delayed),
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
vm.depth + 1,
args,
)
@ -129,9 +130,10 @@ impl Func {
let mut locator = Locator::chained(vt.locator.track());
let vt = Vt {
world: vt.world,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
locator: &mut locator,
introspector: vt.introspector,
locator: &mut locator,
delayed: TrackedMut::reborrow_mut(&mut vt.delayed),
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
};
let mut vm = Vm::new(vt, route.track(), id, scopes);
let args = Args::new(self.span(), args);
@ -326,9 +328,10 @@ impl Closure {
this: &Func,
world: Tracked<dyn World + '_>,
route: Tracked<Route>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
depth: usize,
mut args: Args,
) -> SourceResult<Value> {
@ -344,7 +347,13 @@ impl Closure {
// Prepare VT.
let mut locator = Locator::chained(locator);
let vt = Vt { world, tracer, locator: &mut locator, introspector };
let vt = Vt {
world,
introspector,
locator: &mut locator,
delayed,
tracer,
};
// Prepare VM.
let mut vm = Vm::new(vt, route, closure.location, scopes);

View File

@ -62,9 +62,6 @@ use ecow::{EcoString, EcoVec};
use unicode_segmentation::UnicodeSegmentation;
use self::func::{CapturesVisitor, Closure};
use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
use crate::model::{
Content, Introspector, Label, Locator, Recipe, ShowableSelector, Styles, Transform,
Unlabellable, Vt,
@ -75,6 +72,10 @@ use crate::syntax::{
};
use crate::util::PathExt;
use crate::World;
use crate::{
diag::{bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint},
model::DelayedErrors,
};
const MAX_ITERATIONS: usize = 10_000;
const MAX_CALL_DEPTH: usize = 64;
@ -102,11 +103,13 @@ pub fn eval(
// Prepare VT.
let mut locator = Locator::default();
let introspector = Introspector::default();
let mut delayed = DelayedErrors::default();
let vt = Vt {
world,
tracer,
locator: &mut locator,
introspector: introspector.track(),
locator: &mut locator,
delayed: delayed.track_mut(),
tracer,
};
// Prepare VM.
@ -151,12 +154,14 @@ pub fn eval_string(
// Prepare VT.
let mut tracer = Tracer::default();
let mut locator = Locator::default();
let mut delayed = DelayedErrors::default();
let introspector = Introspector::default();
let vt = Vt {
world,
tracer: tracer.track_mut(),
locator: &mut locator,
introspector: introspector.track(),
locator: &mut locator,
delayed: delayed.track_mut(),
tracer: tracer.track_mut(),
};
// Prepare VM.

View File

@ -241,11 +241,6 @@ impl Introspector {
#[comemo::track]
impl Introspector {
/// Whether this introspector is not yet initialized.
pub fn init(&self) -> bool {
self.pages > 0
}
/// Query for all matching elements.
pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
let hash = crate::util::hash128(selector);

View File

@ -28,7 +28,7 @@ use std::mem::ManuallyDrop;
use comemo::{Track, Tracked, TrackedMut, Validate};
use crate::diag::SourceResult;
use crate::diag::{SourceError, SourceResult};
use crate::doc::Document;
use crate::eval::Tracer;
use crate::World;
@ -46,8 +46,9 @@ pub fn typeset(
let library = world.library();
let styles = StyleChain::new(&library.styles);
let mut document;
let mut iter = 0;
let mut document;
let mut delayed;
// We need `ManuallyDrop` until this lands in stable:
// https://github.com/rust-lang/rust/issues/70919
@ -58,6 +59,8 @@ pub fn typeset(
loop {
tracing::info!("Layout iteration {iter}");
delayed = DelayedErrors::default();
let constraint = <Introspector as Validate>::Constraint::new();
let mut locator = Locator::new();
let mut vt = Vt {
@ -65,6 +68,7 @@ pub fn typeset(
tracer: TrackedMut::reborrow_mut(&mut tracer),
locator: &mut locator,
introspector: introspector.track_with(&constraint),
delayed: delayed.track_mut(),
};
// Layout!
@ -86,6 +90,11 @@ pub fn typeset(
// Drop the introspector.
ManuallyDrop::into_inner(introspector);
// Promote delayed errors.
if !delayed.0.is_empty() {
return Err(Box::new(delayed.0));
}
Ok(document)
}
@ -95,10 +104,45 @@ pub fn typeset(
pub struct Vt<'a> {
/// The compilation environment.
pub world: Tracked<'a, dyn World + 'a>,
/// The tracer for inspection of the values an expression produces.
pub tracer: TrackedMut<'a, Tracer>,
/// Provides stable identities to elements.
pub locator: &'a mut Locator<'a>,
/// Provides access to information about the document.
pub introspector: Tracked<'a, Introspector>,
/// Provides stable identities to elements.
pub locator: &'a mut Locator<'a>,
/// Delayed errors that do not immediately terminate execution.
pub delayed: TrackedMut<'a, DelayedErrors>,
/// The tracer for inspection of the values an expression produces.
pub tracer: TrackedMut<'a, Tracer>,
}
impl Vt<'_> {
/// Perform a fallible operation that does not immediately terminate further
/// execution. Instead it produces a delayed error that is only promoted to
/// a fatal one if it remains at the end of the introspection loop.
pub fn delayed<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Self) -> SourceResult<T>,
T: Default,
{
match f(self) {
Ok(value) => value,
Err(errors) => {
for error in *errors {
self.delayed.push(error);
}
T::default()
}
}
}
}
/// Holds delayed errors.
#[derive(Default, Clone)]
pub struct DelayedErrors(Vec<SourceError>);
#[comemo::track]
impl DelayedErrors {
/// Push a delayed error.
fn push(&mut self, error: SourceError) {
self.0.push(error);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,23 @@
// Test citation in other introspection.
---
#set page(width: 180pt)
#set heading(numbering: "1")
#outline(
title: [List of Figures],
target: figure.where(kind: image),
)
#pagebreak()
= Introduction <intro>
#figure(
rect[-- PIRATE --],
caption: [A pirate @arrgh in @intro],
)
#locate(loc => [Citation @distress on page #loc.page()])
#pagebreak()
#bibliography("/works.bib", style: "chicago-notes")