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