aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TypeOps.scala
blob: 71bc9f8019e8a6204863b215fbe21e79907f5c9f (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
package dotty.tools.dotc.core

import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._

trait TypeOps { this: Context =>

  final def asSeenFrom(tp: Type, pre: Type, clazz: Symbol, theMap: AsSeenFromMap): Type = {

    def skipPrefixOf(pre: Type, clazz: Symbol) =
      (pre eq NoType) || (pre eq NoPrefix) || clazz.isPackageClass

    def toPrefix(pre: Type, clazz: Symbol, thisclazz: ClassSymbol): Type =
      if (skipPrefixOf(pre, clazz))
        tp
      else if ((thisclazz isNonBottomSubClass clazz) &&
        (pre.widen.typeSymbol isNonBottomSubClass thisclazz))
        pre match {
          case SuperType(thispre, _) => thispre
          case _ => pre
        }
      else
        toPrefix(pre.baseType(clazz).normalizedPrefix, clazz.owner, thisclazz)

    tp match {
      case tp: NamedType =>
        val sym = tp.symbol
        if (sym.isStatic) tp
        else {
          val pre0 = tp.prefix
          val pre1 = asSeenFrom(pre0, pre, clazz, theMap)
          if (pre1 eq pre0) tp
          else {
            val tp1 = NamedType(pre1, tp.name)
            if (sym is TypeParam) {
              // short-circuit instantiated type parameters
              // by replacing pre.tp with its alias, if it has one.
              val tp2 = tp1.info
              if (tp2.isAliasTypeBounds) return tp2.bounds.hi
            }
            tp1
          }
        }
      case ThisType(thisclazz) =>
        toPrefix(pre, clazz, thisclazz)
      case _: BoundType | NoPrefix =>
        tp
      case tp: RefinedType =>
        tp.derivedRefinedType(
            asSeenFrom(tp.parent, pre, clazz, theMap),
            tp.name,
            asSeenFrom(tp.info, pre, clazz, theMap))
      case _ =>
        (if (theMap != null) theMap else new AsSeenFromMap(pre, clazz))
          .mapOver(tp)
    }
  }

  class AsSeenFromMap(pre: Type, clazz: Symbol) extends TypeMap {
    def apply(tp: Type) = asSeenFrom(tp, pre, clazz, this)
  }

  final def isVolatile(tp: Type): Boolean = {
    def isAbstractIntersection(tp: Type): Boolean = tp match {
      case tp: TypeRef => tp.symbol.isAbstractType
      case AndType(l, r) => isAbstractIntersection(l) | isAbstractIntersection(l)
      case OrType(l, r) => isAbstractIntersection(l) & isAbstractIntersection(r)
      case _ => false
    }
    def test = {
      tp match {
        case ThisType(_) =>
          false
        case tp: RefinedType =>
          tp.parent.isVolatile ||
            isAbstractIntersection(tp.parent) &&
            (tp.abstractMemberNames contains tp.name)
        case tp: TypeProxy =>
          tp.underlying.isVolatile
        case AndType(l, r) =>
          l.isVolatile || r.isVolatile ||
            isAbstractIntersection(l) && r.abstractMemberNames(tp).nonEmpty
        case OrType(l, r) =>
          l.isVolatile && r.isVolatile
        case _ =>
          false
      }
    }
    // need to be careful not to fall into an infinite recursion here
    // because volatile checking is done before all cycles are detected.
    // the case to avoid is an abstract type directly or
    // indirectly upper-bounded by itself. See #2918
    try {
      ctx.volatileRecursions += 1
      if (ctx.volatileRecursions < LogVolatileThreshold)
        test
      else if (ctx.pendingVolatiles(tp))
        false // we can return false here, because a cycle will be detected
      // here afterwards and an error will result anyway.
      else
        try {
          ctx.pendingVolatiles += tp
          test
        } finally {
          ctx.pendingVolatiles -= tp
        }
    } finally {
      ctx.volatileRecursions -= 1
    }
  }

  final def glb(tp1: Type, tp2: Type): Type =
    if (tp1 eq tp2) tp1
    else if (tp1.isWrong) tp2
    else if (tp2.isWrong) tp1
    else tp2 match {
      case OrType(tp21, tp22) =>
        tp1 & tp21 | tp1 & tp22
      case _ =>
        tp1 match {
          case OrType(tp11, tp12) =>
            tp11 & tp2 | tp12 & tp2
          case _ =>
            val t1 = mergeIfSub(tp1, tp2)
            if (t1.exists) t1
            else {
              val t2 = mergeIfSub(tp2, tp1)
              if (t2.exists) t2
              else AndType(tp1, tp2)
            }
        }
    }

  final def glb(tps: List[Type]): Type =
    (defn.AnyType /: tps)(glb)

  def lub(tp1: Type, tp2: Type): Type =
    if (tp1 eq tp2) tp1
    else if (tp1.isWrong) tp1
    else if (tp2.isWrong) tp2
    else {
      val t1 = mergeIfSuper(tp1, tp2)
      if (t1.exists) t1
      else {
        val t2 = mergeIfSuper(tp2, tp1)
        if (t2.exists) t2
        else OrType(tp1, tp2)
      }
    }

  final def lub(tps: List[Type]): Type =
    (defn.NothingType /: tps)(lub)

  /** Merge `t1` into `tp2` if t1 is a subtype of some part of tp2.
   */
  private def mergeIfSub(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
    if (tp1 <:< tp2)
      if (tp2 <:< tp1) tp2 else tp1
    else tp2 match {
      case tp2 @ AndType(tp21, tp22) =>
        val lower1 = mergeIfSub(tp1, tp21)
        if (lower1 eq tp21) tp2
        else if (lower1.exists) lower1 & tp22
        else {
          val lower2 = mergeIfSub(tp1, tp22)
          if (lower2 eq tp22) tp2
          else if (lower2.exists) tp21 & lower2
          else NoType
        }
      case _ =>
        NoType
    }

  /** Merge `tp1` into `tp2` if tp1 is a supertype of some part of tp2.
   */
  private def mergeIfSuper(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
    if (tp2 <:< tp1)
      if (tp1 <:< tp2) tp2 else tp1
    else tp2 match {
      case tp2 @ OrType(tp21, tp22) =>
        val higher1 = mergeIfSuper(tp1, tp21)
        if (higher1 eq tp21) tp2
        else if (higher1.exists) higher1 | tp22
        else {
          val higher2 = mergeIfSuper(tp1, tp22)
          if (higher2 eq tp22) tp2
          else if (higher2.exists) tp21 | higher2
          else NoType
        }
      case _ =>
        NoType
    }

  /** Normalize a list of parent types of class `cls` that may contain refinements
   *  to a list of typerefs, by converting all refinements to member
   *  definitions in scope `decls`.
   */
  def normalizeToRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = {
    var refinements = Map[TypeName, Type]()
    var formals = Map[TypeName, Symbol]()
    def normalizeToRef(tp: Type): TypeRef = tp match {
      case tp @ RefinedType(tp1, name: TypeName) =>
        refinements = refinements.updated(name,
          refinements get name match {
            case Some(info) => info & tp.info
            case none => tp.info
          })
        formals = formals.updated(name, tp1.member(name).symbol)
        normalizeToRef(tp1)
      case tp: TypeRef =>
        tp
      case _ =>
        throw new TypeError(s"unexpected parent type: $tp")
    }
    val parentRefs = parents map normalizeToRef
    for ((name, tpe) <- refinements) decls.enter {
      val formal = formals(name)
      val bounds = tpe.toRHS(formal)
      ctx.newSymbol(cls, name, formal.flags & RetainedTypeArgFlags, bounds)
    }
    parentRefs
  }
}