aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/References.scala
blob: 0922a2e2ec3413a766710172057d267383fc5396 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
package dotty.tools.dotc
package core

import Denotations.Denotation
import Contexts.Context
import Names.Name
import Names.TypeName
import Periods.containsPeriod
import Symbols.NoSymbol
import Symbols.Symbol
import Types._
import Flags._


/** Classes that implement references and sets of references
 */
object References {

  /** The signature of a reference.
   *  Overloaded references with the same name are distinguished by
   *  their signatures. A signature is a list of the fully qualified names
   *  of the type symbols of the erasure of the parameters of the
   *  reference. For instance a reference to the definition
   *
   *      def f(x: Int)(y: List[String]): String
   *
   *  would have signature
   *
   *      List("scala.Int".toTypeName, "scala.collection.immutable.List".toTypeName)
   */
  type Signature = List[TypeName]

  /** The signature of a val or parameterless def, as opposed
   *  to List(), which is the signature of a zero-parameter def.
   */
  val NullSignature = List(Names.EmptyTypeName)

  /** A reference is the result of resolving a name (either simple identifier or select).
   *
   *  Reference has two subclasses: OverloadedRef and SymRef.
   *
   *  A SymRef refers to a `symbol` and a type (`info`) that the symbol has
   *  when referred through this reference.
   *
   *  References (`SymRef`s) can be combined with `&` and `|`.
   *  & is conjunction, | is disjunction.
   *
   *  `&` will create an overloaded reference from two
   *  non-overloaded references if their signatures differ.
   *  Analogously `|` of two references with different signatures will give
   *  an empty reference `NoRef`.
   *
   *  A reference might refer to `NoSymbo`. This is the case if the reference
   *  was produced from a disjunction of two references with different symbols
   *  and there was no common symbol in a superclass that could substitute for
   *  both symbols. Here is an example:
   *
   *  Say, we have:
   *
   *    class A { def f: A }
   *    class B { def f: B }
   *    val x: A | B = if (???) new A else new B
   *    val y = x.f
   *
   *  Then the reference of `y` is `SymRef(NoSymbol, A | B)`.
   */
  abstract class Reference {

    /** The referenced symbol, exists only for non-overloaded references */
    def symbol: Symbol =
      throw new UnsupportedOperationException(this.getClass + ".symbol")

    /** The type info of the reference, exists only for non-overloaded references */
    def info: Type =
      throw new UnsupportedOperationException(this.getClass+".info")

    /** Is this a reference to a type symbol? */
    def isType: Boolean = false

    /** The signature of the reference */
    def signature: Signature =
      throw new UnsupportedOperationException(this.getClass+".signature")

    def exists: Boolean = true

    def isValid(implicit ctx: Context): Boolean

    /** Form a reference by conjoining with reference `that` */
    def & (that: Reference)(implicit ctx: Context): Reference =
      if (this eq that) this
      else if (!this.exists) that
      else if (!that.exists) this
      else that match {
        case that @ SymRef(sym2, info2) =>
          val r = mergeRef(this, that)
          if (r ne NoRef) r else OverloadedRef(this, that)
        case that @ OverloadedRef(ref1, ref2) =>
          this & ref1 & ref2
      }

    /** Try to merge ref1 and ref2 without adding a new signature.
     *  If unsuccessful, return NoRef.
     */
    private def mergeRef(ref1: Reference, ref2: SymRef)(implicit ctx: Context): Reference = ref1 match {
      case ref1 @ OverloadedRef(ref11, ref12) =>
        val r1 = mergeRef(ref11, ref2)
        if (r1 ne NoRef) r1 else mergeRef(ref12, ref2)
      case ref1 @ SymRef(sym1, info1) =>
        if (ref1 eq ref2) ref1
        else if (ref1.signature == ref2.signature) {
          val SymRef(sym2, info2) = ref2
          def isEligible(sym1: Symbol, sym2: Symbol) =
            if (sym1.isType) !sym1.isClass
            else sym1.isConcrete || sym2.isDeferred
          def normalize(info: Type) =
            if (isType) info.bounds else info
          val sym1Eligible = isEligible(sym1, sym2)
          val sym2Eligible = isEligible(sym2, sym1)
          val bounds1 = normalize(info1)
          val bounds2 = normalize(info2)
          if (sym2Eligible && bounds2 <:< bounds1) ref2
          else if (sym1Eligible && bounds1 <:< bounds2) ref1
          else new JointSymRef(if (sym2Eligible) sym2 else sym1, bounds1 & bounds2)
        } else NoRef
    }

    def | (that: Reference)(pre: Type)(implicit ctx: Context): Reference = {

      def lubSym(sym1: Symbol, sym2: Symbol): Symbol = {
        def qualifies(sym: Symbol) =
          (sym isAccessibleFrom pre) && (sym2.owner isSubClass sym.owner)
        sym1.allOverriddenSymbols find qualifies getOrElse NoSymbol
      }

      def throwError = throw new MatchError(s"orRef($this, $that)")

      if (this eq that) this
      else if (!this.exists) this
      else if (!that.exists) that
      else this match {
        case ref1 @ OverloadedRef(ref11, ref12) =>
          ref1.derivedOverloadedRef((ref11 | that)(pre), (ref12 | that)(pre))
        case _ =>
          that match {
            case ref2 @ OverloadedRef(ref21, ref22) =>
              ref2.derivedOverloadedRef((this | ref21)(pre), (this | ref22)(pre))
            case ref2: SymRef =>
              this match {
                case ref1: SymRef =>
                    if (ref1.signature != ref2.signature) NoRef
                    else new JointSymRef(lubSym(ref1.symbol, ref2.symbol), ref1.info | ref2.info)
                  case _ =>
                    throwError
                }
              case _ =>
                throwError
            }
        }
    }
  }

