rue_compiler/compile/item/
function.rs1use indexmap::IndexMap;
2use log::debug;
3use rue_ast::{AstFunctionItem, AstNode};
4use rue_diagnostic::DiagnosticKind;
5use rue_hir::{Declaration, FunctionKind, FunctionSymbol, ParameterSymbol, Symbol, SymbolId, Test};
6use rue_types::{FunctionType, Type};
7
8use crate::{
9 Compiler, CompletionContext, SyntaxItemKind, compile_block, compile_generic_parameters,
10 compile_type, create_binding,
11};
12
13pub fn declare_function(ctx: &mut Compiler, function: &AstFunctionItem) -> SymbolId {
14 ctx.add_syntax(
15 SyntaxItemKind::CompletionContext(CompletionContext::Item),
16 function.syntax().text_range(),
17 );
18
19 let symbol = ctx.alloc_symbol(Symbol::Unresolved);
20
21 if function.test().is_some() {
22 let path = ctx.source().kind.clone();
23
24 ctx.add_test(Test {
25 name: function.name().map(|name| name.text().to_string()),
26 path,
27 symbol,
28 });
29 }
30
31 ctx.push_declaration(Declaration::Symbol(symbol));
32
33 let scope = ctx.alloc_child_scope();
34
35 let vars = if let Some(generic_parameters) = function.generic_parameters() {
36 compile_generic_parameters(ctx, scope, &generic_parameters)
37 } else {
38 vec![]
39 };
40
41 let range = function.syntax().text_range();
42 ctx.push_scope(scope, range.start());
43
44 let return_type = if let Some(return_type) = function.return_type() {
45 compile_type(ctx, &return_type)
46 } else {
47 ctx.builtins().nil.ty
48 };
49
50 let mut parameters = IndexMap::new();
51 let mut param_types = IndexMap::new();
52 let mut nil_terminated = true;
53
54 let len = function.parameters().count();
55
56 for (i, parameter) in function.parameters().enumerate() {
57 let is_spread = if let Some(spread) = parameter.spread() {
58 if i == len - 1 {
59 true
60 } else {
61 ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
62 false
63 }
64 } else {
65 false
66 };
67
68 if is_spread {
69 nil_terminated = false;
70 }
71
72 let symbol = ctx.alloc_symbol(Symbol::Unresolved);
73
74 ctx.push_declaration(Declaration::Symbol(symbol));
75
76 let ty = if let Some(ty) = parameter.ty() {
77 compile_type(ctx, &ty)
78 } else {
79 debug!("Unresolved function parameter type");
80 ctx.builtins().unresolved.ty
81 };
82
83 *ctx.symbol_mut(symbol) = Symbol::Parameter(ParameterSymbol { name: None, ty });
84
85 let name = parameter
86 .binding()
87 .map_or(String::new(), |binding| binding.syntax().text().to_string());
88
89 param_types.insert(name.clone(), ty);
90 parameters.insert(name, symbol);
91
92 ctx.pop_declaration();
93 }
94
95 ctx.pop_scope(range.end());
96
97 let body = ctx.builtins().unresolved.hir;
98
99 let ty = ctx.alloc_type(Type::Function(FunctionType {
100 params: param_types,
101 nil_terminated,
102 ret: return_type,
103 }));
104
105 let name = function.name().map(|name| ctx.local_name(&name));
106
107 *ctx.symbol_mut(symbol) = Symbol::Function(FunctionSymbol {
108 name,
109 ty,
110 scope,
111 vars,
112 parameters,
113 nil_terminated,
114 return_type,
115 body,
116 kind: if function.inline().is_some() {
117 FunctionKind::Inline
118 } else if function.extern_kw().is_some() {
119 FunctionKind::Sequential
120 } else {
121 FunctionKind::BinaryTree
122 },
123 });
124
125 if let Some(name) = function.name() {
126 if ctx.last_scope().symbol(name.text()).is_some() {
127 ctx.diagnostic(
128 &name,
129 DiagnosticKind::DuplicateSymbol(name.text().to_string()),
130 );
131 } else {
132 ctx.last_scope_mut().insert_symbol(
133 name.text().to_string(),
134 symbol,
135 function.export().is_some(),
136 );
137 }
138
139 ctx.declaration_span(Declaration::Symbol(symbol), name.text_range());
140 }
141
142 ctx.pop_declaration();
143
144 symbol
145}
146
147pub fn compile_function(ctx: &mut Compiler, function: &AstFunctionItem, symbol: SymbolId) {
148 ctx.push_declaration(Declaration::Symbol(symbol));
149
150 let Symbol::Function(FunctionSymbol {
151 scope,
152 parameters,
153 return_type,
154 ..
155 }) = ctx.symbol(symbol).clone()
156 else {
157 unreachable!();
158 };
159
160 let range = function.syntax().text_range();
161 ctx.push_scope(scope, range.start());
162
163 for (i, parameter) in function.parameters().enumerate() {
164 let symbol = parameters[i];
165
166 ctx.push_declaration(Declaration::Symbol(symbol));
167
168 if let Some(binding) = parameter.binding() {
169 create_binding(ctx, symbol, &binding);
170 }
171
172 ctx.pop_declaration();
173 }
174
175 let resolved_body = if let Some(body) = function.body() {
176 let value = compile_block(
177 ctx,
178 &body,
179 true,
180 Some(return_type),
181 function.return_type().is_some(),
182 );
183 ctx.assign_type(body.syntax(), value.ty, return_type);
184 value
185 } else {
186 debug!("Unresolved function body");
187 ctx.builtins().unresolved.clone()
188 };
189
190 ctx.pop_scope(range.end());
191
192 let Symbol::Function(FunctionSymbol { body, .. }) = ctx.symbol_mut(symbol) else {
193 unreachable!();
194 };
195
196 *body = resolved_body.hir;
197
198 ctx.pop_declaration();
199}