Appearance
| 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 |