Bundle deprecation message and version in DeprecationInfo

Because the increase in size of `Binding` seemed to create a performance
regression we'll try to reduce the size by putting the rarely used
deprecation on the heap. In fact, the struct is even smaller than before
now, because `deprecation` was previously a wide pointer.
This commit is contained in:
tinger 2025-07-21 18:44:41 +02:00
parent c854ef8bdc
commit 6eced92e7c
2 changed files with 71 additions and 54 deletions

View File

@ -253,12 +253,8 @@ pub struct Binding {
span: Span, span: Span,
/// The category of the binding. /// The category of the binding.
category: Option<Category>, category: Option<Category>,
/// A deprecation message for the definition. /// The deprecation information if this item is deprecated.
deprecation_message: Option<&'static str>, deprecation: Option<Box<DeprecationInfo>>,
/// A version in which the deprecated binding is planned to be removed.
///
/// This is ignored if `deprecation` is `None`.
deprecation_until: Option<&'static str>,
} }
/// The different kinds of slots. /// The different kinds of slots.
@ -278,8 +274,7 @@ impl Binding {
span, span,
kind: BindingKind::Normal, kind: BindingKind::Normal,
category: None, category: None,
deprecation_message: None, deprecation: None,
deprecation_until: None,
} }
} }
@ -290,7 +285,9 @@ impl Binding {
/// Marks this binding as deprecated, with the given `message`. /// Marks this binding as deprecated, with the given `message`.
pub fn deprecated(&mut self, message: &'static str) -> &mut Self { pub fn deprecated(&mut self, message: &'static str) -> &mut Self {
self.deprecation_message = Some(message); self.deprecation
.get_or_insert_with(|| Box::new(DeprecationInfo::new()))
.deprecated_message(message);
self self
} }
@ -298,7 +295,9 @@ impl Binding {
/// ///
/// This is ignored if [`Binding::deprecated`] isn't also set. /// This is ignored if [`Binding::deprecated`] isn't also set.
pub fn deprecated_until(&mut self, version: &'static str) -> &mut Self { pub fn deprecated_until(&mut self, version: &'static str) -> &mut Self {
self.deprecation_until = Some(version); self.deprecation
.get_or_insert_with(|| Box::new(DeprecationInfo::new()))
.deprecated_until(version);
self self
} }
@ -313,8 +312,8 @@ impl Binding {
/// - pass `()` to ignore the message. /// - pass `()` to ignore the message.
/// - pass `(&mut engine, span)` to emit a warning into the engine. /// - pass `(&mut engine, span)` to emit a warning into the engine.
pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value { pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value {
if let Some(message) = self.deprecation_message { if let Some(info) = &self.deprecation {
sink.emit(message, self.deprecation_until); sink.emit(info.message, info.until);
} }
&self.value &self.value
} }
@ -350,13 +349,8 @@ impl Binding {
} }
/// A deprecation message for the value, if any. /// A deprecation message for the value, if any.
pub fn deprecation_message(&self) -> Option<&'static str> { pub fn deprecation(&self) -> Option<&DeprecationInfo> {
self.deprecation_message self.deprecation.as_deref()
}
/// The version in which a deprecated binding is planned to be removed.
pub fn deprecation_until(&self) -> Option<&'static str> {
self.deprecation_until
} }
/// The category of the value, if any. /// The category of the value, if any.
@ -374,6 +368,51 @@ pub enum Capturer {
Context, Context,
} }
/// Information about a deprecated binding.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DeprecationInfo {
/// A deprecation message for the definition.
message: &'static str,
/// A version in which the deprecated binding is planned to be removed.
until: Option<&'static str>,
}
impl DeprecationInfo {
/// Creates new deprecation info with a default message to display when
/// emitting the deprecation warning.
pub fn new() -> Self {
Self { message: "item is deprecated", until: None }
}
/// Set the message to display when emitting the deprecation warning.
pub fn deprecated_message(&mut self, message: &'static str) -> &mut Self {
self.message = message;
self
}
/// Set the version in which the binding is planned to be removed.
pub fn deprecated_until(&mut self, version: &'static str) -> &mut Self {
self.until = Some(version);
self
}
/// The message to display when emitting the deprecation warning.
pub fn message(&self) -> &'static str {
self.message
}
/// The version in which the binding is planned to be removed.
pub fn until(&self) -> Option<&'static str> {
self.until
}
}
impl Default for DeprecationInfo {
fn default() -> Self {
Self::new()
}
}
/// The error message when trying to mutate a variable from the standard /// The error message when trying to mutate a variable from the standard
/// library. /// library.
#[cold] #[cold]

View File

