Skip to content

Commit 2a06fb1

Browse files
committed
Enter missing symbols in MacroAnnotations
1 parent f91e5d8 commit 2a06fb1

File tree

12 files changed

+153
-9
lines changed

12 files changed

+153
-9
lines changed

compiler/src/dotty/tools/dotc/transform/Inlining.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import dotty.tools.dotc.staging.StagingLevel
1717
import scala.collection.mutable.ListBuffer
1818

1919
/** Inlines all calls to inline methods that are not in an inline method or a quote */
20-
class Inlining extends MacroTransform {
20+
class Inlining extends MacroTransform, IdentityDenotTransformer {
21+
self =>
2122

2223
import tpd.*
2324

@@ -75,7 +76,7 @@ class Inlining extends MacroTransform {
7576
&& StagingLevel.level == 0
7677
&& MacroAnnotations.hasMacroAnnotation(tree.symbol)
7778
then
78-
val trees = (new MacroAnnotations).expandAnnotations(tree)
79+
val trees = (new MacroAnnotations(self)).expandAnnotations(tree)
7980
val trees1 = trees.map(super.transform)
8081

8182
// Find classes added to the top level from a package object

compiler/src/dotty/tools/dotc/transform/MacroAnnotations.scala

+26-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import dotty.tools.dotc.config.Printers.{macroAnnot => debug}
99
import dotty.tools.dotc.core.Annotations.*
1010
import dotty.tools.dotc.core.Contexts.*
1111
import dotty.tools.dotc.core.Decorators.*
12-
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
12+
import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer
1313
import dotty.tools.dotc.core.Flags.*
1414
import dotty.tools.dotc.core.MacroClassLoader
1515
import dotty.tools.dotc.core.Symbols.*
@@ -23,7 +23,8 @@ import scala.util.control.NonFatal
2323

2424
import java.lang.reflect.InvocationTargetException
2525

26-
class MacroAnnotations:
26+
class MacroAnnotations(phase: IdentityDenotTransformer):
27+
2728
import tpd.*
2829
import MacroAnnotations.*
2930

@@ -58,9 +59,11 @@ class MacroAnnotations:
5859
case (prefixed, newTree :: suffixed) =>
5960
allTrees ++= prefixed
6061
insertedAfter = suffixed :: insertedAfter
61-
prefixed.foreach(checkMacroDef(_, tree, annot))
62-
suffixed.foreach(checkMacroDef(_, tree, annot))
63-
transform.TreeChecker.checkMacroGeneratedTree(tree, newTree)
62+
for prefixedTree <- prefixed do
63+
checkMacroDef(prefixedTree, tree, annot)
64+
for suffixedTree <- suffixed do
65+
checkMacroDef(suffixedTree, tree, annot)
66+
TreeChecker.checkMacroGeneratedTree(tree, newTree)
6467
newTree
6568
case (Nil, Nil) =>
6669
report.error(i"Unexpected `Nil` returned by `(${annot.tree}).transform(..)` during macro expansion", annot.tree.srcPos)
@@ -76,6 +79,7 @@ class MacroAnnotations:
7679
insertedAfter.foreach(allTrees.++=)
7780

7881
val result = allTrees.result()
82+
for tree <- result do enterMissingSymbols(tree)
7983
debug.println(result.map(_.show).mkString("expanded to:\n", "\n", ""))
8084
result
8185

@@ -120,7 +124,7 @@ class MacroAnnotations:
120124

121125
/** Check that this tree can be added by the macro annotation */
122126
private def checkMacroDef(newTree: DefTree, annotatedTree: Tree, annot: Annotation)(using Context) =
123-
transform.TreeChecker.checkMacroGeneratedTree(annotatedTree, newTree)
127+
TreeChecker.checkMacroGeneratedTree(annotatedTree, newTree)
124128
val sym = newTree.symbol
125129
val annotated = annotatedTree.symbol
126130
if sym.isType && !sym.isClass then
@@ -130,6 +134,22 @@ class MacroAnnotations:
130134
else if annotated.isClass && annotated.owner.is(Package) /*&& !sym.isClass*/ then
131135
report.error(i"macro annotation can not add top-level ${sym.showKind}. $annot tried to add $sym.", annot.tree)
132136

137+
/**
138+
* Enter the symbols generated by MacroAnnotations
139+
*/
140+
private def enterMissingSymbols(tree: DefTree)(using Context) = new TreeTraverser {
141+
def traverse(tree: tpd.Tree)(using Context): Unit = tree match
142+
case tdef @ TypeDef(_, template: Template) =>
143+
val isSymbolInDecls = tdef.symbol.asClass.info.decls.toList.toSet
144+
for tree <- template.body do
145+
if tree.symbol.owner != tdef.symbol then
146+
report.error(em"Macro added a definition with the wrong owner - ${tree.symbol.owner} - ${tdef.symbol} in ${tree.source}", tree.srcPos)
147+
else if !isSymbolInDecls(tree.symbol) then
148+
tree.symbol.enteredAfter(phase)
149+
traverseChildren(tree)
150+
case _ => traverseChildren(tree)
151+
}.traverse(tree)
152+
133153
object MacroAnnotations:
134154

135155
/** Is this an annotation that implements `scala.annation.MacroAnnotation` */

tests/neg-macros/i18825.check

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
error overriding method toString in class Foo of type (): String;
3+
method toString of type (): String cannot override final member method toString in class Foo

tests/neg-macros/i18825/Macro_1.scala

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.annotation.experimental
2+
import scala.annotation.MacroAnnotation
3+
import scala.quoted.*
4+
5+
@experimental
6+
class toString extends MacroAnnotation :
7+
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
8+
import quotes.reflect.*
9+
tree match
10+
case ClassDef(name, ctr, parents, self, body) =>
11+
val cls = tree.symbol
12+
val toStringSym = Symbol.requiredMethod("java.lang.Object.toString")
13+
val toStringOverrideSym = Symbol.newMethod(cls, "toString", toStringSym.info, Flags.Override, Symbol.noSymbol)
14+
val toStringDef = DefDef(toStringOverrideSym, _ => Some(Literal(StringConstant("Hello from macro"))))
15+
val newClassDef = ClassDef.copy(tree)(name, ctr, parents, self, toStringDef :: body)
16+
List(newClassDef)
17+
case _ =>
18+
report.error("@toString can only be annotated on class definitions")
19+
tree :: Nil

tests/neg-macros/i18825/Test_2.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// nopos-error
2+
3+
import annotation.experimental
4+
5+
class Foo :
6+
final override def toString(): String = "Hello"
7+
8+
@experimental
9+
@toString
10+
class AFoo extends Foo //:
11+
//override def toString(): String = "Hello from macro"
12+
13+
@experimental
14+
@main def run =
15+
println(new AFoo().toString)

tests/neg-macros/wrong-owner.check

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
-- Error: tests/neg-macros/wrong-owner/Test_2.scala:5:6 ----------------------------------------------------------------
3+
3 |@experimental
4+
4 |@wrongOwner
5+
5 |class Foo // error
6+
|^
7+
|Malformed tree was found while expanding macro with -Xcheck-macros.
8+
| |The tree does not conform to the compiler's tree invariants.
9+
| |
10+
| |Macro was:
11+
| |@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo()
12+
| |
13+
| |The macro returned:
14+
| |@scala.annotation.internal.SourceFile("tests/neg-macros/wrong-owner/Test_2.scala") @wrongOwner @scala.annotation.experimental class Foo() {
15+
| override def toString(): java.lang.String = "Hello from macro"
16+
|}
17+
| |
18+
| |Error:
19+
| |assertion failed: bad owner; method toString has owner class String, expected was class Foo
20+
|owner chain = method toString, class String, package java.lang, package java, package <root>, ctxOwners = class Foo, class Foo, package <empty>, package <empty>, package <empty>, package <root>, package <root>, package <root>, package <root>, package <root>, package <root>, <none>, <none>, <none>, <none>, <none>
21+
| |
22+
|stacktrace available when compiling with `-Ydebug`
23+
| |
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.annotation.experimental
2+
import scala.annotation.MacroAnnotation
3+
import scala.quoted.*
4+
5+
@experimental
6+
class wrongOwner extends MacroAnnotation :
7+
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
8+
import quotes.reflect.*
9+
tree match
10+
case ClassDef(name, ctr, parents, self, body) =>
11+
val cls = tree.symbol
12+
val toStringSym = Symbol.requiredMethod("java.lang.Object.toString")
13+
val toStringOverrideSym = Symbol.newMethod(Symbol.classSymbol("java.lang.String"), "toString", toStringSym.info, Flags.Override, Symbol.noSymbol)
14+
val toStringDef = DefDef(toStringOverrideSym, _ => Some(Literal(StringConstant("Hello from macro"))))
15+
val newClassDef = ClassDef.copy(tree)(name, ctr, parents, self, toStringDef :: body)
16+
List(newClassDef)
17+
case _ =>
18+
report.error("@toString can only be annotated on class definitions")
19+
tree :: Nil
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.annotation.experimental
2+
3+
@experimental
4+
@wrongOwner
5+
class Foo // error

tests/run-macros/i18806.check

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hi

tests/run-macros/i18806/Macro_1.scala

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import scala.annotation.{experimental, MacroAnnotation}
2+
import scala.quoted._
3+
4+
@experimental
5+
class gen extends MacroAnnotation:
6+
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
7+
import quotes.reflect._
8+
tree match
9+
case ClassDef(name, ctr, parents, self, body) =>
10+
val cls = tree.symbol
11+
// val meth = cls.methodMember("foo").head
12+
// val fooTpe = cls.typeRef.memberType(meth)
13+
14+
val overrideTpe = MethodType(Nil)(_ => Nil, _ => defn.StringClass.typeRef)
15+
16+
val fooOverrideSym = Symbol.newMethod(cls, "foo", overrideTpe, Flags.Override, Symbol.noSymbol)
17+
18+
val fooDef = DefDef(fooOverrideSym, _ => Some(Literal(StringConstant("hi"))))
19+
20+
val newClassDef = ClassDef.copy(tree)(name, ctr, parents, self, fooDef :: body)
21+
List(newClassDef)
22+
case _ =>
23+
report.error("Annotation only supports `class`")
24+
List(tree)

tests/run-macros/i18806/Test_2.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.annotation.experimental
2+
3+
class Base:
4+
def foo(): Object = ???
5+
6+
@experimental
7+
@gen
8+
class Sub extends Base
9+
// > override def foo(): String = "hi"
10+
11+
@experimental
12+
@main def Test(): Unit =
13+
val sub = new Sub
14+
println(sub.foo())

tests/run/quotes-add-erased/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class erasedParamsMethod extends MacroAnnotation:
1515
assert(methType.hasErasedParams)
1616
assert(methType.erasedParams == List(true, false))
1717

18-
val methSym = Symbol.newMethod(tree.symbol, "takesErased", methType, Flags.EmptyFlags, Symbol.noSymbol)
18+
val methSym = Symbol.newMethod(tree.symbol, "takesErased", methType, Flags.Override, Symbol.noSymbol)
1919
val methDef = DefDef(methSym, _ => Some(Literal(IntConstant(1))))
2020

2121
val clsDef = ClassDef.copy(tree)(name, ctr, parents, self, methDef :: body)

0 commit comments

Comments
 (0)