From fe6028476931b031e712c37d3e570125b1d034ae Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 31 Dec 2012 15:46:47 +0100 Subject: SI-5923 adapt macros when they are deferred Amazingly enough, the fix for the "macro not expanded" problem was super easy. (And I remember spending a day or two trying to find a quick fix somewhen around Scala Days 2012!) The problem was in the implementation of the macro expansion trigger, which was buried in a chain of if-elif-elif. This meant that macro expansion was mutually exclusive with a lot of important adaptations, e.g. with `instantiate`. More precisely, if an expandee contains an undetparam, its expansion should be delayed until all its undetparams are inferred and then retried later. Sometimes such inference can only happen upon a call to instantiate in one of the elif's coming after the macro expansion elif. However this elif would never be called for expandees, because control flow would always enter the macro expansion branch preceding the inference branch. Consequences of this fix are vast. First of all, we can get rid of the "type parameter must be specified" hack. Secondly and most importantly, we can now remove the `materializeImplicit` method from Implicits and rely on implicit macros to materialize tags for us. (This is a tricky change, and I'll do it later after we merge as much of my pending work as possible). Finally, we learn that the current scheme of interaction between macros, type inference and implicits is, in principle, sound! --- src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala | 6 +----- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 6 ++++-- test/files/neg/t5353.check | 2 +- test/files/neg/t5692a.check | 4 ---- test/files/neg/t5692a.flags | 1 - test/files/neg/t5692a/Macros_1.scala | 6 ------ test/files/neg/t5692a/Test_2.scala | 3 --- test/files/neg/t5692b.check | 4 ---- test/files/neg/t5692b.flags | 1 - test/files/neg/t5692b/Macros_1.scala | 6 ------ test/files/neg/t5692b/Test_2.scala | 3 --- test/files/pos/t5692a.check | 4 ++++ test/files/pos/t5692a.flags | 1 + test/files/pos/t5692a/Macros_1.scala | 6 ++++++ test/files/pos/t5692a/Test_2.scala | 3 +++ test/files/pos/t5692b.check | 4 ++++ test/files/pos/t5692b.flags | 1 + test/files/pos/t5692b/Macros_1.scala | 6 ++++++ test/files/pos/t5692b/Test_2.scala | 3 +++ 19 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 test/files/neg/t5692a.check delete mode 100644 test/files/neg/t5692a.flags delete mode 100644 test/files/neg/t5692a/Macros_1.scala delete mode 100644 test/files/neg/t5692a/Test_2.scala delete mode 100644 test/files/neg/t5692b.check delete mode 100644 test/files/neg/t5692b.flags delete mode 100644 test/files/neg/t5692b/Macros_1.scala delete mode 100644 test/files/neg/t5692b/Test_2.scala create mode 100644 test/files/pos/t5692a.check create mode 100644 test/files/pos/t5692a.flags create mode 100644 test/files/pos/t5692a/Macros_1.scala create mode 100644 test/files/pos/t5692a/Test_2.scala create mode 100644 test/files/pos/t5692b.check create mode 100644 test/files/pos/t5692b.flags create mode 100644 test/files/pos/t5692b/Macros_1.scala create mode 100644 test/files/pos/t5692b/Test_2.scala diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index efe7519d5e..c8b7fcee8f 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -77,11 +77,7 @@ abstract class Pickler extends SubComponent { } if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) { - unit.error(t.pos, t.symbol.typeParams.length match { - case 0 => "macro has not been expanded" - case 1 => "this type parameter must be specified" - case _ => "these type parameters must be specified" - }) + unit.error(t.pos, "macro has not been expanded") return } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a9e2d479db..b0b1341a07 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1061,6 +1061,7 @@ trait Typers extends Modes with Adaptations with Tags { instantiateToMethodType(mt) case _ => + def vanillaAdapt(tree: Tree) = { def shouldInsertApply(tree: Tree) = inAllModes(mode, EXPRmode | FUNmode) && (tree.tpe match { case _: MethodType | _: OverloadedType | _: PolyType => false case _ => applyPossible @@ -1076,8 +1077,6 @@ trait Typers extends Modes with Adaptations with Tags { } if (tree.isType) adaptType() - else if (inExprModeButNot(mode, FUNmode) && treeInfo.isMacroApplication(tree)) - macroExpandApply(this, tree, mode, pt) else if (inAllModes(mode, PATTERNmode | FUNmode)) adaptConstrPattern() else if (shouldInsertApply(tree)) @@ -1206,6 +1205,9 @@ trait Typers extends Modes with Adaptations with Tags { } fallBack } + } + val tree1 = if (inExprModeButNot(mode, FUNmode) && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else tree + if (tree == tree1) vanillaAdapt(tree1) else tree1 } } diff --git a/test/files/neg/t5353.check b/test/files/neg/t5353.check index 75e2435600..bc3f77a4d6 100644 --- a/test/files/neg/t5353.check +++ b/test/files/neg/t5353.check @@ -1,4 +1,4 @@ -t5353.scala:2: error: this type parameter must be specified +t5353.scala:2: error: macro has not been expanded def f(x: Boolean) = if (x) Array("abc") else Array() ^ one error found diff --git a/test/files/neg/t5692a.check b/test/files/neg/t5692a.check deleted file mode 100644 index 7fbfb5dba7..0000000000 --- a/test/files/neg/t5692a.check +++ /dev/null @@ -1,4 +0,0 @@ -Test_2.scala:2: error: this type parameter must be specified - def x = Macros.foo - ^ -one error found diff --git a/test/files/neg/t5692a.flags b/test/files/neg/t5692a.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/files/neg/t5692a.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros \ No newline at end of file diff --git a/test/files/neg/t5692a/Macros_1.scala b/test/files/neg/t5692a/Macros_1.scala deleted file mode 100644 index 06b5a3de36..0000000000 --- a/test/files/neg/t5692a/Macros_1.scala +++ /dev/null @@ -1,6 +0,0 @@ -import scala.reflect.macros.Context - -object Macros { - def impl[T](c: Context) = c.literalUnit - def foo[T] = macro impl[T] -} \ No newline at end of file diff --git a/test/files/neg/t5692a/Test_2.scala b/test/files/neg/t5692a/Test_2.scala deleted file mode 100644 index 08d510cc6f..0000000000 --- a/test/files/neg/t5692a/Test_2.scala +++ /dev/null @@ -1,3 +0,0 @@ -class Test { - def x = Macros.foo -} \ No newline at end of file diff --git a/test/files/neg/t5692b.check b/test/files/neg/t5692b.check deleted file mode 100644 index 16796826b4..0000000000 --- a/test/files/neg/t5692b.check +++ /dev/null @@ -1,4 +0,0 @@ -Test_2.scala:2: error: these type parameters must be specified - def x = Macros.foo - ^ -one error found diff --git a/test/files/neg/t5692b.flags b/test/files/neg/t5692b.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/files/neg/t5692b.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros \ No newline at end of file diff --git a/test/files/neg/t5692b/Macros_1.scala b/test/files/neg/t5692b/Macros_1.scala deleted file mode 100644 index b28d19f903..0000000000 --- a/test/files/neg/t5692b/Macros_1.scala +++ /dev/null @@ -1,6 +0,0 @@ -import scala.reflect.macros.Context - -object Macros { - def impl[T, U](c: Context) = c.literalUnit - def foo[T, U] = macro impl[T, U] -} \ No newline at end of file diff --git a/test/files/neg/t5692b/Test_2.scala b/test/files/neg/t5692b/Test_2.scala deleted file mode 100644 index 08d510cc6f..0000000000 --- a/test/files/neg/t5692b/Test_2.scala +++ /dev/null @@ -1,3 +0,0 @@ -class Test { - def x = Macros.foo -} \ No newline at end of file diff --git a/test/files/pos/t5692a.check b/test/files/pos/t5692a.check new file mode 100644 index 0000000000..7fbfb5dba7 --- /dev/null +++ b/test/files/pos/t5692a.check @@ -0,0 +1,4 @@ +Test_2.scala:2: error: this type parameter must be specified + def x = Macros.foo + ^ +one error found diff --git a/test/files/pos/t5692a.flags b/test/files/pos/t5692a.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/pos/t5692a.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/pos/t5692a/Macros_1.scala b/test/files/pos/t5692a/Macros_1.scala new file mode 100644 index 0000000000..06b5a3de36 --- /dev/null +++ b/test/files/pos/t5692a/Macros_1.scala @@ -0,0 +1,6 @@ +import scala.reflect.macros.Context + +object Macros { + def impl[T](c: Context) = c.literalUnit + def foo[T] = macro impl[T] +} \ No newline at end of file diff --git a/test/files/pos/t5692a/Test_2.scala b/test/files/pos/t5692a/Test_2.scala new file mode 100644 index 0000000000..08d510cc6f --- /dev/null +++ b/test/files/pos/t5692a/Test_2.scala @@ -0,0 +1,3 @@ +class Test { + def x = Macros.foo +} \ No newline at end of file diff --git a/test/files/pos/t5692b.check b/test/files/pos/t5692b.check new file mode 100644 index 0000000000..16796826b4 --- /dev/null +++ b/test/files/pos/t5692b.check @@ -0,0 +1,4 @@ +Test_2.scala:2: error: these type parameters must be specified + def x = Macros.foo + ^ +one error found diff --git a/test/files/pos/t5692b.flags b/test/files/pos/t5692b.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/pos/t5692b.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/pos/t5692b/Macros_1.scala b/test/files/pos/t5692b/Macros_1.scala new file mode 100644 index 0000000000..b28d19f903 --- /dev/null +++ b/test/files/pos/t5692b/Macros_1.scala @@ -0,0 +1,6 @@ +import scala.reflect.macros.Context + +object Macros { + def impl[T, U](c: Context) = c.literalUnit + def foo[T, U] = macro impl[T, U] +} \ No newline at end of file diff --git a/test/files/pos/t5692b/Test_2.scala b/test/files/pos/t5692b/Test_2.scala new file mode 100644 index 0000000000..08d510cc6f --- /dev/null +++ b/test/files/pos/t5692b/Test_2.scala @@ -0,0 +1,3 @@ +class Test { + def x = Macros.foo +} \ No newline at end of file -- cgit v1.2.3