@ -17,6 +17,7 @@ use serde::Deserialize;
use serde_yaml as yaml; use serde_yaml as yaml;
use std::sync::LazyLock; use std::sync::LazyLock;
use typst::diag::{StrResult, bail}; use typst::diag::{StrResult, bail};
use typst::foundations::DeprecationInfo;
use typst::foundations::{ use typst::foundations::{
AutoValue, Binding, Bytes, CastInfo, Func, Module, NoneValue, ParamInfo, Repr, Scope, AutoValue, Binding, Bytes, CastInfo, Func, Module, NoneValue, ParamInfo, Repr, Scope,
Smart, Type, Value, Smart, Type, Value,
@ -291,14 +292,8 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
match binding.read() { match binding.read() {
Value::Func(func) => { Value::Func(func) => {
let name = func.name().unwrap(); let name = func.name().unwrap();
let subpage = func_page( let subpage =
resolver, func_page(resolver, &route, func, path, binding.deprecation());
&route,
func,
path,
binding.deprecation_message(),
binding.deprecation_until(),
);
items.push(CategoryItem { items.push(CategoryItem {
name: name.into(), name: name.into(),
route: subpage.route.clone(), route: subpage.route.clone(),
@ -387,11 +382,9 @@ fn func_page(
parent: &str, parent: &str,
func: &Func, func: &Func,
path: &[&str], path: &[&str],
deprecation_message: Option<&'static str>, deprecation: Option<&DeprecationInfo>,
deprecation_until: Option<&'static str>,
) -> PageModel { ) -> PageModel {
let model = let model = func_model(resolver, func, path, false, deprecation);
func_model(resolver, func, path, false, deprecation_message, deprecation_until);
let name = func.name().unwrap(); let name = func.name().unwrap();
PageModel { PageModel {
route: eco_format!("{parent}{}/", urlify(name)), route: eco_format!("{parent}{}/", urlify(name)),
@ -410,8 +403,7 @@ fn func_model(
func: &Func, func: &Func,
path: &[&str], path: &[&str],
nested: bool, nested: bool,
deprecation_message: Option<&'static str>, deprecation: Option<&DeprecationInfo>,
deprecation_until: Option<&'static str>,
) -> FuncModel { ) -> FuncModel {
let name = func.name().unwrap(); let name = func.name().unwrap();
let scope = func.scope().unwrap(); let scope = func.scope().unwrap();
@ -447,8 +439,8 @@ fn func_model(
oneliner: oneliner(details), oneliner: oneliner(details),
element: func.element().is_some(), element: func.element().is_some(),
contextual: func.contextual().unwrap_or(false), contextual: func.contextual().unwrap_or(false),
deprecation_message, deprecation_message: deprecation.map(DeprecationInfo::message),
deprecation_until, deprecation_until: deprecation.and_then(DeprecationInfo::until),
details: Html::markdown(resolver, details, nesting), details: Html::markdown(resolver, details, nesting),
example: example.map(|md| Html::markdown(resolver, md, None)), example: example.map(|md| Html::markdown(resolver, md, None)),
self_, self_,
@ -531,14 +523,7 @@ fn scope_models(resolver: &dyn Resolver, name: &str, scope: &Scope) -> Vec<FuncM
.iter() .iter()
.filter_map(|(_, binding)| { .filter_map(|(_, binding)| {
let Value::Func(func) = binding.read() else { return None }; let Value::Func(func) = binding.read() else { return None };
Some(func_model( Some(func_model(resolver, func, &[name], true, binding.deprecation()))
resolver,
func,
&[name],
true,
binding.deprecation_message(),
binding.deprecation_until(),
))
}) })
.collect() .collect()
} }
@ -619,14 +604,7 @@ fn group_page(
let Ok(ref func) = binding.read().clone().cast::<Func>() else { let Ok(ref func) = binding.read().clone().cast::<Func>() else {
panic!("not a function") panic!("not a function")
}; };
let func = func_model( let func = func_model(resolver, func, &path, true, binding.deprecation());
resolver,
func,
&path,
true,
binding.deprecation_message(),
binding.deprecation_until(),
);
let id_base = urlify(&eco_format!("functions-{}", func.name)); let id_base = urlify(&eco_format!("functions-{}", func.name));
let children = func_outline(&func, &id_base); let children = func_outline(&func, &id_base);
outline_items.push(OutlineItem { outline_items.push(OutlineItem {
@ -693,7 +671,7 @@ fn type_model(resolver: &dyn Resolver, ty: &Type) -> TypeModel {
constructor: ty constructor: ty
.constructor() .constructor()
.ok() .ok()
.map(|func| func_model(resolver, &func, &[], true, None, None)), .map(|func| func_model(resolver, &func, &[], true, None)),
scope: scope_models(resolver, ty.short_name(), ty.scope()), scope: scope_models(resolver, ty.short_name(), ty.scope()),
} }
} }
@ -762,8 +740,8 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
.map(|(other, _, _)| complete(other)) .map(|(other, _, _)| complete(other))
.collect(), .collect(),
deprecation_message: deprecation_message deprecation_message: deprecation_message
.or_else(|| binding.deprecation_message()), .or_else(|| binding.deprecation().map(DeprecationInfo::message)),
deprecation_until: binding.deprecation_until(), deprecation_until: binding.deprecation().and_then(DeprecationInfo::until),
}); });
} }
} }