Skip to content
← Back

src/syntax/process/compile-expressions/compile_expressions_control_flow.ghul

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