Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Logging | |
| 3 | use Source | |
| 4 | use Trees | |
| 5 | ||
| 6 | // Declares everything below type level: functions, properties, fields, | |
| 7 | // enum members, and the block scopes, locals and closures inside | |
| 8 | // bodies. Runs after resolve-uses - the declare-symbols pass declares | |
| 9 | // only the type-level skeleton (namespaces, types, variants, type | |
| 10 | // parameters), so by the time members are declared every import is | |
| 11 | // bound and an impl/partial block's target name can be reached through | |
| 12 | // a use import and declared anywhere. | |
| 13 | // | |
| 14 | // The inherited ScopedVisitor handlers enter the scopes declare-symbols | |
| 15 | // associated with the type-level nodes; the overrides here declare the | |
| 16 | // member-level symbols into them. impl and partial blocks are handled | |
| 17 | // in place: the target resolves against the block's write site, and the | |
| 18 | // block's members are declared into it through an injection scope. | |
| 19 | class DECLARE_MEMBERS: ScopedVisitor is | |
| 20 | _logger: Logger | |
| 21 | _symbol_definition_listener: Semantic.SymbolDefinitionListener | |
| 22 | ||
| 23 | // Labels seen since the last loop body scope was opened; the | |
| 24 | // LABELLED pre pushes and the wrapped loop's pre declares into | |
| 25 | // that scope (see declare_members_bodies.ghul). | |
| 26 | _pending_labels: Collections.LIST[Trees.Identifiers.Identifier] | |
| 27 | _symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS | |
| 28 | _local_id_generator: IR.LOCAL_ID_GENERATOR | |
| 29 | ||
| 30 | _anon_index: int | |
| 31 | _pragma_scope_stack: PRAGMA_SCOPE_STACK | |
| 32 | _generic_argument_declarer: GENERIC_ARGUMENT_DECLARER | |
| 33 | ||
| 34 | init( | |
| 35 | logger: Logger, | |
| 36 | symbol_table: Semantic.SYMBOL_TABLE, | |
| 37 | namespaces: Semantic.NAMESPACES, | |
| 38 | symbol_definition_listener: Semantic.SymbolDefinitionListener, | |
| 39 | symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS, | |
| 40 | local_id_generator: IR.LOCAL_ID_GENERATOR | |
| 41 | ) is | |
| 42 | super.init(logger, symbol_table, namespaces) | |
| 43 | ||
| 44 | _logger = logger | |
| 45 | _symbol_definition_listener = symbol_definition_listener | |
| 46 | _symbol_use_locations = symbol_use_locations | |
| 47 | _local_id_generator = local_id_generator | |
| 48 | ||
| 49 | _pragma_scope_stack = PRAGMA_SCOPE_STACK() | |
| 50 | _generic_argument_declarer = GENERIC_ARGUMENT_DECLARER(logger, symbol_table, symbol_definition_listener) | |
| 51 | ||
| 52 | _pending_labels = Collections.LIST[Trees.Identifiers.Identifier]() | |
| 53 | si | |
| 54 | ||
| 55 | apply(node: Node) is | |
| 56 | assert _pragma_scope_stack.is_balanced | |
| 57 | ||
| 58 | node.walk(self) | |
| 59 | ||
| 60 | assert _pragma_scope_stack.is_balanced | |
| 61 | si | |
| 62 | ||
| 63 | next_anon_name() -> string is | |
| 64 | let result = "$anon_{_anon_index}" | |
| 65 | _anon_index = _anon_index + 1 | |
| 66 | return result | |
| 67 | si | |
| 68 | ||
| 69 | // `_` is a discard placeholder: every occurrence | |
| 70 | // gets its own unique slot so the redefinition check doesn't | |
| 71 | // fire and there's no shared name for code to read from. Used | |
| 72 | // in `let _ = expr`, tuple destructure (`let (_, _, z) = ...`), | |
| 73 | // and lambda parameters. | |
| 74 | next_discard_name() -> string is | |
| 75 | let result = "$discard_{_anon_index}" | |
| 76 | _anon_index = _anon_index + 1 | |
| 77 | return result | |
| 78 | si | |
| 79 | ||
| 80 | // Name of the single physical parameter slot backing a | |
| 81 | // destructured formal argument - the parameter itself has no | |
| 82 | // user-written name, only its leaves do. | |
| 83 | next_argument_group_name() -> string is | |
| 84 | let result = "$arg_{_anon_index}" | |
| 85 | _anon_index = _anon_index + 1 | |
| 86 | return result | |
| 87 | si | |
| 88 | ||
| 89 | pre(pragma: Definitions.PRAGMA) -> bool is | |
| 90 | _pragma_scope_stack.enter(pragma.pragma) | |
| 91 | ||
| 92 | return false | |
| 93 | si | |
| 94 | ||
| 95 | visit(pragma: Definitions.PRAGMA) is | |
| 96 | let p = pragma.pragma | |
| 97 | ||
| 98 | let name = p.name.to_string() | |
| 99 | ||
| 100 | let is_primitive = name =~ "IL.built_in_type" | |
| 101 | ||
| 102 | if | |
| 103 | is_primitive \/ | |
| 104 | name =~ "IL.name" \/ | |
| 105 | name =~ "IL.name.read" \/ | |
| 106 | name =~ "IL.name.assign" | |
| 107 | then | |
| 108 | if p.arguments.expressions.count != 1 then | |
| 109 | _logger.error(p.arguments.location, "expected one argument") | |
| 110 | return | |
| 111 | fi | |
| 112 | ||
| 113 | let argument = p.arguments.expressions[0] | |
| 114 | ||
| 115 | if !isa Expressions.Literals.STRING(argument) then | |
| 116 | _logger.error(p.arguments.location, "expected a string literal argument") | |
| 117 | return | |
| 118 | fi | |
| 119 | ||
| 120 | let il_name = argument.value_string | |
| 121 | ||
| 122 | // A name reaches metadata as written. Quoting was once | |
| 123 | // how a name colliding with an IL keyword was spelled; | |
| 124 | // now it would just become part of the name. | |
| 125 | if il_name.index_of('\'') >= 0 then | |
| 126 | _logger.error( | |
| 127 | p.arguments.location, "an IL name cannot contain a quote") | |
| 128 | ||
| 129 | return | |
| 130 | fi | |
| 131 | ||
| 132 | let definition mut = pragma.definition | |
| 133 | ||
| 134 | while isa Definitions.PRAGMA(definition) do | |
| 135 | definition = cast Definitions.PRAGMA(definition).definition | |
| 136 | od | |
| 137 | ||
| 138 | let symbol = symbol_for(definition) | |
| 139 | ||
| 140 | if symbol? then | |
| 141 | if isa Semantic.Symbols.Property(symbol) then | |
| 142 | let property = symbol | |
| 143 | ||
| 144 | if name =~ "IL.name.read" then | |
| 145 | property.read_function_il_name_override = il_name | |
| 146 | elif name =~ "IL.name.assign" then | |
| 147 | property.assign_function_il_name_override = il_name | |
| 148 | elif name =~ "IL.name" then | |
| 149 | property.il_name_override = il_name | |
| 150 | ||
| 151 | if !property.read_function_il_name_override? then | |
| 152 | property.read_function_il_name_override = "get_{il_name}" | |
| 153 | fi | |
| 154 | ||
| 155 | if !property.assign_function_il_name_override? then | |
| 156 | property.assign_function_il_name_override = "set_{il_name}" | |
| 157 | fi | |
| 158 | fi | |
| 159 | ||
| 160 | return | |
| 161 | fi | |
| 162 | ||
| 163 | if is_primitive then | |
| 164 | symbol.il_is_primitive_type = true | |
| 165 | fi | |
| 166 | ||
| 167 | symbol.il_name_override = il_name | |
| 168 | fi | |
| 169 | else | |
| 170 | _pragma_scope_stack.leave(p) | |
| 171 | fi | |
| 172 | si | |
| 173 | ||
| 174 | visit(`union: Definitions.UNION) is | |
| 175 | // Pick the default variant now that every variant's fields | |
| 176 | // have been declared: prefer one with the explicit-default | |
| 177 | // flag; otherwise fall back to a sole variant whose own | |
| 178 | // (non-inherited) field count is positive. | |
| 179 | // Cross-assembly unions don't pass through here; they | |
| 180 | // get `default_variant` set by | |
| 181 | // `SYMBOL_FACTORY.materialize_variant` when it sees | |
| 182 | // the `DEFAULT_VARIANT_ATTRIBUTE` marker on a reflected | |
| 183 | // variant. | |
| 184 | let union_symbol: Semantic.Symbols.UNION? mut = null | |
| 185 | if isa Semantic.Symbols.UNION(current_scope) then | |
| 186 | union_symbol = cast Semantic.Symbols.UNION(current_scope) | |
| 187 | fi | |
| 188 | ||
| 189 | if union_symbol? then | |
| 190 | let explicit: Semantic.Symbols.VARIANT? mut = null | |
| 191 | let explicit_count mut = 0 | |
| 192 | let sole_with_own_fields: Semantic.Symbols.VARIANT? mut = null | |
| 193 | let with_own_fields_count mut = 0 | |
| 194 | ||
| 195 | for s in union_symbol.symbols do | |
| 196 | if isa Semantic.Symbols.VARIANT(s) then | |
| 197 | let v = cast Semantic.Symbols.VARIANT(s) | |
| 198 | ||
| 199 | if v.is_default then | |
| 200 | explicit = v | |
| 201 | explicit_count = explicit_count + 1 | |
| 202 | fi | |
| 203 | ||
| 204 | if v.own_field_count > 0 then | |
| 205 | sole_with_own_fields = v | |
| 206 | with_own_fields_count = with_own_fields_count + 1 | |
| 207 | fi | |
| 208 | fi | |
| 209 | od | |
| 210 | ||
| 211 | if explicit_count == 1 then | |
| 212 | union_symbol.default_variant = explicit | |
| 213 | elif explicit_count == 0 /\ with_own_fields_count == 1 then | |
| 214 | union_symbol.default_variant = sole_with_own_fields | |
| 215 | fi | |
| 216 | fi | |
| 217 | ||
| 218 | leave_scope(`union) | |
| 219 | si | |
| 220 | ||
| 221 | pre(variant: Definitions.VARIANT) -> bool is | |
| 222 | enter_scope(variant) | |
| 223 | ||
| 224 | // Take charge of the walk so inherited-primary names and | |
| 225 | // own-field declarations register on the variant scope | |
| 226 | // interleaved in source position — positional destructure | |
| 227 | // and field-description rendering both consult | |
| 228 | // `_field_names` by index, so the order must match the | |
| 229 | // synthesised init's argument order (which is taken from | |
| 230 | // `variant.fields` in source order). Variables.VARIABLE.walk | |
| 231 | // still skips inherited entries, so own fields go through | |
| 232 | // the normal visit(VARIABLE) → declare_variable path and | |
| 233 | // inherited entries are registered here directly. | |
| 234 | let variant_scope: Semantic.Symbols.VARIANT? mut = null | |
| 235 | if isa Semantic.Symbols.VARIANT(current_declaration_context) then | |
| 236 | variant_scope = cast Semantic.Symbols.VARIANT?(current_declaration_context)! | |
| 237 | fi | |
| 238 | ||
| 239 | for f in variant.fields do | |
| 240 | if f.is_inherited_primary then | |
| 241 | if variant_scope? then | |
| 242 | if let f.name?, field_name = name.name then | |
| 243 | variant_scope.register_inherited_primary_field_name(field_name) | |
| 244 | fi | |
| 245 | fi | |
| 246 | else | |
| 247 | f.walk(self) | |
| 248 | fi | |
| 249 | od | |
| 250 | ||
| 251 | variant.body.walk(self) | |
| 252 | ||
| 253 | return true | |
| 254 | si | |
| 255 | ||
| 256 | // An impl/partial block declares its members into an | |
| 257 | // already-declared target type. The target resolves here, at the | |
| 258 | // block's write site - after resolve-uses, so the name can come | |
| 259 | // through a use import and the target can be declared anywhere. | |
| 260 | // The block gets an injection scope rather than the target's | |
| 261 | // scope directly, so that names its members reference resolve at | |
| 262 | // the write site while the target's members and type parameters | |
| 263 | // stay visible first; the members are then declared by this | |
| 264 | // visitor's ordinary member handlers. | |
| 265 | pre(`partial: Definitions.PARTIAL) -> bool is | |
| 266 | _enter_injection( | |
| 267 | `partial, | |
| 268 | "cannot find type {`partial.name.name} to add members to", | |
| 269 | "cannot add members to imported type" | |
| 270 | ) | |
| 271 | ||
| 272 | `partial.body.walk(self) | |
| 273 | ||
| 274 | return true | |
| 275 | si | |
| 276 | ||
| 277 | pre(`impl: Definitions.IMPL) -> bool is | |
| 278 | _enter_injection( | |
| 279 | `impl, | |
| 280 | "cannot find type {`impl.name.name} to implement an interface for", | |
| 281 | "cannot implement an interface for imported type" | |
| 282 | ) | |
| 283 | ||
| 284 | `impl.body.walk(self) | |
| 285 | ||
| 286 | return true | |
| 287 | si | |
| 288 | ||
| 289 | _enter_injection(block: Definitions.Classy, cannot_find_message: string, imported_target_message: string) is | |
| 290 | let target = cast Semantic.Symbols.Classy?(find_enclosing(block.name)) | |
| 291 | ||
| 292 | if !target? then | |
| 293 | _logger.error(block.name.location, cannot_find_message) | |
| 294 | associate_and_enter_scope(block, _rejecting_scope()) | |
| 295 | elif target.is_reflected then | |
| 296 | // The CLR fixes a compiled type's members and interfaces at | |
| 297 | // its definition, so only a type declared in this compilation | |
| 298 | // can be reopened. | |
| 299 | _logger.error(block.name.location, "{imported_target_message} {target.name}") | |
| 300 | associate_and_enter_scope(block, _rejecting_scope()) | |
| 301 | else | |
| 302 | // The target name is a reference to the target type, so | |
| 303 | // record it: hover, rename and find-references all read | |
| 304 | // these recorded uses. | |
| 305 | _symbol_use_locations.add_symbol_use(block.name.right_location, cast Semantic.Symbols.Symbol(target)) | |
| 306 | ||
| 307 | if let block.name.qualifier? /\ isa Semantic.Symbols.Classy(target.owner) then | |
| 308 | _symbol_use_locations.add_symbol_use( | |
| 309 | qualifier.right_location, | |
| 310 | cast Semantic.Symbols.Symbol(target.owner) | |
| 311 | ) | |
| 312 | fi | |
| 313 | ||
| 314 | associate_and_enter_scope(block, Semantic.INJECTION_SCOPE(target, current_scope)) | |
| 315 | fi | |
| 316 | si | |
| 317 | ||
| 318 | // When a block's target cannot be resolved to a same-assembly type, | |
| 319 | // its members have nowhere valid to live. Route them into a scope | |
| 320 | // that refuses them without another error each, since the target | |
| 321 | // has already been reported, rather than cascading to "no scope | |
| 322 | // found" downstream or declaring them as globals in the enclosing | |
| 323 | // scope. A block scope | |
| 324 | // still chains name lookups to the enclosing scope, so the block's | |
| 325 | // own header references - an impl's interface name - resolve | |
| 326 | // normally, as do the type names in each rejected member's own | |
| 327 | // header. | |
| 328 | _rejecting_scope() -> Semantic.Scope => | |
| 329 | Semantic.REJECTED_BLOCK_SCOPE(current_scope) | |
| 330 | ||
| 331 | pre(enum_member: Definitions.ENUM_MEMBER) -> bool is | |
| 332 | let value: string? mut = null | |
| 333 | ||
| 334 | if enum_member.initializer? then | |
| 335 | let i = enum_member.initializer | |
| 336 | ||
| 337 | // Any other literal reaches metadata as a Constant row | |
| 338 | // the enum's underlying type cannot hold, so it is | |
| 339 | // rejected here rather than emitted. A NONE stands for | |
| 340 | // an initializer the parser could not read, which it | |
| 341 | // has already reported. | |
| 342 | if isa Trees.Expressions.Literals.INTEGER(i) then | |
| 343 | value = i.value_string | |
| 344 | elif !isa Trees.Expressions.Literals.NONE(i) then | |
| 345 | _logger.error(i.location, "enum member initializer must be an integer literal") | |
| 346 | fi | |
| 347 | fi | |
| 348 | ||
| 349 | current_declaration_context.declare_enum_member( | |
| 350 | enum_member.name.location, | |
| 351 | enum_member.name.name, | |
| 352 | value, | |
| 353 | _symbol_definition_listener | |
| 354 | ) | |
| 355 | return false | |
| 356 | si | |
| 357 | ||
| 358 | pre(function: Definitions.FUNCTION) -> bool is | |
| 359 | // parse recovery can leave the declaration nameless; | |
| 360 | // there is nothing to declare for it | |
| 361 | let name = function.name | |
| 362 | ||
| 363 | if !name? then | |
| 364 | return true | |
| 365 | fi | |
| 366 | ||
| 367 | let is_innate = function.body? /\ isa Trees.Bodies.INNATE(function.body) | |
| 368 | let is_static = function.modifiers.is_static | |
| 369 | ||
| 370 | if is_static /\ name.name =~ "init" /\ function.arguments.count > 0 then | |
| 371 | _logger.error(function.location, "a static constructor cannot take parameters") | |
| 372 | fi | |
| 373 | ||
| 374 | // An operator declared on a type takes its left operand as | |
| 375 | // `self`, so its argument list holds the right operand alone. | |
| 376 | // One declaring both operands type-checks where its name is | |
| 377 | // in lexical scope, but the call it resolves to passes the | |
| 378 | // right operand alone, so it can never be emitted. Declared | |
| 379 | // undefined rather than as half an operator later passes | |
| 380 | // would try to call. | |
| 381 | let is_invalid_operator = | |
| 382 | !is_static /\ | |
| 383 | current_instance_context? /\ | |
| 384 | Lexical.TOKENIZER.is_operator_name(name.name) /\ | |
| 385 | function.arguments.count > 1 | |
| 386 | ||
| 387 | if is_invalid_operator then | |
| 388 | _logger.error( | |
| 389 | function.location, | |
| 390 | "an operator declared on a type takes one argument; its left operand is self", | |
| 391 | function.location, | |
| 392 | "help: declare it static to take both operands as arguments") | |
| 393 | fi | |
| 394 | ||
| 395 | let is_private = function.modifiers.is_private | |
| 396 | ||
| 397 | // An underscore-prefixed method, or the accessor of an underscore- | |
| 398 | // prefixed property (whose own $get_/$set_ name is not underscore- | |
| 399 | // prefixed), is subject to the underscore access policy. | |
| 400 | let is_underscore_method = name.name.starts_with('_') \/ function.is_underscore_scoped | |
| 401 | ||
| 402 | let property: Semantic.Symbols.Property? mut = null | |
| 403 | ||
| 404 | _local_id_generator.enter_function() | |
| 405 | ||
| 406 | if function.for_property? then | |
| 407 | let symbol = symbol_for(function.for_property) | |
| 408 | ||
| 409 | assert symbol? else "function is accessor for property but no property found: {function.location}" | |
| 410 | assert isa Semantic.Symbols.Property(symbol) else "function is accessor for property but associated symbol is not a property: {function.location}" | |
| 411 | ||
| 412 | property = cast Semantic.Symbols.Property(symbol) | |
| 413 | fi | |
| 414 | ||
| 415 | if is_innate then | |
| 416 | let `innate = cast Trees.Bodies.INNATE?(function.body)! | |
| 417 | ||
| 418 | associate_and_enter_scope( | |
| 419 | function, | |
| 420 | current_declaration_context.declare_innate( | |
| 421 | function.location, | |
| 422 | name.name, | |
| 423 | `innate.name.to_string(), | |
| 424 | current_scope,_symbol_definition_listener | |
| 425 | ) | |
| 426 | ) | |
| 427 | else | |
| 428 | // Generator vs async detection. A body with `yield` | |
| 429 | // (not inside a nested lambda) becomes a generator; | |
| 430 | // a body with `await` (in this body, not in a nested | |
| 431 | // lambda) becomes an async function. | |
| 432 | // | |
| 433 | // Either-or only — a body with both is diagnosed | |
| 434 | // and falls back to generator classification. | |
| 435 | let has_body = function.body? /\ !function.body.is_null | |
| 436 | ||
| 437 | let body_has_yield = | |
| 438 | has_body /\ YIELD_SCANNER().body_has_yield(function.body!) | |
| 439 | ||
| 440 | let await_scanner = AWAIT_SCANNER() | |
| 441 | if has_body then | |
| 442 | await_scanner.scan(function.body!) | |
| 443 | fi | |
| 444 | ||
| 445 | let body_has_await = await_scanner.found | |
| 446 | ||
| 447 | function.contains_let_await = body_has_await | |
| 448 | function.is_void_async = body_has_await /\ !await_scanner.found_value_return | |
| 449 | ||
| 450 | if body_has_yield /\ body_has_await then | |
| 451 | _logger.error( | |
| 452 | function.location, | |
| 453 | "function cannot be both a generator and async" | |
| 454 | ) | |
| 455 | fi | |
| 456 | ||
| 457 | let is_generator = body_has_yield | |
| 458 | let is_async = body_has_await /\ !is_generator | |
| 459 | ||
| 460 | let symbol: Semantic.Symbols.Symbol mut | |
| 461 | ||
| 462 | if is_invalid_operator then | |
| 463 | // The rejecting scope below then keeps the header and | |
| 464 | // body resolvable without leaving a callable operator | |
| 465 | // for later passes to select. Built directly rather | |
| 466 | // than through `declare_undefined`, whose own "cannot | |
| 467 | // declare operator here" is not what is wrong here. | |
| 468 | symbol = Semantic.Symbols.UNDEFINED(name.location, current_scope, name.name) | |
| 469 | elif is_generator then | |
| 470 | symbol = current_declaration_context.declare_generator_function( | |
| 471 | name.location, | |
| 472 | function.location, | |
| 473 | name.name, | |
| 474 | is_static, | |
| 475 | is_private, | |
| 476 | has_body, | |
| 477 | current_scope, | |
| 478 | _symbol_definition_listener) | |
| 479 | elif is_async then | |
| 480 | symbol = current_declaration_context.declare_async_function( | |
| 481 | name.location, | |
| 482 | function.location, | |
| 483 | name.name, | |
| 484 | is_static, | |
| 485 | is_private, | |
| 486 | has_body, | |
| 487 | current_scope, | |
| 488 | _symbol_definition_listener) | |
| 489 | else | |
| 490 | symbol = current_declaration_context.declare_function( | |
| 491 | name.location, | |
| 492 | function.location, | |
| 493 | name.name, | |
| 494 | is_static, | |
| 495 | is_underscore_method, | |
| 496 | has_body, | |
| 497 | function.for_property?, | |
| 498 | current_scope, | |
| 499 | _symbol_definition_listener) | |
| 500 | fi | |
| 501 | ||
| 502 | // A rejected declaration has no function symbol to scope | |
| 503 | // its arguments and body against. Entering it anyway makes | |
| 504 | // every name in the header and body report as not found on | |
| 505 | // top of the rejection; a rejecting scope keeps them | |
| 506 | // resolvable. | |
| 507 | if isa Semantic.Symbols.UNDEFINED(symbol) then | |
| 508 | associate_and_enter_scope(function, _rejecting_scope()) | |
| 509 | else | |
| 510 | associate_and_enter_scope(function, symbol) | |
| 511 | fi | |
| 512 | fi | |
| 513 | ||
| 514 | let symbol = symbol_for(function) | |
| 515 | ||
| 516 | if symbol? /\ isa Semantic.Symbols.Function(symbol) then | |
| 517 | let function_symbol = symbol | |
| 518 | ||
| 519 | let pure_owner = current_instance_context | |
| 520 | ||
| 521 | if function.modifiers.is_pure then | |
| 522 | function_symbol.mark_declared_pure() | |
| 523 | elif | |
| 524 | (!function.body? \/ function.body.is_null) /\ | |
| 525 | !function.modifiers.is_static /\ | |
| 526 | !function.is_assign_accessor /\ | |
| 527 | pure_owner? /\ | |
| 528 | pure_owner.is_pure | |
| 529 | then | |
| 530 | // A bodiless instance member of a pure type is a | |
| 531 | // contract rather than an implementation, so it | |
| 532 | // carries the declaration: implementors then inherit | |
| 533 | // the obligation through the pure-override contract. | |
| 534 | function_symbol.mark_declared_pure() | |
| 535 | fi | |
| 536 | ||
| 537 | if function.modifiers.is_stable then | |
| 538 | if function.for_property? then | |
| 539 | // The synthesized accessors copy the property's | |
| 540 | // modifier list; only the read accessor carries | |
| 541 | // the contract — the setter's copy is inert. | |
| 542 | if function.arguments.variables.count == 0 then | |
| 543 | function_symbol.mark_declared_stable() | |
| 544 | fi | |
| 545 | else | |
| 546 | _logger.error(function.modifiers.location, "stable is only valid on a property") | |
| 547 | fi | |
| 548 | fi | |
| 549 | ||
| 550 | if let operation = _pragma_scope_stack.intrinsic_operation then | |
| 551 | function_symbol.intrinsic_operation = operation | |
| 552 | fi | |
| 553 | ||
| 554 | if function.is_top_level_entry then | |
| 555 | function_symbol.is_top_level_entry = true | |
| 556 | fi | |
| 557 | ||
| 558 | // Record the primary constructor on its owning type so a | |
| 559 | // hover on the declaration can show the primary parameters. | |
| 560 | if | |
| 561 | function.is_primary_constructor /\ | |
| 562 | isa Semantic.Symbols.Classy(function_symbol.owner) | |
| 563 | then | |
| 564 | (cast Semantic.Symbols.Classy?(function_symbol.owner)!).primary_constructor = function_symbol | |
| 565 | fi | |
| 566 | ||
| 567 | // Record on the owning type that it has its own | |
| 568 | // zero-argument constructor — needed early (before | |
| 569 | // constructor signatures are resolved) to check a | |
| 570 | // `init` type-parameter constraint. | |
| 571 | if | |
| 572 | name.name =~ "init" /\ | |
| 573 | function.arguments.variables.count == 0 /\ | |
| 574 | isa Semantic.Symbols.Classy(function_symbol.owner) | |
| 575 | then | |
| 576 | (cast Semantic.Symbols.Classy?(function_symbol.owner)!).has_parameterless_constructor = true | |
| 577 | fi | |
| 578 | ||
| 579 | // A user-written body-less instance method on a class | |
| 580 | // makes that class implicitly abstract — the user | |
| 581 | // meant the method as a contract for subclasses to | |
| 582 | // implement, and a bare instance would throw if the | |
| 583 | // method were ever invoked. Skip property accessors, | |
| 584 | // since a write-only property leaves the synthesised | |
| 585 | // getter body-less without the user intending the | |
| 586 | // class to be abstract; skip `init` (that's a | |
| 587 | // separate concern handled by primary-ctor rewriting) | |
| 588 | // and static methods (statics don't make instances | |
| 589 | // abstract). | |
| 590 | let body_is_null = | |
| 591 | !function.body? \/ function.body.is_null | |
| 592 | ||
| 593 | function_symbol.is_declared_without_body = body_is_null | |
| 594 | ||
| 595 | if | |
| 596 | body_is_null /\ | |
| 597 | !function.for_property? /\ | |
| 598 | !is_static /\ | |
| 599 | name.name !~ "init" /\ | |
| 600 | isa Semantic.Symbols.CLASS(function_symbol.owner) | |
| 601 | then | |
| 602 | (cast Semantic.Symbols.CLASS(function_symbol.owner)).mark_has_bodyless_method() | |
| 603 | fi | |
| 604 | ||
| 605 | if property? then | |
| 606 | function_symbol.mark_synthesized() | |
| 607 | ||
| 608 | if function.arguments.variables.count == 0 then | |
| 609 | property.read_function = function_symbol | |
| 610 | ||
| 611 | // an auto property's synthesized getter reads | |
| 612 | // the backing field and nothing else, so it is | |
| 613 | // store-free by construction | |
| 614 | if function.for_property? /\ function.for_property.is_auto_property then | |
| 615 | function_symbol.mark_backing_read() | |
| 616 | fi | |
| 617 | ||
| 618 | if property.read_function_il_name_override? then | |
| 619 | function_symbol.il_name_override = property.read_function_il_name_override | |
| 620 | fi | |
| 621 | else | |
| 622 | property.assign_function = function_symbol | |
| 623 | ||
| 624 | if property.assign_function_il_name_override? then | |
| 625 | function_symbol.il_name_override = property.assign_function_il_name_override | |
| 626 | fi | |
| 627 | fi | |
| 628 | fi | |
| 629 | ||
| 630 | if function.generic_arguments.count > 0 then | |
| 631 | let generic_arguments = _generic_argument_declarer.declare_generic_arguments(function.generic_arguments) | |
| 632 | ||
| 633 | function_symbol.generic_argument_names = generic_arguments.names | |
| 634 | function_symbol.generic_arguments = generic_arguments.types | |
| 635 | function_symbol.is_generic = true | |
| 636 | fi | |
| 637 | ||
| 638 | function_symbol.start_declaring_arguments() | |
| 639 | function.arguments.walk(self) | |
| 640 | function_symbol.end_declaring_arguments() | |
| 641 | elif isa Semantic.BLOCK_SCOPE(current_declaration_context) then | |
| 642 | // The declaration was rejected above, so there is no | |
| 643 | // function symbol to open a declaring-arguments window | |
| 644 | // against — but the parameters still need somewhere to | |
| 645 | // resolve, or every reference downstream reports as | |
| 646 | // missing on top of the rejection. Declare them into the | |
| 647 | // rejecting scope entered above instead. | |
| 648 | function.arguments.walk(self) | |
| 649 | fi | |
| 650 | ||
| 651 | if function.body? then | |
| 652 | function.body.walk(self) | |
| 653 | fi | |
| 654 | ||
| 655 | return true | |
| 656 | si | |
| 657 | ||
| 658 | visit(function: Definitions.FUNCTION) is | |
| 659 | _local_id_generator.leave_function() | |
| 660 | ||
| 661 | leave_scope(function) | |
| 662 | si | |
| 663 | ||
| 664 | // Declare just a function's body — its block scopes, locals and | |
| 665 | // closures — reusing the body-handling visit methods. The caller | |
| 666 | // must already have the cursor positioned at the function's | |
| 667 | // (retained) scope. Used by the incremental body re-walk, which | |
| 668 | // keeps the interface symbols and re-declares only the edited | |
| 669 | // bodies; the interface is never re-walked here, so the pass's | |
| 670 | // non-idempotency on declarations is not exercised. | |
| 671 | declare_body(function: Definitions.FUNCTION) is | |
| 672 | if !function.body? then | |
| 673 | return | |
| 674 | fi | |
| 675 | ||
| 676 | _local_id_generator.enter_function() | |
| 677 | ||
| 678 | function.body!.walk(self) | |
| 679 | ||
| 680 | _local_id_generator.leave_function() | |
| 681 | si | |
| 682 | ||
| 683 | pre(property: Definitions.PROPERTY) -> bool is | |
| 684 | // parse recovery can leave the declaration nameless; | |
| 685 | // there is nothing to declare for it | |
| 686 | let name = property.name | |
| 687 | ||
| 688 | if !name? then | |
| 689 | return true | |
| 690 | fi | |
| 691 | ||
| 692 | let is_static = property.modifiers.is_static | |
| 693 | let instance_context = current_instance_context | |
| 694 | ||
| 695 | if property.modifiers.is_field then | |
| 696 | // is_field implies the storage class modifier is present | |
| 697 | if !instance_context? \/ instance_context.is_trait then | |
| 698 | _logger.error(property.modifiers.storage_class!.location, "field is not valid here") | |
| 699 | ||
| 700 | // Only a trait recovers as a property. Outside any type | |
| 701 | // the declaration stays field-shaped and is declared as | |
| 702 | // a variable below: accessor synthesis has already run | |
| 703 | // and skipped it as a field, so clearing the storage | |
| 704 | // class here would declare a property with no accessors | |
| 705 | // and every read of it would fail with "does not have a | |
| 706 | // read function" - a crash on top of the error already | |
| 707 | // reported. | |
| 708 | if instance_context? then | |
| 709 | property.modifiers.clear_storage_class() | |
| 710 | fi | |
| 711 | elif property.read_body? \/ property.assign_body? \/ property.assign_argument? then | |
| 712 | _logger.error(property.modifiers.storage_class!.location, "field cannot have a body") | |
| 713 | fi | |
| 714 | fi | |
| 715 | ||
| 716 | if | |
| 717 | property.modifiers.is_field \/ ( | |
| 718 | name.name.starts_with('_') /\ | |
| 719 | !property.read_body? /\ | |
| 720 | !property.assign_body? | |
| 721 | ) | |
| 722 | then | |
| 723 | if property.modifiers.is_stable then | |
| 724 | _logger.error(property.modifiers.location, "stable is only valid on a property") | |
| 725 | fi | |
| 726 | ||
| 727 | let symbol = current_declaration_context.declare_variable(name.location, name.name, is_static, _symbol_definition_listener) | |
| 728 | ||
| 729 | associate_node_with_scope(property, symbol) | |
| 730 | ||
| 731 | else | |
| 732 | let owner_is_trait = instance_context? /\ instance_context.is_trait | |
| 733 | ||
| 734 | let is_assignable = !property.read_body? \/ property.assign_argument? | |
| 735 | let is_private = !property.modifiers.is_public /\ !owner_is_trait | |
| 736 | ||
| 737 | let symbol = | |
| 738 | current_declaration_context | |
| 739 | .declare_property( | |
| 740 | name.location, | |
| 741 | property.location, | |
| 742 | name.name, | |
| 743 | is_static, | |
| 744 | is_private, | |
| 745 | is_assignable, | |
| 746 | _symbol_definition_listener | |
| 747 | ) | |
| 748 | ||
| 749 | if property.read_body == null /\ property.assign_body == null then | |
| 750 | current_declaration_context.declare_variable( | |
| 751 | name.location, | |
| 752 | "${name.name}", | |
| 753 | is_static, | |
| 754 | _symbol_definition_listener) | |
| 755 | .mark_synthesized() | |
| 756 | fi | |
| 757 | ||
| 758 | // the rewriter that synthesized the accessors decided | |
| 759 | // auto-ness before any body existed; carry its answer | |
| 760 | // onto the symbol, where flow narrowing keys on it | |
| 761 | if property.is_auto_property /\ isa Semantic.Symbols.Property(symbol) then | |
| 762 | (cast Semantic.Symbols.Property(symbol)).is_auto = true | |
| 763 | fi | |
| 764 | ||
| 765 | // A property with a getter also marks the accessor in | |
| 766 | // the function path; this mark is the only carrier for | |
| 767 | // a body-less declaration (a trait requirement). | |
| 768 | if property.modifiers.is_stable /\ isa Semantic.Symbols.Property(symbol) then | |
| 769 | (cast Semantic.Symbols.Property(symbol)).mark_declared_stable() | |
| 770 | fi | |
| 771 | ||
| 772 | associate_node_with_scope(property, symbol) | |
| 773 | fi | |
| 774 | ||
| 775 | return true | |
| 776 | si | |
| 777 | ||
| 778 | pre(indexer: Definitions.INDEXER) -> bool is | |
| 779 | // ??? | |
| 780 | ||
| 781 | return true | |
| 782 | si | |
| 783 | si | |
| 784 | si |