rue_compiler/compile/item/
function.rs

1use log::debug;
2use rue_ast::{AstFunctionItem, AstNode};
3use rue_diagnostic::DiagnosticKind;
4use rue_hir::{FunctionSymbol, ParameterSymbol, Scope, Symbol, SymbolId};
5use rue_types::{FunctionType, Type};
6
7use crate::{Compiler, compile_block, compile_generic_parameters, compile_type};
8
9pub fn declare_function(ctx: &mut Compiler, function: &AstFunctionItem) -> SymbolId {
10    let scope = ctx.alloc_scope(Scope::new());
11
12    let vars = if let Some(generic_parameters) = function.generic_parameters() {
13        compile_generic_parameters(ctx, scope, &generic_parameters)
14    } else {
15        vec![]
16    };
17
18    ctx.push_scope(scope);
19
20    let return_type = if let Some(return_type) = function.return_type() {
21        compile_type(ctx, &return_type)
22    } else {
23        debug!("Unresolved function return type");
24        ctx.builtins().unresolved.ty
25    };
26
27    let mut parameters = Vec::new();
28    let mut param_types = Vec::new();
29    let mut nil_terminated = true;
30
31    let len = function.parameters().count();
32
33    for (i, parameter) in function.parameters().enumerate() {
34        let is_spread = if let Some(spread) = parameter.spread() {
35            if i == len - 1 {
36                true
37            } else {
38                ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
39                false
40            }
41        } else {
42            false
43        };
44
45        if is_spread {
46            nil_terminated = false;
47        }
48
49        let ty = if let Some(ty) = parameter.ty() {
50            compile_type(ctx, &ty)
51        } else {
52            debug!("Unresolved function parameter type");
53            ctx.builtins().unresolved.ty
54        };
55
56        let symbol = ctx.alloc_symbol(Symbol::Parameter(ParameterSymbol {
57            name: parameter.name(),
58            ty,
59        }));
60
61        if let Some(name) = parameter.name() {
62            if ctx.scope(scope).symbol(name.text()).is_some() {
63                ctx.diagnostic(
64                    &name,
65                    DiagnosticKind::DuplicateSymbol(name.text().to_string()),
66                );
67            }
68
69            ctx.scope_mut(scope)
70                .insert_symbol(name.text().to_string(), symbol, false);
71        }
72
73        param_types.push(ty);
74        parameters.push(symbol);
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    let symbol = ctx.alloc_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        inline: function.inline().is_some(),
97    }));
98
99    if let Some(name) = function.name() {
100        if ctx.last_scope().symbol(name.text()).is_some() {
101            ctx.diagnostic(
102                &name,
103                DiagnosticKind::DuplicateSymbol(name.text().to_string()),
104            );
105        }
106
107        ctx.last_scope_mut().insert_symbol(
108            name.text().to_string(),
109            symbol,
110            function.export().is_some(),
111        );
112    }
113
114    symbol
115}
116
117pub fn compile_function(ctx: &mut Compiler, function: &AstFunctionItem, symbol: SymbolId) {
118    let (scope, return_type) = if let Symbol::Function(FunctionSymbol {
119        scope, return_type, ..
120    }) = ctx.symbol(symbol)
121    {
122        (*scope, *return_type)
123    } else {
124        unreachable!();
125    };
126
127    ctx.push_scope(scope);
128
129    let resolved_body = if let Some(body) = function.body() {
130        let index = ctx.mapping_checkpoint();
131        let value = compile_block(ctx, &body, true, Some(return_type));
132        ctx.assign_type(body.syntax(), value.ty, return_type);
133        ctx.revert_mappings(index);
134        value
135    } else {
136        debug!("Unresolved function body");
137        ctx.builtins().unresolved.clone()
138    };
139
140    ctx.pop_scope();
141
142    let Symbol::Function(FunctionSymbol { body, .. }) = ctx.symbol_mut(symbol) else {
143        unreachable!();
144    };
145
146    *body = resolved_body.hir;
147}