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