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