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
for i in 0..arguments.count -1 do
94
if arguments[i].is_inferred then
95
return true;
96
fi
97
od
98
return false;
99
si
100
101
init(
102
symbol: Symbols.GENERIC
103
) is
104
super.init(symbol);
105
si
106
107
init(
108
location: LOCATION,
109
symbol: Symbols.Classy,
110
arguments: Collections.List[Type]
111
) is
112
super.init(Symbols.GENERIC(location, symbol, arguments));
113
si
114
115
create(
116
location: LOCATION,
117
symbol: Symbols.Classy,
118
arguments: Collections.List[Type]
119
) -> GENERIC =>
120
GENERIC(location, symbol, arguments);
121
122
is_same_symbol(other: Type) -> bool is
123
if !isa GENERIC(other) then
124
return false;
125
fi
126
127
let other_generic = other;
128
129
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
130
let generic_other_symbol = cast Symbols.GENERIC?(other_generic.symbol)!;
131
132
return generic_symbol.symbol == generic_other_symbol.symbol;
133
si
134
135
// The declared variance at this type-argument position: whether
136
// this position may vary at all, and in which direction. This is
137
// purely a property of the generic type's own shape — read from
138
// reflected .NET metadata for imported types (Symbols.Classy.
139
// argument_variances) or, for FUNCTION/ACTION/ARRAY, fixed by
140
// the kind of type they are. It says nothing about whether a
141
// *specific* instantiation may use that variance — see
142
// effective_argument_variance for that.
143
get_argument_type_variance(index: int) -> TypeVariance is
144
let generic_symbol = cast Symbols.GENERIC?(symbol);
145
if !generic_symbol? then
146
return TypeVariance.INVARIANT;
147
fi
148
return generic_symbol.symbol.get_argument_variance(index);
149
si
150
151
// The variance actually usable when converting from `other` to
152
// self at this position. The CLR only allows a variant
153
// conversion when the actual type argument at this position is
154
// a reference type on both sides, so this downgrades a declared
155
// covariant/contravariant position to invariant whenever either
156
// side's actual argument is a value type.
157
effective_argument_variance(other: GENERIC, index: int) -> TypeVariance is
158
let declared = other.get_argument_type_variance(index);
159
160
if declared == TypeVariance.INVARIANT then
161
return TypeVariance.INVARIANT;
162
fi
163
164
if index >= 0 /\ index < arguments.count /\ (arguments[index].is_value_type \/ other.arguments[index].is_value_type) then
165
return TypeVariance.INVARIANT;
166
fi
167
168
return declared;
169
si
170
171
get_element_type() -> Type => arguments[0];
172
173
specialize_generic(type_map: Collections.Map[string,Type]) -> Types.GENERIC is
174
let context = IoC.CONTAINER.instance.symbol_table.current_instance_context;
175
176
let we_are_generic = context? /\ context.arguments.count > 0;
177
178
let seen_any_new mut = false;
179
180
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
181
182
let new_arguments = Collections.LIST[Type](arguments.count);
183
184
for i in 0..arguments.count do
185
let argument_name
186
= generic_symbol.symbol.argument_names[i];
187
188
let mapped_type: Type mut;
189
190
if we_are_generic /\ type_map.try_get_value(argument_name, mapped_type ref) then
191
new_arguments.add(mapped_type);
192
193
seen_any_new = true;
194
else
195
let oa = generic_symbol.arguments[i];
196
let na = oa.specialize(type_map);
197
198
new_arguments.add(na);
199
200
if oa != na then
201
seen_any_new = true;
202
fi
203
fi
204
od
205
206
if seen_any_new then
207
let result = create(symbol.location, generic_symbol.symbol, new_arguments);
208
209
return result;
210
else
211
return self;
212
fi
213
si
214
215
specialize(type_map: Collections.Map[string,Type]) -> Type =>
216
specialize_generic(type_map);
217
218
matches(other: Type) -> bool is
219
if other.is_sentinel then
220
return true;
221
fi
222
223
if !isa GENERIC(other) then
224
return false;
225
fi
226
227
let generic_other = other;
228
229
if symbol == generic_other.symbol then
230
return true;
231
fi
232
233
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
234
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!;
235
236
if generic_symbol.symbol != generic_other_symbol.symbol then
237
return false;
238
fi
239
240
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
241
return false;
242
fi
243
244
for i in 0..generic_symbol.arguments.count do
245
if !generic_symbol.arguments[i].matches(generic_other_symbol.arguments[i]) then
246
return false;
247
fi
248
od
249
250
return true;
251
si
252
253
is_equivalent_to(other: Type) -> bool is
254
if other.is_sentinel then
255
return true;
256
fi
257
258
if !isa GENERIC(other) then
259
return false;
260
fi
261
262
let generic_other = other;
263
264
// Unlike `matches`, the outer optional flag participates:
265
// LIST[cat]? is not equivalent to LIST[cat], whether or
266
// not the two share a symbol instance.
267
if is_optional != generic_other.is_optional then
268
return false;
269
fi
270
271
if symbol == generic_other.symbol then
272
return true;
273
fi
274
275
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
276
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!;
277
278
if generic_symbol.symbol != generic_other_symbol.symbol then
279
return false;
280
fi
281
282
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
283
return false;
284
fi
285
286
for i in 0..generic_symbol.arguments.count do
287
if !generic_symbol.arguments[i].is_equivalent_to(generic_other_symbol.arguments[i]) then
288
return false;
289
fi
290
od
291
292
return true;
293
si
294
295
compare_direct(other: Type) -> Types.MATCH is
296
if other.is_sentinel then
297
return Types.MATCH.SAME;
298
fi
299
300
if !isa GENERIC(other) then
301
return Types.MATCH.DIFFERENT;
302
fi
303
304
let generic_other = other;
305
306
if symbol == generic_other.symbol then
307
// Structurally identical; the outer optional flag
308
// decides the direction. `List[T]?` accepts `List[T]`
309
// (widening); `List[T]` rejects `List[T]?` (would
310
// lose the discriminator).
311
if is_optional == generic_other.is_optional then
312
return Types.MATCH.SAME;
313
elif is_optional then
314
return Types.MATCH.ASSIGNABLE;
315
else
316
return Types.MATCH.DIFFERENT;
317
fi
318
fi
319
320
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
321
let generic_other_symbol = cast Symbols.GENERIC?(generic_other.symbol)!;
322
323
if generic_symbol.symbol != generic_other_symbol.symbol then
324
return Types.MATCH.DIFFERENT;
325
fi
326
327
if generic_symbol.arguments.count != generic_other_symbol.arguments.count then
328
return Types.MATCH.DIFFERENT;
329
fi
330
331
// Same outer-flag rule as the shared-symbol fast path
332
// above, for structurally-equal constructions that don't
333
// share a symbol instance: a bare slot rejects an
334
// optional value outright, and an optional slot accepting
335
// a bare value is a widening, never an exact match.
336
if !is_optional /\ generic_other.is_optional then
337
return Types.MATCH.DIFFERENT;
338
fi
339
340
let result mut =
341
if is_optional == generic_other.is_optional then
342
Types.MATCH.SAME
343
else
344
Types.MATCH.ASSIGNABLE
345
fi;
346
347
for i in 0..generic_symbol.arguments.count do
348
let variance = effective_argument_variance(generic_other, i);
349
350
let argument_score: Types.MATCH mut;
351
352
if variance == TypeVariance.COVARIANT then
353
argument_score = generic_symbol.arguments[i].compare(generic_other_symbol.arguments[i]);
354
elif variance == TypeVariance.CONTRAVARIANT then
355
argument_score = generic_other_symbol.arguments[i].compare(generic_symbol.arguments[i]);
356
elif generic_symbol.arguments[i].is_equivalent_to(generic_other_symbol.arguments[i]) then
357
argument_score = Types.MATCH.SAME;
358
else
359
// `matches` would erase the optional flag here, and an
360
// invariant position must not: reading from the
361
// slot could surface a null its type denies, and
362
// writing through it could store one.
363
return Types.MATCH.DIFFERENT;
364
fi
365
366
if cast int(argument_score) > cast int(result) then
367
result = argument_score;
368
fi
369
od
370
371
return result;
372
si
373
374
compare(other: Type) -> Types.MATCH is
375
if isa ONE_OF(other) /\ !isa ONE_OF(self) then
376
// ONE_OF assignability is "value of underlying union
377
// restricted to a variant subset" — delegate to the
378
// underlying type so the narrowing target's
379
// is_assignable_from check sees the same identity it
380
// would for the plain union. NAMED.compare already
381
// does the right thing for ONE_OF via symbol-match.
382
let one_of = other;
383
return self.compare(one_of.underlying_type);
384
fi
385
386
if other.is_null then
387
return Types.MATCH.ASSIGNABLE;
388
elif symbol == null \/ other == null then
389
return Types.MATCH.DIFFERENT;
390
elif other.is_named then
391
// Strict non-nullable-by-default: a bare slot never
392
// accepts a `T?` value, at every construction shape.
393
// compare_direct already enforces this for the shared
394
// symbol, but the ancestor walk below recurses on
395
// `other.symbol.ancestors`, which are bare (see
396
// `Symbol.ancestors`) — so without this guard an
397
// optional generic silently widens to a non-optional
398
// supertype and its null discriminator is lost. Wild
399
// placeholders skip it, matching NAMED.compare.
400
if !is_wild /\ !is_optional /\ other.is_optional then
401
return Types.MATCH.DIFFERENT;
402
fi
403
404
let direct_score = compare_direct(other);
405
406
if cast int(direct_score) <= cast int(Types.MATCH.ASSIGNABLE) then
407
return direct_score;
408
fi
409
410
for i in 0..other.symbol.ancestors.count do
411
let a = other.symbol.get_ancestor(i);
412
413
let match = self.compare(a);
414
415
if cast int(match) <= cast int(Types.MATCH.ASSIGNABLE) then
416
return Types.MATCH.ASSIGNABLE;
417
elif match == Types.MATCH.CONVERTABLE then
418
return Types.MATCH.CONVERTABLE;
419
fi
420
od
421
fi
422
423
if is_wild \/ other.is_wild then
424
return Types.MATCH.WILD;
425
fi
426
427
return Types.MATCH.DIFFERENT;
428
si
429
430
// attempt to bind type variables in this generic against concrete types in
431
// other by pattern matching
432
bind_type_variables(other: Type, results: GENERIC_ARGUMENT_BIND_RESULTS) -> bool is
433
if other.is_null then
434
return true;
435
fi
436
437
if let other_generic: GENERIC = other then
438
let generic_symbol = cast Symbols.GENERIC?(symbol)!;
439
let generic_other_symbol = cast Symbols.GENERIC?(other_generic.symbol)!;
440
441
if generic_symbol.symbol == generic_other_symbol.symbol then
442
let result mut = true;
443
444
for i in 0..arguments.count do
445
result = arguments[i].bind_type_variables(other.arguments[i], results) /\ result;
446
od
447
448
return result;
449
else
450
for i in 0..other_generic.ancestors.count do
451
let a = other_generic.symbol.get_ancestor(i);
452
453
if bind_type_variables(a, results) then
454
return true;
455
fi
456
od
457
458
return false;
459
fi
460
else
461
for a in other.ancestors do
462
if bind_type_variables(a, results) then
463
return true;
464
fi
465
od
466
467
return false;
468
fi
469
si
470
471
get_type_arguments_into(results: Collections.LIST[GenericArgument]) is
472
for a in arguments do
473
a.get_type_arguments_into(results);
474
od
475
si
476
477
walk(action: (Type) -> void) is
478
for a in arguments do
479
a.walk(action);
480
od
481
482
super.walk(action);
483
si
484
485
to_string() -> string =>
486
if is_optional then
487
"{symbol.to_string()}?";
488
else
489
symbol.to_string();
490
fi;
491
si
492
si