Skip to content
← Back

src/semantic/types/generic.ghul

1
namespace Semantic.Types is
2
use IO.Std
3
4
use Source.LOCATION
5
6
use Logging
7
8
use Ghul.Pipes
9
10
enum TypeVariance is
11
INVARIANT,
12
COVARIANT,
13
CONTRAVARIANT
14
si
15
16
class GENERIC: NAMED is
17
_is_wild: byte
18
19
arguments: Collections.List[Type] => symbol.arguments
20
21
is_error: bool => arguments |> any(a => a.is_error)
22
23
// should be
24
// is_wild: bool => arguments |> any(a => a.is_wild)
25
// but the following shaves about 10% off compiler build time
26
is_wild: bool is
27
if _is_wild == 0b then
28
let a = symbol.arguments
29
let c = a.count
30
31
let i mut = 0
32
33
while i < c do
34
if a[i].is_wild then
35
_is_wild = 1b
36
return true
37
fi
38
39
i = i + 1
40
od
41
42
_is_wild = 2b
43
return false
44
fi
45
46
return _is_wild == 1b
47
si
48
49
// Recurse into arguments — Function[placeholder, int] is
50
// a GENERIC whose self.is_inferred is false but whose
51
// arguments contain a placeholder. The iterative-inference
52
// re-narrowing path uses this to decide whether a let-bound
53
// symbol's type still needs updating after a body retry.
54
contains_inferred: bool => arguments |> any(a => a.contains_inferred)
55
56
contains_function_generic_argument: bool =>
57
arguments |> any(a => a.contains_function_generic_argument)
58
59
short_description: string is
60
let result = System.Text.StringBuilder()
61
62
result
63
.append(symbol.name)
64
.append('[')
65
66
let seen_any mut = false
67
68
for a in arguments do
69
if seen_any then
70
result.append(',')
71
fi
72
73
result.append(a.short_description)
74
75
seen_any = true
76
od
77
78
result.append(']')
79
80
return result.to_string()
81
si
82
83
is_function_with_any_implicit_argument_types: bool is
84
if !is_function then
85
return false
86
fi
87
88
// is_inferred (today only INFERRED_RETURN_TYPE) rather
89
// than is_sentinel: an ERROR-typed argument is a real failure,
90
// not a deferred-inference placeholder, and shouldn't
91
// trigger overload-resolver's "needs second pass to infer
92
// formal-derived arg types" path.
93
//
94
// `arguments` carries a trailing return-type slot only for a
95
// non-void function type; an `is_action`-shaped one (void
96
// return) stores parameter types alone, so excluding the
97
// last entry unconditionally would skip its final - and for
98
// arity-1, only - parameter as though it were a return slot
99
// that isn't there.
100
let param_count = arguments.count - (if is_action then 0 else 1 fi)
101
102
for i in 0..param_count do
103
if arguments[i].is_inferred then
104
return true
105
fi
106
od
107
108
// A function returning a function whose own parameter types are
109
// still implicit - `k => (a, b) => ...` once `k` is typed - has
110
// them too: the second pass pushes the formal's return spine
111
// down into the returned literal just as it does its parameters.
112
return !is_action /\ arguments.count > 0 /\ arguments[arguments.count - 1].is_function_with_any_implicit_argument_types
113
si
114
115
init(
116
symbol: Symbols.GENERIC
117
) is
118
super.init(symbol)
119
si
120
121
init(
122
location: LOCATION,
123
symbol: Symbols.Classy,
124
arguments: Collections.List[Type]
125
) is
126
super.init(Symbols.GENERIC(location, symbol, arguments))
127
si
128
129
create(
130
location: LOCATION,
131
symbol: Symbols.Classy,
132
arguments: Collections.List[Type]
133
) -> GENERIC =>
134
GENERIC(location, symbol, arguments)
135
136
is_same_symbol(other: Type) -> bool is
137
if !isa GENERIC(other) then
138
return false
139
fi
140
141
let other_generic = other
142
143
let generic_symbol = cast Symbols.GENERIC?(symbol)!
144
let generic_other_symbol = cast Symbols.GENERIC?(other_generic.symbol)!
145
146
return generic_symbol.symbol == generic_other_symbol.symbol
147
si
148
149
// The declared variance at this type-argument position: whether
150
// this position may vary at all, and in which direction. This is
151
// purely a property of the generic type's own shape — read from
152
// reflected .NET metadata for imported types (Symbols.Classy.
153
// argument_variances) or, for FUNCTION/ACTION/ARRAY, fixed by
154
// the kind of type they are. It says nothing about whether a
155
// *specific* instantiation may use that variance — see
156
// effective_argument_variance for that.
157
get_argument_type_variance(index: int) -> TypeVariance is
158
let generic_symbol = cast Symbols.GENERIC?(symbol)
159
if !generic_symbol? then
160
return TypeVariance.INVARIANT
161
fi
162
return generic_symbol.symbol.get_argument_variance(index)
163
si
164
165
// The variance actually usable when converting from `other` to
166
// self at this position. The CLR only allows a variant
167
// conversion when the actual type argument at this position is
168
// a reference type on both sides, so this downgrades a declared
169
// covariant/contravariant position to invariant whenever either
170
// side's actual argument is a value type.
171
effective_argument_variance(other: GENERIC, index: int) -> TypeVariance is
172
// One tuple converts to another element by element, repacked
173
// where the value crosses into the other type, so each
174
// position varies with its element, value type or not.
175
if (is_value_tuple \/ isa TUPLE(self)) /\ (other.is_value_tuple \/ isa TUPLE(other)) then
176
return TypeVariance.COVARIANT
177
fi
178
179
let declared = other.get_argument_type_variance(index)
180
181
if declared == TypeVariance.INVARIANT then
182
return TypeVariance.INVARIANT
183
fi
184
185
if index >= 0 /\ index < arguments.count /\ (arguments[index].is_value_type \/ other.arguments[index].is_value_type) then
186
return TypeVariance.INVARIANT
187
fi
188
189
return declared
190
si
191
192
get_element_type() -> Type => arguments[0]
193
194
specialize_generic(type_map: Collections.Map[Symbols.Symbol,Type]) -> Types.GENERIC is
195
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context
196
197
let we_are_generic = context? /\ context.arguments.count > 0
198
199
let seen_any_new mut = false
200
201
let generic_symbol = cast Symbols.GENERIC?(symbol)!
202
203
let new_arguments = Collections.LIST[Type](arguments.count)
204
205
for i in 0..arguments.count do
206
let parameter
207
= cast Semantic.Symbols.GenericArgument?(
208
generic_symbol.symbol.type_parameter_at(i))
209
210
let mapped_type: Type mut
211
212
if we_are_generic /\ parameter? /\ type_map.try_get_value(parameter, mapped_type ref) then
213
new_arguments.add(mapped_type)
214
215
seen_any_new = true
216
else
217
let oa = generic_symbol.arguments[i]
218
let na = oa.specialize(type_map)
219
220
new_arguments.add(na)
221
222
if oa != na then
223
seen_any_new = true
224
fi
225
fi
226
od
227
228
if seen_any_new then
229
let result = create(symbol.location, generic_symbol.symbol, new_arguments)
230
231
if !is_optional then
232
return result
233
fi
234
235
if let optional: GENERIC = result.as_optional_unchecked() then
236
return optional
237
fi
238
239
return result
240
else
241
return self
242
fi
243
si
244
245
specialize(type_map: Collections.Map[Symbols.Symbol,Type]) -> Type =>
246
specialize_generic(type_map)
247
248
matches(other: Type) -> bool is
249
if other.is_sentinel then
250
return true
251
fi
252
253
if !isa GENERIC(other) then
254
return false
255
fi
256
257
let generic_other = other
258
259
if symbol == generic_other.symbol then
260
return true
261
fi
262
263
let generic_symbol = cast Symbols.GENERIC?(symbol)!
264
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!
265
266
if generic_symbol.symbol != generic_other_symbol.symbol then
267
return false
268
fi
269
270
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
271
return false
272
fi
273
274
for i in 0..generic_symbol.arguments.count do
275
if !generic_symbol.arguments[i].matches(generic_other_symbol.arguments[i]) then
276
return false
277
fi
278
od
279
280
return true
281
si
282
283
is_equivalent_to(other: Type) -> bool is
284
if other.is_sentinel then
285
return true
286
fi
287
288
if !isa GENERIC(other) then
289
return false
290
fi
291
292
let generic_other = other
293
294
// Unlike `matches`, the outer optional flag participates:
295
// LIST[cat]? is not equivalent to LIST[cat], whether or
296
// not the two share a symbol instance.
297
if is_optional != generic_other.is_optional then
298
return false
299
fi
300
301
if symbol == generic_other.symbol then
302
return true
303
fi
304
305
let generic_symbol = cast Symbols.GENERIC?(symbol)!
306
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!
307
308
if generic_symbol.symbol != generic_other_symbol.symbol then
309
return false
310
fi
311
312
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
313
return false
314
fi
315
316
for i in 0..generic_symbol.arguments.count do
317
if !generic_symbol.arguments[i].is_equivalent_to(generic_other_symbol.arguments[i]) then
318
return false
319
fi
320
od
321
322
return true
323
si
324
325
compare_direct(other: Type) -> Types.MATCH is
326
if other.is_sentinel then
327
return Types.MATCH.SAME
328
fi
329
330
if !isa GENERIC(other) then
331
return Types.MATCH.DIFFERENT
332
fi
333
334
let generic_other = other
335
336
if symbol == generic_other.symbol then
337
// Structurally identical; the outer optional flag
338
// decides the direction. `List[T]?` accepts `List[T]`
339
// (widening); `List[T]` rejects `List[T]?` (would
340
// lose the discriminator).
341
if is_optional == generic_other.is_optional then
342
return Types.MATCH.SAME
343
elif is_optional then
344
return Types.MATCH.ASSIGNABLE
345
else
346
return Types.MATCH.DIFFERENT
347
fi
348
fi
349
350
let generic_symbol = cast Symbols.GENERIC?(symbol)!
351
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!
352
353
if generic_symbol.symbol != generic_other_symbol.symbol then
354
return Types.MATCH.DIFFERENT
355
fi
356
357
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
358
return Types.MATCH.DIFFERENT
359
fi
360
361
// Same outer-flag rule as the shared-symbol fast path
362
// above, for structurally-equal constructions that don't
363
// share a symbol instance: a bare slot rejects an
364
// optional value outright, and an optional slot accepting
365
// a bare value is a widening, never an exact match.
366
if !is_optional /\ generic_other.is_optional then
367
return Types.MATCH.DIFFERENT
368
fi
369
370
let result mut =
371
if is_optional == generic_other.is_optional then
372
Types.MATCH.SAME
373
else
374
Types.MATCH.ASSIGNABLE
375
fi
376
377
for i in 0..generic_symbol.arguments.count do
378
let variance = effective_argument_variance(generic_other, i)
379
380
let argument_score: Types.MATCH mut
381
382
if variance == TypeVariance.COVARIANT then
383
argument_score = generic_symbol.arguments[i].compare(generic_other_symbol.arguments[i])
384
elif variance == TypeVariance.CONTRAVARIANT then
385
argument_score = generic_other_symbol.arguments[i].compare(generic_symbol.arguments[i])
386
elif generic_symbol.arguments[i].is_equivalent_to(generic_other_symbol.arguments[i]) then
387
argument_score = Types.MATCH.SAME
388
else
389
// `matches` would erase the optional flag here, and an
390
// invariant position must not: reading from the
391
// slot could surface a null its type denies, and
392
// writing through it could store one.
393
return Types.MATCH.DIFFERENT
394
fi
395
396
if cast int(argument_score) > cast int(result) then
397
result = argument_score
398
fi
399
od
400
401
return result
402
si
403
404
compare(other: Type) -> Types.MATCH is
405
if isa ONE_OF(other) /\ !isa ONE_OF(self) then
406
// ONE_OF assignability is "value of underlying union
407
// restricted to a variant subset" — delegate to the
408
// underlying type so the narrowing target's
409
// is_assignable_from check sees the same identity it
410
// would for the plain union. NAMED.compare already
411
// does the right thing for ONE_OF via symbol-match.
412
let one_of = other
413
return self.compare(one_of.underlying_type)
414
fi
415
416
// An already-broken type, and one still being inferred,
417
// both answer `is_null` as well - so this has to come
418
// ahead of the null test, or a construction rejects them
419
// and piles a second diagnostic onto the first.
420
if other.is_sentinel then
421
return Types.MATCH.ASSIGNABLE
422
fi
423
424
if other.is_null /\ accepts_null then
425
return Types.MATCH.ASSIGNABLE
426
elif other.is_named then
427
// Strict non-nullable-by-default: a bare slot never
428
// accepts a `T?` value, at every construction shape.
429
// compare_direct already enforces this for the shared
430
// symbol, but the ancestor walk below recurses on
431
// `other.symbol.ancestors`, which are bare (see
432
// `Symbol.ancestors`) — so without this guard an
433
// optional generic silently widens to a non-optional
434
// supertype and its null discriminator is lost.
435
//
436
// A construction's own optional flag is fixed even when
437
// its arguments are still unbound, so unlike
438
// NAMED.compare this does not stand aside for wild
439
// placeholders: `Iterable[T]` is non-optional whatever
440
// `T` turns out to be.
441
if !is_optional /\ other.is_optional then
442
return Types.MATCH.DIFFERENT
443
fi
444
445
let direct_score = compare_direct(other)
446
447
if cast int(direct_score) <= cast int(Types.MATCH.ASSIGNABLE) then
448
return direct_score
449
fi
450
451
for i in 0..other.symbol.ancestors.count do
452
let a = other.symbol.get_ancestor(i)
453
454
let match = self.compare(a)
455
456
if cast int(match) <= cast int(Types.MATCH.ASSIGNABLE) then
457
return Types.MATCH.ASSIGNABLE
458
elif match == Types.MATCH.CONVERTABLE then
459
return Types.MATCH.CONVERTABLE
460
fi
461
od
462
fi
463
464
if is_wild \/ other.is_wild then
465
return Types.MATCH.WILD
466
fi
467
468
return Types.MATCH.DIFFERENT
469
si
470
471
// attempt to bind type variables in this generic against concrete types in
472
// other by pattern matching
473
bind_type_variables(other: Type, results: GENERIC_ARGUMENT_BIND_RESULTS) -> bool is
474
if other.is_null then
475
return true
476
fi
477
478
if let other_generic: GENERIC = other then
479
let generic_symbol = cast Symbols.GENERIC?(symbol)!
480
let generic_other_symbol = cast Symbols.GENERIC?(other_generic.symbol)!
481
482
if generic_symbol.symbol == generic_other_symbol.symbol then
483
let result mut = true
484
485
for i in 0..arguments.count do
486
// A nested type argument is taken literally by the
487
// CLR: `Func[MAYBE[U]]` is not `Func[string?]` for
488
// any U, so the slot-boundary coercion between the
489
// optional carriers cannot apply here. A MAYBE
490
// formal at this position binds only against a
491
// MAYBE actual, or one still being inferred.
492
if let maybe_formal: GENERIC = arguments[i] /\ maybe_formal.is_maybe /\ !_can_bind_maybe_argument(maybe_formal, other.arguments[i]) then
493
return false
494
fi
495
496
result = arguments[i].bind_type_variables(other.arguments[i], results) /\ result
497
od
498
499
return result
500
else
501
for i in 0..other_generic.ancestors.count do
502
let a = other_generic.symbol.get_ancestor(i)
503
504
if bind_type_variables(a, results) then
505
return true
506
fi
507
od
508
509
return false
510
fi
511
else
512
for a in other.ancestors do
513
if bind_type_variables(a, results) then
514
return true
515
fi
516
od
517
518
return false
519
fi
520
si
521
522
// A `MAYBE[T]` written out by name is a plain construction over the
523
// same symbol - the same CLR type, however it was reached.
524
_can_bind_maybe_argument(formal: GENERIC, actual: Type) -> bool static =>
525
actual.is_maybe \/ actual.is_null \/ actual.is_sentinel \/ formal.is_construction_of_same_symbol_as(actual)
526
527
// Whether `other` is constructed over this construction's own
528
// generic symbol, whatever its arguments and whichever
529
// subclass either side is.
530
is_construction_of_same_symbol_as(other: Type) -> bool is
531
if let other_generic: GENERIC = other then
532
let own_symbol = cast Symbols.GENERIC?(symbol)
533
let other_symbol = cast Symbols.GENERIC?(other_generic.symbol)
534
535
return own_symbol? /\ other_symbol? /\ own_symbol.symbol == other_symbol.symbol
536
fi
537
538
return false
539
si
540
541
542
get_type_arguments_into(results: Collections.LIST[GenericArgument]) is
543
for a in arguments do
544
a.get_type_arguments_into(results)
545
od
546
si
547
548
walk(action: (Type) -> void) is
549
for a in arguments do
550
a.walk(action)
551
od
552
553
super.walk(action)
554
si
555
556
to_string() -> string =>
557
if is_optional then
558
"{symbol.to_string()}?"
559
else
560
symbol.to_string()
561
fi
562
si
563
si