rue_compiler/compile/expr/
struct_initializer.rs

1use 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}