rue_compiler/compile/item/
function.rs

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