rue_compiler/compile/expr/
lambda.rs

1use log::debug;
2use rue_ast::{AstLambdaExpr, AstNode};
3use rue_diagnostic::DiagnosticKind;
4use rue_hir::{FunctionKind, FunctionSymbol, Hir, ParameterSymbol, Symbol, Value};
5use rue_types::{FunctionType, Type, TypeId, Union};
6
7use crate::{Compiler, compile_expr, compile_generic_parameters, compile_type, create_binding};
8
9pub fn compile_lambda_expr(
10    ctx: &mut Compiler,
11    expr: &AstLambdaExpr,
12    expected_type: Option<TypeId>,
13) -> Value {
14    let expected_functions = if let Some(ty) = expected_type {
15        rue_types::extract_functions(ctx.types_mut(), ty)
16    } else {
17        vec![]
18    };
19
20    let scope = ctx.alloc_child_scope();
21
22    let vars = if let Some(generic_parameters) = expr.generic_parameters() {
23        compile_generic_parameters(ctx, scope, &generic_parameters)
24    } else {
25        vec![]
26    };
27
28    let range = expr.syntax().text_range();
29    ctx.push_scope(scope, range.start());
30
31    let mut parameters = Vec::new();
32    let mut params = Vec::new();
33    let mut nil_terminated = true;
34
35    let len = expr.parameters().count();
36
37    for (i, param) in expr.parameters().enumerate() {
38        let is_spread = if let Some(spread) = param.spread() {
39            if i == len - 1 {
40                true
41            } else {
42                ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
43                false
44            }
45        } else {
46            false
47        };
48
49        if is_spread {
50            nil_terminated = false;
51        }
52
53        let ty = if let Some(ty) = param.ty() {
54            compile_type(ctx, &ty)
55        } else if !expected_functions.is_empty()
56            && let params = expected_functions
57                .iter()
58                .filter_map(|function| function.params.get(i).copied())
59                .collect::<Vec<_>>()
60            && !params.is_empty()
61        {
62            if params.len() == 1 {
63                params[0]
64            } else {
65                ctx.alloc_type(Type::Union(Union::new(params)))
66            }
67        } else {
68            debug!("Unresolved lambda parameter type due to missing inference");
69            ctx.diagnostic(param.syntax(), DiagnosticKind::CannotInferParameterType);
70            ctx.builtins().unresolved.ty
71        };
72
73        let symbol = ctx.alloc_symbol(Symbol::Parameter(ParameterSymbol { name: None, ty }));
74
75        if let Some(binding) = param.binding() {
76            create_binding(ctx, symbol, &binding);
77        }
78
79        parameters.push(symbol);
80        params.push(ty);
81    }
82
83    let return_type = expr.ty().map(|ty| compile_type(ctx, &ty));
84
85    let body = if let Some(body) = expr.body() {
86        let result = compile_expr(ctx, &body, return_type);
87        if let Some(return_type) = return_type {
88            ctx.assign_type(expr.syntax(), result.ty, return_type);
89        }
90        result
91    } else {
92        debug!("Unresolved lambda body");
93        ctx.builtins().unresolved.clone()
94    };
95
96    let return_type = return_type.unwrap_or(body.ty);
97
98    let ty = ctx.alloc_type(Type::Function(FunctionType {
99        params,
100        nil_terminated,
101        ret: return_type,
102    }));
103
104    ctx.pop_scope(range.end());
105
106    let symbol = ctx.alloc_symbol(Symbol::Function(FunctionSymbol {
107        name: None,
108        ty,
109        scope,
110        vars,
111        parameters,
112        nil_terminated,
113        return_type,
114        body: body.hir,
115        kind: FunctionKind::Sequential,
116    }));
117
118    let hir = ctx.alloc_hir(Hir::Lambda(symbol));
119
120    Value::new(hir, ty)
121}