Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / ast / constant.rs
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}