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
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}