From 5c2e1eb9cd3a4b258949d54804e141004fb662ab Mon Sep 17 00:00:00 2001 From: Hamza Remmal <56235032+hamzaremmal@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:54:57 +0100 Subject: [PATCH] Add MiniPhase to enter generated symbols from MacroAnnotations --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/transform/PostInlining2.scala | 52 +++++++++++++++++++ tests/neg/i18825.check | 3 ++ tests/neg/i18825/Macro_1.scala | 19 +++++++ tests/neg/i18825/Test_2.scala | 13 +++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/PostInlining2.scala create mode 100644 tests/neg/i18825.check create mode 100644 tests/neg/i18825/Macro_1.scala create mode 100644 tests/neg/i18825/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 22150afad1e8..8e40e23be66a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -7,7 +7,6 @@ import typer.{TyperPhase, RefChecks} import parsing.Parser import Phases.Phase import transform._ -import dotty.tools.backend import backend.jvm.{CollectSuperCalls, GenBCode} import localopt.StringInterpolatorOpt @@ -49,6 +48,7 @@ class Compiler { protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros + List(new PostInlining2) :: // Add mirror support for inlined code List(new PostInlining) :: // Add mirror support for inlined code List(new CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types diff --git a/compiler/src/dotty/tools/dotc/transform/PostInlining2.scala b/compiler/src/dotty/tools/dotc/transform/PostInlining2.scala new file mode 100644 index 000000000000..ff65469f4e50 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PostInlining2.scala @@ -0,0 +1,52 @@ +package dotty.tools.dotc +package transform + +import core.* +import ast.tpd.* +import Contexts.* +import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer + +/** + * ??? + * @author Hamza REMMAL (hamza.remmal@epfl.ch) + */ +class PostInlining2 extends MacroTransform, IdentityDenotTransformer: + thisPhase => + + override def phaseName: String = PostInlining2.name + + override def description: String = PostInlining2.description + + override def changesMembers = true + + def newTransformer(using Context): Transformer = new Transformer: + override def transform(tree: Tree)(using Context): Tree = + super.transform(tree) match + // HR : Check if the owner of the added symbols is correct. + // HR : if yes, and the symbol is not entered. enter it + + /* HR : Questions : + - What symbol are we allowed to change ? Check for now if the content of the tree is consistent + - What happens if we enter a symbol twice ? I have a NamingError reported + - What if we already have that symbol entered ? ignore it... + - Can we merge it with the PostInlining phase ? + - Is there an optimal way to check if a tree was generated by a macro annotation ? + - Define a correct error message below + */ + + case t@TypeDef(_, template: Template) => + for tree <- template.constr :: template.body do + if tree.symbol.owner == t.symbol then + if !t.symbol.asClass.info.decls.exists(_ == tree.symbol) then + tree.symbol.enteredAfter(thisPhase) + else + report.error("Macro added a definition with the wrong owner") + t + case t => t + + + +object PostInlining2: + val name: String = "postInlining2" + val description: String = "check the result of the macro annotation extension" + diff --git a/tests/neg/i18825.check b/tests/neg/i18825.check new file mode 100644 index 000000000000..341b5596fbc9 --- /dev/null +++ b/tests/neg/i18825.check @@ -0,0 +1,3 @@ +error overriding method toString in class Foo of type (): String; + method toString of type (): String cannot override final member method toString in class Foo +1 error found \ No newline at end of file diff --git a/tests/neg/i18825/Macro_1.scala b/tests/neg/i18825/Macro_1.scala new file mode 100644 index 000000000000..5b168ed4b434 --- /dev/null +++ b/tests/neg/i18825/Macro_1.scala @@ -0,0 +1,19 @@ +import scala.annotation.experimental +import scala.annotation.MacroAnnotation +import scala.quoted.* + +@experimental +class toString extends MacroAnnotation : + 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 toStringSym = Symbol.requiredMethod("java.lang.Object.toString") + val toStringOverrideSym = Symbol.newMethod(cls, "toString", toStringSym.info, Flags.Override, Symbol.noSymbol) + val toStringDef = DefDef(toStringOverrideSym, _ => Some(Literal(StringConstant("Hello from macro")))) + val newClassDef = ClassDef.copy(tree)(name, ctr, parents, self, toStringDef :: body) + List(newClassDef) + case _ => + report.error("@toString can only be annotated on class definitions") + tree :: Nil \ No newline at end of file diff --git a/tests/neg/i18825/Test_2.scala b/tests/neg/i18825/Test_2.scala new file mode 100644 index 000000000000..84be37e04533 --- /dev/null +++ b/tests/neg/i18825/Test_2.scala @@ -0,0 +1,13 @@ +import annotation.experimental + +class Foo : + final override def toString(): String = "Hello" + +@experimental +@toString +class AFoo extends Foo //: + //override def toString(): String = "Hello from macro" + +@experimental +@main def run = + println(new AFoo().toString) \ No newline at end of file