rue_compiler/compile/expr/
struct_initializer.rs1use std::collections::HashMap;
2
3use log::debug;
4use rue_ast::{AstNode, AstStructInitializerExpr};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{Hir, Value};
7use rue_types::{Pair, Type, Union};
8
9use crate::{Compiler, PathKind, PathResult, compile_expr, compile_path};
10
11pub fn compile_struct_initializer_expr(
12 ctx: &mut Compiler,
13 expr: &AstStructInitializerExpr,
14) -> Value {
15 let ty = if let Some(path) = expr.path()
16 && let PathResult::Type(ty) =
17 compile_path(ctx, path.syntax(), path.segments(), PathKind::Type)
18 {
19 ty
20 } else {
21 debug!("Unresolved struct initializer path");
22 ctx.builtins().unresolved.ty
23 };
24
25 let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, true);
26
27 let Type::Struct(struct_type) = ctx.ty(semantic).clone() else {
28 debug!("Unresolved struct initializer due to non struct type");
29 let name = ctx.type_name(ty);
30 ctx.diagnostic(expr.syntax(), DiagnosticKind::NonStructInitializer(name));
31 return ctx.builtins().unresolved.clone();
32 };
33
34 let mut expected_field_types = HashMap::new();
35 let mut current = struct_type.inner;
36
37 for field in &struct_type.fields {
38 let pairs = rue_types::extract_pairs(ctx.types_mut(), current);
39
40 if pairs.is_empty() {
41 break;
42 }
43
44 let first = if pairs.len() == 1 {
45 pairs[0].first
46 } else {
47 ctx.alloc_type(Type::Union(Union::new(
48 pairs.iter().map(|pair| pair.first).collect(),
49 )))
50 };
51
52 let rest = if pairs.len() == 1 {
53 pairs[0].rest
54 } else {
55 ctx.alloc_type(Type::Union(Union::new(
56 pairs.iter().map(|pair| pair.rest).collect(),
57 )))
58 };
59
60 expected_field_types.insert(field.clone(), first);
61 current = rest;
62 }
63
64 let mut fields = HashMap::new();
65
66 for field in expr.fields() {
67 let value = if let Some(expr) = field.expr() {
68 compile_expr(
69 ctx,
70 &expr,
71 field
72 .name()
73 .and_then(|name| expected_field_types.get(name.text()).copied()),
74 )
75 } else {
76 debug!("Unresolved struct initializer field expr");
77 ctx.builtins().unresolved.clone()
78 };
79
80 let Some(name) = field.name() else {
81 continue;
82 };
83
84 if fields.insert(name.text().to_string(), value).is_some() {
85 ctx.diagnostic(
86 &name,
87 DiagnosticKind::DuplicateField(name.text().to_string()),
88 );
89 continue;
90 }
91
92 if !struct_type.fields.contains(name.text()) {
93 let type_name = ctx.type_name(ty);
94 ctx.diagnostic(
95 &name,
96 DiagnosticKind::UnknownField(name.text().to_string(), type_name),
97 );
98 }
99 }
100
101 let mut hir = ctx.builtins().nil.hir;
102 let mut list_type = ctx.builtins().nil.ty;
103
104 let mut missing_fields = vec![];
105
106 for (i, name) in struct_type.fields.into_iter().rev().enumerate() {
107 let value = if let Some(value) = fields.remove(&name) {
108 value
109 } else if let Some(value) = ctx.default_field(ty, &name) {
110 value
111 } else {
112 debug!("Unresolved struct initializer field");
113 missing_fields.push(name);
114 ctx.builtins().unresolved.clone()
115 };
116
117 if !struct_type.nil_terminated && i == 0 {
118 hir = value.hir;
119 list_type = value.ty;
120 } else {
121 hir = ctx.alloc_hir(Hir::Pair(value.hir, hir));
122 list_type = ctx.alloc_type(Type::Pair(Pair::new(value.ty, list_type)));
123 }
124 }
125
126 if !missing_fields.is_empty() {
127 let type_name = ctx.type_name(ty);
128 ctx.diagnostic(
129 expr.syntax(),
130 DiagnosticKind::MissingRequiredFields(type_name, missing_fields.join(", ")),
131 );
132 }
133
134 ctx.assign_type(expr.syntax(), list_type, struct_type.inner);
135
136 Value::new(hir, ty)
137}