rue_compiler/compile/item/
function.rs

1use 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}