Skip to content
← Back

src/semantic/types/type.ghul

1
namespace Semantic.Types is
2
use IO.Std
3
4
use System.Text.StringBuilder
5
6
use Source
7
8
trait SettableTyped: Typed is
9
set_type(value: Type)
10
si
11
12
// Open so the unit-test assembly can stand in for it. Nothing
13
// outside this compilation implements it otherwise.
14
trait Typed is
15
type: Type?
16
si
17
18
enum MATCH is
19
SAME = 0,
20
ASSIGNABLE = 1,
21
CONVERTABLE = 2,
22
PARTIAL = 3,
23
WILD = 4,
24
DIFFERENT = 100000
25
si
26
27
class Type: Typed abstract is
28
type: Type? => self
29
30
name: string? => null
31
depth: int => symbol.depth
32
33
scope: Scope? => null
34
35
symbol: Symbols.Symbol =>
36
if scope? /\ isa Symbols.Symbol(scope) then
37
cast Symbols.Symbol?(scope)!
38
else
39
Symbols.NONE.instance
40
fi
41
42
ancestors: Collections.List[Type] => symbol.ancestors
43
arguments: Collections.List[Type] => Collections.LIST[Type](0)
44
45
// The declared upper bound of a type variable, or null for any
46
// other type and for an unbounded variable. A value of a bounded
47
// type variable can be used as its bound; narrowing, destructuring,
48
// and operator resolution peel to this so a bounded `T` behaves as
49
// its bound the way member access already does.
50
bound_type: Type? =>
51
if is_type_variable /\ ancestors.count > 0 then
52
ancestors[0]
53
else
54
null
55
fi
56
57
short_description: string => to_string() ?? ""
58
59
// The type variable this type's runtime value is loaded as, or
60
// null when there is none. A narrow composes a variable with
61
// what it was tested against, and the load still puts a `!!N`
62
// on the stack - so `box` and `constrained.` need the variable
63
// rather than the composed view or the tested-against side.
64
type_variable_side: Type? =>
65
if is_type_variable then self else null fi
66
67
unspecialized_symbol: Symbols.Symbol? =>
68
let s = scope in
69
if s? then
70
s.unspecialized_symbol
71
else
72
null
73
fi
74
75
// FIXME: better than isa XXXX, but still should not need these:
76
is_none: bool => false
77
is_null: bool => false
78
is_consumable: bool => !is_sentinel /\ !is_error /\ !is_wild
79
is_error: bool => false
80
is_wild: bool => false
81
is_inferred: bool => false
82
83
// ===== Inference-state predicates =====
84
//
85
// Three states a type can be in during iterative
86
// inference, captured by two predicates:
87
//
88
// is_sentinel is_settled
89
// sentinel singleton true false e.g. INFERRED_VARIABLE_TYPE, ERROR
90
// provisional composite false false e.g. Function[INFERRED_VARIABLE_TYPE, int]
91
// settled false true e.g. Function[int, int]
92
//
93
// The canonical answers for "what state is this type
94
// in?". Prefer these to ad-hoc combinations like
95
// `!is_inferred /\ !is_error` or `is_error \/
96
// is_inferred` — and if you find yourself editing near
97
// such a combination, migrate it to the named
98
// predicate. The point is to have one spelling per
99
// concept across the codebase; without that every new
100
// call-site reinvents the question and gets the
101
// top/deep distinction subtly wrong.
102
103
// True if this type is one of the three singleton
104
// inference markers: INFERRED_VARIABLE_TYPE,
105
// INFERRED_RETURN_TYPE, or ERROR. Sentinels aren't types
106
// a user could write; the first two stand in for slots
107
// the inference machinery hasn't yet filled, the third
108
// for a slot that failed to fill. Use !is_sentinel as
109
// the "this is a real type I can work with" test — it's
110
// true for every named/composite type, including
111
// provisional composites like Function[placeholder,int]
112
// whose outer shape is real even if inner slots are
113
// unresolved.
114
//
115
// Prefer this over `!is_inferred /\ !is_error` — the
116
// conjunction is just the unfactored spelling of this
117
// predicate.
118
is_sentinel: bool => false
119
120
// True if self or any nested type argument is an
121
// inference placeholder (INFERRED_VARIABLE_TYPE,
122
// INFERRED_RETURN_TYPE). Use this in preference to
123
// is_inferred whenever the type might be composite —
124
// a Function[INFERRED_VARIABLE_TYPE, int] has
125
// is_inferred=false at the top level but is *not* yet
126
// resolved. Default looks only at self.is_inferred;
127
// composite types (NAMED etc.) override to recurse.
128
contains_inferred: bool => is_inferred
129
130
// True if self or any nested type argument is a method-level
131
// generic type parameter that has not yet been bound by the
132
// in-flight overload resolution (FUNCTION_GENERIC_ARGUMENT) -
133
// a bare `U` or a composite like `Tasks.TASK[U]` where `U` is
134
// the callee's own unresolved type argument. Distinct from
135
// is_wild, which also answers true for a class-level type
136
// parameter that is already bound in its own context (e.g. a
137
// generic class's own `T` referenced from inside a method) -
138
// that case must not be treated as unresolved. Default looks
139
// only at self.is_function_generic_argument; composite types
140
// (NAMED etc.) override to recurse.
141
contains_function_generic_argument: bool => is_function_generic_argument
142
143
// True if this type's tree contains a method-level generic
144
// type-parameter reference (see contains_function_generic_argument)
145
// that does not belong to `owner` and is not lexically in
146
// scope from it. Such a reference is legitimate only while it
147
// names a generic parameter of the function currently being
148
// compiled, or of an enclosing function a nested closure can
149
// see — anywhere else its index has meaning only inside the
150
// (possibly already-discarded) overload specialization that
151
// produced it, and committing a type carrying it (e.g. as a
152
// local variable's declared type, or as a call-site type
153
// argument) leaks an unbound !!N into the emitted IL.
154
has_function_generic_argument_foreign_to(owner: Scope?) -> bool is
155
if !contains_function_generic_argument then
156
return false
157
fi
158
159
for a in get_type_arguments() do
160
if !a.is_function_generic_argument then
161
continue
162
fi
163
164
if a.symbol.owner == owner then
165
continue
166
fi
167
168
// A nested closure compiling its body may name the
169
// enclosing function's type parameters; those are in
170
// scope via find_enclosing and must not count as
171
// foreign.
172
if owner? then
173
let found = owner.find_enclosing(a.symbol.name)
174
175
if found? /\ found == a.symbol then
176
continue
177
fi
178
fi
179
180
return true
181
od
182
183
return false
184
si
185
186
// Fully resolved: no inference placeholder, no ERROR,
187
// anywhere in the type tree. The canonical "ready to
188
// commit / push as a constraint" test; describes the
189
// universal end-state every inferred slot is expected
190
// to converge to. !is_settled means a slot holds either
191
// a sentinel or a provisional composite that the body-
192
// retry loop may overwrite on the next iter.
193
//
194
// Stronger than !is_sentinel — provisional composites
195
// pass !is_sentinel but fail is_settled. Pick the
196
// weaker predicate (!is_sentinel) when you can work
197
// with any real type; pick is_settled when you need
198
// the inner slots filled too.
199
//
200
// Prefer this over `!is_error /\ !contains_inferred`
201
// or any other recombination of the underlying flags;
202
// if you spot one while editing, migrate it.
203
is_settled: bool => !contains_inferred /\ !is_error
204
is_named: bool => false // FIXME: what would it mean not to be named?
205
is_object: bool => false
206
is_root_value_type: bool => false
207
is_void: bool => false
208
is_type_variable: bool => false
209
is_classy_generic_argument: bool => false
210
is_function_generic_argument: bool => false
211
is_value_type: bool => false
212
213
// A pointer is a native integer, not an object, and the CLI has
214
// no boxed form for one.
215
is_pointer: bool => false
216
217
is_inheritable: bool => false
218
is_class: bool => false
219
is_trait: bool => false
220
is_action: bool => false
221
is_function: bool => false
222
223
// True for a function type marked `pure` — values are trusted
224
// store-free. Not part of type identity or assignability; see
225
// PURE_FUNCTION.
226
is_pure_function: bool => false
227
is_function_with_any_implicit_argument_types: bool => false
228
is_ref: bool => false // specifically 'ref', not just a reference type
229
is_value_tuple: bool => false
230
is_unsafe_constraints: bool => symbol.is_unsafe_constraints
231
232
// The element names of a value tuple, or null when this is not
233
// a tuple or carries no names. The .NET ValueTuple type holds
234
// no names — they ride on a TupleElementNamesAttribute at the
235
// declaration site — so a reflected tuple starts nameless and
236
// is rebuilt with `apply_tuple_element_names`.
237
tuple_element_names: Collections.List[string?]? => null
238
239
// Return an equivalent value-tuple type carrying `names` (one
240
// per element, null for an unnamed element). A no-op for any
241
// type that is not a value tuple.
242
apply_tuple_element_names(names: Collections.List[string?]) -> Type => self
243
244
// True for a reference type carrying an explicit `?`
245
// nullability annotation, and for the value-type NULLABLE[T].
246
is_optional: bool => false
247
248
// True when `null` is one of this type's own values rather than a
249
// marker wrapped around them. Every optional type qualifies, and so
250
// does a pointer, whose null is the zero address rather than a
251
// wrapper around one, and which has no `?` spelling that would mean
252
// the same thing. A `ref` does not: one is only ever formed by
253
// taking the address of a variable, so there is no null to write.
254
accepts_null: bool => is_optional \/ is_pointer
255
256
// True for `Ghul.MAYBE[T]`, the runtime's unconstrained-T
257
// optional carrier. `T?` slot boundaries accept it via an
258
// implicit coercion.
259
is_maybe: bool => false
260
261
init() is
262
si
263
264
// The `T?` form of this type. A value type yields NULLABLE[T];
265
// a reference type yields itself flagged optional. Overridden
266
// by NAMED; the base covers sentinels, which are left as-is.
267
as_optional() -> Type => self
268
269
// Reflected-import variant of `as_optional`. NAMED overrides
270
// to skip the is_value_type / is_type_variable guards (those
271
// would force premature materialization of a TYPE_WRAPPER's
272
// symbol during bootstrap). Other types fall back to plain
273
// `as_optional` — sentinels stay as-is, NULLABLE / MAYBE are
274
// already optional.
275
as_optional_unchecked() -> Type => as_optional()
276
277
// The non-optional form of this type. For a reference type
278
// carrying `?` this drops the flag; a no-op for everything
279
// else — value-type optionality is the distinct NULLABLE[T],
280
// and sentinels have no `?` form. Overridden by NAMED. Used
281
// when flow-sensitive narrowing establishes a variable is
282
// non-null at a use site.
283
as_non_optional() -> Type => self
284
285
// The `T` of a `T?` carrier, regardless of which lowering
286
// produced it (reference-T flagged `NAMED`, value-T
287
// `NULLABLE[T]`, or unconstrained-T `MAYBE[T]`). Null when
288
// this type is not optional-shaped. The single accessor lets
289
// compare/box sites ask "what's inside?" without having to
290
// know which optional flavour they're looking at.
291
optional_inner_type: Type? => null
292
293
// Erased type identity: same type with the reference-`?`
294
// annotation ignored, so `cat?` matches `cat`. This is the CLR's
295
// view — the flag has no runtime existence — and it is what
296
// override matching, reflected-signature comparison, and
297
// synthesised-member wiring need. It is NOT safe for
298
// assignability decisions; those go through `compare` /
299
// `is_equivalent_to`, where the flag participates.
300
// (`NULLABLE[T]` and `MAYBE[T]` are distinct CLR types, so
301
// value-type and unconstrained optionals never erase.)
302
//
303
// Not an equivalence relation, and deliberately not spelled as
304
// one: sentinel types match anything, so it is neither
305
// symmetric (`NULL` matches `cat`, `cat` does not match `NULL`)
306
// nor transitive (`cat` matches `ERROR` matches `dog`), and the
307
// base returns false rather than true. Do not route it through
308
// `equals` — .NET requires all three of those properties from
309
// anything it uses as a dictionary key.
310
matches(other: Type) -> bool => false
311
312
// Full type equivalence: `matches` plus the optional flag, at
313
// every nesting depth. Invariant generic-argument positions
314
// compare with this — `box_like[cat?]` must not unify with
315
// `box_like[cat]`, and a `cat?` local must not satisfy a
316
// `cat ref` parameter. Sentinel tolerance follows `matches`.
317
is_equivalent_to(other: Type) -> bool
318
=> self.matches(other)
319
320
is_assignable_from(other: Type) -> bool
321
=> cast int(compare(other)) <= cast int (MATCH.ASSIGNABLE)
322
323
compare(other: Type) -> MATCH
324
=> MATCH.DIFFERENT
325
326
find_member(name: string) -> Symbols.Symbol?
327
=> null
328
329
find_destructure_member(index: int) -> Symbols.Symbol? is
330
// A bounded type variable destructures through its bound the
331
// way member access already resolves through it.
332
if let bound = bound_type then
333
return bound.find_destructure_member(index)
334
fi
335
336
let name = get_destructure_member_name(index)
337
338
if !name? then
339
return null
340
fi
341
342
let result = find_member(name)
343
344
if result? then
345
return result
346
fi
347
348
if Symbols.Symbol.is_positional_member_name(name) then
349
// Some assemblies name positional members with a leading
350
// backtick (`0, `1, ...) rather than a bare index; retry
351
// with that spelling before giving up.
352
return find_member("`{name}")
353
fi
354
355
return null
356
si
357
358
get_destructure_member_name(index: int) -> string?
359
=> null
360
361
find_ancestor(type: Type) -> Type? => null
362
363
specialize(type_map: Collections.Map[Symbols.Symbol,Type]) -> Type => throw System.NotImplementedException("not implemented by {self.get_type()}")
364
bind_type_variables(other: Type, results: GENERIC_ARGUMENT_BIND_RESULTS) -> bool =>
365
true
366
367
get_type_arguments_into(results: Collections.LIST[GenericArgument]) is
368
si
369
370
get_type_arguments() -> Collections.LIST[GenericArgument] => (
371
let result = Collections.LIST[GenericArgument]()
372
get_type_arguments_into(result)
373
result
374
)
375
376
freeze() -> Type? => null
377
378
walk(action: (Type) -> void) => throw System.NotImplementedException("not implemented by {self.get_type()}")
379
get_element_type() -> Type? => null
380
381
format(result: StringBuilder) is
382
result.append(self)
383
si
384
385
get_hash_code() -> int => symbol.get_hash_code()
386
si
387
si