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