summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
blob: b3ab6429b05431ae7ee721a9d492bc1604f4b744 (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
103
104
105
106
107
108
109
110
111
112
113
114
115
/* NSC -- new Scala compiler
 * Copyright 2005-2009 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
  final val settings_squeeze = settings.Xsqueeze.value == "on"

  // 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)
    val flags = if (doCheckExhaustive) Nil else List(Flags.TRANS_FLAG)

    def caseIsOk(c: CaseDef) = c match {
      case CaseDef(_: Apply, _, _)            => true
      case CaseDef(Ident(nme.WILDCARD), _, _) => true
      case _                                  => false
    }
    def doApply(fn: Tree): Boolean =
      (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 {
          // These type parameter vars were not being set as synthetic.
          // Temporarily noted on the off chance it was intentional.
          val v = newVar(ti.pos, cunit.fresh.newName(ti.pos, "tp"), selector.tpe.typeArgs(i), flags)
          (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, flags)
        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(irep.toTree)
    var dfatree = typer.typed(Block(vds, mch))

    // cannot use squeezedBlock because of side-effects, see t275
    for ((cs, bx) <- cases.zipWithIndex)
      if (!rep.isReached(bx)) cunit.error(cs.body.pos, "unreachable code")

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

  private object resetTraverser 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 | Flags.MUTABLE)
        }
      case _ =>
        super.traverse(x)
    }
  }
}