  /** The class of overloaded references
   *  @param  variants   The overloaded variants indexed by thheir signatures.
   */
  case class OverloadedRef(ref1: Reference, ref2: Reference) extends Reference {
    def derivedOverloadedRef(r1: Reference, r2: Reference) =
      if ((r1 eq ref1) && (r2 eq ref2)) this else OverloadedRef(r1, r2)
    def isValid(implicit ctx: Context) = ref1.isValid && ref2.isValid
  }

  abstract case class SymRef(override val symbol: Symbol,
                             override val info: Type) extends Reference with RefSet {
    override def isType = symbol.isType
    override def signature: Signature = {
      def sig(tp: Type): Signature = tp match {
        case tp: PolyType =>
          tp.resultType match {
            case mt: MethodType => mt.signature
            case _ => List()
          }
        case mt: MethodType => mt.signature
        case _ => NullSignature
      }
      if (isType) super.signature else sig(info)
    }

    def derivedSymRef(s: Symbol, i: Type): SymRef =
      if ((s eq symbol) && (i eq info)) this else copy(s, i)

    protected def copy(s: Symbol, i: Type): SymRef = this

    // ------ RefSet ops ----------------------------------------------

    def containsSig(sig: Signature)(implicit ctx: Context) =
      signature == sig
    def filter(p: Symbol => Boolean)(implicit ctx: Context): RefSet =
      if (p(symbol)) this else NoRef
    def filterDisjoint(syms: RefSet)(implicit ctx: Context): RefSet =
      if (syms.containsSig(signature)) NoRef else this
    def filterExcluded(flags: FlagSet)(implicit ctx: Context): RefSet =
      if (symbol.hasFlag(flags)) NoRef else this
    def filterAccessibleFrom(pre: Type)(implicit ctx: Context): RefSet =
      if (symbol.isAccessibleFrom(pre)) this else NoRef
    def asSeenFrom(pre: Type, owner: Symbol)(implicit ctx: Context): RefSet =
      derivedSymRef(symbol, info.asSeenFrom(pre, owner))
  }

  class UniqueSymRef(symbol: Symbol, info: Type)(implicit ctx: Context) extends SymRef(symbol, info) {
    private val denot = symbol.deref
    private val runid = ctx.runId
    def isValid(implicit ctx: Context) = ctx.runId == runid && (symbol.deref eq denot)
    override protected def copy(s: Symbol, i: Type): SymRef = new UniqueSymRef(s, i)
  }

  class JointSymRef(symbol: Symbol, info: Type)(implicit ctx: Context) extends SymRef(symbol, info) {
    private val period = ctx.period
    def isValid(implicit ctx: Context) = ctx.period == period
    override protected def copy(s: Symbol, i: Type): SymRef = new JointSymRef(s, i)
  }

  object ErrorRef extends SymRef(NoSymbol, NoType) {
    def isValid(implicit ctx: Context): Boolean = true
  }

  object NoRef extends SymRef(NoSymbol, NoType) {
    override def exists = false
    def isValid(implicit ctx: Context): Boolean = true
  }

// --------------- RefSets -------------------------------------------------

  trait RefSet {
    def exists: Boolean
    def containsSig(sig: Signature)(implicit ctx: Context): Boolean
    def filter(p: Symbol => Boolean)(implicit ctx: Context): RefSet
    def filterDisjoint(syms: RefSet)(implicit ctx: Context): RefSet
    def filterExcluded(flags: FlagSet)(implicit ctx: Context): RefSet
    def filterAccessibleFrom(pre: Type)(implicit ctx: Context): RefSet
    def asSeenFrom(pre: Type, owner: Symbol)(implicit ctx: Context): RefSet
    def union(that: RefSet) =
      if (!this.exists) that
      else if (that.exists) this
      else RefUnion(this, that)
  }

  case class RefUnion(syms1: RefSet, syms2: RefSet) extends RefSet {
    assert(syms1.exists && !syms2.exists)
    private def derivedUnion(s1: RefSet, s2: RefSet) =
      if (!s1.exists) s2
      else if (!s2.exists) s1
      else if ((s1 eq syms2) && (s2 eq syms2)) this
      else new RefUnion(s1, s2)
    def exists = true
    def containsSig(sig: Signature)(implicit ctx: Context) =
      (syms1 containsSig sig) || (syms2 containsSig sig)
    def filter(p: Symbol => Boolean)(implicit ctx: Context) =
      derivedUnion(syms1 filter p, syms2 filter p)
    def filterDisjoint(syms: RefSet)(implicit ctx: Context): RefSet =
      derivedUnion(syms1 filterDisjoint syms, syms2 filterDisjoint syms)
    def filterExcluded(flags: FlagSet)(implicit ctx: Context): RefSet =
      derivedUnion(syms1 filterExcluded flags, syms2 filterExcluded flags)
    def filterAccessibleFrom(pre: Type)(implicit ctx: Context): RefSet =
      derivedUnion(syms1 filterAccessibleFrom pre, syms2 filterAccessibleFrom pre)
    def asSeenFrom(pre: Type, owner: Symbol)(implicit ctx: Context): RefSet =
      derivedUnion(syms1.asSeenFrom(pre, owner), syms2.asSeenFrom(pre, owner))
  }
}