rukalang/elab/
elaborate_calls.rs1use super::helpers::split_variant_path;
2use super::prelude::*;
3use ruka_frontend::SourceSpan;
4
5impl Elaborator {
6 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 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}