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

                             
                                                                 













                                                                                           
                                               




                                                                              


                           






                                                         
                                   

                                                                   




                                                      



               

                                       

                                     

                              
                                                      

                                                           
               

                                                                       







                                                                 


















                                                                      
     








                                                                 
         
       













                                             
                   






                                           
         

     


                                        










                                       
       
     
 


                                        









                                                                             
              



                                               
         


               
 


















                                                                               


                                                                                  
                                                                                
     
                                                                                                    





                                                      

                                                    















                                                                           

 
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.
              tp1.info match {
                case TypeBounds(lo, hi) if lo eq hi =>
                  return hi
                case _ =>
              }
            }
            tp1
          }
        }
      case ThisType(thisclazz) =>
        toPrefix(pre, clazz, thisclazz)
      case _: BoundType | NoPrefix =>
        tp
      case tp: RefinedType =>
        tp.derivedRefinedType(
            asSeenFrom(tp.parent, pre, clazz, theMap),
            tp.refinedName,
            asSeenFrom(tp.refinedInfo, 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 = {
    /** Pre-filter to avoid expensive DNF computation */
    def needsChecking(tp: Type, isPart: Boolean): Boolean = tp match {
      case tp: TypeRef =>
        tp.info match {
          case TypeBounds(lo, hi) =>
            if (lo eq hi) needsChecking(hi, isPart)
            else isPart || isVolatile(hi)
          case _ => false
        }
      case tp: RefinedType =>
        needsChecking(tp.parent, true)
      case tp: TypeProxy =>
        needsChecking(tp.underlying, isPart)
      case AndType(l, r) =>
        needsChecking(l, true) || needsChecking(r, true)
      case OrType(l, r) =>
        isPart || needsChecking(l, isPart) && needsChecking(r, isPart)
      case _ =>
        false
    }
    needsChecking(tp, false) && {
      tp.DNF forall { case (parents, refinedNames) =>
        val absParents = parents filter (_.info.isRealTypeBounds)
        absParents.size >= 2 || {
          val ap = absParents.head
          (parents exists (p =>
            (p ne ap) || p.abstractMemberNames(tp).nonEmpty)) ||
          (refinedNames & tp.abstractMemberNames()).nonEmpty ||
          isVolatile(ap)
        }
      }
    }
  }

  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`. Can add members to `decls` as a side-effect.
   */
  def normalizeToRefs(parents: List[Type], cls: ClassSymbol, decls: MutableScope): 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.refinedInfo
            case none => tp.refinedInfo
          })
        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
  }
}