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