Skip to content
← Back

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

1
namespace Syntax.Process is
2
use System.Exception
3
4
use Logging
5
6
use Semantic.Types.Type
7
8
use IR.Values
9
10
// Compiles bindings, assignments and returns: `let` statements,
11
// assignment statements and their simple-left targets, `let in`
12
// expressions, expression statements and `return`. Split out of
13
// COMPILE_EXPRESSIONS, which delegates the matching pre / visit
14
// methods here. The `super.pre` / `super.visit` base-visitor
15
// calls stay in the visitor's thin stubs; the methods here are
16
// the enclosed logic.
17
class COMPILE_BINDINGS is
18
_logger: Logger
19
_symbol_table: Semantic.SYMBOL_TABLE
20
_symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS
21
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
22
_task_conversion: Semantic.TASK_CONVERSION
23
_awaitable_resolver: Semantic.AWAITABLE_RESOLVER
24
_flow: NARROWING_FLOW
25
_build_flags: Compiler.GLOBAL_BUILD_FLAGS
26
_value_boxer: IR.VALUE_BOXER
27
_pure_slots: PURE_SLOT_CHECK
28
_visitor: COMPILE_EXPRESSIONS
29
_match_propagator: Semantic.MATCH_PROPAGATOR
30
31
// Read-only accessor: COMPILE_EXPRESSIONS (the
32
// expression-body return path) goes through `_bindings` as
33
// its single shared facade and reaches the TASK helpers
34
// via this property.
35
task_conversion: Semantic.TASK_CONVERSION => _task_conversion
36
37
init(
38
logger: Logger,
39
symbol_table: Semantic.SYMBOL_TABLE,
40
symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS,
41
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
42
task_conversion: Semantic.TASK_CONVERSION,
43
awaitable_resolver: Semantic.AWAITABLE_RESOLVER,
44
flow: NARROWING_FLOW,
45
build_flags: Compiler.GLOBAL_BUILD_FLAGS,
46
value_boxer: IR.VALUE_BOXER,
47
pure_slots: PURE_SLOT_CHECK,
48
visitor: COMPILE_EXPRESSIONS
49
) is
50
super.init()
51
52
_logger = logger
53
_symbol_table = symbol_table
54
_symbol_use_locations = symbol_use_locations
55
_innate_symbol_lookup = innate_symbol_lookup
56
_task_conversion = task_conversion
57
_awaitable_resolver = awaitable_resolver
58
_flow = flow
59
_build_flags = build_flags
60
_value_boxer = value_boxer
61
_pure_slots = pure_slots
62
_visitor = visitor
63
_match_propagator = Semantic.MATCH_PROPAGATOR(logger)
64
si
65
66
pre_let(`let: Trees.Statements.LET) -> bool is
67
// Reset is_defined on each LHS variable at the start of
68
// every body-retry walk of the let. Without this reset,
69
// an earlier walk's define() call leaves is_defined=true,
70
// and check_is_defined in `load_captured_value` no longer
71
// fires for a forward self-reference inside the RHS
72
// lambda on subsequent walks (`let f = ... f ...` is the
73
// canonical case). The error gets rolled back per iter
74
// via mark_consumed_any rolling back diagnostics, but
75
// the final iter's error survives so the user-visible
76
// diagnostic is preserved.
77
for v in `let.variables do
78
for name in v.names do
79
let symbol = _visitor.find(name)
80
if symbol? /\ isa Semantic.Symbols.Variable(symbol) then
81
let variable = symbol
82
variable.is_defined = false
83
fi
84
od
85
od
86
87
return false
88
si
89
90
visit_let(`let: Trees.Statements.LET) is
91
// A non-mut local must carry an initializer — an
92
// immutable local variable without a value at its
93
// declaration could only ever be set by a later
94
// assignment, and those are rejected at the store site
95
// below. Catching it here keeps the diagnostic on the
96
// declaration that is missing the `= expr`.
97
for v in `let.variables do
98
if !v.initializer? /\ !v.is_mutable_marked then
99
_logger.error(v.location, "local value must be initialized")
100
fi
101
od
102
103
// Definite assignment: a `let` binding with no
104
// initializer introduces a deferred-init local. Until it
105
// is assigned, a read of it is a use-before-assignment.
106
if !_build_flags.no_warn_definite_assignment then
107
for v in `let.variables do
108
if !v.initializer? then
109
for name in v.names do
110
let symbol = _visitor.find(name)
111
112
if symbol? /\ isa Semantic.Symbols.Variable(symbol) then
113
_flow.track_deferred(symbol)
114
fi
115
od
116
fi
117
od
118
fi
119
120
if `let.want_dispose then
121
// A parenthesised block's statements are not lowered
122
// through the statement-list path that builds a
123
// disposal region, so a local declared directly in one
124
// has nothing to dispose it. Reported here rather than
125
// left to generate-il, which cannot report anything.
126
if
127
let block = _visitor.innermost_val_block /\
128
block.body == _visitor.current_statement_list
129
then
130
_logger.error(
131
`let.location,
132
"cannot dispose a local declared in a parenthesised block")
133
fi
134
135
let idisposable = _innate_symbol_lookup.get_idisposable_type()
136
137
for v in `let.variables do
138
for name in v.names do
139
let symbol = _visitor.find(name)
140
141
if symbol? /\ isa Semantic.Symbols.Variable(symbol) then
142
let variable = symbol
143
variable.is_disposed = true
144
145
let type = variable.type
146
147
if type? /\ !idisposable.is_assignable_from(type) then
148
_logger.error(name.location, "not disposable")
149
fi
150
151
_visitor.current_statement_list.add_variable_to_dispose(variable)
152
fi
153
od
154
od
155
fi
156
si
157
158
pre_simple_left(left: Trees.Expressions.SIMPLE_LEFT_EXPRESSION) -> bool is
159
if let left.value? then
160
if !value.is_consumable then
161
_logger.error(left.location, "cannot use this here")
162
return false
163
fi
164
165
left.expression.compile_expressions_state.value = Need.STORE(value)
166
fi
167
168
return false
169
si
170
171
visit_simple_left(left: Trees.Expressions.SIMPLE_LEFT_EXPRESSION) is
172
let value = left.value
173
let expression_value = left.expression.value
174
175
if !value? \/ !value.type? \/ !expression_value? \/ !expression_value.type? then
176
return
177
fi
178
179
// Snapshot the presence-narrowed type before the queries
180
// below cross the fact it was read through.
181
let value_type = value.type
182
183
if !expression_value.is_consumable then
184
_logger.error(left.location, "cannot assign to this")
185
return
186
fi
187
188
// Bypass narrowing for assignability — the LHS value's
189
// type may be a narrowed view, but assignment must
190
// typecheck against the variable's declared type, or
191
// `if isa T(x) then x = wider_value` (idiomatic across
192
// the codebase) would fail at the narrow.
193
let target_variable = _visitor.try_get_narrowing_target(left.expression)
194
195
// A member of the enclosing type is recorded as written
196
// whether or not the narrowing target agrees - see
197
// try_get_member_assigned. Locals are left to the
198
// mark_assigned below, which the narrowing target covers.
199
let member_target = _visitor.try_get_member_assigned(left.expression)
200
201
if member_target? then
202
_flow.mark_assigned(member_target)
203
fi
204
205
let lhs_type =
206
if target_variable? then
207
// A local assigned before the declaration that types
208
// it - an assignment inside the local's own
209
// initializer - has no declared type to check
210
// against, and the read that reached it is already
211
// reported. What the target compiled to is then all
212
// there is to judge by.
213
_flow.declared_type_of(target_variable) ?? expression_value.type!
214
else
215
expression_value.type!
216
fi
217
218
if !lhs_type.is_assignable_from(value_type) then
219
_logger.error(left.assign_location, "{value_type} is not assignable to {lhs_type}")
220
return
221
fi
222
223
// Read the RHS static type and non-optional-ness off the
224
// RHS expression value before the overwrite below replaces
225
// `left.value` with the Store.SYMBOL IR whose type is the
226
// variable's declared type — so a `BOX? mut` slot would mask
227
// a non-null RHS like `BOX(1)`.
228
let rhs_type = value_type
229
let rhs_is_non_optional = _visitor.is_non_optional_value(value)
230
231
left.compile_expressions_state.value = expression_value
232
233
// Reassignment invalidates any narrow for the target —
234
// the new value may not satisfy it, so subsequent reads
235
// observe the declared type — and makes the target
236
// definitely assigned from here on.
237
if target_variable? then
238
// Kill the invalidated facts and re-narrow to the RHS
239
// static type when it is more specific than the
240
// declared type, so a later read sees DOG after
241
// `pet = DOG()`. The transfer also emits the editor
242
// hint for whichever of kill / re-narrow applied.
243
let narrowed = _flow.on_assignment(target_variable, left.assign_location, rhs_type)
244
245
_flow.mark_assigned(target_variable)
246
247
// A non-optional RHS leaves the target known to hold
248
// a value — so `_field = arg; _field.method()` does
249
// not warn. Keyed off the declared type, not the
250
// possibly-narrowed view above: the presence fact lives
251
// in its own lattice channel and must still be recorded
252
// when a branch also type-narrows, so it composes at a
253
// join with a sibling branch that carries only presence.
254
let declared_target = _flow.declared_type_of(target_variable)
255
let want_presence =
256
rhs_is_non_optional /\
257
declared_target? /\
258
declared_target.is_optional
259
260
if want_presence then
261
_flow.mark_non_null(target_variable)
262
263
// A type narrow's hint has already been emitted by
264
// the transfer; the presence-only view is a hint of
265
// its own only when no narrow applied.
266
if !narrowed? then
267
_flow.report_narrowing_site(
268
left.assign_location,
269
"narrowing-assign",
270
"►",
271
INLAY_TYPE.render(target_variable.type!.as_non_optional())
272
)
273
fi
274
fi
275
fi
276
si
277
278
visit_let_in(let_in: Trees.Expressions.LET_IN) is
279
if let let_in.expression.value? /\ value.check_is_consumable_allow_void(_logger, let_in.expression.location) then
280
let_in.compile_expressions_state.value = value
281
else
282
let_in.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), let_in.location)
283
fi
284
si
285
286
pre_assignment(assignment: Trees.Statements.ASSIGNMENT) -> bool is
287
// A member / index target reads its receiver before the
288
// right-hand side runs, so a call there must not drop a field
289
// narrow the receiver relies on. Shield that field across the
290
// whole assignment; once the target is walked, drop it iff a
291
// call actually occurred, so later statements still see the
292
// post-call state.
293
let receiver_field = _try_get_assignment_receiver_field(assignment.left)
294
let shield_frame = _flow.push_shield(receiver_field)
295
296
// Push the LHS type down to the RHS as a constraint. The
297
// RHS expression node is responsible for either consuming
298
// it (FUNCTION uses it for argument-type inference,
299
// SEQUENCE / TUPLE forward to their elements) or ignoring
300
// it (literals, identifiers — Expression's default
301
// set_constraint is a no-op). The post-walk assignability
302
// check still verifies type correctness for the cases
303
// where the RHS doesn't act on the constraint.
304
//
305
// When the LHS is itself a not-yet-resolved placeholder
306
// (`let l; ...; l = X` shape), the LHS type is not
307
// useful as a constraint to push *down*. Instead, after
308
// walking the RHS, push the RHS's type back to the
309
// LHS variable's symbol via add_constraint — the
310
// existing iterative-inference machinery will collapse
311
// the LUB across all assignments on the next retry-
312
// loop iteration.
313
let lhs_type = _try_get_assignment_left_type(assignment.left)
314
315
// `!is_inferred` (not !is_sentinel or is_settled):
316
// we're asking "is the LHS anything other than a
317
// bare placeholder?". ERROR is pre-filtered by
318
// _try_get_assignment_left_type. A composite-with-
319
// placeholder LHS like Function[placeholder, int]
320
// *is* useful as an RHS constraint — the placeholder
321
// arg slot is filled by the lambda's body inference,
322
// and the return slot does its job.
323
if lhs_type? /\ !lhs_type.is_inferred /\ !lhs_type.is_error then
324
assignment.right.set_expected_type(lhs_type, "{{0}} is not assignable to {{1}}")
325
fi
326
327
assignment.right.walk(_visitor)
328
329
let right_value = assignment.right.value
330
331
if !right_value? then
332
return true
333
fi
334
335
if !right_value.is_consumable then
336
_logger.error(assignment.right.location, "cannot use this here")
337
assignment.left.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), assignment.right.location)
338
else
339
assignment.left.compile_expressions_state.value = right_value
340
341
_visitor.check_non_optional(lhs_type, assignment.right, assignment.right.location)
342
343
_pure_slots.check_store(assignment.right.location, lhs_type, right_value)
344
fi
345
346
// Back-feed the RHS type as a constraint onto a
347
// placeholder-typed LHS variable (`let l; l = X` etc.).
348
// The next retry-loop iteration of the enclosing
349
// function-body walk picks up the LUB via
350
// try_get_inferred_type at visit(SIMPLE_VARIABLE_LEFT).
351
//
352
// `!is_settled` (not is_inferred or contains_inferred): a
353
// provisional composite LHS like `Func[List[int],
354
// INFERRED_RETURN_TYPE]` — recorded on an early iter when
355
// the lambda's body was walked before the return type
356
// resolved — still needs refining once the placeholder
357
// inside settles. Same goes for an ERROR-carrying
358
// composite like `(ERROR, int)` left over from an iter
359
// where a sub-expression poisoned to DUMMY(ERROR):
360
// a later iter may produce a clean `(int, int)` value,
361
// and without the match propagation the LUB never picks it up
362
// and the per-position merge prefers the stale ERROR
363
// slot. `is_settled` ("no placeholders, no errors")
364
// captures both senses in one predicate.
365
if
366
lhs_type? /\
367
(!lhs_type.is_settled \/ _is_joining_local(assignment.left)) /\
368
right_value.type? /\
369
_try_propagate_assignment_lhs(assignment.left, right_value.type)
370
then
371
_logger.mark_consumed_any()
372
fi
373
374
assignment.left.assign_location = assignment.location
375
376
assignment.left.walk(_visitor)
377
378
// The target walk compiles the target's name, which the
379
// narrowing flow sees as a read — but a store target is
380
// written, not read, so any load it recorded is moot.
381
382
if _flow.release_shield(shield_frame) /\ receiver_field? then
383
_flow.forget(receiver_field)
384
fi
385
386
// A store to anything but a local can change what a
387
// property getter returns, so property facts must not
388
// survive it. Field facts do — a store to one field
389
// cannot alter another, and the assignment transfer
390
// above already handled the target itself.
391
if _is_heap_store_target(assignment.left) then
392
_flow.on_heap_store(assignment.location)
393
fi
394
395
assignment.compile_expressions_state.value = assignment.left.value
396
397
return true
398
si
399
400
// True when an assignment target writes the heap: a member,
401
// index, field or property target — anything except a plain
402
// local variable or parameter. Destructuring stores to the
403
// heap when any element does.
404
405
_is_heap_store_target(left: Trees.Expressions.AssignmentLeftExpression?) -> bool is
406
if !left? then
407
return true
408
fi
409
410
if isa Trees.Expressions.DESTRUCTURING_LEFT_EXPRESSION(left) then
411
for element in (cast Trees.Expressions.DESTRUCTURING_LEFT_EXPRESSION(left)).elements do
412
if _is_heap_store_target(element) then
413
return true
414
fi
415
od
416
417
return false
418
fi
419
420
if !isa Trees.Expressions.SIMPLE_LEFT_EXPRESSION(left) then
421
return true
422
fi
423
424
let expression = (cast Trees.Expressions.SIMPLE_LEFT_EXPRESSION(left)).expression
425
426
if !isa Trees.Expressions.IDENTIFIER(expression) then
427
return true
428
fi
429
430
let identifier = (cast Trees.Expressions.IDENTIFIER(expression)).identifier
431
432
if identifier.is_qualified then
433
return true
434
fi
435
436
let symbol = _visitor.try_find(identifier)
437
438
return
439
!symbol? \/
440
!(isa Semantic.Symbols.LOCAL_VARIABLE(symbol) \/ isa Semantic.Symbols.LOCAL_ARGUMENT(symbol))
441
si
442
443
// The field variable a member / index assignment target reads
444
// as its receiver — the shield subject in `pre_assignment`.
445
// Null unless the target is `field.member` / `field[index]`
446
// rooted at a bare field identifier.
447
_try_get_assignment_receiver_field(left: Trees.Expressions.AssignmentLeftExpression) -> Semantic.Symbols.Symbol? is
448
let simple = cast Trees.Expressions.SIMPLE_LEFT_EXPRESSION?(left)
449
450
if !simple? then
451
return null
452
fi
453
454
let target = simple.expression
455
let receiver =
456
if isa Trees.Expressions.MEMBER(target) then
457
(cast Trees.Expressions.MEMBER(target)).left
458
elif isa Trees.Expressions.INDEX(target) then
459
(cast Trees.Expressions.INDEX(target)).left
460
else
461
null
462
fi
463
464
if !receiver? then
465
return null
466
fi
467
468
return _visitor.try_get_narrowing_target(receiver)
469
si
470
471
// If the assignment's LHS is a simple identifier referring
472
// to a Variable whose current type is an inference
473
// placeholder, push the supplied RHS type onto the
474
// variable's LUB accumulator. Returns true when a real
475
// constraint actually landed (so the caller can signal
476
// progress to the retry loop).
477
// A `mut` local whose type is the join of its initializer and its
478
// assignments: each assignment is a bound on that join even after
479
// the local's type has settled.
480
_is_joining_local(left: Trees.Expressions.AssignmentLeftExpression) -> bool is
481
if
482
let simple: Trees.Expressions.SIMPLE_LEFT_EXPRESSION = left,
483
identifier: Trees.Expressions.IDENTIFIER = simple.expression
484
then
485
if let variable: Semantic.Symbols.Variable = _visitor.find(identifier.identifier) then
486
return variable.joins_assignments
487
fi
488
fi
489
490
return false
491
si
492
493
_try_propagate_assignment_lhs(
494
left: Trees.Expressions.AssignmentLeftExpression,
495
rhs_type: Semantic.Types.Type
496
) -> bool is
497
let simple = cast Trees.Expressions.SIMPLE_LEFT_EXPRESSION?(left)
498
499
if !simple? then
500
return false
501
fi
502
503
let identifier = cast Trees.Expressions.IDENTIFIER?(simple.expression)
504
505
if !identifier? then
506
return false
507
fi
508
509
let symbol = _visitor.find(identifier.identifier)
510
511
if !symbol? \/ !isa Semantic.Symbols.Variable(symbol) then
512
return false
513
fi
514
515
let variable = symbol
516
517
// A value typed over another function's type parameter - the
518
// `T[]` a `collect_array[T]` call still resolving reports - is
519
// no type the variable could take; a later walk supplies the
520
// one it settles to.
521
if rhs_type.has_function_generic_argument_foreign_to(_symbol_table.current_scope) then
522
return false
523
fi
524
525
return Semantic.INFERENCE_TRACE.add_lower_bound("bindings.assignment_lhs", variable, rhs_type)
526
si
527
528
// The type a SIMPLE_LEFT_EXPRESSION's target stores. Destructuring
529
// lefts are skipped — extracting their effective type needs a
530
// tuple-shape construction we don't have yet.
531
_try_get_assignment_left_type(left: Trees.Expressions.AssignmentLeftExpression) -> Type? is
532
let simple = cast Trees.Expressions.SIMPLE_LEFT_EXPRESSION?(left)
533
534
if !simple? then
535
return null
536
fi
537
538
let (resolved, type) = _resolve_assignment_target_type(simple.expression)
539
540
if resolved then
541
return type
542
fi
543
544
return _probe_assignment_left_type(simple)
545
si
546
547
// Read the target's type off its symbol without compiling it,
548
// where the target names one: a local, field or readable
549
// property, `self.member`, or the pointee of a `ref` local.
550
// Resolving the name can report it missing, so the logger is
551
// rolled back to keep the lookup invisible, as the probe is.
552
_resolve_assignment_target_type(target: Trees.Expressions.Expression) -> (bool, Type?) is
553
let mark = _logger.mark()
554
555
_logger.speculate()
556
557
try
558
return _resolve_assignment_target_type_silently(target)
559
finally
560
_logger.roll_back()
561
_logger.release(mark)
562
yrt
563
si
564
565
_resolve_assignment_target_type_silently(target: Trees.Expressions.Expression) -> (bool, Type?) is
566
let symbol = _find_assignment_target_symbol(target)
567
568
if !symbol? then
569
return (false, null)
570
fi
571
572
let type = _flow.declared_type_of(symbol)
573
574
if !type? then
575
return (false, null)
576
fi
577
578
if isa Trees.Expressions.UNWRAP(target) then
579
if !type.is_ref then
580
return (false, null)
581
fi
582
583
let element_type = type.get_element_type()
584
585
if !element_type? then
586
return (false, null)
587
fi
588
589
return (true, element_type)
590
fi
591
592
return (true, type)
593
si
594
595
_find_assignment_target_symbol(target: Trees.Expressions.Expression) -> Semantic.Symbols.Symbol? is
596
if let unwrap: Trees.Expressions.UNWRAP = target then
597
return _visitor.try_get_narrowing_target(unwrap.left)
598
fi
599
600
let narrowing_target = _visitor.try_get_narrowing_target(target)
601
602
if narrowing_target? then
603
return narrowing_target
604
fi
605
606
let member = cast Trees.Expressions.MEMBER?(target)
607
608
if
609
!member? \/
610
!isa Trees.Expressions.SELF(member.left) \/
611
member.is_coalesce \/
612
member.identifier.is_qualified
613
then
614
return null
615
fi
616
617
let context = _visitor.current_instance_context
618
619
if !context? then
620
return null
621
fi
622
623
let symbol = context.find_direct(member.identifier.name)
624
625
if isa Semantic.Symbols.Variable(symbol) then
626
return symbol
627
fi
628
629
if let property: Semantic.Symbols.Property = symbol /\ property.read_function? then
630
return property
631
fi
632
633
return null
634
si
635
636
// For targets whose type depends on compiling them - an indexer,
637
// a member reached through anything but `self` - speculatively
638
// walk the target as a regular load, read its type, and roll
639
// back the logger so the probe is invisible to later passes.
640
// The probe walks the live expression rather than a copy: the
641
// subsequent normal walk through pre(SIMPLE_LEFT_EXPRESSION)
642
// overwrites the inner expression's value with the STORE form
643
// anyway, so the probe's read-form value never escapes.
644
_probe_assignment_left_type(simple: Trees.Expressions.SIMPLE_LEFT_EXPRESSION) -> Type? is
645
let mark = _logger.mark()
646
let type: Type? mut = _
647
648
// The probe's walk must be invisible to the symbol-use
649
// maps as well as to the logger: it compiles the target
650
// as a read, and the read-shaped use it would record
651
// out-ranks the store-shaped one the real walk records
652
// at the same location (first recorded wins a hover tie).
653
_symbol_use_locations.begin_suppress()
654
655
try
656
let use retry_site = RETRY_SITE_STATS.enter("bindings.assignment_target_probe", RetrySiteKind.ALTERNATIVE)
657
_logger.speculate()
658
659
simple.expression.walk(_visitor)
660
661
let expression_value = simple.expression.value
662
663
// is_error left unfiltered here: an ERROR-bearing
664
// composite (e.g. `(ERROR, int)` set by an earlier
665
// iter on a delayed-init variable whose first-seen
666
// RHS poisoned to DUMMY(ERROR)) is exactly the case
667
// the assignment match propagation below needs to recognise
668
// — without the LHS type, the match propagation gate
669
// gets null, no refinement constraint lands, and
670
// the LUB stays frozen. Call sites that don't want
671
// to pass ERROR onward (the set_constraint push to
672
// RHS) gate it themselves.
673
if expression_value? /\ expression_value.type? then
674
let target_variable = _visitor.try_get_narrowing_target(simple.expression)
675
676
type =
677
if target_variable? then
678
_flow.declared_type_of(target_variable)
679
else
680
expression_value.type
681
fi
682
fi
683
684
_logger.roll_back()
685
_logger.release(mark)
686
catch e: Exception
687
_logger.release(mark)
688
return null
689
finally
690
_symbol_use_locations.end_suppress()
691
yrt
692
693
return type
694
si
695
696
visit_expression_statement(expression: Trees.Statements.EXPRESSION) is
697
let inner_value = expression.expression.value
698
if inner_value? then
699
Value.check_is_consumable_allow_void(_logger, expression.expression.location, inner_value)
700
701
// A call whose own type parameter appears only in its
702
// return type is left carrying it for whatever consumes
703
// the value to settle, and a statement consumes nothing,
704
// so nothing will. Reported only on an otherwise clean
705
// walk, where it is not the consequence of another error.
706
if
707
_logger.is_clean /\
708
inner_value.type? /\
709
inner_value.type.has_function_generic_argument_foreign_to(_symbol_table.current_function)
710
then
711
_logger.error(expression.expression.location, "cannot infer type here")
712
fi
713
fi
714
715
if !expression.want_value then
716
if expression.expression.must_be_consumed then
717
_logger.error(expression.expression.location, "{expression.expression.description} result is not used")
718
fi
719
720
// A re-walk that no longer wants a value (e.g. the
721
// enclosing literal's return type settled to void
722
// between walks) must not leave behind the value an
723
// earlier value-wanting walk recorded on the
724
// statement — generate-il's val-block tail emission
725
// reads the last statement's value even for a void
726
// block and would emit that stale IR alongside the
727
// fresh one.
728
expression.compile_expressions_state.value = null
729
730
return
731
fi
732
733
expression.compile_expressions_state.value = expression.expression.value
734
si
735
736
pre_return(r: Trees.Statements.RETURN) -> bool is
737
// Stamp the innermost-val-block target onto the RETURN
738
// AST node so visit_return and generate-il dispatch
739
// consistently from a single source of truth, even if the
740
// val-block stack is unwound by the time those fire.
741
r.val_block_target = _visitor.innermost_val_block
742
743
if let target = r.val_block_target then
744
target.has_targeted_return = true
745
fi
746
747
if !r.expression? then
748
return false
749
fi
750
751
if let block = r.val_block_target then
752
// Return targets a val-block — push the block's
753
// pushed-down expected_type (if any) onto the return
754
// expression so it participates in inference /
755
// overload resolution against the consumer's context,
756
// mirroring the function-return path.
757
if let expected = block.expected_type then
758
if let error_message = block.expected_type_error_message then
759
r.expression.set_expected_type(expected, error_message)
760
fi
761
fi
762
763
return false
764
fi
765
766
let function = _symbol_table.current_function
767
768
assert function? else "return outside a function"
769
770
if
771
!function.return_type? \/
772
function.return_type.is_sentinel \/
773
function.return_type.matches(_innate_symbol_lookup.get_void_type())
774
then
775
return false
776
fi
777
778
r.expression!.set_expected_type(function.return_type!, "cannot return value of type {{0}} where {{1}} expected")
779
780
return false
781
si
782
783
visit_return(r: Trees.Statements.RETURN) is
784
// Control does not fall through a return — the rest of
785
// the enclosing block is unreachable.
786
_flow.set_unreachable()
787
788
if let block = r.val_block_target then
789
// Return targets the innermost enclosing val-block:
790
// collect the expression's type into the block's LUB
791
// pool and stop here. Function-level return-type
792
// inference, async-SM rewiring, and the various
793
// assignability / wrap rules below are scoped to
794
// function-return; a val-targeted return is a
795
// local control transfer, not a function exit.
796
if let expression = r.expression then
797
if let value = expression.value then
798
if let value_type = value.type then
799
if !value.check_is_consumable(_logger, expression.location) then
800
return
801
fi
802
803
if !value_type.is_error then
804
if let expected = block.expected_type then
805
// Honour the pushed-down expected_type
806
// the same way function-return does:
807
// error if the expression's type isn't
808
// assignable.
809
if !expected.is_void /\ !expected.is_assignable_from(value_type) then
810
let error_message mut = block.expected_type_error_message
811
if !error_message? then
812
error_message = "cannot return value of type {{0}} where {{1}} expected"
813
fi
814
_logger.error(
815
expression.location,
816
string.format(error_message, value_type, expected)
817
)
818
return
819
fi
820
fi
821
822
block.return_types.add(value_type)
823
fi
824
fi
825
fi
826
else
827
// Bare `return;` inside a val-block. Diagnose
828
// only when an outer expected_type makes the
829
// value requirement unambiguous (typed `let`
830
// initializer, function argument, value-
831
// returning `=> body`). want_value alone reads
832
// stale for inferred-return lambdas — the
833
// lambda's return type isn't pinned until after
834
// the body walks, so a bare return that's
835
// consistent with a void inference would error
836
// here prematurely. Generate-il stores the
837
// default of the result type on the no-
838
// diagnostic path so the IL still verifies.
839
if let expected = block.expected_type then
840
if !expected.is_void then
841
_logger.error(r.location, "return without value from val block requiring a value")
842
fi
843
fi
844
fi
845
846
return
847
fi
848
849
let function = _symbol_table.current_function
850
851
if !function? \/ !function.return_type? then
852
// FIXME: null function happens for properties, null return type happens for anonymous functions
853
return
854
fi
855
856
// State-machine async: `return X` provides X of type T
857
// (the Task element type), generate-il stashes X into
858
// `_result` and leaves to success_label.
859
let async_sm = Semantic.Symbols.async_state_machine_for(function)
860
if async_sm? /\ async_sm.frame? then
861
_visit_return_state_machine_async(r, function, async_sm)
862
return
863
fi
864
865
// A value whose type is still a placeholder has to fit the
866
// declared return type, which bounds it as an argument slot
867
// bounds a placeholder passed to it.
868
if
869
let value = r.expression?.value /\
870
isa Semantic.Types.INFERRED_VARIABLE_TYPE(value.type) /\
871
!function.return_type!.is_sentinel
872
then
873
_match_propagator.propagate_match(function.return_type!, value.type!)
874
fi
875
876
if r.expression? then
877
if let
878
r.expression.value? /\
879
value.type? /\
880
value.check_is_consumable(_logger, r.expression.location)
881
then
882
let null_join = Semantic.ARM_NULL_JOIN()
883
884
// A null says a value can be absent without saying
885
// what it holds when present, so it settles an
886
// inferred return at nothing: it records that the
887
// return is optional, and leaves what it is optional
888
// of to the other returns, or to the slot the
889
// literal goes into.
890
if function.return_type!.is_inferred /\ null_join.is_genuine_null(value.type!) then
891
function.returned_genuine_null = true
892
elif function.return_type!.is_inferred then
893
// Filling in a previously-undeclared return
894
// type from this return statement. is_inferred
895
// (today only INFERRED_RETURN_TYPE) rather than
896
// is_sentinel: a function whose return type was
897
// already set to ERROR by an earlier failing
898
// return shouldn't get silently overwritten
899
// with a concrete type by a later (validly-
900
// typed) return — the original error should
901
// remain reported and the return-type contract
902
// should stay ERROR until the user fixes the
903
// source.
904
//
905
// Async closures: wrap a bare-T body return
906
// to Task[T] before pinning, so the closure's
907
// signature matches its SM emission shape.
908
// Values already typed Task[?] pass through.
909
let value_type: Semantic.Types.Type? mut = value.type!
910
911
if
912
function.wrap_inferred_return_as_task /\
913
value_type.is_settled /\
914
!_task_conversion.is_task_type(value_type)
915
then
916
let task_type = _innate_symbol_lookup.get_task_type(value_type)
917
918
// Wrap as `Tasks.TASK.from_result(orig)`.
919
// Guard on `is_settled` so we don't fire
920
// during iterative-inference walks with a
921
// partially-resolved body type — those
922
// would over-wrap to `Task[Task[?]]`.
923
if task_type? /\ _task_conversion.try_wrap_value_as_task_return(r, task_type, _visitor) then
924
// The wrap replaced r.expression and
925
// re-walked it — read the fresh value,
926
// not the pre-wrap snapshot.
927
value_type = r.expression!.value!.type
928
fi
929
fi
930
931
let pinned = Semantic.MAYBE_RETURN_PIN.of(function, value_type!, _innate_symbol_lookup)
932
933
function.set_return_type(Semantic.NULL_RETURN_JOIN.of(pinned, function.returned_genuine_null, _innate_symbol_lookup))
934
elif function.return_type!.matches(_innate_symbol_lookup.get_void_type()) /\ !function.return_type!.is_type_variable then
935
_logger
936
.error(
937
r.expression!.location,
938
"cannot return value from function of void type"
939
)
940
elif !function.return_type!.is_assignable_from(value.type!) then
941
// Implicit T → TASK[T] widening at return position
942
// (the C# `async` return-rewrap rule applied to ghūl).
943
// Synthesises Tasks.TASK.from_result(orig) around the
944
// original expression and re-resolves the wrapper.
945
// Restricted to return position — variable init /
946
// argument passing slot boundaries don't widen this way.
947
if _task_conversion.try_wrap_value_as_task_return(r, function.return_type!, _visitor) then
948
// wrap succeeded — type now matches
949
elif function.return_type_was_inferred /\ null_join.is_genuine_null(value.type!) then
950
// The return settled on an earlier value and
951
// this one says it can also be absent.
952
function.returned_genuine_null = true
953
954
function.set_return_type(
955
Semantic.NULL_RETURN_JOIN.of(function.return_type!, function.returned_genuine_null, _innate_symbol_lookup))
956
elif function.return_type_was_inferred then
957
let lub = Semantic.LEAST_UPPER_BOUND_MAP()
958
lub.add(function.return_type!)
959
lub.add(value.type!)
960
let widened = lub.get_result()
961
962
if widened? then
963
function.set_return_type(widened)
964
else
965
_logger
966
.error(
967
r.expression!.location,
968
"cannot return value of type {value.type} where {function.return_type} expected"
969
)
970
fi
971
else
972
_logger
973
.error(
974
r.expression!.location,
975
"cannot return value of type {value.type} where {function.return_type} expected"
976
)
977
fi
978
fi
979
980
_visitor.check_non_optional(function.return_type, r.expression, r.expression!.location)
981
982
_pure_slots.check_store(r.expression!.location, function.return_type, r.expression!.value)
983
984
return
985
fi
986
else
987
// Void async: bare `return;` sugar — synthesise
988
// `return Tasks.TASK.completed_task;` and re-walk.
989
// `is_void_async` is stamped on a body that awaits, so
990
// it covers the state-machine lowering; a machine-less
991
// Tasks.TASK return completes the same way.
992
if
993
function.is_void_async \/
994
_task_conversion.is_void_task_return(function.return_type)
995
then
996
r.expression = Semantic.TASK_CONVERSION.build_completed_task_expression(r.location)
997
r.expression!.walk(_visitor)
998
999
return
1000
fi
1001
1002
// In a generator a bare `return` ends the stream, so the
1003
// declared `Pipe[T]` is not a value any return carries.
1004
if Semantic.Symbols.state_machine_for(function)? then
1005
return
1006
fi
1007
1008
// A machine-less return of some other result-less
1009
// task-like completes through its builder too - IL
1010
// generation emits the driving sequence for the bare
1011
// return - so no value is missing here either.
1012
if _task_conversion.completes_without_machine(function.return_type) then
1013
return
1014
fi
1015
1016
if !function.return_type!.matches(_innate_symbol_lookup.get_void_type()) then
1017
_logger
1018
.warn(
1019
r.location,
1020
"return-without-value",
1021
"return without value from non void function returns default value of type {function.return_type}"
1022
)
1023
fi
1024
fi
1025
si
1026
1027
// Type-check `return X` against the element type T (not
1028
// Task[T]); bare `return;` is left for generate-il to map to
1029
// `leave success_label`.
1030
_visit_return_state_machine_async(
1031
r: Trees.Statements.RETURN,
1032
function: Semantic.Symbols.Function,
1033
async_sm: Semantic.Symbols.ASYNC_STATE_MACHINE
1034
) is
1035
let frame = async_sm.frame
1036
1037
assert frame? else "async state machine has no frame at return"
1038
1039
if r.expression? then
1040
let expression_value = r.expression.value
1041
1042
if
1043
!expression_value? \/
1044
!expression_value.type? \/
1045
!expression_value.check_is_consumable(_logger, r.expression.location)
1046
then
1047
return
1048
fi
1049
1050
if frame.is_void then
1051
if !_try_await_returned_value(r, expression_value.type!, null) then
1052
_logger.error(
1053
r.expression!.location,
1054
"cannot return value from function of void type"
1055
)
1056
fi
1057
return
1058
fi
1059
1060
// Async-closure inference: lambdas with
1061
// INFERRED_RETURN_TYPE + wrap_inferred_return_as_task
1062
// pin from the body's value-returning statement here.
1063
// The SM frame's result_type follows via
1064
// ensure_result_field.
1065
if
1066
function.return_type? /\
1067
function.return_type.is_inferred /\
1068
function.wrap_inferred_return_as_task /\
1069
expression_value.type? /\
1070
expression_value.type.is_settled /\
1071
!_task_conversion.is_task_type(expression_value.type!)
1072
then
1073
let task_type =
1074
_task_conversion.async_return_type_for(function, expression_value.type!)
1075
if task_type? then
1076
function.set_return_type(task_type)
1077
fi
1078
fi
1079
1080
let element_type = frame.result_type
1081
1082
if !element_type? then
1083
return
1084
fi
1085
1086
if
1087
!element_type.is_assignable_from(expression_value.type!) /\
1088
!_try_await_returned_value(r, expression_value.type!, element_type)
1089
then
1090
_logger.error(
1091
r.expression!.location,
1092
"cannot return value of type {expression_value.type} where {element_type} expected"
1093
)
1094
return
1095
fi
1096
1097
_visitor.check_non_optional(element_type, r.expression, r.expression!.location)
1098
1099
_pure_slots.check_store(r.expression!.location, element_type, r.expression!.value)
1100
else
1101
// A literal whose return was left to its body to settle:
1102
// a bare `return` is only a return at all where the body
1103
// carries no result, so it settles the literal as
1104
// void-async here rather than waiting for a tail that
1105
// may never deliver one.
1106
if let rt = function.return_type then
1107
if rt.is_inferred /\ function.wrap_inferred_return_as_task then
1108
if let void_task = _innate_symbol_lookup.get_void_task_type() then
1109
function.set_return_type(void_task)
1110
function.is_void_async = true
1111
fi
1112
fi
1113
fi
1114
1115
let settled_frame = async_sm.frame ?? frame
1116
1117
// Bare `return;`. For value-async this is an
1118
// error — the result slot is non-void. For
1119
// void-async it's the usual no-op.
1120
if !settled_frame.is_void then
1121
_logger.warn(
1122
r.location,
1123
"return-without-value",
1124
"return without value from value-async function returns default value of type {settled_frame.result_type}"
1125
)
1126
fi
1127
fi
1128
si
1129
1130
// A returned value that does not fit the result slot but awaits
1131
// to something that does is returned as its awaited result: the
1132
// caller already holds this function's own task, so the
1133
// returned task can only ever complete it. A void slot takes a
1134
// value that awaits to nothing. Rewrites `r` to return the
1135
// await and answers false, leaving `r` alone, when the value is
1136
// not awaitable or its result does not fit either.
1137
_try_await_returned_value(
1138
r: Trees.Statements.RETURN,
1139
value_type: Semantic.Types.Type,
1140
element_type: Semantic.Types.Type?
1141
) -> bool is
1142
let awaitable = _awaitable_resolver.try_resolve(value_type)
1143
1144
if !awaitable? then
1145
return false
1146
fi
1147
1148
let result_type = awaitable.result_type
1149
1150
if element_type? then
1151
if !element_type.is_assignable_from(result_type) then
1152
return false
1153
fi
1154
elif !result_type.is_void then
1155
return false
1156
fi
1157
1158
let awaited = Trees.Expressions.AWAIT(r.expression!.location, r.expression!)
1159
1160
r.expression = awaited
1161
1162
awaited.accept(_visitor)
1163
1164
return true
1165
si
1166
1167
si
1168
si