summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-10-03 22:53:59 +0000
committerPaul Phillips <paulp@improving.org>2010-10-03 22:53:59 +0000
commita03131199109b51b4a3051aa2c016d516bec6e36 (patch)
treed4f6eb62ea09145f8a005ea85a54eb9bd3fc332d
parent2c59afc2c15ddcd124fd175eb30c0cdf9d98463b (diff)
downloadscala-a03131199109b51b4a3051aa2c016d516bec6e36.tar.gz
scala-a03131199109b51b4a3051aa2c016d516bec6e36.tar.bz2
scala-a03131199109b51b4a3051aa2c016d516bec6e36.zip
Work on the pattern matcher.
patches for #3887 and #3888, but I determined that I could achieve the same effect by deleting a bunch of code, so I did. This left only a few lines in TransMatch, so I eliminated it, which led me to remember that many places still reference non-existent phase transmatch, so those were updated. Notes: * This swaps equality tests on stable identifier patterns. They have never conformed to the spec (as noted long ago in ticket #785) which says "The pattern matches any value v such that r == v" whereas until now the test being performed was v == r. * An issue was introduced with specialization in that the implementation of "isTupleType" in Definitions relied upon sym == TupleClass(elems.length). This test is untrue for specialized tuples, causing mysterious behavior because only some tuples are specialized. There is now "isTupleTypeOrSubtype" although it seems likely the former implementation is unnecessary. The issue is sidestepped if one uses "getProductArgs" to retrieve the element types because it sifts through the base types for the Product symbol. Closes #3887 and #3888, review by dmharrah.
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala4
-rw-r--r--src/compiler/scala/tools/nsc/matching/Matrix.scala32
-rw-r--r--src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/ParallelMatching.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/Patterns.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/TransMatcher.scala91
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala10
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala2
-rwxr-xr-xsrc/library/scala/reflect/generic/Trees.scala15
-rw-r--r--src/manual/scala/man1/scalac.scala3
-rw-r--r--test/files/neg/patmatexhaust.check2
-rw-r--r--test/files/run/t3887.scala16
-rw-r--r--test/files/run/t3888.scala26
-rw-r--r--test/files/run/tuple-match.check8
-rw-r--r--test/files/run/tuple-match.scala27
20 files changed, 138 insertions, 124 deletions
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala
index 6cda588419..20ed9fc6d4 100644
--- a/src/compiler/scala/tools/ant/Scalac.scala
+++ b/src/compiler/scala/tools/ant/Scalac.scala
@@ -91,7 +91,7 @@ class Scalac extends MatchingTask with ScalacShared {
/** Defines valid values for properties that refer to compiler phases. */
object CompilerPhase extends PermissibleValue {
val values = List("namer", "typer", "pickler", "uncurry", "tailcalls",
- "transmatch", "explicitouter", "erasure", "lambdalift",
+ "explicitouter", "erasure", "lambdalift",
"flatten", "constructors", "mixin", "icode", "jvm",
"terminal")
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index e0c1453c3a..55e9ec0b89 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -520,7 +520,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory
case tp: TypeRef if (tp.typeSymbol == definitions.ByNameParamClass) =>
nameBuffer append "⇒ "
appendType0(tp.args.head)
- case tp: TypeRef if (definitions.isTupleType(tp)) =>
+ case tp: TypeRef if (definitions.isTupleTypeOrSubtype(tp)) =>
nameBuffer append '('
appendTypes0(tp.args, ", ")
nameBuffer append ')'
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala
index 9b9d9a36f1..e0d678b961 100644
--- a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala
@@ -15,7 +15,7 @@ trait CompletionOutput {
self: Completion =>
import global._
- import definitions.{ NothingClass, AnyClass, isTupleType, isFunctionType, isRepeatedParamType }
+ import definitions.{ NothingClass, AnyClass, isTupleTypeOrSubtype, isFunctionType, isRepeatedParamType }
/** Reducing fully qualified noise for some common packages.
*/
@@ -49,7 +49,7 @@ trait CompletionOutput {
def typeToString(tp: Type): String = relativize(
tp match {
case x if isFunctionType(x) => functionString(x)
- case x if isTupleType(x) => tupleString(x)
+ case x if isTupleTypeOrSubtype(x) => tupleString(x)
case x if isRepeatedParamType(x) => typeToString(x.typeArgs.head) + "*"
case mt @ MethodType(_, _) => methodTypeToString(mt)
case x => x.toString
diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala
index 0bd9caf506..fa3e7371a7 100644
--- a/src/compiler/scala/tools/nsc/matching/Matrix.scala
+++ b/src/compiler/scala/tools/nsc/matching/Matrix.scala
@@ -76,7 +76,36 @@ trait Matrix extends MatrixAdditions {
states. Otherwise,the error state is used after its reference count has been incremented.
**/
+ /** Handles all translation of pattern matching.
+ */
+ def handlePattern(
+ selector: Tree, // tree being matched upon (called scrutinee after this)
+ cases: List[CaseDef], // list of cases in the match
+ isChecked: Boolean, // whether exhaustiveness checking is enabled (disabled with @unchecked)
+ context: MatrixContext): Tree =
+ {
+ import context._
+ log("handlePattern: selector.tpe = " + selector.tpe)
+
+ // sets up top level match
+ val matrixInit: MatrixInit = {
+ val v = copyVar(selector, isChecked, selector.tpe, "temp")
+ MatrixInit(List(v), cases, atPos(selector.pos)(MATCHERROR(v.ident)))
+ }
+
+ val matrix = new MatchMatrix(context) { lazy val data = matrixInit }
+ val rep = matrix.expansion // expands casedefs and assigns name
+ val mch = typer typed rep.toTree // executes algorithm, converts tree to DFA
+ val dfatree = typer typed Block(matrixInit.valDefs, mch) // packages into a code block
+
+ // redundancy check
+ matrix.targets filter (_.isNotReached) foreach (cs => cunit.error(cs.body.pos, "unreachable code"))
+ // optimize performs squeezing and resets any remaining TRANS_FLAGs
+ tracing("handlePattern(" + selector + ")", matrix optimize dfatree)
+ }
+
case class MatrixContext(
+ cunit: CompilationUnit, // current unit
handleOuter: Tree => Tree, // for outer pointer
typer: Typer, // a local typer
owner: Symbol, // the current owner
@@ -164,7 +193,8 @@ trait Matrix extends MatrixAdditions {
override def toString() = "%s: %s = %s".format(lhs, lhs.info, rhs)
}
- // val NoPatternVar = PatternVar(NoSymbol, EmptyTree, false)
+
+ def newName(pos: Position, s: String) = cunit.fresh.newName(pos, s)
/** Sets the rhs to EmptyTree, which makes the valDef ignored in Scrutinee.
*/
diff --git a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
index fbfe00d2c1..47df90d0d0 100644
--- a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
+++ b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
@@ -27,6 +27,8 @@ trait MatrixAdditions extends ast.TreeDSL
private[matching] trait Squeezer {
self: MatrixContext =>
+ private val settings_squeeze = settings.Xsqueeze.value == "on"
+
def squeezedBlockPVs(pvs: List[PatternVar], exp: Tree): Tree =
squeezedBlock(pvs map (_.valDef), exp)
diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
index 3606bda6e0..61a8b81820 100644
--- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
+++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
@@ -547,7 +547,7 @@ trait ParallelMatching extends ast.TreeDSL
owner.newLabel(scrut.pos, newName(scrut.pos, "failCont%")) setInfo MethodType(Nil, labelBody.tpe)
lazy val cond =
- handleOuter(scrut.id MEMBER_== rhs)
+ handleOuter(rhs MEMBER_== scrut.id )
lazy val success = remake(List(
rest.rows.head.insert2(List(NoPattern), head.boundVariables, scrut.sym),
diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala
index a135655b9d..26ad296614 100644
--- a/src/compiler/scala/tools/nsc/matching/Patterns.scala
+++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala
@@ -348,7 +348,7 @@ trait Patterns extends ast.TreeDSL {
def apply(x: Apply): Pattern = {
val Apply(fn, args) = x
def isModule = x.symbol.isModule || x.tpe.termSymbol.isModule
- def isTuple = isTupleType(fn.tpe)
+ def isTuple = isTupleTypeOrSubtype(fn.tpe)
if (fn.isType) {
if (isTuple) TuplePattern(x)
diff --git a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
deleted file mode 100644
index be455b00b0..0000000000
--- a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
+++ /dev/null
@@ -1,91 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2010 LAMP/EPFL
- * Copyright 2007 Google Inc. All Rights Reserved.
- * Author: bqe@google.com (Burak Emir)
- */
-
-package scala.tools.nsc
-package matching
-
-import symtab.SymbolTable
-import transform.ExplicitOuter
-import java.io.{ StringWriter, PrintWriter }
-import scala.reflect.NameTransformer.decode
-import PartialFunction._
-
-/** Translation of pattern matching
- *
- * @author Burak Emir
- */
-trait TransMatcher extends ast.TreeDSL {
- self: ExplicitOuter with ParallelMatching =>
-
- import global.{ typer => _, _ }
- import analyzer.Typer
- import definitions._
- import CODE._
- import Debug.{ TRACE, tracing }
-
- // cunit is set to the current unit in ExplicitOuter's transformUnit,
- // and nulled out afterward to avoid leaking.
- var cunit: CompilationUnit = _
-
- def newName(pos: Position, s: String) = cunit.fresh.newName(pos, s)
-
- final val settings_squeeze = settings.Xsqueeze.value == "on"
-
- /** Handles all translation of pattern matching.
- */
- def handlePattern(
- selector: Tree, // tree being matched upon (called scrutinee after this)
- cases: List[CaseDef], // list of cases in the match
- isChecked: Boolean, // whether exhaustiveness checking is enabled (disabled with @unchecked)
- context: MatrixContext): Tree =
- {
- import context._
-
- def matchError(obj: Tree) = atPos(selector.pos)(MATCHERROR(obj))
- def caseIsOk(c: CaseDef) = cond(c.pat) { case _: Apply | Ident(nme.WILDCARD) => true }
- def rootTypes = selector.tpe.typeArgs
-
- // this appears to be an attempt at optimizing when all case defs are constructor
- // patterns, but I don't think it's correct.
- def doApply(fn: Tree): Boolean =
- (fn.symbol eq (selector.tpe.decls lookup nme.CONSTRUCTOR)) &&
- (cases forall caseIsOk)
-
- // For x match { ... we start with a single root
- def singleMatch(): MatrixInit = {
- val v = copyVar(selector, isChecked)
- context.MatrixInit(List(v), cases, matchError(v.ident))
- }
-
- // For (x, y, z) match { ... we start with multiple roots, called tpXX.
- def tupleMatch(app: Apply): MatrixInit = {
- val Apply(fn, args) = app
- val vs = args zip rootTypes map { case (arg, tpe) => copyVar(arg, isChecked, tpe, "tp") }
- def merror = matchError(treeCopy.Apply(app, fn, vs map (_.ident)))
-
- context.MatrixInit(vs, cases, merror)
- }
-
- // sets up top level variables and algorithm input
- val matrixInit = tracing("matrixInit", selector match {
- case app @ Apply(fn, _) if isTupleType(selector.tpe) && doApply(fn) => tupleMatch(app)
- case _ => singleMatch()
- })
-
- val matrix = new MatchMatrix(context) { lazy val data = matrixInit }
- val rep = matrix.expansion // expands casedefs and assigns name
- val mch = typer typed rep.toTree // executes algorithm, converts tree to DFA
- val dfatree = typer typed Block(matrixInit.valDefs, mch) // packages into a code block
-
- // redundancy check
- matrix.targets filter (_.isNotReached) foreach (cs => cunit.error(cs.body.pos, "unreachable code"))
- // optimize performs squeezing and resets any remaining TRANS_FLAGs
- val res = matrix optimize dfatree
- TRACE("handlePattern(%s, ...) = %s", selector, res)
- res
- }
-}
-
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 4b5632f7d8..461394a475 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -288,9 +288,15 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val AbstractFunctionClass = mkArityArray("runtime.AbstractFunction", MaxFunctionArity, 0)
def tupleField(n: Int, j: Int) = getMember(TupleClass(n), "_" + j)
- def isTupleType(tp: Type): Boolean = cond(tp.normalize) {
- case TypeRef(_, sym, elems) => elems.length <= MaxTupleArity && sym == TupleClass(elems.length)
+ def isTupleType(tp: Type): Boolean = isTupleType(tp, false)
+ def isTupleTypeOrSubtype(tp: Type): Boolean = isTupleType(tp, true)
+ private def isTupleType(tp: Type, subtypeOK: Boolean): Boolean = cond(tp.normalize) {
+ case t @ TypeRef(_, sym, elems) =>
+ elems.length <= MaxTupleArity &&
+ (sym == TupleClass(elems.length) ||
+ subtypeOK && !tp.isHigherKinded && (t <:< TupleClass(elems.length).tpe))
}
+
def tupleType(elems: List[Type]) =
if (elems.length <= MaxTupleArity) {
val sym = TupleClass(elems.length)
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 2535a38eb3..21da19c526 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -1852,7 +1852,7 @@ A type's typeSymbol should never be inspected directly.
return "=> " + args(0).toString
if (isFunctionType(this))
return normalize.typeArgs.init.mkString("(", ", ", ")") + " => " + normalize.typeArgs.last
- if (isTupleType(this))
+ if (isTupleTypeOrSubtype(this))
return normalize.typeArgs.mkString("(", ", ", if (normalize.typeArgs.length == 1) ",)" else ")")
if (sym.isAliasType && (prefixChain exists (_.termSymbol hasFlag SYNTHETIC))) {
val normed = normalize;
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index 0739deec5f..932b05c250 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -47,9 +47,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
private def typedWithPos(pos: Position)(tree: Tree) =
localTyper typed { atPos(pos)(tree) }
- override def transformUnit(unit: CompilationUnit) =
- unit.body = transform(unit.body)
-
/** A value class is defined to be only Java-compatible values: unit is
* not part of it, as opposed to isValueClass in definitions. scala.Int is
* a value class, java.lang.Integer is not. */
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index c4251ffeea..1403377367 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -9,7 +9,7 @@ package transform
import symtab._
import Flags.{ CASE => _, _ }
import scala.collection.mutable.ListBuffer
-import matching.{ TransMatcher, Patterns, ParallelMatching }
+import matching.{ Patterns, ParallelMatching }
/** This class ...
*
@@ -17,7 +17,6 @@ import matching.{ TransMatcher, Patterns, ParallelMatching }
* @version 1.0
*/
abstract class ExplicitOuter extends InfoTransform
- with TransMatcher
with Patterns
with ParallelMatching
with TypingTransformers
@@ -357,7 +356,7 @@ abstract class ExplicitOuter extends InfoTransform
var nselector = transform(selector)
def makeGuardDef(vs: List[Symbol], guard: Tree) = {
- val gdname = newName(guard.pos, "gd")
+ val gdname = unit.fresh.newName(guard.pos, "gd")
val method = currentOwner.newMethod(tree.pos, gdname) setFlag SYNTHETIC
val fmls = vs map (_.tpe)
val tpe = new MethodType(method newSyntheticValueParams fmls, BooleanClass.tpe)
@@ -400,7 +399,7 @@ abstract class ExplicitOuter extends InfoTransform
}
val t = atPos(tree.pos) {
- val context = MatrixContext(transform, localTyper, currentOwner, tree.tpe)
+ val context = MatrixContext(currentRun.currentUnit, transform, localTyper, currentOwner, tree.tpe)
val t_untyped = handlePattern(nselector, ncases, checkExhaustive, context)
/* if @switch annotation is present, verify the resulting tree is a Match */
@@ -488,7 +487,7 @@ abstract class ExplicitOuter extends InfoTransform
})
super.transform(treeCopy.Apply(tree, sel, outerVal :: args))
- // TransMatch hook
+ // entry point for pattern matcher translation
case mch: Match =>
matchTranslation(mch)
@@ -508,9 +507,7 @@ abstract class ExplicitOuter extends InfoTransform
/** The transformation method for whole compilation units */
override def transformUnit(unit: CompilationUnit) {
- cunit = unit
atPhase(phase.next) { super.transformUnit(unit) }
- cunit = null
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index b7a70bdf4b..7ca0f089f1 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -158,7 +158,7 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
override def transformUnit(unit: CompilationUnit) {
freeMutableVars.clear
freeLocalsTraverser(unit.body)
- unit.body = transform(unit.body)
+ super.transformUnit(unit)
}
private var nprinted = 0
diff --git a/src/library/scala/reflect/generic/Trees.scala b/src/library/scala/reflect/generic/Trees.scala
index c880a335de..87ce15dd24 100755
--- a/src/library/scala/reflect/generic/Trees.scala
+++ b/src/library/scala/reflect/generic/Trees.scala
@@ -296,23 +296,23 @@ trait Trees { self: Universe =>
case class Block(stats: List[Tree], expr: Tree)
extends TermTree
- /** Case clause in a pattern match, eliminated by TransMatch
+ /** Case clause in a pattern match, eliminated during explicitouter
* (except for occurrences in switch statements)
*/
case class CaseDef(pat: Tree, guard: Tree, body: Tree)
extends Tree
- /** Alternatives of patterns, eliminated by TransMatch, except for
+ /** Alternatives of patterns, eliminated by explicitouter, except for
* occurrences in encoded Switch stmt (=remaining Match(CaseDef(...))
*/
case class Alternative(trees: List[Tree])
extends TermTree
- /** Repetition of pattern, eliminated by TransMatch */
+ /** Repetition of pattern, eliminated by explicitouter */
case class Star(elem: Tree)
extends TermTree
- /** Bind of a variable to a rhs pattern, eliminated by TransMatch
+ /** Bind of a variable to a rhs pattern, eliminated by explicitouter
*
* @param name
* @param body
@@ -343,12 +343,11 @@ trait Trees { self: Universe =>
extends TermTree
/** <p>
- * Pattern matching expression (before <code>TransMatch</code>)
- * Switch statements (after TransMatch)
+ * Pattern matching expression (before explicitouter)
+ * Switch statements (after explicitouter)
* </p>
* <p>
- * After <code>TransMatch</code>, cases will satisfy the following
- * constraints:
+ * After explicitouter, cases will satisfy the following constraints:
* </p>
* <ul>
* <li>all guards are EmptyTree,</li>
diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala
index d2f6183356..e22abdd1eb 100644
--- a/src/manual/scala/man1/scalac.scala
+++ b/src/manual/scala/man1/scalac.scala
@@ -242,9 +242,6 @@ object scalac extends Command {
MItalic("uncurry"),
"uncurry function types and applications"),
Definition(
- MItalic("transmatch"),
- "translate match expressions"),
- Definition(
MItalic("lambdalift"),
"lambda lifter"),
Definition(
diff --git a/test/files/neg/patmatexhaust.check b/test/files/neg/patmatexhaust.check
index ca769300c0..83c4279b0a 100644
--- a/test/files/neg/patmatexhaust.check
+++ b/test/files/neg/patmatexhaust.check
@@ -13,7 +13,7 @@ missing combination Kult Kult
missing combination Qult Qult
def ma3(x:Mult) = (x,x) match { // not exhaustive
- ^
+ ^
patmatexhaust.scala:49: warning: match is not exhaustive!
missing combination Gp
missing combination Gu
diff --git a/test/files/run/t3887.scala b/test/files/run/t3887.scala
new file mode 100644
index 0000000000..16ce983efc
--- /dev/null
+++ b/test/files/run/t3887.scala
@@ -0,0 +1,16 @@
+object Test {
+ def main(args: Array[String]) {
+ assert( matchPair(1) )
+ assert( !matchPair(2) )
+ }
+
+ def matchPair(i: Int) = {
+ (i, "abc") match {
+ case this.option1 => true
+ case _ => false
+ }
+ }
+
+ val option1: (Int, String) = (1, "abc")
+
+}
diff --git a/test/files/run/t3888.scala b/test/files/run/t3888.scala
new file mode 100644
index 0000000000..ecdf87e925
--- /dev/null
+++ b/test/files/run/t3888.scala
@@ -0,0 +1,26 @@
+object Test {
+
+ val T1 = new P
+ private[this] val T2 = T1
+
+ def m1 =
+ (1, 2) match {
+ case T1 => true
+ case _ => false
+ }
+
+ def m2 =
+ (1, 2) match {
+ case T2 => true
+ case _ => false
+ }
+
+ def main(args: Array[String]) = {
+ assert( m1 )
+ assert( m2 )
+ }
+}
+
+class P extends Pair(1, 1) {
+ override def equals(x: Any) = true
+} \ No newline at end of file
diff --git a/test/files/run/tuple-match.check b/test/files/run/tuple-match.check
new file mode 100644
index 0000000000..0c1ea0005b
--- /dev/null
+++ b/test/files/run/tuple-match.check
@@ -0,0 +1,8 @@
+4, #3
+4, #2
+4, #4
+3, #2
+2, #2
+1, #1
+FOUR
+THREE
diff --git a/test/files/run/tuple-match.scala b/test/files/run/tuple-match.scala
new file mode 100644
index 0000000000..fcaefbff5b
--- /dev/null
+++ b/test/files/run/tuple-match.scala
@@ -0,0 +1,27 @@
+object Test {
+ val FOUR = (-1, -2, -3, "bingo donkey vegas")
+ val THREE = (-1, -2, -3)
+
+ def f(x: Any) = x match {
+ case FOUR => "FOUR"
+ case (_, _, 3, _) => "4, #3"
+ case (_, 2, _, _) => "4, #2"
+ case (_, 2, _) => "3, #2"
+ case Tuple1(1) => "1, #1"
+ case (_, _, _, 4) => "4, #4"
+ case THREE => "THREE"
+ case (_, 2) => "2, #2"
+ case _ => "default"
+ }
+
+ def main(args: Array[String]): Unit = {
+ println(f((1, 2, 3, 4)))
+ println(f((1, 2, 30, 4)))
+ println(f((1, 20, 30, 4)))
+ println(f((1, 2, 3)))
+ println(f((1, 2)))
+ println(f(Tuple1(1)))
+ println(f((-1, -2, -3, "bingo donkey vegas")))
+ println(f((-1, -2, -3)))
+ }
+}