Skip to content
← Back

src/syntax/process/compile-expressions/compile_expressions.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
16
use Syntax.Trees.Definitions.PRAGMA
17
18
use Ghul.Pipes
19
20
class COMPILE_EXPRESSIONS: ScopedVisitor is
21
_logger: Logger
22
_symbol_table: Semantic.SYMBOL_TABLE
23
_symbol_loader: Semantic.SYMBOL_LOADER
24
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
25
_awaitable_resolver: Semantic.AWAITABLE_RESOLVER
26
_function_caller: Semantic.FUNCTION_CALLER
27
_overload_resolver: Semantic.OVERLOAD_RESOLVER
28
_owner_type_arg_specializer: Semantic.OWNER_TYPE_ARG_SPECIALIZER
29
_owner_constraint_specializer: Semantic.OWNER_CONSTRAINT_SPECIALIZER
30
_unit_variant_constructor: Semantic.UNIT_VARIANT_CONSTRUCTOR
31
_under_determination_detector: Semantic.UNDER_DETERMINATION_DETECTOR
32
_type_arg_placeholder_registry: Semantic.TYPE_ARG_PLACEHOLDER_REGISTRY
33
_closure_arg_resolver: Semantic.CLOSURE_ARG_RESOLVER
34
_numeric_literal_classifier: NUMERIC_LITERAL_CLASSIFIER
35
_literals: COMPILE_LITERALS
36
_tuples: COMPILE_TUPLES
37
_operators: COMPILE_OPERATORS
38
_access: COMPILE_ACCESS
39
_generic_application: COMPILE_GENERIC_APPLICATION
40
_calls: COMPILE_CALLS
41
_conditionals: COMPILE_CONDITIONALS
42
_loops: COMPILE_LOOPS_AND_EXCEPTIONS
43
_bindings: COMPILE_BINDINGS
44
45
_pure_slots: PURE_SLOT_CHECK
46
_lambdas: COMPILE_LAMBDAS
47
_type_caster: Semantic.TYPE_CASTER
48
_symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS
49
_value_converter: VALUE_CONVERTER
50
_value_boxer: VALUE_BOXER
51
52
// The immutable, set-once command-line build flags. The
53
// GLOBAL_BUILD_FLAGS object is created with the IoC container;
54
// its fields are populated by the driver afterwards, so this
55
// captures the reference in init and reads fields at walk time.
56
// The `--no-warn-*` opt-outs for the definite-return /
57
// definite-assignment / possible-null-dereference / non-
58
// optional-by-default flow warnings are read straight off it.
59
_build_flags: Compiler.GLOBAL_BUILD_FLAGS
60
_variable_left_state: VARIABLE_LEFT_STATE_STORE
61
_composite_spill_state: COMPOSITE_SPILL_STORE
62
_function_reference_adapter: FUNCTION_REFERENCE_ADAPTER
63
64
_pragma_scope_stack: PRAGMA_SCOPE_STACK
65
_attribute_resolver: ATTRIBUTE_RESOLVER
66
67
// Depth-counter for nested `let await` walks. When > 0,
68
// Flow-sensitive narrowing — holds the narrowing environment
69
// in force at the current walk point and keeps each narrowed
70
// variable's `.type` reconciled to it. See
71
// `docs/claude/flow-sensitive-narrowing.md`.
72
_flow: NARROWING_FLOW
73
74
// Walk a subtree again for a retry. The retry has pushed what
75
// it learned onto the subtree as inputs; what the attempt being
76
// retried produced is dropped first, so the re-walk starts from
77
// a first walk's inputs and none of the previous outputs, as a
78
// whole-body re-walk does.
79
rewalk(node: Trees.Node) is
80
CLEAR_STATE_VISITOR(true, true).apply(node)
81
node.walk(self)
82
si
83
84
// The target of the `ref` whose operand is currently being
85
// walked. An operand of `ref` is an address, not a value read,
86
// so its identifier load must skip the definite-assignment
87
// check: the read-or-write decision belongs to the resolved
88
// call, in `note_reference_arguments`. Set in `pre(REFERENCE)`,
89
// cleared in `visit(reference)`.
90
_reference_operand_target: Semantic.Symbols.Symbol?
91
92
// Pure analysis of boolean conditions into then/else
93
// narrowing environments, plus the variant-type helpers.
94
_condition_analyzer: CONDITION_ANALYZER
95
96
// Stack of per-IF frames, one per IF currently being walked.
97
// pre(IF) pushes; each branch's controlled walk records its
98
// exit environment; visit(IF) joins them and pops.
99
_if_flow_stack: Collections.LIST[IF_FLOW_FRAME]
100
101
// Stack of loop kill-set environments, one per `while`/`do`
102
// currently being walked. pre(DO) computes and pushes;
103
// visit(DO) pops to restore the after-loop environment.
104
_loop_kept_stack: Collections.LIST[NARROW_ENV]
105
106
// One open loop's value bookkeeping (see LOOP_VALUE_FRAME):
107
// pushed by the DO / FOR pre handlers, popped when the loop's
108
// walk settles its expression value. `break` resolves against
109
// this stack — a bare identifier naming a label targets the
110
// matching frame; anything else contributes to the innermost
111
// frame.
112
loop_value_frames: Collections.LIST[LOOP_VALUE_FRAME]
113
114
// Labels seen since the last loop frame was pushed: the
115
// LABELLED pre pushes, the wrapped loop's pre takes the name
116
// into its frame.
117
_pending_label_names: Collections.LIST[Trees.Identifiers.Identifier]
118
119
// Per-ASSERT flag from the controlled walk in pre(ASSERT):
120
// true when the condition's own walk killed heap facts, so
121
// visit(ASSERT) drops them from the fall-through narrowing.
122
// A stack because a block expression inside an assert's
123
// condition or message can contain another assert.
124
_assert_condition_killed_stack: Collections.LIST[bool]
125
_assert_condition_marks_stack: Collections.LIST[(from_mark: int, to_mark: int)]
126
127
// Stack of per-`try` frames, one per `try` currently being
128
// walked. pre(TRY) pushes; visit(TRY) pops. The
129
// definite-assignment facts established before a try survive
130
// it; the try / catch bodies' own narrowing is discarded
131
// conservatively (an exception can leave the body anywhere).
132
_try_flow_stack: Collections.LIST[TRY_FLOW_FRAME]
133
134
// Stack of `val ... lav` blocks currently being walked. A
135
// `return E` inside a val-block targets the innermost — top
136
// of stack — block rather than the enclosing function. The
137
// block accumulates return types into its `return_types`
138
// list; visit(VAL_BLOCK) LUBs them with the tail expression's
139
// value type to settle the block's own value type.
140
//
141
// A `null` entry is a function-literal boundary marker:
142
// returns inside a nested function literal must target that
143
// function, not an outer val-block, so pre(FUNCTION) pushes
144
// null before walking the literal's body and visit(FUNCTION)
145
// pops it. The lookup at the top returns null on encounter,
146
// so the return falls through to the function-return path.
147
_val_block_stack: Collections.LIST[Trees.Expressions.VAL_BLOCK?]
148
149
// Public accessor used by compile_bindings.pre_return /
150
// visit_return to decide whether a `return` targets a
151
// val-block (top of stack) or the enclosing function (empty
152
// stack, or the top is a function-boundary null marker).
153
innermost_val_block: Trees.Expressions.VAL_BLOCK? =>
154
if _val_block_stack.count > 0 then
155
_val_block_stack[_val_block_stack.count - 1]
156
else
157
null
158
fi
159
160
current_statement_list: Trees.Statements.LIST
161
162
// current_statement_list is walk state: it is set on entering a
163
// statement list and restored on leaving one, and means nothing
164
// outside a walk.
165
@suppress("field-definite-assignment")
166
init(
167
logger: Logger,
168
symbol_table: Semantic.SYMBOL_TABLE,
169
namespaces: Semantic.NAMESPACES,
170
symbol_loader: Semantic.SYMBOL_LOADER,
171
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
172
function_caller: Semantic.FUNCTION_CALLER,
173
type_caster: Semantic.TYPE_CASTER,
174
task_conversion: Semantic.TASK_CONVERSION,
175
overload_resolver: Semantic.OVERLOAD_RESOLVER,
176
symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS,
177
context: IR.CONTEXT,
178
value_converter: VALUE_CONVERTER,
179
value_boxer: VALUE_BOXER,
180
build_flags: Compiler.GLOBAL_BUILD_FLAGS,
181
variable_left_state: VARIABLE_LEFT_STATE_STORE,
182
composite_spill_state: COMPOSITE_SPILL_STORE
183
)
184
is
185
super.init(logger, symbol_table, namespaces)
186
187
_logger = logger
188
_symbol_table = symbol_table
189
_symbol_loader = symbol_loader
190
_innate_symbol_lookup = innate_symbol_lookup
191
_awaitable_resolver = Semantic.AWAITABLE_RESOLVER(innate_symbol_lookup, logger)
192
_function_caller = function_caller
193
_type_caster = type_caster
194
_overload_resolver = overload_resolver
195
_owner_type_arg_specializer = Semantic.OWNER_TYPE_ARG_SPECIALIZER()
196
_owner_constraint_specializer = Semantic.OWNER_CONSTRAINT_SPECIALIZER()
197
_under_determination_detector = Semantic.UNDER_DETERMINATION_DETECTOR()
198
_type_arg_placeholder_registry = Semantic.TYPE_ARG_PLACEHOLDER_REGISTRY(symbol_table)
199
_unit_variant_constructor = Semantic.UNIT_VARIANT_CONSTRUCTOR(_owner_constraint_specializer, _type_arg_placeholder_registry, function_caller)
200
_closure_arg_resolver = Semantic.CLOSURE_ARG_RESOLVER(logger)
201
_numeric_literal_classifier = NUMERIC_LITERAL_CLASSIFIER(logger, innate_symbol_lookup)
202
_literals = COMPILE_LITERALS(logger, innate_symbol_lookup, _numeric_literal_classifier, symbol_use_locations)
203
_symbol_use_locations = symbol_use_locations
204
_value_converter = value_converter
205
_value_boxer = value_boxer
206
_build_flags = build_flags
207
_variable_left_state = variable_left_state
208
_composite_spill_state = composite_spill_state
209
210
_tuples = COMPILE_TUPLES(logger, innate_symbol_lookup, value_boxer, self)
211
212
_flow = NARROWING_FLOW(logger)
213
_condition_analyzer = CONDITION_ANALYZER(
214
expr => try_get_narrowing_target(expr),
215
expr => try_build_access_path(expr),
216
expr => _flow.test_site_mark_of(expr),
217
logger
218
)
219
220
_access = COMPILE_ACCESS(
221
logger,
222
symbol_table,
223
symbol_loader,
224
symbol_use_locations,
225
innate_symbol_lookup,
226
overload_resolver,
227
function_caller,
228
_unit_variant_constructor,
229
_flow,
230
_condition_analyzer,
231
_build_flags,
232
self
233
)
234
235
_function_reference_adapter = FUNCTION_REFERENCE_ADAPTER(symbol_table)
236
237
_calls = COMPILE_CALLS(
238
logger,
239
symbol_table,
240
symbol_use_locations,
241
innate_symbol_lookup,
242
overload_resolver,
243
function_caller,
244
_owner_constraint_specializer,
245
_owner_type_arg_specializer,
246
_under_determination_detector,
247
_type_arg_placeholder_registry,
248
_access,
249
self,
250
_flow,
251
symbol_loader,
252
_function_reference_adapter
253
)
254
255
_operators = COMPILE_OPERATORS(
256
logger,
257
symbol_table,
258
innate_symbol_lookup,
259
overload_resolver,
260
symbol_use_locations,
261
function_caller,
262
_calls,
263
_flow,
264
_condition_analyzer,
265
self
266
)
267
268
_generic_application = COMPILE_GENERIC_APPLICATION(
269
logger,
270
symbol_table,
271
symbol_use_locations,
272
symbol_loader,
273
innate_symbol_lookup,
274
overload_resolver,
275
_unit_variant_constructor,
276
self
277
)
278
279
_if_flow_stack = Collections.LIST[IF_FLOW_FRAME]()
280
_loop_kept_stack = Collections.LIST[NARROW_ENV]()
281
loop_value_frames = Collections.LIST[LOOP_VALUE_FRAME]()
282
_pending_label_names = Collections.LIST[Trees.Identifiers.Identifier]()
283
_assert_condition_killed_stack = Collections.LIST[bool]()
284
_assert_condition_marks_stack = Collections.LIST[(from_mark: int, to_mark: int)]()
285
_try_flow_stack = Collections.LIST[TRY_FLOW_FRAME]()
286
_val_block_stack = Collections.LIST[Trees.Expressions.VAL_BLOCK?]()
287
288
_conditionals = COMPILE_CONDITIONALS(
289
logger,
290
innate_symbol_lookup,
291
_flow,
292
_condition_analyzer,
293
_if_flow_stack,
294
self,
295
build_flags,
296
variable_left_state
297
)
298
299
_loops = COMPILE_LOOPS_AND_EXCEPTIONS(
300
logger,
301
innate_symbol_lookup,
302
_flow,
303
_condition_analyzer,
304
_conditionals,
305
self,
306
_try_flow_stack,
307
_loop_kept_stack
308
)
309
310
_pure_slots = PURE_SLOT_CHECK(_flow, _build_flags)
311
312
_bindings = COMPILE_BINDINGS(
313
logger,
314
symbol_table,
315
symbol_use_locations,
316
innate_symbol_lookup,
317
task_conversion,
318
_awaitable_resolver,
319
_flow,
320
_build_flags,
321
value_boxer,
322
_pure_slots,
323
self
324
)
325
326
_attribute_resolver = ATTRIBUTE_RESOLVER(logger, innate_symbol_lookup, overload_resolver, self)
327
328
_lambdas = COMPILE_LAMBDAS(
329
logger,
330
symbol_table,
331
symbol_use_locations,
332
symbol_loader,
333
innate_symbol_lookup,
334
task_conversion,
335
_closure_arg_resolver,
336
_type_arg_placeholder_registry,
337
_flow,
338
_build_flags,
339
self,
340
_attribute_resolver,
341
_composite_spill_state,
342
_function_reference_adapter
343
)
344
345
_pragma_scope_stack = PRAGMA_SCOPE_STACK()
346
si
347
348
// Statements.LIST.walk calls these around every child. We
349
// push the child's debug location onto the workspace
350
// LOCATION_STACK; every IR Value constructed while that
351
// statement is being walked picks it up as its ambient
352
// location in Value.init(). Generic at the Node level — the
353
// hook fires before any subtype dispatch.
354
//
355
// Skipped when `--debug` is off (the only consumer is the
356
// .line emission gated on the same flag): leaves the stack
357
// permanently empty so Value.init() captures null and the
358
// ambient-location bookkeeping costs nothing.
359
enter_node(node: Trees.Node) is
360
if !_build_flags.want_debug then
361
return
362
fi
363
IoC.CONTAINER.instance.location_stack.push(node.debug_location)
364
si
365
366
leave_node(node: Trees.Node) is
367
if !_build_flags.want_debug then
368
return
369
fi
370
IoC.CONTAINER.instance.location_stack.pop()
371
si
372
373
// Drop every piece of state this visitor carries between walks.
374
// Callers that drive the visitor without going through `apply` —
375
// the incremental body re-walk — must call this themselves,
376
// because the visitor is one per IoC container rather than one
377
// per build.
378
//
379
// Phantom Variables for unbound owner type-args at constructor
380
// sites are cached on this visitor (not the AST), keyed by AST
381
// node reference, so cache entries from a previous pass would
382
// still hit on the same AST nodes after a re-compile, returning
383
// phantoms whose `_lub_map` references Type instances from the
384
// previous build's symbol table (now wiped by clear_symbols).
385
// Body-retry iterations of one function need stable phantoms,
386
// but those happen inside visit(function: FUNCTION), so clearing
387
// before the walk starts is safe.
388
//
389
// The narrowing stack must be empty at a walk boundary — every
390
// push has to be balanced by a release before its owning AST
391
// exits. Reset defensively so a narrowing leaked by an aborted
392
// earlier walk (early return / exception) doesn't poison
393
// subsequent runs — restores symbol types and empties the
394
// environment.
395
reset_between_walks() is
396
_type_arg_placeholder_registry.clear()
397
398
_flow.reset()
399
_if_flow_stack.clear()
400
_loop_kept_stack.clear()
401
loop_value_frames.clear()
402
_pending_label_names.clear()
403
_try_flow_stack.clear()
404
_assert_condition_killed_stack.clear()
405
_assert_condition_marks_stack.clear()
406
si
407
408
// Flushed by the build loop once the compile-expressions pass has
409
// covered every file - see FIELD_ASSIGNMENT_CHECKER.
410
report_field_assignments() is
411
_lambdas.report_field_assignments()
412
si
413
414
// Public entry for the loop settle path (COMPILE_LOOPS_AND_
415
// EXCEPTIONS cannot reach the private helper across classes):
416
// records the spill-vs-capture decision for a composite whose
417
// body may suspend. See _declare_composite_spill_field.
418
declare_composite_spill_field_for(node: Trees.Node, value: IR.Values.Value?) is
419
_declare_composite_spill_field(node, value)
420
si
421
422
apply(root: Trees.Node) is
423
assert _pragma_scope_stack.is_balanced
424
425
IR.LABEL.reset_id()
426
IR.LABEL.set_pass("E")
427
428
reset_between_walks()
429
430
KILL_LEDGER.current_file = root.location.file_name
431
432
root.walk(self)
433
434
assert _pragma_scope_stack.is_balanced
435
assert _if_flow_stack.count == 0
436
else "if-flow frame stack leaked: {_if_flow_stack.count} unreleased"
437
assert _loop_kept_stack.count == 0
438
else "loop-kept env stack leaked: {_loop_kept_stack.count} unreleased"
439
assert _try_flow_stack.count == 0
440
else "try-flow frame stack leaked: {_try_flow_stack.count} unreleased"
441
assert _assert_condition_killed_stack.count == 0
442
else "assert-condition flag stack leaked: {_assert_condition_killed_stack.count} unreleased"
443
si
444
445
get_zero_argument_function(type: Type, name: string) -> Semantic.Symbols.Function? is
446
let symbol = type.find_member(name)
447
448
if symbol? /\ isa Semantic.Symbols.FUNCTION_GROUP(symbol) then
449
let function_group = symbol
450
451
for f in function_group.functions do
452
if f.arguments.count == 0 then
453
return f
454
fi
455
od
456
fi
457
return null
458
si
459
460
si
461
si