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