Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Source | |
| 3 | ||
| 4 | // Settles the synthesized equality members a class was given | |
| 5 | // before its ancestors were known. | |
| 6 | // | |
| 7 | // ADD_ACCESSORS_FOR_PROPERTIES answers eligibility from a class's | |
| 8 | // own body, which is all it can see: at rewrite time a superclass | |
| 9 | // is an unresolved name. What inheritance decides is settled here, | |
| 10 | // once every file's ancestors have resolved and before argument | |
| 11 | // types are stamped, so that a retyped parameter is stamped from | |
| 12 | // the tree like any other. | |
| 13 | // | |
| 14 | // Two things are decided. A class that inherits an equality it did | |
| 15 | // not get from here - one the author wrote, one reached through a | |
| 16 | // trait, one imported - keeps that operator and loses the | |
| 17 | // synthesized members: a second one at this class's own parameter | |
| 18 | // type would be an overload against the inherited one rather than | |
| 19 | // an override of it, and every use would be ambiguous. And a class | |
| 20 | // whose base is synthesized too joins the base's operator instead | |
| 21 | // of opening a second: its parameter is retyped to the base's, so | |
| 22 | // the two are one virtual slot, and its comparison ends by | |
| 23 | // delegating the base's own members to the base. | |
| 24 | // | |
| 25 | // A class over a base that has no equality at all also loses them. | |
| 26 | // The base's state is part of this class's values and the operator | |
| 27 | // could not read it, so a comparison over this class's members | |
| 28 | // alone would answer equal for two values the base distinguishes - | |
| 29 | // the same unsoundness that keeps a partly-private type out. | |
| 30 | class SYNTHESIZE_CLASS_EQUALITY: Visitor is | |
| 31 | _logger: Logging.Logger | |
| 32 | _symbol_table: Semantic.SYMBOL_TABLE | |
| 33 | _innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup | |
| 34 | ||
| 35 | init( | |
| 36 | logger: Logging.Logger, | |
| 37 | symbol_table: Semantic.SYMBOL_TABLE, | |
| 38 | innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup | |
| 39 | ) is | |
| 40 | super.init() | |
| 41 | ||
| 42 | _logger = logger | |
| 43 | _symbol_table = symbol_table | |
| 44 | _innate_symbol_lookup = innate_symbol_lookup | |
| 45 | si | |
| 46 | ||
| 47 | apply(root: Trees.Node) is | |
| 48 | root.walk(self) | |
| 49 | si | |
| 50 | ||
| 51 | pre(`class: Trees.Definitions.CLASS) -> bool is | |
| 52 | _settle(`class) | |
| 53 | ||
| 54 | return false | |
| 55 | si | |
| 56 | ||
| 57 | _settle(`class: Trees.Definitions.CLASS) is | |
| 58 | let class_symbol = cast Semantic.Symbols.CLASS?(_symbol_table.scope_for(`class)) | |
| 59 | ||
| 60 | if !class_symbol? then | |
| 61 | return | |
| 62 | fi | |
| 63 | ||
| 64 | let equals = _find_synthesized(`class.body, "=~") | |
| 65 | ||
| 66 | if !equals? then | |
| 67 | _report_uncomparable_state(`class, class_symbol) | |
| 68 | ||
| 69 | return | |
| 70 | fi | |
| 71 | ||
| 72 | _ensure_return_type(equals) | |
| 73 | ||
| 74 | // Enforcing the override requirement needs to see every | |
| 75 | // subclass, and an open class can be extended from another | |
| 76 | // assembly where nothing here is checked. The requirement | |
| 77 | // would be silently unenforced there, and the operator it | |
| 78 | // guards would answer for subclasses it cannot read. | |
| 79 | if class_symbol.is_declared_open then | |
| 80 | _withdraw(`class, class_symbol) | |
| 81 | ||
| 82 | _logger.error( | |
| 83 | `class.name.location, | |
| 84 | "{`class.name.name} is open, so equality cannot be synthesized for it: a subclass in another assembly cannot be held to supplying its own" | |
| 85 | ) | |
| 86 | ||
| 87 | return | |
| 88 | fi | |
| 89 | ||
| 90 | if !_keeps_equality(class_symbol, 0) then | |
| 91 | _withdraw(`class, class_symbol) | |
| 92 | ||
| 93 | _report_uncomparable_state(`class, class_symbol) | |
| 94 | ||
| 95 | return | |
| 96 | fi | |
| 97 | ||
| 98 | let base = _base_class_type(class_symbol) | |
| 99 | ||
| 100 | if !base? \/ !_is_synthesized_equality(base) then | |
| 101 | return | |
| 102 | fi | |
| 103 | ||
| 104 | _join_to_base(`class, equals) | |
| 105 | si | |
| 106 | ||
| 107 | // A class under one whose equality the compiler wrote. The | |
| 108 | // inherited operator reads the base's members and compares by | |
| 109 | // exact runtime type, so it answers for this class without | |
| 110 | // ever seeing what this class adds. Every subclass therefore | |
| 111 | // supplies its own, by asking for one or by writing it. | |
| 112 | _report_uncomparable_state( | |
| 113 | `class: Trees.Definitions.CLASS, | |
| 114 | class_symbol: Semantic.Symbols.CLASS | |
| 115 | ) is | |
| 116 | // Answered from this class's own declarations: a member | |
| 117 | // lookup on the symbol reaches inherited members, and | |
| 118 | // what is inherited is the very thing being reported. | |
| 119 | if declares_function(`class.body, "=~") then | |
| 120 | return | |
| 121 | fi | |
| 122 | ||
| 123 | let owner = _synthesized_equality_ancestor(class_symbol, 0) | |
| 124 | ||
| 125 | if !owner? then | |
| 126 | return | |
| 127 | fi | |
| 128 | ||
| 129 | _logger.error( | |
| 130 | `class.name.location, | |
| 131 | "{`class.name.name} must declare =~ and get_hash_code", | |
| 132 | owner.location, | |
| 133 | "help: this equality applies only to {owner.name} - @equality() writes them for a subclass" | |
| 134 | ) | |
| 135 | si | |
| 136 | ||
| 137 | // The nearest ancestor carrying an operator the compiler | |
| 138 | // wrote, so that the report can point at what is inherited. | |
| 139 | _synthesized_equality_ancestor( | |
| 140 | symbol: Semantic.Symbols.Classy, | |
| 141 | depth: int | |
| 142 | ) -> Semantic.Symbols.Classy? is | |
| 143 | if depth > 64 then | |
| 144 | return null | |
| 145 | fi | |
| 146 | ||
| 147 | for ancestor in symbol.ancestors do | |
| 148 | let ancestor_symbol = cast Semantic.Symbols.Classy?(ancestor.symbol) | |
| 149 | ||
| 150 | if !ancestor_symbol? then | |
| 151 | continue | |
| 152 | fi | |
| 153 | ||
| 154 | let equals = ancestor_symbol.find_member("=~") | |
| 155 | ||
| 156 | if equals? /\ equals.is_internal then | |
| 157 | return ancestor_symbol | |
| 158 | fi | |
| 159 | ||
| 160 | let further = _synthesized_equality_ancestor(ancestor_symbol, depth + 1) | |
| 161 | ||
| 162 | if further? then | |
| 163 | return further | |
| 164 | fi | |
| 165 | od | |
| 166 | ||
| 167 | return null | |
| 168 | si | |
| 169 | ||
| 170 | // The operator returns `bool`, which the synthesis names and | |
| 171 | // resolve-type-expressions looks up like any other name. A file | |
| 172 | // whose scope is already broken - globals mixed with a | |
| 173 | // namespace, say - may not have it in reach, and a synthesized | |
| 174 | // member reporting that is noise on top of the real diagnostic. | |
| 175 | _ensure_return_type(equals: Trees.Definitions.FUNCTION) is | |
| 176 | if equals.type_expression.type? then | |
| 177 | return | |
| 178 | fi | |
| 179 | ||
| 180 | equals.type_expression.type = _innate_symbol_lookup.get_bool_type() | |
| 181 | si | |
| 182 | ||
| 183 | // Whether a class's synthesized members survive. Answered from | |
| 184 | // the ancestors alone, so the order files are visited in does | |
| 185 | // not change the answer: a subclass reaches the same decision | |
| 186 | // whether or not its base has been visited yet. | |
| 187 | _keeps_equality(class_symbol: Semantic.Symbols.CLASS, depth: int) -> bool is | |
| 188 | // A hierarchy that refers to itself is already an error; | |
| 189 | // stopping here keeps this from following it forever. | |
| 190 | if depth > 64 then | |
| 191 | return false | |
| 192 | fi | |
| 193 | ||
| 194 | let own = class_symbol.find_member("=~") | |
| 195 | ||
| 196 | // A `partial` or `impl` block declared an operator that the | |
| 197 | // body this was synthesized from could not see. | |
| 198 | if !own? \/ !own.is_internal then | |
| 199 | return false | |
| 200 | fi | |
| 201 | ||
| 202 | // An operator reached through a trait, or through any | |
| 203 | // ancestor that did not get one from here, answers for this | |
| 204 | // class already. A second one at this class's parameter | |
| 205 | // type would be an overload against it rather than an | |
| 206 | // override of it. | |
| 207 | if _inherits_foreign_equality(class_symbol, 0) then | |
| 208 | return false | |
| 209 | fi | |
| 210 | ||
| 211 | let base = _base_class_type(class_symbol) | |
| 212 | ||
| 213 | if !base? then | |
| 214 | return true | |
| 215 | fi | |
| 216 | ||
| 217 | // A base taking type arguments is declined. The parameter | |
| 218 | // a subclass overrides at is the base's own read through | |
| 219 | // the instantiation, and an ancestor lookup on a closed | |
| 220 | // instantiation answers the unspecialised type instead. | |
| 221 | if base.arguments.count > 0 then | |
| 222 | return false | |
| 223 | fi | |
| 224 | ||
| 225 | let base_symbol = cast Semantic.Symbols.CLASS?(base.symbol) | |
| 226 | ||
| 227 | if !base_symbol? then | |
| 228 | return false | |
| 229 | fi | |
| 230 | ||
| 231 | if !base_symbol.find_member("=~")? then | |
| 232 | // `object` holds no state, so a class directly over it | |
| 233 | // is the root of its own comparison. Any other base | |
| 234 | // without an operator holds state this one cannot read. | |
| 235 | return _is_object(base) | |
| 236 | fi | |
| 237 | ||
| 238 | if !_is_synthesized_equality(base) then | |
| 239 | return false | |
| 240 | fi | |
| 241 | ||
| 242 | // A base whose own members are withdrawn leaves nothing to | |
| 243 | // join to. | |
| 244 | return _keeps_equality(base_symbol, depth + 1) | |
| 245 | si | |
| 246 | ||
| 247 | // Whether any ancestor - a superclass, a trait, or one of | |
| 248 | // theirs - declares an equality this class did not get from | |
| 249 | // here. Genericity is not in the way of the question: what is | |
| 250 | // being asked is whether an operator is there at all. | |
| 251 | _inherits_foreign_equality(symbol: Semantic.Symbols.Classy, depth: int) -> bool is | |
| 252 | if depth > 64 then | |
| 253 | return false | |
| 254 | fi | |
| 255 | ||
| 256 | for ancestor in symbol.ancestors do | |
| 257 | if _is_object(ancestor) then | |
| 258 | continue | |
| 259 | fi | |
| 260 | ||
| 261 | let ancestor_symbol = cast Semantic.Symbols.Classy?(ancestor.symbol) | |
| 262 | ||
| 263 | if !ancestor_symbol? then | |
| 264 | continue | |
| 265 | fi | |
| 266 | ||
| 267 | let equals = ancestor_symbol.find_member("=~") | |
| 268 | ||
| 269 | if equals? /\ !equals.is_internal then | |
| 270 | return true | |
| 271 | fi | |
| 272 | ||
| 273 | if _inherits_foreign_equality(ancestor_symbol, depth + 1) then | |
| 274 | return true | |
| 275 | fi | |
| 276 | od | |
| 277 | ||
| 278 | return false | |
| 279 | si | |
| 280 | ||
| 281 | // The class this one extends, or null where it extends nothing | |
| 282 | // but `object`. | |
| 283 | _base_class_type(class_symbol: Semantic.Symbols.CLASS) -> Semantic.Types.Type? is | |
| 284 | for ancestor in class_symbol.ancestors do | |
| 285 | if !ancestor.is_class then | |
| 286 | continue | |
| 287 | fi | |
| 288 | ||
| 289 | if _is_object(ancestor) then | |
| 290 | return null | |
| 291 | fi | |
| 292 | ||
| 293 | return ancestor | |
| 294 | od | |
| 295 | ||
| 296 | return null | |
| 297 | si | |
| 298 | ||
| 299 | _is_object(type: Semantic.Types.Type) -> bool => | |
| 300 | type.unspecialized_symbol == _innate_symbol_lookup.get_object_type().unspecialized_symbol | |
| 301 | ||
| 302 | _is_synthesized_equality(type: Semantic.Types.Type?) -> bool is | |
| 303 | if !type? then | |
| 304 | return false | |
| 305 | fi | |
| 306 | ||
| 307 | let symbol = cast Semantic.Symbols.CLASS?(type.symbol) | |
| 308 | ||
| 309 | if !symbol? then | |
| 310 | return false | |
| 311 | fi | |
| 312 | ||
| 313 | let equals = symbol.find_member("=~") | |
| 314 | ||
| 315 | return equals? /\ equals.is_internal | |
| 316 | si | |
| 317 | ||
| 318 | // The type the whole chain's operator takes, which is the type | |
| 319 | // of the topmost class the members were synthesized for. Every | |
| 320 | // class below it overrides at that type rather than narrowing | |
| 321 | // to its own, since a narrowed parameter would be a second | |
| 322 | // overload rather than an override. | |
| 323 | _root_type(class_symbol: Semantic.Symbols.CLASS, depth: int) -> Semantic.Types.Type is | |
| 324 | if depth > 64 then | |
| 325 | return class_symbol.type! | |
| 326 | fi | |
| 327 | ||
| 328 | let base = _base_class_type(class_symbol) | |
| 329 | ||
| 330 | if !base? \/ !_is_synthesized_equality(base) then | |
| 331 | return class_symbol.type! | |
| 332 | fi | |
| 333 | ||
| 334 | let base_symbol = cast Semantic.Symbols.CLASS?(base.symbol) | |
| 335 | ||
| 336 | if !base_symbol? \/ !_keeps_equality(base_symbol, 0) then | |
| 337 | return class_symbol.type! | |
| 338 | fi | |
| 339 | ||
| 340 | return _root_type(base_symbol, depth + 1) | |
| 341 | si | |
| 342 | ||
| 343 | // Retypes the operator's parameter to the one the base takes, | |
| 344 | // so that the two are one virtual slot rather than two | |
| 345 | // overloads. Nothing else: what the body does about the base | |
| 346 | // is decided where it is emitted, which is the only place that | |
| 347 | // knows what the base holds. | |
| 348 | // | |
| 349 | // The type expression keeps the name it was built with and is | |
| 350 | // given the resolved type directly - it carries an internal | |
| 351 | // location, so the name is never read back, and | |
| 352 | // resolve-type-expressions has already run. | |
| 353 | _join_to_base( | |
| 354 | `class: Trees.Definitions.CLASS, | |
| 355 | equals: Trees.Definitions.FUNCTION | |
| 356 | ) is | |
| 357 | let class_symbol = cast Semantic.Symbols.CLASS?(_symbol_table.scope_for(`class))! | |
| 358 | ||
| 359 | let root_type = _root_type(class_symbol, 0) | |
| 360 | ||
| 361 | for argument in equals.arguments do | |
| 362 | argument.type_expression.type = root_type | |
| 363 | od | |
| 364 | si | |
| 365 | ||
| 366 | // Takes the synthesized members back out, tree node and symbol | |
| 367 | // alike, so that what the class inherits is what answers. | |
| 368 | _withdraw(`class: Trees.Definitions.CLASS, class_symbol: Semantic.Symbols.CLASS) is | |
| 369 | for name in ["=~", "get_hash_code", "equals"] do | |
| 370 | let definition = _find_synthesized(`class.body, name) | |
| 371 | ||
| 372 | if !definition? then | |
| 373 | continue | |
| 374 | fi | |
| 375 | ||
| 376 | `class.body.remove(definition) | |
| 377 | ||
| 378 | let group = cast Semantic.Symbols.FUNCTION_GROUP?(class_symbol.find_member(name)) | |
| 379 | ||
| 380 | if !group? then | |
| 381 | continue | |
| 382 | fi | |
| 383 | ||
| 384 | // Only the synthesized halves go: the class may hold a | |
| 385 | // `partial` block's own member under the same name, | |
| 386 | // and that is the one left to answer. | |
| 387 | for function in Collections.LIST[Semantic.Symbols.Function](group.functions) do | |
| 388 | if function.is_internal then | |
| 389 | group.remove(function) | |
| 390 | fi | |
| 391 | od | |
| 392 | ||
| 393 | if group.is_empty then | |
| 394 | class_symbol.remove_direct(name) | |
| 395 | fi | |
| 396 | od | |
| 397 | si | |
| 398 | ||
| 399 | _find_synthesized( | |
| 400 | body: Trees.Definitions.LIST, | |
| 401 | name: string | |
| 402 | ) -> Trees.Definitions.FUNCTION? static is | |
| 403 | for definition in body do | |
| 404 | if | |
| 405 | isa Trees.Definitions.FUNCTION(definition) /\ | |
| 406 | definition.is_synthesized /\ | |
| 407 | definition.name? /\ | |
| 408 | definition.name.name =~ name | |
| 409 | then | |
| 410 | return definition | |
| 411 | fi | |
| 412 | od | |
| 413 | ||
| 414 | return null | |
| 415 | si | |
| 416 | si | |
| 417 | si |