Skip to content

TreeMap doesn't traverse Template class #10931

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

Closed
xerial opened this issue Dec 28, 2020 · 4 comments
Closed

TreeMap doesn't traverse Template class #10931

xerial opened this issue Dec 28, 2020 · 4 comments
Assignees

Comments

@xerial
Copy link
Contributor

xerial commented Dec 28, 2020

Minimized code

val expr = '{ trait A; new A {}  }  // This code expression will produce Template expression
new TreeMap {}.transformTree(expr.asTerm)(Symbol.spliceOwner) // This code should traverse all child nodes of Template

Expectation

  • scala.quoted.reflect should have Template type definition
  • TreeMap traverses the child nodes of Template node

Scala 3 macro should support traversing nodes inside Template node (https://github.com/lampepfl/dotty/blob/M3/compiler/src/dotty/tools/dotc/ast/Trees.scala#L784-L794), which will be generated by the code like above. Currently, TreeMap.transformTree https://github.com/lampepfl/dotty/blob/M3/library/src/scala/quoted/Quotes.scala#L3989-L3991 has no such pattern so we cannot rewrite a tree if it contains Template node.

QuotesImpl in Scala 3.0.0-M3 also has no corresponding definition for Template node: https://github.com/lampepfl/dotty/blob/M3/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Related: lampepfl/dotty-feature-requests#153

@xerial
Copy link
Contributor Author

xerial commented Jan 11, 2021

@nicolasstucki If you need help (e.g., creating a PR for this), let me know because I'm a stakeholder of this metaprogramming feature. Looks like adding model classes for the Template node itself is not so difficult, but I'm not yet familiar with the entire code base of dotty, so I need some advice for the direction to go.

@nicolasstucki
Copy link
Contributor

@xerial, the fix should be done here https://github.com/lampepfl/dotty/blob/d0491025682da0c958c9b8e85ca356d76d550583/library/src-bootstrapped/scala/quoted/Quotes.scala#L4019

The cases above show how to transform the sub-trees.

Tests can be added in tests/run-macros/i10931 and executed with sbt "scala3-bootstrapped/testCompilation i10931" or sbt "scala3-bootstrapped/testCompilation macros"

@xerial
Copy link
Contributor Author

xerial commented Apr 6, 2021

@nicolasstucki I've tried to traverse ClassDef and found we can use ClassDef.parents to rewrite Template nodes of an anonymous class like new {} to new A {}, but still have no luck.

An example code: https://github.com/lampepfl/dotty/compare/master...xerial:template-node-traversal?expand=1

// Rewritten tree
Inlined(Ident(Macro_1$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(TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class java)),object lang),Object)]),<init>),List()), TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <empty>)),object Example),trait A)]),ValDef(_,EmptyTree,EmptyTree),List()))),Typed(Apply(Select(New(Ident($anon)),<init>),List()),TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <empty>)),object Example),trait A)])))

// Compile error because of type cast failure 
Test 'tests/run-macros/i10931' failed with output:
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at dotty.tools.vulpix.ChildJVMMain.runMain(ChildJVMMain.java:40)
	at dotty.tools.vulpix.ChildJVMMain.main(ChildJVMMain.java:47)
Caused by: java.lang.ClassCastException: class Test_2$package$$anon$1 cannot be cast to class Example$A (Test_2$package$$anon$1 and Example$A are in unnamed module of loader java.net.URLClassLoader @567d299b)
	at Test_2$package$.Test(Test_2.scala:7)
	at Test.main(Test_2.scala:6)
	... 6 more

Basically, what I need would be a support for generating an AnonClass from a given trait type (e.g., A). tpd.scala has such code only for compiler-internal use:
https://github.com/lampepfl/dotty/blob/3.0.0-RC2/compiler/src/dotty/tools/dotc/ast/tpd.scala#L336-L357

For example:

// I'd like to generate code to create an anonymous class from a given type A, like `new A {}`
def mk[A] = '{ newInstanceOf[A] }

Can we expose this functionality to Scala macros? It would be great if you have any idea to support generating anonymous class with Scala macros.

@xerial
Copy link
Contributor Author

xerial commented May 15, 2021

Traversing details of Template type definitions itself was possible, so this is not a lack of functionality. Let me close the issue.

@xerial xerial closed this as completed May 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants