Skip to content

Commit

Permalink
TASTy macro annotations class modifications (part 2)
Browse files Browse the repository at this point in the history
Enable modification of classes with `TastyAnnotation`:
 * Can annotate `class` to transform it
 * Can annotate `object` to transform the companion class

Supported class modifications:
 * Modify the implementations of `def`, `val`, `var`, `lazy val`, `class`, `object` in the class
 * Add new `def`, `val`, `var`, `lazy val`, `class`, `object` members to the class
 * Add a new override for a `def`, `val`, `var`, `lazy val` members in the class

Restrictions:
 * An annotation on a top-level class cannot return a top-level `def`, `val`, `var`, `lazy val`
  • Loading branch information
nicolasstucki committed Dec 5, 2022
1 parent 6853a75 commit 1dc36c6
Show file tree
Hide file tree
Showing 33 changed files with 556 additions and 135 deletions.
18 changes: 8 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/TastyAnnotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dotty.tools.dotc.config.Printers.{macroAnnot => debug}
import dotty.tools.dotc.core.Annotations.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Decorators.*
import dotty.tools.dotc.core.Decorators.*
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.MacroClassLoader
Expand All @@ -30,14 +31,10 @@ class TastyAnnotations(thisPhase: DenotTransformer):
def expandAnnotations(tree: MemberDef)(using Context): List[DefTree] =
if !hasTastyAnnotation(tree.symbol) then
List(tree)
else if tree.symbol.is(Module) then
if tree.symbol.isClass then // error only reported on module class
report.error("TASTy annotations are not supported on object", tree)
List(tree)
else if tree.symbol.isClass then
report.error("TASTy annotations are not supported on class", tree)
else if tree.symbol.is(Module) && !tree.symbol.isClass then
// only class is transformed
List(tree)
else if tree.symbol.isType then
else if tree.symbol.isType && !tree.symbol.isClass then
report.error("TASTy annotations are not supported on type", tree)
List(tree)
else
Expand Down Expand Up @@ -71,7 +68,6 @@ class TastyAnnotations(thisPhase: DenotTransformer):
else
tree
}

allTrees += transformedTree
insertedAfter.foreach(allTrees.++=)

Expand Down Expand Up @@ -99,11 +95,13 @@ class TastyAnnotations(thisPhase: DenotTransformer):
private def checkAndEnter(newTree: Tree, annotated: Symbol, annot: Annotation)(using Context) =
val sym = newTree.symbol
if sym.isClass then
report.error("Generating classes is not supported", annot.tree)
report.error(i"TASTy annotation returning a `class` is not yet supported. $annot tried to add $sym", annot.tree)
else if sym.isType then
report.error("Generating type is not supported", annot.tree)
report.error(i"TASTy annotation cannot return a `type`. $annot tried to add $sym", annot.tree)
else if sym.owner != annotated.owner then
report.error(i"TASTy annotation $annot added $sym with an inconsistent owner. Expected it to be owned by ${annotated.owner} but was owned by ${sym.owner}.", annot.tree)
else if annotated.isClass && annotated.owner.is(Package) /*&& !sym.isClass*/ then
report.error(i"TASTy annotation can not add top-level ${sym.showKind}. $annot tried to add $sym.", annot.tree)
else
sym.enteredAfter(thisPhase)

Expand Down
126 changes: 78 additions & 48 deletions tests/neg-macros/annot-error-annot.check
Original file line number Diff line number Diff line change
@@ -1,68 +1,98 @@

