summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Shabalin <denys.shabalin@typesafe.com>2014-03-06 17:45:49 +0200
committerDenys Shabalin <denys.shabalin@typesafe.com>2014-03-10 20:35:22 +0200
commit6dbd770b41ae125c4c11158c687f68452b09e51f (patch)
treec93c5fb38b857171215077a26151bf1ea3843d2d
parent7f07d44bcc97ba8435e8c77393554571c9a006ad (diff)
downloadscala-6dbd770b41ae125c4c11158c687f68452b09e51f.tar.gz
scala-6dbd770b41ae125c4c11158c687f68452b09e51f.tar.bz2
scala-6dbd770b41ae125c4c11158c687f68452b09e51f.zip
SI-8366 make partial function and match trees disjoint
Previously one could match a partial function with match quasiquote: scala> val q"$scrutinee match { case ..$cases }" = q"{ case Foo => Bar }" scrutinee: universe.Tree = <empty> cases: List[universe.CaseDef] = List(case Foo => Bar) This was quite annoying as it leaked encoding of partial functions as Match trees with empty tree in place of scrutinee. This commit make sure that matches and partial functions are disjoint and don't match one another (while preserving original encoding under the hood out of sight of the end user.)
-rw-r--r--bincompat-backward.whitelist.conf5
-rw-r--r--bincompat-forward.whitelist.conf11
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala6
-rw-r--r--src/reflect/scala/reflect/api/Internals.scala8
-rw-r--r--src/reflect/scala/reflect/internal/ReificationSupport.scala19
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala14
-rw-r--r--test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala6
8 files changed, 58 insertions, 12 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index e82c0fd16d..ae31c286f3 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -122,6 +122,11 @@ filter {
{
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticSelectTerm"
problemName=MissingMethodProblem
+ },
+ // see SI-8366
+ {
+ matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticPartialFunction"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index f9bd0441a2..3eba1effc1 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -130,6 +130,15 @@ filter {
{
matchName="scala.reflect.api.Internals$ReificationSupportApi$SyntacticSelectTypeExtractor"
problemName=MissingClassProblem
- }
+ },
+ // see SI-8366
+ {
+ matchName="scala.reflect.api.Internals$ReificationSupportApi$SyntacticPartialFunctionExtractor"
+ problemName=MissingClassProblem
+ },
+ {
+ matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticPartialFunction"
+ problemName=MissingMethodProblem
+ }
]
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 850030e9ba..1b8b0686e8 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -199,6 +199,10 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticEmptyTypeTree)
case SyntacticImport(expr, selectors) =>
reifyBuildCall(nme.SyntacticImport, expr, selectors)
+ case SyntacticPartialFunction(cases) =>
+ reifyBuildCall(nme.SyntacticPartialFunction, cases)
+ case SyntacticMatch(scrutinee, cases) =>
+ reifyBuildCall(nme.SyntacticMatch, scrutinee, cases)
case Q(tree) if fillListHole.isDefinedAt(tree) =>
mirrorBuildCall(nme.SyntacticBlock, fillListHole(tree))
case Q(other) =>
@@ -211,8 +215,6 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticBlock, Nil)
case Try(block, catches, finalizer) =>
reifyBuildCall(nme.SyntacticTry, block, catches, finalizer)
- case Match(selector, cases) =>
- reifyBuildCall(nme.SyntacticMatch, selector, cases)
case CaseDef(pat, guard, body) if fillListHole.isDefinedAt(body) =>
mirrorCall(nme.CaseDef, reify(pat), reify(guard), mirrorBuildCall(nme.SyntacticBlock, fillListHole(body)))
// parser emits trees with scala package symbol to ensure
diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala
index d2e742726d..9faba7efd7 100644
--- a/src/reflect/scala/reflect/api/Internals.scala
+++ b/src/reflect/scala/reflect/api/Internals.scala
@@ -762,9 +762,15 @@ trait Internals { self: Universe =>
def unapply(lst: List[List[Tree]]): Option[List[List[T]]]
}
+ val SyntacticPartialFunction: SyntacticPartialFunctionExtractor
+ trait SyntacticPartialFunctionExtractor {
+ def apply(cases: List[Tree]): Match
+ def unapply(tree: Match): Option[List[CaseDef]]
+ }
+
val SyntacticMatch: SyntacticMatchExtractor
trait SyntacticMatchExtractor {
- def apply(selector: Tree, cases: List[Tree]): Match
+ def apply(scrutinee: Tree, cases: List[Tree]): Match
def unapply(tree: Match): Option[(Tree, List[CaseDef])]
}
diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala
index dfe1811ff0..9a130337b4 100644
--- a/src/reflect/scala/reflect/internal/ReificationSupport.scala
+++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala
@@ -850,9 +850,24 @@ trait ReificationSupport { self: SymbolTable =>
case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case")
}
+ object SyntacticPartialFunction extends SyntacticPartialFunctionExtractor {
+ def apply(cases: List[Tree]): Match = Match(EmptyTree, mkCases(cases))
+ def unapply(tree: Match): Option[List[CaseDef]] = tree match {
+ case Match(EmptyTree, cases) => Some(cases)
+ case _ => None
+ }
+ }
+
object SyntacticMatch extends SyntacticMatchExtractor {
- def apply(selector: Tree, cases: List[Tree]) = Match(selector, mkCases(cases))
- def unapply(tree: Match): Option[(Tree, List[CaseDef])] = Match.unapply(tree)
+ def apply(scrutinee: Tree, cases: List[Tree]) = {
+ require(scrutinee.nonEmpty, "match's scrutinee may not be empty")
+ Match(scrutinee, mkCases(cases))
+ }
+
+ def unapply(tree: Match): Option[(Tree, List[CaseDef])] = tree match {
+ case Match(scrutinee, cases) if scrutinee.nonEmpty => Some((scrutinee, cases))
+ case _ => None
+ }
}
object SyntacticTry extends SyntacticTryExtractor {
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 640e0606c7..10959ff41f 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -630,6 +630,7 @@ trait StdNames {
val SyntacticNew: NameType = "SyntacticNew"
val SyntacticObjectDef: NameType = "SyntacticObjectDef"
val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef"
+ val SyntacticPartialFunction: NameType = "SyntacticPartialFunction"
val SyntacticPatDef: NameType = "SyntacticPatDef"
val SyntacticSelectType: NameType = "SyntacticSelectType"
val SyntacticSelectTerm: NameType = "SyntacticSelectTerm"
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
index 74d0d54ea8..7447037399 100644
--- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -95,12 +95,6 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
body1 ≈ body && cond1 ≈ cond
}
- property("unquote trees into alternative") = forAll { (c: Tree, A: Tree, B: Tree) =>
- q"$c match { case $A | $B => }" ≈
- Match(c, List(
- CaseDef(Alternative(List(A, B)), EmptyTree, Literal(Constant(())))))
- }
-
def blockInvariant(quote: Tree, trees: List[Tree]) =
quote ≈ (trees match {
case Nil => q"{}"
@@ -303,4 +297,12 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
property("SI-8385 b") = test {
assertEqAst(q"(() => ())()", "(() => ())()")
}
+
+ property("match scrutinee may not be empty") = test {
+ assertThrows[IllegalArgumentException] {
+ val scrutinee = q""
+ val cases = List(cq"_ =>")
+ q"$scrutinee match { case ..$cases }"
+ }
+ }
}
diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
index f558a2f078..993ceea8e9 100644
--- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
@@ -211,4 +211,10 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction
val q"$f[..$targs]" = tq"foo[bar]"
}
}
+
+ property("match doesn't match partial function") = test {
+ assertThrows[MatchError] {
+ val q"$_ match { case ..$_ }" = q"{ case _ => }"
+ }
+ }
}