typst_eval/
call.rs

1use comemo::{Tracked, TrackedMut};
2use ecow::{eco_format, EcoString, EcoVec};
3use typst_library::diag::{
4    bail, error, At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult,
5    Trace, Tracepoint,
6};
7use typst_library::engine::{Engine, Sink, Traced};
8use typst_library::foundations::{
9    Arg, Args, Binding, Capturer, Closure, Content, Context, Func, NativeElement, Scope,
10    Scopes, SymbolElem, Value,
11};
12use typst_library::introspection::Introspector;
13use typst_library::math::LrElem;
14use typst_library::routines::Routines;
15use typst_library::World;
16use typst_syntax::ast::{self, AstNode, Ident};
17use typst_syntax::{Span, Spanned, SyntaxNode};
18use typst_utils::LazyHash;
19
20use crate::{call_method_mut, is_mutating_method, Access, Eval, FlowEvent, Route, Vm};
21
22impl Eval for ast::FuncCall<'_> {
23    type Output = Value;
24
25    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
26        let span = self.span();
27        let callee = self.callee();
28        let in_math = in_math(callee);
29        let callee_span = callee.span();
30        let args = self.args();
31        let trailing_comma = args.trailing_comma();
32
33        vm.engine.route.check_call_depth().at(span)?;
34
35        // Try to evaluate as a call to an associated function or field.
36        let (callee, args) = if let ast::Expr::FieldAccess(access) = callee {
37            let target = access.target();
38            let field = access.field();
39            match eval_field_call(target, field, args, span, vm)? {
40                FieldCall::Normal(callee, args) => (callee, args),
41                FieldCall::Resolved(value) => return Ok(value),
42            }
43        } else {
44            // Function call order: we evaluate the callee before the arguments.
45            (callee.eval(vm)?, args.eval(vm)?.spanned(span))
46        };
47
48        let func_result = callee.clone().cast::<Func>();
49        if in_math && func_result.is_err() {
50            return wrap_args_in_math(callee, callee_span, args, trailing_comma);
51        }
52
53        let func = func_result
54            .map_err(|err| hint_if_shadowed_std(vm, &self.callee(), err))
55            .at(callee_span)?;
56
57        let point = || Tracepoint::Call(func.name().map(Into::into));
58        let f = || {
59            func.call(&mut vm.engine, vm.context, args)
60                .trace(vm.world(), point, span)
61        };
62
63        // Stacker is broken on WASM.
64        #[cfg(target_arch = "wasm32")]
65        return f();
66
67        #[cfg(not(target_arch = "wasm32"))]
68        stacker::maybe_grow(32 * 1024, 2 * 1024 * 1024, f)
69    }
70}
71
72impl Eval for ast::Args<'_> {
73    type Output = Args;
74
75    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
76        let mut items = EcoVec::with_capacity(self.items().count());
77
78        for arg in self.items() {
79            let span = arg.span();
80            match arg {
81                ast::Arg::Pos(expr) => {
82                    items.push(Arg {
83                        span,
84                        name: None,
85                        value: Spanned::new(expr.eval(vm)?, expr.span()),
86                    });
87                }
88                ast::Arg::Named(named) => {
89                    let expr = named.expr();
90                    items.push(Arg {
91                        span,
92                        name: Some(named.name().get().clone().into()),
93                        value: Spanned::new(expr.eval(vm)?, expr.span()),
94                    });
95                }
96                ast::Arg::Spread(spread) => match spread.expr().eval(vm)? {
97                    Value::None => {}
98                    Value::Array(array) => {
99                        items.extend(array.into_iter().map(|value| Arg {
100                            span,
101                            name: None,
102                            value: Spanned::new(value, span),
103                        }));
104                    }
105                    Value::Dict(dict) => {
106                        items.extend(dict.into_iter().map(|(key, value)| Arg {
107                            span,
108                            name: Some(key),
109                            value: Spanned::new(value, span),
110                        }));
111                    }
112                    Value::Args(args) => items.extend(args.items),
113                    v => bail!(spread.span(), "cannot spread {}", v.ty()),
114                },
115            }
116        }
117
118        // We do *not* use the `self.span()` here because we want the callsite
119        // span to be one level higher (the whole function call).
120        Ok(Args { span: Span::detached(), items })
121    }
122}
123
124impl Eval for ast::Closure<'_> {
125    type Output = Value;
126
127    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
128        // Evaluate default values of named parameters.
129        let mut defaults = Vec::new();
130        for param in self.params().children() {
131            if let ast::Param::Named(named) = param {
132                defaults.push(named.expr().eval(vm)?);
133            }
134        }
135
136        // Collect captured variables.
137        let captured = {
138            let mut visitor = CapturesVisitor::new(Some(&vm.scopes), Capturer::Function);
139            visitor.visit(self.to_untyped());
140            visitor.finish()
141        };
142
143        // Define the closure.
144        let closure = Closure {
145            node: self.to_untyped().clone(),
146            defaults,
147            captured,
148            num_pos_params: self
149                .params()
150                .children()
151                .filter(|p| matches!(p, ast::Param::Pos(_)))
152                .count(),
153        };
154
155        Ok(Value::Func(Func::from(closure).spanned(self.params().span())))
156    }
157}
158
159/// Call the function in the context with the arguments.
160#[comemo::memoize]
161#[allow(clippy::too_many_arguments)]
162pub fn eval_closure(
163    func: &Func,
164    closure: &LazyHash<Closure>,
165    routines: &Routines,
166    world: Tracked<dyn World + '_>,
167    introspector: Tracked<Introspector>,
168    traced: Tracked<Traced>,
169    sink: TrackedMut<Sink>,
170    route: Tracked<Route>,
171    context: Tracked<Context>,
172    mut args: Args,
173) -> SourceResult<Value> {
174    let (name, params, body) = match closure.node.cast::<ast::Closure>() {
175        Some(node) => (node.name(), node.params(), node.body()),
176        None => (None, ast::Params::default(), closure.node.cast().unwrap()),
177    };
178
179    // Don't leak the scopes from the call site. Instead, we use the scope
180    // of captured variables we collected earlier.
181    let mut scopes = Scopes::new(None);
182    scopes.top = closure.captured.clone();
183
184    // Prepare the engine.
185    let engine = Engine {
186        routines,
187        world,
188        introspector,
189        traced,
190        sink,
191        route: Route::extend(route),
192    };
193
194    // Prepare VM.
195    let mut vm = Vm::new(engine, context, scopes, body.span());
196
197    // Provide the closure itself for recursive calls.
198    if let Some(name) = name {
199        vm.define(name, func.clone());
200    }
201
202    let num_pos_args = args.to_pos().len();
203    let sink_size = num_pos_args.checked_sub(closure.num_pos_params);
204
205    let mut sink = None;
206    let mut sink_pos_values = None;
207    let mut defaults = closure.defaults.iter();
208    for p in params.children() {
209        match p {
210            ast::Param::Pos(pattern) => match pattern {
211                ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
212                    vm.define(ident, args.expect::<Value>(&ident)?)
213                }
214                pattern => {
215                    crate::destructure(
216                        &mut vm,
217                        pattern,
218                        args.expect::<Value>("pattern parameter")?,
219                    )?;
220                }
221            },
222            ast::Param::Spread(spread) => {
223                sink = Some(spread.sink_ident());
224                if let Some(sink_size) = sink_size {
225                    sink_pos_values = Some(args.consume(sink_size)?);
226                }
227            }
228            ast::Param::Named(named) => {
229                let name = named.name();
230                let default = defaults.next().unwrap();
231                let value =
232                    args.named::<Value>(&name)?.unwrap_or_else(|| default.clone());
233                vm.define(name, value);
234            }
235        }
236    }
237
238    if let Some(sink) = sink {
239        // Remaining args are captured regardless of whether the sink is named.
240        let mut remaining_args = args.take();
241        if let Some(sink_name) = sink {
242            if let Some(sink_pos_values) = sink_pos_values {
243                remaining_args.items.extend(sink_pos_values);
244            }
245            vm.define(sink_name, remaining_args);
246        }
247    }
248
249    // Ensure all arguments have been used.
250    args.finish()?;
251
252    // Handle control flow.
253    let output = body.eval(&mut vm)?;
254    match vm.flow {
255        Some(FlowEvent::Return(_, Some(explicit), _)) => return Ok(explicit),
256        Some(FlowEvent::Return(_, None, _)) => {}
257        Some(flow) => bail!(flow.forbidden()),
258        None => {}
259    }
260
261    Ok(output)
262}
263
264/// This used only as the return value of `eval_field_call`.
265/// - `Normal` means that we have a function to call and the arguments to call it with.
266/// - `Resolved` means that we have already resolved the call and have the value.
267enum FieldCall {
268    Normal(Value, Args),
269    Resolved(Value),
270}
271
272/// Evaluate a field call's callee and arguments.
273///
274/// This follows the normal function call order: we evaluate the callee before the
275/// arguments.
276///
277/// Prioritize associated functions on the value's type (e.g., methods) over its fields.
278/// A function call on a field is only allowed for functions, types, modules (because
279/// they are scopes), and symbols (because they have modifiers or associated functions).
280///
281/// For dictionaries, it is not allowed because it would be ambiguous - prioritizing
282/// associated functions would make an addition of a new associated function a breaking
283/// change and prioritizing fields would break associated functions for certain
284/// dictionaries.
285fn eval_field_call(
286    target_expr: ast::Expr,
287    field: Ident,
288    args: ast::Args,
289    span: Span,
290    vm: &mut Vm,
291) -> SourceResult<FieldCall> {
292    // Evaluate the field-call's target and overall arguments.
293    let (target, mut args) = if is_mutating_method(&field) {
294        // If `field` looks like a mutating method, we evaluate the arguments first,
295        // because `target_expr.access(vm)` mutably borrows the `vm`, so that we can't
296        // evaluate the arguments after it.
297        let args = args.eval(vm)?.spanned(span);
298        // However, this difference from the normal call order is not observable because
299        // expressions like `(1, arr.len(), 2, 3).push(arr.pop())` evaluate the target to
300        // a temporary which we disallow mutation on (returning an error).
301        // Theoretically this could be observed if a method matching `is_mutating_method`
302        // was added to some type in the future and we didn't update this function.
303        match target_expr.access(vm)? {
304            // Only arrays and dictionaries have mutable methods.
305            target @ (Value::Array(_) | Value::Dict(_)) => {
306                let value = call_method_mut(target, &field, args, span);
307                let point = || Tracepoint::Call(Some(field.get().clone()));
308                return Ok(FieldCall::Resolved(value.trace(vm.world(), point, span)?));
309            }
310            target => (target.clone(), args),
311        }
312    } else {
313        let target = target_expr.eval(vm)?;
314        let args = args.eval(vm)?.spanned(span);
315        (target, args)
316    };
317
318    let field_span = field.span();
319    let sink = (&mut vm.engine, field_span);
320    if let Some(callee) = target.ty().scope().get(&field) {
321        args.insert(0, target_expr.span(), target);
322        Ok(FieldCall::Normal(callee.read_checked(sink).clone(), args))
323    } else if let Value::Content(content) = &target {
324        if let Some(callee) = content.elem().scope().get(&field) {
325            args.insert(0, target_expr.span(), target);
326            Ok(FieldCall::Normal(callee.read_checked(sink).clone(), args))
327        } else {
328            bail!(missing_field_call_error(target, field))
329        }
330    } else if matches!(
331        target,
332        Value::Symbol(_) | Value::Func(_) | Value::Type(_) | Value::Module(_)
333    ) {
334        // Certain value types may have their own ways to access method fields.
335        // e.g. `$arrow.r(v)$`, `table.cell[..]`
336        let value = target.field(&field, sink).at(field_span)?;
337        Ok(FieldCall::Normal(value, args))
338    } else {
339        // Otherwise we cannot call this field.
340        bail!(missing_field_call_error(target, field))
341    }
342}
343
344/// Produce an error when we cannot call the field.
345fn missing_field_call_error(target: Value, field: Ident) -> SourceDiagnostic {
346    let mut error = match &target {
347        Value::Content(content) => error!(
348            field.span(),
349            "element {} has no method `{}`",
350            content.elem().name(),
351            field.as_str(),
352        ),
353        _ => error!(
354            field.span(),
355            "type {} has no method `{}`",
356            target.ty(),
357            field.as_str()
358        ),
359    };
360
361    match target {
362        Value::Dict(ref dict) if matches!(dict.get(&field), Ok(Value::Func(_))) => {
363            error.hint(eco_format!(
364                "to call the function stored in the dictionary, surround \
365                the field access with parentheses, e.g. `(dict.{})(..)`",
366                field.as_str(),
367            ));
368        }
369        _ if target.field(&field, ()).is_ok() => {
370            error.hint(eco_format!(
371                "did you mean to access the field `{}`?",
372                field.as_str(),
373            ));
374        }
375        _ => {}
376    }
377
378    error
379}
380
381/// Check if the expression is in a math context.
382fn in_math(expr: ast::Expr) -> bool {
383    match expr {
384        ast::Expr::MathIdent(_) => true,
385        ast::Expr::FieldAccess(access) => in_math(access.target()),
386        _ => false,
387    }
388}
389
390/// For non-functions in math, we wrap the arguments in parentheses.
391fn wrap_args_in_math(
392    callee: Value,
393    callee_span: Span,
394    mut args: Args,
395    trailing_comma: bool,
396) -> SourceResult<Value> {
397    let mut body = Content::empty();
398    for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
399        if i > 0 {
400            body += SymbolElem::packed(',');
401        }
402        body += arg;
403    }
404    if trailing_comma {
405        body += SymbolElem::packed(',');
406    }
407    Ok(Value::Content(
408        callee.display().spanned(callee_span)
409            + LrElem::new(SymbolElem::packed('(') + body + SymbolElem::packed(')'))
410                .pack()
411                .spanned(args.span),
412    ))
413}
414
415/// Provide a hint if the callee is a shadowed standard library function.
416fn hint_if_shadowed_std(
417    vm: &mut Vm,
418    callee: &ast::Expr,
419    mut err: HintedString,
420) -> HintedString {
421    if let ast::Expr::Ident(ident) = callee {
422        let ident = ident.get();
423        if vm.scopes.check_std_shadowed(ident) {
424            err.hint(eco_format!(
425                "use `std.{ident}` to access the shadowed standard library function",
426            ));
427        }
428    }
429    err
430}
431
432/// A visitor that determines which variables to capture for a closure.
433pub struct CapturesVisitor<'a> {
434    external: Option<&'a Scopes<'a>>,
435    internal: Scopes<'a>,
436    captures: Scope,
437    capturer: Capturer,
438}
439
440impl<'a> CapturesVisitor<'a> {
441    /// Create a new visitor for the given external scopes.
442    pub fn new(external: Option<&'a Scopes<'a>>, capturer: Capturer) -> Self {
443        Self {
444            external,
445            internal: Scopes::new(None),
446            captures: Scope::new(),
447            capturer,
448        }
449    }
450
451    /// Return the scope of captured variables.
452    pub fn finish(self) -> Scope {
453        self.captures
454    }
455
456    /// Visit any node and collect all captured variables.
457    pub fn visit(&mut self, node: &SyntaxNode) {
458        match node.cast() {
459            // Every identifier is a potential variable that we need to capture.
460            // Identifiers that shouldn't count as captures because they
461            // actually bind a new name are handled below (individually through
462            // the expressions that contain them).
463            Some(ast::Expr::Ident(ident)) => self.capture(ident.get(), Scopes::get),
464            Some(ast::Expr::MathIdent(ident)) => {
465                self.capture(ident.get(), Scopes::get_in_math)
466            }
467
468            // Code and content blocks create a scope.
469            Some(ast::Expr::Code(_) | ast::Expr::Content(_)) => {
470                self.internal.enter();
471                for child in node.children() {
472                    self.visit(child);
473                }
474                self.internal.exit();
475            }
476
477            // Don't capture the field of a field access.
478            Some(ast::Expr::FieldAccess(access)) => {
479                self.visit(access.target().to_untyped());
480            }
481
482            // A closure contains parameter bindings, which are bound before the
483            // body is evaluated. Care must be taken so that the default values
484            // of named parameters cannot access previous parameter bindings.
485            Some(ast::Expr::Closure(expr)) => {
486                for param in expr.params().children() {
487                    if let ast::Param::Named(named) = param {
488                        self.visit(named.expr().to_untyped());
489                    }
490                }
491
492                self.internal.enter();
493                if let Some(name) = expr.name() {
494                    self.bind(name);
495                }
496
497                for param in expr.params().children() {
498                    match param {
499                        ast::Param::Pos(pattern) => {
500                            for ident in pattern.bindings() {
501                                self.bind(ident);
502                            }
503                        }
504                        ast::Param::Named(named) => self.bind(named.name()),
505                        ast::Param::Spread(spread) => {
506                            if let Some(ident) = spread.sink_ident() {
507                                self.bind(ident);
508                            }
509                        }
510                    }
511                }
512
513                self.visit(expr.body().to_untyped());
514                self.internal.exit();
515            }
516
517            // A let expression contains a binding, but that binding is only
518            // active after the body is evaluated.
519            Some(ast::Expr::Let(expr)) => {
520                if let Some(init) = expr.init() {
521                    self.visit(init.to_untyped());
522                }
523
524                for ident in expr.kind().bindings() {
525                    self.bind(ident);
526                }
527            }
528
529            // A for loop contains one or two bindings in its pattern. These are
530            // active after the iterable is evaluated but before the body is
531            // evaluated.
532            Some(ast::Expr::For(expr)) => {
533                self.visit(expr.iterable().to_untyped());
534                self.internal.enter();
535
536                let pattern = expr.pattern();
537                for ident in pattern.bindings() {
538                    self.bind(ident);
539                }
540
541                self.visit(expr.body().to_untyped());
542                self.internal.exit();
543            }
544
545            // An import contains items, but these are active only after the
546            // path is evaluated.
547            Some(ast::Expr::Import(expr)) => {
548                self.visit(expr.source().to_untyped());
549                if let Some(ast::Imports::Items(items)) = expr.imports() {
550                    for item in items.iter() {
551                        self.bind(item.bound_name());
552                    }
553                }
554            }
555
556            _ => {
557                // Never capture the name part of a named pair.
558                if let Some(named) = node.cast::<ast::Named>() {
559                    self.visit(named.expr().to_untyped());
560                    return;
561                }
562
563                // Everything else is traversed from left to right.
564                for child in node.children() {
565                    self.visit(child);
566                }
567            }
568        }
569    }
570
571    /// Bind a new internal variable.
572    fn bind(&mut self, ident: ast::Ident) {
573        // The concrete value does not matter as we only use the scoping
574        // mechanism of `Scopes`, not the values themselves.
575        self.internal
576            .top
577            .bind(ident.get().clone(), Binding::detached(Value::None));
578    }
579
580    /// Capture a variable if it isn't internal.
581    fn capture(
582        &mut self,
583        ident: &EcoString,
584        getter: impl FnOnce(&'a Scopes<'a>, &str) -> HintedStrResult<&'a Binding>,
585    ) {
586        if self.internal.get(ident).is_ok() {
587            return;
588        }
589
590        let binding = match self.external {
591            Some(external) => match getter(external, ident) {
592                Ok(binding) => binding.capture(self.capturer),
593                Err(_) => return,
594            },
595            // The external scopes are only `None` when we are doing IDE capture
596            // analysis, in which case the concrete value doesn't matter.
597            None => Binding::detached(Value::None),
598        };
599
600        self.captures.bind(ident.clone(), binding);
601    }
602}
603
604#[cfg(test)]
605mod tests {
606    use typst_syntax::parse;
607
608    use super::*;
609
610    #[track_caller]
611    fn test(scopes: &Scopes, text: &str, result: &[&str]) {
612        let mut visitor = CapturesVisitor::new(Some(scopes), Capturer::Function);
613        let root = parse(text);
614        visitor.visit(&root);
615
616        let captures = visitor.finish();
617        let mut names: Vec<_> = captures.iter().map(|(k, ..)| k).collect();
618        names.sort();
619
620        assert_eq!(names, result);
621    }
622
623    #[test]
624    fn test_captures() {
625        let mut scopes = Scopes::new(None);
626        scopes.top.define("f", 0);
627        scopes.top.define("x", 0);
628        scopes.top.define("y", 0);
629        scopes.top.define("z", 0);
630        let s = &scopes;
631
632        // Let binding and function definition.
633        test(s, "#let x = x", &["x"]);
634        test(s, "#let x; #(x + y)", &["y"]);
635        test(s, "#let f(x, y) = x + y", &[]);
636        test(s, "#let f(x, y) = f", &[]);
637        test(s, "#let f = (x, y) => f", &["f"]);
638
639        // Closure with different kinds of params.
640        test(s, "#((x, y) => x + z)", &["z"]);
641        test(s, "#((x: y, z) => x + z)", &["y"]);
642        test(s, "#((..x) => x + y)", &["y"]);
643        test(s, "#((x, y: x + z) => x + y)", &["x", "z"]);
644        test(s, "#{x => x; x}", &["x"]);
645
646        // Show rule.
647        test(s, "#show y: x => x", &["y"]);
648        test(s, "#show y: x => x + z", &["y", "z"]);
649        test(s, "#show x: x => x", &["x"]);
650
651        // For loop.
652        test(s, "#for x in y { x + z }", &["y", "z"]);
653        test(s, "#for (x, y) in y { x + y }", &["y"]);
654        test(s, "#for x in y {} #x", &["x", "y"]);
655
656        // Import.
657        test(s, "#import z: x, y", &["z"]);
658        test(s, "#import x + y: x, y, z", &["x", "y"]);
659
660        // Blocks.
661        test(s, "#{ let x = 1; { let y = 2; y }; x + y }", &["y"]);
662        test(s, "#[#let x = 1]#x", &["x"]);
663
664        // Field access.
665        test(s, "#x.y.f(z)", &["x", "z"]);
666
667        // Parenthesized expressions.
668        test(s, "#f(x: 1)", &["f"]);
669        test(s, "#(x: 1)", &[]);
670        test(s, "#(x = 1)", &["x"]);
671        test(s, "#(x += y)", &["x", "y"]);
672        test(s, "#{ (x, z) = (y, 1) }", &["x", "y", "z"]);
673        test(s, "#(x.at(y) = 5)", &["x", "y"]);
674    }
675
676    #[test]
677    fn test_captures_in_math() {
678        let mut scopes = Scopes::new(None);
679        scopes.top.define("f", 0);
680        scopes.top.define("x", 0);
681        scopes.top.define("y", 0);
682        scopes.top.define("z", 0);
683        // Multi-letter variables are required for math.
684        scopes.top.define("foo", 0);
685        scopes.top.define("bar", 0);
686        scopes.top.define("x-bar", 0);
687        scopes.top.define("x_bar", 0);
688        let s = &scopes;
689
690        // Basic math identifier differences.
691        test(s, "$ x f(z) $", &[]); // single letters not captured.
692        test(s, "$ #x #f(z) $", &["f", "x", "z"]);
693        test(s, "$ foo f(bar) $", &["bar", "foo"]);
694        test(s, "$ #foo[#$bar$] $", &["bar", "foo"]);
695        test(s, "$ #let foo = x; foo $", &["x"]);
696
697        // Math idents don't have dashes/underscores
698        test(s, "$ x-y x_y foo-x x_bar $", &["bar", "foo"]);
699        test(s, "$ #x-bar #x_bar $", &["x-bar", "x_bar"]);
700
701        // Named-params.
702        test(s, "$ foo(bar: y) $", &["foo"]);
703        test(s, "$ foo(x-y: 1, bar-z: 2) $", &["foo"]);
704
705        // Field access in math.
706        test(s, "$ foo.bar $", &["foo"]);
707        test(s, "$ foo.x $", &["foo"]);
708        test(s, "$ x.foo $", &["foo"]);
709        test(s, "$ foo . bar $", &["bar", "foo"]);
710        test(s, "$ foo.x.y.bar(z) $", &["foo"]);
711        test(s, "$ foo.x-bar $", &["bar", "foo"]);
712        test(s, "$ foo.x_bar $", &["bar", "foo"]);
713        test(s, "$ #x_bar.x-bar $", &["x_bar"]);
714    }
715}