aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TyperState.scala
blob: 8ee219cecac0f26d6d92d41a4b5861bcb9402b51 (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
151
152
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 annotation.elidable

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

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

  /** The currently uninstantiated TypeVars */
  def undetVars: Set[TypeVar] = Set()

  /** 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: SimpleMap[TypeVar, Type] = SimpleMap.Empty

  def constraint_=(c: Constraint): Unit = {}
  def undetVars_=(vs: Set[TypeVar]): Unit = unsupported("undetVars_=")
  def instType_=(m: SimpleMap[TypeVar, Type]): Unit = unsupported("instType_=")

  def fresh(committable: Boolean): TyperState = this

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

  @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, committable: Boolean)
extends TyperState(reporter) {

  private var myConstraint: Constraint = previous.constraint
  private var myUndetVars: Set[TypeVar] = previous.undetVars
  private var myInstType: SimpleMap[TypeVar, Type] = previous.instType
  private var checkingEnabled: Boolean = committable

  override def constraint = myConstraint
  override def undetVars = myUndetVars
  override def instType = myInstType

  override def constraint_=(c: Constraint) = {
    myConstraint = c
    checkConsistent()
  }
  override def undetVars_=(vs: Set[TypeVar]) = {
    myUndetVars = vs
    checkConsistent()
  }
  override def instType_=(m: SimpleMap[TypeVar, Type]): Unit = myInstType = m

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

  /** 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.undetVars = undetVars
    targetState.instType = instType
    targetState.enableChecking(prev)

    def adjustOwningState(tvar: TypeVar) =
      if (tvar.owningState eq this) tvar.owningState = targetState
    undetVars foreach adjustOwningState
    instType foreachKey { tvar =>
      adjustOwningState(tvar)
      if (tvar.owningState == targetState) {
        tvar.inst = instType(tvar)
        targetState.instType = targetState.instType remove tvar
      }
    }
    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 <- undetVars)
      assert(constraint(tvar.origin).exists, err("unconstrained type var", tvar))
    val undetParams = undetVars map (_.origin)
    for (param <- constraint.domainParams)
      assert(undetParams contains param, err("junk constraint on", param))
    instType.foreachKey { tvar =>
      assert(undetVars contains tvar, err("junk instType on", tvar))
    }
  }

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

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

  override def withCheckingDisabled[T](op: => T)(implicit ctx: Context): T = {
    val prev = enableChecking(false)
    try op
    finally {
      enableChecking(prev)
      checkConsistent
    }
  }

  override def toText(printer: Printer): Text = {
    val header: Text = "Typer state:"
    val undetVarsText =
      " undetVars: " ~
      Text(undetVars map (_.toText(printer)), ", ") ~ "."
    val constrainedText =
      " constrained types: " ~ constraint.constrainedTypesText(printer) ~ "."
    val constraintText =
      " constraint: " ~ constraint.constraintText(3, printer)
    val instTypeText =
      " instType: " ~
      Text(instType.map2((k, v) => s"${k.toText(printer)} -> ${v.toText(printer)}"), ", ") ~ "."
    Text.lines(List(header, undetVarsText, constrainedText, constraintText, instTypeText))
  }
}

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