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