Fork of daniellemaywood.uk/gleam — Wasm codegen work
16 kB
481 lines
1use super::*;
2use crate::analyse::Inferred;
3use crate::type_::{FieldMap, HasType};
4
5pub type TypedConstant = Constant<Arc<Type>, EcoString>;
6pub type UntypedConstant = Constant<(), ()>;
7
8// TODO: remove RecordTag paramter
9#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
10pub enum Constant<T, RecordTag> {
11 Int {
12 location: SrcSpan,
13 value: EcoString,
14 int_value: BigInt,
15 },
16
17 Float {
18 location: SrcSpan,
19 value: EcoString,
20 float_value: LiteralFloatValue,
21 },
22
23 String {
24 location: SrcSpan,
25 value: EcoString,
26 },
27
28 Tuple {
29 location: SrcSpan,
30 elements: Vec<Self>,
31 type_: T,
32 },
33
34 List {
35 location: SrcSpan,
36 elements: Vec<Self>,
37 type_: T,
38 tail: Option<Box<Self>>,
39 },
40
41 Record {
42 location: SrcSpan,
43 module: Option<(EcoString, SrcSpan)>,
44 name: EcoString,
45 arguments: Vec<CallArg<Self>>,
46 tag: RecordTag,
47 type_: T,
48 field_map: Inferred<FieldMap>,
49 record_constructor: Option<Box<ValueConstructor>>,
50 },
51
52 RecordUpdate {
53 location: SrcSpan,
54 constructor_location: SrcSpan,
55 module: Option<(EcoString, SrcSpan)>,
56 name: EcoString,
57 record: RecordBeingUpdated<Self>,
58 arguments: Vec<RecordUpdateArg<Self>>,
59 tag: RecordTag,
60 type_: T,
61 field_map: Inferred<FieldMap>,
62 },
63
64 BitArray {
65 location: SrcSpan,
66 segments: Vec<BitArraySegment<Self, T>>,
67 },
68
69 Var {
70 location: SrcSpan,
71 module: Option<(EcoString, SrcSpan)>,
72 name: EcoString,
73 constructor: Option<Box<ValueConstructor>>,
74 type_: T,
75 },
76
77 StringConcatenation {
78 location: SrcSpan,
79 left: Box<Self>,
80 right: Box<Self>,
81 },
82
83 /// A placeholder constant used to allow module analysis to continue
84 /// even when there are type errors. Should never end up in generated code.
85 Invalid {
86 location: SrcSpan,
87 type_: T,
88 /// Extra information about the invalid expression, useful for providing
89 /// addition help or information, such as code actions to fix invalid
90 /// states.
91 extra_information: Option<InvalidExpression>,
92 },
93}
94
95impl TypedConstant {
96 pub fn type_(&self) -> Arc<Type> {
97 match self {
98 Constant::Int { .. } => type_::int(),
99 Constant::Float { .. } => type_::float(),
100 Constant::String { .. } | Constant::StringConcatenation { .. } => type_::string(),
101 Constant::BitArray { .. } => type_::bit_array(),
102
103 Constant::List { type_, .. }
104 | Constant::Tuple { type_, .. }
105 | Constant::Record { type_, .. }
106 | Constant::RecordUpdate { type_, .. }
107 | Constant::Var { type_, .. }
108 | Constant::Invalid { type_, .. } => type_.clone(),
109 }
110 }
111
112 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
113 if !self.location().contains(byte_index) {
114 return None;
115 }
116 Some(match self {
117 Constant::Int { .. }
118 | Constant::Float { .. }
119 | Constant::String { .. }
120 | Constant::Invalid { .. } => Located::Constant(self),
121 Constant::Var {
122 module: Some((module_alias, location)),
123 constructor: Some(constructor),
124 ..
125 }
126 | Constant::Record {
127 module: Some((module_alias, location)),
128 record_constructor: Some(constructor),
129 ..
130 } if location.contains(byte_index) => match &constructor.variant {
131 ValueConstructorVariant::ModuleConstant { module, .. }
132 | ValueConstructorVariant::ModuleFn { module, .. }
133 | ValueConstructorVariant::Record { module, .. } => Located::ModuleName {
134 location: *location,
135 module_name: module.clone(),
136 module_alias: module_alias.clone(),
137 layer: Layer::Value,
138 },
139 ValueConstructorVariant::LocalVariable { .. } => Located::Constant(self),
140 },
141 Constant::Var { .. } => Located::Constant(self),
142 Constant::Tuple { elements, .. } => elements
143 .iter()
144 .find_map(|element| element.find_node(byte_index))
145 .unwrap_or(Located::Constant(self)),
146 Constant::List { elements, tail, .. } => elements
147 .iter()
148 .find_map(|element| element.find_node(byte_index))
149 .or_else(|| tail.as_deref().and_then(|tail| tail.find_node(byte_index)))
150 .unwrap_or(Located::Constant(self)),
151 Constant::Record { arguments, .. } => arguments
152 .iter()
153 .find_map(|argument| argument.find_node(byte_index))
154 .unwrap_or(Located::Constant(self)),
155 Constant::RecordUpdate {
156 record, arguments, ..
157 } => record
158 .base
159 .find_node(byte_index)
160 .or_else(|| {
161 arguments
162 .iter()
163 .find_map(|arg| arg.value.find_node(byte_index))
164 })
165 .unwrap_or(Located::Constant(self)),
166 Constant::BitArray { segments, .. } => segments
167 .iter()
168 .find_map(|segment| segment.find_node(byte_index))
169 .unwrap_or(Located::Constant(self)),
170 Constant::StringConcatenation { left, right, .. } => left
171 .find_node(byte_index)
172 .or_else(|| right.find_node(byte_index))
173 .unwrap_or(Located::Constant(self)),
174 })
175 }
176
177 pub fn definition_location(&self) -> Option<DefinitionLocation> {
178 match self {
179 Constant::Int { .. }
180 | Constant::Float { .. }
181 | Constant::String { .. }
182 | Constant::Tuple { .. }
183 | Constant::List { .. }
184 | Constant::BitArray { .. }
185 | Constant::StringConcatenation { .. }
186 | Constant::Invalid { .. } => None,
187 Constant::Record {
188 record_constructor: value_constructor,
189 ..
190 }
191 | Constant::Var {
192 constructor: value_constructor,
193 ..
194 } => value_constructor
195 .as_ref()
196 .map(|constructor| constructor.definition_location()),
197 Constant::RecordUpdate { .. } => None,
198 }
199 }
200
201 pub(crate) fn referenced_variables(&self) -> im::HashSet<&EcoString> {
202 match self {
203 Constant::Var { name, .. } => im::hashset![name],
204
205 Constant::Invalid { .. }
206 | Constant::Int { .. }
207 | Constant::Float { .. }
208 | Constant::String { .. } => im::hashset![],
209
210 Constant::List { elements, .. } | Constant::Tuple { elements, .. } => elements
211 .iter()
212 .map(|element| element.referenced_variables())
213 .fold(im::hashset![], im::HashSet::union),
214
215 Constant::Record { arguments, .. } => arguments
216 .iter()
217 .map(|argument| argument.value.referenced_variables())
218 .fold(im::hashset![], im::HashSet::union),
219
220 Constant::RecordUpdate {
221 record, arguments, ..
222 } => record.base.referenced_variables().union(
223 arguments
224 .iter()
225 .map(|arg| arg.value.referenced_variables())
226 .fold(im::hashset![], im::HashSet::union),
227 ),
228
229 Constant::BitArray { segments, .. } => segments
230 .iter()
231 .map(|segment| {
232 segment
233 .options
234 .iter()
235 .map(|option| option.referenced_variables())
236 .fold(segment.value.referenced_variables(), im::HashSet::union)
237 })
238 .fold(im::hashset![], im::HashSet::union),
239
240 Constant::StringConcatenation { left, right, .. } => left
241 .referenced_variables()
242 .union(right.referenced_variables()),
243 }
244 }
245
246 pub(crate) fn syntactically_eq(&self, other: &Self) -> bool {
247 match (self, other) {
248 (Constant::Int { int_value: n, .. }, Constant::Int { int_value: m, .. }) => n == m,
249 (Constant::Int { .. }, _) => false,
250
251 (Constant::Float { float_value: n, .. }, Constant::Float { float_value: m, .. }) => {
252 n == m
253 }
254 (Constant::Float { .. }, _) => false,
255
256 (
257 Constant::String { value, .. },
258 Constant::String {
259 value: other_value, ..
260 },
261 ) => value == other_value,
262 (Constant::String { .. }, _) => false,
263
264 (
265 Constant::Tuple { elements, .. },
266 Constant::Tuple {
267 elements: other_elements,
268 ..
269 },
270 ) => pairwise_all(elements, other_elements, |(one, other)| {
271 one.syntactically_eq(other)
272 }),
273 (Constant::Tuple { .. }, _) => false,
274
275 (
276 Constant::List { elements, .. },
277 Constant::List {
278 elements: other_elements,
279 ..
280 },
281 ) => pairwise_all(elements, other_elements, |(one, other)| {
282 one.syntactically_eq(other)
283 }),
284 (Constant::List { .. }, _) => false,
285
286 (
287 Constant::Record {
288 module,
289 name,
290 arguments,
291 ..
292 },
293 Constant::Record {
294 module: other_module,
295 name: other_name,
296 arguments: other_arguments,
297 ..
298 },
299 ) => {
300 let modules_are_equal = match (module, other_module) {
301 (None, None) => true,
302 (None, Some(_)) | (Some(_), None) => false,
303 (Some((one, _)), Some((other, _))) => one == other,
304 };
305
306 modules_are_equal
307 && name == other_name
308 && pairwise_all(arguments, other_arguments, |(one, other)| {
309 one.label == other.label && one.value.syntactically_eq(&other.value)
310 })
311 }
312 (Constant::Record { .. }, _) => false,
313
314 (
315 Constant::RecordUpdate {
316 module,
317 name,
318 record,
319 arguments,
320 ..
321 },
322 Constant::RecordUpdate {
323 module: other_module,
324 name: other_name,
325 record: other_record,
326 arguments: other_arguments,
327 ..
328 },
329 ) => {
330 let modules_are_equal = match (module, other_module) {
331 (None, None) => true,
332 (None, Some(_)) | (Some(_), None) => false,
333 (Some((one, _)), Some((other, _))) => one == other,
334 };
335
336 modules_are_equal
337 && name == other_name
338 && record.base.syntactically_eq(&other_record.base)
339 && pairwise_all(arguments, other_arguments, |(one, other)| {
340 one.label == other.label && one.value.syntactically_eq(&other.value)
341 })
342 }
343 (Constant::RecordUpdate { .. }, _) => false,
344
345 (
346 Constant::BitArray { segments, .. },
347 Constant::BitArray {
348 segments: other_segments,
349 ..
350 },
351 ) => pairwise_all(segments, other_segments, |(one, other)| {
352 one.syntactically_eq(other)
353 }),
354 (Constant::BitArray { .. }, _) => false,
355
356 (
357 Constant::Var { module, name, .. },
358 Constant::Var {
359 module: other_module,
360 name: other_name,
361 ..
362 },
363 ) => {
364 let modules_are_equal = match (module, other_module) {
365 (None, None) => true,
366 (None, Some(_)) | (Some(_), None) => false,
367 (Some((one, _)), Some((other, _))) => one == other,
368 };
369
370 modules_are_equal && name == other_name
371 }
372 (Constant::Var { .. }, _) => false,
373
374 (
375 Constant::StringConcatenation { left, right, .. },
376 Constant::StringConcatenation {
377 left: other_left,
378 right: other_right,
379 ..
380 },
381 ) => left.syntactically_eq(other_left) && right.syntactically_eq(other_right),
382 (Constant::StringConcatenation { .. }, _) => false,
383
384 (Constant::Invalid { .. }, _) => false,
385 }
386 }
387
388 pub fn list_elements(&self) -> Option<Vec<&Self>> {
389 match self {
390 Constant::List { elements, tail, .. } => Some(
391 elements
392 .iter()
393 .chain(
394 tail.as_deref()
395 .and_then(|tail| tail.list_elements())
396 .unwrap_or_default()
397 .into_iter(),
398 )
399 .collect(),
400 ),
401 Constant::Var {
402 constructor: Some(constructor),
403 ..
404 } => match &constructor.variant {
405 ValueConstructorVariant::ModuleConstant { literal, .. } => literal.list_elements(),
406 ValueConstructorVariant::LocalVariable { .. }
407 | ValueConstructorVariant::ModuleFn { .. }
408 | ValueConstructorVariant::Record { .. } => None,
409 },
410
411 Constant::Int { .. }
412 | Constant::Float { .. }
413 | Constant::String { .. }
414 | Constant::Tuple { .. }
415 | Constant::Record { .. }
416 | Constant::RecordUpdate { .. }
417 | Constant::BitArray { .. }
418 | Constant::StringConcatenation { .. }
419 | Constant::Var { .. }
420 | Constant::Invalid { .. } => None,
421 }
422 }
423}
424
425impl HasType for TypedConstant {
426 fn type_(&self) -> Arc<Type> {
427 self.type_()
428 }
429}
430
431impl<A, B> Constant<A, B> {
432 pub fn location(&self) -> SrcSpan {
433 match self {
434 Constant::Int { location, .. }
435 | Constant::List { location, .. }
436 | Constant::Float { location, .. }
437 | Constant::Tuple { location, .. }
438 | Constant::String { location, .. }
439 | Constant::Record { location, .. }
440 | Constant::RecordUpdate { location, .. }
441 | Constant::BitArray { location, .. }
442 | Constant::Var { location, .. }
443 | Constant::Invalid { location, .. }
444 | Constant::StringConcatenation { location, .. } => *location,
445 }
446 }
447
448 #[must_use]
449 pub fn can_have_multiple_per_line(&self) -> bool {
450 match self {
451 Constant::Int { .. }
452 | Constant::Float { .. }
453 | Constant::String { .. }
454 | Constant::Var { .. } => true,
455
456 Constant::Tuple { .. }
457 | Constant::List { .. }
458 | Constant::Record { .. }
459 | Constant::RecordUpdate { .. }
460 | Constant::BitArray { .. }
461 | Constant::StringConcatenation { .. }
462 | Constant::Invalid { .. } => false,
463 }
464 }
465}
466
467impl<A, B> HasLocation for Constant<A, B> {
468 fn location(&self) -> SrcSpan {
469 self.location()
470 }
471}
472
473impl<A, B> bit_array::GetLiteralValue for Constant<A, B> {
474 fn as_int_literal(&self) -> Option<BigInt> {
475 if let Constant::Int { int_value, .. } = self {
476 Some(int_value.clone())
477 } else {
478 None
479 }
480 }
481}