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 9eb2cafcc3
commit 47df51c6f1
2 changed files with 71 additions and 54 deletions

View File

@ -253,12 +253,8 @@ pub struct Binding {
span: Span,
/// The category of the binding.
category: Option<Category>,
/// A deprecation message for the definition.
deprecation_message: Option<&'static str>,
/// 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 deprecation information if this item is deprecated.
deprecation: Option<Box<DeprecationInfo>>,
}
/// The different kinds of slots.
@ -278,8 +274,7 @@ impl Binding {
span,
kind: BindingKind::Normal,
category: None,
deprecation_message: None,
deprecation_until: None,
deprecation: None,
}
}
@ -290,7 +285,9 @@ impl Binding {
/// Marks this binding as deprecated, with the given `message`.
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
}
@ -298,7 +295,9 @@ impl Binding {
///
/// This is ignored if [`Binding::deprecated`] isn't also set.
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
}
@ -313,8 +312,8 @@ impl Binding {
/// - pass `()` to ignore the message.
/// - pass `(&mut engine, span)` to emit a warning into the engine.
pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value {
if let Some(message) = self.deprecation_message {
sink.emit(message, self.deprecation_until);
if let Some(info) = &self.deprecation {
sink.emit(info.message, info.until);
}
&self.value
}
@ -350,13 +349,8 @@ impl Binding {
}
/// A deprecation message for the value, if any.
pub fn deprecation_message(&self) -> Option<&'static str> {
self.deprecation_message
}
/// The version in which a deprecated binding is planned to be removed.
pub fn deprecation_until(&self) -> Option<&'static str> {
self.deprecation_until
pub fn deprecation(&self) -> Option<&DeprecationInfo> {
self.deprecation.as_deref()
}
/// The category of the value, if any.
@ -374,6 +368,51 @@ pub enum Capturer {
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
/// library.
#[cold]

View File

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