rue_compiler/compile/expr/
function_call.rs1use std::collections::HashMap;
2
3use log::debug;
4use rue_ast::{AstFunctionCallExpr, AstNode};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{FunctionCall, Hir, Value};
7use rue_types::{Type, Union, substitute_with_mappings};
8
9use crate::{Compiler, compile_expr};
10
11pub fn compile_function_call_expr(ctx: &mut Compiler, call: &AstFunctionCallExpr) -> Value {
12 let Some(expr) = call.expr() else {
13 debug!("Unresolved function call expr");
14 return ctx.builtins().unresolved.clone();
15 };
16
17 let expr = compile_expr(ctx, &expr, None);
18
19 let expected_functions = rue_types::extract_functions(ctx.types_mut(), expr.ty);
20
21 if expected_functions.len() > 1 {
22 let name = ctx.type_name(expr.ty);
23 ctx.diagnostic(
24 call.syntax(),
25 DiagnosticKind::CannotDisambiguateFunctionTypes(name),
26 );
27 }
28
29 if expected_functions.is_empty() {
30 let name = ctx.type_name(expr.ty);
31 ctx.diagnostic(call.syntax(), DiagnosticKind::InvalidFunctionCall(name));
32 }
33
34 let expected_function = expected_functions.first();
35
36 let mut args = Vec::new();
37 let mut nil_terminated = true;
38 let mut next_spreadable_type = None;
39 let mut mappings = HashMap::new();
40
41 let len = call.args().count();
42
43 for (i, arg) in call.args().enumerate() {
44 let is_spread = if let Some(spread) = arg.spread() {
45 if i == len - 1 {
46 if let Some(function) = expected_function {
47 if function.nil_terminated {
48 ctx.diagnostic(&spread, DiagnosticKind::InvalidSpread);
49 } else {
50 nil_terminated = false;
51 }
52 }
53 true
54 } else {
55 ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
56 false
57 }
58 } else {
59 false
60 };
61
62 let expected_type = if let Some(function) = expected_function {
63 if function.nil_terminated || i < function.params.len() - 1 {
64 function.params.get(i).copied()
65 } else {
66 if next_spreadable_type.is_none() {
67 next_spreadable_type = Some(function.params.last().copied());
68 }
69
70 match next_spreadable_type {
71 None => unreachable!(),
72 Some(Some(ty)) if is_spread => {
73 next_spreadable_type = None;
74 Some(ty)
75 }
76 Some(Some(ty)) => {
77 let pairs = rue_types::extract_pairs(ctx.types_mut(), ty);
78
79 if pairs.is_empty() {
80 next_spreadable_type = None;
81 None
82 } else if pairs.len() == 1 {
83 next_spreadable_type = Some(Some(pairs[0].rest));
84
85 Some(pairs[0].first)
86 } else {
87 next_spreadable_type = Some(Some(ctx.alloc_type(Type::Union(
88 Union::new(pairs.iter().map(|pair| pair.rest).collect()),
89 ))));
90
91 Some(ctx.alloc_type(Type::Union(Union::new(
92 pairs.iter().map(|pair| pair.first).collect(),
93 ))))
94 }
95 }
96 Some(None) => None,
97 }
98 }
99 } else {
100 None
101 };
102
103 let expected_type = expected_type.map(|expected_type| {
104 substitute_with_mappings(ctx.types_mut(), expected_type, &mappings)
105 });
106
107 let value = if let Some(expr) = arg.expr() {
108 let value = compile_expr(ctx, &expr, expected_type);
109
110 if let Some(expected_type) = expected_type {
111 ctx.infer_type(arg.syntax(), value.ty, expected_type, &mut mappings);
112 }
113
114 value
115 } else {
116 debug!("Unresolved function call arg");
117 ctx.builtins().unresolved.clone()
118 };
119
120 args.push(value.hir);
121 }
122
123 let ty = if expected_functions.is_empty() {
124 debug!("Unresolved function call return type due to unresolved function type");
125 ctx.builtins().unresolved.ty
126 } else if expected_functions.len() == 1 {
127 let function = expected_functions[0].clone();
128
129 if !function.nil_terminated {
130 if args.len() < function.params.len().saturating_sub(1) {
131 ctx.diagnostic(
132 call.syntax(),
133 DiagnosticKind::ExpectedArgumentsBeforeSpread(
134 function.params.len(),
135 args.len(),
136 ),
137 );
138 }
139 } else if args.len() != function.params.len() {
140 ctx.diagnostic(
141 call.syntax(),
142 DiagnosticKind::ExpectedArgumentsExact(function.params.len(), args.len()),
143 );
144 }
145
146 function.ret
147 } else {
148 ctx.alloc_type(Type::Union(Union::new(
149 expected_functions
150 .iter()
151 .map(|function| function.ret)
152 .collect(),
153 )))
154 };
155
156 let ty = substitute_with_mappings(ctx.types_mut(), ty, &mappings);
157
158 let hir = ctx.alloc_hir(Hir::FunctionCall(FunctionCall {
159 function: expr.hir,
160 args,
161 nil_terminated,
162 }));
163
164 Value::new(hir, ty)
165}