Remove 'static bound on World

Thanks to improvements in comemo, tracked types don't need to be 'static anymore. This means that the 'static bound on the `World` is now lifted and that the `Route` doesn't need to use unsafe code anymore to manage its lifetime.
This commit is contained in:
Laurenz 2023-05-11 10:50:30 +02:00
parent d9ba84085e
commit 47dff3765d
20 changed files with 59 additions and 50 deletions

6
Cargo.lock generated
View File

@ -369,8 +369,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]] [[package]]
name = "comemo" name = "comemo"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/typst/comemo#0c141bbe47ca2d8c123fffc3f7f1ce35bb693993"
checksum = "1ba423e212681b51c5452a458bb24e88165f4c09857a783c802719cc46313f3f"
dependencies = [ dependencies = [
"comemo-macros", "comemo-macros",
"siphasher", "siphasher",
@ -379,8 +378,7 @@ dependencies = [
[[package]] [[package]]
name = "comemo-macros" name = "comemo-macros"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/typst/comemo#0c141bbe47ca2d8c123fffc3f7f1ce35bb693993"
checksum = "fca5ceeb99665bad04a32fe297d1581a68685e36fb6da92a1c9b7d9693638c01"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -36,7 +36,7 @@ bench = false
typst-macros = { path = "macros" } typst-macros = { path = "macros" }
bitflags = { version = "2", features = ["serde"] } bitflags = { version = "2", features = ["serde"] }
bytemuck = "1" bytemuck = "1"
comemo = "0.2.2" comemo = { git = "https://github.com/typst/comemo" }
ecow = "0.1" ecow = "0.1"
flate2 = "1" flate2 = "1"
fontdb = "0.13" fontdb = "0.13"

View File

@ -25,7 +25,7 @@ typst-library = { path = "../library" }
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
clap = { version = "4.2.4", features = ["derive", "env"] } clap = { version = "4.2.4", features = ["derive", "env"] }
codespan-reporting = "0.11" codespan-reporting = "0.11"
comemo = "0.2.2" comemo = { git = "https://github.com/typst/comemo" }
dirs = "5" dirs = "5"
elsa = "1.8" elsa = "1.8"
inferno = "0.11.15" inferno = "0.11.15"

View File

@ -13,7 +13,7 @@ bench = false
[dependencies] [dependencies]
typst = { path = ".." } typst = { path = ".." }
typst-library = { path = "../library" } typst-library = { path = "../library" }
comemo = "0.2.2" comemo = { git = "https://github.com/typst/comemo" }
heck = "0.4" heck = "0.4"
include_dir = "0.7" include_dir = "0.7"
once_cell = "1" once_cell = "1"

View File

@ -19,7 +19,7 @@ bench = false
typst = { path = ".." } typst = { path = ".." }
az = "1.2" az = "1.2"
chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] } chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] }
comemo = "0.2.2" comemo = { git = "https://github.com/typst/comemo" }
csv = "1" csv = "1"
ecow = "0.1" ecow = "0.1"
hayagriva = "0.3" hayagriva = "0.3"

View File

