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, 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}