Skip to content
← Back

src/syntax/process/compile_operators.ghul

1
namespace Syntax.Process is
2
use Logging;
3
use Source;
4
5
use Semantic.Types.Type;
6
use Semantic.LEAST_UPPER_BOUND_MAP;
7
8
use IR.Values;
9
10
// Compiles unary, binary and index expressions. Split out of
11
// COMPILE_EXPRESSIONS, which delegates visit(unary), pre(binary),
12
// visit(binary) and visit(index) here. The exception-handling
13
// wrappers of visit(binary) / visit(index) stay on the visitor;
14
// visit_binary / visit_index are the enclosed logic.
15
class COMPILE_OPERATORS is
16
_logger: Logger;
17
_symbol_table: Semantic.SYMBOL_TABLE;
18
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup;
19
_overload_resolver: Semantic.OVERLOAD_RESOLVER;
20
_symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS;
21
_function_caller: Semantic.FUNCTION_CALLER;
22
_calls: COMPILE_CALLS;
23
_flow: NARROWING_FLOW;
24
_condition_analyzer: CONDITION_ANALYZER;
25
_visitor: ScopedVisitor;
26
_value_boxer: IR.VALUE_BOXER;
27
28
init(
29
logger: Logger,
30
symbol_table: Semantic.SYMBOL_TABLE,
31
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
32
overload_resolver: Semantic.OVERLOAD_RESOLVER,
33
symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS,
34
function_caller: Semantic.FUNCTION_CALLER,
35
calls: COMPILE_CALLS,
36
flow: NARROWING_FLOW,
37
condition_analyzer: CONDITION_ANALYZER,
38
visitor: ScopedVisitor
39
) is
40
super.init();
41
42
_logger = logger;
43
_symbol_table = symbol_table;
44
_innate_symbol_lookup = innate_symbol_lookup;
45
_overload_resolver = overload_resolver;
46
_symbol_use_locations = symbol_use_locations;
47
_function_caller = function_caller;
48
_calls = calls;
49
_flow = flow;
50
_condition_analyzer = condition_analyzer;
51
_visitor = visitor;
52
_value_boxer = IR.VALUE_BOXER(logger);
53
si
54
55
visit_unary(unary: Trees.Expressions.UNARY) is
56
unary.compile_expressions_state.value = null;
57
58
if unary.right.value? then
59
// TODO do we need to search members and globals like we do with binary operators
60
// or if not, can we simplify the binary operator function search to match this?
61
62
let function_group_symbol = _visitor.find(unary.operation);
63
64
if function_group_symbol == null \/ !isa Semantic.Symbols.FUNCTION_GROUP(function_group_symbol) then
65
_logger.error(unary.operation.location, "no unary operator {unary.operation} found");
66
67
unary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), unary.location);
68
return;
69
fi
70
71
let function_group = cast Semantic.Symbols.FUNCTION_GROUP(function_group_symbol);
72
let right_value = unary.right.value!;
73
// FIXME: this doesn't seem right
74
let want_instance = right_value.is_consumable;
75
76
let argument_values = Collections.LIST[Value]([right_value]);
77
let argument_types = Collections.LIST[Type]([right_value.type!]);
78
let argument_expressions = Collections.LIST[Trees.Expressions.Expression]([unary.right]);
79
80
let (overload_result, errors) =
81
_resolve_with_retry(
82
unary.location,
83
function_group,
84
argument_values,
85
argument_types,
86
argument_expressions,
87
want_instance,
88
unary
89
);
90
91
if overload_result == null then
92
if errors? /\ errors.count > 0 then
93
_logger.merge(errors);
94
fi
95
96
unary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), unary.location);
97
return;
98
fi
99
100
let function = overload_result.function;
101
102
if function.is_unsafe_constraints then
103
_logger.warn(unary.location, "unchecked-constraints", "call to {function} has unchecked constraints");
104
fi
105
106
_symbol_use_locations.add_symbol_use(unary.operation.location, function);
107
108
let value = function.call(unary.location, null, argument_values, null, _function_caller);
109
110
unary.compile_expressions_state.value = value;
111
fi
112
si
113
114
// Resolve an operator overload with the same constraint-push
115
// retry sequence visit_call uses: initial type-based resolve,
116
// then on null or PARTIAL re-walk function-literal operands
117
// with the chosen formal pushed as expected_type so a lambda
118
// whose parameter type was unknown on the first walk can
119
// infer it from the operator's signature.
120
//
121
// Operates inside its own speculation snapshot: the retry's
122
// re-walks update operand `.value` fields in place but the
123
// logger diagnostics from speculation are scrubbed when the
124
// snapshot backtracks, matching the visit_call pattern.
125
_resolve_with_retry(
126
location: Source.LOCATION,
127
function_group: Semantic.Symbols.FUNCTION_GROUP,
128
argument_values: Collections.LIST[Value],
129
argument_types: Collections.LIST[Type],
130
argument_expressions: Collections.List[Trees.Expressions.Expression],
131
want_instance: bool,
132
cache_key: Trees.Node
133
) -> (Semantic.OVERLOAD_RESOLVE_RESULT?, Logging.DIAGNOSTICS_STATE?) is
134
let use logger_snapshot = _logger.speculate_then_backtrack();
135
136
// Operator operands walk during ordinary tree descent, so
137
// the baseline captured here is the env after that walk; a
138
// retry re-walk resets to it through `_flow.restore()`, and
139
// the whole attempt rolls the env back on exit — the operand
140
// values are already set, only the diagnostics matter.
141
let use flow_speculation = _flow.speculate_then_roll_back();
142
143
let overload_result mut =
144
_overload_resolver.resolve(
145
location,
146
function_group,
147
argument_types,
148
false,
149
want_instance,
150
false
151
);
152
153
if !overload_result? then
154
let retry = _calls.try_overload_after_null(
155
function_group,
156
argument_values,
157
argument_types,
158
want_instance,
159
null,
160
argument_expressions,
161
location,
162
cache_key
163
);
164
165
if retry? then
166
overload_result = retry;
167
fi
168
fi
169
170
if overload_result? /\ overload_result.needs_retry then
171
let retry = _calls.try_overload_on_partial(
172
overload_result,
173
function_group,
174
argument_values,
175
argument_types,
176
want_instance,
177
null,
178
argument_expressions,
179
location,
180
cache_key
181
);
182
183
if retry? then
184
overload_result = retry;
185
fi
186
fi
187
188
let errors = logger_snapshot.backtrack();
189
190
return (overload_result, errors);
191
si
192
193
pre_binary(binary: Trees.Expressions.BINARY) -> bool is
194
// For `/\` / `\/`, walk the operands with the narrowing
195
// environment threaded so within-condition narrowing
196
// applies as the right operand compiles: in
197
// `isa T(x) /\ x.foo`, `x.foo` sees `x` as `T`. The
198
// `/\` right walks under the left's true edge, the `\/`
199
// right under its false edge. The BINARY as a whole
200
// leaves the ambient environment unchanged — the
201
// enclosing IF derives the overall narrowing from
202
// analyze_condition once the condition is fully walked.
203
let op = binary.operation.name;
204
205
if op =~ "/\\" \/ op =~ "\\/" then
206
let saved = _flow.current_env.copy();
207
let epoch = _flow.heap_epoch;
208
209
binary.left.walk(_visitor);
210
211
let left_facts = _condition_analyzer.analyze_condition_facts_only(binary.left, saved);
212
213
let right_env =
214
if op =~ "/\\" then left_facts.then_env else left_facts.else_env fi;
215
216
// The right operand's environment derives from
217
// the pre-left snapshot; a kill during the left's
218
// own walk (`x? /\ mutate() /\ x.use`) means its
219
// heap facts cannot be trusted while the right
220
// compiles.
221
if _flow.heap_killed_since(epoch) then
222
right_env.drop_heap_facts();
223
fi
224
225
_flow.set_env(right_env);
226
227
binary.right.walk(_visitor);
228
229
// Same for the ambient environment restored after
230
// the operator: it predates both operand walks.
231
if _flow.heap_killed_since(epoch) then
232
saved.drop_heap_facts();
233
fi
234
235
_flow.set_env(saved);
236
237
return true;
238
fi
239
240
return false;
241
si
242
243
visit_binary(binary: Trees.Expressions.BINARY) is
244
binary.compile_expressions_state.value = null;
245
246
if !binary.left.value? \/ !binary.right.value? then
247
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
248
return;
249
fi
250
251
// `??` does not go through overload resolution: the
252
// "chain closer" vs "stays optional" distinction is
253
// determined by the right operand's own nullability,
254
// which a single typing rule expresses directly. Going
255
// through the user-operator path would force this
256
// distinction into two ambiguous prelude overloads.
257
if binary.operation.name =~ "??" then
258
_visit_null_coalesce(binary);
259
return;
260
fi
261
262
let left_value = binary.left.value;
263
let right_value = binary.right.value;
264
let left_is_consumable = left_value.check_is_consumable(_logger, binary.left.location);
265
let right_is_consumable = right_value.check_is_consumable(_logger, binary.right.location);
266
267
// Two separate value/type/expression lists. The free-function
268
// path takes both operands as arguments; the member-call path
269
// takes only the right operand (left is the receiver). The
270
// retry helper updates the lists in place when it re-walks,
271
// so the two paths can't share storage.
272
let binary_argument_values = Collections.LIST[Value]([left_value, right_value]);
273
let binary_argument_types = Collections.LIST[Type]([left_value.type!, right_value.type!]);
274
let binary_argument_expressions = Collections.LIST[Trees.Expressions.Expression]([binary.left, binary.right]);
275
276
let member_argument_values = Collections.LIST[Value]([right_value]);
277
let member_argument_types = Collections.LIST[Type]([right_value.type!]);
278
let member_argument_expressions = Collections.LIST[Trees.Expressions.Expression]([binary.right]);
279
280
let binary_function_group: Semantic.Symbols.FUNCTION_GROUP mut;
281
let binary_overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut = null;
282
let binary_errors: Logging.DIAGNOSTICS_STATE? mut = null;
283
284
let binary_function_symbol = _visitor.find(binary.operation.name);
285
286
if binary_function_symbol? /\ isa Semantic.Symbols.FUNCTION_GROUP(binary_function_symbol) then
287
binary_function_group = binary_function_symbol;
288
289
(binary_overload_result, binary_errors) =
290
_resolve_with_retry(
291
binary.location,
292
binary_function_group,
293
binary_argument_values,
294
binary_argument_types,
295
binary_argument_expressions,
296
_symbol_table.current_instance_context?,
297
binary
298
);
299
fi
300
301
let member_overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut = null;
302
let member_function_group: Semantic.Symbols.FUNCTION_GROUP mut;
303
let member_errors: Logging.DIAGNOSTICS_STATE? mut = null;
304
305
if let binary.left.value?, value.type? then
306
let member_function_symbol =
307
type.find_member(binary.operation.name);
308
309
if member_function_symbol? /\ isa Semantic.Symbols.FUNCTION_GROUP(member_function_symbol) then
310
member_function_group = member_function_symbol;
311
312
(member_overload_result, member_errors) =
313
_resolve_with_retry(
314
binary.location,
315
cast Semantic.Symbols.FUNCTION_GROUP(member_function_group),
316
member_argument_values,
317
member_argument_types,
318
member_argument_expressions,
319
value.is_consumable,
320
binary
321
);
322
fi
323
fi
324
325
let function: Semantic.Symbols.Function? mut = null;
326
327
if member_overload_result? /\ binary_overload_result? then
328
if cast int(member_overload_result.score) <= cast int(binary_overload_result.score) then
329
function = member_overload_result.function;
330
else
331
function = binary_overload_result.function;
332
fi
333
elif member_overload_result? then
334
function = member_overload_result.function;
335
elif binary_overload_result? then
336
function = binary_overload_result.function;
337
fi
338
339
if function? then
340
_symbol_use_locations.add_symbol_use(binary.operation.location, function);
341
342
// Mirror the inferred-T constraint check in compile_calls: an
343
// operator function resolved via overload resolution has its
344
// type arguments bound by inference (the operator is never
345
// explicitly specialized), so the declared kind / type-bound
346
// constraints have not been checked yet.
347
if
348
function.is_generic /\
349
function.generic_arguments.count == function.generic_argument_names.count
350
then
351
Semantic.Symbols.GENERIC_CONSTRAINT_CHECKER().check_arguments(
352
binary.location,
353
_logger,
354
function,
355
function.generic_argument_names,
356
function.generic_arguments
357
);
358
fi
359
360
let force_type: Type? mut = _;
361
let want_not mut = false;
362
363
if binary.operation.name =~ "<>" then
364
if !function.return_type!.matches(_innate_symbol_lookup.get_int_type()) then
365
// FIXME: do this check on the definition:
366
_logger.error(binary.location, "<> order operator must return int");
367
fi
368
369
force_type = _innate_symbol_lookup.get_bool_type();
370
elif binary.operation.name =~ "==" then
371
let left_type = binary.left.value?.type;
372
let right_type = binary.right.value?.type;
373
374
if
375
left_is_consumable /\ right_is_consumable /\
376
left_type? /\ right_type? /\
377
cast int(left_type.compare(right_type)) > cast int(Semantic.Types.MATCH.ASSIGNABLE) /\
378
cast int(right_type.compare(left_type)) > cast int(Semantic.Types.MATCH.ASSIGNABLE)
379
then
380
if binary.actual_operation =~ "==" then
381
_logger.error(binary.location, "== cannot be applied to values of non-assignable types");
382
else
383
_logger.error(binary.location, "!= cannot be applied to values of non-assignable types");
384
fi
385
fi
386
elif !function.is_innate /\ binary.actual_operation =~ "!~" then
387
want_not = true;
388
fi
389
390
let value: Value mut;
391
392
if function.is_unsafe_constraints then
393
_logger.warn(binary.location, "unchecked-constraints", "call to {function} has unchecked constraints");
394
fi
395
396
// Pass the path-specific argument-value list so the
397
// values used for the call reflect the retry's re-walks
398
// for whichever group was selected — without this, a
399
// member-path retry that re-walked the right operand
400
// would overwrite values the binary-path retry already
401
// committed to its own list (and vice versa).
402
if function.is_instance then
403
value = function.call(binary.location, left_value, member_argument_values, force_type, _function_caller);
404
else
405
value = function.call(binary.location, null, binary_argument_values, force_type, _function_caller);
406
407
if isa Call.INNATE(value) then
408
let innate_value = value;
409
410
innate_value.actual_operation = binary.actual_operation;
411
412
value = innate_value.lower();
413
fi
414
fi
415
416
if !function.is_innate /\ binary.operation.name =~ "<>" then
417
value = COMPARE_ORDER_TO_ZERO(value, binary.actual_operation, _innate_symbol_lookup.get_bool_type());
418
fi
419
420
if want_not then
421
value = NOT(value);
422
fi
423
424
binary.compile_expressions_state.value = value;
425
else
426
if binary_errors? /\ binary_errors.count > 0 then
427
_logger.merge(binary_errors);
428
fi
429
430
if member_errors? /\ member_errors.count > 0 then
431
_logger.merge(member_errors);
432
fi
433
434
if (!binary_errors? \/ binary_errors.count == 0) /\ (!member_errors? \/ member_errors.count == 0) then
435
_logger.error(binary.operation.location, "operator {binary.left.value?.type} {binary.operation} {binary.right.value?.type} not found");
436
fi
437
438
// FIXME QQ should probably do this
439
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
440
fi
441
si
442
443
// `a ?? b` — short-circuit null-coalesce. Result type is the
444
// LUB of the left's underlying type and the right's type, with
445
// `?` re-applied iff the right is itself optional. Right is
446
// evaluated only when left is null.
447
_visit_null_coalesce(binary: Trees.Expressions.BINARY) is
448
let left_value = binary.left.value!;
449
let right_value = binary.right.value!;
450
451
452
left_value.check_is_consumable(_logger, binary.left.location);
453
right_value.check_is_consumable(_logger, binary.right.location);
454
455
let left_type = left_value.type;
456
let right_type = right_value.type;
457
458
if !left_type? \/ left_type.is_error \/ !right_type? \/ right_type.is_error then
459
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
460
return;
461
fi
462
463
// Flow analysis routinely narrows a declared `T?` to `T`
464
// after a non-null assignment. Treating that as an error
465
// would reject defensive `present ?? fallback` patterns
466
// — degrade to just emitting `left` instead, matching how
467
// `?.` falls back to `.` when the receiver is non-optional.
468
if !left_type.is_optional then
469
binary.compile_expressions_state.value = left_value;
470
return;
471
fi
472
473
let left_inner = left_type.optional_inner_type;
474
475
if !left_inner? then
476
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
477
return;
478
fi
479
480
let right_is_optional = right_type.is_optional;
481
482
let right_inner =
483
if right_is_optional then
484
right_type.optional_inner_type;
485
else
486
right_type;
487
fi;
488
489
let lub = LEAST_UPPER_BOUND_MAP();
490
491
lub.add(left_inner);
492
493
if right_inner? then
494
lub.add(right_inner);
495
fi
496
497
let result_inner = lub.get_result();
498
499
if !result_inner? then
500
_logger.error(binary.location, "no common type for ?? operands {left_type} and {right_type}");
501
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
502
return;
503
fi
504
505
// The result's optional kind derives from the LUB of the
506
// inner types, not from either operand: reference inner →
507
// flagged reference optional, value inner → NULLABLE, and
508
// a type-variable inner keeps the left's MAYBE carrier —
509
// the only lowering an unconstrained T? has. The operands
510
// may each be a different kind of optional; each arm
511
// coerces to the canonical result.
512
let result_type =
513
if !right_is_optional then
514
result_inner;
515
elif result_inner.is_type_variable then
516
left_type;
517
else
518
COMPILE_ACCESS.build_optional_type(result_inner, _innate_symbol_lookup);
519
fi;
520
521
let absent_arm = _coerce_coalesce_arm(right_value, result_type);
522
523
if !left_type.is_value_type then
524
binary.compile_expressions_state.value = NULL_COALESCE(left_value, absent_arm, result_type);
525
return;
526
fi
527
528
// Value-shape left — NULLABLE[T] or MAYBE[T]: spill the
529
// receiver to a local and drive the presence test and
530
// payload extract through its address, mirroring the
531
// `?.` lowering.
532
let has_value_member = left_type.find_member("has_value");
533
let value_member = left_type.find_member("value");
534
535
if !has_value_member? \/ !value_member? then
536
_logger.poison(binary.location, "?? left operand {left_type} missing has_value or value member");
537
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location);
538
return;
539
fi
540
541
let address_stand_in = IR.Values.STACK_TOP_ADDRESS(left_type);
542
let symbol_loader = IoC.CONTAINER.instance.symbol_loader;
543
544
let presence_test = has_value_member.load(binary.left.location, address_stand_in, symbol_loader);
545
let value_extract = value_member.load(binary.left.location, address_stand_in, symbol_loader);
546
547
let present_arm = _coerce_coalesce_arm(IR.Values.STACK_TOP(left_inner), result_type);
548
549
binary.compile_expressions_state.value =
550
IR.Values.NULL_COALESCE_VALUE(
551
left_value,
552
presence_test,
553
value_extract,
554
present_arm,
555
absent_arm,
556
result_type
557
);
558
si
559
560
// Bring one arm of `??` to the result type: T → T? wrapping
561
// (Nullable / MAYBE construction), MAYBE → reference-optional
562
// payload load, NULLABLE ↔ MAYBE pass through unchanged (the
563
// layouts are identical), and a value-typed arm boxes when
564
// the LUB widened the result to a reference type.
565
_coerce_coalesce_arm(value: IR.Values.Value, result_type: Semantic.Types.Type) -> IR.Values.Value is
566
let coerced = _value_boxer.wrap_if_needed(value, result_type)!;
567
568
if !result_type.is_value_type /\ coerced.type? /\ coerced.type!.is_value_type then
569
return IR.Values.BOX(coerced);
570
fi
571
572
return coerced;
573
si
574
575
visit_index(index: Trees.Expressions.INDEX) is
576
let existing_value = index.value;
577
let need_store = existing_value? /\ existing_value.is_need_store;
578
579
if index.left.value? /\ index.index.value? then
580
let left_value = index.left.value;
581
let type = left_value.type;
582
583
if type == null \/ type.is_error then
584
_logger.poison(index.left.location, "index left has no type");
585
586
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location);
587
return;
588
fi
589
590
// Indexable inference: an `INFERRED_VARIABLE_TYPE`
591
// receiver records an INDEX_CONSTRAINT carrying the
592
// index expression's type, so the body-retry loop
593
// can filter candidates to indexable types. Skip
594
// the "cannot index" hard error path on placeholder
595
// — the existing speculate/roll-back would discard
596
// it on retry but emitting it is noise; soft-error
597
// out to DUMMY(ERROR) instead.
598
let index_type = index.index?.value?.type;
599
600
if
601
isa Semantic.Types.INFERRED_VARIABLE_TYPE(type) /\
602
index_type?
603
then
604
let placeholder = type;
605
let constraint = Semantic.INDEX_CONSTRAINT(index_type);
606
607
_logger.mark_consumed_any_if(placeholder.origin.add_constraint(constraint));
608
609
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location);
610
return;
611
fi
612
613
if !isa Semantic.Types.NAMED(type) then
614
_logger.error(index.left.location, "cannot index {type}");
615
616
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location);
617
return;
618
fi
619
620
let named_type = type;
621
622
let function_name: string mut;
623
let arguments: Collections.LIST[Value] mut;
624
let argument_types: Collections.LIST[Type] mut;
625
626
let index_value = index.index.value!;
627
628
if need_store then
629
function_name = "set_Item";
630
631
let need_store_value = cast Need.STORE?(index.value)!.value;
632
633
arguments = Collections.LIST[Value]([index_value, need_store_value]);
634
argument_types = Collections.LIST[Type]([index_value.type!, need_store_value.type!]);
635
else
636
function_name = "get_Item";
637
arguments = Collections.LIST[Value]([index_value]);
638
argument_types = Collections.LIST[Type]([index_value.type!]);
639
fi
640
641
let symbol = named_type.find_member(function_name);
642
643
if symbol == null then
644
if need_store then
645
if named_type.find_member("get_Item")? then
646
_logger.error(index.location, "indexer is read-only in {type}");
647
else
648
_logger.error(index.location, "no indexer found in {type}");
649
fi
650
else
651
_logger.error(index.location, "no indexer found in {type}");
652
fi
653
654
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location);
655
return;
656
fi
657
658
if !isa Semantic.Symbols.FUNCTION_GROUP(symbol) then
659
_logger.poison(index.index.location, "indexer is not a function group: {symbol}");
660
fi
661
662
let overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut;
663
664
let use logger_snapshot = _logger.speculate_then_backtrack();
665
666
overload_result =
667
_overload_resolver.resolve(
668
index.location,
669
cast Semantic.Symbols.FUNCTION_GROUP?(symbol)!,
670
argument_types,
671
false,
672
true,
673
false
674
);
675
676
logger_snapshot.backtrack();
677
678
if overload_result == null then
679
if need_store then
680
_logger.error(index.location, "indexer [{argument_types[0]}] = {argument_types[1]} not found in {type}");
681
else
682
_logger.error(index.location, "indexer [{argument_types[0]}] not found in {type}");
683
fi
684
685
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location);
686
return;
687
fi
688
689
let function = overload_result.function;
690
691
// Record the indexer-accessor use over the `[...]`
692
// bracket span — from the column after the LHS (the
693
// `[`) to the end of the INDEX expression (the `]`).
694
// find_hover_use prefers shorter matches, so any
695
// inner identifier or variable use nested inside the
696
// brackets still wins on hover; the strict-
697
// containment filter in the semantic-tokens handler
698
// drops this outer span when an inner use is
699
// present, keeping the index expression coloured as
700
// itself.
701
_symbol_use_locations.add_symbol_use(
702
LOCATION(
703
index.left.location.file_name,
704
index.left.location.end_line,
705
index.left.location.end_column + 1,
706
index.location.end_line,
707
index.location.end_column
708
), function);
709
710
if function.is_unsafe_constraints then
711
_logger.warn(index.location, "unchecked-constraints", "call to {function} has unchecked constraints");
712
fi
713
714
if need_store then
715
index.compile_expressions_state.value =
716
TYPE_WRAPPER(
717
function.arguments[1],
718
function.call(index.location, left_value, arguments, null, _function_caller));
719
else
720
index.compile_expressions_state.value = function.call(index.location, left_value, arguments, null, _function_caller);
721
fi
722
fi
723
si
724
si
725
si