-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:18:6 ---------------------------------------------------------
17 | @error
18 | val vMember: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:17:6 ---------------------------------------------------------
16 |@error
17 |class cGlobal // error
|^
|MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:20:7 ---------------------------------------------------------
19 |@error
20 |object oGlobal // error
|^
|MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:24:6 ---------------------------------------------------------
23 | @error
24 | val vMember: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:20:11 --------------------------------------------------------
19 | @error
20 | lazy val lvMember: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:26:11 --------------------------------------------------------
25 | @error
26 | lazy val lvMember: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:22:6 ---------------------------------------------------------
21 | @error
22 | def dMember: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:28:6 ---------------------------------------------------------
27 | @error
28 | def dMember: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:24:8 ---------------------------------------------------------
23 | @error
24 | given gMember: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:30:8 ---------------------------------------------------------
29 | @error
30 | given gMember: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:26:8 ---------------------------------------------------------
25 | @error
26 | given gMember2: Num[Int] with // error: object not supported (TODO support)
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:32:8 ---------------------------------------------------------
31 | @error
32 | given gMember2: Num[Int] with // error
| ^
| MACRO ERROR
33 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:35:8 ---------------------------------------------------------
34 | @error
35 | given gMember3(using DummyImplicit): Num[Int] with // error
| ^
| MACRO ERROR
36 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:39:8 ---------------------------------------------------------
38 | @error
39 | class cMember // error
| ^
| TASTy annotations are not supported on object
27 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:29:8 ---------------------------------------------------------
28 | @error
29 | given gMember3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:42:9 ---------------------------------------------------------
41 | @error
42 | object oMember // error
| ^
| TASTy annotations are not supported on class
30 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:34:8 ---------------------------------------------------------
33 | @error
34 | val vLocal: Int = 1 // error
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:46:8 ---------------------------------------------------------
45 | @error
46 | val vLocal: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:36:13 --------------------------------------------------------
35 | @error
36 | lazy val lvLocal: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:48:13 --------------------------------------------------------
47 | @error
48 | lazy val lvLocal: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:38:8 ---------------------------------------------------------
37 | @error
38 | def dLocal: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:50:8 ---------------------------------------------------------
49 | @error
50 | def dLocal: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:40:10 --------------------------------------------------------
39 | @error
40 | given gLocal: Int = 1 // error
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:52:10 --------------------------------------------------------
51 | @error
52 | given gLocal: Int = 1 // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:42:10 --------------------------------------------------------
41 | @error
42 | given gLocal2: Num[Int] with // error: object not supported (TODO support)
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:54:10 --------------------------------------------------------
53 | @error
54 | given gLocal2: Num[Int] with // error
| ^
| TASTy annotations are not supported on object
43 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:45:10 --------------------------------------------------------
44 | @error
45 | given gLocal3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
| MACRO ERROR
55 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:57:10 --------------------------------------------------------
56 | @error
57 | given gLocal3(using DummyImplicit): Num[Int] with // error
| ^
| TASTy annotations are not supported on class
46 | def zero = 0
| MACRO ERROR
58 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:61:10 --------------------------------------------------------
60 | @error
61 | class cLocal // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:63:11 --------------------------------------------------------
62 | @error
63 | object oLocal // error
| ^
| MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:2:4 ----------------------------------------------------------
1 |@error
2 |val vGlobal: Int = 1 // error
Expand All @@ -85,13 +115,13 @@
|MACRO ERROR
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:10:6 ---------------------------------------------------------
9 |@error
10 |given gGlobal2: Num[Int] with // error: object not supported (TODO support)
10 |given gGlobal2: Num[Int] with // error
|^
|TASTy annotations are not supported on object
|MACRO ERROR
11 | def zero = 0
-- Error: tests/neg-macros/annot-error-annot/Test_2.scala:13:6 ---------------------------------------------------------
12 |@error
13 |given gGlobal3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
13 |given gGlobal3(using DummyImplicit): Num[Int] with // error
|^
|TASTy annotations are not supported on class
|MACRO ERROR
14 | def zero = 0
29 changes: 23 additions & 6 deletions tests/neg-macros/annot-error-annot/Test_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ def dGlobal: Int = 1 // error
@error
given gGlobal: Int = 1 // error
@error
given gGlobal2: Num[Int] with // error: object not supported (TODO support)
given gGlobal2: Num[Int] with // error
def zero = 0
@error
given gGlobal3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
given gGlobal3(using DummyImplicit): Num[Int] with // error
def zero = 0

@error
class cGlobal // error

@error
object oGlobal // error

class B:
@error
val vMember: Int = 1 // error
Expand All @@ -23,12 +29,18 @@ class B:
@error
given gMember: Int = 1 // error
@error
given gMember2: Num[Int] with // error: object not supported (TODO support)
given gMember2: Num[Int] with // error
def zero = 0
@error
given gMember3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
given gMember3(using DummyImplicit): Num[Int] with // error
def zero = 0

