summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-10-23 16:26:46 +1000
committerJason Zaugg <jzaugg@gmail.com>2014-10-27 22:34:33 +1000
commit337b52b298be95df0b4b9a4bacb7c4b802620cf6 (patch)
treeb9e211946e905a455082bf56dbd8095a5515b7ff
parent850899b1c9347e804763a258f64d54cef0ffd69a (diff)
downloadscala-337b52b298be95df0b4b9a4bacb7c4b802620cf6.tar.gz
scala-337b52b298be95df0b4b9a4bacb7c4b802620cf6.tar.bz2
scala-337b52b298be95df0b4b9a4bacb7c4b802620cf6.zip
SI-8934 Fix whitebox extractor macros in the pres. compiler
The code that "aligns" patterns and extractors assumes that it can look at the type of the unapply method to figure the arity of the extractor. However, the result type of a whitebox macro does not tell the whole story, only after expanding an application of that macro do we know the result type. During regular compilation, this isn't a problem, as the macro application is expanded to a call to a synthetic unapply: { class anon$1 { def unapply(tree: Any): Option[(Tree, List[Treed])] } new anon$1 }.unapply(<unapply selector>) In the presentation compiler, however, we now use `-Ymacro-expand:discard`, which expands macros only to compute the type of the application (and to allow the macro to issue warnings/errors). The original application is retained in the typechecked tree, modified only by attributing the potentially-sharper type taken from the expanded macro. This was done to improve hyperlinking support in the IDE. This commit passes `sel.tpe` (which is the type computed by the macro expansion) to `unapplyMethodTypes`, rather than using the `finalResultType` of the unapply method. This is tested with a presentation compiler test (which closely mimics the reported bug), and with a pos test that also exercises `-Ymacro-expand:discard`. Prior to this patch, they used to fail with: too many patterns for trait api: expected 1, found 2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala9
-rw-r--r--test/files/pos/t8934a/A_1.scala18
-rw-r--r--test/files/pos/t8934a/Test_2.flags1
-rw-r--r--test/files/pos/t8934a/Test_2.scala12
-rw-r--r--test/files/presentation/quasiquotes.flags0
-rw-r--r--test/files/presentation/t8934.check2
-rw-r--r--test/files/presentation/t8934/Runner.scala27
-rw-r--r--test/files/presentation/t8934/src/Source.scala10
8 files changed, 74 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
index 79f5e3bee8..7fcfdf4868 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
@@ -73,9 +73,7 @@ trait ScalacPatternExpanders {
* Unfortunately the MethodType does not carry the information of whether
* it was unapplySeq, so we have to funnel that information in separately.
*/
- def unapplyMethodTypes(method: Type, isSeq: Boolean): Extractor = {
- val whole = firstParamType(method)
- val result = method.finalResultType
+ def unapplyMethodTypes(whole: Type, result: Type, isSeq: Boolean): Extractor = {
val expanded = (
if (result =:= BooleanTpe) Nil
else typeOfMemberNamedGet(result) match {
@@ -126,9 +124,10 @@ trait ScalacPatternExpanders {
val patterns = newPatterns(args)
val isSeq = sel.symbol.name == nme.unapplySeq
val isUnapply = sel.symbol.name == nme.unapply
+
val extractor = sel.symbol.name match {
- case nme.unapply => unapplyMethodTypes(fn.tpe, isSeq = false)
- case nme.unapplySeq => unapplyMethodTypes(fn.tpe, isSeq = true)
+ case nme.unapply => unapplyMethodTypes(firstParamType(fn.tpe), sel.tpe, isSeq = false)
+ case nme.unapplySeq => unapplyMethodTypes(firstParamType(fn.tpe), sel.tpe, isSeq = true)
case _ => applyMethodTypes(fn.tpe)
}
diff --git a/test/files/pos/t8934a/A_1.scala b/test/files/pos/t8934a/A_1.scala
new file mode 100644
index 0000000000..6c1f29d030
--- /dev/null
+++ b/test/files/pos/t8934a/A_1.scala
@@ -0,0 +1,18 @@
+import language.experimental.macros
+import reflect.macros.whitebox.Context
+
+object Unapply {
+ def impl1(c: Context)(a: c.Tree): c.Tree = {
+ import c.universe._
+ q"(new { def unapply[T](a: String): Option[(Int, String)] = ??? }).unapply($a)"
+ }
+ def unapply(a: Any): Any = macro impl1
+}
+
+object UnapplySeq {
+ def impl1(c: Context)(a: c.Tree): c.Tree = {
+ import c.universe._
+ q"(new { def unapplySeq[T](a: String): Option[(Int, Seq[String])] = ??? }).unapplySeq($a)"
+ }
+ def unapplySeq(a: Any): Any = macro impl1
+}
diff --git a/test/files/pos/t8934a/Test_2.flags b/test/files/pos/t8934a/Test_2.flags
new file mode 100644
index 0000000000..618dfe2b75
--- /dev/null
+++ b/test/files/pos/t8934a/Test_2.flags
@@ -0,0 +1 @@
+-Ystop-after:typer -Ymacro-expand:discard -nowarn
diff --git a/test/files/pos/t8934a/Test_2.scala b/test/files/pos/t8934a/Test_2.scala
new file mode 100644
index 0000000000..e1792ed3c5
--- /dev/null
+++ b/test/files/pos/t8934a/Test_2.scala
@@ -0,0 +1,12 @@
+object Test {
+ "" match {
+ case Unapply(a, b) =>
+ a: Int
+ b: String
+ case UnapplySeq(a, b1, b2) =>
+ a: Int
+ b1: String
+ b2: String
+ }
+}
+// These used to fail `too many patterns` under -Ymacro-expand:discard
diff --git a/test/files/presentation/quasiquotes.flags b/test/files/presentation/quasiquotes.flags
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/presentation/quasiquotes.flags
diff --git a/test/files/presentation/t8934.check b/test/files/presentation/t8934.check
new file mode 100644
index 0000000000..0ece87f808
--- /dev/null
+++ b/test/files/presentation/t8934.check
@@ -0,0 +1,2 @@
+reload: Source.scala
+Test OK
diff --git a/test/files/presentation/t8934/Runner.scala b/test/files/presentation/t8934/Runner.scala
new file mode 100644
index 0000000000..944f458391
--- /dev/null
+++ b/test/files/presentation/t8934/Runner.scala
@@ -0,0 +1,27 @@
+import scala.tools.nsc.interactive.tests.InteractiveTest
+import scala.reflect.internal.util.SourceFile
+import scala.tools.nsc.interactive.Response
+
+object Test extends InteractiveTest {
+
+ override def execute(): Unit = {
+ val src = loadSourceAndWaitUntilTypechecked("Source.scala")
+ checkErrors(src)
+ }
+
+ private def loadSourceAndWaitUntilTypechecked(sourceName: String): SourceFile = {
+ val sourceFile = sourceFiles.find(_.file.name == sourceName).head
+ askReload(List(sourceFile)).get
+ askLoadedTyped(sourceFile).get
+ sourceFile
+ }
+
+ private def checkErrors(source: SourceFile): Unit = compiler.getUnitOf(source) match {
+ case Some(unit) =>
+ val problems = unit.problems.toList
+ if(problems.isEmpty) reporter.println("Test OK")
+ else problems.foreach(problem => reporter.println(problem.msg))
+
+ case None => reporter.println("No compilation unit found for " + source.file.name)
+ }
+}
diff --git a/test/files/presentation/t8934/src/Source.scala b/test/files/presentation/t8934/src/Source.scala
new file mode 100644
index 0000000000..769c8fd38b
--- /dev/null
+++ b/test/files/presentation/t8934/src/Source.scala
@@ -0,0 +1,10 @@
+class Quasi {
+ import reflect.runtime.universe._
+
+ def test: Unit = {
+ (null: Any) match {
+ case q"$foo($bar)" =>
+ }
+ ()
+ }
+}