compose_eval/expression/
call.rs1use crate::vm::ErrorMode;
2use crate::{Eval, Evaluated, Machine};
3use compose_library::diag::{At, SourceResult, Spanned, Trace, TracePoint, bail};
4use compose_library::{Arg, Args, Func, NativeScope, Type, UnboundItem, Value};
5use compose_syntax::ast::AstNode;
6use compose_syntax::{Label, Span, ast};
7use ecow::{EcoString, EcoVec, eco_format};
8use extension_traits::extension;
9
10impl Eval for ast::FuncCall<'_> {
11 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
12 let callee_span = self.callee().span();
13
14 let (callee, args) = self.eval_callee_and_args(vm)?;
15
16 let func = callee.value.cast::<Func>().at(callee_span)?;
17
18 func.call(vm, args).map(Evaluated::mutable).trace(
19 || TracePoint::Call(func.name().map(EcoString::from)),
20 self.span(),
21 )
22 }
23}
24
25#[extension(trait FuncCallExt)]
26impl ast::FuncCall<'_> {
27 fn eval_callee_and_args(&self, vm: &mut Machine) -> SourceResult<(Evaluated, Args)> {
28 if let ast::Expr::FieldAccess(field_access) = self.callee() {
29 self.eval_method_call(&field_access, vm)
30 } else {
31 self.eval_regular_call(vm)
32 }
33 }
34
35 fn eval_method_call(
36 &self,
37 field_access: &ast::FieldAccess,
38 vm: &mut Machine,
39 ) -> SourceResult<(Evaluated, Args)> {
40 let target_expr = field_access.target();
41 let field = field_access.field();
42
43 let target = target_expr.eval(vm)?;
44 let mut args = self.args().eval(vm)?;
45
46 let callee_binding = {
47 let res = target.value.ty().scope().try_get(&field);
48
49 match res {
50 Ok(binding) => binding,
51 Err(err) => {
52 let value_scope = Value::scope();
53 match value_scope.try_get(&field) {
54 Ok(binding) => binding,
55 Err(mut err2) => {
56 err2.possible_misspellings.extend(err.possible_misspellings);
57 return Err(err2.with_item(UnboundItem::FieldOrMethod(Some(
58 target.value.ty().name().into(),
59 ))))
60 .at(field.span());
61 }
62 }
63 }
64 }
65 };
66 let callee = callee_binding
67 .read_checked(target_expr.span(), vm.sink_mut())
68 .clone();
69
70 let target_ty = target.value.ty();
71 if let Value::Func(func) = &callee {
72 if func.requires_mut_self() && !target.mutable {
73 let target_text = target_expr.to_untyped().to_text();
74 bail!(field.span(), "cannot call method `{}` on an immutable {}", field.as_str(), target_ty.name();
75 label_message: "cannot call `{}` on `{target_text}` because it is not mutable", field.as_str();
76 label: Label::secondary(target.origin.unwrap_or(Span::detached()), eco_format!("help: try making `{target_text}` mutable with `mut"));
77 note: "only mutable variables can call methods that modify their contents";
78 hint: "if mutation is not intended, you can use `.clone()` to create a new copy";
79 )
80 }
81
82 if func.is_associated_function() {
83 return err_call_associated_function_as_method(
84 &target_ty,
85 field.as_str(),
86 field.span(),
87 );
88 }
89 }
90
91 args.insert(0, target_expr.span(), target.value);
92
93 Ok((Evaluated::new(callee, target.mutable), args))
94 }
95
96 fn eval_regular_call(&self, vm: &mut Machine) -> SourceResult<(Evaluated, Args)> {
97 let callee = self.callee().eval(vm)?;
98 let args = self.args().eval(vm)?.spanned(self.span());
99 Ok((callee, args))
100 }
101}
102
103fn err_call_associated_function_as_method<T>(
104 target_ty: &Type,
105 field: &str,
106 span: Span,
107) -> SourceResult<T> {
108 bail!(
109 span,
110 "cannot call associated function `{}::{}` as a method", target_ty.name(), field;
111 label_message: "not a method on `{}`", target_ty.name();
112 note: "`{}` is an associated function of `{}`, not a method", field, target_ty.name();
113 hint: "use path syntax instead: `{}::{}(args)`", target_ty.name(), field;
114 )
115}
116
117#[extension(trait EvalArgs)]
118impl ast::Args<'_> {
119 fn eval(self, vm: &mut Machine) -> SourceResult<Args> {
120 let mut items = EcoVec::with_capacity(self.items().count());
121
122 vm.with_closure_capture_errors_mode(ErrorMode::Immediate, |vm| {
125 for arg in self.items() {
126 let span = arg.span();
127 match arg {
128 ast::Arg::Pos(expr) => items.push(Arg {
129 span,
130 name: None,
131 value: Spanned::new(expr.eval(vm)?.value, expr.span()),
132 }),
133 ast::Arg::Named(named) => items.push(Arg {
134 span,
135 name: Some(named.name().get().into()),
136 value: Spanned::new(named.expr().eval(vm)?.value, named.expr().span()),
137 }),
138 }
139 }
140
141 Ok(())
142 })?;
143
144 Ok(Args {
146 span: Span::detached(),
147 items,
148 })
149 }
150}