Appearance
| 1 | // FIXME: correct this namespace: | |
| 2 | namespace Syntax.Trees.Variables is | |
| 3 | ||
| 4 | use Source | |
| 5 | ||
| 6 | use Ghul.Pipes | |
| 7 | ||
| 8 | class VARIABLE: Trees.Definitions.Definition, ScopeCarrier is | |
| 9 | scope: Semantic.Scope? public | |
| 10 | ||
| 11 | left: VariableLeft | |
| 12 | // Null on a destructure-pattern variable — only a SIMPLE_VARIABLE_LEFT | |
| 13 | // has a single name. Callers that work only on simple variables can | |
| 14 | // check `is_simple_name` first; callers that need to iterate every | |
| 15 | // bound identifier should use `names`. | |
| 16 | name: Identifiers.Identifier? => left.name | |
| 17 | ||
| 18 | // every concrete VariableLeft subclass overrides names to non-null | |
| 19 | names: Collections.Iterator[Identifiers.Identifier] => left.names! | |
| 20 | ||
| 21 | type_expression: TypeExpressions.TypeExpression | |
| 22 | initializer: Expressions.Expression? | |
| 23 | is_static: bool | |
| 24 | is_explicit_type: bool | |
| 25 | is_variable: bool => true | |
| 26 | want_dispose: bool | |
| 27 | is_mutable_marked: bool | |
| 28 | // Set on the binding of an `if let` arm so the compile pass can | |
| 29 | // treat the initializer's optionality as already-checked: a | |
| 30 | // refutable destructure unwraps `T?` to `T` before resolving | |
| 31 | // element members. | |
| 32 | is_refutable: bool | |
| 33 | // Set on every formal-argument VARIABLE. A formal argument's | |
| 34 | // initializer is its declared default value (consumed by | |
| 35 | // callers), not code to run at function entry. | |
| 36 | is_argument: bool | |
| 37 | // Set on a VARIABLE that represents the `..` splice marker in | |
| 38 | // a secondary `init` formal-argument list. Carries only its | |
| 39 | // location; the rewrite pass expands it into the surrounding | |
| 40 | // class's primary parameters before any downstream phase | |
| 41 | // observes it. | |
| 42 | is_splice: bool | |
| 43 | ||
| 44 | // Set on the variables of a `let` that is a direct top-level | |
| 45 | // statement of a file's synthesised entry point. Declare-members | |
| 46 | // declares these into the file's namespace as top-level variables | |
| 47 | // (static fields on the globals container) instead of as locals | |
| 48 | // of the entry, so sibling global functions can read them. | |
| 49 | is_top_level: bool public | |
| 50 | ||
| 51 | // Set on a variant field VARIABLE inserted by expanding a `..` | |
| 52 | // splice against the enclosing union's primary-constructor | |
| 53 | // parameters. The variant's synthesised init forwards these | |
| 54 | // names to super.init(...) and skips the self.<field>= | |
| 55 | // assignment the union base already performs. | |
| 56 | is_inherited_primary: bool | |
| 57 | ||
| 58 | // The union's own member an inherited field is read through. | |
| 59 | // It differs from the parameter's name where the header | |
| 60 | // declares the parameter private, which captures it under an | |
| 61 | // underscore. | |
| 62 | inherited_member_name: string? | |
| 63 | ||
| 64 | // Set on a VARIABLE synthesised as the backing field of an | |
| 65 | // auto-property. It carries the property's own source location | |
| 66 | // (so hover and go-to-definition on the backing field still work) | |
| 67 | // rather than LOCATION.internal, so declare-members needs this to | |
| 68 | // mark the resulting symbol synthesised. | |
| 69 | is_synthesized: bool | |
| 70 | ||
| 71 | // Populated only for primary-ctor parameters. Carries the | |
| 72 | // trailing modifier suffixes (`public` / `field` / `init`) that | |
| 73 | // describe the auto-generated body field/property the rewrite | |
| 74 | // pass will synthesise for this parameter. Null in every other | |
| 75 | // variable-parsing context. | |
| 76 | modifiers: Modifiers.LIST? public | |
| 77 | ||
| 78 | // Attribute pragmas (`@Foo() name: T`) written directly before a | |
| 79 | // formal-argument VARIABLE — only ever populated when the parser | |
| 80 | // is inside a function/method parameter list (see | |
| 81 | // CONTEXT.in_formal_arguments); null everywhere else. | |
| 82 | pragmas: Collections.LIST[Pragmas.PRAGMA]? public | |
| 83 | ||
| 84 | init( | |
| 85 | location: LOCATION, | |
| 86 | left: VariableLeft, | |
| 87 | type_expression: TypeExpressions.TypeExpression, | |
| 88 | is_static: bool, | |
| 89 | is_explicit_type: bool, | |
| 90 | initializer: Expressions.Expression? | |
| 91 | ) is | |
| 92 | super.init(location) | |
| 93 | ||
| 94 | ||
| 95 | self.left = left | |
| 96 | self.type_expression = type_expression | |
| 97 | self.is_static = is_static | |
| 98 | self.is_explicit_type = is_explicit_type | |
| 99 | self.initializer = initializer | |
| 100 | si | |
| 101 | ||
| 102 | init( | |
| 103 | location: LOCATION, | |
| 104 | name: Identifiers.Identifier, | |
| 105 | type_expression: TypeExpressions.TypeExpression, | |
| 106 | is_static: bool, | |
| 107 | is_explicit_type: bool, | |
| 108 | initializer: Expressions.Expression? | |
| 109 | ) is | |
| 110 | init( | |
| 111 | location, | |
| 112 | SIMPLE_VARIABLE_LEFT(location, name), | |
| 113 | type_expression, | |
| 114 | is_static, | |
| 115 | is_explicit_type, | |
| 116 | initializer | |
| 117 | ) | |
| 118 | si | |
| 119 | ||
| 120 | set_type_expression(type_expression: TypeExpressions.TypeExpression) is | |
| 121 | self.type_expression = type_expression | |
| 122 | si | |
| 123 | ||
| 124 | mark_want_dispose() is | |
| 125 | want_dispose = true | |
| 126 | si | |
| 127 | ||
| 128 | mark_mutable() is | |
| 129 | is_mutable_marked = true | |
| 130 | si | |
| 131 | ||
| 132 | mark_refutable() is | |
| 133 | is_refutable = true | |
| 134 | si | |
| 135 | ||
| 136 | mark_argument() is | |
| 137 | is_argument = true | |
| 138 | si | |
| 139 | ||
| 140 | mark_splice() is | |
| 141 | is_splice = true | |
| 142 | si | |
| 143 | ||
| 144 | mark_inherited_primary(member_name: string) is | |
| 145 | is_inherited_primary = true | |
| 146 | inherited_member_name = member_name | |
| 147 | si | |
| 148 | ||
| 149 | mark_synthesized() is | |
| 150 | is_synthesized = true | |
| 151 | si | |
| 152 | ||
| 153 | set_modifiers(modifiers: Modifiers.LIST) is | |
| 154 | self.modifiers = modifiers | |
| 155 | si | |
| 156 | ||
| 157 | set_pragmas(pragmas: Collections.LIST[Pragmas.PRAGMA]) is | |
| 158 | self.pragmas = pragmas | |
| 159 | si | |
| 160 | ||
| 161 | copy() -> VARIABLE is | |
| 162 | assert initializer == null else "cannot copy a variable node with non null initializer" | |
| 163 | ||
| 164 | let result = | |
| 165 | VARIABLE( | |
| 166 | location, | |
| 167 | left.copy(), | |
| 168 | type_expression.copy(), | |
| 169 | is_static, | |
| 170 | is_explicit_type, | |
| 171 | null | |
| 172 | ) | |
| 173 | ||
| 174 | if let self_pragmas = pragmas then | |
| 175 | result.set_pragmas(self_pragmas) | |
| 176 | fi | |
| 177 | ||
| 178 | return result | |
| 179 | si | |
| 180 | ||
| 181 | accept(visitor: Visitor) is | |
| 182 | visitor.visit(self) | |
| 183 | si | |
| 184 | ||
| 185 | walk(visitor: Visitor) is | |
| 186 | // Inherited-primary entries in a variant's field list | |
| 187 | // exist purely to feed init synthesis (the variant's | |
| 188 | // init forwards them to super.init). The union base | |
| 189 | // owns the storage and visits all the symbol-side | |
| 190 | // processing (declare_symbols, resolve types, etc.) | |
| 191 | // through the union's own field declarations. Walking | |
| 192 | // them at the variant level would double-process the | |
| 193 | // same name and trip "set type twice" / shadow checks. | |
| 194 | if is_inherited_primary then | |
| 195 | return | |
| 196 | fi | |
| 197 | if !visitor.pre(self) then | |
| 198 | if pragmas? then | |
| 199 | for pragma in pragmas do | |
| 200 | pragma.walk(visitor) | |
| 201 | od | |
| 202 | fi | |
| 203 | left.walk(visitor) | |
| 204 | type_expression.walk(visitor) | |
| 205 | if initializer? then | |
| 206 | initializer.walk(visitor) | |
| 207 | fi | |
| 208 | fi | |
| 209 | accept(visitor) | |
| 210 | si | |
| 211 | si | |
| 212 | ||
| 213 | class VariableLeft: Trees.Node is | |
| 214 | is_simple_name: bool => false | |
| 215 | name: Identifiers.Identifier? => null | |
| 216 | names: Collections.Iterator[Identifiers.Identifier]? => null | |
| 217 | elements: Collections.List[VariableLeft]? => null | |
| 218 | ||
| 219 | // Per-element type ascription. Allowed on any element of a | |
| 220 | // destructure pattern, at any nesting depth. In an ordinary | |
| 221 | // `let`, this is a static type assertion on the bound slot; | |
| 222 | // in an `if let` arm (parent VARIABLE.is_refutable), it is | |
| 223 | // additionally a runtime narrowing test on that element. | |
| 224 | type_expression: TypeExpressions.TypeExpression? | |
| 225 | ||
| 226 | // Set on every element of a by-name destructure group: | |
| 227 | // `(local = field, …) = source` records `field` here so the | |
| 228 | // resolver pulls `source.field` rather than the positional | |
| 229 | // slot. Null on positional-group elements. The parser | |
| 230 | // enforces all-or-nothing per `(...)` group; mixing is a | |
| 231 | // parse error. | |
| 232 | source_field_name: Identifiers.Identifier? public | |
| 233 | ||
| 234 | set_source_field_name(name: Identifiers.Identifier) is | |
| 235 | self.source_field_name = name | |
| 236 | si | |
| 237 | ||
| 238 | // Compile-expressions output for this node lives in the pass's | |
| 239 | // own VARIABLE_LEFT_STATE_STORE, keyed by this node, not here. | |
| 240 | ||
| 241 | // Propagated from the parent VARIABLE for `if let` arms. | |
| 242 | is_refutable: bool public | |
| 243 | ||
| 244 | // Propagated from the parent VARIABLE when it's a formal | |
| 245 | // argument. A destructured formal argument has no initializer | |
| 246 | // to derive a value from - the leaves' types are assigned | |
| 247 | // directly from the parameter's aggregate type by resolve- | |
| 248 | // explicit-types, and generate-il sources the unpack from the | |
| 249 | // synthesised parameter symbol rather than a walked value - so | |
| 250 | // compile-expressions' usual initializer-driven handling of a | |
| 251 | // destructure pattern doesn't apply and skips this subtree. | |
| 252 | is_argument_left: bool public | |
| 253 | ||
| 254 | init(location: LOCATION) is | |
| 255 | super.init(location) | |
| 256 | si | |
| 257 | ||
| 258 | set_type_expression(type_expression: TypeExpressions.TypeExpression) is | |
| 259 | self.type_expression = type_expression | |
| 260 | si | |
| 261 | ||
| 262 | // Propagate `is_refutable` to every leaf of a destructure tree | |
| 263 | // so a `LITERAL_VARIABLE_LEFT` nested inside a tuple destructure | |
| 264 | // (`if let (1, y) = pair`) knows it sits in a refutable | |
| 265 | // context and is not a silent no-op. | |
| 266 | mark_refutable_recursive() is | |
| 267 | is_refutable = true | |
| 268 | si | |
| 269 | ||
| 270 | // Propagate `is_argument_left` to every leaf, same shape as | |
| 271 | // mark_refutable_recursive above. | |
| 272 | mark_argument_recursive() is | |
| 273 | is_argument_left = true | |
| 274 | si | |
| 275 | ||
| 276 | // True when this leaf's own `: T` ascription (if any) performs | |
| 277 | // a runtime test that can genuinely fail — a reference-type | |
| 278 | // narrow (an isinst test), or a value-type ascription that is | |
| 279 | // itself option-shaped (`T?` / NULLABLE[T], tested via | |
| 280 | // presence). A non-optional value-type ascription (`i: int`) | |
| 281 | // can never fail, so it contributes no refutability — the | |
| 282 | // same reasoning `PATTERN_CHECKER.should_emit_value_type_narrow_error` | |
| 283 | // applies to the top-level ascription. | |
| 284 | _ascription_is_refutable: bool is | |
| 285 | let ascribed = type_expression?.type | |
| 286 | ||
| 287 | if !ascribed? then | |
| 288 | return false | |
| 289 | fi | |
| 290 | ||
| 291 | if !ascribed.is_value_type then | |
| 292 | return true | |
| 293 | fi | |
| 294 | ||
| 295 | return ascribed.find_member("has_value")? /\ ascribed.find_member("value")? | |
| 296 | si | |
| 297 | ||
| 298 | // True when this leaf, or any leaf nested inside it, carries | |
| 299 | // its own runtime refutability — a literal/null/enum-member | |
| 300 | // leaf, or a genuinely-refutable `: T` ascription — independent | |
| 301 | // of whether the top-level scrutinee itself is optional. An | |
| 302 | // `if let` pattern needs at least one of these somewhere in | |
| 303 | // its shape, or the arm can never fail to match. | |
| 304 | // DESTRUCTURING_VARIABLE_LEFT additionally recurses into its | |
| 305 | // elements. | |
| 306 | has_intrinsic_refutability: bool => _ascription_is_refutable | |
| 307 | ||
| 308 | // True when this node, or any nested destructure group at any | |
| 309 | // depth, is a by-name group (source_field_name set on its own | |
| 310 | // first element - the parser enforces all-or-nothing per | |
| 311 | // group, so checking the first element is enough for that | |
| 312 | // group). Used to reject named destructuring anywhere in a | |
| 313 | // formal-argument pattern, not just at its outermost level. | |
| 314 | has_named_group: bool => false | |
| 315 | ||
| 316 | get_names_into(into: Collections.MutableList[Identifiers.Identifier]) | |
| 317 | ||
| 318 | copy() -> VariableLeft | |
| 319 | ||
| 320 | copy_base_values_from(other: VariableLeft) is | |
| 321 | if let other_type_expression = other.type_expression then | |
| 322 | type_expression = other_type_expression.copy() | |
| 323 | fi | |
| 324 | ||
| 325 | if let other_source_field_name = other.source_field_name then | |
| 326 | source_field_name = other_source_field_name.copy() | |
| 327 | fi | |
| 328 | si | |
| 329 | si | |
| 330 | ||
| 331 | class SIMPLE_VARIABLE_LEFT: VariableLeft is | |
| 332 | name: Identifiers.Identifier | |
| 333 | is_simple_name: bool => true | |
| 334 | ||
| 335 | elements: Collections.List[VariableLeft] => System.Array.empty`[VariableLeft]() | |
| 336 | names: Collections.Iterator[Identifiers.Identifier] => [name].iterator | |
| 337 | ||
| 338 | init( | |
| 339 | location: LOCATION, | |
| 340 | name: Identifiers.Identifier | |
| 341 | ) is | |
| 342 | super.init(location) | |
| 343 | ||
| 344 | self.name = name | |
| 345 | si | |
| 346 | ||
| 347 | get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is | |
| 348 | into.add(name) | |
| 349 | si | |
| 350 | ||
| 351 | accept(visitor: Visitor) is | |
| 352 | visitor.visit(self) | |
| 353 | si | |
| 354 | ||
| 355 | walk(visitor: Visitor) is | |
| 356 | if !visitor.pre(self) then | |
| 357 | name.walk(visitor) | |
| 358 | ||
| 359 | if let self.type_expression? then | |
| 360 | type_expression.walk(visitor) | |
| 361 | fi | |
| 362 | fi | |
| 363 | ||
| 364 | accept(visitor) | |
| 365 | si | |
| 366 | ||
| 367 | copy() -> VariableLeft is | |
| 368 | let result = SIMPLE_VARIABLE_LEFT(location, name) | |
| 369 | result.copy_base_values_from(self) | |
| 370 | return result | |
| 371 | si | |
| 372 | si | |
| 373 | ||
| 374 | // A literal / `null` / enum-member leaf inside a destructure | |
| 375 | // pattern. Has no binding name — at runtime, the source's | |
| 376 | // corresponding element is value-equality tested against the | |
| 377 | // wrapped expression, and the arm only matches when the test | |
| 378 | // succeeds. Visible only on the LHS of refutable bindings | |
| 379 | // (`if let` / `case`-when patterns); a plain `let` never has | |
| 380 | // a refutable element. | |
| 381 | class LITERAL_VARIABLE_LEFT: VariableLeft is | |
| 382 | expression: Expressions.Expression | |
| 383 | ||
| 384 | // Whether the source wrote the `~` match marker. It changes | |
| 385 | // nothing about what the leaf does — a bare name only reaches | |
| 386 | // this node by being marked, and every other leaf form matches | |
| 387 | // with or without it — but the formatter has to write back | |
| 388 | // what it read. | |
| 389 | is_marked: bool | |
| 390 | ||
| 391 | is_simple_name: bool => false | |
| 392 | elements: Collections.List[VariableLeft] => System.Array.empty`[VariableLeft]() | |
| 393 | names: Collections.Iterator[Identifiers.Identifier] => System.Array.empty`[Identifiers.Identifier]().iterator | |
| 394 | ||
| 395 | // A literal leaf is itself a runtime equality test, so it | |
| 396 | // always provides refutability regardless of any ascription. | |
| 397 | has_intrinsic_refutability: bool => true | |
| 398 | ||
| 399 | init(location: LOCATION, expression: Expressions.Expression, is_marked: bool) is | |
| 400 | super.init(location) | |
| 401 | ||
| 402 | self.expression = expression | |
| 403 | self.is_marked = is_marked | |
| 404 | si | |
| 405 | ||
| 406 | get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is | |
| 407 | si | |
| 408 | ||
| 409 | accept(visitor: Visitor) is | |
| 410 | visitor.visit(self) | |
| 411 | si | |
| 412 | ||
| 413 | walk(visitor: Visitor) is | |
| 414 | if !visitor.pre(self) then | |
| 415 | expression.walk(visitor) | |
| 416 | fi | |
| 417 | ||
| 418 | accept(visitor) | |
| 419 | si | |
| 420 | ||
| 421 | copy() -> VariableLeft is | |
| 422 | let result = LITERAL_VARIABLE_LEFT(location, expression, is_marked) | |
| 423 | result.copy_base_values_from(self) | |
| 424 | return result | |
| 425 | si | |
| 426 | si | |
| 427 | ||
| 428 | class DESTRUCTURING_VARIABLE_LEFT: VariableLeft is | |
| 429 | elements: Collections.List[VariableLeft] | |
| 430 | ||
| 431 | names: Collections.Iterator[Identifiers.Identifier] => _flatten_names() | |
| 432 | ||
| 433 | _flatten_names() -> Ghul.Pipes.Pipe[Identifiers.Identifier] is | |
| 434 | for element in elements do | |
| 435 | let element_names = element.names | |
| 436 | ||
| 437 | if element_names? then | |
| 438 | for n in element_names do | |
| 439 | yield n | |
| 440 | od | |
| 441 | fi | |
| 442 | od | |
| 443 | si | |
| 444 | ||
| 445 | get_names_into(into: Collections.MutableList[Identifiers.Identifier]) is | |
| 446 | for e in elements do | |
| 447 | e.get_names_into(into) | |
| 448 | od | |
| 449 | si | |
| 450 | ||
| 451 | mark_refutable_recursive() is | |
| 452 | is_refutable = true | |
| 453 | for e in elements do | |
| 454 | e.mark_refutable_recursive() | |
| 455 | od | |
| 456 | si | |
| 457 | ||
| 458 | // Own ascription (a nested group's `: T`), if it is itself | |
| 459 | // refutable, or any element's, at any depth. | |
| 460 | has_intrinsic_refutability: bool => | |
| 461 | _ascription_is_refutable \/ (elements |> any(e => e.has_intrinsic_refutability)) | |
| 462 | ||
| 463 | mark_argument_recursive() is | |
| 464 | is_argument_left = true | |
| 465 | for e in elements do | |
| 466 | e.mark_argument_recursive() | |
| 467 | od | |
| 468 | si | |
| 469 | ||
| 470 | has_named_group: bool => | |
| 471 | (elements.count > 0 /\ elements[0].source_field_name?) \/ | |
| 472 | elements |> any(e => e.has_named_group) | |
| 473 | ||
| 474 | init(location: LOCATION, elements: Collections.List[VariableLeft]) is | |
| 475 | super.init(location) | |
| 476 | ||
| 477 | ||
| 478 | self.elements = elements | |
| 479 | si | |
| 480 | ||
| 481 | accept(visitor: Visitor) is | |
| 482 | visitor.visit(self) | |
| 483 | si | |
| 484 | ||
| 485 | walk(visitor: Visitor) is | |
| 486 | if !visitor.pre(self) then | |
| 487 | for e in elements do | |
| 488 | e.walk(visitor) | |
| 489 | od | |
| 490 | ||
| 491 | if let self.type_expression? then | |
| 492 | type_expression.walk(visitor) | |
| 493 | fi | |
| 494 | fi | |
| 495 | accept(visitor) | |
| 496 | si | |
| 497 | ||
| 498 | copy() -> VariableLeft is | |
| 499 | let new_elements = Collections.LIST[VariableLeft](elements.count) | |
| 500 | ||
| 501 | for e in elements do | |
| 502 | new_elements.add(e.copy()) | |
| 503 | od | |
| 504 | ||
| 505 | let result = DESTRUCTURING_VARIABLE_LEFT(location, new_elements) | |
| 506 | result.copy_base_values_from(self) | |
| 507 | return result | |
| 508 | si | |
| 509 | si | |
| 510 | si |