Appearance
| 1 | namespace Semantic.Types is | |
| 2 | use IO.Std | |
| 3 | ||
| 4 | use Source.LOCATION | |
| 5 | ||
| 6 | use Logging | |
| 7 | ||
| 8 | // Internal narrowing-only sum type. Represents "value of closed | |
| 9 | // root R restricted to a non-empty proper subset of R's | |
| 10 | // alternatives" — built only by the narrowing path in | |
| 11 | // compile_expressions when an if/elif chain rules out one or | |
| 12 | // more alternatives, never produced by user-written type | |
| 13 | // expressions and never reaches IL gen (reads inside a narrowed | |
| 14 | // scope compile against the underlying root; release_scope | |
| 15 | // restores the declared type). Two closed-root shapes feed | |
| 16 | // this: a union (alternatives are its variants) and a closed | |
| 17 | // class (alternatives are its in-assembly direct subclasses). | |
| 18 | // | |
| 19 | // Subclasses NAMED with the root's own symbol so find_member, | |
| 20 | // ancestors, scope, gen_* and qualified_name all delegate to | |
| 21 | // the root — the issue's "members common to every subtype" | |
| 22 | // requirement is met by exposing the root's own members as a | |
| 23 | // strict lower bound. Construction is by Types.ONE_OF.create | |
| 24 | // which degenerates |subtypes| = 1 to a plain subtype and | |
| 25 | // rejects |subtypes| = 0 (returns null — the caller's signal | |
| 26 | // that the chain is exhaustive). | |
| 27 | // | |
| 28 | // Identity is by underlying type + subtype set: the `matches` override | |
| 29 | // refuses equality against the plain underlying root so that | |
| 30 | // NARROWING.try_push sees the type as changed. Comparison is | |
| 31 | // handled symmetrically: NAMED.compare(ONE_OF) already produces | |
| 32 | // SAME because the symbols match (assignability-to-root holds | |
| 33 | // by construction), GENERIC.compare(ONE_OF) delegates to the | |
| 34 | // underlying type via a small guard so the same identity falls | |
| 35 | // out for generic roots. | |
| 36 | class ONE_OF: NAMED is | |
| 37 | _underlying_type: NAMED | |
| 38 | _subtypes: Collections.LIST[Symbols.Classy] | |
| 39 | ||
| 40 | underlying_type: NAMED => _underlying_type | |
| 41 | subtypes: Collections.Iterable[Symbols.Classy] => _subtypes | |
| 42 | subtypes_count: int => _subtypes.count | |
| 43 | ||
| 44 | init(underlying_type: NAMED, subtypes: Collections.LIST[Symbols.Classy]) is | |
| 45 | super.init(underlying_type.symbol) | |
| 46 | ||
| 47 | _underlying_type = underlying_type | |
| 48 | _subtypes = subtypes | |
| 49 | si | |
| 50 | ||
| 51 | // Build the narrowed type for `subtypes` over the given | |
| 52 | // underlying root type. Returns null when `subtypes` is | |
| 53 | // empty (the caller's exhaustiveness signal). Returns a | |
| 54 | // plain subtype for the singleton case so the size-1 | |
| 55 | // ONE_OF edge case is never representable. Returns a | |
| 56 | // ONE_OF for |subtypes| >= 2. | |
| 57 | create( | |
| 58 | underlying_type: NAMED?, | |
| 59 | subtypes: Collections.LIST[Symbols.Classy]? | |
| 60 | ) -> NAMED? static is | |
| 61 | if !underlying_type? \/ !subtypes? \/ subtypes.count == 0 then | |
| 62 | return null | |
| 63 | fi | |
| 64 | ||
| 65 | // Eliminating every alternative but one leaves a type | |
| 66 | // indistinguishable from the one a positive test would have | |
| 67 | // produced, and nothing checked the value on the way. The | |
| 68 | // mark is what a later phase reads to guard a use of it; a | |
| 69 | // multi-member ONE_OF needs none, since only the root's own | |
| 70 | // members are reachable through it. | |
| 71 | // | |
| 72 | // Marked here rather than in build_singleton_subtype, which | |
| 73 | // the positive-narrow path calls too, and which must stay | |
| 74 | // unmarked: that path has checked the value. | |
| 75 | if subtypes.count == 1 then | |
| 76 | let singleton = build_singleton_subtype(underlying_type, subtypes[0]) | |
| 77 | ||
| 78 | // Only a session can gain an alternative after the fact, | |
| 79 | // so only a session records this; an ordinary build does | |
| 80 | // not reach the set at all. | |
| 81 | if IoC.CONTAINER.instance.build_flags.submission_name? then | |
| 82 | IoC.CONTAINER.instance.complement_singletons.mark(singleton) | |
| 83 | fi | |
| 84 | ||
| 85 | return singleton | |
| 86 | fi | |
| 87 | ||
| 88 | return ONE_OF(underlying_type, subtypes) | |
| 89 | si | |
| 90 | ||
| 91 | // Build the subtype carrying the underlying receiver's generic | |
| 92 | // args. For a variant subtype of a generic union, args are | |
| 93 | // shared with the union slot-for-slot and the singleton | |
| 94 | // collapses to a GENERIC over the variant. For a closed-class | |
| 95 | // subclass the receiver's generic args don't lift uniformly, | |
| 96 | // so we emit the bare subtype NAMED — the narrowing | |
| 97 | // complement path already rejects generic-class receivers. | |
| 98 | build_singleton_subtype( | |
| 99 | underlying_type: NAMED, | |
| 100 | subtype: Symbols.Classy | |
| 101 | ) -> NAMED? static => | |
| 102 | if isa GENERIC(underlying_type) /\ subtype.is_variant then | |
| 103 | let generic = underlying_type in | |
| 104 | GENERIC(subtype.location, subtype, generic.arguments) | |
| 105 | else | |
| 106 | NAMED(subtype) | |
| 107 | fi | |
| 108 | ||
| 109 | contains_subtype(v: Symbols.Classy?) -> bool is | |
| 110 | if !v? then | |
| 111 | return false | |
| 112 | fi | |
| 113 | ||
| 114 | for u in _subtypes do | |
| 115 | if u == v then | |
| 116 | return true | |
| 117 | fi | |
| 118 | od | |
| 119 | ||
| 120 | return false | |
| 121 | si | |
| 122 | ||
| 123 | // Join two flow-narrow types over the same closed root by | |
| 124 | // subtype-set union — the exact join at a control-flow merge | |
| 125 | // where each edge narrowed the same variable to a subset of | |
| 126 | // the root's alternatives. Applicable when at least one side | |
| 127 | // is a ONE_OF and the other is a ONE_OF over the same root or | |
| 128 | // a single alternative of it. Returns the underlying root | |
| 129 | // type when the union covers every alternative (the merge | |
| 130 | // learns nothing beyond the declared type), a ONE_OF or | |
| 131 | // single subtype otherwise, and null when the shapes don't | |
| 132 | // line up — the caller falls back to the general LUB. | |
| 133 | // | |
| 134 | // Optionality is unioned too: an edge that can be null keeps | |
| 135 | // the null in the merged type. | |
| 136 | try_join(a: Type?, b: Type?) -> Type? static is | |
| 137 | if !a? \/ !b? then | |
| 138 | return null | |
| 139 | fi | |
| 140 | ||
| 141 | if !isa ONE_OF(a) /\ !isa ONE_OF(b) then | |
| 142 | return null | |
| 143 | fi | |
| 144 | ||
| 145 | let result_is_optional = a.is_optional \/ b.is_optional | |
| 146 | ||
| 147 | let one_of mut = cast ONE_OF?(a.as_non_optional()) | |
| 148 | let other mut = b.as_non_optional() | |
| 149 | ||
| 150 | if !one_of? then | |
| 151 | one_of = cast ONE_OF?(b.as_non_optional()) | |
| 152 | other = a.as_non_optional() | |
| 153 | fi | |
| 154 | ||
| 155 | if !one_of? then | |
| 156 | return null | |
| 157 | fi | |
| 158 | ||
| 159 | let underlying = cast NAMED?(one_of.underlying_type.as_non_optional()) | |
| 160 | let root_classy = _try_drill_to_classy(underlying) | |
| 161 | ||
| 162 | if !underlying? \/ !root_classy? then | |
| 163 | return null | |
| 164 | fi | |
| 165 | ||
| 166 | let members = Collections.LIST[Symbols.Classy]() | |
| 167 | ||
| 168 | for s in one_of.subtypes do | |
| 169 | members.add(s) | |
| 170 | od | |
| 171 | ||
| 172 | let other_one_of = cast ONE_OF?(other) | |
| 173 | ||
| 174 | if other_one_of? then | |
| 175 | if !underlying.matches(other_one_of.underlying_type) then | |
| 176 | return null | |
| 177 | fi | |
| 178 | ||
| 179 | for s in other_one_of.subtypes do | |
| 180 | if !one_of.contains_subtype(s) then | |
| 181 | members.add(s) | |
| 182 | fi | |
| 183 | od | |
| 184 | else | |
| 185 | let classy = _try_drill_to_classy(other) | |
| 186 | ||
| 187 | if !classy? then | |
| 188 | return null | |
| 189 | fi | |
| 190 | ||
| 191 | if classy == root_classy then | |
| 192 | // The other edge holds the whole root — the merge | |
| 193 | // is the root itself. | |
| 194 | return _flag_optional(underlying, result_is_optional) | |
| 195 | fi | |
| 196 | ||
| 197 | let is_alternative mut = false | |
| 198 | ||
| 199 | for s in root_classy.closed_alternatives do | |
| 200 | if s == classy then | |
| 201 | is_alternative = true | |
| 202 | fi | |
| 203 | od | |
| 204 | ||
| 205 | if !is_alternative then | |
| 206 | return null | |
| 207 | fi | |
| 208 | ||
| 209 | if !one_of.contains_subtype(classy) then | |
| 210 | members.add(classy) | |
| 211 | fi | |
| 212 | fi | |
| 213 | ||
| 214 | if _covers_every_alternative(root_classy, members) then | |
| 215 | return _flag_optional(underlying, result_is_optional) | |
| 216 | fi | |
| 217 | ||
| 218 | return _flag_optional(create(underlying, members), result_is_optional) | |
| 219 | si | |
| 220 | ||
| 221 | // True when `members` includes every alternative of the | |
| 222 | // closed root — the same universe rule the complement | |
| 223 | // builder uses: the root's closed alternatives, plus the | |
| 224 | // root itself when it is a concrete class. | |
| 225 | _covers_every_alternative( | |
| 226 | root_classy: Symbols.Classy, | |
| 227 | members: Collections.LIST[Symbols.Classy] | |
| 228 | ) -> bool static is | |
| 229 | if !root_classy.is_closed_root then | |
| 230 | return false | |
| 231 | fi | |
| 232 | ||
| 233 | for s in root_classy.closed_alternatives do | |
| 234 | if !members.contains(s) then | |
| 235 | return false | |
| 236 | fi | |
| 237 | od | |
| 238 | ||
| 239 | if | |
| 240 | root_classy.is_class /\ | |
| 241 | !root_classy.is_abstract /\ | |
| 242 | !members.contains(root_classy) | |
| 243 | then | |
| 244 | return false | |
| 245 | fi | |
| 246 | ||
| 247 | return true | |
| 248 | si | |
| 249 | ||
| 250 | _flag_optional(type: Type?, is_optional: bool) -> Type? static => | |
| 251 | if type? /\ is_optional then | |
| 252 | type.as_optional() | |
| 253 | else | |
| 254 | type | |
| 255 | fi | |
| 256 | ||
| 257 | // The Classy behind `type`, peeling Symbols.GENERIC wrapping | |
| 258 | // for specialized generics. Null when `type` isn't a NAMED | |
| 259 | // of a Classy. | |
| 260 | _try_drill_to_classy(type: Type?) -> Symbols.Classy? static is | |
| 261 | if !type? then | |
| 262 | return null | |
| 263 | fi | |
| 264 | ||
| 265 | if !isa NAMED(type) then | |
| 266 | return null | |
| 267 | fi | |
| 268 | ||
| 269 | let symbol mut = type.symbol | |
| 270 | ||
| 271 | if isa Symbols.GENERIC(symbol) then | |
| 272 | symbol = symbol.symbol | |
| 273 | fi | |
| 274 | ||
| 275 | return cast Symbols.Classy?(symbol) | |
| 276 | si | |
| 277 | ||
| 278 | matches(other: Type) -> bool is | |
| 279 | if !isa ONE_OF(other) then | |
| 280 | return false | |
| 281 | fi | |
| 282 | ||
| 283 | let o = other | |
| 284 | ||
| 285 | if !_underlying_type.matches(o._underlying_type) then | |
| 286 | return false | |
| 287 | fi | |
| 288 | ||
| 289 | if _subtypes.count != o._subtypes.count then | |
| 290 | return false | |
| 291 | fi | |
| 292 | ||
| 293 | for v in _subtypes do | |
| 294 | if !o.contains_subtype(v) then | |
| 295 | return false | |
| 296 | fi | |
| 297 | od | |
| 298 | ||
| 299 | return true | |
| 300 | si | |
| 301 | ||
| 302 | short_description: string is | |
| 303 | let buffer = System.Text.StringBuilder() | |
| 304 | ||
| 305 | buffer.append(_underlying_type.short_description) | |
| 306 | buffer.append('{') | |
| 307 | ||
| 308 | let seen_any mut = false | |
| 309 | ||
| 310 | for v in _subtypes do | |
| 311 | if seen_any then | |
| 312 | buffer.append('|') | |
| 313 | fi | |
| 314 | ||
| 315 | buffer.append(v.name) | |
| 316 | ||
| 317 | seen_any = true | |
| 318 | od | |
| 319 | ||
| 320 | buffer.append('}') | |
| 321 | ||
| 322 | if is_optional then | |
| 323 | buffer.append('?') | |
| 324 | fi | |
| 325 | ||
| 326 | return buffer.to_string() | |
| 327 | si | |
| 328 | ||
| 329 | to_string() -> string => short_description | |
| 330 | ||
| 331 | walk(action: (Type) -> void) is | |
| 332 | _underlying_type.walk(action) | |
| 333 | ||
| 334 | action(self) | |
| 335 | si | |
| 336 | si | |
| 337 | si |