Materialize singleton classes for namespace declarations#872
Conversation
Assisted-By: devx/7c3d70e1-327c-4158-8ab5-ffccba197089
Assisted-By: devx/7c3d70e1-327c-4158-8ab5-ffccba197089
Assisted-By: devx/7c3d70e1-327c-4158-8ab5-ffccba197089
vinistock
left a comment
There was a problem hiding this comment.
If we make the move to always create singleton classes eagerly, then I think the right layer for the fix would to create a singleton class whenever the class gets created. It may require studying the impact for lazily creating singleton class of singleton classes (e.g.: Foo::<Foo>::<<Foo>>).
Also, I wonder if it would still be worth trying to minimize the amount of work. So far, if no operation in the code reaches into the singleton class, we've been considering that it doesn't exist in our modelling (lazy). Maybe we should only create singleton classes for the descendants of existing singleton classes (instead of all of them).
It makes sense. Will fix the PR to do so. And we still need to keep the lazy path for meta-singleton classes anyway. Ancestor/descendant enumeration will remain broken for meta-singleton classes, but that seems acceptable.
It would be possible, but it would require a significant re-architecture of the ancestor/descendant handling. I think we should keep the eager materialization strategy for this issue for now. |
Assisted-By: devx/7c3d70e1-327c-4158-8ab5-ffccba197089
Assisted-By: devx/7c3d70e1-327c-4158-8ab5-ffccba197089
|
When you say meta singleton classes, do you mean multiple levels of singleton classes (e.g.: If this eager creation of singleton classes doesn't handle that, then it's not quite fitting the algorithm at the right stop and we may need to invest a bit more time thinking about how to address the problem. Maybe when a singleton class is created, we need to trigger automatic creation of singleton classes for all of the descendants of the attached object, keeping the mechanism lazy, but ensuring that it works for any depth of singleton. |
|
Oops, I mixed up the similar concepts in Smalltalk (metaclasses) and Ruby (singleton classes)... I understand this fix is incomplete, but I think it’s still worth merging. This patch doesn’t make the singleton-class-of-a-singleton-class case any worse, and it fixes the ancestor/descendant relationships for singleton classes. We can probably improve the implementation further in the future. |
|
We should be able to fix the problem completely. In Once we reach the attached object, could we check which descendants of the attached object are missing singleton classes and then enqueue their creation? That would likely both continue to have lazy creation and fix the issue no matter how deep the singleton class level. |
This PR materializes all singleton class declarations for class and module namespaces.
This is required for
descendantsenumeration and forfind_member_in_ancestorscorrectness. Without these declarations, a subclass singleton class such asChild::<Child>may not exist whenChildis only defined asclass Child < Parent, so inherited singleton ancestors and members cannot be found.Impacted Ruby methods
Namespace#descendantsnow includes materialized singleton class declarations.Namespace#find_membernow returns members inherited through singleton class ancestors.Other user-facing changes
Performance impact
Because this materializes singleton classes for every namespace declaration, it can add memory use and runtime cost. In practice, the measured impact was small.
Validation
origin/main:Extension.descendantslacksChild::<Child>find_memberregression fails onorigin/main:Child::<Child>returnsDeclarationNotFoundshadowenv exec -- cargo test -p rubydex find_member_in_ancestors_returns_inherited_singleton_membershadowenv exec -- bundle exec ruby -Itest test/declaration_test.rb -n /test_find_member_returns_inherited_members_on_singleton_class/shadowenv exec -- cargo test -p rubydex exact_match_empty_query_returns_allshadowenv exec -- cargo test -p rubydexshadowenv exec -- bundle exec rake ruby_testgit diff --check HEAD~2..HEAD