summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
blob: e96de6a0d5d68368a19bd1d0fd653230eda92f89 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* NSC -- new Scala compiler
 * Copyright 2005-2007 LAMP/EPFL
 * Copyright 2007 Google Inc. All Rights Reserved.
 * Author: bqe@google.com (Burak Emir)
 */
// $Id$

package scala.tools.nsc.matching

/** Translation of pattern matching
 *
 *  @author Burak Emir
 */
trait TransMatcher { self: transform.ExplicitOuter with PatternNodes with ParallelMatching with CodeFactory =>

  import global.{ typer => _, _ }
  import analyzer.Typer;
  import definitions._
  import symtab.Flags

  var cunit: CompilationUnit = _  // memory leak?
  var resultType: Type = _

  // cache these
  final val settings_debug       = settings.debug.value

  // check special case Seq(p1,...,pk,_*)
  protected def isRightIgnoring(p: ArrayValue): Boolean = {
    def isDefaultStar(tree: Tree): Boolean = tree match {
      case Bind(_, q)                 => isDefaultStar(q)
      case Star(Ident(nme.WILDCARD))  => true
      case _                          => false
    }

    !p.elems.isEmpty && isDefaultStar(p.elems.last)
  }

  /** handles all translation of pattern matching
   */
  def handlePattern(selector: Tree, cases: List[CaseDef], doCheckExhaustive: Boolean, owner: Symbol, handleOuter: Tree => Tree)(implicit typer : Typer): Tree = {
    DBG("****")
    DBG("**** initalize, selector = "+selector+" selector.tpe = "+selector.tpe)
    DBG("****    doCheckExhaustive == "+doCheckExhaustive)

    implicit val theOwner = owner
    implicit val rep = new RepFactory(handleOuter)

    def caseIsOk(c: CaseDef) = c match {
      case CaseDef(_: Apply, _, _)            => true
      case CaseDef(Ident(nme.WILDCARD), _, _) => true
      case _                                  => false
    }
    def doApply(fn: Tree) = (fn.symbol eq selector.tpe.decls.lookup(nme.CONSTRUCTOR)) && (cases forall caseIsOk)

    def processApply(app: Apply): (List[Symbol], List[Tree], Tree) = {
      val Apply(fn, args) = app
      val (tmps, vds) = List.unzip(
        for ((ti, i) <- args.zipWithIndex) yield {
          val v = newVar(ti.pos, cunit.fresh.newName(ti.pos, "tp"), selector.tpe.typeArgs(i))
          if (!doCheckExhaustive) v setFlag Flags.TRANS_FLAG
          (v, typedValDef(v, ti))
        }
      )
      (tmps, vds, ThrowMatchError(selector.pos, copy.Apply(app, fn, tmps map mkIdent)))
    }

    // sets temporaries, variable declarations, and the fail tree
    val (tmps, vds, theFailTree) = selector match {
      case app @ Apply(fn, _) if isTupleType(selector.tpe) && doApply(fn) => processApply(app)
      case _ =>
        val root: Symbol      = newVar(selector.pos, selector.tpe)
        if (!doCheckExhaustive)
          root setFlag Flags.TRANS_FLAG
        val vdef: Tree        = typer.typed(ValDef(root, selector))
        val failTree: Tree    = ThrowMatchError(selector.pos, mkIdent(root))
        (List(root), List(vdef), failTree)
    }

    implicit val fail: Tree = theFailTree
    val irep                = initRep(tmps, cases, rep)
    val mch                 = typer.typed(repToTree(irep))
    var dfatree             = typer.typed(Block(vds, mch))

    for ((cs, bx) <- cases.zipWithIndex)
      if (!rep.isReached(bx)) cunit.error(cs.body.pos, "unreachable code")

    dfatree = rep.cleanup(dfatree)
    resetTrav.traverse(dfatree)
    dfatree
  }

  object resetTrav extends Traverser {
    override def traverse(x: Tree): Unit = x match {
      case (vd: ValDef) => if (vd.symbol hasFlag Flags.SYNTHETIC) {
        vd.symbol resetFlag Flags.TRANS_FLAG
        vd.symbol resetFlag Flags.MUTABLE
      }
      case _ =>
        super.traverse(x)
    }
  }
}