@error
class cMember // error

@error
object oMember // error

def locals: Unit =
@error
val vLocal: Int = 1 // error
Expand All @@ -39,11 +51,16 @@ class B:
@error
given gLocal: Int = 1 // error
@error
given gLocal2: Num[Int] with // error: object not supported (TODO support)
given gLocal2: Num[Int] with // error
def zero = 0
@error
given gLocal3(using DummyImplicit): Num[Int] with // error: class not supported (TODO support)
given gLocal3(using DummyImplicit): Num[Int] with // error
def zero = 0

@error
class cLocal // error
@error
object oLocal // error
()

trait Num[T]:
Expand Down
9 changes: 9 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-method.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

-- Error: tests/neg-macros/annot-mod-class-add-top-method/Test_2.scala:1:0 ---------------------------------------------
1 |@addTopLevelMethod // error
|^^^^^^^^^^^^^^^^^^
|TASTy annotation can not add top-level method. @addTopLevelMethod tried to add method toLevelMethod$1.
-- Error: tests/neg-macros/annot-mod-class-add-top-method/Test_2.scala:4:0 ---------------------------------------------
4 |@addTopLevelMethod // error
|^^^^^^^^^^^^^^^^^^
|TASTy annotation can not add top-level method. @addTopLevelMethod tried to add method toLevelMethod$2.
18 changes: 18 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-method/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.annotation.{experimental, TastyAnnotation}
import scala.quoted._
import scala.collection.mutable

@experimental
class addTopLevelMethod extends TastyAnnotation:
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect._
tree match
case ClassDef(name, ctr, parents, self, body) =>
val cls = tree.symbol
val methType = MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Int])
val methSym = Symbol.newUniqueMethod(cls.owner, "toLevelMethod", methType, Flags.EmptyFlags, Symbol.noSymbol)
val methDef = DefDef(methSym, _ => Some(Literal(IntConstant(1))))
List(methDef, tree)
case _ =>
report.error("Annotation only supports `class`")
List(tree)
5 changes: 5 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-method/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@addTopLevelMethod // error
class Foo

@addTopLevelMethod // error
object Foo
9 changes: 9 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

-- Error: tests/neg-macros/annot-mod-class-add-top-val/Test_2.scala:1:0 ------------------------------------------------
1 |@addTopLevelVal // error
|^^^^^^^^^^^^^^^
|TASTy annotation can not add top-level value. @addTopLevelVal tried to add value toLevelvalod$1.
-- Error: tests/neg-macros/annot-mod-class-add-top-val/Test_2.scala:4:0 ------------------------------------------------
4 |@addTopLevelVal // error
|^^^^^^^^^^^^^^^
|TASTy annotation can not add top-level value. @addTopLevelVal tried to add value toLevelvalod$2.
17 changes: 17 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-val/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import scala.annotation.{experimental, TastyAnnotation}
import scala.quoted._
import scala.collection.mutable

@experimental
class addTopLevelVal extends TastyAnnotation:
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect._
tree match
case ClassDef(name, ctr, parents, self, body) =>
val cls = tree.symbol
val valSym = Symbol.newUniqueVal(cls.owner, "toLevelvalod", TypeRepr.of[Int], Flags.EmptyFlags, Symbol.noSymbol)
val valDef = DefDef(valSym, _ => Some(Literal(IntConstant(1))))
List(valDef, tree)
case _ =>
report.error("Annotation only supports `class`")
List(tree)
5 changes: 5 additions & 0 deletions tests/neg-macros/annot-mod-class-add-top-val/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@addTopLevelVal // error
class Foo

@addTopLevelVal // error
object Foo
17 changes: 0 additions & 17 deletions tests/neg-macros/annot-on-class.check

This file was deleted.

8 changes: 0 additions & 8 deletions tests/neg-macros/annot-on-class/Macro_1.scala

This file was deleted.

11 changes: 0 additions & 11 deletions tests/neg-macros/annot-on-class/Test_2.scala

This file was deleted.

16 changes: 0 additions & 16 deletions tests/neg-macros/annot-on-object.check

This file was deleted.

Loading

0 comments on commit 1dc36c6

Please sign in to comment.