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