aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/core/TypeOps.scala
blob: 4b2f3f09faa392c409b670bb94495e2db61ea346 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                             
                                                                 


                                
                                                                                         
 

                                                                      
          
                                                                                
                   
                                               


                       
                                                                        
 


                           

                            






                                                                                
             
                  
         

                                   

                                     

                              
                                                    
                           
                                                         
               
                                                                     
                      


     

                                                               


                                             





                                                                      
                                                        











                                                                      
     








                                                                 
         
       




                                             
                                                                          





                                    
                   






                                           
         

     


                                        

                                       






                                       
       
     
 


                                        
                                                                        


                                                                             
                                                                     




                                             
              



                                               
         


               
 
                                                                            


                                                                               
                                                                     













                                                 


                                                                                  
                                                                                
     
                                                                                                    





                                                      

                                                    















                                                                           

 
package dotty.tools.dotc.core

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

trait TypeOps { this: Context =>

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

    def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type =
      if ((pre eq NoType) || (pre eq NoPrefix) || cls.isPackageClass)
        tp
      else if (thiscls.isNonBottomSubClass(cls) && pre.baseType(thiscls).exists)
        pre match {
          case SuperType(thispre, _) => thispre
          case _ => pre
        }
      else
        toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls)

    tp match {
      case tp: NamedType =>
        val sym = tp.symbol
        if (sym.isStatic) tp
        else {
          val tp1 = tp.derivedNamedType(asSeenFrom(tp.prefix, pre, cls, theMap))
          if ((tp1 ne tp) && (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 => hi
              case _ => tp1
            }
          else tp1
        }
      case ThisType(thiscls) =>
        toPrefix(pre, cls, thiscls)
      case _: BoundType | NoPrefix =>
        tp
      case tp: RefinedType =>
        tp.derivedRefinedType(
            asSeenFrom(tp.parent, pre, cls, theMap),
            tp.refinedName,
            asSeenFrom(tp.refinedInfo, pre, cls, theMap))
      case _ =>
        (if (theMap != null) theMap else new AsSeenFromMap(pre, cls))
          .mapOver(tp)
    }
  }

  class AsSeenFromMap(pre: Type, cls: Symbol) extends TypeMap {
    def apply(tp: Type) = asSeenFrom(tp, pre, cls, 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 || tp.controlled(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 tp2 match {  // normalize to disjunctive normal form if possible.
      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 {
      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 &-summand of tp2.
   */
  private def mergeIfSub(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
    if (tp1 <:< tp2)
      if (tp2 <:< tp1) tp2 else tp1 // keep existing type if possible
    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 |-summand of tp2.
   */
  private def mergeIfSuper(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
    if (tp2 <:< tp1)
      if (tp1 <:< tp2) tp2 else tp1 // keep existing type if possible
    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
  }
}