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, SymbolPath, Value};
7use rue_types::{Pair, Type, Union};
8
9use crate::{
10 Compiler, CompletionContext, PathKind, PathResult, SyntaxField, SyntaxItem, SyntaxItemKind,
11 compile_expr, compile_path,
12};
13
14pub fn compile_struct_initializer_expr(
15 ctx: &mut Compiler,
16 expr: &AstStructInitializerExpr,
17) -> Value {
18 let ty = if let Some(path) = expr.path()
19 && let PathResult::Type(ty) =
20 compile_path(ctx, path.syntax(), path.segments(), PathKind::Type)
21 {
22 ty
23 } else {
24 debug!("Unresolved struct initializer path");
25 ctx.builtins().unresolved.ty
26 };
27
28 let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, true);
29
30 ctx.syntax_map_mut().add_item(SyntaxItem::new(
31 SyntaxItemKind::CompletionContext(CompletionContext::StructFields {
32 ty: semantic,
33 specified_fields: Some(
34 expr.fields()
35 .filter_map(|field| field.name())
36 .map(|field| field.text().to_string())
37 .collect(),
38 ),
39 }),
40 expr.syntax().text_range(),
41 ));
42
43 let Type::Struct(struct_type) = ctx.ty(semantic).clone() else {
44 debug!("Unresolved struct initializer due to non struct type");
45 let name = ctx.type_name(ty);
46 ctx.diagnostic(expr.syntax(), DiagnosticKind::NonStructInitializer(name));
47 return ctx.builtins().unresolved.clone();
48 };
49
50 let mut expected_field_types = HashMap::new();
51 let mut current = struct_type.inner;
52
53 for (i, field) in struct_type.fields.iter().enumerate() {
54 if i == struct_type.fields.len() - 1 && !struct_type.nil_terminated {
55 expected_field_types.insert(field.clone(), current);
56 continue;
57 }
58
59 let pairs = rue_types::extract_pairs(ctx.types_mut(), current, false);
60
61 if pairs.is_empty() {
62 break;
63 }
64
65 let first = if pairs.len() == 1 {
66 pairs[0].first
67 } else {
68 ctx.alloc_type(Type::Union(Union::new(
69 pairs.iter().map(|pair| pair.first).collect(),
70 )))
71 };
72
73 let rest = if pairs.len() == 1 {
74 pairs[0].rest
75 } else {
76 ctx.alloc_type(Type::Union(Union::new(
77 pairs.iter().map(|pair| pair.rest).collect(),
78 )))
79 };
80
81 expected_field_types.insert(field.clone(), first);
82 current = rest;
83 }
84
85 let mut fields = HashMap::new();
86
87 for field in expr.fields() {
88 let value = if let Some(expr) = field.expr() {
89 compile_expr(
90 ctx,
91 &expr,
92 field
93 .name()
94 .and_then(|name| expected_field_types.get(name.text()).copied()),
95 )
96 } else {
97 let Some(name) = field.name() else {
98 continue;
99 };
100
101 if let PathResult::Symbol(symbol, override_type) =
102 compile_path(ctx, field.syntax(), [name].into_iter(), PathKind::Symbol)
103 {
104 let ty = ctx.symbol_type(symbol);
105
106 let mut value = Value::new(ctx.alloc_hir(Hir::Reference(symbol)), ty)
107 .with_reference(SymbolPath {
108 symbol,
109 path: vec![],
110 });
111
112 if let Some(override_type) = override_type {
113 value = value.with_type(override_type);
114 }
115
116 value
117 } else {
118 debug!("Unresolved field path in struct initializer");
119 ctx.builtins().unresolved.clone()
120 }
121 };
122
123 let Some(name) = field.name() else {
124 continue;
125 };
126
127 ctx.syntax_map_mut().add_item(SyntaxItem::new(
128 SyntaxItemKind::FieldInitializer(SyntaxField {
129 name: name.text().to_string(),
130 container: semantic,
131 ty: expected_field_types
132 .get(name.text())
133 .copied()
134 .unwrap_or(value.ty),
135 }),
136 name.text_range(),
137 ));
138
139 if struct_type.fields.contains(name.text()) {
140 if let Some(expected_type) = expected_field_types.get(name.text()) {
141 ctx.assign_type(field.syntax(), value.ty, *expected_type);
142 }
143 } else {
144 let type_name = ctx.type_name(ty);
145 ctx.diagnostic(
146 &name,
147 DiagnosticKind::UnknownField(name.text().to_string(), type_name),
148 );
149 continue;
150 }
151
152 if fields.insert(name.text().to_string(), value).is_some() {
153 ctx.diagnostic(
154 &name,
155 DiagnosticKind::DuplicateField(name.text().to_string()),
156 );
157 }
158 }
159
160 let mut hir = ctx.builtins().nil.hir;
161 let mut list_type = ctx.builtins().nil.ty;
162
163 let mut missing_fields = vec![];
164
165 for (i, name) in struct_type.fields.into_iter().rev().enumerate() {
166 let value = if let Some(value) = fields.remove(&name) {
167 value
168 } else if let Some(value) = ctx.default_field(struct_type.semantic, &name) {
169 value
170 } else {
171 debug!("Unresolved struct initializer field");
172 missing_fields.push(name);
173 ctx.builtins().unresolved.clone()
174 };
175
176 if !struct_type.nil_terminated && i == 0 {
177 hir = value.hir;
178 list_type = value.ty;
179 } else {
180 hir = ctx.alloc_hir(Hir::Pair(value.hir, hir));
181 list_type = ctx.alloc_type(Type::Pair(Pair::new(value.ty, list_type)));
182 }
183 }
184
185 if !missing_fields.is_empty() {
186 let type_name = ctx.type_name(ty);
187 ctx.diagnostic(
188 expr.syntax(),
189 DiagnosticKind::MissingRequiredFields(type_name, missing_fields.join(", ")),
190 );
191 }
192
193 Value::new(hir, ty)
194}