Skip to content
← Back

src/compiler/compiler.ghul

1
namespace Compiler is
2
use System.Exception;
3
4
use IO.Std;
5
6
use Collections.Iterable;
7
use Collections.MutableList;
8
use Collections.LIST;
9
10
use IoC;
11
use Logging;
12
13
class COMPILER is
14
container: CONTAINER;
15
logger: Logger;
16
17
source_files: LIST[SOURCE_FILE];
18
19
post_parse_passes: MutableList[Pass];
20
21
build_passes: MutableList[Pass];
22
23
_compile_expressions_pass: Pass;
24
25
is_full_compile_needed: bool public;
26
27
// True when the declaration-level state - symbols, ancestries,
28
// override links, store-free bits - reflects every registered
29
// file's current source: set by a completed build, cleared the
30
// moment clear_symbols abandons the tables. Distinct from
31
// is_full_compile_needed, which tracks whether expression-level
32
// DIAGNOSTICS are stale for some files; right after an
33
// interface-affecting EDIT's rebuild the tables are current while
34
// a full compile is still needed.
35
are_tables_current: bool public;
36
37
// The client's open-file set changed since the last full compile.
38
// Editor-only hints are gated on open files at emission time, so
39
// files that are already compiled through expressions need their
40
// walks re-run from cleared state - the expressions-only COMPILE
41
// cannot serve that; the next COMPILE takes the full rebuild.
42
is_open_set_changed: bool public;
43
44
generated_source_files: MutableList[string];
45
46
init() is
47
container = IoC.CONTAINER.instance;
48
logger = container.logger;
49
50
source_files = LIST[SOURCE_FILE]();
51
generated_source_files = LIST[string]();
52
53
post_parse_passes = LIST[Pass]();
54
add_pass(post_parse_passes, "conditional-compilation", (source_file) -> bool => conditional_compilation_pass(source_file));
55
add_pass(post_parse_passes, "collect-modifier-keyword-locations", (source_file) -> bool => collect_modifier_keyword_locations_pass(source_file));
56
add_pass(post_parse_passes, "rewrite-syntax-trees", (source_file) -> bool => rewrite_syntax_tree_pass(source_file));
57
add_pass(post_parse_passes, "collect-suppress-pragmas", (source_file) -> bool => collect_suppress_pragmas_pass(source_file));
58
59
build_passes = LIST[Pass]();
60
// Declares the type-level skeleton only: namespaces, types,
61
// variants and type parameters - everything a type expression
62
// can name. Members are declared by declare-members below.
63
add_pass(build_passes, "declare-symbols", (source_file) -> bool => declare_symbols_pass(source_file));
64
// First round: binds imports of namespaces and types. Imports
65
// of members bind in resolve-member-uses, once they exist.
66
add_pass(build_passes, "resolve-uses", (source_file) -> bool => resolve_uses_pass(source_file));
67
// Runs after resolve-uses so a member - in particular an
68
// impl/partial block's target name - can be reached through a
69
// use import and declared anywhere relative to its uses.
70
add_pass(build_passes, "declare-members", (source_file) -> bool => declare_members_pass(source_file));
71
// Second round: binds imports of members - static methods,
72
// global functions, enum members - and reports anything still
73
// unresolved.
74
add_pass(build_passes, "resolve-member-uses", (source_file) -> bool => resolve_member_uses_pass(source_file));
75
// Runs after declare-members so it can read each class's
76
// computed `is_abstract` (explicit + implicit) rather than
77
// just the syntactic modifier.
78
// Walks every file to report warnings but leaves nothing a
79
// later pass reads, so it reports its walk without advancing
80
// the compiled_through marker.
81
add_pass(build_passes, "check-name-conventions", (source_file) -> bool => check_name_conventions_pass(source_file))
82
.advances_milestone = false;
83
add_pass(build_passes, "resolve-type-expressions", (source_file: SOURCE_FILE) -> bool => resolve_type_expressions_pass(source_file));
84
add_pass(build_passes, "resolve-ancestors", (source_file) -> bool => resolve_ancestors_pass(source_file));
85
add_pass(build_passes, "resolve-explicit-types", (source_file) -> bool => resolve_explicit_types_pass(source_file));
86
add_pass(build_passes, "check-type-argument-bounds", (source_file) -> bool => check_type_argument_bounds_pass(source_file));
87
add_pass(
88
build_passes,
89
"resolve-overrides",
90
() -> void is si,
91
(source_file) -> bool => resolve_overrides_pass(source_file),
92
() -> void is
93
container.resolve_overrides.check_duplicate_global_functions();
94
si
95
);
96
add_pass(
97
build_passes,
98
"infer-store-free",
99
() -> void is
100
container.infer_store_free.start_run();
101
si,
102
(source_file) -> bool => infer_store_free_pass(source_file),
103
() -> void is
104
container.infer_store_free.finish_run();
105
si
106
);
107
add_pass(build_passes, "register-source-intrinsics", (source_file) -> bool => register_source_intrinsics_pass(source_file));
108
add_pass(build_passes, "check-pure-overrides", (source_file) -> bool => check_pure_overrides_pass(source_file));
109
add_pass(build_passes, "record-type-argument-uses", (source_file) -> bool => record_type_argument_uses_pass(source_file));
110
111
add_pass(build_passes, "mark-boxed-locals", (source_file) -> bool => mark_boxed_locals_pass(source_file));
112
113
_compile_expressions_pass = add_pass(build_passes, "compile-expressions", (source_file) -> bool => compile_expressions_pass(source_file));
114
115
add_pass(
116
build_passes,
117
"generate-il",
118
() -> void is
119
container.referenced_assemblies.gen();
120
si,
121
(source_file) -> bool => generate_il_pass(source_file),
122
() -> void is
123
// An executable build with no `.entrypoint` emitted would
124
// otherwise reach ilasm, which fails with the cryptic "No
125
// entry point declared for executable". Surface it as a
126
// located compiler error instead; `finish_build` aborts
127
// on `any_errors` before ilasm runs. Library builds
128
// (`--library`) legitimately have no entry point. Gated
129
// on no prior errors so it doesn't pile onto a build
130
// already failing for another reason (an error-aborted
131
// build never reaches `.entrypoint` emission, so
132
// `seen_entrypoint` is false regardless of whether the
133
// source declares an `entry`).
134
let flags = container.build_flags;
135
136
if flags.want_executable
137
/\ !flags.want_library
138
/\ !container.ir_context.seen_entrypoint
139
/\ !container.logger.any_errors
140
then
141
container.logger.error(
142
Source.LOCATION.internal,
143
"no entry point declared; add an 'entry()' function or build with --library"
144
);
145
fi
146
si
147
);
148
149
// Pass.order is set in registration order so a SOURCE_FILE's
150
// compiled_through.order can be compared numerically against the
151
// order of a target pass (e.g. _compile_expressions_pass). Post-
152
// parse passes get their own counter starting at zero and don't
153
// participate in compiled_through queries.
154
assign_pass_orders(post_parse_passes);
155
assign_pass_orders(build_passes);
156
si
157
158
assign_pass_orders(passes: Collections.Iterable[Pass]) static is
159
let order mut = 0;
160
for p in passes do
161
p.order = order;
162
order = order + 1;
163
od
164
si
165
166
dump_counts() is
167
container.namespaces.dump_counts();
168
container.symbol_use_locations.dump_counts();
169
container.symbol_definition_locations.dump_counts();
170
si
171
172
clear_symbols() is
173
are_tables_current = false;
174
175
container.symbol_table.clear();
176
container.namespaces.clear();
177
container.symbol_use_locations.clear();
178
container.symbol_definition_locations.clear();
179
Semantic.Symbols.STORE_FREE_BITS.clear();
180
Semantic.Symbols.MEMBER_OPERATOR_NAMES.clear();
181
// container.logger.clear();
182
183
// Every previously-queued file's expression-level state now
184
// references symbols that have just been abandoned. Drop the
185
// marker so query handlers can tell, and the retained
186
// store-free facts, whose keys and callee edges are those
187
// abandoned symbols.
188
for f in source_files do
189
f.compiled_through = null;
190
f.store_free_facts = null;
191
od
192
si
193
194
is_compiled_through_expressions(source_file: SOURCE_FILE) -> bool =>
195
let compiled_through = source_file?.compiled_through in
196
compiled_through? /\
197
compiled_through.order >= _compile_expressions_pass.order;
198
199
clear_queue() is
200
source_files.clear();
201
si
202
203
add_pass(passes: Collections.MutableList[Pass], description: string, apply: SOURCE_FILE -> bool) -> Pass is
204
let pass = PASS(container.timers, description, null, apply, null);
205
passes.add(pass);
206
return pass;
207
si
208
209
add_pass(
210
passes: Collections.MutableList[Pass],
211
description: string,
212
start: () -> void,
213
apply: SOURCE_FILE -> bool,
214
finish: () -> void
215
) -> Pass
216
is
217
let pass = PASS(container.timers, description, start, apply, finish);
218
passes.add(pass);
219
return pass;
220
si
221
222
add_pass(passes: Collections.MutableList[Pass], pass: Pass) is
223
passes.add(pass);
224
si
225
226
parse_and_queue(path: string, reader: IO.TextReader, want_compile_up_to_expressions: bool, want_compile_expressions: bool, is_internal_file: bool) is
227
queue(
228
parse(path, reader, want_compile_up_to_expressions, want_compile_expressions, is_internal_file)
229
);
230
si
231
232
queue(source_file: SOURCE_FILE) is
233
source_files.add(source_file);
234
si
235
236
queue(source_files: Collections.Iterable[SOURCE_FILE]) is
237
self.source_files.add_range(source_files);
238
si
239
240
parse(path: string, reader: IO.TextReader, want_compile_up_to_expressions: bool, want_compile_expressions: bool, is_internal_file: bool) -> SOURCE_FILE =>
241
parse(path, reader, want_compile_up_to_expressions, want_compile_expressions, is_internal_file, logger);
242
243
// Parsing into an explicitly-supplied logger lets a caller (the
244
// analysis-mode format handlers) parse without touching the shared
245
// diagnostics store: a throwaway logger keeps parse diagnostics and
246
// the speculation state stack isolated from the live analyser state.
247
parse(
248
path: string,
249
reader: IO.TextReader,
250
want_compile_up_to_expressions: bool,
251
want_compile_expressions: bool,
252
is_internal_file: bool,
253
logger: Logger
254
) -> SOURCE_FILE is
255
let tokenizer = Lexical.TOKENIZER(
256
logger,
257
path,
258
reader,
259
is_internal_file
260
);
261
262
// Speculative-lookahead ring buffer; a power of 2 (the
263
// queue masks indices with `size - 1`), sized generously so
264
// deep-but-finite speculation does not overflow it. A true
265
// speculation loop is caught separately by the tokenizer's
266
// backtrack-loop detector.
267
let token_queue = Lexical.TOKEN_QUEUE(2048);
268
269
let token_lookahead = Lexical.TOKEN_LOOKAHEAD(
270
token_queue,
271
tokenizer
272
);
273
274
let context = Syntax.Parsers.CONTEXT(
275
token_lookahead,
276
logger
277
);
278
279
// The file root parses as a definition list at namespace depth 0,
280
// where a file with no namespace may carry bare top-level
281
// statements (collected into a synthesised entry point).
282
let definitions = container.definition_global_list_parser.parse(context)!;
283
284
let result =
285
SOURCE_FILE(
286
want_compile_up_to_expressions,
287
want_compile_expressions,
288
path,
289
definitions
290
);
291
292
result.trivia = tokenizer.trivia;
293
294
return result;
295
si
296
297
post_parse(source_files: Iterable[SOURCE_FILE]) is
298
build(post_parse_passes, source_files);
299
si
300
301
post_parse() is
302
build(post_parse_passes, source_files);
303
si
304
305
build() is
306
container.ghul_namespace_creator.create_namespaces();
307
build(build_passes, source_files);
308
309
are_tables_current = true;
310
si
311
312
build(passes: Collections.Iterable[Pass], source_files: Collections.Iterable[SOURCE_FILE]) is
313
let is_compiling_expressions mut = false;
314
315
for pass in passes do
316
if pass == _compile_expressions_pass then
317
is_compiling_expressions = true;
318
fi
319
320
logger.set_is_compiling_expressions(is_compiling_expressions);
321
322
pass.start();
323
324
for i in source_files do
325
let symbol_table = IoC.CONTAINER.instance.symbol_table;
326
let symbol_table_mark = symbol_table.mark_scope_stack();
327
let diagnostics_mark = logger.mark();
328
let symbol_uses_mark = container.symbol_use_locations.mark();
329
330
let did_work mut = false;
331
332
try
333
did_work = pass.apply(i);
334
catch e: Exception
335
logger.exception(i.definition.location, e, "caught exception running pass {pass} on work item {i}");
336
finally
337
logger.release(diagnostics_mark);
338
container.symbol_use_locations.release(symbol_uses_mark);
339
symbol_table.release_scope_stack(symbol_table_mark);
340
yrt
341
342
if did_work then
343
WORK_COUNTERS.file_pass(container.timers, pass.description);
344
345
if pass.advances_milestone then
346
i.compiled_through = pass;
347
fi
348
fi
349
350
IoC.CONTAINER.instance.namespaces.pop_all_namespaces();
351
od
352
353
pass.finish();
354
od
355
si
356
357
// On-demand expression compile for one file against the retained
358
// symbol table, replaying only the pipeline's compile-expressions
359
// step with the same per-file bookkeeping as the build loop.
360
// Valid when the file has been built up to expressions in the
361
// current symbol generation - declare/resolve state current, only
362
// the expression-level walk missing. The analysis-mode query-miss
363
// recompile takes this instead of a whole-project rebuild when no
364
// interface change is pending.
365
compile_expressions_only(source_file: SOURCE_FILE) is
366
source_file.want_compile_expressions = true;
367
368
logger.set_is_compiling_expressions(true);
369
370
let symbol_table = container.symbol_table;
371
let symbol_table_mark = symbol_table.mark_scope_stack();
372
let diagnostics_mark = logger.mark();
373
let symbol_uses_mark = container.symbol_use_locations.mark();
374
375
let did_work mut = false;
376
377
try
378
did_work = _compile_expressions_pass.apply(source_file);
379
catch e: Exception
380
logger.exception(source_file.definition.location, e, "caught exception compiling expressions on demand for {source_file}");
381
finally
382
logger.release(diagnostics_mark);
383
container.symbol_use_locations.release(symbol_uses_mark);
384
symbol_table.release_scope_stack(symbol_table_mark);
385
386
container.namespaces.pop_all_namespaces();
387
388
logger.set_is_compiling_expressions(false);
389
yrt
390
391
if did_work then
392
WORK_COUNTERS.file_pass(container.timers, _compile_expressions_pass.description);
393
394
source_file.compiled_through = _compile_expressions_pass;
395
fi
396
si
397
398
// Re-derive every function's store-free bit from the retained
399
// ASTs and report whether any bit moved. The incremental EDIT
400
// paths recompile expressions without re-running infer-store-free,
401
// so a body edit that adds or drops a store, or changes a callee
402
// set, can flip a callee's bit while every caller's narrowing was
403
// computed against the old one. The whole-program fixpoint needs
404
// facts from every file, so this walks them all; comparing the
405
// fresh bits against the previous run tells the debounced compile
406
// whether trusting the incremental expression state is still
407
// sound. compiled_through is untouched - only the bits are. The
408
// file set is passed in: the compiler's own queue is cleared
409
// between builds, so the analysis handler owns the live set.
410
refresh_store_free(files: Collections.Iterable[SOURCE_FILE]) -> bool is
411
let symbol_table = container.symbol_table;
412
413
container.infer_store_free.start_run();
414
415
for source_file in files do
416
source_file.want_compile_up_to_expressions = true;
417
418
let symbol_table_mark = symbol_table.mark_scope_stack();
419
420
try
421
infer_store_free_pass(source_file);
422
catch e: Exception
423
logger.exception(source_file.definition.location, e, "caught exception refreshing store-free bits for {source_file}");
424
finally
425
symbol_table.release_scope_stack(symbol_table_mark);
426
container.namespaces.pop_all_namespaces();
427
yrt
428
od
429
430
container.infer_store_free.finish_run();
431
432
return container.infer_store_free.last_run_changed;
433
si
434
435
// The functions the most recent refresh_store_free flipped, or
436
// null when the flip set could not be bounded. Read by the
437
// debounced compile to true up just the files referencing a
438
// flipped function instead of rebuilding the whole project.
439
last_store_free_flips: Collections.List[Semantic.Symbols.Function]? =>
440
container.infer_store_free.last_flipped;
441
442
// How many files the most recent refresh_store_free actually
443
// re-walked, versus serving retained facts. Refresh telemetry.
444
last_store_free_walked_files: int =>
445
container.infer_store_free.last_run_walked_files;
446
447
// Recompile one file's expressions against the current symbol
448
// table, keeping its resolved interface. A CE-output-only clear
449
// (preserve_resolved_types) drops the file's compile-expressions
450
// output - including the node-keyed stores - while leaving
451
// TypeExpression.type intact, so re-running compile-expressions
452
// rebinds every body without re-resolving (and thus without
453
// double-setting) the retained interface types.
454
recompile_file_expressions(source_file: SOURCE_FILE) is
455
let ce_clear = Syntax.Process.CLEAR_STATE_VISITOR(true);
456
ce_clear.apply(source_file.definition);
457
458
container.state_store_registry.drop_file(source_file.file_name);
459
logger.clear_expression_diagnostics(source_file.file_name);
460
461
// this file's bodies are rebinding, so facts derived from
462
// the old bindings no longer describe it
463
source_file.store_free_facts = null;
464
465
compile_expressions_only(source_file);
466
si
467
468
// Declare, resolve and compile just the definitions an append-only
469
// interface edit added to the end of a file, against the retained
470
// symbol table. The appended subtree is new - nothing referenced
471
// it before this edit - so no retained symbol changes and no other
472
// file's state is touched: each pass walks only the new nodes.
473
// infer-store-free is not re-run; the new functions carry no
474
// store-free bit, which is the sound not-proven default until the
475
// next full rebuild. Namespaces named by the new declarations
476
// merge into the retained namespace map exactly as they would in
477
// a full build.
478
build_appended(source_file: SOURCE_FILE, new_definitions: Syntax.Trees.Definitions.LIST) is
479
source_file.want_compile_up_to_expressions = true;
480
source_file.want_compile_expressions = true;
481
482
_run_appended_pass(source_file, "declare-symbols", () is container.declare_symbols.apply(new_definitions); si);
483
_run_appended_pass(source_file, "resolve-uses", () is container.resolve_uses.apply(new_definitions); si);
484
_run_appended_pass(source_file, "declare-members", () is container.declare_members.apply(new_definitions); si);
485
_run_appended_pass(source_file, "resolve-member-uses", () is container.resolve_member_uses.apply(new_definitions); si);
486
_run_appended_pass(source_file, "resolve-type-expressions", () is container.resolve_type_expressions.apply(new_definitions); si);
487
_run_appended_pass(source_file, "resolve-ancestors", () is container.resolve_ancestors.apply(new_definitions); si);
488
_run_appended_pass(source_file, "resolve-explicit-types", () is container.resolve_explicit_types.apply(new_definitions); si);
489
_run_appended_pass(source_file, "check-type-argument-bounds", () is container.check_type_argument_bounds.apply(new_definitions); si);
490
_run_appended_pass(source_file, "resolve-overrides", () is container.resolve_overrides.apply(new_definitions); si);
491
492
logger.set_is_compiling_expressions(true);
493
494
_run_appended_pass(source_file, "compile-expressions", () is container.compile_expressions.apply(new_definitions); si);
495
496
logger.set_is_compiling_expressions(false);
497
si
498
499
// Declare and resolve a container's changed function members
500
// against the retained symbol table, with the scope cursor
501
// positioned inside the enclosing namespace and class scopes the
502
// way the build loop's tree walk would have it. Split from the
503
// expression walk so a class's pull-down can re-run between the
504
// two: overrides and pulled-down members must reflect the new
505
// member set before any body compiles against them.
506
build_members_interface(
507
source_file: SOURCE_FILE,
508
enclosing: Collections.List[Syntax.Trees.Definitions.Definition],
509
members: Syntax.Trees.Definitions.LIST
510
) is
511
source_file.want_compile_up_to_expressions = true;
512
source_file.want_compile_expressions = true;
513
514
_run_positioned_pass(source_file, enclosing, "declare-members", () is container.declare_members.apply(members); si);
515
_run_positioned_pass(source_file, enclosing, "resolve-type-expressions", () is container.resolve_type_expressions.apply(members); si);
516
_run_positioned_pass(source_file, enclosing, "resolve-explicit-types", () is container.resolve_explicit_types.apply(members); si);
517
_run_positioned_pass(source_file, enclosing, "check-type-argument-bounds", () is container.check_type_argument_bounds.apply(members); si);
518
si
519
520
build_members_expressions(
521
source_file: SOURCE_FILE,
522
enclosing: Collections.List[Syntax.Trees.Definitions.Definition],
523
members: Syntax.Trees.Definitions.LIST
524
) is
525
logger.set_is_compiling_expressions(true);
526
527
_run_positioned_pass(source_file, enclosing, "compile-expressions", () is container.compile_expressions.apply(members); si);
528
529
logger.set_is_compiling_expressions(false);
530
si
531
532
// One pass application over a changed member subtree, with the
533
// symbol-table and namespace cursors positioned inside the
534
// enclosing scopes first - a namespace enters through its own
535
// node's scope plus the namespace prefix stack, a class-like
536
// definition through its own node's scope, mirroring
537
// ScopedVisitor. The finally's release and pop restore both
538
// cursors however deep the positioning got.
539
_run_positioned_pass(
540
source_file: SOURCE_FILE,
541
enclosing: Collections.List[Syntax.Trees.Definitions.Definition],
542
pass_name: string,
543
apply_pass: () -> void
544
) is
545
WORK_COUNTERS.subtree_pass(container.timers, pass_name);
546
547
let symbol_table = container.symbol_table;
548
let symbol_table_mark = symbol_table.mark_scope_stack();
549
let diagnostics_mark = logger.mark();
550
let symbol_uses_mark = container.symbol_use_locations.mark();
551
552
try
553
for node in enclosing do
554
if let namespace_definition: Syntax.Trees.Definitions.NAMESPACE = node then
555
container.namespaces.enter_namespace(namespace_definition.name.location, namespace_definition.name.name);
556
symbol_table.enter_scope(namespace_definition);
557
elif let carrier: Syntax.Trees.ScopeCarrier = node then
558
symbol_table.enter_scope(carrier);
559
else
560
logger.poison(node.location, "no scope found for {node.get_type()}");
561
fi
562
od
563
564
apply_pass();
565
catch e: Exception
566
logger.exception(source_file.definition.location, e, "caught exception building changed members in {source_file}");
567
finally
568
logger.release(diagnostics_mark);
569
container.symbol_use_locations.release(symbol_uses_mark);
570
symbol_table.release_scope_stack(symbol_table_mark);
571
572
container.namespaces.pop_all_namespaces();
573
yrt
574
si
575
576
// One pass application over an appended subtree, under the build
577
// loop's per-file bookkeeping.
578
_run_appended_pass(source_file: SOURCE_FILE, pass_name: string, apply_pass: () -> void) is
579
WORK_COUNTERS.subtree_pass(container.timers, pass_name);
580
581
let symbol_table = container.symbol_table;
582
let symbol_table_mark = symbol_table.mark_scope_stack();
583
let diagnostics_mark = logger.mark();
584
let symbol_uses_mark = container.symbol_use_locations.mark();
585
586
try
587
apply_pass();
588
catch e: Exception
589
logger.exception(source_file.definition.location, e, "caught exception building appended declarations in {source_file}");
590
finally
591
logger.release(diagnostics_mark);
592
container.symbol_use_locations.release(symbol_uses_mark);
593
symbol_table.release_scope_stack(symbol_table_mark);
594
595
container.namespaces.pop_all_namespaces();
596
yrt
597
si
598
599
// Incremental body re-walk: re-process only the edited file's new
600
// function bodies, after its donor bodies have been spliced in by the
601
// analysis-mode EDIT handler. Every pass runs body-scoped — through
602
// BODY_DECLARER for declare-symbols, BODY_REWALKER for the rest — so
603
// the retained interface (arguments, signatures, indexer / property
604
// parameters) is never re-processed. Re-resolving a retained,
605
// already-typed argument would poison it ("set type twice"); the
606
// bodies are leaves, so processing them alone is also sufficient.
607
// resolve-uses / resolve-ancestors / resolve-overrides are skipped —
608
// they do not visit body interiors and the interface is unchanged.
609
//
610
// Before the re-walk re-records the bodies, the edited file's stale
611
// symbol-use / definition entries are reconciled: `correspondence`
612
// gives every retained interface node's post-edit location, and
613
// `body_spans` the pre-edit spans of the re-walked bodies. Body
614
// entries are dropped (re-recorded here), interface entries and
615
// symbols moved to their post-edit locations.
616
rewalk_bodies(
617
source_file: SOURCE_FILE,
618
correspondence: Source.LOCATION_CORRESPONDENCE,
619
body_spans: Source.BODY_SPANS
620
) is
621
source_file.want_compile_up_to_expressions = true;
622
source_file.want_compile_expressions = true;
623
624
// Definition reconciliation first — it classifies symbols by
625
// their pre-edit location, before the use reconciliation moves
626
// the interface symbols.
627
container.symbol_definition_locations.refresh_edited_file(source_file.file_name, body_spans);
628
container.symbol_use_locations.refresh_edited_file(source_file.file_name, correspondence, body_spans);
629
630
let flags = container.build_flags;
631
let symbol_table = container.symbol_table;
632
633
let body_declarer = Syntax.Process.BODY_DECLARER(
634
logger,
635
symbol_table,
636
container.namespaces,
637
container.declare_members
638
);
639
640
// body-scoped declare-symbols
641
WORK_COUNTERS.file_rewalk(container.timers, "declare-symbols");
642
643
let mark = symbol_table.mark_scope_stack();
644
645
try
646
source_file.definition.walk(body_declarer);
647
catch e: Exception
648
logger.exception(source_file.definition.location, e, "caught exception declaring bodies in incremental re-walk of {source_file}");
649
finally
650
symbol_table.release_scope_stack(mark);
651
container.namespaces.pop_all_namespaces();
652
yrt
653
654
rewalk_pass(source_file, "resolve-type-expressions", container.resolve_type_expressions);
655
rewalk_pass(source_file, "resolve-explicit-types", container.resolve_explicit_types);
656
rewalk_pass(source_file, "check-type-argument-bounds", container.check_type_argument_bounds);
657
658
if flags.want_assembler \/ flags.want_executable then
659
rewalk_pass(source_file, "record-type-argument-uses", container.record_type_argument_uses);
660
fi
661
662
logger.set_is_compiling_expressions(true);
663
664
rewalk_pass(source_file, "compile-expressions", container.compile_expressions);
665
666
logger.set_is_compiling_expressions(false);
667
si
668
669
// Run one body-visiting pass over the edited file's bodies only,
670
// driven by BODY_REWALKER, under a scope-stack mark/release. The
671
// pass name is passed separately because a body-visiting pass is
672
// a bare visitor, not a registered Pass with a description.
673
rewalk_pass(source_file: SOURCE_FILE, pass_name: string, pass: Syntax.Visitor) is
674
WORK_COUNTERS.file_rewalk(container.timers, pass_name);
675
676
let symbol_table = container.symbol_table;
677
let mark = symbol_table.mark_scope_stack();
678
679
let rewalker = Syntax.Process.BODY_REWALKER(
680
logger,
681
symbol_table,
682
container.namespaces,
683
pass
684
);
685
686
try
687
source_file.definition.walk(rewalker);
688
catch e: Exception
689
logger.exception(source_file.definition.location, e, "caught exception in incremental re-walk of {source_file}");
690
finally
691
symbol_table.release_scope_stack(mark);
692
container.namespaces.pop_all_namespaces();
693
yrt
694
si
695
696
conditional_compilation_pass(source_file: SOURCE_FILE) -> bool is
697
let definition = source_file.definition;
698
699
container
700
.conditional_compilation
701
.apply(
702
definition
703
);
704
705
return true;
706
si
707
708
rewrite_syntax_tree_pass(source_file: SOURCE_FILE) -> bool is
709
let definition = source_file.definition;
710
711
container
712
.rewrite_primary_constructors
713
.apply(definition);
714
715
container
716
.synthesise_top_level_entry
717
.apply(definition);
718
719
container
720
.expand_namespaces
721
.apply(definition);
722
723
container
724
.add_accessors_for_properties
725
.apply(definition);
726
727
container
728
.spill_awaits
729
.apply(definition);
730
731
return true;
732
si
733
734
collect_suppress_pragmas_pass(source_file: SOURCE_FILE) -> bool is
735
container
736
.collect_suppress_pragmas
737
.apply(source_file);
738
739
return true;
740
si
741
742
collect_modifier_keyword_locations_pass(source_file: SOURCE_FILE) -> bool is
743
container
744
.collect_modifier_keyword_locations
745
.apply(source_file);
746
747
return true;
748
si
749
750
check_name_conventions_pass(source_file: SOURCE_FILE) -> bool is
751
container
752
.check_name_conventions
753
.apply(source_file);
754
755
return true;
756
si
757
758
declare_symbols_pass(source_file: SOURCE_FILE) -> bool is
759
let flags = container.build_flags;
760
let definition = source_file.definition;
761
762
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
763
container
764
.declare_symbols
765
.apply(definition);
766
767
return true;
768
fi
769
770
return false;
771
si
772
773
resolve_uses_pass(source_file: SOURCE_FILE) -> bool is
774
let flags = container.build_flags;
775
let definition = source_file.definition;
776
777
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
778
container
779
.resolve_uses
780
.apply(definition);
781
782
return true;
783
fi
784
785
return false;
786
si
787
788
declare_members_pass(source_file: SOURCE_FILE) -> bool is
789
let flags = container.build_flags;
790
let definition = source_file.definition;
791
792
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
793
container
794
.declare_members
795
.apply(definition);
796
797
return true;
798
fi
799
800
return false;
801
si
802
803
resolve_member_uses_pass(source_file: SOURCE_FILE) -> bool is
804
let flags = container.build_flags;
805
let definition = source_file.definition;
806
807
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
808
container
809
.resolve_member_uses
810
.apply(definition);
811
812
return true;
813
fi
814
815
return false;
816
si
817
818
resolve_ancestors_pass(source_file: SOURCE_FILE) -> bool is
819
let flags = container.build_flags;
820
let definition = source_file.definition;
821
822
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
823
container
824
.resolve_ancestors
825
.apply(definition);
826
827
return true;
828
fi
829
830
return false;
831
si
832
833
resolve_type_expressions_pass(source_file: SOURCE_FILE) -> bool is
834
let flags = container.build_flags;
835
let definition = source_file.definition;
836
837
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
838
container
839
.resolve_type_expressions
840
.apply(definition);
841
842
return true;
843
fi
844
845
return false;
846
si
847
848
resolve_explicit_types_pass(source_file: SOURCE_FILE) -> bool is
849
let flags = container.build_flags;
850
let definition = source_file.definition;
851
852
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
853
container
854
.resolve_explicit_types
855
.apply(definition);
856
857
return true;
858
fi
859
860
return false;
861
si
862
863
check_type_argument_bounds_pass(source_file: SOURCE_FILE) -> bool is
864
let flags = container.build_flags;
865
let definition = source_file.definition;
866
867
if flags.want_declare_symbols \/ source_file.want_compile_up_to_expressions then
868
container
869
.check_type_argument_bounds
870
.apply(definition);
871
872
return true;
873
fi
874
875
return false;
876
si
877
878
compile_expressions_pass(source_file: SOURCE_FILE) -> bool is
879
let definition = source_file.definition;
880
881
if source_file.want_compile_expressions then
882
container
883
.compile_expressions
884
.apply(definition);
885
886
return true;
887
fi
888
889
return false;
890
si
891
892
mark_boxed_locals_pass(source_file: SOURCE_FILE) -> bool is
893
let flags = container.build_flags;
894
let definition = source_file.definition;
895
896
// Boxing only matters once the IR is lowered to IL:
897
// assignability of a captured local is keyed on its
898
// mut marker alone, so analysis-only builds skip
899
// deriving the boxing flags. Within an IL-bound
900
// build, pair the gate with compile-expressions -
901
// if the file isn't being walked for expression-level
902
// semantics this run, no downstream consumer would
903
// see the `is_boxed` flags we'd set.
904
if
905
source_file.want_compile_expressions /\
906
(flags.want_assembler \/ flags.want_executable)
907
then
908
container
909
.mark_boxed_locals
910
.apply(definition);
911
912
return true;
913
fi
914
915
return false;
916
si
917
918
resolve_overrides_pass(source_file: SOURCE_FILE) -> bool is
919
let definition = source_file.definition;
920
921
if source_file.want_compile_up_to_expressions then
922
container
923
.resolve_overrides
924
.apply(definition);
925
926
return true;
927
fi
928
929
return false;
930
si
931
932
infer_store_free_pass(source_file: SOURCE_FILE) -> bool is
933
let definition = source_file.definition;
934
935
if source_file.want_compile_up_to_expressions then
936
let infer_store_free = container.infer_store_free;
937
938
// A file whose tree and bindings are unchanged since its
939
// facts were last derived contributes them as-is; only
940
// stale files pay the body re-walk. The fixpoint always
941
// runs over the whole run's union, so cross-file
942
// propagation is unaffected.
943
if let retained_facts = source_file.store_free_facts then
944
infer_store_free.absorb(retained_facts);
945
946
return true;
947
fi
948
949
infer_store_free.begin_file_bucket();
950
951
infer_store_free.apply(definition);
952
953
let bucket = infer_store_free.end_file_bucket();
954
955
// an abandoned walk's partial facts stay in this run's
956
// union (conservative) but are not retained for reuse
957
if !infer_store_free.last_walk_failed then
958
source_file.store_free_facts = bucket;
959
fi
960
961
return true;
962
fi
963
964
return false;
965
si
966
967
register_source_intrinsics_pass(source_file: SOURCE_FILE) -> bool is
968
container
969
.register_source_intrinsics
970
.apply(source_file.definition);
971
972
return true;
973
si
974
975
check_pure_overrides_pass(source_file: SOURCE_FILE) -> bool is
976
let definition = source_file.definition;
977
978
if source_file.want_compile_up_to_expressions then
979
container
980
.check_pure_overrides
981
.apply(definition);
982
983
return true;
984
fi
985
986
return false;
987
si
988
989
record_type_argument_uses_pass(source_file: SOURCE_FILE) -> bool is
990
let flags = container.build_flags;
991
992
if flags.want_assembler \/ flags.want_executable then
993
let definition = source_file.definition;
994
995
container
996
.record_type_argument_uses
997
.apply(definition);
998
999
return true;
1000
fi
1001
1002
return false;
1003
si
1004
1005
generate_il_pass(source_file: SOURCE_FILE) -> bool is
1006
let flags = container.build_flags;
1007
1008
if flags.want_assembler \/ flags.want_executable then
1009
// FIXME why are we doing this?
1010
container
1011
.ir_context
1012
.throw_on_fixme = false;
1013
1014
let definition = source_file.definition;
1015
1016
container
1017
.generate_il
1018
.apply(definition);
1019
1020
return true;
1021
fi
1022
1023
return false;
1024
si
1025
si
1026
si