Skip to content
← Back

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

1
namespace Syntax.Process is
2
use Logging
3
use Source
4
use Ghul.Disposable
5
6
use Semantic.Types.Type
7
use Semantic.LEAST_UPPER_BOUND_MAP
8
9
use IR.Values
10
11
// An operator or index expression whose operand walks are held
12
// under a speculation because an operand takes its type from
13
// context and has none to offer resolution. `retried` records that
14
// the resolved formal type has been pushed in and the operand
15
// walked again, so that happens once however the second walk turns
16
// out.
17
class DEFERRED_OPERANDS(node: Trees.Node) is
18
retried: bool public
19
si
20
21
// Compiles unary, binary and index expressions. Split out of
22
// COMPILE_EXPRESSIONS, which delegates visit(unary), pre(binary),
23
// visit(binary) and visit(index) here. The exception-handling
24
// wrappers of visit(binary) / visit(index) stay on the visitor;
25
// visit_binary / visit_index are the enclosed logic.
26
class COMPILE_OPERATORS is
27
_deferred_operands: Collections.LIST[DEFERRED_OPERANDS] field
28
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: COMPILE_EXPRESSIONS
39
_value_boxer: IR.VALUE_BOXER
40
_range_slice_resolver: Semantic.RANGE_SLICE_RESOLVER
41
42
init(
43
logger: Logger,
44
symbol_table: Semantic.SYMBOL_TABLE,
45
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
46
overload_resolver: Semantic.OVERLOAD_RESOLVER,
47
symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS,
48
function_caller: Semantic.FUNCTION_CALLER,
49
calls: COMPILE_CALLS,
50
flow: NARROWING_FLOW,
51
condition_analyzer: CONDITION_ANALYZER,
52
visitor: COMPILE_EXPRESSIONS
53
) is
54
super.init()
55
56
_logger = logger
57
_symbol_table = symbol_table
58
_innate_symbol_lookup = innate_symbol_lookup
59
_overload_resolver = overload_resolver
60
61
_range_slice_resolver =
62
Semantic.RANGE_SLICE_RESOLVER(
63
innate_symbol_lookup,
64
overload_resolver,
65
function_caller,
66
logger)
67
_symbol_use_locations = symbol_use_locations
68
_function_caller = function_caller
69
_calls = calls
70
_flow = flow
71
_condition_analyzer = condition_analyzer
72
_visitor = visitor
73
_value_boxer = IR.VALUE_BOXER(logger)
74
_deferred_operands = Collections.LIST[DEFERRED_OPERANDS]()
75
si
76
77
visit_unary(unary: Trees.Expressions.UNARY) is
78
unary.compile_expressions_state.value = null
79
80
if unary.right.value? then
81
// An operand with no type has nothing for overload
82
// resolution to match against, and every consumer below
83
// reads the type as though it were there.
84
if !unary.right.value.type? then
85
unary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), unary.location)
86
return
87
fi
88
89
let right_value = unary.right.value
90
91
let function_group =
92
_static_operator_candidates(
93
unary.operation.location,
94
unary.operation.name,
95
Collections.LIST[Type]([right_value.type!])
96
)
97
98
if !function_group? then
99
if
100
_defer_on_unsettled_operand(
101
unary,
102
unary.operation.location,
103
right_value.type,
104
null
105
)
106
then
107
return
108
fi
109
110
_logger.error(unary.operation.location, "no unary operator {unary.operation} found")
111
112
unary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), unary.location)
113
return
114
fi
115
116
// FIXME: this doesn't seem right
117
let want_instance = right_value.is_consumable
118
119
let argument_values = Collections.LIST[Value]([right_value])
120
let argument_types = Collections.LIST[Type]([right_value.type!])
121
let argument_expressions = Collections.LIST[Trees.Expressions.Expression]([unary.right])
122
123
let (overload_result, errors) =
124
_resolve_with_retry(
125
unary.location,
126
function_group,
127
argument_values,
128
argument_types,
129
argument_expressions,
130
want_instance,
131
unary
132
)
133
134
if overload_result == null then
135
if
136
_defer_on_unsettled_operand(
137
unary,
138
unary.operation.location,
139
right_value.type,
140
null
141
)
142
then
143
return
144
fi
145
146
if errors? /\ errors.count > 0 then
147
_logger.merge(errors)
148
fi
149
150
unary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), unary.location)
151
return
152
fi
153
154
let function = overload_result.function
155
156
if function.is_unsafe_constraints then
157
_logger.warn(unary.location, "unchecked-constraints", "call to {function} has unchecked constraints")
158
fi
159
160
_symbol_use_locations.add_symbol_use(unary.operation.location, function)
161
162
let value = function.call(unary.location, null, argument_values, null, _function_caller)
163
164
unary.compile_expressions_state.value = value
165
fi
166
si
167
168
// Resolve an operator overload with the same constraint-push
169
// retry sequence visit_call uses: initial type-based resolve,
170
// then on null or PARTIAL re-walk function-literal operands
171
// with the chosen formal pushed as expected_type so a lambda
172
// whose parameter type was unknown on the first walk can
173
// infer it from the operator's signature.
174
//
175
// Operates inside its own speculation snapshot: the retry's
176
// re-walks update operand `.value` fields in place but the
177
// logger diagnostics from speculation are scrubbed when the
178
// snapshot backtracks, matching the visit_call pattern.
179
_resolve_with_retry(
180
location: Source.LOCATION,
181
function_group: Semantic.Symbols.FUNCTION_GROUP,
182
argument_values: Collections.LIST[Value],
183
argument_types: Collections.LIST[Type],
184
argument_expressions: Collections.List[Trees.Expressions.Expression],
185
want_instance: bool,
186
cache_key: Trees.Node
187
) -> (Semantic.OVERLOAD_RESOLVE_RESULT?, Logging.DIAGNOSTICS_STATE?) is
188
RETRY_SITE_STATS.note("operators.resolve_with_retry")
189
let use logger_snapshot = _logger.speculate_then_backtrack()
190
191
// Operator operands walk during ordinary tree descent, so
192
// the baseline captured here is the env after that walk; a
193
// retry re-walk resets to it through `_flow.restore()`, and
194
// the whole attempt rolls the env back on exit — the operand
195
// values are already set, only the diagnostics matter.
196
let use flow_speculation = _flow.speculate_then_roll_back()
197
198
let overload_result mut =
199
_overload_resolver.resolve(
200
location,
201
function_group,
202
argument_types,
203
false,
204
want_instance,
205
false
206
)
207
208
if !overload_result? then
209
let retry = _calls.try_overload_after_null(
210
function_group,
211
argument_values,
212
argument_types,
213
want_instance,
214
null,
215
argument_expressions,
216
location,
217
cache_key
218
)
219
220
if retry? then
221
overload_result = retry
222
fi
223
fi
224
225
if overload_result? /\ overload_result.needs_retry then
226
let retry = _calls.try_overload_on_partial(
227
overload_result,
228
function_group,
229
argument_values,
230
argument_types,
231
want_instance,
232
null,
233
argument_expressions,
234
location,
235
cache_key
236
)
237
238
if retry? then
239
overload_result = retry
240
fi
241
fi
242
243
let errors = logger_snapshot.backtrack()
244
245
return (overload_result, errors)
246
si
247
248
pre_binary(binary: Trees.Expressions.BINARY) -> bool is
249
// For `/\` / `\/`, walk the operands with the narrowing
250
// environment threaded so within-condition narrowing
251
// applies as the right operand compiles: in
252
// `isa T(x) /\ x.foo`, `x.foo` sees `x` as `T`. The
253
// `/\` right walks under the left's true edge, the `\/`
254
// right under its false edge. The BINARY as a whole
255
// leaves the ambient environment unchanged — the
256
// enclosing IF derives the overall narrowing from
257
// analyze_condition once the condition is fully walked.
258
let op = binary.operation.name
259
260
// A null-comparison leaf forms a presence fact from its
261
// non-null operand; stamp the log position the test is
262
// walked at, so facts formed from it skip the calls that
263
// ran before it. Both surface forms walk through a node
264
// whose operation name is `==`.
265
if op =~ "==" then
266
if isa Trees.Expressions.NULL(binary.left) \/ isa Trees.Expressions.NULL(binary.right) then
267
_flow.note_test_site(binary)
268
fi
269
fi
270
271
if op =~ "/\\" \/ op =~ "\\/" then
272
let saved = _flow.current_env.copy()
273
let epoch = _flow.heap_epoch
274
let mark = _flow.crossing_mark
275
276
binary.left.walk(_visitor)
277
278
let left_facts = _condition_analyzer.analyze_condition_facts_only(binary.left, saved)
279
280
let right_env =
281
if op =~ "/\\" then left_facts.then_env else left_facts.else_env fi
282
283
// The right operand's environment derives from the
284
// pre-left snapshot; a store during the left's own
285
// walk means its heap facts cannot be trusted while
286
// the right compiles, and its calls attach as
287
// crossings.
288
if _flow.heap_killed_since(epoch) then
289
right_env.drop_heap_facts()
290
else
291
_flow.adopt_crossings_since(right_env, mark)
292
fi
293
294
_flow.set_env(right_env)
295
296
binary.right.walk(_visitor)
297
298
// Same for the ambient environment restored after
299
// the operator: it predates both operand walks.
300
if _flow.heap_killed_since(epoch) then
301
saved.drop_heap_facts()
302
else
303
_flow.adopt_crossings_since(saved, mark)
304
fi
305
306
_flow.set_env(saved)
307
308
return true
309
fi
310
311
// An operand that takes its type from context has none to
312
// offer resolution, and reports so as it walks. Open a
313
// speculation over the operand walks, the way visit_call
314
// does over its argument walks, so that error can be rolled
315
// back once resolution has settled on a formal type to push
316
// in. The operands still walk in their ordinary order and
317
// through the ordinary walker — only the diagnostics are
318
// held.
319
if binary.left.awaits_context_type \/ binary.right.awaits_context_type then
320
_deferred_operands.add(DEFERRED_OPERANDS(binary))
321
322
RETRY_SITE_STATS.note("operators.pre_binary_deferred")
323
_logger.speculate()
324
_flow.speculate()
325
fi
326
327
return false
328
si
329
330
// The operand walks of a binary carrying a context-typed
331
// operand run inside a speculation opened by pre_binary; close
332
// it here, whichever way the resolution below went.
333
visit_binary(binary: Trees.Expressions.BINARY) is
334
if !_deferred_for(binary)? then
335
_visit_binary(binary)
336
337
return
338
fi
339
340
try
341
_visit_binary(binary)
342
finally
343
_deferred_operands.remove_at(_deferred_operands.count - 1)
344
345
_flow.commit()
346
_logger.commit()
347
yrt
348
si
349
350
// The innermost binary currently deferring an operand, when it
351
// is this one. Nested binaries push in walk order, so only the
352
// top of the stack can be the node being visited.
353
_deferred_for(node: Trees.Node) -> DEFERRED_OPERANDS? is
354
if _deferred_operands.count == 0 then
355
return null
356
fi
357
358
let top = _deferred_operands[_deferred_operands.count - 1]
359
360
return if top.node == node then top else null fi
361
si
362
363
// Once resolution has settled on a single `function`, push its
364
// formal type into an operand that was still waiting for one
365
// and walk that operand again — the same step visit_call takes
366
// for a waiting argument, and for the same reason: nothing
367
// could type the operand until the callee was known. Resolution
368
// reaches a function at all because an untyped operand carries
369
// the ERROR type, which matches any formal, so the operator's
370
// own declaration decides the target type rather than anything
371
// being assumed about how operators are declared.
372
//
373
// True when an operand was re-walked, which invalidates every
374
// type resolution just read off the operands; the caller starts
375
// over against the settled ones. `retried` keeps that to a
376
// single attempt, so an operand left error-typed by its second
377
// walk does not drive another.
378
_resolve_deferred_operands(
379
binary: Trees.Expressions.BINARY,
380
function: Semantic.Symbols.Function
381
) -> bool is
382
let deferred = _deferred_for(binary)
383
384
if !deferred? \/ deferred.retried then
385
return false
386
fi
387
388
// An instance operator takes the left operand as its
389
// receiver, so only the right is one of its arguments. A
390
// waiting left operand cannot reach that path at all: its
391
// ERROR type has no members, so no operator is found on it.
392
let left_formal =
393
if function.is_instance then null else _formal_type(function, 0) fi
394
395
let right_formal =
396
_formal_type(function, if function.is_instance then 0 else 1 fi)
397
398
let left_type = if binary.left.awaits_context_type then left_formal else null fi
399
let right_type = if binary.right.awaits_context_type then right_formal else null fi
400
401
if !left_type? /\ !right_type? then
402
return false
403
fi
404
405
deferred.retried = true
406
407
let use retry_site = RETRY_SITE_STATS.enter("operators.resolve_deferred_operands", RetrySiteKind.REWALK_WITH_INFORMATION)
408
_logger.roll_back()
409
_logger.speculate()
410
_flow.restore()
411
412
if left_type? then
413
binary.left.set_expected_type(left_type, "")
414
fi
415
416
if right_type? then
417
binary.right.set_expected_type(right_type, "")
418
fi
419
420
_visitor.rewalk(binary.left)
421
_visitor.rewalk(binary.right)
422
423
return true
424
si
425
426
// An index by a `System.Range` that reached no indexer takes a
427
// slice of the source instead. Only where no indexer matched, so
428
// a type declaring its own `[System.Range]` indexer still wins, and
429
// only for a read: there is no slice to assign through.
430
_try_range_slice(
431
index: Trees.Expressions.INDEX,
432
source: Value,
433
range: Value,
434
index_type: Semantic.Types.Type
435
) -> bool is
436
let resolved = _range_slice_resolver.try_resolve(index.location, source, index_type)
437
438
if !resolved? then
439
return false
440
fi
441
442
_symbol_use_locations.add_symbol_use(index.location, resolved.function)
443
444
index.compile_expressions_state.value =
445
_range_slice_resolver.call(index.location, resolved, source, range)
446
447
return true
448
si
449
450
// The index counterpart of _resolve_deferred_operands: the
451
// resolved indexer's first formal is the index argument's type.
452
// Both accessors take the index there, so `get_Item` and
453
// `set_Item` need no distinguishing.
454
_resolve_deferred_index(
455
index: Trees.Expressions.INDEX,
456
function: Semantic.Symbols.Function
457
) -> bool is
458
let deferred = _deferred_for(index)
459
460
if !deferred? \/ deferred.retried then
461
return false
462
fi
463
464
let formal = _formal_type(function, 0)
465
466
if !formal? \/ !index.index.awaits_context_type then
467
return false
468
fi
469
470
deferred.retried = true
471
472
let use retry_site = RETRY_SITE_STATS.enter("operators.resolve_deferred_index", RetrySiteKind.REWALK_WITH_INFORMATION)
473
_logger.roll_back()
474
_logger.speculate()
475
_flow.restore()
476
477
index.index.set_expected_type(formal, "")
478
_visitor.rewalk(index.index)
479
480
return true
481
si
482
483
// The formal at `index`, or null when the function has no such
484
// argument or the one it has is wild — a type variable free
485
// only in the waiting operand's own slot pins nothing, and the
486
// operand is left to report that it still has no type.
487
_formal_type(function: Semantic.Symbols.Function, index: int) -> Type? is
488
if index >= function.arguments.count then
489
return null
490
fi
491
492
let formal = function.arguments[index]
493
494
return if formal.is_wild then null else formal fi
495
si
496
497
_visit_binary(binary: Trees.Expressions.BINARY) is
498
binary.compile_expressions_state.value = null
499
500
// An operand with no type - a reference to a local variable
501
// that is not defined yet, whose type is never inferred - has
502
// nothing for overload resolution to match against, and every
503
// consumer below reads the type as though it were there.
504
if
505
!binary.left.value? \/ !binary.right.value? \/
506
!binary.left.value.type? \/ !binary.right.value.type?
507
then
508
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
509
return
510
fi
511
512
// `??` does not go through overload resolution: the
513
// "chain closer" vs "stays optional" distinction is
514
// determined by the right operand's own nullability,
515
// which a single typing rule expresses directly. Going
516
// through the user-operator path would force this
517
// distinction into two ambiguous prelude overloads.
518
if binary.operation.name =~ "??" then
519
_visit_null_coalesce(binary)
520
return
521
fi
522
523
let left_value = binary.left.value
524
let right_value = binary.right.value
525
let left_is_consumable = left_value.check_is_consumable(_logger, binary.left.location)
526
let right_is_consumable = right_value.check_is_consumable(_logger, binary.right.location)
527
528
// Neither operand settled: an operand whose type is a
529
// placeholder offers no candidates of its own, so with
530
// both of them waiting nothing says which candidate is
531
// meant, and resolving anyway pushes the formal of
532
// whichever one was found onto both placeholders as a
533
// bound. One settled operand is a different case - it is
534
// what picks the candidate, and the candidate is then
535
// entitled to say what the other operand must be - so
536
// this waits only for the pair.
537
if
538
(left_value.type?.is_inferred ?? false) /\
539
(right_value.type?.is_inferred ?? false) /\
540
_defer_on_unsettled_operand(
541
binary,
542
binary.operation.location,
543
left_value.type,
544
right_value.type
545
)
546
then
547
return
548
fi
549
550
// `=~` over an optional operand gets its null checks
551
// written around the call. This runs ahead of resolution
552
// rather than as a fallback: only an optional *argument*
553
// fails to resolve, while an optional *receiver* resolves
554
// to an ordinary member call and dereferences null.
555
let null_safe = _try_null_safe_equality(binary, left_value, right_value)
556
557
if null_safe? then
558
binary.compile_expressions_state.value = null_safe
559
return
560
fi
561
562
// Two separate value/type/expression lists. The free-function
563
// path takes both operands as arguments; the member-call path
564
// takes only the right operand (left is the receiver). The
565
// retry helper updates the lists in place when it re-walks,
566
// so the two paths can't share storage.
567
let binary_argument_values = Collections.LIST[Value]([left_value, right_value])
568
let binary_argument_types = Collections.LIST[Type]([left_value.type!, right_value.type!])
569
let binary_argument_expressions = Collections.LIST[Trees.Expressions.Expression]([binary.left, binary.right])
570
571
let member_argument_values = Collections.LIST[Value]([right_value])
572
let member_argument_types = Collections.LIST[Type]([right_value.type!])
573
let member_argument_expressions = Collections.LIST[Trees.Expressions.Expression]([binary.right])
574
575
let binary_function_group: Semantic.Symbols.FUNCTION_GROUP mut
576
let binary_overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut = null
577
let binary_errors: Logging.DIAGNOSTICS_STATE? mut = null
578
579
let binary_candidates =
580
_static_operator_candidates(
581
binary.operation.location,
582
binary.operation.name,
583
binary_argument_types
584
)
585
586
if binary_candidates? then
587
binary_function_group = binary_candidates
588
589
(binary_overload_result, binary_errors) =
590
_resolve_with_retry(
591
binary.location,
592
binary_function_group,
593
binary_argument_values,
594
binary_argument_types,
595
binary_argument_expressions,
596
_symbol_table.current_instance_context?,
597
binary
598
)
599
fi
600
601
let member_overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut = null
602
let member_function_group: Semantic.Symbols.FUNCTION_GROUP mut
603
let member_errors: Logging.DIAGNOSTICS_STATE? mut = null
604
605
// An instance operator's left operand is `self`, which is
606
// never absent, so no member candidate can accept a left
607
// operand that may hold no value - the same rejection any
608
// other non-optional formal makes of an optional actual. An
609
// author who wants to answer for an absent left operand
610
// declares a global operator taking `T?`, where the operand
611
// really is an argument. `=~` and `!~` never reach here over
612
// an optional operand: the null-safe path above answers them.
613
//
614
// The candidate is resolved against the arguments before it
615
// is declined, so this is set only where the absent receiver
616
// is the sole reason it did not apply. A candidate the
617
// arguments would have rejected anyway keeps its own
618
// diagnostic, which says what is actually wrong.
619
let declined_absent_receiver mut = false
620
621
if let binary.left.value?, value.type? then
622
let member_candidates =
623
_instance_operator_candidates(
624
binary.operation.location,
625
binary.operation.name,
626
type.find_member(binary.operation.name)
627
)
628
629
if member_candidates? then
630
member_function_group = member_candidates
631
632
(member_overload_result, member_errors) =
633
_resolve_with_retry(
634
binary.location,
635
cast Semantic.Symbols.FUNCTION_GROUP(member_function_group),
636
member_argument_values,
637
member_argument_types,
638
member_argument_expressions,
639
value.is_consumable,
640
binary
641
)
642
643
// Discard a hidden member whether resolution
644
// succeeded or failed. Keeping the diagnostics of a
645
// failed one would report a candidate operator
646
// resolution can never select, ahead of the real
647
// error from the innate.
648
if
649
HIDDEN_OPERATOR_MEMBERS.is_hidden(member_function_group) \/
650
(member_overload_result? /\ member_overload_result.function.is_hidden_from_operator_resolution)
651
then
652
member_overload_result = null
653
member_errors = null
654
fi
655
656
if member_overload_result? /\ _visitor.receiver_may_be_absent(binary.left) then
657
declined_absent_receiver = true
658
659
member_overload_result = null
660
member_errors = null
661
fi
662
fi
663
fi
664
665
let function: Semantic.Symbols.Function? mut = null
666
667
if member_overload_result? /\ binary_overload_result? then
668
if cast int(member_overload_result.score) <= cast int(binary_overload_result.score) then
669
function = member_overload_result.function
670
else
671
function = binary_overload_result.function
672
fi
673
elif member_overload_result? then
674
function = member_overload_result.function
675
elif binary_overload_result? then
676
function = binary_overload_result.function
677
fi
678
679
if function? then
680
// An operand that was waiting for a type now has the
681
// resolved formal to take one from. Everything read off
682
// the operands above is stale once it has been walked
683
// again, so resolve the whole operator afresh.
684
if _resolve_deferred_operands(binary, function) then
685
_visit_binary(binary)
686
687
return
688
fi
689
690
_symbol_use_locations.add_symbol_use(binary.operation.location, function)
691
692
// Mirror the inferred-T constraint check in compile_calls: an
693
// operator function resolved via overload resolution has its
694
// type arguments bound by inference (the operator is never
695
// explicitly specialized), so the declared kind / type-bound
696
// constraints have not been checked yet.
697
if
698
function.is_generic /\
699
function.generic_arguments.count == function.generic_argument_names.count
700
then
701
Semantic.Symbols.GENERIC_CONSTRAINT_CHECKER().check_arguments(
702
binary.location,
703
_logger,
704
function,
705
function.generic_argument_names,
706
function.generic_arguments
707
)
708
fi
709
710
let is_relational_rewrite =
711
RELATIONAL_REWRITE.is_relational_rewrite(binary.operation.name, binary.actual_operation)
712
713
let force_type: Type? mut = _
714
let want_not mut = false
715
716
// A property of the operator's declaration, so it holds
717
// however the call was spelled - the relational tokens
718
// all rewrite to `<>`, and a directly written `<>` is
719
// the same member with the same requirement.
720
if
721
binary.operation.name =~ "<>" /\
722
!function.return_type!.matches(_innate_symbol_lookup.get_int_type())
723
then
724
// FIXME: do this check on the definition:
725
_logger.error(binary.location, "<> order operator must return int")
726
fi
727
728
if is_relational_rewrite then
729
force_type = _innate_symbol_lookup.get_bool_type()
730
elif binary.operation.name =~ "==" then
731
let left_type = binary.left.value?.type
732
let right_type = binary.right.value?.type
733
734
if
735
left_is_consumable /\ right_is_consumable /\
736
left_type? /\ right_type? /\
737
cast int(left_type.compare(right_type)) > cast int(Semantic.Types.MATCH.ASSIGNABLE) /\
738
cast int(right_type.compare(left_type)) > cast int(Semantic.Types.MATCH.ASSIGNABLE)
739
then
740
if binary.actual_operation =~ "==" then
741
_logger.error(binary.location, "== cannot be applied to values of non-assignable types")
742
else
743
_logger.error(binary.location, "!= cannot be applied to values of non-assignable types")
744
fi
745
elif
746
isa Semantic.Symbols.InnateFunction(function) /\
747
function.innate_name =~ "compare.value" /\
748
left_type? /\
749
!VALUE_EQUALITY_OPERANDS.can_compare(left_type)
750
then
751
// compare.value lowers to a bare ceq, which for
752
// struct operands the runtime either refuses at
753
// JIT time or runs as a shallow bitwise
754
// comparison rather than value equality.
755
// Rejected here, where the operand type is known
756
// and the location is useful.
757
if binary.actual_operation =~ "==" then
758
_logger.error(binary.location, "== cannot be applied to values of type {left_type}", binary.location, "help: use =~ to compare by value")
759
else
760
_logger.error(binary.location, "!= cannot be applied to values of type {left_type}", binary.location, "help: use !~ to compare by value")
761
fi
762
fi
763
elif !function.is_innate /\ binary.actual_operation =~ "!~" then
764
want_not = true
765
fi
766
767
let value: Value mut
768
769
if function.is_unsafe_constraints then
770
_logger.warn(binary.location, "unchecked-constraints", "call to {function} has unchecked constraints")
771
fi
772
773
// Pass the path-specific argument-value list so the
774
// values used for the call reflect the retry's re-walks
775
// for whichever group was selected — without this, a
776
// member-path retry that re-walked the right operand
777
// would overwrite values the binary-path retry already
778
// committed to its own list (and vice versa).
779
if function.is_instance then
780
value = function.call(binary.location, left_value, member_argument_values, force_type, _function_caller)
781
else
782
// A pack-marked declared return type re-presents the
783
// call's value as the N-ary function of the bound
784
// tuple - the same presentation the named-call path
785
// applies. Instance operators cannot carry the
786
// marker: their left operand is a receiver, not an
787
// argument the pack could spread.
788
_calls.present_pack_arguments(binary, [binary.left, binary.right], function, binary_argument_values)
789
790
_calls.feed_pack_from_expected(function, binary.expected_type)
791
792
let wrapped = _calls.wrap_return_pack(binary, function, binary.location, null, binary_argument_values)
793
794
if wrapped? then
795
binary.compile_expressions_state.value = wrapped
796
797
return
798
fi
799
800
value = function.call(binary.location, null, binary_argument_values, force_type, _function_caller)
801
802
if isa Call.INNATE(value) then
803
let innate_value = value
804
805
innate_value.actual_operation = binary.actual_operation
806
807
value = innate_value.lower()
808
fi
809
fi
810
811
if !function.is_innate /\ is_relational_rewrite then
812
value = COMPARE_ORDER_TO_ZERO(value, binary.actual_operation, _innate_symbol_lookup.get_bool_type())
813
fi
814
815
if want_not then
816
value = NOT(value)
817
fi
818
819
binary.compile_expressions_state.value = value
820
else
821
// `a =~ b` where no operator resolved but the operands
822
// still compare: a bare type parameter or a tuple through
823
// the runtime's comparer, two sequences element by
824
// element, or a `<>` read against zero.
825
let comparison = _try_lowered_equality(binary, left_value, right_value)
826
827
if comparison? then
828
let value = if binary.actual_operation =~ "!~" then NOT(comparison) else comparison fi
829
830
binary.compile_expressions_state.value = value
831
832
return
833
fi
834
835
// No operator can resolve against an operand whose type
836
// is still an inference placeholder, and the walk that
837
// settles it has not happened yet. Record an obligation
838
// and leave the site for the next walk rather than
839
// reporting an operator the user may well have: what the
840
// message would name is `***`, and the type it is waiting
841
// for arrives by ordinary inference.
842
if
843
_defer_on_unsettled_operand(
844
binary,
845
binary.operation.location,
846
binary.left.value?.type,
847
binary.right.value?.type
848
)
849
then
850
return
851
fi
852
853
// The type declares this operator but its left operand
854
// may hold no value, so the member candidate was never
855
// in the running. Reported here rather than left to the
856
// global-path failure below, which would list the innate
857
// overloads and never mention the operator the user
858
// actually wrote.
859
if declined_absent_receiver then
860
let left_type = binary.left.value?.type
861
let right_type = binary.right.value?.type
862
863
// An error-typed operand already drew its own
864
// diagnostic, and the member candidate only matched
865
// because an error type is assignable to anything, so
866
// reporting the operator here would render the error
867
// type back at the user as if it were what they wrote.
868
if
869
!(left_type? /\ left_type.is_error) /\
870
!(right_type? /\ right_type.is_error)
871
then
872
_logger.error(
873
binary.operation.location,
874
"operator {left_type} {binary.operation} {right_type} not found",
875
binary.operation.location,
876
"help: an operator declared on a type takes a left operand that always holds a value; narrow the left operand first")
877
fi
878
879
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
880
881
return
882
fi
883
884
if binary_errors? /\ binary_errors.count > 0 then
885
_logger.merge(binary_errors)
886
fi
887
888
if member_errors? /\ member_errors.count > 0 then
889
_logger.merge(member_errors)
890
fi
891
892
// A compound-statement left operand got here through a
893
// parenthesised group's expression reading; with no
894
// operator resolving, the intended reading may have been
895
// the block one, which needs a `;` after the statement.
896
if isa Trees.Expressions.STATEMENT(binary.left) then
897
_logger.info(binary.operation.location, "the compound statement reads as the left operand of this operator; end it with ; to read what follows as a separate statement")
898
fi
899
900
let left_type = binary.left.value?.type
901
let right_type = binary.right.value?.type
902
903
// An error-typed operand already drew its own diagnostic,
904
// and no operator can resolve against it, so reporting the
905
// operator as missing renders the error type back at the
906
// user as if it were what they wrote.
907
if
908
(!binary_errors? \/ binary_errors.count == 0) /\
909
(!member_errors? \/ member_errors.count == 0) /\
910
!(left_type? /\ left_type.is_error) /\
911
!(right_type? /\ right_type.is_error)
912
then
913
_logger.error(binary.operation.location, "operator {left_type} {binary.operation} {right_type} not found")
914
fi
915
916
// FIXME QQ should probably do this
917
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
918
fi
919
si
920
921
// No operator resolves against an operand whose type is still an
922
// inference placeholder, and the walk that settles it has not
923
// happened yet. Record an obligation and leave the site waiting
924
// rather than reporting an operator the user may well have: the
925
// message would name `***`, and the type it waits for arrives by
926
// ordinary inference. The result waits with it, so a consumer
927
// reads a placeholder rather than an ERROR that no later walk
928
// displaces.
929
_defer_on_unsettled_operand(
930
node: Trees.Expressions.Expression,
931
operator_location: Source.LOCATION,
932
left_type: Semantic.Types.Type?,
933
right_type: Semantic.Types.Type?
934
) -> bool is
935
let unsettled = Semantic.OPERAND_WAIT.for_operands(left_type, right_type)
936
937
if !unsettled? then
938
return false
939
fi
940
941
Semantic.OBLIGATIONS.defer("operator", unsettled, operator_location)
942
943
node.compile_expressions_state.value =
944
DUMMY(Semantic.Types.INFERRED_JOIN_TYPE(unsettled), node.location)
945
946
return true
947
si
948
949
// `a =~ b` where no operator resolved: the operands may still
950
// compare through a lowering that is not an operator at all -
951
// the runtime's comparer for a tuple or a bare type parameter,
952
// element by element for two sequences, or a `<>` read against
953
// zero. Identity is not offered here: two references of a class
954
// declaring neither operator stay an error when written out,
955
// and only compare by identity as a member of something that
956
// synthesizes its own. Checked after resolution so a bound
957
// declaring `=~` still resolves its own operator.
958
_try_lowered_equality(
959
binary: Trees.Expressions.BINARY,
960
left_value: Value,
961
right_value: Value
962
) -> Value? is
963
if !(binary.operation.name =~ "=~") then
964
return null
965
fi
966
967
let left_type = left_value.type
968
let right_type = right_value.type
969
970
if !left_type? \/ !right_type? then
971
return null
972
fi
973
974
let lowering = _choose_equality(left_type, right_type, binary.location)
975
976
if !lowering? \/ isa EqualityLowering.OPERATOR(lowering) \/ isa EqualityLowering.IDENTITY(lowering) then
977
return null
978
fi
979
980
return _build_equality(lowering, left_value, right_value, binary.location)
981
si
982
983
984
// A reflected tuple type answers is_value_tuple; the
985
// source-path type is Types.TUPLE directly.
986
_is_tuple(type: Semantic.Types.Type) -> bool =>
987
type.is_value_tuple \/ isa Semantic.Types.TUPLE(type)
988
989
// The types that cannot declare a `=~` of their own and so are
990
// compared through the runtime's comparer instead.
991
_compares_through_comparer(type: Semantic.Types.Type) -> bool =>
992
type.is_type_variable \/ _is_tuple(type)
993
994
// Which comparison two present values of the given static types
995
// get, decided once here for every place an equality is built.
996
//
997
// In order: an operator the left type declares or inherits, or
998
// the innate or global one a scalar, string or enum carries; a
999
// `<>` the type declares, read against zero; the runtime's
1000
// comparer, for a tuple or a bare type parameter that can
1001
// declare nothing; a sequence, element by element at the
1002
// element's own static type; and reference identity for a class
1003
// that declares neither operator. Null when nothing compares
1004
// them - a struct declaring neither, or a sequence of one, where
1005
// the bitwise or identity answer .NET would give is wrong rather
1006
// than merely weak.
1007
_choose_equality(
1008
left_type: Type,
1009
right_type: Type,
1010
location: Source.LOCATION
1011
) -> EqualityLowering? =>
1012
_choose_equality(left_type, right_type, location, Collections.LIST[Type]())
1013
1014
// `resolving` holds the element types of the sequences being
1015
// lowered further out. A type that is a sequence of itself - a
1016
// class implementing `List` at itself, or two that implement
1017
// `List` at each other - would otherwise be its own element
1018
// for ever; the sequence rule declines for such an element and
1019
// the class rules answer for it instead.
1020
_choose_equality(
1021
left_type: Type,
1022
right_type: Type,
1023
location: Source.LOCATION,
1024
resolving: Collections.LIST[Type]
1025
) -> EqualityLowering? is
1026
if left_type.is_error \/ right_type.is_error then
1027
return null
1028
fi
1029
1030
let member = _find_member_equals_operator(left_type, right_type, location)
1031
1032
if member? then
1033
return EqualityLowering.OPERATOR(member)
1034
fi
1035
1036
let global_function = _find_global_equals_operator(left_type, right_type, location)
1037
1038
if
1039
global_function? /\
1040
NULL_SAFE_EQUALITY_GATE.accepts_global_operator(
1041
global_function,
1042
_innate_symbol_lookup.get_bool_type())
1043
then
1044
return EqualityLowering.OPERATOR(global_function)
1045
fi
1046
1047
let order = _find_order_operator(left_type, right_type, location)
1048
1049
if order? then
1050
return EqualityLowering.ORDER(order)
1051
fi
1052
1053
if _compares_through_comparer(left_type) /\ left_type.matches(right_type) then
1054
let comparer_type = _innate_symbol_lookup.get_equality_comparer_type(left_type)
1055
1056
if comparer_type? then
1057
return EqualityLowering.COMPARER(comparer_type)
1058
fi
1059
fi
1060
1061
let left_element = _sequence_element_type(left_type)
1062
let right_element = _sequence_element_type(right_type)
1063
1064
if
1065
left_element? /\ right_element? /\
1066
left_element.matches(right_element) /\
1067
!_is_resolving(left_element, resolving)
1068
then
1069
resolving.add(left_element)
1070
1071
let element = _choose_equality(left_element, right_element, location, resolving)
1072
1073
resolving.remove_at(resolving.count - 1)
1074
1075
if element? then
1076
return EqualityLowering.SEQUENCE(left_element, element)
1077
fi
1078
1079
return null
1080
fi
1081
1082
if
1083
!isa Semantic.Types.NAMED(left_type) \/
1084
left_type.is_value_type \/
1085
!left_type.matches(right_type)
1086
then
1087
return null
1088
fi
1089
1090
// A class declaring neither operator. One declared in ghūl
1091
// source with no `equals` of its own compares by reference,
1092
// which is what the runtime comparer would answer for it
1093
// too. Anything else keeps the comparer: an imported class
1094
// overriding `Equals` without `IEquatable`, or a source
1095
// class writing `equals` out, answers through that override
1096
// and a bare reference test would not.
1097
if
1098
isa Semantic.Symbols.Classy(left_type.symbol) /\
1099
!left_type.symbol.is_reflected /\
1100
_equals_is_objects_own(left_type)
1101
then
1102
return EqualityLowering.IDENTITY
1103
fi
1104
1105
let comparer_type = _innate_symbol_lookup.get_equality_comparer_type(left_type)
1106
1107
if comparer_type? then
1108
return EqualityLowering.COMPARER(comparer_type)
1109
fi
1110
1111
return null
1112
si
1113
1114
// A `=~` the left type declares or inherits that takes the right
1115
// type, reached with the left operand as its receiver. Hidden
1116
// members - string's static op_Equality - are left to the global
1117
// operator that stands in for them.
1118
_find_member_equals_operator(
1119
left_type: Type,
1120
right_type: Type,
1121
location: Source.LOCATION
1122
) -> Semantic.Symbols.Function? is
1123
let member_symbol = left_type.find_member("=~")
1124
1125
if !member_symbol? \/ !isa Semantic.Symbols.FUNCTION_GROUP(member_symbol) then
1126
return null
1127
fi
1128
1129
let group = cast Semantic.Symbols.FUNCTION_GROUP(member_symbol)
1130
1131
if HIDDEN_OPERATOR_MEMBERS.is_hidden(group) then
1132
return null
1133
fi
1134
1135
let overload_result =
1136
_resolve_silently(location, group, Collections.LIST[Type]([right_type]), true)
1137
1138
if !overload_result? then
1139
return null
1140
fi
1141
1142
let function = overload_result.function
1143
1144
if
1145
!NULL_SAFE_EQUALITY_GATE.accepts_operator(
1146
function,
1147
_innate_symbol_lookup.get_bool_type())
1148
then
1149
return null
1150
fi
1151
1152
return function
1153
si
1154
1155
// A `<>` the left type declares or inherits that takes the right
1156
// type and answers int, so it can be read against zero.
1157
_find_order_operator(
1158
left_type: Type,
1159
right_type: Type,
1160
location: Source.LOCATION
1161
) -> Semantic.Symbols.Function? is
1162
let member_symbol = left_type.find_member("<>")
1163
1164
if !member_symbol? \/ !isa Semantic.Symbols.FUNCTION_GROUP(member_symbol) then
1165
return null
1166
fi
1167
1168
let group = cast Semantic.Symbols.FUNCTION_GROUP(member_symbol)
1169
1170
if HIDDEN_OPERATOR_MEMBERS.is_hidden(group) then
1171
return null
1172
fi
1173
1174
let overload_result =
1175
_resolve_silently(location, group, Collections.LIST[Type]([right_type]), true)
1176
1177
if !overload_result? then
1178
return null
1179
fi
1180
1181
let function = overload_result.function
1182
1183
if
1184
!(function.is_instance \/ isa Semantic.Symbols.INNATE_METHOD(function)) \/
1185
function.is_hidden_from_operator_resolution \/
1186
function.arguments.count != 1
1187
then
1188
return null
1189
fi
1190
1191
let return_type = function.return_type
1192
1193
if !return_type? \/ !return_type.matches(_innate_symbol_lookup.get_int_type()) then
1194
return null
1195
fi
1196
1197
return function
1198
si
1199
1200
_is_resolving(type: Type, resolving: Collections.LIST[Type]) -> bool is
1201
for t in resolving do
1202
if t.matches(type) then
1203
return true
1204
fi
1205
od
1206
1207
return false
1208
si
1209
1210
// Whether the only `equals` a type has is the one every object
1211
// inherits: nothing along its ancestry overrides it, so a
1212
// reference test and the runtime comparer answer alike.
1213
_equals_is_objects_own(type: Type) -> bool is
1214
let member = type.find_member("equals")
1215
1216
if !member? then
1217
return true
1218
fi
1219
1220
if !isa Semantic.Symbols.FUNCTION_GROUP(member) then
1221
return false
1222
fi
1223
1224
let object_symbol = _innate_symbol_lookup.get_object_type().symbol.unspecialized_symbol
1225
1226
for function in cast Semantic.Symbols.FUNCTION_GROUP(member).functions do
1227
let owner = function.owner
1228
1229
if !owner? \/ owner.unspecialized_symbol != object_symbol then
1230
return false
1231
fi
1232
od
1233
1234
return true
1235
si
1236
1237
// The element type of an array or list, and null for anything
1238
// that is neither. A string is not one: it satisfies neither
1239
// `List[char]` nor an array, and it has its own operator anyway.
1240
_sequence_element_type(type: Type) -> Type? is
1241
if let array: Semantic.Types.ARRAY = type then
1242
return array.arguments[0]
1243
fi
1244
1245
let list = _innate_symbol_lookup.get_unspecialized_list_type()
1246
1247
return Semantic.Symbols.TYPE_ARGUMENT_EXTRACTOR.extract(type, list)
1248
si
1249
1250
// The comparison a lowering describes, over two present values.
1251
_build_equality(
1252
lowering: EqualityLowering,
1253
left_value: Value,
1254
right_value: Value,
1255
location: Source.LOCATION
1256
) -> Value is
1257
let bool_type = _innate_symbol_lookup.get_bool_type()
1258
1259
case lowering
1260
when operator: EqualityLowering.OPERATOR then
1261
return _build_operator_call(operator.function, "=~", left_value, right_value, location)
1262
when order: EqualityLowering.ORDER then
1263
let call = _build_operator_call(order.function, "<>", left_value, right_value, location)
1264
1265
return COMPARE_ORDER_TO_ZERO(call, "==", bool_type)
1266
when comparer: EqualityLowering.COMPARER then
1267
return EQUALITY_COMPARISON(left_value, right_value, comparer.comparer_type, bool_type)
1268
when sequence: EqualityLowering.SEQUENCE then
1269
return _build_sequence_equality(sequence, left_value, right_value, location)
1270
else
1271
return REFERENCE_EQUALITY(left_value, right_value, bool_type)
1272
esac
1273
si
1274
1275
// A call of an operator over two present operands. A declared
1276
// operator takes the left operand as its receiver; an innate or
1277
// a global one takes both as arguments. An innate lowers to an
1278
// opcode and has to be told which spelling it stands in for -
1279
// the positive one always, since `!~` negates the whole result.
1280
_build_operator_call(
1281
function: Semantic.Symbols.Function,
1282
spelling: string,
1283
left_value: Value,
1284
right_value: Value,
1285
location: Source.LOCATION
1286
) -> Value is
1287
_symbol_use_locations.add_symbol_use(location, function)
1288
1289
let value =
1290
if function.is_instance then
1291
function.call(
1292
location,
1293
left_value,
1294
Collections.LIST[Value]([right_value]),
1295
null,
1296
_function_caller
1297
)
1298
else
1299
function.call(
1300
location,
1301
null,
1302
Collections.LIST[Value]([left_value, right_value]),
1303
null,
1304
_function_caller
1305
)
1306
fi
1307
1308
if isa Call.INNATE(value) then
1309
let innate_value = value
1310
1311
innate_value.actual_operation = spelling
1312
1313
return innate_value.lower()
1314
fi
1315
1316
return value
1317
si
1318
1319
// Two sequences compared by count and then element by element.
1320
// Each operand is read once per element and its count twice, so
1321
// both are spilled; the element comparison is built against a
1322
// load of the index local the loop node declares, at the element
1323
// lowering already chosen.
1324
_build_sequence_equality(
1325
sequence: EqualityLowering.SEQUENCE,
1326
left_value: Value,
1327
right_value: Value,
1328
location: Source.LOCATION
1329
) -> Value is
1330
let bool_type = _innate_symbol_lookup.get_bool_type()
1331
let int_type = _innate_symbol_lookup.get_int_type()
1332
1333
let block = IR.Values.BLOCK(bool_type)
1334
1335
let load_left = left_value.get_temp_copier(block, "sequence_left")
1336
let load_right = right_value.get_temp_copier(block, "sequence_right")
1337
1338
let left_count = _load_property(load_left(), "count", location)
1339
let right_count = _load_property(load_right(), "count", location)
1340
1341
if !left_count? \/ !right_count? then
1342
_logger.error(location, "cannot compare {left_value.type} element-wise: it has no count")
1343
1344
return DUMMY(Semantic.Types.ERROR(), location)
1345
fi
1346
1347
let left_count_temp = IR.TEMP(block, "sequence_left_count", left_count)
1348
let right_count_temp = IR.TEMP(block, "sequence_right_count", right_count)
1349
1350
let index_name = ".sequence_index.{IR.TEMP.get_next_id()}"
1351
1352
let left_element = _index_element(load_left(), IR.Values.Load.TEMP(index_name, int_type), location)
1353
let right_element = _index_element(load_right(), IR.Values.Load.TEMP(index_name, int_type), location)
1354
1355
if !left_element? \/ !right_element? then
1356
_logger.error(location, "cannot compare {left_value.type} element-wise: it has no indexer")
1357
1358
return DUMMY(Semantic.Types.ERROR(), location)
1359
fi
1360
1361
let element_equals = _build_equality(sequence.element, left_element, right_element, location)
1362
1363
block.add(
1364
SEQUENCE_EQUALITY(
1365
left_count_temp.load(),
1366
right_count_temp.load(),
1367
index_name,
1368
int_type,
1369
element_equals,
1370
bool_type))
1371
1372
block.close()
1373
1374
return block
1375
si
1376
1377
// A read of a named property on a receiver, or null when the
1378
// receiver's type has no property of that name.
1379
_load_property(receiver: Value, name: string, location: Source.LOCATION) -> Value? is
1380
let type = receiver.type
1381
1382
if !type? then
1383
return null
1384
fi
1385
1386
let symbol = type.find_member(name)
1387
1388
if let property: Semantic.Symbols.Property = symbol then
1389
return property.load(location, receiver, IoC.CONTAINER.instance.symbol_loader)
1390
fi
1391
1392
return null
1393
si
1394
1395
// `receiver[index]` through the receiver type's read indexer, or
1396
// null when none takes the index.
1397
_index_element(receiver: Value, index: Value, location: Source.LOCATION) -> Value? is
1398
let type = receiver.type
1399
1400
if !type? \/ !index.type? then
1401
return null
1402
fi
1403
1404
let symbol = type.find_member(Semantic.Symbols.INDEXER_NAMES.read)
1405
1406
if !symbol? \/ !isa Semantic.Symbols.FUNCTION_GROUP(symbol) then
1407
return null
1408
fi
1409
1410
let overload_result =
1411
_resolve_silently(
1412
location,
1413
cast Semantic.Symbols.FUNCTION_GROUP(symbol),
1414
Collections.LIST[Type]([index.type!]),
1415
true)
1416
1417
if !overload_result? then
1418
return null
1419
fi
1420
1421
let function = overload_result.function
1422
1423
_symbol_use_locations.add_symbol_use(location, function)
1424
1425
return function.call(location, receiver, Collections.LIST[Value]([index]), null, _function_caller)
1426
si
1427
1428
1429
// A field comparison in a synthesized union `=~`. The operands
1430
// are plain member loads compiled by the ordinary walk; what is
1431
// decided here is which comparison they run, which depends on
1432
// the field's static type and so was not knowable when the body
1433
// was built.
1434
// The whole body of a synthesized `=~`, emitted here rather
1435
// than built as a tree where the members were named. What the
1436
// comparison does depends on the owner's kind, on what its
1437
// ancestors contribute and on each member's static type, none
1438
// of which exist where a body would have to be written.
1439
//
1440
// Three guards, one per kind of owner. A struct has no other
1441
// runtime type to be confused with, so it needs none. A union
1442
// variant tests `isa` against itself: its parameter is the
1443
// union, and no other variant can pass. A class compares
1444
// runtime types, because a subclass value passes an `isa` for
1445
// its base and the two must not compare equal.
1446
visit_memberwise_equals(memberwise: Trees.Expressions.MEMBERWISE_EQUALS) is
1447
memberwise.compile_expressions_state.value = null
1448
1449
let bool_type = _innate_symbol_lookup.get_bool_type()
1450
let location = memberwise.location
1451
1452
let owner = _visitor.current_instance_context
1453
1454
if !owner? \/ !isa Semantic.Symbols.Classy(owner) then
1455
memberwise.compile_expressions_state.value =
1456
DUMMY(Semantic.Types.ERROR(), location)
1457
1458
return
1459
fi
1460
1461
let owner_classy = cast Semantic.Symbols.Classy(owner)
1462
1463
let self_value = _load_self(owner_classy, location)
1464
let other_value = _load_other(location)
1465
1466
if !self_value? \/ !other_value? then
1467
memberwise.compile_expressions_state.value =
1468
DUMMY(Semantic.Types.ERROR(), location)
1469
1470
return
1471
fi
1472
1473
let inherited = _inherited_memberwise_equals(owner_classy)
1474
1475
let operands = Collections.LIST[Value]()
1476
1477
let guard = _memberwise_guard(owner_classy, self_value, other_value, location)
1478
1479
if guard? then
1480
operands.add(guard)
1481
fi
1482
1483
// Read through the owner's own type: a class parameter is
1484
// the type the whole chain overrides at, which is a base
1485
// of this one wherever this class is not the root.
1486
let other_as_owner = _narrow_to_owner(other_value, owner_classy, location)
1487
1488
for i in 0..memberwise.members.count do
1489
let member = memberwise.members[i]
1490
1491
// The member's own declaration, so that a report about
1492
// how it compares lands where it was written.
1493
let member_location = memberwise.member_locations[i]
1494
1495
let left = _load_member(self_value, owner_classy, member, member_location)
1496
let right = _load_member(other_as_owner, owner_classy, member, member_location)
1497
1498
if !left? \/ !right? then
1499
continue
1500
fi
1501
1502
operands.add(
1503
_compile_field_equals(
1504
left,
1505
right,
1506
member_location,
1507
memberwise.reports_fallback
1508
)
1509
)
1510
1511
_note_memberwise_callees(
1512
memberwise.called_functions,
1513
left.type,
1514
right.type,
1515
member_location
1516
)
1517
od
1518
1519
// Whatever the base holds is compared by the base's own
1520
// operator, reached non-virtually so that it answers for
1521
// its own members rather than dispatching back to this
1522
// one. A base that compares nothing is not called at all:
1523
// its operator is reference identity, which would answer
1524
// false for two distinct values this class holds equal.
1525
if inherited? then
1526
memberwise.called_functions.add(inherited)
1527
1528
operands.add(
1529
IR.Values.Call.INSTANCE(
1530
IR.Values.Load.SUPER(owner_classy),
1531
inherited,
1532
Collections.LIST[Value]([other_value]),
1533
bool_type
1534
)
1535
)
1536
fi
1537
1538
memberwise.compile_expressions_state.value = ALL_OF(operands, bool_type)
1539
si
1540
1541
// `self`, at the owner's own type. A value type is loaded by
1542
// address, as it is anywhere else it is read from.
1543
_load_self(owner: Semantic.Symbols.Classy, location: Source.LOCATION) -> Value? is
1544
let type = _own_instance_type(owner, location)
1545
1546
if owner.is_value_type then
1547
return IR.Values.Load.VALUE_SELF(owner, type)
1548
fi
1549
1550
return IR.Values.Load.REFERENCE_SELF(owner, type)
1551
si
1552
1553
// The type a value of the owner has inside its own body. A
1554
// generic owner is its own instantiation at its own parameters,
1555
// as reading `self` builds it: the unspecialized symbol's type
1556
// would name the open form, and a field read through that is
1557
// not a field of any type that exists.
1558
_own_instance_type(
1559
owner: Semantic.Symbols.Classy,
1560
location: Source.LOCATION
1561
) -> Semantic.Types.Type? is
1562
if owner.argument_names.count == 0 then
1563
return owner.type
1564
fi
1565
1566
let arguments = Collections.LIST[Type]()
1567
1568
for name in owner.argument_names do
1569
let argument = owner.find_member(name)
1570
let argument_type = if argument? then argument.type else null fi
1571
1572
if argument? /\ argument.is_type_variable /\ argument_type? then
1573
arguments.add(argument_type)
1574
fi
1575
od
1576
1577
if arguments.count != owner.argument_names.count then
1578
return owner.type
1579
fi
1580
1581
return Semantic.Types.GENERIC(location, owner, arguments)
1582
si
1583
1584
// The operator's own parameter, found by the name the
1585
// synthesis gave it.
1586
_load_other(location: Source.LOCATION) -> Value? is
1587
let symbol = _visitor.try_find(Trees.Identifiers.Identifier(location, "other"))
1588
1589
if !symbol? then
1590
return null
1591
fi
1592
1593
return IR.Values.Load.LOCAL_ARGUMENT(symbol)
1594
si
1595
1596
// What the comparison tests before it reads a member. A struct
1597
// has no other runtime type to be handed, so it needs nothing.
1598
// A variant's parameter is its union, and testing `isa` against
1599
// itself turns away every other variant. A class needs the
1600
// runtime types to agree: a subclass value passes an `isa` for
1601
// its base, and a base and a subclass must not compare equal.
1602
_memberwise_guard(
1603
owner: Semantic.Symbols.Classy,
1604
self_value: Value,
1605
other_value: Value,
1606
location: Source.LOCATION
1607
) -> Value? is
1608
let bool_type = _innate_symbol_lookup.get_bool_type()
1609
1610
if owner.is_value_type then
1611
return null
1612
fi
1613
1614
if owner.is_variant then
1615
return IR.Values.ISA(bool_type, _own_instance_type(owner, location)!, other_value)
1616
fi
1617
1618
let self_type = _load_runtime_type(self_value, location)
1619
let other_type = _load_runtime_type(other_value, location)
1620
1621
if !self_type? \/ !other_type? then
1622
return null
1623
fi
1624
1625
return REFERENCE_EQUALITY(self_type, other_type, bool_type)
1626
si
1627
1628
// `value.get_type()`, the runtime type of a value.
1629
_load_runtime_type(value: Value, location: Source.LOCATION) -> Value? is
1630
let object_type = _innate_symbol_lookup.get_object_type()
1631
let group = object_type.find_member("get_type")
1632
1633
if !group? \/ !isa Semantic.Symbols.FUNCTION_GROUP(group) then
1634
return null
1635
fi
1636
1637
let functions = (cast Semantic.Symbols.FUNCTION_GROUP(group)).functions
1638
1639
for function in functions do
1640
if function.arguments.count == 0 then
1641
return IR.Values.Call.INSTANCE(
1642
value,
1643
function,
1644
Collections.LIST[Value](),
1645
function.return_type
1646
)
1647
fi
1648
od
1649
1650
return null
1651
si
1652
1653
// The parameter read as the owner. Where the owner is a class
1654
// below the one the chain overrides at, or a variant of a
1655
// union, the parameter is that wider type and the members
1656
// declared here are reached through a cast the guard has
1657
// already made safe.
1658
_narrow_to_owner(
1659
other_value: Value,
1660
owner: Semantic.Symbols.Classy,
1661
location: Source.LOCATION
1662
) -> Value is
1663
let owner_type = _own_instance_type(owner, location)
1664
1665
if !owner_type? then
1666
return other_value
1667
fi
1668
1669
let value_type = other_value.type
1670
1671
if value_type? /\ value_type.matches(owner_type) then
1672
return other_value
1673
fi
1674
1675
// The guard has already established the runtime type, so
1676
// the cast cannot fail where it is reached.
1677
return IR.Values.CAST(owner_type, other_value, false)
1678
si
1679
1680
// One member, read off a value of the owner. The name is the
1681
// one a value is read by rather than the one it was declared
1682
// under, so an auto-property arrives here as its backing field.
1683
_load_member(
1684
receiver: Value,
1685
owner: Semantic.Symbols.Classy,
1686
name: string,
1687
location: Source.LOCATION
1688
) -> Value? is
1689
let symbol = owner.find_member(name)
1690
1691
if !symbol? then
1692
return null
1693
fi
1694
1695
if symbol.is_field then
1696
return IR.Values.Load.INSTANCE_FIELD(receiver, symbol)
1697
fi
1698
1699
// A variant reads the fields it takes from its union's
1700
// header through the union's own properties: the storage
1701
// is the union's, and nothing on the variant names it.
1702
if let property: Semantic.Symbols.Property = symbol then
1703
return property.load(location, receiver, IoC.CONTAINER.instance.symbol_loader)
1704
fi
1705
1706
return null
1707
si
1708
1709
// The base's own operator, where calling it is what compares
1710
// the members this class does not declare. Null when there is
1711
// nothing to delegate: a struct or a variant has no base to
1712
// ask, and a base whose chain holds no members compares by
1713
// reference, which would answer false for two distinct values
1714
// this class holds equal.
1715
_inherited_memberwise_equals(
1716
owner: Semantic.Symbols.Classy
1717
) -> Semantic.Symbols.Function? is
1718
if owner.is_value_type \/ owner.is_variant then
1719
return null
1720
fi
1721
1722
let base = _base_classy(owner)
1723
1724
if !base? \/ !_holds_members(base, 0) then
1725
return null
1726
fi
1727
1728
let group = base.find_member("=~")
1729
1730
if !group? \/ !isa Semantic.Symbols.FUNCTION_GROUP(group) then
1731
return null
1732
fi
1733
1734
for function in (cast Semantic.Symbols.FUNCTION_GROUP(group)).functions do
1735
if function.arguments.count == 1 /\ !function.is_abstract then
1736
return function
1737
fi
1738
od
1739
1740
return null
1741
si
1742
1743
_base_classy(owner: Semantic.Symbols.Classy) -> Semantic.Symbols.Classy? is
1744
for ancestor in owner.ancestors do
1745
if !ancestor.is_class then
1746
continue
1747
fi
1748
1749
let symbol = ancestor.symbol
1750
1751
if isa Semantic.Symbols.Classy(symbol) then
1752
return cast Semantic.Symbols.Classy(symbol)
1753
fi
1754
od
1755
1756
return null
1757
si
1758
1759
// Whether a type, or anything it extends, holds state a
1760
// comparison could read.
1761
_holds_members(owner: Semantic.Symbols.Classy, depth: int) -> bool is
1762
if depth > 64 then
1763
return false
1764
fi
1765
1766
for member in owner.symbols do
1767
if member.is_field /\ member.is_instance then
1768
return true
1769
fi
1770
od
1771
1772
let base = _base_classy(owner)
1773
1774
return base? /\ _holds_members(base, depth + 1)
1775
si
1776
1777
// Every function a member's comparison can reach, taken from
1778
// the same chooser that decides the comparison. A synthesized
1779
// body stores nothing itself, so these are exactly what it can
1780
// do, and the effects solve is handed them rather than left to
1781
// read them back out of emitted values.
1782
//
1783
// Asking the chooser a second time costs a resolution and
1784
// keeps the answer a total match over a closed union, which
1785
// reading the built value back would not be: a value kind
1786
// nobody thought of would contribute nothing and the body
1787
// would look purer than it is.
1788
_note_memberwise_callees(
1789
into: Collections.LIST[Semantic.Symbols.Function],
1790
left_type: Type?,
1791
right_type: Type?,
1792
location: Source.LOCATION
1793
) is
1794
if !left_type? \/ !right_type? then
1795
return
1796
fi
1797
1798
let left_inner = _non_optional(left_type) ?? left_type
1799
let right_inner = _non_optional(right_type) ?? right_type
1800
1801
let lowering = _choose_equality(left_inner, right_inner, location)
1802
1803
if !lowering? then
1804
return
1805
fi
1806
1807
_note_lowering_callees(into, lowering)
1808
si
1809
1810
_note_lowering_callees(
1811
into: Collections.LIST[Semantic.Symbols.Function],
1812
lowering: EqualityLowering
1813
) is
1814
case lowering
1815
when operator: EqualityLowering.OPERATOR then
1816
into.add(operator.function)
1817
when order: EqualityLowering.ORDER then
1818
into.add(order.function)
1819
when sequence: EqualityLowering.SEQUENCE then
1820
_note_lowering_callees(into, sequence.element)
1821
when _: EqualityLowering.COMPARER then
1822
// The runtime's comparer reaches whatever the type
1823
// argument implements, which is not knowable here and
1824
// is not a function this body names.
1825
else
1826
esac
1827
si
1828
1829
// The whole body of a synthesized `get_hash_code`, hashing the
1830
// members its `=~` compares and folding them together. What
1831
// each member contributes is decided by the same rule the
1832
// comparison is: a member compared element by element hashes
1833
// by its count, and one compared more finely than its own hash
1834
// answers for contributes nothing.
1835
visit_memberwise_hash(memberwise: Trees.Expressions.MEMBERWISE_HASH) is
1836
memberwise.compile_expressions_state.value = null
1837
1838
let int_type = _innate_symbol_lookup.get_int_type()
1839
let location = memberwise.location
1840
1841
let owner = _visitor.current_instance_context
1842
1843
if !owner? \/ !isa Semantic.Symbols.Classy(owner) then
1844
memberwise.compile_expressions_state.value =
1845
DUMMY(Semantic.Types.ERROR(), location)
1846
1847
return
1848
fi
1849
1850
let owner_classy = cast Semantic.Symbols.Classy(owner)
1851
1852
let self_value = _load_self(owner_classy, location)
1853
let get_hash_code = _object_get_hash_code()
1854
1855
if !self_value? \/ !get_hash_code? then
1856
memberwise.compile_expressions_state.value =
1857
DUMMY(Semantic.Types.ERROR(), location)
1858
1859
return
1860
fi
1861
1862
let operands = Collections.LIST[Value]()
1863
1864
for i in 0..memberwise.members.count do
1865
let member = memberwise.members[i]
1866
let member_location = memberwise.member_locations[i]
1867
1868
let load = _load_member(self_value, owner_classy, member, member_location)
1869
1870
if !load? then
1871
continue
1872
fi
1873
1874
let operand = _compile_hash_operand(load, member_location)
1875
1876
// Boxed unconditionally where the member is a value
1877
// type: the hash is read through `object`, so an int
1878
// field reaches it as a box rather than as a bare
1879
// value the call would treat as a reference.
1880
operands.add(
1881
if operand.is_value_type then
1882
IR.Values.BOX(operand)
1883
else
1884
operand
1885
fi
1886
)
1887
1888
_note_memberwise_callees(
1889
memberwise.called_functions,
1890
load.type,
1891
load.type,
1892
member_location
1893
)
1894
od
1895
1896
let inherited = _inherited_memberwise_hash(owner_classy)
1897
1898
let seed =
1899
if let f = inherited then
1900
memberwise.called_functions.add(f)
1901
1902
IR.Values.Call.INSTANCE(
1903
IR.Values.Load.SUPER(owner_classy),
1904
f,
1905
Collections.LIST[Value](),
1906
int_type
1907
)
1908
else
1909
null
1910
fi
1911
1912
memberwise.compile_expressions_state.value =
1913
IR.Values.HASH_COMBINE(operands, get_hash_code, seed, int_type)
1914
si
1915
1916
_object_get_hash_code() -> Semantic.Symbols.Function? is
1917
let group = _innate_symbol_lookup.get_object_type().find_member("get_hash_code")
1918
1919
if !group? \/ !isa Semantic.Symbols.FUNCTION_GROUP(group) then
1920
return null
1921
fi
1922
1923
for function in (cast Semantic.Symbols.FUNCTION_GROUP(group)).functions do
1924
if function.arguments.count == 0 then
1925
return function
1926
fi
1927
od
1928
1929
return null
1930
si
1931
1932
// The base's own hash, where the base holds members this type
1933
// does not declare - the partner of the comparison delegating
1934
// to the base's operator.
1935
_inherited_memberwise_hash(
1936
owner: Semantic.Symbols.Classy
1937
) -> Semantic.Symbols.Function? is
1938
if owner.is_value_type \/ owner.is_variant then
1939
return null
1940
fi
1941
1942
let base = _base_classy(owner)
1943
1944
if !base? \/ !_holds_members(base, 0) then
1945
return null
1946
fi
1947
1948
let group = base.find_member("get_hash_code")
1949
1950
if !group? \/ !isa Semantic.Symbols.FUNCTION_GROUP(group) then
1951
return null
1952
fi
1953
1954
for function in (cast Semantic.Symbols.FUNCTION_GROUP(group)).functions do
1955
if function.arguments.count == 0 /\ !function.is_abstract then
1956
return function
1957
fi
1958
od
1959
1960
return null
1961
si
1962
1963
visit_field_equals(field_equals: Trees.Expressions.FIELD_EQUALS) is
1964
field_equals.compile_expressions_state.value = null
1965
1966
let left_value = field_equals.left.value
1967
let right_value = field_equals.right.value
1968
1969
if !left_value? \/ !right_value? then
1970
field_equals.compile_expressions_state.value =
1971
DUMMY(Semantic.Types.ERROR(), field_equals.location)
1972
1973
return
1974
fi
1975
1976
field_equals.compile_expressions_state.value =
1977
_compile_field_equals(
1978
left_value,
1979
right_value,
1980
field_equals.location,
1981
field_equals.reports_fallback)
1982
si
1983
1984
1985
// The value one member of a synthesized `get_hash_code` is
1986
// hashed by. .NET requires two values that compare equal to
1987
// hash equal, and the member's own `get_hash_code` only keeps
1988
// that promise when it answers for the same distinctions the
1989
// member's comparison does.
1990
//
1991
// Three answers. A member compared element-wise is hashed by
1992
// its count: two equal sequences agree on that, where the
1993
// reference each would hash by does not. A member whose
1994
// comparison is finer than its own hash - a type ordered by
1995
// `<>` alone, or one declaring `=~` without the
1996
// `get_hash_code` that pairs with it - contributes a constant,
1997
// since no hash of it would be sound; a hash may ignore what
1998
// equality reads, but never the reverse. Everything else is
1999
// hashed as it stands.
2000
visit_hash_operand(hash_operand: Trees.Expressions.HASH_OPERAND) is
2001
hash_operand.compile_expressions_state.value = null
2002
2003
let operand = hash_operand.left.value
2004
2005
if !operand? then
2006
hash_operand.compile_expressions_state.value =
2007
DUMMY(Semantic.Types.ERROR(), hash_operand.location)
2008
2009
return
2010
fi
2011
2012
hash_operand.compile_expressions_state.value =
2013
_compile_hash_operand(operand, hash_operand.location)
2014
si
2015
2016
_compile_hash_operand(operand: Value, location: Source.LOCATION) -> Value is
2017
let type = operand.type
2018
2019
if !type? then
2020
return operand
2021
fi
2022
2023
let inner = _non_optional(type) ?? type
2024
let lowering = _choose_equality(inner, inner, location)
2025
2026
if !lowering? then
2027
return operand
2028
fi
2029
2030
case lowering
2031
when _: EqualityLowering.SEQUENCE then
2032
// An absent sequence has no count to read, and a
2033
// presence test here would answer a question the
2034
// surrounding read already asks, so an optional one
2035
// contributes nothing instead.
2036
if type.is_optional then
2037
return _zero(location)
2038
fi
2039
2040
let count = _load_property(operand, "count", location)
2041
2042
return count ?? _zero(location)
2043
when _: EqualityLowering.OPERATOR then
2044
return _hashable_or_zero(operand, inner, location)
2045
when _: EqualityLowering.ORDER then
2046
return _hashable_or_zero(operand, inner, location)
2047
else
2048
return operand
2049
esac
2050
si
2051
2052
_hashable_or_zero(operand: Value, type: Type, location: Source.LOCATION) -> Value =>
2053
if _declares_hash_code(type) then
2054
operand
2055
else
2056
_zero(location)
2057
fi
2058
2059
// Whether the type answers `get_hash_code` for itself rather
2060
// than inheriting `object`'s, which hashes by identity and so
2061
// says nothing about the values an operator compares.
2062
_declares_hash_code(type: Type) -> bool is
2063
let member = type.find_member("get_hash_code")
2064
2065
if !member? \/ !isa Semantic.Symbols.FUNCTION_GROUP(member) then
2066
return false
2067
fi
2068
2069
let object_symbol = _innate_symbol_lookup.get_object_type().symbol.unspecialized_symbol
2070
2071
for function in cast Semantic.Symbols.FUNCTION_GROUP(member).functions do
2072
let owner = function.owner
2073
2074
if owner? /\ owner.unspecialized_symbol != object_symbol then
2075
return true
2076
fi
2077
od
2078
2079
return false
2080
si
2081
2082
_zero(location: Source.LOCATION) -> Value =>
2083
IR.Values.Literal.NUMBER(0, _innate_symbol_lookup.get_int_type())
2084
2085
// The comparison a synthesized `=~` runs for one field: the
2086
// lowering the field's static type gets anywhere else, with the
2087
// null checks written around it where the field is optional.
2088
//
2089
// Where nothing compares the type, the runtime's comparer is the
2090
// only answer left, and a type declared in ghūl source is told
2091
// so at the field: it could declare `=~` and does not. Imported
2092
// types cannot, so for those the fallback stays silent.
2093
_compile_field_equals(
2094
left_value: Value,
2095
right_value: Value,
2096
location: Source.LOCATION,
2097
reports_fallback: bool
2098
) -> Value is
2099
let left_type = left_value.type
2100
let right_type = right_value.type
2101
2102
if !left_type? \/ !right_type? then
2103
return DUMMY(Semantic.Types.ERROR(), location)
2104
fi
2105
2106
if left_type.is_optional \/ right_type.is_optional then
2107
// The global innates declare their own optional
2108
// parameters, so an optional string field is answered
2109
// by the operator itself, exactly as a written
2110
// `a =~ b` over the same operands would be.
2111
let global_value = _try_global_equals_operator(left_value, right_value, location)
2112
2113
if global_value? then
2114
return global_value
2115
fi
2116
2117
let null_safe = _build_null_safe_equality(left_value, right_value, location)
2118
2119
if null_safe? then
2120
return null_safe
2121
fi
2122
else
2123
let lowering = _choose_equality(left_type, right_type, location)
2124
2125
if lowering? then
2126
if reports_fallback /\ isa EqualityLowering.IDENTITY(lowering) then
2127
_warn_equality_fallback(left_type, location, "by reference")
2128
elif reports_fallback /\ isa EqualityLowering.COMPARER(lowering) then
2129
_warn_equality_fallback(left_type, location, "with the default equality comparer")
2130
fi
2131
2132
return _build_equality(lowering, left_value, right_value, location)
2133
fi
2134
fi
2135
2136
let comparer_type = _innate_symbol_lookup.get_equality_comparer_type(left_type)
2137
2138
if !comparer_type? then
2139
_logger.error(location, "cannot compare values of type {left_type}")
2140
2141
return DUMMY(Semantic.Types.ERROR(), location)
2142
fi
2143
2144
if reports_fallback then
2145
_warn_equality_fallback(left_type, location, "with the default equality comparer")
2146
fi
2147
2148
return EQUALITY_COMPARISON(
2149
left_value,
2150
right_value,
2151
comparer_type,
2152
_innate_symbol_lookup.get_bool_type())
2153
si
2154
2155
// Reported only where the author could act: a type declared in
2156
// ghūl source could declare `=~` and does not. Imported types,
2157
// tuples, arrays, collection instantiations and function types
2158
// cannot declare one, so for those the fallback is the only
2159
// semantics available and a report would have no remedy to
2160
// point at.
2161
_warn_equality_fallback(type: Type, location: Source.LOCATION, how: string) is
2162
let inner = _non_optional(type) ?? type
2163
2164
if
2165
isa Semantic.Symbols.Classy(inner.symbol) /\
2166
!inner.symbol.is_reflected
2167
then
2168
_logger.warn(
2169
location,
2170
"synthesized-equality-fallback",
2171
"{type} defines no =~, so this field compares {how}")
2172
fi
2173
si
2174
2175
2176
// A `=~` operator in scope as a global — the scalar and string
2177
// innates. Returns null when nothing in scope answers for these
2178
// operand types.
2179
// The global `=~` that would be called for two operands of the
2180
// given types, if any. Resolution only - nothing is recorded
2181
// and no call is built, so a caller can ask the question and
2182
// then decline.
2183
_find_global_equals_operator(
2184
left_type: Type,
2185
right_type: Type,
2186
location: Source.LOCATION
2187
) -> Semantic.Symbols.Function? is
2188
let global_symbol = _visitor.find(Trees.Identifiers.Identifier(location, "=~"))
2189
2190
if !global_symbol? \/ !isa Semantic.Symbols.FUNCTION_GROUP(global_symbol) then
2191
return null
2192
fi
2193
2194
let overload_result =
2195
_resolve_silently(
2196
location,
2197
cast Semantic.Symbols.FUNCTION_GROUP(global_symbol),
2198
Collections.LIST[Type]([left_type, right_type]),
2199
false)
2200
2201
if !overload_result? then
2202
return null
2203
fi
2204
2205
return overload_result.function
2206
si
2207
2208
_try_global_equals_operator(
2209
left_value: Value,
2210
right_value: Value,
2211
location: Source.LOCATION
2212
) -> Value? is
2213
let function =
2214
_find_global_equals_operator(left_value.type!, right_value.type!, location)
2215
2216
if !function? then
2217
return null
2218
fi
2219
2220
_symbol_use_locations.add_symbol_use(location, function)
2221
2222
let value =
2223
function.call(
2224
location,
2225
null,
2226
Collections.LIST[Value]([left_value, right_value]),
2227
null,
2228
_function_caller)
2229
2230
if isa Call.INNATE(value) then
2231
let innate_value = value
2232
2233
innate_value.actual_operation = "=~"
2234
2235
return innate_value.lower()
2236
fi
2237
2238
return value
2239
si
2240
2241
// `a =~ b` where an operand is optional. Resolve against the
2242
// operands' non-optional types and write the null checks
2243
// around the call.
2244
//
2245
// An absent left operand is always answered here: there is no
2246
// receiver to dispatch on. An absent right operand is answered
2247
// here only when the operator declares its parameter
2248
// non-optional and so could never have been handed one.
2249
//
2250
// Answers null for anything else, leaving the operands to
2251
// resolve however they did before.
2252
// `a =~ b` where an operand is optional. Delegates the null-safe
2253
// building to _build_null_safe_equality (shared with `case`'s
2254
// value-equality tests) and applies the `!~` negation the AST
2255
// spelling carries. Only `=~` is null-safe here; the binary
2256
// visitor's main body handles the non-optional path with its
2257
// full diagnostics and retry, so this returns null unless an
2258
// operand is optional.
2259
_try_null_safe_equality(
2260
binary: Trees.Expressions.BINARY,
2261
left_value: Value,
2262
right_value: Value
2263
) -> Value? is
2264
if !(binary.operation.name =~ "=~") then
2265
return null
2266
fi
2267
2268
let left_type = left_value.type
2269
let right_type = right_value.type
2270
2271
if
2272
(!left_type? \/ !left_type.is_optional) /\
2273
(!right_type? \/ !right_type.is_optional)
2274
then
2275
return null
2276
fi
2277
2278
let result = _build_null_safe_equality(left_value, right_value, binary.location)
2279
2280
if !result? then
2281
return null
2282
fi
2283
2284
if binary.actual_operation =~ "!~" then
2285
let negated = IR.Values.BLOCK(_innate_symbol_lookup.get_bool_type())
2286
negated.add(NOT(result))
2287
negated.close()
2288
return negated
2289
fi
2290
2291
return result
2292
si
2293
2294
// Build a null-safe `=~` test for two operands, at least one of
2295
// which is optional. Resolves the left type's `=~` against the
2296
// operands' non-optional inner types and writes the null checks
2297
// around the call: an absent left operand is always answered
2298
// here (no receiver to dispatch on), an absent right operand is
2299
// answered here unless the operator declares its parameter
2300
// optional and so could be handed one. Returns null when the
2301
// gate rejects the operands or operator, or no `=~` resolves.
2302
//
2303
// Extracted from _try_null_safe_equality so `case` value-equality
2304
// (see _try_equality_test) reuses the same lowering rather than
2305
// duplicating it.
2306
_build_null_safe_equality(
2307
left_value: Value,
2308
right_value: Value,
2309
location: Source.LOCATION
2310
) -> Value? is
2311
let left_type = left_value.type
2312
let right_type = right_value.type
2313
2314
if !NULL_SAFE_EQUALITY_GATE.accepts_operands(left_type, right_type) then
2315
return null
2316
fi
2317
2318
let left_inner = _non_optional(left_type!)
2319
let right_inner = _non_optional(right_type!)
2320
2321
if !left_inner? \/ !right_inner? then
2322
return null
2323
fi
2324
2325
let comparison =
2326
_null_safe_comparison(left_type, right_type, left_inner, right_inner, location)
2327
2328
if !comparison? then
2329
return null
2330
fi
2331
2332
let guards_argument =
2333
if let operator: EqualityLowering.OPERATOR = comparison then
2334
NULL_SAFE_EQUALITY_GATE.guards_argument(operator.function)
2335
elif let order: EqualityLowering.ORDER = comparison then
2336
NULL_SAFE_EQUALITY_GATE.guards_argument(order.function)
2337
else
2338
// The comparer, a sequence and identity all take
2339
// two present values, so an absent one has to be
2340
// answered out here.
2341
true
2342
fi
2343
2344
// With an operator that answers for an absent argument
2345
// itself, a present left operand needs nothing doing: the
2346
// call already resolves and already reaches the body.
2347
//
2348
// Except for a value-shape argument, which is answered here
2349
// whatever the operator declares. Declining on the strength
2350
// of the left operand alone would make that turn on
2351
// something unrelated to it, so the same comparison would
2352
// answer differently for an optional left and a present
2353
// one.
2354
if
2355
!left_type.is_optional /\
2356
!guards_argument /\
2357
!(right_type.is_optional /\ right_type.is_value_type)
2358
then
2359
return null
2360
fi
2361
2362
let bool_type = _innate_symbol_lookup.get_bool_type()
2363
let block = IR.Values.BLOCK(bool_type)
2364
2365
// Each operand is read up to three times - by its own
2366
// presence test, and by the call - so spill anything that
2367
// is not already a repeatable load. A value-shape optional
2368
// additionally needs its address, which a spilled temp has
2369
// and an expression does not.
2370
let load_left = left_value.get_temp_copier(block, "equality_left")
2371
let load_right = right_value.get_temp_copier(block, "equality_right")
2372
2373
let operand =
2374
OPTIONAL_OPERAND(IoC.CONTAINER.instance.symbol_loader, bool_type)
2375
2376
if
2377
!operand.can_split(location, load_left()) \/
2378
!operand.can_split(location, load_right())
2379
then
2380
return null
2381
fi
2382
2383
// A value-shape argument is guarded whatever the operator
2384
// declares: its parameter is a bare `T` at IL, so there is
2385
// no absent value to hand a body in the first place.
2386
let guards = guards_argument \/ OPTIONAL_OPERAND.must_be_guarded(load_right())
2387
2388
let left_payload = operand.payload(location, load_left())
2389
let right_payload = operand.payload(location, load_right())
2390
2391
if !left_payload? \/ !right_payload? then
2392
return null
2393
fi
2394
2395
let call = _build_equality(comparison, left_payload, right_payload, location)
2396
2397
let result =
2398
IR.Values.NULL_SAFE_EQUALITY(
2399
operand.presence(location, load_left()),
2400
operand.presence(location, load_right()),
2401
call,
2402
guards,
2403
bool_type
2404
)
2405
2406
block.add(result)
2407
block.close()
2408
2409
return block
2410
si
2411
2412
// Which comparison a null-safe `=~` wraps: the one two present
2413
// values of the inner types get.
2414
//
2415
// An operator that accepts the operands as they stand is left
2416
// alone: declaring an optional parameter is how one says it
2417
// answers for an absent operand itself, and wrapping it would
2418
// take that decision away. Null for that, and for operands
2419
// nothing compares, leaving the comparison to resolve as it
2420
// did.
2421
_null_safe_comparison(
2422
left_type: Type,
2423
right_type: Type,
2424
left_inner: Type,
2425
right_inner: Type,
2426
location: Source.LOCATION
2427
) -> EqualityLowering? is
2428
if _find_global_equals_operator(left_type, right_type, location)? then
2429
return null
2430
fi
2431
2432
let lowering = _choose_equality(left_inner, right_inner, location)
2433
2434
// A reference of a class declaring neither operator is not
2435
// compared by identity from a written `=~`, null-safe or
2436
// not; a synthesized member reaches identity through the
2437
// field path instead.
2438
if lowering? /\ isa EqualityLowering.IDENTITY(lowering) then
2439
return null
2440
fi
2441
2442
return lowering
2443
si
2444
2445
2446
_try_equality_test(
2447
left_value: Value,
2448
right_value: Value,
2449
op_name: string,
2450
location: Source.LOCATION
2451
) -> Value? is
2452
let left_type = left_value.type
2453
let right_type = right_value.type
2454
2455
if !left_type? \/ !right_type? then
2456
return null
2457
fi
2458
2459
let optional = left_type.is_optional \/ right_type.is_optional
2460
2461
if op_name =~ "=~" then
2462
if optional then
2463
let null_safe = _build_null_safe_equality(left_value, right_value, location)
2464
2465
if null_safe? then
2466
return null_safe
2467
fi
2468
2469
// An instance `=~` the gate declined (a static
2470
// operator like string's op_Equality, or one hidden
2471
// from operator resolution) reaches the free-function
2472
// `Ghul.=~`, which is null-safe over optionals - the
2473
// same call the `=~` expression makes for these types.
2474
return _try_global_equals_operator(left_value, right_value, location)
2475
fi
2476
2477
let lowering = _choose_equality(left_type, right_type, location)
2478
2479
if !lowering? \/ isa EqualityLowering.IDENTITY(lowering) then
2480
return null
2481
fi
2482
2483
return _build_equality(lowering, left_value, right_value, location)
2484
fi
2485
2486
if op_name =~ "<>" then
2487
if optional then
2488
return null
2489
fi
2490
2491
let order = _find_order_operator(left_type, right_type, location)
2492
2493
if !order? then
2494
return null
2495
fi
2496
2497
return _build_equality(EqualityLowering.ORDER(order), left_value, right_value, location)
2498
fi
2499
2500
return null
2501
si
2502
2503
2504
_non_optional(type: Type) -> Type? =>
2505
if type.is_optional then
2506
type.optional_inner_type
2507
else
2508
type
2509
fi
2510
2511
// Whether an operator takes the left operand as its receiver: an
2512
// instance member, or an innate declared with one, such as an
2513
// enum's `=~`.
2514
_takes_receiver(function: Semantic.Symbols.Function) -> bool =>
2515
function.is_instance \/ isa Semantic.Symbols.INNATE_METHOD(function)
2516
2517
// The instance operators in a type's group under the operator's
2518
// name, which take the left operand as their receiver. A static
2519
// operator in the same group takes both operands as arguments
2520
// and is a candidate on the other route, so it is left out here
2521
// - a unary static would otherwise match the right operand alone.
2522
_instance_operator_candidates(
2523
location: Source.LOCATION,
2524
name: string,
2525
member: Semantic.Symbols.Symbol?
2526
) -> Semantic.Symbols.FUNCTION_GROUP? is
2527
let group = cast Semantic.Symbols.FUNCTION_GROUP?(member)
2528
2529
if !group? then
2530
return null
2531
fi
2532
2533
if group.functions |> Ghul.Pipes.all(f => _takes_receiver(f)) then
2534
return group
2535
fi
2536
2537
let result = Semantic.Symbols.FUNCTION_GROUP(location, _symbol_table.current_scope, name)
2538
2539
for function in group.functions do
2540
if _takes_receiver(function) then
2541
result.add(function)
2542
fi
2543
od
2544
2545
return if result.is_empty then null else result fi
2546
si
2547
2548
// The candidates for an operator taking its operands as
2549
// arguments: whatever is in scope under the operator's name -
2550
// global operators, and members brought in by `use` - plus the
2551
// static operators each operand's own type declares, so a type
2552
// that declares `+` for itself supplies it wherever one of its
2553
// values is an operand. Instance operators are the left
2554
// operand's to resolve as a receiver and are left out here. Null
2555
// when nothing answers to the name at all.
2556
//
2557
// The scope group is handed back as it stands when the operand
2558
// types add nothing, so a group that is copied is always one
2559
// that needed combining.
2560
_static_operator_candidates(
2561
location: Source.LOCATION,
2562
name: string,
2563
operand_types: Collections.List[Type]
2564
) -> Semantic.Symbols.FUNCTION_GROUP? is
2565
let scope_group = cast Semantic.Symbols.FUNCTION_GROUP?(_visitor.find(name))
2566
let result: Semantic.Symbols.FUNCTION_GROUP? mut = scope_group
2567
let seen = Collections.LIST[Semantic.Symbols.FUNCTION_GROUP]()
2568
2569
for operand_type in operand_types do
2570
let inner = _non_optional(operand_type) ?? operand_type
2571
2572
// A type parameter reaches its bounds' operators through
2573
// every bound that inherits them, and a static one is only
2574
// resolvable once brought into scope by name.
2575
if inner.is_type_variable then
2576
continue
2577
fi
2578
2579
let group = cast Semantic.Symbols.FUNCTION_GROUP?(inner.find_member(name))
2580
2581
if !group? \/ seen.contains(group) then
2582
continue
2583
fi
2584
2585
seen.add(group)
2586
2587
for function in group.functions do
2588
if _takes_receiver(function) \/ function.is_hidden_from_operator_resolution then
2589
continue
2590
fi
2591
2592
if !result? \/ result == scope_group then
2593
let combined = Semantic.Symbols.FUNCTION_GROUP(location, _symbol_table.current_scope, name)
2594
2595
if scope_group? then
2596
combined.add(scope_group)
2597
fi
2598
2599
result = combined
2600
fi
2601
2602
if !(result.functions |> Ghul.Pipes.any(f => f == function)) then
2603
result.add(function)
2604
fi
2605
od
2606
od
2607
2608
return result
2609
si
2610
2611
// Resolve an operator overload under speculation, so a failed
2612
// resolution (no matching overload) leaves no diagnostics: the
2613
// `case` path treats "no operator" as "fall back to a raw
2614
// compare", not an error. Mirrors the speculation
2615
// _resolve_with_retry gives the `=~` expression.
2616
_resolve_silently(
2617
location: Source.LOCATION,
2618
group: Semantic.Symbols.FUNCTION_GROUP,
2619
argument_types: Collections.LIST[Type],
2620
want_instance: bool
2621
) -> Semantic.OVERLOAD_RESOLVE_RESULT? is
2622
RETRY_SITE_STATS.note("operators.resolve_silently")
2623
let use snapshot = _logger.speculate_then_backtrack()
2624
2625
let result =
2626
_overload_resolver.resolve(location, group, argument_types, false, want_instance, false)
2627
2628
snapshot.backtrack()
2629
2630
return result
2631
si
2632
2633
// `a ?? b` — short-circuit null-coalesce. Result type is the
2634
// LUB of the left's underlying type and the right's type, with
2635
// `?` re-applied iff the right is itself optional. Right is
2636
// evaluated only when left is null.
2637
_visit_null_coalesce(binary: Trees.Expressions.BINARY) is
2638
let left_value = binary.left.value!
2639
let right_value = binary.right.value!
2640
2641
2642
left_value.check_is_consumable(_logger, binary.left.location)
2643
right_value.check_is_consumable(_logger, binary.right.location)
2644
2645
let left_type = left_value.type
2646
let right_type = right_value.type
2647
2648
if !left_type? \/ left_type.is_error \/ !right_type? \/ right_type.is_error then
2649
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
2650
return
2651
fi
2652
2653
// A left operand whose type is still a placeholder may yet
2654
// settle optional, so it cannot stand in for the whole
2655
// expression the way a non-optional operand does: the
2656
// coalesce waits for a later walk, and nothing learns the
2657
// placeholder's type from the expression meanwhile.
2658
if left_type.is_inferred then
2659
// A local that has held `null` and falls back to a value
2660
// here holds that value's type when present.
2661
if
2662
let placeholder: Semantic.Types.INFERRED_VARIABLE_TYPE = left_type /\
2663
placeholder.origin.has_seen_null /\
2664
right_type.is_settled /\
2665
!right_type.is_null
2666
then
2667
let fallback = right_type.optional_inner_type ?? right_type
2668
2669
if Semantic.INFERENCE_TRACE.add_lower_bound("operators.null_coalesce_fallback", placeholder.origin, fallback) then
2670
_logger.mark_consumed_any()
2671
fi
2672
fi
2673
2674
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
2675
return
2676
fi
2677
2678
// Flow analysis routinely narrows a declared `T?` to `T`
2679
// after a non-null assignment. Treating that as an error
2680
// would reject defensive `present ?? fallback` patterns
2681
// — degrade to just emitting `left` instead, matching how
2682
// `?.` falls back to `.` when the receiver is non-optional.
2683
if !left_type.is_optional then
2684
binary.compile_expressions_state.value = left_value
2685
return
2686
fi
2687
2688
let left_inner = left_type.optional_inner_type
2689
2690
if !left_inner? then
2691
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
2692
return
2693
fi
2694
2695
let right_is_optional = right_type.is_optional
2696
2697
let right_inner =
2698
if right_is_optional then
2699
right_type.optional_inner_type
2700
else
2701
right_type
2702
fi
2703
2704
let lub = LEAST_UPPER_BOUND_MAP()
2705
2706
lub.add(left_inner)
2707
2708
if right_inner? then
2709
lub.add(right_inner)
2710
fi
2711
2712
let result_inner = lub.get_result()
2713
2714
if !result_inner? then
2715
_logger.error(binary.location, "no common type for ?? operands {left_type} and {right_type}")
2716
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
2717
return
2718
fi
2719
2720
// The result's optional kind derives from the LUB of the
2721
// inner types, not from either operand: reference inner →
2722
// flagged reference optional, value inner → NULLABLE, and
2723
// a type-variable inner keeps the left's MAYBE carrier —
2724
// the only lowering an unconstrained T? has. The operands
2725
// may each be a different kind of optional; each arm
2726
// coerces to the canonical result.
2727
let result_type =
2728
if !right_is_optional then
2729
result_inner
2730
elif result_inner.is_type_variable then
2731
left_type
2732
else
2733
COMPILE_ACCESS.build_optional_type(result_inner, _innate_symbol_lookup)
2734
fi
2735
2736
let absent_arm = _coerce_coalesce_arm(right_value, result_type)
2737
2738
if !left_type.is_value_type then
2739
binary.compile_expressions_state.value = NULL_COALESCE(left_value, absent_arm, result_type)
2740
return
2741
fi
2742
2743
// Value-shape left — NULLABLE[T] or MAYBE[T]: spill the
2744
// receiver to a local and drive the presence test and
2745
// payload extract through its address, mirroring the
2746
// `?.` lowering.
2747
let has_value_member = left_type.find_member("has_value")
2748
let value_member = left_type.find_member("value")
2749
2750
if !has_value_member? \/ !value_member? then
2751
_logger.poison(binary.location, "?? left operand {left_type} missing has_value or value member")
2752
binary.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), binary.location)
2753
return
2754
fi
2755
2756
let address_stand_in = IR.Values.STACK_TOP_ADDRESS(left_type)
2757
let symbol_loader = IoC.CONTAINER.instance.symbol_loader
2758
2759
let presence_test = has_value_member.load(binary.left.location, address_stand_in, symbol_loader)
2760
let value_extract = value_member.load(binary.left.location, address_stand_in, symbol_loader)
2761
2762
let present_arm = _coerce_coalesce_arm(IR.Values.STACK_TOP(left_inner), result_type)
2763
2764
binary.compile_expressions_state.value =
2765
IR.Values.NULL_COALESCE_VALUE(
2766
left_value,
2767
presence_test,
2768
value_extract,
2769
present_arm,
2770
absent_arm,
2771
result_type
2772
)
2773
si
2774
2775
// Bring one arm of `??` to the result type: T → T? wrapping
2776
// (Nullable / MAYBE construction), MAYBE → reference-optional
2777
// payload load, NULLABLE ↔ MAYBE pass through unchanged (the
2778
// layouts are identical), and a value-typed arm boxes when
2779
// the LUB widened the result to a reference type.
2780
_coerce_coalesce_arm(value: IR.Values.Value, result_type: Semantic.Types.Type) -> IR.Values.Value is
2781
let coerced = _value_boxer.wrap_if_needed(value, result_type)!
2782
2783
if !result_type.is_value_type /\ coerced.type? /\ coerced.type.is_value_type then
2784
return IR.Values.BOX(coerced)
2785
fi
2786
2787
return coerced
2788
si
2789
2790
// An index argument takes its type from the indexer resolution
2791
// settles on, so it walks under a speculation for the same
2792
// reason an operator operand does. The receiver is not a
2793
// candidate: an untyped one has no members for the indexer to
2794
// be found on, so there is nothing to resolve against.
2795
pre_index(index: Trees.Expressions.INDEX) -> bool is
2796
if index.index.awaits_context_type then
2797
_deferred_operands.add(DEFERRED_OPERANDS(index))
2798
2799
RETRY_SITE_STATS.note("operators.pre_index_deferred")
2800
_logger.speculate()
2801
_flow.speculate()
2802
fi
2803
2804
return false
2805
si
2806
2807
visit_index(index: Trees.Expressions.INDEX) is
2808
if !_deferred_for(index)? then
2809
_visit_index(index)
2810
2811
return
2812
fi
2813
2814
try
2815
_visit_index(index)
2816
finally
2817
_deferred_operands.remove_at(_deferred_operands.count - 1)
2818
2819
_flow.commit()
2820
_logger.commit()
2821
yrt
2822
si
2823
2824
_visit_index(index: Trees.Expressions.INDEX) is
2825
let existing_value = index.value
2826
let need_store = existing_value? /\ existing_value.is_need_store
2827
2828
if index.left.value? /\ index.index.value? then
2829
let left_value = index.left.value
2830
let type = left_value.type
2831
2832
if type == null \/ type.is_error then
2833
_logger.poison(index.left.location, "index left has no type")
2834
2835
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2836
return
2837
fi
2838
2839
// Indexable inference: an `INFERRED_VARIABLE_TYPE`
2840
// receiver records an INDEX_CONSTRAINT carrying the
2841
// index expression's type, so the body-retry loop
2842
// can filter candidates to indexable types. Skip
2843
// the "cannot index" hard error path on placeholder
2844
// — the existing speculate/roll-back would discard
2845
// it on retry but emitting it is noise; soft-error
2846
// out to DUMMY(ERROR) instead.
2847
let index_type = index.index?.value?.type
2848
2849
if
2850
isa Semantic.Types.INFERRED_VARIABLE_TYPE(type) /\
2851
index_type?
2852
then
2853
let placeholder = type
2854
2855
// An index whose own type is not settled yet says
2856
// only that the receiver is indexed by something
2857
// unknown, and a constraint is never withdrawn - so
2858
// recording it here judges every candidate the
2859
// receiver later settles on against an index type
2860
// that no longer describes the index. Wait for it
2861
// instead: what the index resolves to is the
2862
// deduction this constraint is meant to carry.
2863
if index_type.is_settled then
2864
let constraint = Semantic.INDEX_CONSTRAINT(index_type)
2865
2866
_logger.mark_consumed_any_if(Semantic.INFERENCE_TRACE.add_constraint("operators.index", placeholder.origin, constraint))
2867
else
2868
Semantic.OBLIGATIONS.defer("index", index_type, index.location)
2869
fi
2870
2871
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2872
return
2873
fi
2874
2875
if !isa Semantic.Types.NAMED(type) then
2876
_logger.error(index.left.location, "cannot index {type}")
2877
2878
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2879
return
2880
fi
2881
2882
let named_type = type
2883
2884
let function_name: string mut
2885
let arguments: Collections.LIST[Value] mut
2886
let argument_types: Collections.LIST[Type] mut
2887
2888
let index_value = index.index.value!
2889
2890
// An index with no type has nothing for the indexer's
2891
// overload resolution to match against.
2892
if !index_value.type? then
2893
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2894
return
2895
fi
2896
2897
if need_store then
2898
function_name = Semantic.Symbols.INDEXER_NAMES.assign
2899
2900
let need_store_value = cast Need.STORE?(index.value)!.value
2901
2902
// The value being stored has no type when its own
2903
// expression failed to resolve, which is reported
2904
// where it is written.
2905
if !need_store_value.type? then
2906
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2907
return
2908
fi
2909
2910
arguments = Collections.LIST[Value]([index_value, need_store_value])
2911
argument_types = Collections.LIST[Type]([index_value.type!, need_store_value.type!])
2912
else
2913
function_name = Semantic.Symbols.INDEXER_NAMES.read
2914
arguments = Collections.LIST[Value]([index_value])
2915
argument_types = Collections.LIST[Type]([index_value.type!])
2916
fi
2917
2918
let symbol = named_type.find_member(function_name)
2919
2920
if symbol == null then
2921
if need_store then
2922
if named_type.find_member(Semantic.Symbols.INDEXER_NAMES.read)? then
2923
_logger.error(index.location, "indexer is read-only in {type}")
2924
else
2925
_logger.error(index.location, "no indexer found in {type}")
2926
fi
2927
else
2928
_logger.error(index.location, "no indexer found in {type}")
2929
fi
2930
2931
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2932
return
2933
fi
2934
2935
if !isa Semantic.Symbols.FUNCTION_GROUP(symbol) then
2936
_logger.poison(index.index.location, "indexer is not a function group: {symbol}")
2937
fi
2938
2939
let overload_result: Semantic.OVERLOAD_RESOLVE_RESULT? mut
2940
2941
RETRY_SITE_STATS.note("operators.index_resolve")
2942
let use logger_snapshot = _logger.speculate_then_backtrack()
2943
2944
overload_result =
2945
_overload_resolver.resolve(
2946
index.location,
2947
cast Semantic.Symbols.FUNCTION_GROUP?(symbol)!,
2948
argument_types,
2949
false,
2950
true,
2951
false
2952
)
2953
2954
logger_snapshot.backtrack()
2955
2956
if overload_result == null then
2957
if !need_store /\ _try_range_slice(index, left_value, index_value, argument_types[0]) then
2958
return
2959
fi
2960
2961
// An argument that is already an error has been
2962
// reported where it was written.
2963
if argument_types[0].is_error \/ (need_store /\ argument_types[1].is_error) then
2964
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2965
return
2966
fi
2967
2968
if need_store then
2969
_logger.error(index.location, "indexer [{argument_types[0]}] = {argument_types[1]} not found in {type}")
2970
else
2971
_logger.error(index.location, "indexer [{argument_types[0]}] not found in {type}")
2972
fi
2973
2974
index.compile_expressions_state.value = DUMMY(Semantic.Types.ERROR(), index.location)
2975
return
2976
fi
2977
2978
let function = overload_result.function
2979
2980
if _resolve_deferred_index(index, function) then
2981
_visit_index(index)
2982
2983
return
2984
fi
2985
2986
// Record the indexer-accessor use over the `[...]`
2987
// bracket span — from the column after the LHS (the
2988
// `[`) to the end of the INDEX expression (the `]`).
2989
// find_hover_use prefers shorter matches, so any
2990
// inner identifier or variable use nested inside the
2991
// brackets still wins on hover; the strict-
2992
// containment filter in the semantic-tokens handler
2993
// drops this outer span when an inner use is
2994
// present, keeping the index expression coloured as
2995
// itself.
2996
_symbol_use_locations.add_symbol_use(
2997
LOCATION(
2998
index.left.location.file_name,
2999
index.left.location.end_line,
3000
index.left.location.end_column + 1,
3001
index.location.end_line,
3002
index.location.end_column
3003
), function)
3004
3005
if function.is_unsafe_constraints then
3006
_logger.warn(index.location, "unchecked-constraints", "call to {function} has unchecked constraints")
3007
fi
3008
3009
if need_store then
3010
index.compile_expressions_state.value =
3011
TYPE_WRAPPER(
3012
function.arguments[1],
3013
function.call(index.location, left_value, arguments, null, _function_caller))
3014
else
3015
index.compile_expressions_state.value = function.call(index.location, left_value, arguments, null, _function_caller)
3016
fi
3017
fi
3018
si
3019
si
3020
si