Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use System.Exception | |
| 3 | use Ghul.Disposable | |
| 4 | ||
| 5 | use IO.Std | |
| 6 | ||
| 7 | use Logging | |
| 8 | use Source | |
| 9 | ||
| 10 | use IR.Values | |
| 11 | use IR.VALUE_CONVERTER | |
| 12 | use IR.VALUE_BOXER | |
| 13 | ||
| 14 | use Semantic.LEAST_UPPER_BOUND_MAP | |
| 15 | use Semantic.Types.Type | |
| 16 | ||
| 17 | use Syntax.Trees.Definitions.PRAGMA | |
| 18 | ||
| 19 | use Ghul.Pipes | |
| 20 | ||
| 21 | ||
| 22 | // Expression walks: function literals, tuples, sequences, self, super, spill, await, cast, | |
| 23 | // isa, typeof, operators, indexing, member access, generic applications and literals. | |
| 24 | partial COMPILE_EXPRESSIONS is | |
| 25 | pre(function: Trees.Expressions.FUNCTION) -> bool is | |
| 26 | // Push a function-literal boundary marker on the val- | |
| 27 | // block stack. A `return` inside this lambda's body | |
| 28 | // looks up the innermost val-block via the top of the | |
| 29 | // stack; the null marker reports "no val-block target" | |
| 30 | // and the return falls through to the function-return | |
| 31 | // path — exiting the lambda, not the enclosing val- | |
| 32 | // block. | |
| 33 | _val_block_stack.add(null) | |
| 34 | ||
| 35 | return true | |
| 36 | si | |
| 37 | ||
| 38 | visit(function: Trees.Expressions.FUNCTION) is | |
| 39 | let mark = _logger.mark() | |
| 40 | let use uses_guard = _symbol_use_locations.mark_then_release() | |
| 41 | ||
| 42 | // The lambda body walks under the enclosing method's | |
| 43 | // environment (a closure may soundly observe a narrow | |
| 44 | // in force at its construction point), but its own ifs / | |
| 45 | // assignments / divergence must not leak back out — so | |
| 46 | // the enclosing environment is saved and restored. | |
| 47 | // | |
| 48 | // The tracked deferred-init locals are deliberately NOT | |
| 49 | // swapped out. Closures capture by value at construction | |
| 50 | // time, so the closure body walking under `saved_env` | |
| 51 | // observes exactly the definite-assignment facts in | |
| 52 | // force where the closure is built — reading a captured | |
| 53 | // local that is unassigned there is a genuine | |
| 54 | // use-before-assignment (the closure captured a | |
| 55 | // not-yet-assigned value), not a false positive. | |
| 56 | let saved_env = _flow.current_env.copy() | |
| 57 | ||
| 58 | try | |
| 59 | RETRY_SITE_STATS.note("expressions.function_literal") | |
| 60 | _logger.speculate() | |
| 61 | _symbol_use_locations.speculate() | |
| 62 | ||
| 63 | super.pre(function) | |
| 64 | ||
| 65 | _lambdas.visit_function(function) | |
| 66 | ||
| 67 | // The lambda's signature is settled by the line above, | |
| 68 | // so this is the first point at which an async one is | |
| 69 | // known to have a state machine — and the last before | |
| 70 | // its body walks. A named async function's frame is | |
| 71 | // realised at the equivalent point in | |
| 72 | // visit(Definitions.FUNCTION); without the same here, | |
| 73 | // the frame's constructor is first declared while the | |
| 74 | // body is emitted, long after its row would have been | |
| 75 | // numbered. | |
| 76 | _declare_lambda_state_machine_frame(function) | |
| 77 | ||
| 78 | super.visit(function) | |
| 79 | ||
| 80 | _logger.commit() | |
| 81 | _symbol_use_locations.commit() | |
| 82 | catch e: Exception | |
| 83 | function.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), function.location) | |
| 84 | _logger.release(mark) | |
| 85 | ||
| 86 | _logger.exception(function.location, e, "exception compiling function literal") | |
| 87 | yrt | |
| 88 | ||
| 89 | _flow.set_env(saved_env) | |
| 90 | ||
| 91 | // Pop the function-literal boundary marker pushed by | |
| 92 | // pre(FUNCTION). After this point an outer val-block | |
| 93 | // (if any) is once again the innermost return target. | |
| 94 | assert _val_block_stack.count > 0 else "val_block_stack underflow at function literal exit" | |
| 95 | assert !_val_block_stack[_val_block_stack.count - 1]? else "val_block_stack head is not the function-literal marker" | |
| 96 | _val_block_stack.remove_at(_val_block_stack.count - 1) | |
| 97 | si | |
| 98 | ||
| 99 | // See COMPILE_LAMBDAS.try_load_self_reference. Reached from the | |
| 100 | // identifier load, which cannot see the lambda machinery itself. | |
| 101 | try_load_self_reference(location: Source.LOCATION, symbol: Semantic.Symbols.Symbol) -> IR.Values.Value? => | |
| 102 | _lambdas.try_load_self_reference(location, symbol) | |
| 103 | ||
| 104 | visit(recurse: Trees.Expressions.RECURSE) is | |
| 105 | _lambdas.visit_recurse(recurse) | |
| 106 | si | |
| 107 | ||
| 108 | pre(tuple: Trees.Expressions.TUPLE) -> bool => | |
| 109 | _tuples.pre_tuple(tuple) | |
| 110 | ||
| 111 | visit(tuple: Trees.Expressions.TUPLE) is | |
| 112 | _tuples.visit_tuple(tuple) | |
| 113 | si | |
| 114 | ||
| 115 | pre(sequence: Trees.Expressions.SEQUENCE) -> bool => true | |
| 116 | visit(sequence: Trees.Expressions.SEQUENCE) is | |
| 117 | let mark = _logger.mark() | |
| 118 | ||
| 119 | try | |
| 120 | RETRY_SITE_STATS.note("expressions.sequence") | |
| 121 | _logger.speculate() | |
| 122 | ||
| 123 | super.pre(sequence) | |
| 124 | ||
| 125 | sequence.type_expression.walk(self) | |
| 126 | ||
| 127 | // If the sequence is going to compile against a known | |
| 128 | // list/array type — either an inline type annotation | |
| 129 | // (`[1, 2, 3]: int[]`) or a constraint pushed by the | |
| 130 | // surrounding context (typed initializer / list-of- | |
| 131 | // lambdas) — push the corresponding element type down | |
| 132 | // to each element as its own constraint. Constraint- | |
| 133 | // aware element types (notably function literals) use | |
| 134 | // it to infer their argument types; element types that | |
| 135 | // ignore constraint (most literals) are unaffected. | |
| 136 | let element_constraint: Type? mut = _ | |
| 137 | ||
| 138 | if let sequence.type_expression? /\ !isa Trees.TypeExpressions.INFER(type_expression), type_expression.type? then | |
| 139 | element_constraint = type.get_element_type() | |
| 140 | elif let sequence.expected_type? then | |
| 141 | element_constraint = expected_type.get_element_type() | |
| 142 | fi | |
| 143 | ||
| 144 | for e in sequence.elements do | |
| 145 | if element_constraint? then | |
| 146 | e.set_expected_type(element_constraint, "element type {{0}} not compatible with inferred list type {{1}}") | |
| 147 | else | |
| 148 | e.clear_expected_type() | |
| 149 | fi | |
| 150 | od | |
| 151 | ||
| 152 | sequence.elements.walk(self) | |
| 153 | ||
| 154 | _tuples.visit_sequence(sequence) | |
| 155 | ||
| 156 | _logger.commit() | |
| 157 | catch e: Exception | |
| 158 | sequence.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), sequence.location) | |
| 159 | _logger.release(mark) | |
| 160 | ||
| 161 | _logger.exception(sequence.location, e, "exception compiling call") | |
| 162 | yrt | |
| 163 | si | |
| 164 | ||
| 165 | visit(`self: Trees.Expressions.SELF) is | |
| 166 | let s = current_instance_context | |
| 167 | ||
| 168 | let type: Type? mut = null | |
| 169 | ||
| 170 | if s? then | |
| 171 | let f = current_function | |
| 172 | ||
| 173 | if !f? \/ !f.is_instance then | |
| 174 | _logger.error(`self.location, "cannot access self from non-instance context") | |
| 175 | fi | |
| 176 | ||
| 177 | // A closure body that reads `self` is self-dependent even | |
| 178 | // if it captures no values (e.g. `() => self`), so its | |
| 179 | // delegate must not be memoized and shared across receivers. | |
| 180 | if let closure: Semantic.Symbols.Closure = f then | |
| 181 | closure.note_delegate_body_loads_self() | |
| 182 | fi | |
| 183 | ||
| 184 | if s.argument_names.count > 0 then | |
| 185 | let arguments = Collections.LIST[Type]() | |
| 186 | ||
| 187 | for n in s.argument_names do | |
| 188 | let argument = s.find_member(n) | |
| 189 | let argument_type = if argument? then argument.type else null fi | |
| 190 | ||
| 191 | if argument? /\ argument.is_type_variable /\ argument_type? then | |
| 192 | arguments.add(argument_type) | |
| 193 | fi | |
| 194 | od | |
| 195 | ||
| 196 | if arguments.count == s.argument_names.count then | |
| 197 | type = Semantic.Types.GENERIC( | |
| 198 | `self.location, | |
| 199 | cast Semantic.Symbols.Classy(s), | |
| 200 | arguments) | |
| 201 | fi | |
| 202 | fi | |
| 203 | ||
| 204 | // A flow narrowing on `self` (keyed on its instance | |
| 205 | // context) presents `self` at the narrowed type, so | |
| 206 | // `isa`/destructure and member access see the variant. | |
| 207 | // A bare variant narrow (`CONS`) is specialised against | |
| 208 | // `self`'s closed type (`List[T]`) so it resolves to its | |
| 209 | // closed-generic form (`CONS[T]`) for a loadable reference. | |
| 210 | if let narrowed = _flow.current_env.narrowed_type_of(s) then | |
| 211 | if type? then | |
| 212 | type = _condition_analyzer.specialize_variant_for_receiver(type, narrowed) | |
| 213 | else | |
| 214 | type = narrowed | |
| 215 | fi | |
| 216 | fi | |
| 217 | ||
| 218 | // Generator instance methods read `self` from the | |
| 219 | // frame's _outer_self field — the state-machine's | |
| 220 | // ldarg.0 is the state machine itself, not the | |
| 221 | // user instance. | |
| 222 | let state_machine = Semantic.Symbols.state_machine_for(current_function) | |
| 223 | ||
| 224 | if state_machine? /\ state_machine.frame? /\ !s.is_value_type then | |
| 225 | if let frame = state_machine.frame then | |
| 226 | frame.declare() | |
| 227 | ||
| 228 | if frame.outer_self_field? then | |
| 229 | `self.compile_expressions_state.value = Load.OUTER_SELF(s, type, frame.outer_self_field) | |
| 230 | fi | |
| 231 | fi | |
| 232 | fi | |
| 233 | ||
| 234 | if !`self.value? then | |
| 235 | if s.is_value_type then | |
| 236 | `self.compile_expressions_state.value = | |
| 237 | Load.VALUE_SELF( | |
| 238 | s, type | |
| 239 | ) | |
| 240 | else | |
| 241 | `self.compile_expressions_state.value = | |
| 242 | Load.REFERENCE_SELF( | |
| 243 | s, type | |
| 244 | ) | |
| 245 | fi | |
| 246 | fi | |
| 247 | ||
| 248 | // FIXME: this breaks VSCode rename symbol: | |
| 249 | // _symbol_use_locations.add_symbol_use(`self.location, s); | |
| 250 | else | |
| 251 | _logger.error(`self.location, "cannot access self from non-instance context") | |
| 252 | fi | |
| 253 | si | |
| 254 | ||
| 255 | visit(`super: Trees.Expressions.SUPER) is | |
| 256 | // FIXME: | |
| 257 | let s = _symbol_table.current_instance_context | |
| 258 | ||
| 259 | if s? then | |
| 260 | if s.is_trait then | |
| 261 | _logger.error(`super.location, "{s.short_description} does not have a super class") | |
| 262 | ||
| 263 | return | |
| 264 | fi | |
| 265 | ||
| 266 | let `classy = cast Semantic.Symbols.Classy(s) | |
| 267 | let super_type mut = `classy.ancestors[0] | |
| 268 | ||
| 269 | // If the enclosing method overrides exactly one trait method, | |
| 270 | // prefer that trait as the super type so super.foo() resolves | |
| 271 | // to the trait's default body and emits as a non-virtual | |
| 272 | // `call` (.NET DIM). Class-chain super stays on `ancestors[0]` | |
| 273 | // because that already specialises generic base types — using | |
| 274 | // the overridee's owner type would lose the specialisation. | |
| 275 | let current = current_function | |
| 276 | if current? /\ current.overridees? then | |
| 277 | let overridee_count = current.overridees |> count() | |
| 278 | if overridee_count == 1 then | |
| 279 | let overridee = current.overridees |> first() | |
| 280 | if overridee? /\ isa Semantic.Symbols.Classy(overridee.owner) then | |
| 281 | let owner = cast Semantic.Symbols.Classy(overridee.owner) | |
| 282 | let owner_type = owner.type | |
| 283 | ||
| 284 | if owner.is_trait /\ owner_type? then | |
| 285 | super_type = owner_type | |
| 286 | fi | |
| 287 | fi | |
| 288 | fi | |
| 289 | fi | |
| 290 | ||
| 291 | `super.compile_expressions_state.value = Load.SUPER(s, super_type) | |
| 292 | ||
| 293 | // FIXME: breaks VSCode rename symbol: | |
| 294 | // _symbol_use_locations.add_symbol_use(`super.location, s); | |
| 295 | fi | |
| 296 | si | |
| 297 | ||
| 298 | // SPILL is synthesised by the SPILL_AWAITS pass, in the | |
| 299 | // rewrite-syntax-trees phase, so one is already in the tree by | |
| 300 | // the time this runs. It types as a transparent wrapper over | |
| 301 | // its operand; generate-il emits the operand eagerly and stores | |
| 302 | // the result in the frame field allocated here. | |
| 303 | visit(spill: Trees.Expressions.SPILL) is | |
| 304 | spill.compile_expressions_state.value = null | |
| 305 | ||
| 306 | let operand_value = spill.operand.value | |
| 307 | ||
| 308 | if !operand_value? \/ !operand_value.type? then | |
| 309 | spill.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), spill.location) | |
| 310 | return | |
| 311 | fi | |
| 312 | ||
| 313 | // The receiver of a static call names a type rather than | |
| 314 | // holding a value, so there is nothing on the stack for the | |
| 315 | // suspend to lose and nothing a frame field could hold. | |
| 316 | // Pass it through untouched. | |
| 317 | if operand_value.is_type_expression then | |
| 318 | spill.compile_expressions_state.value = operand_value | |
| 319 | return | |
| 320 | fi | |
| 321 | ||
| 322 | _declare_spill_field(spill, operand_value.type) | |
| 323 | ||
| 324 | spill.compile_expressions_state.value = IR.Values.WRAPPER(DUMMY(operand_value.type!, spill.location)) | |
| 325 | si | |
| 326 | ||
| 327 | visit(`await: Trees.Expressions.AWAIT) is | |
| 328 | `await.compile_expressions_state.value = null | |
| 329 | ||
| 330 | let operand_value = `await.operand.value | |
| 331 | ||
| 332 | if !operand_value? \/ !operand_value.type? then | |
| 333 | `await.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `await.location) | |
| 334 | return | |
| 335 | fi | |
| 336 | ||
| 337 | let operand_type = operand_value.type | |
| 338 | ||
| 339 | let awaitable = _awaitable_resolver.resolve(`await.location, operand_type) | |
| 340 | ||
| 341 | if !awaitable? then | |
| 342 | `await.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `await.location) | |
| 343 | return | |
| 344 | fi | |
| 345 | ||
| 346 | `await.awaitable = awaitable | |
| 347 | ||
| 348 | let element_type = awaitable.result_type | |
| 349 | ||
| 350 | if !element_type.is_void then | |
| 351 | _declare_await_result_field(`await, element_type) | |
| 352 | fi | |
| 353 | ||
| 354 | _declare_awaiter_field(`await, awaitable.awaiter_type) | |
| 355 | ||
| 356 | // Generate-il fills the wrapper with IR.Values.AWAIT_SUSPEND | |
| 357 | // when emitting the SM body. The wrapper carries the result | |
| 358 | // type so surrounding expressions / let-bindings see the | |
| 359 | // right type at compile-expressions time. | |
| 360 | `await.compile_expressions_state.value = IR.Values.WRAPPER(DUMMY(element_type, `await.location)) | |
| 361 | si | |
| 362 | ||
| 363 | // The `cast` keyword's own span. A CAST's location runs to the | |
| 364 | // closing parenthesis, and the node keeps no separate span for | |
| 365 | // the keyword, so it is derived: the location starts at the | |
| 366 | // keyword, which is four characters wide, and a span's end | |
| 367 | // column is inclusive. | |
| 368 | _cast_keyword_location(`cast: Trees.Expressions.CAST) -> Source.LOCATION => | |
| 369 | Source.LOCATION( | |
| 370 | `cast.location.file_name, | |
| 371 | `cast.location.start_line, | |
| 372 | `cast.location.start_column, | |
| 373 | `cast.location.start_line, | |
| 374 | `cast.location.start_column + 3 | |
| 375 | ) | |
| 376 | ||
| 377 | visit(`cast: Trees.Expressions.CAST) is | |
| 378 | `cast.compile_expressions_state.value = null | |
| 379 | ||
| 380 | // `cast (X)(args)` settled as a call of the cast value: the | |
| 381 | // written-out reading was walked as this node's only child, | |
| 382 | // so its value is this node's value. | |
| 383 | if `cast.reading == Trees.Expressions.CastReading.CALLED then | |
| 384 | `cast.compile_expressions_state.value = `cast.called_form!.value | |
| 385 | ||
| 386 | return | |
| 387 | fi | |
| 388 | ||
| 389 | // `cast(v)` writes no target type and takes the one its | |
| 390 | // context supplies, the way a bare `_` does. Nothing has | |
| 391 | // pushed one in on the first walk of a call argument or an | |
| 392 | // operator operand, so the error here is what tells the | |
| 393 | // consumer this node is still waiting; the consumer rolls | |
| 394 | // it back and re-walks once resolution has settled on a | |
| 395 | // formal type. | |
| 396 | let type mut = | |
| 397 | if `cast.type_expression? then | |
| 398 | `cast.type_expression.type | |
| 399 | else | |
| 400 | `cast.expected_type | |
| 401 | fi | |
| 402 | ||
| 403 | if !type? then | |
| 404 | `cast.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `cast.location) | |
| 405 | ||
| 406 | if `cast.type_expression? then | |
| 407 | _logger.error(`cast.type_expression.location, "cast has no type") | |
| 408 | else | |
| 409 | _logger.error(`cast.location, "cannot infer the type to cast to here") | |
| 410 | fi | |
| 411 | ||
| 412 | return | |
| 413 | fi | |
| 414 | ||
| 415 | // The slot a typeless cast takes its target from can still | |
| 416 | // hold an inference placeholder - the element type of a | |
| 417 | // `LIST()` that a sibling `add` settles, say. A placeholder | |
| 418 | // cannot be named in an instruction, so it is no more of a | |
| 419 | // target than a missing one: report, and flag the walk as | |
| 420 | // having consumed an unresolved type so the body-retry loop | |
| 421 | // walks again once the placeholder has settled. | |
| 422 | if !`cast.type_expression? /\ type.contains_inferred then | |
| 423 | _logger.mark_consumed_any() | |
| 424 | _logger.error(`cast.location, "cannot infer the type to cast to here") | |
| 425 | ||
| 426 | `cast.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `cast.location) | |
| 427 | ||
| 428 | return | |
| 429 | fi | |
| 430 | ||
| 431 | if `cast.type_expression? then | |
| 432 | `cast.type_expression.check_is_not_reference(_logger, "cannot cast to a reference type") | |
| 433 | else | |
| 434 | // The target type is written nowhere, so hover over the | |
| 435 | // keyword is where the reader sees what it resolved to. | |
| 436 | // Recorded over the keyword alone rather than the whole | |
| 437 | // expression, so hovering the value being converted | |
| 438 | // still describes the value. | |
| 439 | _symbol_use_locations.add_inferred_type_hover(_cast_keyword_location(`cast), type) | |
| 440 | fi | |
| 441 | ||
| 442 | let right_value = `cast.right.value | |
| 443 | ||
| 444 | if !right_value? \/ !right_value.type? then | |
| 445 | _logger.poison(`cast.right.location, "cast has no value") | |
| 446 | ||
| 447 | return | |
| 448 | fi | |
| 449 | ||
| 450 | let specialized = _condition_analyzer.specialize_variant_for_receiver( | |
| 451 | right_value.type, | |
| 452 | type | |
| 453 | ) | |
| 454 | ||
| 455 | if specialized? then | |
| 456 | type = specialized | |
| 457 | fi | |
| 458 | ||
| 459 | // just check the cast is possible at this stage | |
| 460 | _type_caster.check_cast_is_valid(`cast.location, right_value.type!, type) | |
| 461 | ||
| 462 | // Warn when the cast can never succeed at runtime: the source's | |
| 463 | // statically-known type is sealed enough that we can rule out | |
| 464 | // any subtype that satisfies the target. The conservative form | |
| 465 | // is a value-type source against a *reference* target that | |
| 466 | // isn't one of its boxed-form ancestors — a value type's | |
| 467 | // runtime type is its declared type, so `cast string(42)` and | |
| 468 | // similar are unconditionally a null result. Value→value | |
| 469 | // casts go through the numeric-conversion path in TYPE_CASTER | |
| 470 | // and are left to that check; reference-source casts stay | |
| 471 | // unwarned because the runtime value may be a subtype | |
| 472 | // unrelated to the declared source. | |
| 473 | // | |
| 474 | // Skip the warning when the source is optional — `T?` to | |
| 475 | // a reference target lowers to boxing the Nullable<T> | |
| 476 | // (null when absent, the boxed T when present), which the | |
| 477 | // CLR supports unconditionally. The strict non-nullable- | |
| 478 | // by-default rule rejects `T? → T` at slot assignment, but | |
| 479 | // an explicit cast expressing the boxing is sound and the | |
| 480 | // warning would be a false positive. | |
| 481 | let source_type = right_value.type | |
| 482 | ||
| 483 | let is_impossible_cast = | |
| 484 | source_type? /\ | |
| 485 | source_type.is_settled /\ !source_type.is_type_variable /\ | |
| 486 | !source_type.is_sentinel /\ !source_type.is_error /\ | |
| 487 | type.is_settled /\ !type.is_type_variable /\ | |
| 488 | !type.is_sentinel /\ !type.is_error /\ | |
| 489 | source_type.is_value_type /\ !type.is_value_type /\ | |
| 490 | !source_type.is_optional /\ | |
| 491 | !type.is_assignable_from(source_type) /\ | |
| 492 | !source_type.is_assignable_from(type) /\ | |
| 493 | !_type_caster.find_user_defined_conversion(source_type, type)? | |
| 494 | ||
| 495 | if !_build_flags.no_warn_impossible_cast /\ is_impossible_cast then | |
| 496 | _logger.warn( | |
| 497 | `cast.location, | |
| 498 | "impossible-cast", | |
| 499 | "cast from {source_type} to {type} can never succeed" | |
| 500 | ) | |
| 501 | fi | |
| 502 | ||
| 503 | // A cast to a non-optional reference target throws when it | |
| 504 | // fails, so a site that means "null on failure" wants | |
| 505 | // `cast T?(...)`, which says so at the type level and lets the | |
| 506 | // strict-optional slot check catch misuses where the value is | |
| 507 | // stored. Skipped when the target is not a non-optional | |
| 508 | // reference - `cast T?(...)` is the fix rather than a target of | |
| 509 | // the warning, and a value-type or type-variable target does | |
| 510 | // not throw - when the source is statically assignable to the | |
| 511 | // target, so the cast is provably safe, and when the | |
| 512 | // impossible-cast form already reported the site, since that | |
| 513 | // message subsumes this one. A cast at an internal location was | |
| 514 | // written by the compiler rather than by the user, who has | |
| 515 | // nowhere to apply the advice. | |
| 516 | if | |
| 517 | !_build_flags.no_warn_cast_may_throw /\ | |
| 518 | !`cast.location.is_internal /\ | |
| 519 | !is_impossible_cast /\ | |
| 520 | source_type? /\ | |
| 521 | source_type.is_settled /\ !source_type.is_type_variable /\ | |
| 522 | !source_type.is_sentinel /\ !source_type.is_error /\ | |
| 523 | _is_non_optional_reference(type) /\ | |
| 524 | !_cast_target_covers_source(type, source_type) | |
| 525 | then | |
| 526 | let target = type | |
| 527 | _logger.warn( | |
| 528 | `cast.location, | |
| 529 | "cast-may-throw", | |
| 530 | "cast to non-optional {target} may throw", | |
| 531 | `cast.location, | |
| 532 | "help: use cast {target}? for null on failure" | |
| 533 | ) | |
| 534 | fi | |
| 535 | ||
| 536 | // we will fill this wrapper with the actual code to cast in the generate IL pass: | |
| 537 | `cast.compile_expressions_state.value = | |
| 538 | IR.Values.WRAPPER(DUMMY(type, `cast.location)) | |
| 539 | si | |
| 540 | ||
| 541 | visit(`isa: Trees.Expressions.ISA) is | |
| 542 | // Stamp the log position the test is walked at, so facts | |
| 543 | // formed from it skip the calls that ran before it. | |
| 544 | _flow.note_test_site(`isa) | |
| 545 | ||
| 546 | `isa.compile_expressions_state.value = null | |
| 547 | ||
| 548 | let isa_type mut = `isa.type_expression.type | |
| 549 | ||
| 550 | if isa_type == null then | |
| 551 | _logger.error(`isa.type_expression.location, "isa has no type") | |
| 552 | `isa.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `isa.location) | |
| 553 | return | |
| 554 | fi | |
| 555 | ||
| 556 | if let `isa.right?, right.value?, value.type? then | |
| 557 | let specialized = _condition_analyzer.specialize_variant_for_receiver( | |
| 558 | type, | |
| 559 | isa_type | |
| 560 | ) | |
| 561 | ||
| 562 | if specialized? then | |
| 563 | isa_type = specialized | |
| 564 | fi | |
| 565 | fi | |
| 566 | ||
| 567 | let bool_type = _innate_symbol_lookup.get_bool_type() | |
| 568 | ||
| 569 | `isa.compile_expressions_state.value = | |
| 570 | ISA( | |
| 571 | bool_type, | |
| 572 | isa_type, | |
| 573 | `isa.right.value! | |
| 574 | ) | |
| 575 | si | |
| 576 | ||
| 577 | visit(`typeof: Trees.Expressions.TYPEOF) is | |
| 578 | `typeof.compile_expressions_state.value = null | |
| 579 | ||
| 580 | let typeof_type = `typeof.type_expression.type | |
| 581 | ||
| 582 | if !typeof_type? then | |
| 583 | _logger.error(`typeof.type_expression.location, "typeof has no type") | |
| 584 | `typeof.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), `typeof.location) | |
| 585 | return | |
| 586 | fi | |
| 587 | ||
| 588 | let type_type = _innate_symbol_lookup.get_type_type() | |
| 589 | ||
| 590 | `typeof.compile_expressions_state.value = | |
| 591 | TYPEOF( | |
| 592 | type_type, | |
| 593 | typeof_type | |
| 594 | ) | |
| 595 | si | |
| 596 | ||
| 597 | visit(construct: Trees.Expressions.CONSTRUCT) is | |
| 598 | _calls.visit_construct(construct) | |
| 599 | _check_pure_slots(construct.location, construct.value, construct.arguments) | |
| 600 | _note_call(construct.location, construct.value) | |
| 601 | si | |
| 602 | ||
| 603 | visit(unary: Trees.Expressions.UNARY) is | |
| 604 | _operators.visit_unary(unary) | |
| 605 | _check_pure_slots(unary.location, unary.value, null) | |
| 606 | _note_call(unary.location, unary.value) | |
| 607 | si | |
| 608 | ||
| 609 | pre(binary: Trees.Expressions.BINARY) -> bool is | |
| 610 | return _operators.pre_binary(binary) | |
| 611 | si | |
| 612 | ||
| 613 | visit(binary: Trees.Expressions.BINARY) is | |
| 614 | try | |
| 615 | _operators.visit_binary(binary) | |
| 616 | _check_pure_slots(binary.location, binary.value, null) | |
| 617 | _note_call(binary.location, binary.value) | |
| 618 | catch ex: Exception | |
| 619 | _logger.exception(binary.location, ex, "exception compiling binary operator (called from {System.Diagnostics.StackTrace().to_string().replace_line_endings(" ")})") | |
| 620 | yrt | |
| 621 | si | |
| 622 | ||
| 623 | visit(field_equals: Trees.Expressions.FIELD_EQUALS) is | |
| 624 | try | |
| 625 | _operators.visit_field_equals(field_equals) | |
| 626 | _note_call(field_equals.location, field_equals.value) | |
| 627 | catch ex: Exception | |
| 628 | _logger.exception(field_equals.location, ex, "exception compiling field comparison (called from {System.Diagnostics.StackTrace().to_string().replace_line_endings(" ")})") | |
| 629 | yrt | |
| 630 | si | |
| 631 | ||
| 632 | visit(memberwise_equals: Trees.Expressions.MEMBERWISE_EQUALS) is | |
| 633 | try | |
| 634 | _operators.visit_memberwise_equals(memberwise_equals) | |
| 635 | _note_call(memberwise_equals.location, memberwise_equals.value) | |
| 636 | catch ex: Exception | |
| 637 | _logger.exception(memberwise_equals.location, ex, "exception compiling synthesized equality") | |
| 638 | yrt | |
| 639 | si | |
| 640 | ||
| 641 | visit(memberwise_hash: Trees.Expressions.MEMBERWISE_HASH) is | |
| 642 | try | |
| 643 | _operators.visit_memberwise_hash(memberwise_hash) | |
| 644 | _note_call(memberwise_hash.location, memberwise_hash.value) | |
| 645 | catch ex: Exception | |
| 646 | _logger.exception(memberwise_hash.location, ex, "exception compiling synthesized hash") | |
| 647 | yrt | |
| 648 | si | |
| 649 | ||
| 650 | visit(hash_operand: Trees.Expressions.HASH_OPERAND) is | |
| 651 | try | |
| 652 | _operators.visit_hash_operand(hash_operand) | |
| 653 | _note_call(hash_operand.location, hash_operand.value) | |
| 654 | catch ex: Exception | |
| 655 | _logger.exception(hash_operand.location, ex, "exception compiling hash operand (called from {System.Diagnostics.StackTrace().to_string().replace_line_endings(" ")})") | |
| 656 | yrt | |
| 657 | si | |
| 658 | ||
| 659 | // Build a value-equality test Value for a `case` `when` label, | |
| 660 | // the way `=~` (or `<>`, compared to zero) would. Forwards to | |
| 661 | // the shared operator machinery so `case` reuses the same | |
| 662 | // null-safe lowering and resolution as the `=~` operator. See | |
| 663 | // COMPILE_OPERATORS._try_equality_test. | |
| 664 | // | |
| 665 | // No source token in either caller names the operator, so the | |
| 666 | // operator use is recorded at whatever location the caller | |
| 667 | // hands in: the whole `case` arm for a label, the leaf itself | |
| 668 | // for a literal destructure, colouring that span as a method | |
| 669 | // call. Suppress symbol-use recording for the whole build. A | |
| 670 | // written `=~` is unaffected because the suppression is scoped | |
| 671 | // to this call frame: the binary-operator visitor records its | |
| 672 | // use outside it, whichever lowering it takes (including | |
| 673 | // _build_null_safe_equality, which this builder also reaches). | |
| 674 | build_equality_test( | |
| 675 | left_value: IR.Values.Value, | |
| 676 | right_value: IR.Values.Value, | |
| 677 | op_name: string, | |
| 678 | location: Source.LOCATION | |
| 679 | ) -> IR.Values.Value? is | |
| 680 | _symbol_use_locations.begin_suppress() | |
| 681 | ||
| 682 | try | |
| 683 | return _operators._try_equality_test(left_value, right_value, op_name, location) | |
| 684 | finally | |
| 685 | _symbol_use_locations.end_suppress() | |
| 686 | yrt | |
| 687 | si | |
| 688 | ||
| 689 | pre(index: Trees.Expressions.INDEX) -> bool => | |
| 690 | _operators.pre_index(index) | |
| 691 | ||
| 692 | visit(index: Trees.Expressions.INDEX) is | |
| 693 | try | |
| 694 | _operators.visit_index(index) | |
| 695 | check_receiver_present(index.left) | |
| 696 | _check_pure_slots(index.location, index.value, null) | |
| 697 | _note_call(index.location, index.value) | |
| 698 | catch e: Exception | |
| 699 | index.compile_expressions_state.value = null | |
| 700 | ||
| 701 | _logger.exception(index.location, e, "exception compiling index") | |
| 702 | yrt | |
| 703 | si | |
| 704 | ||
| 705 | visit(member: Trees.Expressions.MEMBER) is | |
| 706 | _access.visit_member(member) | |
| 707 | _note_call(member.location, member.value) | |
| 708 | si | |
| 709 | ||
| 710 | pre(ambiguous_expression: Trees.Expressions.AMBIGUOUS_EXPRESSION) -> bool => | |
| 711 | _generic_application.pre_ambiguous_expression(ambiguous_expression) | |
| 712 | ||
| 713 | visit(ambiguous_expression: Trees.Expressions.AMBIGUOUS_EXPRESSION) is | |
| 714 | si | |
| 715 | ||
| 716 | pre(generic_application: Trees.Expressions.GENERIC_APPLICATION) -> bool => | |
| 717 | _generic_application.pre_generic_application(generic_application) | |
| 718 | ||
| 719 | visit(generic_application: Trees.Expressions.GENERIC_APPLICATION) is | |
| 720 | si | |
| 721 | ||
| 722 | visit(integer: Trees.Expressions.Literals.INTEGER) is | |
| 723 | _literals.visit_integer(integer) | |
| 724 | si | |
| 725 | ||
| 726 | visit(float: Trees.Expressions.Literals.FLOAT) is | |
| 727 | _literals.visit_float(float) | |
| 728 | si | |
| 729 | ||
| 730 | visit(interpolation: Trees.Expressions.STRING_INTERPOLATION) is | |
| 731 | _literals.visit_interpolation(interpolation) | |
| 732 | ||
| 733 | // An alignment clause is handed to the interpolation | |
| 734 | // handler's int parameter as-is, so a non-int alignment | |
| 735 | // has to be rejected here rather than reach emission. | |
| 736 | let int_type = _innate_symbol_lookup.get_int_type() | |
| 737 | ||
| 738 | for fragment in interpolation.values do | |
| 739 | if !fragment.is_expression then | |
| 740 | continue | |
| 741 | fi | |
| 742 | ||
| 743 | if let alignment = fragment.alignment then | |
| 744 | if let alignment_type = alignment.value?.type then | |
| 745 | if !alignment_type.is_error /\ !alignment_type.is_inferred /\ | |
| 746 | alignment_type.compare(int_type) != Semantic.Types.MATCH.SAME then | |
| 747 | _logger.error( | |
| 748 | alignment.location, | |
| 749 | "interpolation alignment must be int, not {alignment_type}") | |
| 750 | fi | |
| 751 | fi | |
| 752 | fi | |
| 753 | od | |
| 754 | ||
| 755 | // Interpolation formats each fragment through to_string at | |
| 756 | // run time, but those calls are synthesised at emission and | |
| 757 | // never surface as call values here, so the call transfer | |
| 758 | // has to fire for them. Each fragment is recorded against | |
| 759 | // the to_string it will dispatch to, so the judge can | |
| 760 | // discharge it like any other call; a fragment whose target | |
| 761 | // cannot be named records an unbounded crossing instead. | |
| 762 | _note_interpolation_calls(interpolation) | |
| 763 | si | |
| 764 | ||
| 765 | _note_interpolation_calls(interpolation: Trees.Expressions.STRING_INTERPOLATION) is | |
| 766 | for fragment in interpolation.values do | |
| 767 | if !fragment.is_expression then | |
| 768 | continue | |
| 769 | fi | |
| 770 | ||
| 771 | // a format specifier selects a different, culture-aware | |
| 772 | // formatting path | |
| 773 | if fragment.format? then | |
| 774 | _flow.on_call(interpolation.location) | |
| 775 | return | |
| 776 | fi | |
| 777 | ||
| 778 | let fragment_type = fragment.expression.value?.type | |
| 779 | ||
| 780 | if !fragment_type? then | |
| 781 | _flow.on_call(interpolation.location) | |
| 782 | return | |
| 783 | fi | |
| 784 | ||
| 785 | let to_string = _zero_argument_to_string(fragment_type.find_member("to_string")) | |
| 786 | ||
| 787 | if !to_string? then | |
| 788 | _flow.on_call(interpolation.location) | |
| 789 | return | |
| 790 | fi | |
| 791 | ||
| 792 | // same structural gate as _note_call | |
| 793 | if to_string.is_store_free then | |
| 794 | continue | |
| 795 | fi | |
| 796 | ||
| 797 | _flow.on_call_of(interpolation.location, to_string) | |
| 798 | od | |
| 799 | si | |
| 800 | ||
| 801 | // The zero-argument to_string a fragment formats through. | |
| 802 | _zero_argument_to_string(member: Semantic.Symbols.Symbol?) -> Semantic.Symbols.Function? is | |
| 803 | if let group: Semantic.Symbols.FUNCTION_GROUP = member then | |
| 804 | for function in group.functions do | |
| 805 | if !function.are_arguments_declared \/ function.arguments.count == 0 then | |
| 806 | return function | |
| 807 | fi | |
| 808 | od | |
| 809 | ||
| 810 | return null | |
| 811 | fi | |
| 812 | ||
| 813 | if let function: Semantic.Symbols.Function = member then | |
| 814 | if !function.are_arguments_declared \/ function.arguments.count == 0 then | |
| 815 | return function | |
| 816 | fi | |
| 817 | fi | |
| 818 | ||
| 819 | return null | |
| 820 | si | |
| 821 | ||
| 822 | visit(`string: Trees.Expressions.Literals.STRING) is | |
| 823 | _literals.visit_string(`string) | |
| 824 | si | |
| 825 | ||
| 826 | visit(character: Trees.Expressions.Literals.CHARACTER) is | |
| 827 | _literals.visit_character(character) | |
| 828 | si | |
| 829 | ||
| 830 | visit(boolean: Trees.Expressions.Literals.BOOLEAN) is | |
| 831 | _literals.visit_boolean(boolean) | |
| 832 | si | |
| 833 | ||
| 834 | si | |
| 835 | si |