-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Contextualize owners in reflection API #10394
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
2e48405
to
ee64418
Compare
83ece6a
to
9e3e20a
Compare
@@ -122,7 +122,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl | |||
end extension | |||
|
|||
extension [ThisTree <: Tree](self: ThisTree): | |||
def changeOwner(newOwner: Symbol): ThisTree = | |||
def adaptOwner(using newOwner: Symbol): ThisTree = | |||
tpd.TreeOps(self).changeNonLocalOwners(newOwner).asInstanceOf[ThisTree] |
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.
When a meta-programmer calls this method explicitly, it seems better to use explicit arguments.
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 can try to handle them explicitly, but this would complicate quite a few abstractions. Mainly TreeTraverser
, TreeMap
, TreeAccumulator
and ValDef.let
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 meant that it might be better to change using newOwner: Symbol
to newOwner: Symbol
. The reason is about consistency: meta-programmers are already calling the method explicitly, it does not make sense to make its only important argument implicit.
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.
There is only one parameter that can be passed here which is the current owner. Which is itself looking for this implicit and returning it. This has the same implicitness but requires the user to write the same parameter on every use site.
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin) | ||
def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = | ||
def newBind(name: String, flags: Flags, tpe: TypeRepr)(using owner: Owner): Symbol = | ||
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe) | ||
def noSymbol: Symbol = dotc.core.Symbols.NoSymbol |
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.
Since creating a symbol is untrivial, it might be better to be explicit about the owner instead of being implicit. This way, they have a consistent model about how symbols work, the invariants, how to debug and fix owner-related problems.
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.
The early owner check has proven to make simplify any bugs in the owners at the point where it is mishandled.
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 think part of the ergonomics of the API is a simple and consistent programming model. As we cannot completely hide the technical detail of owners and the owner-invariant is error-prone, it might be better to make them explicit:
- meta-programmers will learn a consistent model for symbols, where each symbol has an owner.
- they will be more careful when using the APIs. If they mess up with owners, they will not blame the macro system. If the argument is implicit, part of the responsibility of the owner-errors is due to the macro system.
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.
Then I will try to make a version where we handle owners explicitly. I believe this will not be as simple to handle.
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 added a version of the ValDef.let
fix in #10402
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.
Also added #10406 with a version where owners are handled explicitly
@@ -2200,22 +2201,35 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl | |||
case _ => None | |||
end AmbiguousImplicitsTypeTest | |||
|
|||
type Owner = dotc.core.Symbols.Symbol |
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 still find this introduces unnecessary conceptual complexity to meta-programmers. The programming model with explicit owners seems to be better and simpler.
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.
If we want it explicit we would also need to use it explicitly in the TreeTraverser
, TreeMap
, TreeAccumulator
and ValDef.let
. We would also need to remove Symbol.currentOwner
. That would be quite annoying.
Note that the current design already has a contextual owner mechanism but it is hidden behind the Context
abstraction. No one has understood how to propagate this correctly so far. As this PR shows even I missed the connection between the two concepts in some cases. By making it clear what we actually propagate contextually it was trivially obvious where it was missing.
09250fd
to
86b9e37
Compare
* Introduce an `Owner` abstraction * Replace `Tree.changeOwner` with contextualized `Tree.adaptOwner` * Contextualize owner in `Lambda.apply` * Contextualize owner in `Term.etaExpand` * Contextualize owner in `Symbol.{newVal, newMethod, newBind}` * Recontextualize `TreeMap`, `TreeAccumulator`, `TreeTaverser` * Fix owner in `ValDef.let`
86b9e37
to
80828c5
Compare
Replaced by #10406 |
Owner
abstractionTree.changeOwner
with contextualizedTree.adaptOwner
Lambda.apply
Term.etaExpand
Symbol.{newVal, newMethod, newBind}
TreeMap
,TreeAccumulator
,TreeTaverser
ValDef.let