rukalang/elab/
elaborate_calls.rs

1use super::helpers::split_variant_path;
2use super::prelude::*;
3use ruka_frontend::SourceSpan;
4
5impl Elaborator {
6    /// Elaborate one runtime call expression and return its resulting type.
7    pub(super) fn elaborate_call_expr(
8        &mut self,
9        callee: &mut Box<Expr>,
10        args: &mut Vec<CallArg>,
11        expected: Option<&Ty>,
12        locals: &mut Vec<(String, Ty)>,
13    ) -> Result<Ty, ElabError> {
14        let (callee_name, callee_span) = match callee.as_ref() {
15            Expr::Ident { name, span } => (name.clone(), *span),
16            _ => return Err(ElabError::InvalidCallTarget),
17        };
18        if callee_name == "box" {
19            return Err(ElabError::UnknownFunction { name: callee_name });
20        }
21
22        self.normalize_runtime_calls_and_spreads(args, locals)?;
23
24        if let Some(signature) = self.signatures.get(&callee_name).cloned() {
25            self.elaborate_call_runtime_args(args, &signature.params, locals, &callee_name)?;
26            return Ok(signature.return_ty);
27        }
28
29        if let Some(template) = self.function_templates.get(&callee_name).cloned() {
30            let binding = self.bind_template_call_args(&callee_name, &template, args, locals)?;
31            let concrete_name = self.instantiate_runtime_function(
32                &callee_name,
33                &template,
34                &binding.type_bindings,
35                &binding.meta_bindings,
36                &binding.specialization_key,
37                &binding.variadic_pack_tys,
38            )?;
39            let signature = self
40                .signatures
41                .get(&concrete_name)
42                .cloned()
43                .ok_or_else(|| ElabError::UnknownFunction {
44                    name: concrete_name.clone(),
45                })?;
46            *callee = Box::new(Expr::Ident {
47                name: concrete_name,
48                span: callee_span,
49            });
50            *args = binding
51                .runtime_args
52                .into_iter()
53                .map(CallArg::Expr)
54                .collect();
55            self.elaborate_call_runtime_args(args, &signature.params, locals, &callee_name)?;
56            return Ok(signature.return_ty);
57        }
58
59        self.elaborate_enum_constructor_call(
60            callee,
61            &callee_name,
62            callee_span,
63            args,
64            expected,
65            locals,
66        )
67    }
68
69    /// Elaborate one enum-constructor call expression and return its resulting type.
70    fn elaborate_enum_constructor_call(
71        &mut self,
72        callee: &mut Box<Expr>,
73        callee_name: &str,
74        callee_span: SourceSpan,
75        args: &mut Vec<CallArg>,
76        expected: Option<&Ty>,
77        locals: &mut Vec<(String, Ty)>,
78    ) -> Result<Ty, ElabError> {
79        let mut candidates = Vec::<(String, String)>::new();
80        let has_user_variant = self
81            .enum_templates
82            .values()
83            .any(|template| template.variants.contains_key(callee_name));
84        if matches!(callee_name, "Option::Some" | "Option::None")
85            || (matches!(callee_name, "Some" | "None") && !has_user_variant)
86        {
87            let variant = if callee_name.ends_with("::Some") || callee_name == "Some" {
88                "Some"
89            } else {
90                "None"
91            };
92            candidates.push(("Option".to_owned(), variant.to_owned()));
93        }
94        if let Some((enum_name, variant)) = split_variant_path(callee_name) {
95            if let Some(template) = self.enum_templates.get(enum_name)
96                && template.variants.contains_key(variant)
97            {
98                candidates.push((enum_name.to_owned(), variant.to_owned()));
99            }
100        } else {
101            for (enum_name, template) in &self.enum_templates {
102                if template.variants.contains_key(callee_name) {
103                    candidates.push((enum_name.clone(), callee_name.to_owned()));
104                }
105            }
106        }
107
108        if let Some(Ty::Enum {
109            name: expected_enum,
110            ..
111        }) = expected
112        {
113            candidates.retain(|(name, _)| name == expected_enum);
114        }
115
116        if candidates.len() != 1 {
117            return Err(ElabError::UnknownFunction {
118                name: callee_name.to_owned(),
119            });
120        }
121
122        let (enum_name, variant) = candidates.remove(0);
123        let enum_args = match expected {
124            Some(Ty::Enum {
125                name: expected_name,
126                args,
127            }) if expected_name == &enum_name => args.clone(),
128            _ => Vec::new(),
129        };
130        let payload_tys = if enum_name == "Option" {
131            match variant.as_str() {
132                "None" => Vec::new(),
133                "Some" => {
134                    if let Some(Ty::Option(item)) = expected {
135                        vec![(**item).clone()]
136                    } else if let Some(first) = args.first_mut() {
137                        let CallArg::Expr(first_expr) = first else {
138                            return Err(ElabError::UnsupportedTypeExpr);
139                        };
140                        vec![self.elaborate_expr(first_expr, None, locals)?]
141                    } else {
142                        Vec::new()
143                    }
144                }
145                _ => {
146                    return Err(ElabError::UnknownIdentifier {
147                        name: format!("{enum_name}::{variant}"),
148                    });
149                }
150            }
151        } else {
152            self.resolve_enum_variant_payload_tys(&enum_name, &enum_args, &variant)?
153        };
154        if args.len() != payload_tys.len() {
155            return Err(ElabError::ArityMismatch {
156                function: format!("{enum_name}::{variant}"),
157                expected: payload_tys.len(),
158                actual: args.len(),
159            });
160        }
161        for (arg, payload_ty) in args.iter_mut().zip(payload_tys.iter()) {
162            let CallArg::Expr(arg_expr) = arg else {
163                return Err(ElabError::UnsupportedTypeExpr);
164            };
165            let _ = self.elaborate_expr(arg_expr, Some(payload_ty), locals)?;
166        }
167        *callee = Box::new(Expr::Ident {
168            name: format!("{enum_name}::{variant}"),
169            span: callee_span,
170        });
171        if enum_name == "Option" {
172            let item_ty = if variant == "Some" {
173                payload_tys.first().cloned().unwrap_or(Ty::Unit)
174            } else if let Some(Ty::Option(item)) = expected {
175                (**item).clone()
176            } else {
177                Ty::Unit
178            };
179            return Ok(Ty::Option(Box::new(item_ty)));
180        }
181        Ok(Ty::Enum {
182            name: enum_name,
183            args: enum_args,
184        })
185    }
186}