Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use System.Exception | |
| 3 | ||
| 4 | use IO.Std | |
| 5 | ||
| 6 | use Logging | |
| 7 | use Source | |
| 8 | ||
| 9 | use IR.Values | |
| 10 | use IR.VALUE_CONVERTER | |
| 11 | use IR.VALUE_BOXER | |
| 12 | ||
| 13 | use Semantic.LEAST_UPPER_BOUND_MAP | |
| 14 | use Semantic.Types.Type | |
| 15 | use Semantic.ARM_NULL_JOIN | |
| 16 | use Semantic.ArmNullJoinDecision | |
| 17 | ||
| 18 | use Syntax.Trees.Definitions.PRAGMA | |
| 19 | ||
| 20 | use Ghul.Pipes | |
| 21 | ||
| 22 | ||
| 23 | // Control-flow walks: yield, throw, break, continue, assertions, try, catch, do, if, case, | |
| 24 | // statement lists and expression bodies. | |
| 25 | partial COMPILE_EXPRESSIONS is | |
| 26 | visit(`throw: Trees.Statements.THROW) is | |
| 27 | super.visit(`throw) | |
| 28 | ||
| 29 | // Control does not fall through a throw. | |
| 30 | _flow.set_unreachable() | |
| 31 | ||
| 32 | if !`throw.expression? then | |
| 33 | return | |
| 34 | fi | |
| 35 | ||
| 36 | if | |
| 37 | !Value.check_is_consumable(_logger, `throw.expression.location, `throw.expression.value) | |
| 38 | then | |
| 39 | return | |
| 40 | fi | |
| 41 | ||
| 42 | let exception_type = _innate_symbol_lookup.get_exception_type() | |
| 43 | ||
| 44 | if !exception_type.is_assignable_from(`throw.expression!.value!.type!) then | |
| 45 | _logger.warn(`throw.expression!.location, "non-exception-throw", "thrown value is not derived from System.Exception") | |
| 46 | fi | |
| 47 | ||
| 48 | // FIXME: need to signal to any enclosing expression if statement that this is a throw so | |
| 49 | // if all branches are throws, an error can be reported | |
| 50 | si | |
| 51 | ||
| 52 | pre(`yield: Trees.Statements.YIELD) -> bool is | |
| 53 | super.pre(`yield) | |
| 54 | ||
| 55 | // The yielded expression's type must match the element | |
| 56 | // type T (drawn from the enclosing function's | |
| 57 | // `Iterable[T]` / `Iterator[T]` return type). Setting | |
| 58 | // the constraint here — before walking the expression — | |
| 59 | // lets the expression participate in inference / overload | |
| 60 | // resolution against the expected type, like RETURN's | |
| 61 | // value does in compile_bindings.pre_return. | |
| 62 | let function = _symbol_table.current_function | |
| 63 | ||
| 64 | assert function? else "yield outside a function" | |
| 65 | ||
| 66 | let state_machine = Semantic.Symbols.state_machine_for(function) | |
| 67 | ||
| 68 | if state_machine? /\ function.return_type? then | |
| 69 | let element_type = _yield_element_type_for(function) | |
| 70 | ||
| 71 | if element_type? /\ !element_type.is_inferred then | |
| 72 | `yield.expression.set_expected_type( | |
| 73 | element_type, | |
| 74 | "yielded value of type {{0}} is not assignable to element type {{1}}" | |
| 75 | ) | |
| 76 | fi | |
| 77 | fi | |
| 78 | ||
| 79 | return false | |
| 80 | si | |
| 81 | ||
| 82 | visit(`yield: Trees.Statements.YIELD) is | |
| 83 | super.visit(`yield) | |
| 84 | ||
| 85 | let function = _symbol_table.current_function | |
| 86 | let state_machine = Semantic.Symbols.state_machine_for(function) | |
| 87 | ||
| 88 | if !state_machine? then | |
| 89 | if function? /\ function.is_closure then | |
| 90 | _logger.error( | |
| 91 | `yield.location, | |
| 92 | "cannot yield in function literal" | |
| 93 | ) | |
| 94 | else | |
| 95 | _logger.error( | |
| 96 | `yield.location, | |
| 97 | "generator must return Pipe[T]" | |
| 98 | ) | |
| 99 | fi | |
| 100 | ||
| 101 | return | |
| 102 | fi | |
| 103 | ||
| 104 | assert function? else "state_machine_for returned non-null for null function" | |
| 105 | ||
| 106 | if !Value.check_is_consumable(_logger, `yield.expression.location, `yield.expression.value) then | |
| 107 | return | |
| 108 | fi | |
| 109 | ||
| 110 | // Verify the function's return type is actually an | |
| 111 | // Iterable[T] / Iterator[T]. The yield-presence test in | |
| 112 | // declare-symbols routes here without inspecting the | |
| 113 | // return type, so a body with `yield` and a non-iterable | |
| 114 | // return type lands here as a diagnostic. | |
| 115 | let element_type = _yield_element_type_for(function) | |
| 116 | ||
| 117 | if !element_type? then | |
| 118 | _logger.error( | |
| 119 | `yield.location, | |
| 120 | "generator must return Pipe[T]" | |
| 121 | ) | |
| 122 | fi | |
| 123 | si | |
| 124 | ||
| 125 | // `yield in E` - the elements of E are yielded one at a time, | |
| 126 | // so E is resolved exactly as a `for` loop's expression is and | |
| 127 | // it is E's element type, not E itself, that has to match the | |
| 128 | // generator's. | |
| 129 | visit(`yield: Trees.Statements.YIELD_ALL) is | |
| 130 | super.visit(`yield) | |
| 131 | ||
| 132 | let function = _symbol_table.current_function | |
| 133 | let state_machine = Semantic.Symbols.state_machine_for(function) | |
| 134 | ||
| 135 | if !state_machine? then | |
| 136 | if function? /\ function.is_closure then | |
| 137 | _logger.error( | |
| 138 | `yield.location, | |
| 139 | "cannot yield in function literal" | |
| 140 | ) | |
| 141 | else | |
| 142 | _logger.error( | |
| 143 | `yield.location, | |
| 144 | "generator must return Pipe[T]" | |
| 145 | ) | |
| 146 | fi | |
| 147 | ||
| 148 | return | |
| 149 | fi | |
| 150 | ||
| 151 | assert function? else "state_machine_for returned non-null for null function" | |
| 152 | ||
| 153 | let expression = `yield.expression | |
| 154 | ||
| 155 | if !Value.check_is_consumable(_logger, expression.location, expression.value) then | |
| 156 | return | |
| 157 | fi | |
| 158 | ||
| 159 | let element_type = _yield_element_type_for(function) | |
| 160 | ||
| 161 | if !element_type? then | |
| 162 | _logger.error( | |
| 163 | `yield.location, | |
| 164 | "generator must return Pipe[T]" | |
| 165 | ) | |
| 166 | ||
| 167 | return | |
| 168 | fi | |
| 169 | ||
| 170 | record_iterable_constraint(expression) | |
| 171 | ||
| 172 | let value = expression.value | |
| 173 | ||
| 174 | if !value? \/ !value.type? \/ value.type.is_error then | |
| 175 | return | |
| 176 | fi | |
| 177 | ||
| 178 | if !set_iterator_for(`yield, value.type!, false) then | |
| 179 | return | |
| 180 | fi | |
| 181 | ||
| 182 | check_receiver_present(expression) | |
| 183 | ||
| 184 | let yielded_type = `yield.read_current!.return_type! | |
| 185 | ||
| 186 | if !element_type.is_assignable_from(yielded_type) then | |
| 187 | _logger.error( | |
| 188 | expression.location, | |
| 189 | "yielded value of type {yielded_type} is not assignable to element type {element_type}" | |
| 190 | ) | |
| 191 | fi | |
| 192 | si | |
| 193 | ||
| 194 | // Extract `T` from a generator's `Pipe[T]` return type, or | |
| 195 | // null if the return type is not a Pipe[T] (a generator must | |
| 196 | // return Ghul.Pipes.Pipe[T]). T is taken from the Pipe itself, | |
| 197 | // not its Iterable[T] base, so the concrete element type — not | |
| 198 | // Pipe's own type parameter — is recovered. | |
| 199 | // | |
| 200 | // When the Pipe trait is unavailable (compiling ghul-runtime | |
| 201 | // itself, where the assembly is not yet loadable) fall back to | |
| 202 | // the bare Iterable[T] / Iterator[T] forms. | |
| 203 | _yield_element_type_for(function: Semantic.Symbols.Function) -> Semantic.Types.Type? is | |
| 204 | if !function.return_type? then | |
| 205 | return null | |
| 206 | fi | |
| 207 | ||
| 208 | let return_type = function.return_type | |
| 209 | ||
| 210 | let pipe = _innate_symbol_lookup.get_unspecialized_pipe_type() | |
| 211 | ||
| 212 | if pipe? then | |
| 213 | return Semantic.Symbols.TYPE_ARGUMENT_EXTRACTOR.extract(return_type, pipe) | |
| 214 | fi | |
| 215 | ||
| 216 | let iterator = _innate_symbol_lookup.get_unspecialized_iterator_type() | |
| 217 | let iterable = _innate_symbol_lookup.get_unspecialized_iterable_type() | |
| 218 | ||
| 219 | let candidates = Collections.LIST[Semantic.Types.Type]() | |
| 220 | candidates.add(iterator) | |
| 221 | candidates.add(iterable) | |
| 222 | ||
| 223 | return Semantic.Symbols.TYPE_ARGUMENT_EXTRACTOR.extract_from_any(return_type, candidates) | |
| 224 | si | |
| 225 | ||
| 226 | // `continue` keeps its label-only grammar; resolution goes | |
| 227 | // through the same loop-label symbols as `break`, and the | |
| 228 | // resolved target rides on the node. An unresolved label is an | |
| 229 | // error here rather than at codegen: every diagnostic must be | |
| 230 | // visible to analysis mode, which never runs generate-il. | |
| 231 | pre(`continue: Trees.Statements.CONTINUE) -> bool is | |
| 232 | super.pre(`continue) | |
| 233 | ||
| 234 | if let `continue.label? then | |
| 235 | let symbol = try_find(`continue.label!) | |
| 236 | ||
| 237 | if let label: Semantic.Symbols.LABEL = symbol then | |
| 238 | let frame = _loops.find_labelled_loop_frame(label.name) | |
| 239 | ||
| 240 | if frame? then | |
| 241 | `continue.resolved_target = frame.node | |
| 242 | _symbol_use_locations.add_symbol_use(`continue.label!.location, label) | |
| 243 | else | |
| 244 | let name = label.name | |
| 245 | ||
| 246 | _logger.error(`continue.location, "no enclosing loop named {name}") | |
| 247 | fi | |
| 248 | else | |
| 249 | let name = `continue.label!.name | |
| 250 | ||
| 251 | _logger.error(`continue.location, "no enclosing loop labelled {name}") | |
| 252 | fi | |
| 253 | fi | |
| 254 | ||
| 255 | return false | |
| 256 | si | |
| 257 | ||
| 258 | visit(`continue: Trees.Statements.CONTINUE) is | |
| 259 | super.visit(`continue) | |
| 260 | ||
| 261 | // Control does not fall through a continue. | |
| 262 | _flow.set_unreachable() | |
| 263 | si | |
| 264 | ||
| 265 | _check_assertion_condition(condition: Trees.Expressions.Expression) -> bool is | |
| 266 | let value = condition.value | |
| 267 | ||
| 268 | if | |
| 269 | !value? \/ | |
| 270 | !value.type? \/ | |
| 271 | !value.check_is_consumable(_logger, condition.location) | |
| 272 | then | |
| 273 | return false | |
| 274 | fi | |
| 275 | ||
| 276 | if | |
| 277 | !_innate_symbol_lookup | |
| 278 | .get_bool_type() | |
| 279 | .is_assignable_from(value.type!) | |
| 280 | then | |
| 281 | _logger.error(condition.location, "assertion expression must be bool") | |
| 282 | fi | |
| 283 | ||
| 284 | return true | |
| 285 | si | |
| 286 | ||
| 287 | _check_assertion_message(message: Trees.Expressions.Expression?) is | |
| 288 | if !message? then | |
| 289 | return | |
| 290 | fi | |
| 291 | ||
| 292 | let value = message.value | |
| 293 | ||
| 294 | if !value? \/ !value.type? then | |
| 295 | return | |
| 296 | fi | |
| 297 | ||
| 298 | value.check_is_consumable(_logger, message.location) | |
| 299 | ||
| 300 | if | |
| 301 | !_innate_symbol_lookup | |
| 302 | .get_exception_type() | |
| 303 | .is_assignable_from(value.type!) /\ | |
| 304 | !_innate_symbol_lookup | |
| 305 | .get_string_type() | |
| 306 | .is_assignable_from(value.type!) | |
| 307 | then | |
| 308 | _logger.error(message.location, "assertion else must be string or System.Exception") | |
| 309 | fi | |
| 310 | si | |
| 311 | ||
| 312 | pre(`assert: Trees.Statements.ASSERT) -> bool is | |
| 313 | super.pre(`assert) | |
| 314 | ||
| 315 | // Controlled walk so the epoch span covers the condition | |
| 316 | // alone: a kill during its walk means its derived heap | |
| 317 | // facts cannot survive (`assert _f? /\ mutate();`), but | |
| 318 | // the message only runs on the failure path, so its kills | |
| 319 | // don't invalidate what the passing condition proves. | |
| 320 | let epoch = _flow.heap_epoch | |
| 321 | let mark = _flow.crossing_mark | |
| 322 | ||
| 323 | _access.assert_condition_depth = _access.assert_condition_depth + 1 | |
| 324 | ||
| 325 | `assert.expression.walk(self) | |
| 326 | ||
| 327 | _access.assert_condition_depth = _access.assert_condition_depth - 1 | |
| 328 | ||
| 329 | _assert_condition_killed_stack.add(_flow.heap_killed_since(epoch)) | |
| 330 | _assert_condition_marks_stack.add((from_mark = mark, to_mark = _flow.crossing_mark)) | |
| 331 | ||
| 332 | if `assert.message? then | |
| 333 | `assert.message.walk(self) | |
| 334 | fi | |
| 335 | ||
| 336 | return true | |
| 337 | si | |
| 338 | ||
| 339 | visit(`assert: Trees.Statements.ASSERT) is | |
| 340 | super.visit(`assert) | |
| 341 | ||
| 342 | let condition_killed = _assert_condition_killed_stack[_assert_condition_killed_stack.count - 1] | |
| 343 | _assert_condition_killed_stack.remove_at(_assert_condition_killed_stack.count - 1) | |
| 344 | ||
| 345 | let condition_marks = _assert_condition_marks_stack[_assert_condition_marks_stack.count - 1] | |
| 346 | _assert_condition_marks_stack.remove_at(_assert_condition_marks_stack.count - 1) | |
| 347 | ||
| 348 | // `assert false` always throws — control does not | |
| 349 | // continue past it. | |
| 350 | if | |
| 351 | isa Trees.Expressions.Literals.BOOLEAN(`assert.expression) /\ | |
| 352 | (cast Trees.Expressions.Literals.BOOLEAN(`assert.expression)).value_string =~ "false" | |
| 353 | then | |
| 354 | _flow.set_unreachable() | |
| 355 | fi | |
| 356 | ||
| 357 | if !_check_assertion_condition(`assert.expression) then | |
| 358 | return | |
| 359 | fi | |
| 360 | ||
| 361 | // Apply the assert's narrowing to the fall-through: | |
| 362 | // since a failed assert throws, only the then-branch of | |
| 363 | // the condition reaches subsequent code. So `assert x?` | |
| 364 | // narrows x to non-optional and `assert isa T(x)` to T | |
| 365 | // in the rest of the enclosing scope, the same way | |
| 366 | // `if !cond then throw ... fi` would. | |
| 367 | let facts = _condition_analyzer.analyze_condition(`assert.expression, _flow.current_env) | |
| 368 | ||
| 369 | if condition_killed then | |
| 370 | facts.then_env.drop_heap_facts() | |
| 371 | else | |
| 372 | _flow.adopt_crossings_between(facts.then_env, condition_marks.from_mark, condition_marks.to_mark) | |
| 373 | fi | |
| 374 | ||
| 375 | _flow.set_env(facts.then_env) | |
| 376 | ||
| 377 | _check_assertion_message(`assert.message) | |
| 378 | si | |
| 379 | ||
| 380 | pre(`try: Trees.Statements.TRY) -> bool is | |
| 381 | super.pre(`try) | |
| 382 | return _loops.pre_try(`try) | |
| 383 | si | |
| 384 | ||
| 385 | visit(`try: Trees.Statements.TRY) is | |
| 386 | super.visit(`try) | |
| 387 | _loops.visit_try(`try) | |
| 388 | si | |
| 389 | ||
| 390 | pre(`catch: Trees.Statements.CATCH) -> bool is | |
| 391 | super.pre(`catch) | |
| 392 | return _loops.pre_catch(`catch) | |
| 393 | si | |
| 394 | ||
| 395 | visit(`catch: Trees.Statements.CATCH) is | |
| 396 | super.visit(`catch) | |
| 397 | _loops.visit_catch(`catch) | |
| 398 | si | |
| 399 | ||
| 400 | pre(`do: Trees.Statements.DO) -> bool is | |
| 401 | super.pre(`do) | |
| 402 | ||
| 403 | _loops.push_loop_value_frame( | |
| 404 | `do, | |
| 405 | take_pending_label(), | |
| 406 | `do.want_value, | |
| 407 | `do.expected_type, | |
| 408 | `do.expected_type_error_message | |
| 409 | ) | |
| 410 | ||
| 411 | return _loops.pre_do(`do) | |
| 412 | si | |
| 413 | ||
| 414 | visit(`do: Trees.Statements.DO) is | |
| 415 | if | |
| 416 | let `do?.condition? /\ | |
| 417 | Value.check_is_consumable(_logger, condition.location, condition.value) | |
| 418 | then | |
| 419 | if !condition.value!.type!.matches(_innate_symbol_lookup.get_bool_type()) then | |
| 420 | _logger.error(condition.location, "while condition must be bool") | |
| 421 | fi | |
| 422 | fi | |
| 423 | ||
| 424 | super.visit(`do) | |
| 425 | ||
| 426 | _loops.visit_do(`do) | |
| 427 | ||
| 428 | _loops.pop_and_settle_loop_value(`do) | |
| 429 | si | |
| 430 | ||
| 431 | // The label of a LABELLED wrapper, consumed by the wrapped | |
| 432 | // loop's pre. Empty whenever the last LABELLED did not wrap a | |
| 433 | // loop (the parser does not produce one). | |
| 434 | take_pending_label() -> string? is | |
| 435 | if _pending_label_names.count == 0 then | |
| 436 | return null | |
| 437 | fi | |
| 438 | ||
| 439 | let label = _pending_label_names[_pending_label_names.count - 1] | |
| 440 | _pending_label_names.remove_at(_pending_label_names.count - 1) | |
| 441 | ||
| 442 | return label.name | |
| 443 | si | |
| 444 | ||
| 445 | pre(labelled: Trees.Statements.LABELLED) -> bool is | |
| 446 | super.pre(labelled) | |
| 447 | ||
| 448 | _pending_label_names.add(labelled.label) | |
| 449 | ||
| 450 | return false | |
| 451 | si | |
| 452 | ||
| 453 | pre(`break: Trees.Statements.BREAK) -> bool is | |
| 454 | super.pre(`break) | |
| 455 | ||
| 456 | let expression = `break.expression | |
| 457 | ||
| 458 | if !expression? then | |
| 459 | return false | |
| 460 | fi | |
| 461 | ||
| 462 | // A bare qualified-less identifier may name a loop label; | |
| 463 | // ordinary scope lookup decides. A hit targets that loop | |
| 464 | // and is not compiled as a value; a miss falls through to | |
| 465 | // the value reading. | |
| 466 | if let identifier: Trees.Expressions.IDENTIFIER = expression then | |
| 467 | if !identifier.identifier.qualifier? then | |
| 468 | let symbol = try_find(identifier.identifier) | |
| 469 | ||
| 470 | if let label: Semantic.Symbols.LABEL = symbol then | |
| 471 | let frame = _loops.find_labelled_loop_frame(label.name) | |
| 472 | ||
| 473 | if !frame? then | |
| 474 | _logger.error(identifier.location, "no enclosing loop named {label.name}") | |
| 475 | ||
| 476 | return true | |
| 477 | fi | |
| 478 | ||
| 479 | `break.resolved_target = frame.node | |
| 480 | _symbol_use_locations.add_symbol_use(identifier.right_location, label) | |
| 481 | ||
| 482 | return true | |
| 483 | fi | |
| 484 | fi | |
| 485 | fi | |
| 486 | ||
| 487 | // Value break: thread the consuming loop's expected type, | |
| 488 | // unwrapped, onto the expression so it infers against T. | |
| 489 | let frame = _loops.innermost_consuming_frame() | |
| 490 | ||
| 491 | if frame? /\ frame.expected_type? then | |
| 492 | let expected mut = frame.expected_type | |
| 493 | let inner = expected.optional_inner_type | |
| 494 | ||
| 495 | if inner? then | |
| 496 | expected = inner | |
| 497 | fi | |
| 498 | ||
| 499 | expression.set_expected_type(expected, frame.expected_type_error_message) | |
| 500 | fi | |
| 501 | ||
| 502 | return false | |
| 503 | si | |
| 504 | ||
| 505 | visit(`break: Trees.Statements.BREAK) is | |
| 506 | let expression = `break.expression | |
| 507 | ||
| 508 | // A valued break targets the innermost *consuming* loop: | |
| 509 | // loops that are not expressions are exited through, since | |
| 510 | // the value needs a taker. Labelled breaks were resolved in | |
| 511 | // pre and contribute nothing. | |
| 512 | if !`break.resolved_target? then | |
| 513 | if expression? then | |
| 514 | let frame = _loops.innermost_consuming_frame() | |
| 515 | ||
| 516 | if frame? then | |
| 517 | if let v = expression.value, t = v.type then | |
| 518 | frame.note_contribution(t, expression.location) | |
| 519 | fi | |
| 520 | elif loop_value_frames.count > 0 then | |
| 521 | _logger.error(expression.location, "no enclosing loop takes this break value") | |
| 522 | else | |
| 523 | _logger.error(expression.location, "break outside of loop") | |
| 524 | fi | |
| 525 | elif loop_value_frames.count == 0 then | |
| 526 | _logger.error(`break.location, "break outside of loop") | |
| 527 | fi | |
| 528 | fi | |
| 529 | ||
| 530 | // Control does not fall through a break. | |
| 531 | _flow.set_unreachable() | |
| 532 | si | |
| 533 | ||
| 534 | pre(`if: Trees.Statements.IF_BRANCH) -> bool is | |
| 535 | super.pre(`if) | |
| 536 | return _conditionals.pre_if_branch(`if) | |
| 537 | si | |
| 538 | ||
| 539 | visit(`if: Trees.Statements.IF_BRANCH) is | |
| 540 | _conditionals.visit_if_branch(`if) | |
| 541 | super.visit(`if) | |
| 542 | si | |
| 543 | ||
| 544 | pre(expression: Trees.Bodies.EXPRESSION) -> bool is | |
| 545 | super.pre(expression) | |
| 546 | ||
| 547 | let function = current_function | |
| 548 | ||
| 549 | if | |
| 550 | !function? \/ | |
| 551 | !function.return_type? | |
| 552 | then | |
| 553 | return false | |
| 554 | fi | |
| 555 | ||
| 556 | if function.return_type.is_sentinel then | |
| 557 | // An inferred return type has not yet said whether the | |
| 558 | // body yields a value, so a body that only performs | |
| 559 | // void work is not an error here. Mark the position | |
| 560 | // void-tolerant; visit settles the return as void once | |
| 561 | // the body has been walked. | |
| 562 | if function.return_type.is_inferred then | |
| 563 | _mark_body_void_tolerated(expression.expression) | |
| 564 | fi | |
| 565 | ||
| 566 | return false | |
| 567 | fi | |
| 568 | ||
| 569 | // The marker a pack formal put on this position says what the | |
| 570 | // value is for, whatever type it is then expected to have. | |
| 571 | let pack_marker = expression.expression.compile_expressions_state.pack_marker | |
| 572 | ||
| 573 | expression.expression.set_expected_type(function.return_type, "cannot return value of type {{0}} where {{1}} expected") | |
| 574 | ||
| 575 | if pack_marker > 0 then | |
| 576 | // A literal is compiled to the shape the return has. Anything | |
| 577 | // else is a function of the pack's elements to be presented | |
| 578 | // in that shape, and is expected to be one. | |
| 579 | if pack_marker == 1 /\ !COMPILE_CALLS.parenthesised_literal(expression.expression)? then | |
| 580 | if let spread = Semantic.ARGUMENT_PACK.spread_shape(function.return_type) then | |
| 581 | expression.expression.set_expected_type(spread, "cannot return value of type {{0}} where {{1}} expected") | |
| 582 | fi | |
| 583 | fi | |
| 584 | ||
| 585 | expression.expression.set_expects_pack(pack_marker - 1) | |
| 586 | fi | |
| 587 | ||
| 588 | // A void-returning `=> body` doesn't need its body to yield | |
| 589 | // a value. Push that down into any STATEMENT- / VAL_BLOCK- | |
| 590 | // shaped body so a non-value-providing tail is accepted | |
| 591 | // silently. | |
| 592 | if function.return_type!.is_void then | |
| 593 | if let statement_expression: Trees.Expressions.STATEMENT = expression.expression then | |
| 594 | statement_expression.want_value = false | |
| 595 | elif let val_block: Trees.Expressions.VAL_BLOCK = expression.expression then | |
| 596 | val_block.want_value = false | |
| 597 | fi | |
| 598 | fi | |
| 599 | ||
| 600 | return false | |
| 601 | ||
| 602 | si | |
| 603 | ||
| 604 | _mark_body_void_tolerated(body: Trees.Expressions.Expression) is | |
| 605 | if let statement_expression: Trees.Expressions.STATEMENT = body then | |
| 606 | statement_expression.void_tolerated = true | |
| 607 | elif let val_block: Trees.Expressions.VAL_BLOCK = body then | |
| 608 | val_block.void_tolerated = true | |
| 609 | fi | |
| 610 | si | |
| 611 | ||
| 612 | visit(expression: Trees.Bodies.EXPRESSION) is | |
| 613 | let function = current_function | |
| 614 | ||
| 615 | // A diverging body — `=> throw E`, or an `=> if/case` whose | |
| 616 | // every arm diverges — yields no value but is valid. Keep an | |
| 617 | // explicitly declared return type; settle an inferred one as | |
| 618 | // void since there is nothing to infer from. | |
| 619 | if | |
| 620 | function? /\ | |
| 621 | !expression.expression.value? /\ | |
| 622 | _flow.is_unreachable | |
| 623 | then | |
| 624 | super.visit(expression) | |
| 625 | ||
| 626 | if DIVERGING_VALUE_POSITION.settles_inferred_return_to_void(function.return_type) then | |
| 627 | function.set_return_type(_innate_symbol_lookup.get_void_type()) | |
| 628 | fi | |
| 629 | ||
| 630 | return | |
| 631 | fi | |
| 632 | ||
| 633 | // A body that evaluates to a function of the pack's elements, | |
| 634 | // in a position a pack formal marked as taking their tuple, is | |
| 635 | // presented as the function of the tuple. | |
| 636 | if expression.expression.expects_pack then | |
| 637 | if let presented = _calls.present_function_value(expression.expression) then | |
| 638 | expression.expression.compile_expressions_state.value = presented | |
| 639 | fi | |
| 640 | fi | |
| 641 | ||
| 642 | let value = expression.expression?.value | |
| 643 | ||
| 644 | if | |
| 645 | !function? \/ | |
| 646 | !function.return_type? \/ | |
| 647 | !value? \/ | |
| 648 | !value.type? | |
| 649 | then | |
| 650 | super.visit(expression) | |
| 651 | ||
| 652 | expression.expression.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), expression.expression.location) | |
| 653 | ||
| 654 | if function? /\ (!function.return_type? \/ function.return_type.is_wild) then | |
| 655 | function.set_return_type(Semantic.Types.ERROR()) | |
| 656 | fi | |
| 657 | ||
| 658 | return | |
| 659 | fi | |
| 660 | ||
| 661 | let void_type = _innate_symbol_lookup.get_void_type() | |
| 662 | ||
| 663 | if function.return_type!.is_inferred then | |
| 664 | if Value.check_is_consumable_allow_void(_logger, expression.expression.location, value) then | |
| 665 | // Body contains let-await → wrap the bare-T body | |
| 666 | // expression as `Tasks.TASK.from_result(expr)` and | |
| 667 | // settle the inferred return as Task[T]. Values | |
| 668 | // already typed Task[?] are pinned as-is. | |
| 669 | // Mirrors the wrap in visit_return. | |
| 670 | let value_type: Semantic.Types.Type? mut = value.type! | |
| 671 | ||
| 672 | if | |
| 673 | function.wrap_inferred_return_as_task /\ | |
| 674 | value_type.is_settled /\ | |
| 675 | !_bindings.task_conversion.is_task_type(value_type) | |
| 676 | then | |
| 677 | if Semantic.Symbols.async_state_machine_for(function)? then | |
| 678 | // The body awaits, so the machine delivers | |
| 679 | // the value to its builder: the return is | |
| 680 | // pinned to the task carrying it rather than | |
| 681 | // the value being wrapped, exactly as the | |
| 682 | // RETURN visitor pins it. | |
| 683 | if value_type.matches(void_type) then | |
| 684 | if let void_task = _innate_symbol_lookup.get_void_task_type() then | |
| 685 | value_type = void_task | |
| 686 | function.is_void_async = true | |
| 687 | fi | |
| 688 | elif let task_type = _bindings.task_conversion.async_return_type_for(function, value_type!) then | |
| 689 | value_type = task_type | |
| 690 | fi | |
| 691 | elif function.async_task_like_template? then | |
| 692 | // A task-like other than Task has no | |
| 693 | // from_result to wrap through: the state | |
| 694 | // machine delivers the value to its builder, | |
| 695 | // and only the return type is pinned here. | |
| 696 | let task_type = _bindings.task_conversion.async_return_type_for(function, value_type!) | |
| 697 | ||
| 698 | if task_type? then | |
| 699 | value_type = task_type | |
| 700 | fi | |
| 701 | else | |
| 702 | let task_type = _innate_symbol_lookup.get_task_type(value_type) | |
| 703 | ||
| 704 | if task_type? then | |
| 705 | let wrapped = _bindings.task_conversion.try_wrap_value_as_task_expression(expression.expression, task_type, self) | |
| 706 | if wrapped? then | |
| 707 | expression.expression = wrapped | |
| 708 | value_type = expression.expression.value!.type | |
| 709 | fi | |
| 710 | fi | |
| 711 | fi | |
| 712 | fi | |
| 713 | ||
| 714 | // A null body says the return can be absent without | |
| 715 | // saying what it holds when present, so it settles | |
| 716 | // nothing: what it is optional of comes from the slot | |
| 717 | // the literal goes into, the same answer `return | |
| 718 | // null` reaches in a block body. | |
| 719 | if Semantic.ARM_NULL_JOIN().is_genuine_null(value_type!) then | |
| 720 | function.returned_genuine_null = true | |
| 721 | elif Semantic.OPERAND_WAIT.is_held_by(value_type!) then | |
| 722 | // Part of what the body produced is a rule waiting | |
| 723 | // for an operand the call site types. Pinning the | |
| 724 | // return to it now freezes that wait into the | |
| 725 | // literal's own type, where no later walk reaches | |
| 726 | // it; the return stays inferred instead. No | |
| 727 | // progress signal of its own: the walk that | |
| 728 | // settles the operand raises one. | |
| 729 | else | |
| 730 | function.set_return_type(Semantic.MAYBE_RETURN_PIN.of(function, value_type!, _innate_symbol_lookup)) | |
| 731 | fi | |
| 732 | elif value.type? /\ value.type.is_error then | |
| 733 | function.set_return_type(Semantic.Types.ERROR()) | |
| 734 | fi | |
| 735 | elif function.return_type!.matches(void_type) /\ !function.return_type!.is_type_variable then | |
| 736 | // `=> E` is the one-expression spelling of `is E si`, | |
| 737 | // where a void body discards whatever its tail leaves | |
| 738 | // standing, so a value here is discarded rather than | |
| 739 | // returned. Generate-il pops it. | |
| 740 | Value.check_is_consumable_allow_void(_logger, expression.expression.location, value) | |
| 741 | elif let async_sm = Semantic.Symbols.async_state_machine_for(function) then | |
| 742 | // The body awaits, so the machine delivers its value to | |
| 743 | // the builder, whose result slot holds the element type. | |
| 744 | // Judging against the task, and wrapping to reach it, | |
| 745 | // would hand the builder a task where it takes a T - | |
| 746 | // the same reason the RETURN visitor judges against the | |
| 747 | // frame's element. | |
| 748 | if let element = async_sm.frame?.result_type then | |
| 749 | if !element.is_assignable_from(value.type!) then | |
| 750 | _logger | |
| 751 | .error( | |
| 752 | expression.location, | |
| 753 | "cannot return value of type {value.type} where {element} expected" | |
| 754 | ) | |
| 755 | fi | |
| 756 | fi | |
| 757 | elif !function.return_type!.is_assignable_from(value.type!) then | |
| 758 | // Implicit T → TASK[T] widening at expression-body return | |
| 759 | // position. Same mechanism as visit_return: synthesise | |
| 760 | // Tasks.TASK.from_result(orig) and re-resolve. | |
| 761 | let wrapped = _bindings.task_conversion.try_wrap_value_as_task_expression(expression.expression, function.return_type!, self) | |
| 762 | if wrapped? then | |
| 763 | expression.expression = wrapped | |
| 764 | else | |
| 765 | _logger | |
| 766 | .error( | |
| 767 | expression.location, | |
| 768 | "cannot return value of type {value.type} where {function.return_type} expected" | |
| 769 | ) | |
| 770 | fi | |
| 771 | else | |
| 772 | Value.check_is_consumable_allow_void(_logger, expression.expression.location, value) | |
| 773 | fi | |
| 774 | ||
| 775 | check_non_optional(function.return_type, expression.expression, expression.expression.location) | |
| 776 | ||
| 777 | _pure_slots.check_store(expression.expression.location, function.return_type, expression.expression.value) | |
| 778 | ||
| 779 | super.visit(expression) | |
| 780 | si | |
| 781 | ||
| 782 | pre(statement: Trees.Expressions.STATEMENT) -> bool is | |
| 783 | super.pre(statement) | |
| 784 | ||
| 785 | statement.statement.compile_expressions_state.want_value = statement.want_value | |
| 786 | statement.statement.compile_expressions_state.void_tolerated = statement.void_tolerated | |
| 787 | ||
| 788 | return false | |
| 789 | si | |
| 790 | ||
| 791 | visit(statement: Trees.Expressions.STATEMENT) is | |
| 792 | // An `if` / `case` in a void-tolerant position that declined | |
| 793 | // to produce a value is a void body, not a missing one, so | |
| 794 | // this position is void too. Dropping want_value leaves the | |
| 795 | // statement shape IL generation already emits for a body | |
| 796 | // whose return type was written out as void. | |
| 797 | if statement.void_tolerated /\ !statement.statement.value? then | |
| 798 | statement.want_value = false | |
| 799 | fi | |
| 800 | ||
| 801 | // A diverging statement (`throw`, or an `if`/`case` whose | |
| 802 | // every arm diverges) legitimately yields no value; the | |
| 803 | // unreachable continuation needs none. | |
| 804 | if DIVERGING_VALUE_POSITION.is_missing_value_reportable(statement.want_value, statement.statement.value?, _flow.is_unreachable) then | |
| 805 | _logger.warn(statement.location, "statement-expression-no-value", "statement expression has no value") | |
| 806 | fi | |
| 807 | ||
| 808 | if statement.statement.value? then | |
| 809 | statement.compile_expressions_state.value = statement.statement.value | |
| 810 | elif !statement.want_value then | |
| 811 | // Void-tolerant position (expression-statement, void- | |
| 812 | // returning `=>` body) with an `if`/`case` whose | |
| 813 | // branches don't all provide values: synthesise a void | |
| 814 | // block value so the consumer has something to thread | |
| 815 | // through rather than a null that downstream passes | |
| 816 | // treat as an error. | |
| 817 | statement.compile_expressions_state.value = IR.Values.BLOCK( | |
| 818 | _innate_symbol_lookup.get_void_type() | |
| 819 | ) | |
| 820 | fi | |
| 821 | si | |
| 822 | ||
| 823 | pre(block: Trees.Expressions.VAL_BLOCK) -> bool is | |
| 824 | super.pre(block) | |
| 825 | ||
| 826 | // The body's tail is the only fall-through value | |
| 827 | // contributor. Push want_value down through the LIST to | |
| 828 | // its last statement so it must provide a value when this | |
| 829 | // block is consumed; the surrounding context (expression- | |
| 830 | // statement, void-returning `=>` body) may also write | |
| 831 | // false here to allow a void tail. | |
| 832 | block.body.compile_expressions_state.want_value = block.want_value | |
| 833 | block.body.compile_expressions_state.void_tolerated = block.void_tolerated | |
| 834 | ||
| 835 | // The returns targeting this block are collected afresh by | |
| 836 | // every walk of its body; entries left by an earlier walk | |
| 837 | // of the same body carry that walk's provisional types. | |
| 838 | block.return_types.clear() | |
| 839 | block.has_targeted_return = false | |
| 840 | ||
| 841 | // Push this block as the innermost return target before | |
| 842 | // walking its body. Returns inside read the top of stack | |
| 843 | // in pre_return / visit_return; nested val-blocks override | |
| 844 | // the current top while their own body is walked. | |
| 845 | _val_block_stack.add(block) | |
| 846 | ||
| 847 | return false | |
| 848 | si | |
| 849 | ||
| 850 | visit(block: Trees.Expressions.VAL_BLOCK) is | |
| 851 | // Pop ourselves off the val-block stack regardless of | |
| 852 | // outcome — every push in pre must be balanced by a pop | |
| 853 | // here, otherwise an outer return-target lookup picks up | |
| 854 | // a stale inner block. | |
| 855 | assert _val_block_stack.count > 0 else "val_block_stack underflow" | |
| 856 | let top = _val_block_stack[_val_block_stack.count - 1] | |
| 857 | assert top? /\ top == block else "val_block_stack head is not the block being visited" | |
| 858 | _val_block_stack.remove_at(_val_block_stack.count - 1) | |
| 859 | ||
| 860 | // A body that yields no value in a void-tolerant position | |
| 861 | // (an inferred function-literal return) makes the block | |
| 862 | // void rather than valueless. Nothing returns out of it | |
| 863 | // either, so there is no other contributor to LUB with. | |
| 864 | if | |
| 865 | block.void_tolerated /\ | |
| 866 | !block.body.value? /\ | |
| 867 | block.return_types.count == 0 | |
| 868 | then | |
| 869 | block.want_value = false | |
| 870 | fi | |
| 871 | ||
| 872 | if !block.want_value then | |
| 873 | // Void-tolerant position: yield a void block value so | |
| 874 | // the consumer (expression-statement, void-returning | |
| 875 | // `=>` body) has something to thread through. Returns | |
| 876 | // from inside the block are still emitted as branches | |
| 877 | // by generate-il; the value here is the fall-through | |
| 878 | // type, which is void in this branch. | |
| 879 | block.compile_expressions_state.value = IR.Values.BLOCK( | |
| 880 | _innate_symbol_lookup.get_void_type() | |
| 881 | ) | |
| 882 | return | |
| 883 | fi | |
| 884 | ||
| 885 | // LUB over every value-contributing source: returns | |
| 886 | // targeting us (recorded by visit_return as we walked the | |
| 887 | // body) and the tail expression's value type (if the tail | |
| 888 | // provides one). When the tail itself diverges (every | |
| 889 | // path returns from us), block.body.value may still be | |
| 890 | // set by the LIST visitor — it conservatively types as | |
| 891 | // BLOCK(last.value.type) — but the LUB shape below | |
| 892 | // tolerates either presence. | |
| 893 | // | |
| 894 | // A settled `null` contributor joins as optionality only, | |
| 895 | // never as a pool entry: on its own it would win | |
| 896 | // best-assignable outright, and beside a named type it | |
| 897 | // has no ancestors to intersect with, so the map would | |
| 898 | // discard the named type with it. The non-null pool LUBs | |
| 899 | // first; a genuine null then widens the result to its | |
| 900 | // optional carrier — the same join if / case expression | |
| 901 | // arms use. Without the widen, a `null` tail settles this | |
| 902 | // block at the null type itself, and a state-machine | |
| 903 | // frame spill field declared from that type is unencodable. | |
| 904 | let lub = LEAST_UPPER_BOUND_MAP() | |
| 905 | let null_join = ARM_NULL_JOIN() | |
| 906 | let seen_genuine_null mut = false | |
| 907 | ||
| 908 | for t in block.return_types do | |
| 909 | if !t.is_error then | |
| 910 | if null_join.is_genuine_null(t) then | |
| 911 | seen_genuine_null = true | |
| 912 | else | |
| 913 | lub.add(t) | |
| 914 | fi | |
| 915 | fi | |
| 916 | od | |
| 917 | ||
| 918 | if let block.body.value?, value.type? /\ !type.is_error then | |
| 919 | if null_join.is_genuine_null(type) then | |
| 920 | seen_genuine_null = true | |
| 921 | else | |
| 922 | lub.add(type) | |
| 923 | fi | |
| 924 | fi | |
| 925 | ||
| 926 | let lub_type mut = lub.get_result() | |
| 927 | ||
| 928 | // Passing seen_null as seen_genuine_null keeps sentinels | |
| 929 | // and error types (which also answer is_null) on the old | |
| 930 | // path: an unsettled contribution must not force a widen | |
| 931 | // or an incompatible verdict while inference converges. | |
| 932 | let decision = null_join.decide(lub_type, seen_genuine_null, seen_genuine_null) | |
| 933 | ||
| 934 | if decision == ArmNullJoinDecision.WIDEN_VALUE_OPTIONAL then | |
| 935 | lub_type = _innate_symbol_lookup.get_optional_type(lub_type!) | |
| 936 | elif decision == ArmNullJoinDecision.WIDEN_REFERENCE_OPTIONAL then | |
| 937 | lub_type = lub_type!.as_optional() | |
| 938 | elif decision == ArmNullJoinDecision.INCOMPATIBLE then | |
| 939 | // A type-variable LUB against a settled null: the same | |
| 940 | // verdict if / case arms reach, since NULLABLE[T] is | |
| 941 | // wrong for a type variable. Report and recover on the | |
| 942 | // plain LUB, as those callers do. | |
| 943 | _logger.error( | |
| 944 | block.location, | |
| 945 | "incompatible types in val-block: null and {lub_type}" | |
| 946 | ) | |
| 947 | fi | |
| 948 | ||
| 949 | if lub_type? then | |
| 950 | block.compile_expressions_state.value = IR.Values.BLOCK(lub_type) | |
| 951 | elif seen_genuine_null /\ block.expected_type? /\ block.expected_type.is_assignable_from(Semantic.Types.NULL()) then | |
| 952 | // Every contributor was a settled null, so there is no | |
| 953 | // LUB of our own to take; settle at the type the | |
| 954 | // consumer's context pushed down instead. Returns were | |
| 955 | // already checked against it by visit_return. | |
| 956 | block.compile_expressions_state.value = IR.Values.BLOCK(block.expected_type!) | |
| 957 | elif seen_genuine_null /\ !block.expected_type? then | |
| 958 | // All-null with no context to take a type from: the | |
| 959 | // same verdict the if-expression form reaches, rather | |
| 960 | // than settling the block at the null type. | |
| 961 | _logger.error(block.location, "all val-block contributions are null") | |
| 962 | ||
| 963 | block.compile_expressions_state.value = IR.Values.DUMMY_BLOCK( | |
| 964 | Semantic.Types.ERROR(), | |
| 965 | block.location, | |
| 966 | "all val-block contributions are null" | |
| 967 | ) | |
| 968 | elif block.body.value? then | |
| 969 | // No usable LUB contributions but the body produced a | |
| 970 | // value of some sentinel / error type — pass it | |
| 971 | // through so downstream phases see a real Value and | |
| 972 | // diagnose at the original location. | |
| 973 | block.compile_expressions_state.value = block.body.value | |
| 974 | else | |
| 975 | // Body's tail doesn't provide a value and there are | |
| 976 | // no returns to LUB with. The body walk has already | |
| 977 | // emitted "expected a value" at the offending | |
| 978 | // statement (compile_expressions.visit(Statements | |
| 979 | // .LIST) — the val-block contract is the same as | |
| 980 | // the LIST contract there); add the parallel "no | |
| 981 | // value" warning the if/case-in-expression form | |
| 982 | // emits in the same situation, and stand up a DUMMY | |
| 983 | // value so downstream walks have a typed Value. | |
| 984 | _logger.warn( | |
| 985 | block.location, | |
| 986 | "statement-expression-no-value", | |
| 987 | "statement expression has no value" | |
| 988 | ) | |
| 989 | ||
| 990 | block.compile_expressions_state.value = IR.Values.DUMMY_BLOCK( | |
| 991 | Semantic.Types.ERROR(), | |
| 992 | block.location, | |
| 993 | "val block produced no value" | |
| 994 | ) | |
| 995 | fi | |
| 996 | ||
| 997 | _declare_composite_spill_field(block, block.value) | |
| 998 | si | |
| 999 | ||
| 1000 | pre(list: Trees.Statements.LIST) -> bool is | |
| 1001 | super.pre(list) | |
| 1002 | return true | |
| 1003 | si | |
| 1004 | ||
| 1005 | // Wrap a body tail's bare-T value as the task the return | |
| 1006 | // calls for, the way the RETURN visitor wraps a `return T`. | |
| 1007 | // Answers the tail's type after the wrap, and its type | |
| 1008 | // unchanged where no wrap applies. Both tail-wrap callers - | |
| 1009 | // a declared task return and an inferred one about to be | |
| 1010 | // settled from the tail - ask the same question of the same | |
| 1011 | // tail, and differ only in how they decide to ask it. | |
| 1012 | _wrap_tail_as_task( | |
| 1013 | tail: Trees.Statements.Statement, | |
| 1014 | tail_type: Semantic.Types.Type? | |
| 1015 | ) -> Semantic.Types.Type? is | |
| 1016 | if | |
| 1017 | !tail_type? \/ | |
| 1018 | !tail_type.is_settled \/ | |
| 1019 | _bindings.task_conversion.is_task_type(tail_type) \/ | |
| 1020 | !isa Trees.Statements.EXPRESSION(tail) | |
| 1021 | then | |
| 1022 | return tail_type | |
| 1023 | fi | |
| 1024 | ||
| 1025 | let se = cast Trees.Statements.EXPRESSION(tail) | |
| 1026 | let task_type = _innate_symbol_lookup.get_task_type(tail_type) | |
| 1027 | ||
| 1028 | if !task_type? then | |
| 1029 | return tail_type | |
| 1030 | fi | |
| 1031 | ||
| 1032 | let wrapped = | |
| 1033 | _bindings.task_conversion.try_wrap_value_as_task_expression( | |
| 1034 | se.expression, | |
| 1035 | task_type, | |
| 1036 | self | |
| 1037 | ) | |
| 1038 | ||
| 1039 | if !wrapped? then | |
| 1040 | return tail_type | |
| 1041 | fi | |
| 1042 | ||
| 1043 | se.expression = wrapped | |
| 1044 | tail.compile_expressions_state.value = wrapped.value! | |
| 1045 | ||
| 1046 | return wrapped.value!.type | |
| 1047 | si | |
| 1048 | ||
| 1049 | visit(list: Trees.Statements.LIST) is | |
| 1050 | let enclosing_statement_list = current_statement_list | |
| 1051 | current_statement_list = list | |
| 1052 | ||
| 1053 | try | |
| 1054 | if let list.last? then | |
| 1055 | last.compile_expressions_state.want_value = list.want_value | |
| 1056 | last.compile_expressions_state.void_tolerated = list.void_tolerated | |
| 1057 | fi | |
| 1058 | ||
| 1059 | for s in list.statements do | |
| 1060 | self.enter_node(s) | |
| 1061 | try | |
| 1062 | s.walk(self) | |
| 1063 | finally | |
| 1064 | self.leave_node(s) | |
| 1065 | yrt | |
| 1066 | od | |
| 1067 | ||
| 1068 | // Implicit tail return: this list is a function body | |
| 1069 | // under a tail demand, and the tail is judged by its | |
| 1070 | // type while scope and flow are live - the async wrap | |
| 1071 | // replaces the tail expression and re-walks it in | |
| 1072 | // place, keeping resolution inside the function scope. | |
| 1073 | if list.function_tail /\ !_flow.is_unreachable then | |
| 1074 | if let fs = current_function, rt = fs.return_type, tail = list.last, v = tail.value then | |
| 1075 | // A void tail is effect-only: not an error, not a | |
| 1076 | // return. The demand goes undelivered, the body | |
| 1077 | // falls through to the default-value return, and | |
| 1078 | // the definite-return warning judges that path. | |
| 1079 | // The null literal's type matches every type, void | |
| 1080 | // included, but a null tail is a value. | |
| 1081 | let tail_is_void = | |
| 1082 | v.type? /\ | |
| 1083 | !isa Semantic.Types.NULL(v.type) /\ | |
| 1084 | v.type.matches(_innate_symbol_lookup.get_void_type()) | |
| 1085 | ||
| 1086 | if !tail_is_void /\ v.check_is_consumable(_logger, tail.location) then | |
| 1087 | let tt: Semantic.Types.Type? mut = v.type | |
| 1088 | ||
| 1089 | // An async function's tail wraps a bare-T value as | |
| 1090 | // TASK[T], exactly as an explicit `return T` does - | |
| 1091 | // except where a state machine consumes the return. | |
| 1092 | // Its result slot holds the element type (SetResult | |
| 1093 | // takes T), so like the RETURN visitor those | |
| 1094 | // functions never see the wrap and the tail is | |
| 1095 | // judged against the element. Await-free asyncs | |
| 1096 | // lower without a machine and keep the wrap. | |
| 1097 | let sm = Semantic.Symbols.async_state_machine_for(fs) | |
| 1098 | ||
| 1099 | let judge: Semantic.Types.Type? mut = rt | |
| 1100 | ||
| 1101 | if let frm = sm?.frame, et = frm.result_type then | |
| 1102 | judge = et | |
| 1103 | fi | |
| 1104 | ||
| 1105 | // A function literal that left its return | |
| 1106 | // type to be inferred, and said nothing | |
| 1107 | // about it in a `return`, is settled by its | |
| 1108 | // tail: nothing else in the body names the | |
| 1109 | // type, and the tail is the value the | |
| 1110 | // literal hands back. An async literal | |
| 1111 | // wraps a bare-T tail first, as the RETURN | |
| 1112 | // visitor does before pinning. | |
| 1113 | if rt.is_inferred /\ sm? then | |
| 1114 | // The literal awaits, so its return is | |
| 1115 | // pinned rather than wrapped: the machine | |
| 1116 | // delivers the value to its builder, whose | |
| 1117 | // result slot holds the element. Parity | |
| 1118 | // with the RETURN visitor's state-machine | |
| 1119 | // branch, which pins the same way. | |
| 1120 | if let settled = tt then | |
| 1121 | if | |
| 1122 | fs.wrap_inferred_return_as_task /\ | |
| 1123 | settled.is_settled /\ | |
| 1124 | !settled.is_sentinel /\ | |
| 1125 | !_bindings.task_conversion.is_task_type(settled) | |
| 1126 | then | |
| 1127 | if let task_type = _bindings.task_conversion.async_return_type_for(fs, settled) then | |
| 1128 | fs.set_return_type(task_type) | |
| 1129 | ||
| 1130 | if let element = sm.frame?.result_type then | |
| 1131 | judge = element | |
| 1132 | fi | |
| 1133 | fi | |
| 1134 | fi | |
| 1135 | fi | |
| 1136 | elif rt.is_inferred /\ !sm? then | |
| 1137 | if fs.wrap_inferred_return_as_task then | |
| 1138 | tt = _wrap_tail_as_task(tail, tt) | |
| 1139 | fi | |
| 1140 | ||
| 1141 | if let settled = tt then | |
| 1142 | if settled.is_settled /\ !settled.is_sentinel then | |
| 1143 | fs.set_return_type(Semantic.MAYBE_RETURN_PIN.of(fs, settled, _innate_symbol_lookup)) | |
| 1144 | ||
| 1145 | judge = fs.return_type | |
| 1146 | fi | |
| 1147 | fi | |
| 1148 | fi | |
| 1149 | ||
| 1150 | if | |
| 1151 | !sm? /\ | |
| 1152 | !rt.is_sentinel /\ | |
| 1153 | _bindings.task_conversion.is_task_type(rt) | |
| 1154 | then | |
| 1155 | tt = _wrap_tail_as_task(tail, tt) | |
| 1156 | fi | |
| 1157 | ||
| 1158 | // A judge still standing at a placeholder | |
| 1159 | // settles nothing: an inferred return whose | |
| 1160 | // tail reads the literal's own not-yet-known | |
| 1161 | // type has to wait for a later walk, and | |
| 1162 | // delivering against the placeholder would | |
| 1163 | // take the tail as a return value the | |
| 1164 | // literal never had. | |
| 1165 | if let t2 = tt, jd = judge /\ !jd.is_sentinel then | |
| 1166 | list.compile_expressions_state.function_tail_delivered = true | |
| 1167 | ||
| 1168 | if !t2.is_error /\ !jd.is_assignable_from(t2) then | |
| 1169 | _logger.error( | |
| 1170 | tail.location, | |
| 1171 | string.format( | |
| 1172 | "cannot return value of type {{0}} where {{1}} expected", | |
| 1173 | t2, | |
| 1174 | jd | |
| 1175 | ) | |
| 1176 | ) | |
| 1177 | elif sm? then | |
| 1178 | // Parity with the RETURN visitor's | |
| 1179 | // state-machine branch: the same two | |
| 1180 | // value-quality checks run on the value | |
| 1181 | // the tail delivers. | |
| 1182 | if let se = cast Trees.Statements.EXPRESSION?(tail) then | |
| 1183 | check_non_optional(jd, se.expression, tail.location) | |
| 1184 | fi | |
| 1185 | ||
| 1186 | _pure_slots.check_store(tail.location, jd, v) | |
| 1187 | fi | |
| 1188 | fi | |
| 1189 | fi | |
| 1190 | fi | |
| 1191 | fi | |
| 1192 | ||
| 1193 | if | |
| 1194 | list.function_tail /\ | |
| 1195 | !list.compile_expressions_state.function_tail_delivered | |
| 1196 | then | |
| 1197 | // Nothing to deliver: a void or valueless tail under a | |
| 1198 | // function-body demand falls through rather than | |
| 1199 | // returning, so no list value reaches IL generation. | |
| 1200 | // The tail goes back to being an ordinary statement - | |
| 1201 | // with want_value standing, IL generation would | |
| 1202 | // suppress its emission for a consumer that no longer | |
| 1203 | // exists, losing its side effects. | |
| 1204 | if let tail = list.last then | |
| 1205 | tail.compile_expressions_state.want_value = false | |
| 1206 | fi | |
| 1207 | ||
| 1208 | // An awaiting literal whose return was left to its | |
| 1209 | // tail, and whose tail turned out to deliver nothing, | |
| 1210 | // is void-async after all: settle it as the | |
| 1211 | // result-less task, which is what the pre-walk | |
| 1212 | // classification declines to do while a value tail is | |
| 1213 | // still possible. | |
| 1214 | if let fs = current_function, rt = fs.return_type then | |
| 1215 | if | |
| 1216 | rt.is_inferred /\ | |
| 1217 | fs.wrap_inferred_return_as_task /\ | |
| 1218 | Semantic.Symbols.async_state_machine_for(fs)? | |
| 1219 | then | |
| 1220 | if let void_task = _innate_symbol_lookup.get_void_task_type() then | |
| 1221 | fs.set_return_type(void_task) | |
| 1222 | fs.is_void_async = true | |
| 1223 | fi | |
| 1224 | fi | |
| 1225 | fi | |
| 1226 | ||
| 1227 | // A machine-less Tasks.TASK return completes at | |
| 1228 | // fall-through rather than returning the default null | |
| 1229 | // task: append the completing return after the tail, | |
| 1230 | // resolved here while the function's scope is live. | |
| 1231 | // The appended return joins the list, so a re-walk | |
| 1232 | // walks it before this visit runs again and leaves | |
| 1233 | // flow unreachable - the !is_unreachable guard above | |
| 1234 | // is what prevents a second append, and dropping it | |
| 1235 | // would grow one completing return per re-walk. A | |
| 1236 | // machine-less return of some other result-less | |
| 1237 | // task-like completes the same way, but its value | |
| 1238 | // comes from driving its builder, which IL | |
| 1239 | // generation emits at the fall-through - no append. | |
| 1240 | if | |
| 1241 | let fs = current_function, | |
| 1242 | rt = fs.return_type | |
| 1243 | then | |
| 1244 | if | |
| 1245 | !_flow.is_unreachable /\ | |
| 1246 | !rt.is_sentinel /\ | |
| 1247 | _bindings.task_conversion.is_void_task_return(rt) /\ | |
| 1248 | !Semantic.Symbols.async_state_machine_for(fs)? /\ | |
| 1249 | !Semantic.Symbols.state_machine_for(fs)? | |
| 1250 | then | |
| 1251 | let loc = if let tail = list.last then tail.location else list.location fi | |
| 1252 | ||
| 1253 | let completed = | |
| 1254 | Trees.Statements.RETURN( | |
| 1255 | loc, | |
| 1256 | Semantic.TASK_CONVERSION.build_completed_task_expression(loc)) | |
| 1257 | ||
| 1258 | list.add(completed) | |
| 1259 | ||
| 1260 | completed.expression!.walk(self) | |
| 1261 | fi | |
| 1262 | fi | |
| 1263 | ||
| 1264 | return | |
| 1265 | fi | |
| 1266 | ||
| 1267 | if !list.want_value then | |
| 1268 | return | |
| 1269 | fi | |
| 1270 | ||
| 1271 | if list.is_empty then | |
| 1272 | _logger.error(list.location, "expected a value") | |
| 1273 | return | |
| 1274 | fi | |
| 1275 | ||
| 1276 | let last = list.last | |
| 1277 | ||
| 1278 | if !last? then | |
| 1279 | return | |
| 1280 | fi | |
| 1281 | ||
| 1282 | if !last.provides_value then | |
| 1283 | // Tolerate a non-value-providing tail when every | |
| 1284 | // path through the body has already diverged | |
| 1285 | // (returned / thrown). Fall-through is dead | |
| 1286 | // code; the consumer needs the IL emitted for | |
| 1287 | // its side effects but no value need be left | |
| 1288 | // on the stack. A void-tolerant list - a function | |
| 1289 | // body under a tail demand, or an arm of the if | |
| 1290 | // that tail is - reads a valueless tail (a loop, | |
| 1291 | // say) as a void one: it falls through, and | |
| 1292 | // definite-return judges the fall-through, the | |
| 1293 | // same as a body whose tail is that loop directly. | |
| 1294 | if !_flow.is_unreachable /\ !list.void_tolerated then | |
| 1295 | _logger.error(last.location, "expected a value") | |
| 1296 | fi | |
| 1297 | return | |
| 1298 | fi | |
| 1299 | ||
| 1300 | if let last.value? then | |
| 1301 | // A tail whose type is still an inference | |
| 1302 | // placeholder - a local read before its own | |
| 1303 | // initializer has completed, say - carries no type | |
| 1304 | // to build the block from. The list takes the error | |
| 1305 | // type instead, so a consumer that joins it against | |
| 1306 | // a sibling reports the mismatch rather than | |
| 1307 | // dereferencing a type that is not there. | |
| 1308 | list.compile_expressions_state.value = | |
| 1309 | IR.Values.BLOCK(value.type ?? Semantic.Types.ERROR()) | |
| 1310 | ||
| 1311 | // A list that disposes needs its body inside the | |
| 1312 | // protected region, which the spill path does not | |
| 1313 | // build - so it always captures. | |
| 1314 | // | |
| 1315 | // The direct body of a val-block never generates | |
| 1316 | // through visit(Statements.LIST) — generate-il's | |
| 1317 | // pre(VAL_BLOCK) walks the statements itself — so a | |
| 1318 | // spill field recorded here would be a member | |
| 1319 | // nothing reads. Nested lists (an if arm inside the | |
| 1320 | // block, say) keep theirs: those do generate here. | |
| 1321 | let is_direct_body mut = false | |
| 1322 | ||
| 1323 | if _val_block_stack.count > 0 then | |
| 1324 | let top = _val_block_stack[_val_block_stack.count - 1] | |
| 1325 | ||
| 1326 | if top? /\ top.body == list then | |
| 1327 | is_direct_body = true | |
| 1328 | fi | |
| 1329 | fi | |
| 1330 | ||
| 1331 | if !list.want_dispose /\ !is_direct_body then | |
| 1332 | _declare_composite_spill_field(list, list.value) | |
| 1333 | fi | |
| 1334 | fi | |
| 1335 | finally | |
| 1336 | current_statement_list = enclosing_statement_list | |
| 1337 | yrt | |
| 1338 | si | |
| 1339 | ||
| 1340 | pre(`if: Trees.Statements.IF) -> bool is | |
| 1341 | super.pre(`if) | |
| 1342 | return _conditionals.pre_if(`if) | |
| 1343 | si | |
| 1344 | ||
| 1345 | visit(`if: Trees.Statements.IF) is | |
| 1346 | _conditionals.visit_if(`if) | |
| 1347 | ||
| 1348 | _declare_composite_spill_field(`if, `if.value) | |
| 1349 | si | |
| 1350 | ||
| 1351 | pre(`case: Trees.Statements.CASE) -> bool is | |
| 1352 | super.pre(`case) | |
| 1353 | return _conditionals.pre_case(`case) | |
| 1354 | si | |
| 1355 | ||
| 1356 | visit(`case: Trees.Statements.CASE) is | |
| 1357 | _conditionals.visit_case(`case) | |
| 1358 | ||
| 1359 | _declare_composite_spill_field(`case, `case.value) | |
| 1360 | ||
| 1361 | super.visit(`case) | |
| 1362 | si | |
| 1363 | ||
| 1364 | pre(arm: Trees.Statements.CASE_MATCH) -> bool is | |
| 1365 | super.pre(arm) | |
| 1366 | return _conditionals.pre_case_match(arm) | |
| 1367 | si | |
| 1368 | ||
| 1369 | visit(arm: Trees.Statements.CASE_MATCH) is | |
| 1370 | _conditionals.visit_case_match(arm) | |
| 1371 | super.visit(arm) | |
| 1372 | si | |
| 1373 | ||
| 1374 | // Do not descend into properties (otherwise accessor functions will be walked twice) | |
| 1375 | pre(property: Trees.Definitions.PROPERTY) -> bool => true | |
| 1376 | ||
| 1377 | visit(property: Trees.Definitions.PROPERTY) is | |
| 1378 | si | |
| 1379 | si | |
| 1380 | si |