Appearance
| 1 | namespace Semantic is | |
| 2 | use Collections; | |
| 3 | use Semantic.Symbols.Symbol; | |
| 4 | use Semantic.Types.Type; | |
| 5 | ||
| 6 | use Ghul.Pipes; | |
| 7 | ||
| 8 | use Logging; | |
| 9 | ||
| 10 | enum LubMode is | |
| 11 | ONLY_ELEMENT_TYPES, | |
| 12 | ANY_CONCRETE, | |
| 13 | ANY_TRAIT | |
| 14 | si | |
| 15 | ||
| 16 | class LEAST_UPPER_BOUND_MAP is | |
| 17 | _any_unsafe_constraints: bool; | |
| 18 | _mode: LubMode; | |
| 19 | ||
| 20 | types: LIST[Type]; | |
| 21 | element_names: LIST[string?]?; | |
| 22 | ||
| 23 | result: MAP[Symbol,LIST[Type]]?; | |
| 24 | current: MAP[Symbol,LIST[Type]]?; | |
| 25 | ||
| 26 | init() is | |
| 27 | types = LIST[Type](); | |
| 28 | _mode = LubMode.ONLY_ELEMENT_TYPES; | |
| 29 | si | |
| 30 | ||
| 31 | add(type: Type) is | |
| 32 | // Per-position structural merge: if an already-collected | |
| 33 | // constraint shares a head with `type` and they're each | |
| 34 | // concrete in slots the other isn't, collapse them into a | |
| 35 | // single refined entry instead of leaving both in the LUB | |
| 36 | // pool where one would out-rank the other and lose the | |
| 37 | // complementary information. Required for iterative | |
| 38 | // inference cases where two match propagation channels each pin a | |
| 39 | // different position of the same Function shape. | |
| 40 | // | |
| 41 | // Only useful when at least one side is a structurally- | |
| 42 | // recursable GENERIC carrying placeholder slots — for the | |
| 43 | // common (and very hot) flat-type case (int + int, bool + | |
| 44 | // bool, ...) the existing LUB picker does the right thing | |
| 45 | // and the merge would be pure overhead. | |
| 46 | if isa Types.GENERIC(type) /\ type.contains_inferred then | |
| 47 | for i in 0..types.count do | |
| 48 | let merged = _try_merge_per_position(types[i], type); | |
| 49 | ||
| 50 | if merged? then | |
| 51 | types[i] = merged; | |
| 52 | ||
| 53 | if type.is_unsafe_constraints then | |
| 54 | _any_unsafe_constraints = true; | |
| 55 | fi | |
| 56 | ||
| 57 | _update_element_names_from(merged); | |
| 58 | return; | |
| 59 | fi | |
| 60 | od | |
| 61 | else | |
| 62 | for i in 0..types.count do | |
| 63 | let existing = types[i]; | |
| 64 | ||
| 65 | if isa Types.GENERIC(existing) /\ existing.contains_inferred then | |
| 66 | let merged = _try_merge_per_position(existing, type); | |
| 67 | ||
| 68 | if merged? then | |
| 69 | types[i] = merged; | |
| 70 | ||
| 71 | if type.is_unsafe_constraints then | |
| 72 | _any_unsafe_constraints = true; | |
| 73 | fi | |
| 74 | ||
| 75 | _update_element_names_from(merged); | |
| 76 | return; | |
| 77 | fi | |
| 78 | fi | |
| 79 | od | |
| 80 | fi | |
| 81 | ||
| 82 | types.add(type); | |
| 83 | ||
| 84 | if type.is_unsafe_constraints then | |
| 85 | _any_unsafe_constraints = true; | |
| 86 | fi | |
| 87 | ||
| 88 | _update_element_names_from(type); | |
| 89 | si | |
| 90 | ||
| 91 | // When the per-position merge defers a placeholder slot to a | |
| 92 | // concrete one, push the concrete back to the placeholder's | |
| 93 | // origin so iterative-inference resolves the phantom to the | |
| 94 | // same concrete type. Without this the LUB result picks the | |
| 95 | // right concrete at the top level but the AST nodes that | |
| 96 | // produced the placeholder side keep their placeholder-bearing | |
| 97 | // value type — surfaces in analyse-mode HOVER as `***` and in | |
| 98 | // ancestor LUB merges as the placeholder collapsing to object. | |
| 99 | // | |
| 100 | // Only fires for INFERRED_VARIABLE_TYPE (the placeholder kind | |
| 101 | // that carries a back-pointer origin). INFERRED_RETURN_TYPE is | |
| 102 | // a singleton sentinel with no origin to push into; ERROR | |
| 103 | // similarly. Concrete must itself not be a sentinel / inferred | |
| 104 | // — pushing one placeholder into another's LUB would just | |
| 105 | // propagate uncertainty. | |
| 106 | // | |
| 107 | // Identical in spirit to `Semantic.MATCH_PROPAGATOR.pair`'s | |
| 108 | // placeholder-formal branch, but reached from inside the LUB | |
| 109 | // merge — the LUB has no Logger field and isn't constructed | |
| 110 | // with one (callers create instances ad-hoc), so the retry | |
| 111 | // signal is sent via the IoC-resolved logger rather than a | |
| 112 | // dependency-injected reference. Calling through MATCH_PROPAGATOR | |
| 113 | // would have required threading a Logger through every LUB | |
| 114 | // construction site. | |
| 115 | _propagate_to_placeholder(placeholder: Type, concrete: Type?) is | |
| 116 | if !isa Types.INFERRED_VARIABLE_TYPE(placeholder) then | |
| 117 | return; | |
| 118 | fi | |
| 119 | ||
| 120 | if !concrete? \/ concrete.is_sentinel \/ concrete.is_inferred \/ concrete.is_error then | |
| 121 | return; | |
| 122 | fi | |
| 123 | ||
| 124 | let p = cast Types.INFERRED_VARIABLE_TYPE(placeholder); | |
| 125 | ||
| 126 | IoC.CONTAINER.instance.logger.mark_consumed_any_if(p.origin.add_lower_bound(concrete)); | |
| 127 | si | |
| 128 | ||
| 129 | // Returns a per-position structural refinement of a and b, or | |
| 130 | // null if the two aren't shape-compatible. At each position the | |
| 131 | // non-placeholder side wins; recurses through generic args to | |
| 132 | // handle nested cases like Function[Function[?, int], bool]. | |
| 133 | _try_merge_per_position(a: Type?, b: Type?) -> Type? is | |
| 134 | if !a? then return b; fi | |
| 135 | if !b? then return a; fi | |
| 136 | ||
| 137 | // Inference placeholder OR ERROR at the top — defer to the | |
| 138 | // other. ERROR is the "this slot failed" sentinel; an | |
| 139 | // inference placeholder is "still working on it". Either | |
| 140 | // way the other side, however it's resolved, is strictly | |
| 141 | // more informative than this one. Without the is_error leg | |
| 142 | // the tuple LUB from `let v18; v18 = (delayed_lambda([…]), | |
| 143 | // 5);` froze at `(ERROR, int)` on iter 1 (call to the | |
| 144 | // not-yet-resolved lambda returned DUMMY(ERROR)) and out- | |
| 145 | // ranked iter 2's refined `(placeholder, int)` because the | |
| 146 | // top of the latter was treated as is_inferred and got | |
| 147 | // discarded in favour of ERROR. | |
| 148 | if a.is_inferred \/ a.is_error then | |
| 149 | _propagate_to_placeholder(a, b); | |
| 150 | return b; | |
| 151 | fi | |
| 152 | if b.is_inferred \/ b.is_error then | |
| 153 | _propagate_to_placeholder(b, a); | |
| 154 | return a; | |
| 155 | fi | |
| 156 | ||
| 157 | // Otherwise both must be GENERIC with the same head and arity. | |
| 158 | if !isa Types.GENERIC(a) \/ !isa Types.GENERIC(b) then | |
| 159 | if a.matches(b) then return a; fi | |
| 160 | return null; | |
| 161 | fi | |
| 162 | ||
| 163 | let ga = a; | |
| 164 | let gb = b; | |
| 165 | ||
| 166 | let sa = cast Symbols.GENERIC?(ga.symbol); | |
| 167 | let sb = cast Symbols.GENERIC?(gb.symbol); | |
| 168 | ||
| 169 | if !sa? \/ !sb? then return null; fi | |
| 170 | ||
| 171 | // Heads differ — try promoting both to a shared generic | |
| 172 | // ancestor (variant -> union being the motivating case: | |
| 173 | // `DONE[<phantom>]` and `YIELD[int]` share `STEP[…]`). | |
| 174 | // The non-merge fallback in get_result walks ancestors too | |
| 175 | // but intersects via `matches` on the specialised ancestor; a | |
| 176 | // phantom slot vs concrete won't equate, so that path | |
| 177 | // drops to `object`. Merging through the ancestor here | |
| 178 | // lets the per-position rule (placeholder slot defers to | |
| 179 | // concrete) fire on the slots that need it. | |
| 180 | if sa.symbol != sb.symbol then | |
| 181 | return _try_merge_through_shared_ancestor(ga, gb, sa, sb); | |
| 182 | fi | |
| 183 | ||
| 184 | if ga.arguments.count != gb.arguments.count then return null; fi | |
| 185 | ||
| 186 | let merged_args = LIST[Types.Type](ga.arguments.count); | |
| 187 | ||
| 188 | for j in 0..ga.arguments.count do | |
| 189 | let merged_arg = _try_merge_per_position(ga.arguments[j], gb.arguments[j]); | |
| 190 | ||
| 191 | if !merged_arg? then return null; fi | |
| 192 | ||
| 193 | merged_args.add(merged_arg); | |
| 194 | od | |
| 195 | ||
| 196 | // Tuples carry element names alongside element types. | |
| 197 | // Reconciliation rules: both unnamed → unnamed; one named one | |
| 198 | // unnamed → take the name; both named the same → that name; | |
| 199 | // both named differently → take the name from the side whose | |
| 200 | // arg is concrete, or fail the merge if both are concrete. | |
| 201 | if a.is_value_tuple /\ b.is_value_tuple then | |
| 202 | let merged_names = LIST[string](ga.arguments.count); | |
| 203 | let any_name mut = false; | |
| 204 | ||
| 205 | for j in 0..ga.arguments.count do | |
| 206 | let name_a = ga.symbol.get_element_name(j); | |
| 207 | let name_b = gb.symbol.get_element_name(j); | |
| 208 | ||
| 209 | let name = _try_merge_element_name(name_a, name_b, ga.arguments[j], gb.arguments[j]); | |
| 210 | ||
| 211 | if name_a? /\ name_b? /\ name_a !~ name_b /\ !name? then | |
| 212 | return null; | |
| 213 | fi | |
| 214 | ||
| 215 | if name? then any_name = true; fi | |
| 216 | ||
| 217 | // Positional placeholder for an unnamed slot, per the | |
| 218 | // tuple-construction convention. | |
| 219 | merged_names.add(if name? then name else "{j}" fi); | |
| 220 | od | |
| 221 | ||
| 222 | let names: Collections.List[string] = if any_name then merged_names else LIST[string]() fi; | |
| 223 | ||
| 224 | return IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(merged_args, names); | |
| 225 | fi | |
| 226 | ||
| 227 | return ga.create(ga.symbol.location, sa.symbol, merged_args); | |
| 228 | si | |
| 229 | ||
| 230 | // For two GENERICs with different head symbols, look for a | |
| 231 | // shared generic ancestor with matching head and arity, and | |
| 232 | // attempt the per-position merge on the specialised ancestor | |
| 233 | // pair. Returns the merged ancestor type, or null if no | |
| 234 | // ancestor pair lined up. | |
| 235 | // | |
| 236 | // Specialised ancestors come from `Symbols.GENERIC.get_ancestor`, | |
| 237 | // which substitutes the GENERIC's type_map — so for `DONE[?p_T]` | |
| 238 | // the STEP ancestor is `STEP[?p_T]`, and the recursive merge | |
| 239 | // can defer the placeholder slot to the concrete slot of | |
| 240 | // `STEP[int]` coming from a sibling `YIELD[int]`. | |
| 241 | // | |
| 242 | // Walks `sa`'s ancestor list outermost-first (push_ancestor | |
| 243 | // puts direct parents at index 0, so STEP appears before | |
| 244 | // object for a variant), so the closest shared ancestor wins. | |
| 245 | _try_merge_through_shared_ancestor( | |
| 246 | ga: Types.GENERIC, gb: Types.GENERIC, | |
| 247 | sa: Symbols.GENERIC, sb: Symbols.GENERIC | |
| 248 | ) -> Types.Type? is | |
| 249 | for i in 0..sa.ancestors.count do | |
| 250 | let a_anc = sa.get_ancestor(i); | |
| 251 | ||
| 252 | if !isa Types.GENERIC(a_anc) then | |
| 253 | continue; | |
| 254 | fi | |
| 255 | ||
| 256 | let a_anc_gen = a_anc; | |
| 257 | let a_anc_sym = cast Symbols.GENERIC?(a_anc_gen.symbol); | |
| 258 | ||
| 259 | if !a_anc_sym? then | |
| 260 | continue; | |
| 261 | fi | |
| 262 | ||
| 263 | for j in 0..sb.ancestors.count do | |
| 264 | let b_anc = sb.get_ancestor(j); | |
| 265 | ||
| 266 | if !isa Types.GENERIC(b_anc) then | |
| 267 | continue; | |
| 268 | fi | |
| 269 | ||
| 270 | let b_anc_gen = b_anc; | |
| 271 | let b_anc_sym = cast Symbols.GENERIC?(b_anc_gen.symbol); | |
| 272 | ||
| 273 | if !b_anc_sym? then | |
| 274 | continue; | |
| 275 | fi | |
| 276 | ||
| 277 | if | |
| 278 | a_anc_sym.symbol =~ b_anc_sym.symbol /\ | |
| 279 | a_anc_gen.arguments.count == b_anc_gen.arguments.count | |
| 280 | then | |
| 281 | let merged = _try_merge_per_position(a_anc, b_anc); | |
| 282 | ||
| 283 | if merged? then | |
| 284 | return merged; | |
| 285 | fi | |
| 286 | fi | |
| 287 | od | |
| 288 | od | |
| 289 | ||
| 290 | return null; | |
| 291 | si | |
| 292 | ||
| 293 | _try_merge_element_name(name_a: string? mut, name_b: string? mut, arg_a: Types.Type, arg_b: Types.Type) -> string? is | |
| 294 | // Auto-generated positional names like 0 / 1 don't carry | |
| 295 | // user intent; treat them as absent. | |
| 296 | if name_a? /\ Symbols.Symbol.is_positional_member_name(name_a) then name_a = null; fi | |
| 297 | if name_b? /\ Symbols.Symbol.is_positional_member_name(name_b) then name_b = null; fi | |
| 298 | ||
| 299 | if !name_a? /\ !name_b? then return null; fi | |
| 300 | if !name_a? then return name_b; fi | |
| 301 | if !name_b? then return name_a; fi | |
| 302 | if name_a =~ name_b then return name_a; fi | |
| 303 | ||
| 304 | // Names differ. If one side's element type is still a | |
| 305 | // placeholder we trust the named side's intent more than | |
| 306 | // the placeholder's; otherwise the conflict is real. | |
| 307 | if !arg_a.contains_inferred /\ arg_b.contains_inferred then return name_a; fi | |
| 308 | if arg_a.contains_inferred /\ !arg_b.contains_inferred then return name_b; fi | |
| 309 | ||
| 310 | return null; | |
| 311 | si | |
| 312 | ||
| 313 | _add(type: Type) is | |
| 314 | if !result? then | |
| 315 | result = MAP[Symbol,LIST[Type]](); | |
| 316 | _add_all_first(type); | |
| 317 | else | |
| 318 | current = MAP[Symbol,LIST[Type]](); | |
| 319 | ||
| 320 | _add_all_subsequent(type); | |
| 321 | ||
| 322 | _apply_intersection(); | |
| 323 | fi | |
| 324 | si | |
| 325 | ||
| 326 | get_result() -> Type? is | |
| 327 | if types.count == 0 then | |
| 328 | return null; | |
| 329 | fi | |
| 330 | ||
| 331 | // this was the original common element type | |
| 332 | // inference heuristic | |
| 333 | let best_assignable = _try_get_best_assignable(); | |
| 334 | if best_assignable? then | |
| 335 | return best_assignable; | |
| 336 | fi | |
| 337 | ||
| 338 | // this is LUB applied to only element types, but | |
| 339 | // it's actually worse that the original heuristic | |
| 340 | // in some cases because it doesn't handle type variance | |
| 341 | // TODO consider removing | |
| 342 | let best_element_type = _try_get_best_element_type(); | |
| 343 | if best_element_type? then | |
| 344 | return best_element_type; | |
| 345 | fi | |
| 346 | ||
| 347 | // this is LUB applied to all elements and all concrete | |
| 348 | // element ancestor types. If all the types are concrete | |
| 349 | // then this will always return a result. In the worse | |
| 350 | // case it will return object, which may still be better | |
| 351 | // than a fairly random choice of interface type | |
| 352 | let best_concrete_type = _try_get_best_concrete(); | |
| 353 | ||
| 354 | // this is LUB applied to all elements and all traits | |
| 355 | // they implement. It has a tendency to select unhelpful | |
| 356 | // traits, and needs tuning somehow to prioritize | |
| 357 | // traits that are relevant based on context | |
| 358 | let best_trait_type = _try_get_best_trait(); | |
| 359 | ||
| 360 | // this still might not be enough - we might need to record the | |
| 361 | // average depth difference (='specificity') per type, but that | |
| 362 | // could be slow | |
| 363 | if best_concrete_type? /\ best_trait_type? then | |
| 364 | let total_concrete_depth_difference = | |
| 365 | types |> reduce(0, (d, t) => d + (t.depth - best_concrete_type.depth)); | |
| 366 | ||
| 367 | let total_trait_depth_difference = | |
| 368 | types |> reduce(0, (d, t) => d + (t.depth - best_trait_type.depth)); | |
| 369 | ||
| 370 | if total_trait_depth_difference < total_concrete_depth_difference then | |
| 371 | return best_trait_type | |
| 372 | else | |
| 373 | return best_concrete_type | |
| 374 | fi | |
| 375 | elif best_concrete_type? then | |
| 376 | return best_concrete_type | |
| 377 | else | |
| 378 | return best_trait_type | |
| 379 | fi | |
| 380 | si | |
| 381 | ||
| 382 | _try_get_best_assignable() -> Type? is | |
| 383 | let best: Type? mut = null; | |
| 384 | ||
| 385 | for type in types do | |
| 386 | if !best? then | |
| 387 | best = type; | |
| 388 | elif type.is_assignable_from(best) then | |
| 389 | best = type; | |
| 390 | elif !best.is_assignable_from(type) then | |
| 391 | return null; | |
| 392 | fi | |
| 393 | od | |
| 394 | ||
| 395 | return _with_element_names(best); | |
| 396 | si | |
| 397 | ||
| 398 | _try_get_best_element_type() -> Type? is | |
| 399 | _mode = LubMode.ONLY_ELEMENT_TYPES; | |
| 400 | ||
| 401 | for type in types do | |
| 402 | _add(type); | |
| 403 | od | |
| 404 | ||
| 405 | return | |
| 406 | let best = _get_best() in | |
| 407 | if best? /\ !best.is_object then | |
| 408 | best | |
| 409 | else | |
| 410 | null | |
| 411 | fi | |
| 412 | si | |
| 413 | ||
| 414 | _try_get_best_concrete() -> Type? is | |
| 415 | result = null; | |
| 416 | current = null; | |
| 417 | _mode = LubMode.ANY_CONCRETE; | |
| 418 | ||
| 419 | for type in types do | |
| 420 | _add(type); | |
| 421 | od | |
| 422 | ||
| 423 | return _get_best(); | |
| 424 | si | |
| 425 | ||
| 426 | _try_get_best_trait() -> Type? is | |
| 427 | result = null; | |
| 428 | current = null; | |
| 429 | _mode = LubMode.ANY_TRAIT; | |
| 430 | ||
| 431 | for type in types do | |
| 432 | _add(type); | |
| 433 | od | |
| 434 | ||
| 435 | return _get_best(); | |
| 436 | si | |
| 437 | ||
| 438 | _get_best() -> Type? is | |
| 439 | let result = self.result!; | |
| 440 | ||
| 441 | let best: Type? mut = null; | |
| 442 | let is_ambiguous mut = false; | |
| 443 | ||
| 444 | for list in result.values do | |
| 445 | for type in list do | |
| 446 | if !_any_unsafe_constraints /\ type.is_unsafe_constraints then | |
| 447 | // only allow unsafe constraints if some of the element | |
| 448 | // types have unsafe constraints: | |
| 449 | continue; | |
| 450 | fi | |
| 451 | ||
| 452 | if !best? then | |
| 453 | best = type; | |
| 454 | is_ambiguous = false; | |
| 455 | elif type.depth > best.depth then | |
| 456 | best = type; | |
| 457 | is_ambiguous = false; | |
| 458 | elif type.depth == best.depth then | |
| 459 | is_ambiguous = true; | |
| 460 | fi | |
| 461 | od | |
| 462 | od | |
| 463 | ||
| 464 | if !is_ambiguous then | |
| 465 | return _with_element_names(best); | |
| 466 | fi | |
| 467 | return null; | |
| 468 | si | |
| 469 | ||
| 470 | _apply_intersection() is | |
| 471 | let result = self.result!; | |
| 472 | let current = self.current!; | |
| 473 | ||
| 474 | let new_result = MAP[Symbol,LIST[Type]](); | |
| 475 | ||
| 476 | for kv in result do | |
| 477 | let rsf = kv.key; | |
| 478 | let result_list = kv.value; | |
| 479 | ||
| 480 | let current_list: LIST[Type] mut; | |
| 481 | ||
| 482 | if !current.try_get_value(rsf, current_list ref) then | |
| 483 | continue; | |
| 484 | fi | |
| 485 | ||
| 486 | // TODO this could be slow, although in practice for non-generic types | |
| 487 | // there will only ever be a single type in the list | |
| 488 | let new_result_list = result_list |> filter(t => current_list |> any(u => t.matches(u))) |> collect_list(); | |
| 489 | ||
| 490 | if new_result_list.count > 0 then | |
| 491 | new_result.add(rsf, new_result_list); | |
| 492 | fi | |
| 493 | od | |
| 494 | ||
| 495 | self.result = new_result; | |
| 496 | si | |
| 497 | ||
| 498 | // Yield ancestors of `type` for the LUB walk, specialised via | |
| 499 | // the type's type-map when the symbol is generic. Without | |
| 500 | // specialisation, the ancestors come back with each class's | |
| 501 | // OWN type-variable symbols intact — so two siblings sharing | |
| 502 | // an `Iterable[T]` ancestor look distinct to the intersection | |
| 503 | // step because their T's are different symbols. Specialising | |
| 504 | // collapses `array_class[int].Iterable[T]` and | |
| 505 | // `list_class[int].Iterable[T]` to a common `Iterable[int]`, | |
| 506 | // which is the only way the trait-walk can find them | |
| 507 | // intersecting. | |
| 508 | _specialised_ancestor(type: Type, i: int) -> Type is | |
| 509 | if let symbol: Symbols.GENERIC = type.symbol then | |
| 510 | return symbol.get_ancestor(i); | |
| 511 | fi | |
| 512 | ||
| 513 | return type.ancestors[i]; | |
| 514 | si | |
| 515 | ||
| 516 | // populate the result set from type and all | |
| 517 | // its ancestor types | |
| 518 | _add_all_first(type: Type) is | |
| 519 | if | |
| 520 | _mode == LubMode.ONLY_ELEMENT_TYPES \/ | |
| 521 | (_mode == LubMode.ANY_CONCRETE /\ !type.is_trait) \/ | |
| 522 | (_mode == LubMode.ANY_TRAIT /\ type.is_trait) | |
| 523 | then | |
| 524 | _add_single_first(type); | |
| 525 | fi | |
| 526 | ||
| 527 | if _mode == LubMode.ANY_CONCRETE /\ type.ancestors.count > 0 then | |
| 528 | _add_all_first(_specialised_ancestor(type, 0)); | |
| 529 | elif _mode == LubMode.ANY_TRAIT then | |
| 530 | for i in 0..type.ancestors.count do | |
| 531 | _add_all_first(_specialised_ancestor(type, i)); | |
| 532 | od | |
| 533 | fi | |
| 534 | si | |
| 535 | ||
| 536 | _add_all_subsequent(type: Type) is | |
| 537 | if | |
| 538 | _mode == LubMode.ONLY_ELEMENT_TYPES \/ | |
| 539 | (_mode == LubMode.ANY_CONCRETE /\ !type.is_trait) \/ | |
| 540 | (_mode == LubMode.ANY_TRAIT /\ type.is_trait) | |
| 541 | then | |
| 542 | _add_single_subsequent(type); | |
| 543 | fi | |
| 544 | ||
| 545 | if _mode == LubMode.ANY_CONCRETE /\ type.ancestors.count > 0 then | |
| 546 | _add_all_subsequent(_specialised_ancestor(type, 0)); | |
| 547 | elif _mode == LubMode.ANY_TRAIT then | |
| 548 | for i in 0..type.ancestors.count do | |
| 549 | _add_all_subsequent(_specialised_ancestor(type, i)); | |
| 550 | od | |
| 551 | fi | |
| 552 | si | |
| 553 | ||
| 554 | // add a single type to the result set | |
| 555 | _add_single_first(type: Type) is | |
| 556 | if type.is_root_value_type then | |
| 557 | // System.ValueType is not a real type for | |
| 558 | // our purposes here, so exclude it: | |
| 559 | return; | |
| 560 | fi | |
| 561 | ||
| 562 | // what unspecialized type was this type ultimately | |
| 563 | // specialized from. If the type is not generic | |
| 564 | // then this is the classy type that represents it | |
| 565 | let rsf = type.symbol.root_unspecialized_symbol; | |
| 566 | ||
| 567 | // we can't put types in a set directly, because types | |
| 568 | // are not interned. However we can reduce the amount | |
| 569 | // of linear searching we need to do by partitioning | |
| 570 | // by root-specialized-from symbols, which are unique | |
| 571 | // per unspecialized generic type. we then only need | |
| 572 | // to search under the matching root specialized from symbol | |
| 573 | ||
| 574 | let result = self.result!; | |
| 575 | ||
| 576 | let list: LIST[Type] mut; | |
| 577 | ||
| 578 | if !result.try_get_value(rsf, list ref) then | |
| 579 | list = LIST[Type](); | |
| 580 | result.add(rsf, list); | |
| 581 | list.add(type); | |
| 582 | elif list |> all(t => !t.matches(type)) then | |
| 583 | list.add(type); | |
| 584 | fi | |
| 585 | ||
| 586 | assert result.contains_key(rsf) else "somehow haven't added {rsf} to {result.keys |}"; | |
| 587 | si | |
| 588 | ||
| 589 | // add a single type to the current set | |
| 590 | _add_single_subsequent(type: Type) is | |
| 591 | let rsf = type.symbol.root_unspecialized_symbol; | |
| 592 | ||
| 593 | let result = self.result!; | |
| 594 | let current = self.current!; | |
| 595 | ||
| 596 | if !result.contains_key(rsf) then | |
| 597 | // cannot intersect | |
| 598 | return; | |
| 599 | fi | |
| 600 | ||
| 601 | let list: LIST[Type] mut; | |
| 602 | ||
| 603 | if !current.try_get_value(rsf, list ref) then | |
| 604 | list = LIST[Type](); | |
| 605 | current.add(rsf, list); | |
| 606 | list.add(type); | |
| 607 | elif list |> all(t => !t.matches(type)) then | |
| 608 | list.add(type); | |
| 609 | fi | |
| 610 | si | |
| 611 | ||
| 612 | _update_element_names_from(type: Type) is | |
| 613 | if type.is_value_tuple then | |
| 614 | if !element_names? then | |
| 615 | element_names = LIST[string?](); | |
| 616 | fi | |
| 617 | ||
| 618 | let names = element_names!; | |
| 619 | ||
| 620 | for i in 0..type.arguments.count do | |
| 621 | if i >= names.count then | |
| 622 | names.add(null); | |
| 623 | fi | |
| 624 | ||
| 625 | let element_name = type.symbol.get_element_name(i); | |
| 626 | ||
| 627 | if element_name? /\ ! Symbols.Symbol.is_positional_member_name(element_name) then | |
| 628 | names[i] = element_name; | |
| 629 | fi | |
| 630 | od | |
| 631 | fi | |
| 632 | si | |
| 633 | ||
| 634 | _with_element_names(type: Type?) -> Type? => | |
| 635 | if !type? then | |
| 636 | null | |
| 637 | elif type.is_value_tuple /\ element_names? /\ element_names.count == type.arguments.count then | |
| 638 | IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(type.arguments, element_names); | |
| 639 | else | |
| 640 | type | |
| 641 | fi; | |
| 642 | si | |
| 643 | si |