rue_compiler/compile/item/
function.rs

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