Skip to content
← Back

src/syntax/process/case_pattern_domain.ghul

1
namespace Syntax.Process is
2
use Semantic.Types.Type
3
use Semantic.Symbols.Classy
4
use Semantic.Symbols.ENUM_STRUCT_MEMBER
5
6
enum ConstructorKind is
7
// Absence, the extra alternative an optional type carries.
8
NULL,
9
FALSE,
10
TRUE,
11
// A union variant, a subclass of a closed root, or the
12
// constructible root itself.
13
CLASSY,
14
ENUM_MEMBER,
15
// The single, irrefutable alternative of a type that
16
// destructures — a tuple, or anything else with a positional
17
// shape.
18
PRODUCT,
19
// A literal drawn from a domain too large to enumerate. Never
20
// completes a domain, and never equal to any other.
21
OPAQUE,
22
si
23
24
// One alternative a value of some domain can take.
25
//
26
// Identity is the object, never the name — two constructors are
27
// the same alternative when they are the same instance. A domain
28
// builds its alternatives once and every pattern that selects one
29
// refers back to that instance, so `==` answers correctly and
30
// nothing is keyed on rendered text. `name` exists only to
31
// describe a missing case in a diagnostic.
32
class PATTERN_CONSTRUCTOR is
33
kind: ConstructorKind public
34
name: string public
35
36
// The type a sub-pattern of this alternative is matched
37
// against, when the alternative carries fields. Null for a
38
// leaf alternative.
39
alternative_type: Type? public
40
41
// Set for CLASSY, and compared unspecialized, so an arm
42
// ascribing a constructed `Result.OK[int, string]` selects
43
// the same alternative the union declared.
44
classy: Classy? public
45
46
// Set for ENUM_MEMBER. The member's own symbol is the key; a
47
// literal arm is resolved to it once, by value, at the point
48
// the pattern is built.
49
enum_member: ENUM_STRUCT_MEMBER? public
50
51
_classifier: CASE_DOMAIN_CLASSIFIER?
52
_field_domains: Collections.List[PATTERN_DOMAIN?]?
53
54
// An alternative whose fields are described by a type the
55
// classifier will read when, and only when, a pattern
56
// descends into them.
57
init(
58
kind: ConstructorKind,
59
name: string,
60
alternative_type: Type?,
61
classifier: CASE_DOMAIN_CLASSIFIER
62
) is
63
super.init()
64
65
self.kind = kind
66
self.name = name
67
self.alternative_type = alternative_type
68
self._classifier = classifier
69
si
70
71
// An alternative whose field domains are already known.
72
init(kind: ConstructorKind, name: string, field_domains: Collections.List[PATTERN_DOMAIN?]) is
73
super.init()
74
75
self.kind = kind
76
self.name = name
77
self._field_domains = field_domains
78
si
79
80
set_classy(classy: Classy) is
81
self.classy = classy
82
si
83
84
set_enum_member(member: ENUM_STRUCT_MEMBER) is
85
self.enum_member = member
86
si
87
88
// The domain of each field, read on first use. Deferred
89
// because a type reaches itself through its own fields
90
// whenever it is recursive, and the walk only ever descends
91
// as far as a written pattern goes.
92
field_domains: Collections.List[PATTERN_DOMAIN?] is
93
if !_field_domains? then
94
_field_domains = _classifier!.field_domains_of(alternative_type)
95
fi
96
97
return _field_domains
98
si
99
100
arity: int => field_domains.count
101
si
102
103
// The finite set of alternatives a value of a type can take. A
104
// type whose values cannot be enumerated this way — `int`,
105
// `string`, an open class hierarchy — has no domain, and the
106
// classifier answers null for it.
107
class PATTERN_DOMAIN(constructors: Collections.List[PATTERN_CONSTRUCTOR]) is
108
// Set when the alternatives are enum members, so a literal
109
// arm can be checked against the scrutinee's own enum before
110
// its value is matched to a member.
111
enum_root: Classy? public
112
113
super()
114
115
set_enum_root(root: Classy) is
116
self.enum_root = root
117
si
118
si
119
120
// Classifies a type into the alternatives its values can take.
121
//
122
// The order alternatives are produced in is the order a
123
// diagnostic lists them: absence first, then `false` before
124
// `true`, then a constructible root before its subclasses, then
125
// variants and enum members in declaration order.
126
class CASE_DOMAIN_CLASSIFIER is
127
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
128
129
// Set by `_classify_core` for the classification in progress
130
// and consumed by `classify` before it returns; an enum
131
// domain carries its root so a literal arm can be checked
132
// against the scrutinee's own enum.
133
_enum_root: Classy?
134
135
init(innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup) is
136
super.init()
137
138
_innate_symbol_lookup = innate_symbol_lookup
139
si
140
141
classify(type: Type?) -> PATTERN_DOMAIN? is
142
if !type? \/ !type.is_settled then
143
return null
144
fi
145
146
let is_optional = type.is_optional
147
let core = if is_optional then type.as_non_optional() else type fi
148
149
_enum_root = null
150
151
let core_constructors = _classify_core(core)
152
let enum_root = _enum_root
153
154
if !core_constructors? then
155
return null
156
fi
157
158
if !is_optional then
159
return _domain(core_constructors, enum_root)
160
fi
161
162
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
163
164
constructors.add(PATTERN_CONSTRUCTOR(ConstructorKind.NULL, "null", null, self))
165
166
for c in core_constructors do
167
constructors.add(c)
168
od
169
170
return _domain(constructors, enum_root)
171
si
172
173
_domain(
174
constructors: Collections.List[PATTERN_CONSTRUCTOR],
175
enum_root: Classy?
176
) -> PATTERN_DOMAIN static is
177
let domain = PATTERN_DOMAIN(constructors)
178
179
if let root = enum_root then
180
domain.set_enum_root(root)
181
fi
182
183
return domain
184
si
185
186
_classify_core(core: Type) -> Collections.List[PATTERN_CONSTRUCTOR]? is
187
if core.matches(_innate_symbol_lookup.get_bool_type()) then
188
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
189
190
constructors.add(PATTERN_CONSTRUCTOR(ConstructorKind.FALSE, "false", null, self))
191
constructors.add(PATTERN_CONSTRUCTOR(ConstructorKind.TRUE, "true", null, self))
192
193
return constructors
194
fi
195
196
if let one_of = cast Semantic.Types.ONE_OF?(core) then
197
// A narrowed receiver lists the exact runtime types it
198
// can hold, so the root is an alternative only when it
199
// appears among them.
200
if !(cast Classy?(one_of.underlying_type.unspecialized_symbol))? then
201
return null
202
fi
203
204
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
205
206
for subtype in one_of.subtypes do
207
constructors.add(_classy_constructor(subtype))
208
od
209
210
return constructors
211
fi
212
213
let root = _try_get_classy(core)
214
215
if !root? then
216
return _product_constructors(core)
217
fi
218
219
if root.is_closed_root then
220
let alternatives = Collections.LIST[Classy]()
221
222
for alternative in root.closed_alternatives do
223
alternatives.add(alternative)
224
od
225
226
if alternatives.count == 0 then
227
return _product_constructors(core)
228
fi
229
230
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
231
232
// A concrete root is constructible in its own right,
233
// so covering every subclass leaves bare root
234
// instances unaccounted for.
235
if root.is_class /\ !root.is_abstract then
236
let root_constructor = PATTERN_CONSTRUCTOR(ConstructorKind.CLASSY, root.name, core, self)
237
238
root_constructor.set_classy(root)
239
240
constructors.add(root_constructor)
241
fi
242
243
for alternative in alternatives do
244
constructors.add(_classy_constructor(alternative))
245
od
246
247
return constructors
248
fi
249
250
if root.symbol_kind == Semantic.Symbols.SymbolKind.ENUM then
251
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
252
253
for symbol in root.symbols do
254
if let member: ENUM_STRUCT_MEMBER = symbol then
255
let constructor = PATTERN_CONSTRUCTOR(ConstructorKind.ENUM_MEMBER, member.name, null, self)
256
257
constructor.set_enum_member(member)
258
259
constructors.add(constructor)
260
fi
261
od
262
263
if constructors.count > 0 then
264
_enum_root = root
265
266
return constructors
267
fi
268
fi
269
270
return _product_constructors(core)
271
si
272
273
// A type that is not a sum of alternatives can still be a
274
// product of them, when it destructures. The single
275
// constructor is irrefutable, so such a domain is covered
276
// exactly when its fields are.
277
_product_constructors(core: Type) -> Collections.List[PATTERN_CONSTRUCTOR]? is
278
if !_destructure_element_types(core)? then
279
return null
280
fi
281
282
let constructors = Collections.LIST[PATTERN_CONSTRUCTOR]()
283
284
constructors.add(PATTERN_CONSTRUCTOR(ConstructorKind.PRODUCT, core.short_description, core, self))
285
286
return constructors
287
si
288
289
_classy_constructor(alternative: Classy) -> PATTERN_CONSTRUCTOR is
290
let constructor = PATTERN_CONSTRUCTOR(ConstructorKind.CLASSY, alternative.name, alternative.type, self)
291
292
constructor.set_classy(alternative)
293
294
return constructor
295
si
296
297
field_domains_of(type: Type?) -> Collections.List[PATTERN_DOMAIN?] is
298
let result = Collections.LIST[PATTERN_DOMAIN?]()
299
300
if !type? then
301
return result
302
fi
303
304
let element_types = _destructure_element_types(type)
305
306
if !element_types? then
307
return result
308
fi
309
310
for element_type in element_types do
311
result.add(classify(element_type))
312
od
313
314
return result
315
si
316
317
// The types a positional destructure of `type` binds, in
318
// order, or null when the type does not destructure. Resolved
319
// through DESTRUCTURE_RESOLVER, so the shape the checker
320
// reasons about is the shape the language matches.
321
_destructure_element_types(type: Type) -> Collections.List[Type]? is
322
let arity = _destructure_arity(type)
323
324
if arity <= 0 then
325
return null
326
fi
327
328
let strategy = DESTRUCTURE_RESOLVER.resolve_strategy(type, arity)
329
330
let result = Collections.LIST[Type]()
331
332
if let deconstruct = strategy.deconstruct_function then
333
// A `deconstruct` writes each element through a `T
334
// ref` parameter, so the element's own type is the
335
// one the reference points at.
336
for argument in deconstruct.arguments do
337
let element_type = _pointee(argument.type)
338
339
if !element_type? then
340
return null
341
fi
342
343
result.add(element_type)
344
od
345
346
return result
347
fi
348
349
for member in strategy.members do
350
if !member? \/ !member.type? then
351
return null
352
fi
353
354
result.add(member.type!)
355
od
356
357
return result
358
si
359
360
_pointee(type: Type?) -> Type? static is
361
if !type? then
362
return null
363
fi
364
365
if let reference = cast Semantic.Types.REFERENCE?(type) then
366
if reference.arguments.count == 1 then
367
return reference.arguments[0]
368
fi
369
370
return null
371
fi
372
373
return type
374
si
375
376
_destructure_arity(type: Type) -> int is
377
if type.is_value_tuple then
378
return type.arguments.count
379
fi
380
381
let positional = DESTRUCTURE_RESOLVER.positional_member_count(type)
382
383
if positional > 0 then
384
return positional
385
fi
386
387
return _deconstruct_arity(type)
388
si
389
390
// The arity of the type's `deconstruct`, when exactly one
391
// viable overload exists. Several viable arities leave the
392
// shape ambiguous, and the type is treated as not
393
// destructurable rather than one of them being picked.
394
_deconstruct_arity(type: Type) -> int is
395
let member = type.find_member("deconstruct")
396
397
if !member? then
398
return 0
399
fi
400
401
let candidates = Collections.LIST[Semantic.Symbols.Function]()
402
403
if let group: Semantic.Symbols.FUNCTION_GROUP = member then
404
for f in group.functions do
405
candidates.add(f)
406
od
407
elif let function: Semantic.Symbols.Function = member then
408
candidates.add(function)
409
fi
410
411
let arity mut = 0
412
413
for candidate in candidates do
414
if !candidate.are_arguments_declared then
415
continue
416
fi
417
418
let candidate_arity = candidate.arguments.count
419
420
if !DESTRUCTURE_RESOLVER.is_viable_deconstruct(candidate, candidate_arity) then
421
continue
422
fi
423
424
if arity > 0 /\ arity != candidate_arity then
425
return 0
426
fi
427
428
arity = candidate_arity
429
od
430
431
return arity
432
si
433
434
_try_get_classy(type: Type) -> Classy? is
435
if let named = cast Semantic.Types.NAMED?(type) then
436
return cast Classy?(named.symbol.unspecialized_symbol)
437
fi
438
439
return null
440
si
441
si
442
si