aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TyperState.scala
blob: 8b5e2c85ec05a7f8b571025529cd610550d745b2 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package dotty.tools
package dotc
package core

import Types._
import Flags._
import Contexts._
import util.SimpleMap
import reporting._
import printing.{Showable, Printer}
import printing.Texts._
import collection.mutable
import annotation.elidable

class TyperState(val reporter: Reporter) extends DotClass with Showable {

  /** The current constraint set */
  def constraint: Constraint = new Constraint(SimpleMap.Empty)

  def uninstVars = constraint.uninstVars

  /** A map that records for instantiated type vars their instance type.
   *  Used only in a temporary way for contexts that may be retracted
   *  without also retracting the type var as a whole.
   */
  def instType(tvar: TypeVar): Type = constraint.at(tvar.origin) match {
    case _: TypeBounds => NoType
    case tp => tp
  }

  def constraint_=(c: Constraint): Unit = {}

  def fresh(isCommittable: Boolean): TyperState = this

  def commit()(implicit ctx: Context): Unit = unsupported("commit")
  def isCommittable: Boolean = false

  @elidable(elidable.FINER)
  def checkConsistent(implicit ctx: Context) = ()

  @elidable(elidable.FINER)
  def enableChecking(b: Boolean): Boolean = true

  def withCheckingDisabled[T](op: => T)(implicit ctx: Context): T = op

  override def toText(printer: Printer): Text = "ImmutableTyperState"
}

class MutableTyperState(previous: TyperState, reporter: Reporter, override val isCommittable: Boolean)
extends TyperState(reporter) {

  private var myConstraint: Constraint = previous.constraint
  private var checkingEnabled: Boolean = true

  override def constraint = myConstraint
  override def constraint_=(c: Constraint) = {
    myConstraint = c
    checkConsistent()
  }

  override def fresh(isCommittable: Boolean): TyperState =
    new MutableTyperState(this, new StoreReporter, isCommittable)

  /** Commit typer state so that its information is copied into current typer state
   *  In addition (1) the owning state of undetermined or temporarily instantiated
   *  type variables changes from this typer state to the current one. (2) Variables
   *  that were temporarily instantiated in the current typer state are permanently
   *  instantiated instead.
   */
  override def commit()(implicit ctx: Context) = {
    checkConsistent
    val targetState = ctx.typerState
    val prev = targetState.enableChecking(false)
    targetState.constraint = constraint
    targetState.enableChecking(prev)

    val toCollect = new mutable.ListBuffer[PolyType]
    constraint foreachTypeVar { tvar =>
      if (tvar.owningState eq this)
        tvar.owningState = targetState
      if (!tvar.inst.exists) {
        val inst = instType(tvar)
        if (inst.exists && (tvar.owningState eq targetState)) {
          tvar.inst = inst
          val poly = tvar.origin.binder
          if (targetState.constraint.isRemovable(poly)) toCollect += poly
        }
      }
    }
    for (poly <- toCollect)
      targetState.constraint = targetState.constraint.remove(poly)

    targetState.checkConsistent // !!! DEBUG

    reporter.flush()
  }

  @elidable(elidable.FINER)
  def checkConsistent(show: Showable => String = MutableTyperState.toStr): Unit = if (checkingEnabled) {
    def err(msg: String, what: Showable) = s"$msg: ${show(what)}\n${show(this)}"
    for (tvar <- uninstVars)
      assert(constraint contains tvar.origin, err("unconstrained type var", tvar.origin))
    if (isCommittable) {
      val undetParams = uninstVars map (_.origin)
      for (param <- constraint.domainParams)
        assert(undetParams contains param, err("junk constraint on", param))
    }
  }

  @elidable(elidable.FINER)
  override def checkConsistent(implicit ctx: Context): Unit = checkConsistent(_.show)

  @elidable(elidable.FINER)
  override def enableChecking(b: Boolean) = {
    val prev = checkingEnabled
    checkingEnabled = b
    prev
  }

  override def withCheckingDisabled[T](op: => T)(implicit ctx: Context): T = {
    val prev = enableChecking(false)
    var thrown = false
    try op
    catch {
      case ex: Throwable =>
        thrown = true
        throw ex
    }
    finally {
      enableChecking(prev)
      if (!thrown) checkConsistent
    }
  }

  override def toText(printer: Printer): Text = {
    val header: Text = "Typer state:"
    val uninstVarsText =
      " uninstVars: " ~
      Text(uninstVars map (_.toText(printer)), ", ") ~ "."
    val constrainedText =
      " constrained types: " ~ constraint.constrainedTypesText(printer) ~ "."
    val constraintText =
      " constraint: " ~ constraint.constraintText(3, printer)
    Text.lines(List(header, uninstVarsText, constrainedText, constraintText))
  }
}

object MutableTyperState {
  private def toStr(x: Any) = x.toString
}