@ -73,7 +73,7 @@ impl LayoutRoot for Content {
#[comemo::memoize] #[comemo::memoize]
fn cached( fn cached(
content: &Content, content: &Content,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
@ -139,7 +139,7 @@ impl Layout for Content {
#[comemo::memoize] #[comemo::memoize]
fn cached( fn cached(
content: &Content, content: &Content,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,

View File

@ -139,7 +139,7 @@ impl ParElem {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn cached( fn cached(
par: &ParElem, par: &ParElem,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,

View File

@ -121,7 +121,7 @@ impl BibliographyElem {
/// Find all bibliography keys. /// Find all bibliography keys.
pub fn keys( pub fn keys(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
) -> Vec<(EcoString, Option<EcoString>)> { ) -> Vec<(EcoString, Option<EcoString>)> {
Self::find(introspector) Self::find(introspector)
@ -426,7 +426,7 @@ impl Works {
/// Generate all citations and the whole bibliography. /// Generate all citations and the whole bibliography.
#[comemo::memoize] #[comemo::memoize]
fn create( fn create(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
bibliography: BibliographyElem, bibliography: BibliographyElem,
citations: Vec<CiteElem>, citations: Vec<CiteElem>,
) -> Arc<Works> { ) -> Arc<Works> {
@ -582,7 +582,7 @@ fn create(
/// Load bibliography entries from a path. /// Load bibliography entries from a path.
#[comemo::memoize] #[comemo::memoize]
fn load( fn load(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
paths: &BibPaths, paths: &BibPaths,
) -> StrResult<EcoVec<hayagriva::Entry>> { ) -> StrResult<EcoVec<hayagriva::Entry>> {
let mut result = EcoVec::new(); let mut result = EcoVec::new();

View File

@ -405,7 +405,7 @@ impl Counter {
#[comemo::memoize] #[comemo::memoize]
fn sequence_impl( fn sequence_impl(
&self, &self,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,

View File

@ -320,7 +320,7 @@ impl State {
#[comemo::memoize] #[comemo::memoize]
fn sequence_impl( fn sequence_impl(
&self, &self,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,

View File

@ -448,8 +448,8 @@ impl Debug for ShapedText<'_> {
} }
/// Holds shaping results and metadata common to all shaped segments. /// Holds shaping results and metadata common to all shaped segments.
struct ShapingContext<'a> { struct ShapingContext<'a, 'v> {
vt: &'a Vt<'a>, vt: &'a Vt<'v>,
spans: &'a SpanMapper, spans: &'a SpanMapper,
glyphs: Vec<ShapedGlyph>, glyphs: Vec<ShapedGlyph>,
used: Vec<Font>, used: Vec<Font>,

View File

@ -165,7 +165,7 @@ pub enum ImageFit {
/// Load an image from a path. /// Load an image from a path.
#[comemo::memoize] #[comemo::memoize]
fn load( fn load(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
full: &str, full: &str,
fallback_family: Option<&str>, fallback_family: Option<&str>,
alt: Option<EcoString>, alt: Option<EcoString>,

View File

@ -130,13 +130,13 @@ impl Display for Tracepoint {
/// Enrich a [`SourceResult`] with a tracepoint. /// Enrich a [`SourceResult`] with a tracepoint.
pub trait Trace<T> { pub trait Trace<T> {
/// Add the tracepoint to all errors that lie outside the `span`. /// Add the tracepoint to all errors that lie outside the `span`.
fn trace<F>(self, world: Tracked<dyn World>, make_point: F, span: Span) -> Self fn trace<F>(self, world: Tracked<dyn World + '_>, make_point: F, span: Span) -> Self
where where
F: Fn() -> Tracepoint; F: Fn() -> Tracepoint;
} }
impl<T> Trace<T> for SourceResult<T> { impl<T> Trace<T> for SourceResult<T> {
fn trace<F>(self, world: Tracked<dyn World>, make_point: F, span: Span) -> Self fn trace<F>(self, world: Tracked<dyn World + '_>, make_point: F, span: Span) -> Self
where where
F: Fn() -> Tracepoint, F: Fn() -> Tracepoint,
{ {

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use comemo::{Prehashed, Track, Tracked, TrackedMut}; use comemo::{Prehashed, Tracked, TrackedMut};
use ecow::eco_format; use ecow::eco_format;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -317,7 +317,7 @@ impl Closure {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn call( fn call(
this: &Func, this: &Func,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
route: Tracked<Route>, route: Tracked<Route>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>, provider: TrackedMut<StabilityProvider>,

View File

@ -67,7 +67,7 @@ pub struct LangItems {
/// The keys contained in the bibliography and short descriptions of them. /// The keys contained in the bibliography and short descriptions of them.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub bibliography_keys: fn( pub bibliography_keys: fn(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
) -> Vec<(EcoString, Option<EcoString>)>, ) -> Vec<(EcoString, Option<EcoString>)>,
/// A section heading: `= Introduction`. /// A section heading: `= Introduction`.

View File

@ -41,7 +41,7 @@ use std::collections::HashSet;
use std::mem; use std::mem;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut, Validate};
use ecow::{EcoString, EcoVec}; use ecow::{EcoString, EcoVec};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -67,7 +67,7 @@ const MAX_CALL_DEPTH: usize = 64;
#[comemo::memoize] #[comemo::memoize]
#[tracing::instrument(skip(world, route, tracer, source))] #[tracing::instrument(skip(world, route, tracer, source))]
pub fn eval( pub fn eval(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
route: Tracked<Route>, route: Tracked<Route>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
source: &Source, source: &Source,
@ -84,7 +84,7 @@ pub fn eval(
set_lang_items(library.items.clone()); set_lang_items(library.items.clone());
// Evaluate the module. // Evaluate the module.
let route = unsafe { Route::insert(route, id) }; let route = Route::insert(route, id);
let scopes = Scopes::new(Some(library)); let scopes = Scopes::new(Some(library));
let mut provider = StabilityProvider::new(); let mut provider = StabilityProvider::new();
let introspector = Introspector::new(&[]); let introspector = Introspector::new(&[]);
@ -117,7 +117,7 @@ pub fn eval(
/// Everything in the output is associated with the given `span`. /// Everything in the output is associated with the given `span`.
#[comemo::memoize] #[comemo::memoize]
pub fn eval_string( pub fn eval_string(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
code: &str, code: &str,
span: Span, span: Span,
) -> SourceResult<Value> { ) -> SourceResult<Value> {
@ -164,7 +164,7 @@ pub struct Vm<'a> {
/// The language items. /// The language items.
items: LangItems, items: LangItems,
/// The route of source ids the VM took to reach its current location. /// The route of source ids the VM took to reach its current location.
route: Tracked<'a, Route>, route: Tracked<'a, Route<'a>>,
/// The current location. /// The current location.
location: SourceId, location: SourceId,
/// A control flow event that is currently happening. /// A control flow event that is currently happening.
@ -200,7 +200,7 @@ impl<'a> Vm<'a> {
} }
/// Access the underlying world. /// Access the underlying world.
pub fn world(&self) -> Tracked<'a, dyn World> { pub fn world(&self) -> Tracked<'a, dyn World + 'a> {
self.vt.world self.vt.world
} }
@ -263,34 +263,45 @@ impl Flow {
/// A route of source ids. /// A route of source ids.
#[derive(Default)] #[derive(Default)]
pub struct Route { pub struct Route<'a> {
parent: Option<Tracked<'static, Self>>, // We need to override the constraint's lifetime here so that `Tracked` is
// covariant over the constraint. If it becomes invariant, we're in for a
// world of lifetime pain.
outer: Option<Tracked<'a, Self, <Route<'static> as Validate>::Constraint>>,
id: Option<SourceId>, id: Option<SourceId>,
} }
impl Route { impl<'a> Route<'a> {
/// Create a new route with just one entry. /// Create a new route with just one entry.
pub fn new(id: SourceId) -> Self { pub fn new(id: SourceId) -> Self {
Self { id: Some(id), parent: None } Self { id: Some(id), outer: None }
} }
/// Insert a new id into the route. /// Insert a new id into the route.
/// ///
/// You must guarantee that `outer` lives longer than the resulting /// You must guarantee that `outer` lives longer than the resulting
/// route is ever used. /// route is ever used.
unsafe fn insert(outer: Tracked<Route>, id: SourceId) -> Route { pub fn insert(outer: Tracked<'a, Self>, id: SourceId) -> Self {
Route { Route { outer: Some(outer), id: Some(id) }
parent: Some(std::mem::transmute(outer)), }
id: Some(id),
/// Start tracking this locator.
///
/// In comparison to [`Track::track`], this method skips this chain link
/// if it does not contribute anything.
pub fn track(&self) -> Tracked<'_, Self> {
match self.outer {
Some(outer) if self.id.is_none() => outer,
_ => Track::track(self),
} }
} }
} }
#[comemo::track] #[comemo::track]
impl Route { impl<'a> Route<'a> {
/// Whether the given id is part of the route. /// Whether the given id is part of the route.
fn contains(&self, id: SourceId) -> bool { fn contains(&self, id: SourceId) -> bool {
self.id == Some(id) || self.parent.map_or(false, |parent| parent.contains(id)) self.id == Some(id) || self.outer.map_or(false, |outer| outer.contains(id))
} }
} }

View File

@ -53,7 +53,7 @@ impl Image {
pub fn with_fonts( pub fn with_fonts(
data: Buffer, data: Buffer,
format: ImageFormat, format: ImageFormat,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
fallback_family: Option<&str>, fallback_family: Option<&str>,
alt: Option<EcoString>, alt: Option<EcoString>,
) -> StrResult<Self> { ) -> StrResult<Self> {
@ -240,7 +240,7 @@ fn decode_svg(data: &Buffer) -> StrResult<Arc<DecodedImage>> {
#[comemo::memoize] #[comemo::memoize]
fn decode_svg_with_fonts( fn decode_svg_with_fonts(
data: &Buffer, data: &Buffer,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
fallback_family: Option<&str>, fallback_family: Option<&str>,
) -> StrResult<Arc<DecodedImage>> { ) -> StrResult<Arc<DecodedImage>> {
let mut opts = usvg::Options::default(); let mut opts = usvg::Options::default();
@ -269,7 +269,7 @@ fn decode_svg_with_fonts(
/// Discover and load the fonts referenced by an SVG. /// Discover and load the fonts referenced by an SVG.
fn load_svg_fonts( fn load_svg_fonts(
tree: &usvg::Tree, tree: &usvg::Tree,
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
fallback_family: Option<&str>, fallback_family: Option<&str>,
) -> fontdb::Database { ) -> fontdb::Database {
let mut referenced = BTreeMap::<EcoString, bool>::new(); let mut referenced = BTreeMap::<EcoString, bool>::new();

View File

@ -65,7 +65,7 @@ use crate::util::Buffer;
/// Compile a source file into a fully layouted document. /// Compile a source file into a fully layouted document.
#[tracing::instrument(skip(world))] #[tracing::instrument(skip(world))]
pub fn compile(world: &(dyn World + 'static)) -> SourceResult<Document> { pub fn compile(world: &dyn World) -> SourceResult<Document> {
// Evaluate the source file into a module. // Evaluate the source file into a module.
let route = Route::default(); let route = Route::default();
let mut tracer = Tracer::default(); let mut tracer = Tracer::default();

View File

@ -14,7 +14,7 @@ pub use self::styles::*;
pub use typst_macros::element; pub use typst_macros::element;
use comemo::{Constraint, Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut, Validate};
use crate::diag::SourceResult; use crate::diag::SourceResult;
use crate::doc::Document; use crate::doc::Document;
@ -25,7 +25,7 @@ use crate::World;
#[comemo::memoize] #[comemo::memoize]
#[tracing::instrument(skip(world, tracer, content))] #[tracing::instrument(skip(world, tracer, content))]
pub fn typeset( pub fn typeset(
world: Tracked<dyn World>, world: Tracked<dyn World + '_>,
mut tracer: TrackedMut<Tracer>, mut tracer: TrackedMut<Tracer>,
content: &Content, content: &Content,
) -> SourceResult<Document> { ) -> SourceResult<Document> {
@ -42,8 +42,8 @@ pub fn typeset(
loop { loop {
tracing::info!("Layout iteration {iter}"); tracing::info!("Layout iteration {iter}");
let constraint = Constraint::new();
let mut provider = StabilityProvider::new(); let mut provider = StabilityProvider::new();
let constraint = <Introspector as Validate>::Constraint::new();
let mut vt = Vt { let mut vt = Vt {
world, world,
tracer: TrackedMut::reborrow_mut(&mut tracer), tracer: TrackedMut::reborrow_mut(&mut tracer),
@ -56,7 +56,7 @@ pub fn typeset(
introspector = Introspector::new(&document.pages); introspector = Introspector::new(&document.pages);
if iter >= 5 || introspector.valid(&constraint) { if iter >= 5 || introspector.validate(&constraint) {
break; break;
} }
} }
@ -69,7 +69,7 @@ pub fn typeset(
/// Holds the state needed to [typeset] content. /// Holds the state needed to [typeset] content.
pub struct Vt<'a> { pub struct Vt<'a> {
/// The compilation environment. /// The compilation environment.
pub world: Tracked<'a, dyn World>, pub world: Tracked<'a, dyn World + 'a>,
/// The tracer for inspection of the values an expression produces. /// The tracer for inspection of the values an expression produces.
pub tracer: TrackedMut<'a, Tracer>, pub tracer: TrackedMut<'a, Tracer>,
/// Provides stable identities to elements. /// Provides stable identities to elements.

View File

@ -9,7 +9,7 @@ publish = false
[dev-dependencies] [dev-dependencies]
typst = { path = ".." } typst = { path = ".." }
typst-library = { path = "../library" } typst-library = { path = "../library" }
comemo = "0.2.2" comemo = { git = "https://github.com/typst/comemo" }
elsa = "1.8" elsa = "1.8"
iai = { git = "https://github.com/reknih/iai" } iai = { git = "https://github.com/reknih/iai" }
once_cell = "1" once_cell = "1"