aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/Referenceds.scala
blob: f0db3ff6c94f3c4c7a4a171f6926fd9f7d01e3ca (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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
package dotty.tools.dotc
package core

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


/** Classes that implement referenced items and sets of them
 */
object Referenceds {

  /** The signature of a referenced.
   *  Overloaded referenceds 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
   *  referenced. For instance a referenced 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 referenced is the result of resolving
   *  a name (either simple identifier or select) during a given period.
   *
   *  Referenced has two subclasses: OverloadedRefd and SymRefd.
   *
   *  A SymRefd refers to a `symbol` and a type (`info`) that the symbol has
   *  when seen from the reference.
   *
   *  Referenceds 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 `NoRefd`.
   *
   *  A referenced might refer to `NoSymbol`. This is the case if the referenced
   *  was produced from a disjunction of two referenceds 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 referenced of `y` is `SymRefd(NoSymbol, A | B)`.
   */
  abstract class Referenced extends DotClass {

    /** The referenced symbol, exists only for non-overloaded references */
    def symbol: Symbol

    /** The type info of the reference, exists only for non-overloaded references */
    def info: Type

    /** The period during which this reference is valid. */
    def validFor: Period

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

    /** The signature of the reference */
    def signature: Signature

    /** Resolve overloaded reference to pick the one with the given signature */
    def atSignature(sig: Signature): Referenced

    /** The variant of this reference that's current in the given context. */
    def current(implicit ctx: Context): Referenced

    def exists: Boolean = true

    def orElse(that: => Referenced) = if (this.exists) this else that

    /** Form a reference by conjoining with reference `that` */
    def & (that: Referenced)(implicit ctx: Context): Referenced =
      if (this eq that) this
      else if (!this.exists) that
      else if (!that.exists) this
      else that match {
        case that: SymRefd =>
          val r = mergeRef(this, that)
          if (r ne NoRefd) 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 NoRefd.
     */
    private def mergeRef(ref1: Referenced, ref2: SymRefd)(implicit ctx: Context): Referenced = ref1 match {
      case ref1 @ OverloadedRef(ref11, ref12) =>
        val r1 = mergeRef(ref11, ref2)
        if (r1 ne NoRefd) r1 else mergeRef(ref12, ref2)
      case ref1: SymRefd =>
        if (ref1 eq ref2) ref1
        else if (ref1.signature == ref2.signature) {
          def isEligible(sym1: Symbol, sym2: Symbol) =
            if (sym1.isType) !sym1.isClass
            else sym1.isConcrete || sym2.isDeferred || !sym2.exists
          def normalize(info: Type) =
            if (isType) info.bounds else info
          val sym1 = ref1.symbol
          val info1 = ref1.info
          val sym2 = ref2.symbol
          val info2 = ref2.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 JointSymRefd(
              if (sym2Eligible) sym2 else sym1,
              bounds1 & bounds2,
              ref1.validFor & ref2.validFor)
        } else NoRefd
    }

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

      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: SymRefd =>
              this match {
                case ref1: SymRefd =>
                    if (ref1.signature != ref2.signature) NoRefd
                    else new JointSymRefd(
                      lubSym(ref1.symbol, ref2.symbol),
                      ref1.info | ref2.info,
                      ref1.validFor & ref2.validFor)
                  case _ =>
                    throwError
                }
              case _ =>
                throwError
            }
        }
    }
  }

  /** The class of overloaded references
   *  @param  variants   The overloaded variants indexed by thheir signatures.
   */
  case class OverloadedRef(ref1: Referenced, ref2: Referenced) extends Referenced {
    def derivedOverloadedRef(r1: Referenced, r2: Referenced) =
      if ((r1 eq ref1) && (r2 eq ref2)) this else OverloadedRef(r1, r2)
    def symbol = unsupported("symbol")
    def info = unsupported("info")
    def signature = unsupported("signature")
    def atSignature(sig: Signature): Referenced =
      ref1.atSignature(sig) orElse ref2.atSignature(sig)
    def validFor = ref1.validFor & ref2.validFor
    def current(implicit ctx: Context): Referenced =
      derivedOverloadedRef(ref1.current, ref2.current)
  }

  abstract class SymRefd extends Referenced with ReferencedSet {

    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) NullSignature else sig(info)
    }

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

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

    def atSignature(sig: Signature): Referenced =
      if (sig == signature) this else NoRefd

    // ------ Transformations -----------------------------------------

    private[this] var _validFor: Period = Nowhere

    def validFor = _validFor
    def validFor_=(p: Period) =
      _validFor = p

    /** The next SymRefd in this run, with wrap-around from last to first.
     *
     *  There may be several `SymRefd`s with different validity
     *  representing the same underlying definition at different phases.
     *  These are called a "flock". Flock members are generated by
     *  @see SymRef.current. Flock members are connected in a ring
     *  with their `nextInRun` fields.
     *
     *  There are the following invariants converning flock members
     *
     *  1) validity periods must be non-overlapping
     *  2) the union of all validity periods must be a contiguous
     *     interval starting in FirstPhaseId.
     */
    var nextInRun: SymRefd = this

    /** The version of this SymRefd that was valid in the first phase
     *  of this run.
     */
    def initial: SymRefd = {
      var current = nextInRun
      while (current.validFor.code > this._validFor.code) current = current.nextInRun
      current
    }

    def current(implicit ctx: Context): SymRefd = {
      val currentPeriod = ctx.period
      val valid = _validFor
      var current = this
      if (currentPeriod.code > valid.code) {
        // search for containing period as long as nextInRun increases.
        var next = nextInRun
        while (next.validFor.code > valid.code &&
               !(next.validFor contains currentPeriod)) {
          current = next
          next = next.nextInRun
        }
        if (next.validFor.code > valid.code) {
          // in this case, containsPeriod(next._validFor, currentPeriod)
          current = next
        } else {
          // not found, current points to highest existing variant
          var startPid = current.validFor.lastPhaseId + 1
          val trans = ctx.root.transformersFor(current)
          val endPid = trans.nextTransformer(startPid + 1).phaseId - 1
          next = trans.nextTransformer(startPid) transform current
          if (next eq current)
            startPid = current.validFor.firstPhaseId
          else {
            current.nextInRun = next
            current = next
          }
          current.validFor = Period(currentPeriod.runId, startPid, endPid)
        }
      } else {
        // currentPeriod < valid; in this case a version must exist
        do {
          current = current.nextInRun
        } while (!(current.validFor contains currentPeriod))
      }
      current
    }

    def asDenotation = asInstanceOf[Denotation]

    // ------ ReferencedSet ops ----------------------------------------------

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

  class UniqueSymRefd(val symbol: Symbol,
                     val info: Type,
                     initValidFor: Period) extends SymRefd {
    validFor = initValidFor
    override protected def copy(s: Symbol, i: Type): SymRefd = new UniqueSymRefd(s, i, validFor)
  }

  class JointSymRefd(val symbol: Symbol,
                    val info: Type,
                    initValidFor: Period) extends SymRefd {
    validFor = initValidFor
    override protected def copy(s: Symbol, i: Type): SymRefd = new JointSymRefd(s, i, validFor)
  }

  class ErrorRefd(implicit ctx: Context) extends SymRefd {
    val symbol = NoSymbol
    val info = NoType
    validFor = Period.allInRun(ctx.runId)
  }

  object NoRefd extends SymRefd {
    val symbol = NoSymbol
    val info = NoType
    validFor = Nowhere
    override def exists = false
  }

// --------------- ReferencedSets -------------------------------------------------

  /** A ReferencedSet represents a set of referenced */
  trait ReferencedSet {
    def exists: Boolean
    def toRef(implicit ctx: Context): Referenced
    def containsSig(sig: Signature)(implicit ctx: Context): Boolean
    def filter(p: Symbol => Boolean)(implicit ctx: Context): ReferencedSet
    def filterDisjoint(refs: ReferencedSet)(implicit ctx: Context): ReferencedSet
    def filterExcluded(flags: FlagSet)(implicit ctx: Context): ReferencedSet
    def filterAccessibleFrom(pre: Type)(implicit ctx: Context): ReferencedSet
    def asSeenFrom(pre: Type, owner: Symbol)(implicit ctx: Context): ReferencedSet
    def union(that: ReferencedSet) =
      if (!this.exists) that
      else if (that.exists) this
      else RefUnion(this, that)
  }

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