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::{Declaration, Hir, SymbolPath, 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    ctx.reference(Declaration::Type(ty));
26
27    let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, true);
28
29    let Type::Struct(struct_type) = ctx.ty(semantic).clone() else {
30        debug!("Unresolved struct initializer due to non struct type");
31        let name = ctx.type_name(ty);
32        ctx.diagnostic(expr.syntax(), DiagnosticKind::NonStructInitializer(name));
33        return ctx.builtins().unresolved.clone();
34    };
35
36    let mut expected_field_types = HashMap::new();
37    let mut current = struct_type.inner;
38
39    for field in &struct_type.fields {
40        let pairs = rue_types::extract_pairs(ctx.types_mut(), current, false);
41
42        if pairs.is_empty() {
43            break;
44        }
45
46        let first = if pairs.len() == 1 {
47            pairs[0].first
48        } else {
49            ctx.alloc_type(Type::Union(Union::new(
50                pairs.iter().map(|pair| pair.first).collect(),
51            )))
52        };
53
54        let rest = if pairs.len() == 1 {
55            pairs[0].rest
56        } else {
57            ctx.alloc_type(Type::Union(Union::new(
58                pairs.iter().map(|pair| pair.rest).collect(),
59            )))
60        };
61
62        expected_field_types.insert(field.clone(), first);
63        current = rest;
64    }
65
66    let mut fields = HashMap::new();
67
68    for field in expr.fields() {
69        let value = if let Some(expr) = field.expr() {
70            compile_expr(
71                ctx,
72                &expr,
73                field
74                    .name()
75                    .and_then(|name| expected_field_types.get(name.text()).copied()),
76            )
77        } else {
78            let Some(name) = field.name() else {
79                continue;
80            };
81
82            if let PathResult::Symbol(symbol, override_type) =
83                compile_path(ctx, field.syntax(), [name].into_iter(), PathKind::Symbol)
84            {
85                ctx.reference(Declaration::Symbol(symbol));
86
87                let ty = ctx.symbol_type(symbol);
88
89                let mut value = Value::new(ctx.alloc_hir(Hir::Reference(symbol)), ty)
90                    .with_reference(SymbolPath {
91                        symbol,
92                        path: vec![],
93                    });
94
95                if let Some(override_type) = override_type {
96                    value = value.with_type(override_type);
97                }
98
99                value
100            } else {
101                debug!("Unresolved field path in struct initializer");
102                ctx.builtins().unresolved.clone()
103            }
104        };
105
106        let Some(name) = field.name() else {
107            continue;
108        };
109
110        if fields.insert(name.text().to_string(), value).is_some() {
111            ctx.diagnostic(
112                &name,
113                DiagnosticKind::DuplicateField(name.text().to_string()),
114            );
115            continue;
116        }
117
118        if !struct_type.fields.contains(name.text()) {
119            let type_name = ctx.type_name(ty);
120            ctx.diagnostic(
121                &name,
122                DiagnosticKind::UnknownField(name.text().to_string(), type_name),
123            );
124        }
125    }
126
127    let mut hir = ctx.builtins().nil.hir;
128    let mut list_type = ctx.builtins().nil.ty;
129
130    let mut missing_fields = vec![];
131
132    for (i, name) in struct_type.fields.into_iter().rev().enumerate() {
133        let value = if let Some(value) = fields.remove(&name) {
134            value
135        } else if let Some(value) = ctx.default_field(struct_type.semantic, &name) {
136            value
137        } else {
138            debug!("Unresolved struct initializer field");
139            missing_fields.push(name);
140            ctx.builtins().unresolved.clone()
141        };
142
143        if !struct_type.nil_terminated && i == 0 {
144            hir = value.hir;
145            list_type = value.ty;
146        } else {
147            hir = ctx.alloc_hir(Hir::Pair(value.hir, hir));
148            list_type = ctx.alloc_type(Type::Pair(Pair::new(value.ty, list_type)));
149        }
150    }
151
152    if !missing_fields.is_empty() {
153        let type_name = ctx.type_name(ty);
154        ctx.diagnostic(
155            expr.syntax(),
156            DiagnosticKind::MissingRequiredFields(type_name, missing_fields.join(", ")),
157        );
158    }
159
160    ctx.assign_type(expr.syntax(), list_type, struct_type.inner);
161
162    Value::new(hir, ty)
163}