-
Notifications
You must be signed in to change notification settings - Fork 23
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
Parents Manipulation Example #20
base: main
Are you sure you want to change the base?
Conversation
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.
We should not use dotty.tools.dotc.ast.tpd
in examples we give to users
So how do we achieve the result in question? And why doesn't the hacky solution work, given that it constructed the valid asts? |
We should never teach users to use hacks. We show them the correct to do it. If something is missing, then we should add it to the API before the users can use it. |
This PR is a work in progress draft. It's propose is to showcase what we want to do, not to get merged as an example (at least not in a hacky form). So I take it currently there's no good way to do it? |
This example aims to inject a type passed to a macro as a mixin parent for a given `new` expression.
c44150f
to
0561cc1
Compare
Ok, I refactored it so that not to use compiler internals, with the help of Expression{
final class $anon() extends Base with Foo with Bar
(new $anon(): Base & Foo & Bar)
}
Inlined(Some(TypeIdent("macro$package$")), Nil, Block(List(ClassDef("$anon", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(TypeIdent("Base")), "<init>"), Nil), TypeIdent("Foo"), Inferred()), None, Nil)), Typed(Apply(Select(New(TypeIdent("$anon")), "<init>"), Nil), Inferred()))) However the output is still "Me" and not "Stuff" as expected – that is, the resulting class does not get the overrides from injected |
@anatoliykmetyuk I tried something different using the same idea, but I noticed something strange. I want to get case t@ClassDef(name, constr, parents, selfOpt, body) =>
ClassDef.copy(t)(name, constr, parents.head :+ TypeTree.of[T], selfOpt, body)
case t@AndType(base, foo) => AndType(t, TypeTree.of[T].tpe) to case t@ClassDef(name, constr, parents, selfOpt, body) =>
ClassDef.copy(t)(name, constr, List(parents.head, TypeTree.of[T]), selfOpt, body)
case t@AndType(base, foo) => AndType(base, TypeTree.of[T].tpe) The result of
newExpr.show{
final class $anon() extends Base with Bar
(new $anon(): Base & Bar)
} newExpr.asTermInlined(Ident(macro$package$),List(),Block(List(TypeDef($anon,Template(DefDef(<init>,List(List()),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Unit)],EmptyTree),List(Apply(Select(New(Ident(Base)),<init>),List()), TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),trait Bar)]),ValDef(_,EmptyTree,EmptyTree),List()))),Typed(Apply(Select(New(Ident($anon)),<init>),List()),TypeTree[AndType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),Base),TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),trait Bar))]))) newTree.show(using Printer.TreeStructure)Inlined(Some(TypeIdent("macro$package$")), Nil, Block(List(ClassDef("$anon", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(TypeIdent("Base")), "<init>"), Nil), Inferred()), None, Nil)), Typed(Apply(Select(New(TypeIdent("$anon")), "<init>"), Nil), Inferred()))) |
Yeah, the overrides don't seem to go through if you merely replace the ClassDef node with a copy with different parents. Something else seems to be needed... |
The idea is:
new Base with Foo
in a macronew Base with Foo with T
, whereT
is passed as a type parameter to the macro by the userAn expression
new Base with Foo
is desugared to the following block:If we want
new Base with Foo with T
, we aim to create the following block:Parents,
Foo
andBar
, live in aTemplate
node for which there is no reflect API.AST of the $anon class
This PR adopts a hacky solution. To add
T
as a parent tonew Base with Foo
, we start with an expression'{new Base with Foo}
. We then useTreeMap
to detect theTemplate
node, break the interface and use the compiler internals to copy it withT
as an injected parent. We then use a similar approach to transform(new $anon(): Base & Foo)
, the last statement of the block showcased above, into(new $anon(): Base & Foo & T)
.However, even though the ASTs produced appear to be correct, it appears that the expression is compiled as if it is still
new Base with Foo
. That is, in the test case from the PR, the override fromBar
is not visible.@nicolasstucki is there a cleaner approach to solve this problem? Why does the hacky AST not work? And why there's no API for template manipulation?