aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/core/TypeOps.scala
blob: 08d7c0cd6b17778a492a84a1c0f15f58fe975adb (plain) (tree)
1
2
3

                             
                                              













                                                                                           
                                               




                                                                              


                           















                                                                   

                                       













                                                      
               

                                                                       







                                                                 



























                                                                                     
       



































                                                                           
                   






                                           
         

     










                                       
       
     
 









                                                                             
              



                                               
         


               
 


















                                                                               

 
package dotty.tools.dotc.core

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

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.isTypeParameter) {
              // 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: RefinedType1 =>
        tp.derivedRefinedType1(
            asSeenFrom(tp.parent, pre, clazz, theMap),
            tp.name1,
            asSeenFrom(tp.info1, pre, clazz, theMap))
      case tp: RefinedType2 =>
        tp.derivedRefinedType2(
            asSeenFrom(tp.parent, pre, clazz, theMap),
            tp.name1,
            asSeenFrom(tp.info1, pre, clazz, theMap),
            tp.name2,
            asSeenFrom(tp.info2, 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.isAbstractType
      case AndType(l, r) => isAbstractIntersection(l) | isAbstractIntersection(l)
      case OrType(l, r) => isAbstractIntersection(l) & isAbstractIntersection(r)
      case _ => false
    }
    def containsName(names: Set[Name], tp: RefinedType): Boolean = tp match {
      case tp: RefinedType1 => names contains tp.name1
      case tp: RefinedType2 => (names contains tp.name1) || (names contains tp.name2)
      case _ => tp.names exists (names contains)
    }
    def test = {
      tp match {
        case ThisType(_) =>
          false
        case tp: RefinedType =>
          tp.parent.isVolatile ||
            isAbstractIntersection(tp.parent) &&
            containsName(tp.abstractMemberNames(tp), tp)
        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
    import ctx.root.{ volatileRecursions, pendingVolatiles }
    try {
      volatileRecursions += 1
      if (volatileRecursions < LogVolatileThreshold)
        test
      else if (pendingVolatiles(tp))
        false // we can return false here, because a cycle will be detected
      // here afterwards and an error will result anyway.
      else
        try {
          pendingVolatiles += tp
          test
        } finally {
          pendingVolatiles -= tp
        }
    } finally {
      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)
            }
        }
    }

  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)
      }
    }

  /** 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
    }
}