-
Notifications
You must be signed in to change notification settings - Fork 127
Simplify how inheritance is computed. #4079
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The motivation here is that `Inheritable.computeCanonicalEnclosingContainer` is a complicated subroutine, [even](dart-lang@12d271a) [after](dart-lang@d629e1e) [several](dart-lang@dcc239a) [refactorings](dart-lang@95f4208). This is the code that decides where to link inherited members. So questions like "ah ha, `List` has a `.isEmpty`, but it inherits the docs from... `Iterable.isEmpty`." It's the wild west out there, with docs coming from indirect supertypes, interfaces, mixins, etc. A big portion of the complexity comes from choosing an order in which to search the supertypes. We have to take a directed acyclic graph, and choose an order. Prior to this change, the ordering comes from each container class's implementation of `get inheritanceChain`. I think on paper this sounds like a good idea: let a Mixin compute its inheritance chain, let an Enum compute its inheritance chain. In practice, it spread complexity out too much, and a combined implementation is actually quite compact. So in this change we remove `InheritingContainer.inheritanceChain`, the `expandInheritanceChain` extension getter, and `Inheritable._inheritance`. We replace all of this code with `Inheritable._enclosingSuperTypes`, which returns all of the various supertypes of the enclosing element, in a specific order. This getter performs a fairly simple walk up the inheritance tree (extended types, implemented types, mixed-in types, and superclass constraints). The single call to `Inheritable._inheritance` is now a call to `Inheritable._enclosingSuperTypes`. `computeCanonicalEnclosingContainer` calls this in order to collect its set of candidates for the canonical enclosing container. `_enclosingSuperTypes` handles `Object` differently, which allows the impl of `computeCanonicalEnclosingContainer` to be much simplified, no longer concerning itself with any _previous_ candidate. This is very nearly a no-op. There is some change in the handling of `Object` and it's members. Because of this, we tweak the `enum_test` to no longer use `--no-link-to-remote`, so that the links to Object.hashCode and Enum.index continue to work.
if (processed.contains(container)) return; | ||
processed.add(container); | ||
for (var interface in container.interfaceElements) { | ||
addSupertype(interface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry that using recursion here could lead to stackoverflows...
Perhaps better to manually maintain a stack of nodes being processed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I get what you mean. I think I tried that initially, and couldn't map this recursive algorithm into a loop with a stack. I think since it is a kind of depth-first walk, with the deepest containers in the hierarchy being added first. It would require a second mapping maybe, where you also track the containers that you will add to the stack.
In any case, it shouldn't be a problem because (a) the very first thing we do is check if container
is in the processed
set, and return quick if it is (so any illegal loops would not break this algorithm). And it's pretty unfathomable to have a straight-line hierarchy of more than... 100 containers? Even 10 is a wild stretch, much less 100 or 1,000 or 10,000.
…, web, webdriver Revisions updated by `dart tools/rev_sdk_deps.dart`. ai (https://github.com/dart-lang/ai/compare/7fbe88a..72a9283): 72a9283 2025-07-31 Jacob MacDonald add --exclude-tool option to exclude tools by name (dart-lang/ai#253) 0dbbd8b 2025-07-30 Jacob MacDonald only run in shell when on windows and running a .bat file (dart-lang/ai#252) 0858b0b 2025-07-28 Jacob MacDonald further prompt refinement, discourage use of temp ids and encourage getting the widget tree often (dart-lang/ai#249) 912c1f3 2025-07-28 Jacob MacDonald release dart_mcp v0.3.3 (dart-lang/ai#247) 0f7cba8 2025-07-28 Jacob MacDonald add analytics tracking for prompts (dart-lang/ai#246) dartdoc (https://github.com/dart-lang/dartdoc/compare/882aea9..414953e): 414953ed 2025-08-04 Sam Rawlins Simplify how inheritance is computed. (dart-lang/dartdoc#4079) ecosystem (https://github.com/dart-lang/ecosystem/compare/d5233c6..2fe3618): 2fe3618 2025-08-01 dependabot[bot] Bump the github-actions group with 3 updates (dart-lang/ecosystem#361) protobuf (https://github.com/dart-lang/protobuf/compare/44ecd74..0b73b0d): 0b73b0d 2025-07-30 Devon Carew workspace, formatting, and proto version updates (google/protobuf.dart#1033) a664760 2025-07-30 Devon Carew various CI workflow updates (google/protobuf.dart#1035) test (https://github.com/dart-lang/test/compare/6aeb1e4..953e828): 953e8282 2025-08-01 Nate Bosch Drop executable arguments forwarding (dart-lang/test#2528) tools (https://github.com/dart-lang/tools/compare/2a2a2d6..5e977d6): 5e977d6f 2025-07-30 Jacob MacDonald Fix mixtures of parentheses and spaces in windows command paths (dart-lang/tools#2138) 607340ca 2025-07-29 Devon Carew disable failing test (dart-lang/tools#2136) vector_math (https://github.com/google/vector_math.dart/compare/13f185f..3939545): 3939545 2025-08-04 Kevin Moore Bump min SDK to 3.7, update dependencies, reformat (google/vector_math.dart#348) web (https://github.com/dart-lang/web/compare/da1dd5d..1d5771b): 1d5771b 2025-07-31 Nikechukwu [interop] Add support for importing and exporting declarations, as well as multi-file output (dart-lang/web#418) webdriver (https://github.com/google/webdriver.dart/compare/cfab787..595649d): 595649d 2025-08-01 dependabot[bot] Bump nanasess/setup-chromedriver (google/webdriver.dart#331) Change-Id: Ia014bf6cafa9edcdaf453edda9cd5ecff516e16d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/443625 Auto-Submit: Devon Carew <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
Hard to imagine writing by hand - yes. But nothing in the spec prevents it, and our tools should be able to handle also bizarre generated code. |
The motivation here is that
Inheritable.computeCanonicalEnclosingContainer
is a complicated subroutine,even after several refactorings. This is the code that decides where to link inherited members. So questions like "ah ha,
List
has a.isEmpty
, but it inherits the docs from...Iterable.isEmpty
." It's the wild west out there, with docs coming from indirect supertypes, interfaces, mixins, etc.A big portion of the complexity comes from choosing an order in which to search the supertypes. We have to take a directed acyclic graph, and choose an order. Prior to this change, the ordering comes from each container class's implementation of
get inheritanceChain
. I think on paper this sounds like a good idea: let a Mixin compute its inheritance chain, let an Enum compute its inheritance chain. In practice, it spread complexity out too much, and a combined implementation is actually quite compact.So in this change we remove
InheritingContainer.inheritanceChain
, theexpandInheritanceChain
extension getter, andInheritable._inheritance
. We replace all of this code withInheritable._enclosingSuperTypes
, which returns all of the various supertypes of the enclosing element, in a specific order. This getter performs a fairly simple walk up the inheritance tree (extended types, implemented types, mixed-in types, and superclass constraints).The single call to
Inheritable._inheritance
is now a call toInheritable._enclosingSuperTypes
.computeCanonicalEnclosingContainer
calls this in order to collect its set of candidates for the canonical enclosing container._enclosingSuperTypes
handlesObject
differently, which allows the impl ofcomputeCanonicalEnclosingContainer
to be much simplified, no longer concerning itself with any previous candidate.This is very nearly a no-op. There is some change in the handling of
Object
and it's members. Because of this, we tweak theenum_test
to no longer use--no-link-to-remote
, so that the links to Object.hashCode and Enum.index continue to work.I tested this with some manual verification of the Dart SDK docs, Flutter SDK docs, and the collection package docs.