From 348c8fac9f897f9661f84e32949b8a4e0c99e93a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 25 Dec 2012 02:37:31 +0100 Subject: adds c.introduceTopLevel The first in the family of mutators for the global symbol table, `introduceTopLevel` is capable of creating synthetic top-level classes and modules. The addition of nme.EMPTY_PACKAGE_NAME is necessary to let programmers insert definitions into the empty package. That's explicitly discouraged in the docs, but at times might come in handy. This patch introduce workarounds to avoid incompatibilities with SBT. First of all SBT doesn't like VirtualFiles having JFile set to null. Secondly SBT gets confused when someone depends on synthetic files added by c.introduceTopLevel. Strictly speaking these problems require changes to SBT, and that will be done later. However the main target of the patch is paradise/macros, which needs to be useful immediately, therefore we apply workarounds. --- .../pos/annotated-treecopy/Impls_Macros_1.scala | 2 +- .../attachments-typed-another-ident/Impls_1.scala | 2 +- .../pos/attachments-typed-ident/Impls_1.scala | 2 +- .../files/run/macro-duplicate/Impls_Macros_1.scala | 6 +-- test/files/run/macro-toplevel-companion-a.check | 0 test/files/run/macro-toplevel-companion-a.flags | 1 + .../Impls_Macros_1.scala | 14 ++++++ .../run/macro-toplevel-companion-a/Test_2.scala | 8 ++++ test/files/run/macro-toplevel-companion-b.check | 4 ++ test/files/run/macro-toplevel-companion-b.flags | 1 + .../Impls_Macros_1.scala | 15 +++++++ .../run/macro-toplevel-companion-b/Test_2.scala | 11 +++++ test/files/run/macro-toplevel-companion-c.check | 3 ++ test/files/run/macro-toplevel-companion-c.flags | 1 + test/files/run/macro-toplevel-companion-c.scala | 51 ++++++++++++++++++++++ test/files/run/macro-toplevel.check | 2 + test/files/run/macro-toplevel/Macros_1.scala | 15 +++++++ test/files/run/macro-toplevel/Test_2.scala | 6 +++ test/files/run/t6548.scala | 2 +- 19 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 test/files/run/macro-toplevel-companion-a.check create mode 100644 test/files/run/macro-toplevel-companion-a.flags create mode 100644 test/files/run/macro-toplevel-companion-a/Impls_Macros_1.scala create mode 100644 test/files/run/macro-toplevel-companion-a/Test_2.scala create mode 100644 test/files/run/macro-toplevel-companion-b.check create mode 100644 test/files/run/macro-toplevel-companion-b.flags create mode 100644 test/files/run/macro-toplevel-companion-b/Impls_Macros_1.scala create mode 100644 test/files/run/macro-toplevel-companion-b/Test_2.scala create mode 100644 test/files/run/macro-toplevel-companion-c.check create mode 100644 test/files/run/macro-toplevel-companion-c.flags create mode 100644 test/files/run/macro-toplevel-companion-c.scala create mode 100644 test/files/run/macro-toplevel.check create mode 100644 test/files/run/macro-toplevel/Macros_1.scala create mode 100644 test/files/run/macro-toplevel/Test_2.scala (limited to 'test') diff --git a/test/files/pos/annotated-treecopy/Impls_Macros_1.scala b/test/files/pos/annotated-treecopy/Impls_Macros_1.scala index d92fbca380..cf58bc3dfd 100644 --- a/test/files/pos/annotated-treecopy/Impls_Macros_1.scala +++ b/test/files/pos/annotated-treecopy/Impls_Macros_1.scala @@ -21,7 +21,7 @@ object Macros { // normalize argument name var b1 = new Transformer { override def transform(tree: Tree): Tree = tree match { - case Ident(x) if (x==n) => Ident(newTermName("_arg")) + case Ident(x) if (x==n) => Ident(TermName("_arg")) case tt @ TypeTree() if tt.original != null => TypeTree(tt.tpe) setOriginal transform(tt.original) // without the fix to LazyTreeCopier.Annotated, we would need to uncomment the line below to make the macro work // that's because the pattern match in the input expression gets expanded into Typed(, TypeTree()) diff --git a/test/files/pos/attachments-typed-another-ident/Impls_1.scala b/test/files/pos/attachments-typed-another-ident/Impls_1.scala index 957bafc6ae..c3f541075e 100644 --- a/test/files/pos/attachments-typed-another-ident/Impls_1.scala +++ b/test/files/pos/attachments-typed-another-ident/Impls_1.scala @@ -6,7 +6,7 @@ object MyAttachment object Macros { def impl(c: Context) = { import c.universe._ - val ident = Ident(newTermName("bar")) updateAttachment MyAttachment + val ident = Ident(TermName("bar")) updateAttachment MyAttachment assert(ident.attachments.get[MyAttachment.type].isDefined, ident.attachments) val typed = c.typeCheck(ident) assert(typed.attachments.get[MyAttachment.type].isDefined, typed.attachments) diff --git a/test/files/pos/attachments-typed-ident/Impls_1.scala b/test/files/pos/attachments-typed-ident/Impls_1.scala index cc40893a93..c382cabc59 100644 --- a/test/files/pos/attachments-typed-ident/Impls_1.scala +++ b/test/files/pos/attachments-typed-ident/Impls_1.scala @@ -6,7 +6,7 @@ object MyAttachment object Macros { def impl(c: Context) = { import c.universe._ - val ident = Ident(newTermName("bar")) updateAttachment MyAttachment + val ident = Ident(TermName("bar")) updateAttachment MyAttachment assert(ident.attachments.get[MyAttachment.type].isDefined, ident.attachments) val typed = c.typeCheck(ident) assert(typed.attachments.get[MyAttachment.type].isDefined, typed.attachments) diff --git a/test/files/run/macro-duplicate/Impls_Macros_1.scala b/test/files/run/macro-duplicate/Impls_Macros_1.scala index de81923330..af80147a90 100644 --- a/test/files/run/macro-duplicate/Impls_Macros_1.scala +++ b/test/files/run/macro-duplicate/Impls_Macros_1.scala @@ -10,11 +10,11 @@ object Macros { case Template(_, _, ctor :: defs) => val defs1 = defs collect { case ddef @ DefDef(mods, name, tparams, vparamss, tpt, body) => - val future = Select(Select(Select(Ident(newTermName("scala")), newTermName("concurrent")), newTermName("package")), newTermName("future")) - val Future = Select(Select(Ident(newTermName("scala")), newTermName("concurrent")), newTypeName("Future")) + val future = Select(Select(Select(Ident(TermName("scala")), TermName("concurrent")), TermName("package")), TermName("future")) + val Future = Select(Select(Ident(TermName("scala")), TermName("concurrent")), TypeName("Future")) val tpt1 = if (tpt.isEmpty) tpt else AppliedTypeTree(Future, List(tpt)) val body1 = Apply(future, List(body)) - val name1 = newTermName("async" + name.toString.capitalize) + val name1 = TermName("async" + name.toString.capitalize) DefDef(mods, name1, tparams, vparamss, tpt1, body1) } Template(Nil, emptyValDef, ctor +: defs ::: defs1) diff --git a/test/files/run/macro-toplevel-companion-a.check b/test/files/run/macro-toplevel-companion-a.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/files/run/macro-toplevel-companion-a.flags b/test/files/run/macro-toplevel-companion-a.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-toplevel-companion-a.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-a/Impls_Macros_1.scala b/test/files/run/macro-toplevel-companion-a/Impls_Macros_1.scala new file mode 100644 index 0000000000..23e8694ddc --- /dev/null +++ b/test/files/run/macro-toplevel-companion-a/Impls_Macros_1.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +object Macros { + def impl(c: Context) = { + import c.universe._ + val synthetic = reify{ class C { override def toString = "C" }; object C { implicit val c = new C } }.tree + val defs = synthetic.asInstanceOf[Block].stats.asInstanceOf[List[ImplDef]] + if (c.topLevelRef(TypeName("C")).isEmpty) c.introduceTopLevel(nme.EMPTY_PACKAGE_NAME.toString, defs: _*) + c.literalUnit + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-a/Test_2.scala b/test/files/run/macro-toplevel-companion-a/Test_2.scala new file mode 100644 index 0000000000..78b65b5b1f --- /dev/null +++ b/test/files/run/macro-toplevel-companion-a/Test_2.scala @@ -0,0 +1,8 @@ +import Macros._ + +object Test extends App { + foo; + implicitly[C]; + foo; + implicitly[C]; +} \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-b.check b/test/files/run/macro-toplevel-companion-b.check new file mode 100644 index 0000000000..bd30dc75d3 --- /dev/null +++ b/test/files/run/macro-toplevel-companion-b.check @@ -0,0 +1,4 @@ +reflective compilation has failed: + +Companions 'class C' and 'object C' must be defined in same file: + Found in and diff --git a/test/files/run/macro-toplevel-companion-b.flags b/test/files/run/macro-toplevel-companion-b.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-toplevel-companion-b.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-b/Impls_Macros_1.scala b/test/files/run/macro-toplevel-companion-b/Impls_Macros_1.scala new file mode 100644 index 0000000000..f30adc2965 --- /dev/null +++ b/test/files/run/macro-toplevel-companion-b/Impls_Macros_1.scala @@ -0,0 +1,15 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +object Macros { + def impl(c: Context) = { + import c.universe._ + val Block(List(cdef: ClassDef), _) = reify{ class C }.tree + val classRef = c.topLevelRef(TypeName("C")) orElse c.introduceTopLevel(nme.EMPTY_PACKAGE_NAME.toString, cdef) + val Block(List(mdef: ModuleDef), _) = reify{ object C }.tree + val moduleRef = c.topLevelRef(TermName("C")) orElse c.introduceTopLevel(nme.EMPTY_PACKAGE_NAME.toString, mdef) + c.literalUnit + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-b/Test_2.scala b/test/files/run/macro-toplevel-companion-b/Test_2.scala new file mode 100644 index 0000000000..ca202d053f --- /dev/null +++ b/test/files/run/macro-toplevel-companion-b/Test_2.scala @@ -0,0 +1,11 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.{ToolBox, ToolBoxError} +import Macros._ + +object Test extends App { + val tb = cm.mkToolBox() + try tb.compile(Select(Ident(TermName("Macros")), TermName("foo"))) + catch { case ToolBoxError(message, _) => println("""macroSynthetic-.*?\.scala""".r.replaceAllIn(message, "")) } +} \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-c.check b/test/files/run/macro-toplevel-companion-c.check new file mode 100644 index 0000000000..8b422c2061 --- /dev/null +++ b/test/files/run/macro-toplevel-companion-c.check @@ -0,0 +1,3 @@ +error: Companions 'class C' and 'object C' must be defined in same file: + Found in and newSource1 + diff --git a/test/files/run/macro-toplevel-companion-c.flags b/test/files/run/macro-toplevel-companion-c.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-toplevel-companion-c.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/run/macro-toplevel-companion-c.scala b/test/files/run/macro-toplevel-companion-c.scala new file mode 100644 index 0000000000..0e99903158 --- /dev/null +++ b/test/files/run/macro-toplevel-companion-c.scala @@ -0,0 +1,51 @@ +import scala.tools.partest._ +import java.io._ + +object Test extends DirectTest { + def code = ??? + + def macros_1 = """ + package test + + import scala.reflect.macros.Context + import language.experimental.macros + + object Macros { + def impl(c: Context) = { + import c.universe._ + val Block(List(cdef: ClassDef), _) = reify{ class C }.tree + val ref = c.topLevelRef(TypeName("test.C")) orElse c.introduceTopLevel("test", cdef) + c.literalUnit + } + + def foo = macro impl + } + """ + def compileMacros() = { + val classpath = List(sys.props("partest.lib"), sys.props("partest.reflect")) mkString sys.props("path.separator") + compileString(newCompiler("-language:experimental.macros", "-cp", classpath, "-d", testOutput.path))(macros_1) + } + + def test_2 = """ + package test + object C { Macros.foo } + """ + def compileTest() = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(test_2) + } + + def show(): Unit = { + // redirect err to string, for logging + val prevErr = System.err + val baos = new ByteArrayOutputStream() + System.setErr(new PrintStream(baos)) + log("Compiling Macros_1...") + if (compileMacros()) { + log("Compiling Test_2...") + if (compileTest()) log("Success!") else log("Failed...") + } + println("""macroSynthetic-.*?\.scala""".r.replaceAllIn(baos.toString, "")) + System.setErr(prevErr) + } +} \ No newline at end of file diff --git a/test/files/run/macro-toplevel.check b/test/files/run/macro-toplevel.check new file mode 100644 index 0000000000..257c3764fd --- /dev/null +++ b/test/files/run/macro-toplevel.check @@ -0,0 +1,2 @@ +I've been created from Macros.foo +I've been created from Macros.foo diff --git a/test/files/run/macro-toplevel/Macros_1.scala b/test/files/run/macro-toplevel/Macros_1.scala new file mode 100644 index 0000000000..f681c86735 --- /dev/null +++ b/test/files/run/macro-toplevel/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +object Macros { + def impl(c: Context) = { + import c.universe._ + val msg = "I've been created from " + c.macroApplication + val Block(List(synthetic: ClassDef), _) = reify{ class SomeUniqueName { def hello = c.literal(msg).splice } }.tree + val ref = c.topLevelRef(synthetic.name) orElse c.introduceTopLevel(nme.EMPTY_PACKAGE_NAME.toString, synthetic) + c.Expr[String](Select(Apply(Select(New(ref), nme.CONSTRUCTOR), List()), TermName("hello"))) + } + + def foo = macro impl + def foo2 = macro impl +} diff --git a/test/files/run/macro-toplevel/Test_2.scala b/test/files/run/macro-toplevel/Test_2.scala new file mode 100644 index 0000000000..eee2d6ae13 --- /dev/null +++ b/test/files/run/macro-toplevel/Test_2.scala @@ -0,0 +1,6 @@ +import Macros._ + +object Test extends App { + println(Macros.foo) + println(Macros.foo2) +} \ No newline at end of file diff --git a/test/files/run/t6548.scala b/test/files/run/t6548.scala index be3eb5b932..b4d09fd8f6 100644 --- a/test/files/run/t6548.scala +++ b/test/files/run/t6548.scala @@ -8,5 +8,5 @@ class Bean { object Test extends App { println(cm.staticClass("Bean").isCaseClass) - println(typeOf[Bean].declaration(newTermName("value")).annotations) + println(typeOf[Bean].declaration(TermName("value")).annotations) } -- cgit v1.2.3