rue_compiler/compile/
field.rs1use log::debug;
2use rue_diagnostic::DiagnosticKind;
3use rue_hir::{Hir, TypePath, UnaryOp, Value};
4use rue_parser::SyntaxToken;
5use rue_types::{Type, Union};
6
7use crate::{Compiler, GetTextRange};
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum Field<'a> {
11 Named(&'a str),
12 First,
13 Rest,
14}
15
16#[derive(Debug, Clone)]
17pub enum FieldResult {
18 Value(Value),
19 Unknown,
20 Error,
21}
22
23pub fn compile_named_field(ctx: &mut Compiler, expr: &Value, name: &SyntaxToken) -> Value {
24 match compile_field(ctx, expr.clone(), &Field::Named(name.text())) {
25 FieldResult::Value(value) => value,
26 FieldResult::Unknown => {
27 debug!("Unresolved field access due to unknown field");
28 let type_name = ctx.type_name(expr.ty);
29 ctx.diagnostic(
30 name,
31 DiagnosticKind::UnknownField(name.text().to_string(), type_name),
32 );
33 ctx.builtins().unresolved.clone()
34 }
35 FieldResult::Error => {
36 debug!("Unresolved field access due to missing field in underlying struct type");
37 let type_name = ctx.type_name(expr.ty);
38 ctx.diagnostic(
39 name,
40 DiagnosticKind::MissingField(name.text().to_string(), type_name),
41 );
42 ctx.builtins().unresolved.clone()
43 }
44 }
45}
46
47pub fn compile_pair_fields(
48 ctx: &mut Compiler,
49 node: &impl GetTextRange,
50 expr: &Value,
51) -> (Value, Value) {
52 let first = compile_field(ctx, expr.clone(), &Field::First);
53 let rest = compile_field(ctx, expr.clone(), &Field::Rest);
54
55 let (FieldResult::Value(first), FieldResult::Value(rest)) = (first, rest) else {
56 let name = ctx.type_name(expr.ty);
57 ctx.diagnostic(node, DiagnosticKind::CannotDestructurePair(name));
58 return (
59 ctx.builtins().unresolved.clone(),
60 ctx.builtins().unresolved.clone(),
61 );
62 };
63
64 (first, rest)
65}
66
67pub fn compile_field(ctx: &mut Compiler, expr: Value, name: &Field<'_>) -> FieldResult {
68 let ty = rue_types::unwrap_semantic(ctx.types_mut(), expr.ty, true);
69
70 match ctx.ty(ty).clone() {
71 Type::Apply(_) | Type::Alias(_) | Type::Ref(_) => unreachable!(),
72 Type::Unresolved => FieldResult::Unknown,
73 Type::Generic(_) | Type::Function(_) | Type::Never | Type::Any => FieldResult::Unknown,
74 Type::Atom(_) => {
75 if let &Field::Named(name) = name
76 && name == "length"
77 {
78 let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Strlen, expr.hir));
79 FieldResult::Value(Value::new(hir, ctx.builtins().types.int))
80 } else {
81 FieldResult::Unknown
82 }
83 }
84 Type::Pair(_) | Type::Union(_) => {
85 let pairs = rue_types::extract_pairs(ctx.types_mut(), ty, true);
86
87 match name {
88 Field::Named("first") | Field::First if !pairs.is_empty() => {
89 let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::First, expr.hir));
90
91 let first = if pairs.len() == 1 {
92 pairs[0].first
93 } else {
94 ctx.alloc_type(Type::Union(Union::new(
95 pairs.iter().map(|pair| pair.first).collect(),
96 )))
97 };
98
99 let mut value = Value::new(hir, first);
100
101 if let Some(mut reference) = expr.reference {
102 reference.path.push(TypePath::First);
103 value = value.with_reference(reference);
104 }
105
106 FieldResult::Value(value)
107 }
108 Field::Named("rest") | Field::Rest if !pairs.is_empty() => {
109 let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Rest, expr.hir));
110
111 let rest = if pairs.len() == 1 {
112 pairs[0].rest
113 } else {
114 ctx.alloc_type(Type::Union(Union::new(
115 pairs.iter().map(|pair| pair.rest).collect(),
116 )))
117 };
118
119 let mut value = Value::new(hir, rest);
120
121 if let Some(mut reference) = expr.reference {
122 reference.path.push(TypePath::Rest);
123 value = value.with_reference(reference);
124 }
125
126 FieldResult::Value(value)
127 }
128 _ => FieldResult::Unknown,
129 }
130 }
131 Type::Struct(ty) => {
132 let &Field::Named(name) = name else {
133 return FieldResult::Unknown;
134 };
135
136 let Some(index) = ty.fields.get_index_of(name) else {
137 return FieldResult::Unknown;
138 };
139
140 let mut hir = expr.hir;
141 let mut field_type = ty.inner;
142 let mut reference = expr.reference;
143
144 let needs_first = index + 1 < ty.fields.len() || ty.nil_terminated;
145
146 for i in 0..index {
147 hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Rest, hir));
148
149 let pairs = rue_types::extract_pairs(ctx.types_mut(), field_type, true);
150
151 if pairs.is_empty() || (pairs.len() > 1 && (i + 1 < index || needs_first)) {
152 return FieldResult::Error;
153 }
154
155 field_type = if pairs.len() == 1 {
156 pairs[0].rest
157 } else {
158 ctx.alloc_type(Type::Union(Union::new(
159 pairs.into_iter().map(|pair| pair.rest).collect(),
160 )))
161 };
162
163 if let Some(reference) = reference.as_mut() {
164 reference.path.push(TypePath::Rest);
165 }
166 }
167
168 if needs_first {
169 hir = ctx.alloc_hir(Hir::Unary(UnaryOp::First, hir));
170
171 let pairs = rue_types::extract_pairs(ctx.types_mut(), field_type, true);
172
173 if pairs.is_empty() {
174 return FieldResult::Error;
175 }
176
177 field_type = if pairs.len() == 1 {
178 pairs[0].first
179 } else {
180 ctx.alloc_type(Type::Union(Union::new(
181 pairs.into_iter().map(|pair| pair.first).collect(),
182 )))
183 };
184
185 if let Some(reference) = reference.as_mut() {
186 reference.path.push(TypePath::First);
187 }
188 }
189
190 let mut value = Value::new(hir, field_type);
191
192 if let Some(reference) = reference {
193 value = value.with_reference(reference);
194 }
195
196 FieldResult::Value(value)
197 }
198 }
199}