From 03a41a6de78aca38fd3830ea4f9181ee8b68fe93 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] Enter missing symbols in MacroAnnotations --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 - .../dotty/tools/dotc/transform/Inlining.scala | 5 +-- .../dotc/transform/MacroAnnotations.scala | 32 +++++++++++++++---- tests/neg-macros/i18825.check | 3 ++ tests/neg-macros/i18825/Macro_1.scala | 19 +++++++++++ tests/neg-macros/i18825/Test_2.scala | 15 +++++++++ 6 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 tests/neg-macros/i18825.check create mode 100644 tests/neg-macros/i18825/Macro_1.scala create mode 100644 tests/neg-macros/i18825/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 22150afad1e8..3972ef654b72 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 diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 10f73fa94e08..e36061a79820 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -17,7 +17,8 @@ import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer /** Inlines all calls to inline methods that are not in an inline method or a quote */ -class Inlining extends MacroTransform { +class Inlining extends MacroTransform, IdentityDenotTransformer { + self => import tpd._ @@ -75,7 +76,7 @@ class Inlining extends MacroTransform { && StagingLevel.level == 0 && MacroAnnotations.hasMacroAnnotation(tree.symbol) then - val trees = (new MacroAnnotations).expandAnnotations(tree) + val trees = (new MacroAnnotations(self)).expandAnnotations(tree) val trees1 = trees.map(super.transform) // Find classes added to the top level from a package object diff --git a/compiler/src/dotty/tools/dotc/transform/MacroAnnotations.scala b/compiler/src/dotty/tools/dotc/transform/MacroAnnotations.scala index 7878b5795703..464f73fc486c 100644 --- a/compiler/src/dotty/tools/dotc/transform/MacroAnnotations.scala +++ b/compiler/src/dotty/tools/dotc/transform/MacroAnnotations.scala @@ -9,7 +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.DenotTransformers.DenotTransformer +import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.MacroClassLoader import dotty.tools.dotc.core.Symbols.* @@ -23,7 +23,8 @@ import scala.util.control.NonFatal import java.lang.reflect.InvocationTargetException -class MacroAnnotations: +class MacroAnnotations(phase: IdentityDenotTransformer): + import tpd.* import MacroAnnotations.* @@ -58,9 +59,14 @@ class MacroAnnotations: case (prefixed, newTree :: suffixed) => allTrees ++= prefixed insertedAfter = suffixed :: insertedAfter - prefixed.foreach(checkMacroDef(_, tree, annot)) - suffixed.foreach(checkMacroDef(_, tree, annot)) - transform.TreeChecker.checkMacroGeneratedTree(tree, newTree) + for prefixedTree <- prefixed do + checkMacroDef(prefixedTree, tree, annot) + enterMissingSymbols(prefixedTree) + for suffixedTree <- suffixed do + checkMacroDef(suffixedTree, tree, annot) + enterMissingSymbols(suffixedTree) + TreeChecker.checkMacroGeneratedTree(tree, newTree) + enterMissingSymbols(newTree) newTree case (Nil, Nil) => report.error(i"Unexpected `Nil` returned by `(${annot.tree}).transform(..)` during macro expansion", annot.tree.srcPos) @@ -120,7 +126,7 @@ class MacroAnnotations: /** Check that this tree can be added by the macro annotation */ private def checkMacroDef(newTree: DefTree, annotatedTree: Tree, annot: Annotation)(using Context) = - transform.TreeChecker.checkMacroGeneratedTree(annotatedTree, newTree) + TreeChecker.checkMacroGeneratedTree(annotatedTree, newTree) val sym = newTree.symbol val annotated = annotatedTree.symbol if sym.isType && !sym.isClass then @@ -130,6 +136,20 @@ class MacroAnnotations: else if annotated.isClass && annotated.owner.is(Package) /*&& !sym.isClass*/ then report.error(i"macro annotation can not add top-level ${sym.showKind}. $annot tried to add $sym.", annot.tree) + private def enterMissingSymbols(tree: MemberDef)(using Context) = new TreeTraverser { + def traverse(tree: tpd.Tree)(using Context): Unit = tree match + case tdef @ TypeDef(_, template: Template) => + //for tree <- template.constr :: template.body do + val isSymbolInDecls = tdef.symbol.asClass.info.decls.toList.toSet + for tree <- template.body do + if tree.symbol.owner != tdef.symbol then + report.error(em"Macro added a definition with the wrong owner - ${tree.symbol.owner} - ${tdef.symbol} in ${tree.source}") + else if !isSymbolInDecls(tree.symbol) then + tree.symbol.enteredAfter(phase) + traverseChildren(tree) // Taverse before or after dealing with this class? + case _ => traverseChildren(tree) + }.traverse(tree) + object MacroAnnotations: /** Is this an annotation that implements `scala.annation.MacroAnnotation` */ diff --git a/tests/neg-macros/i18825.check b/tests/neg-macros/i18825.check new file mode 100644 index 000000000000..0269f9880828 --- /dev/null +++ b/tests/neg-macros/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 diff --git a/tests/neg-macros/i18825/Macro_1.scala b/tests/neg-macros/i18825/Macro_1.scala new file mode 100644 index 000000000000..5b168ed4b434 --- /dev/null +++ b/tests/neg-macros/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-macros/i18825/Test_2.scala b/tests/neg-macros/i18825/Test_2.scala new file mode 100644 index 000000000000..0d784b4bf93e --- /dev/null +++ b/tests/neg-macros/i18825/Test_2.scala @@ -0,0 +1,15 @@ +// nopos-error + +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