aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala
blob: 8f99ef1d080575667aa9ff96d5a68cad6c41527c (plain) (tree)
1
2
3
4
5
6



                        
                               
                                                                                         






                       
 




                         
                                 
 
                     
                                         
 
                                                                  
                                                                       
  


                                                                                



                                                                                        


                                                                
   
                                                                                               



                                                  
                                                                                                   

                                                                            







                                                                              
                                    


                                                                                
                                                                                                                                        




               


                                                                                 





                                                                                   


                                                                        













                                                                                        
                                                                                                                                    
                                                    
     









                                                                                              







                                                                                                       

                                                          


        
 
package dotty.tools.dotc
package transform

import core._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.DenotTransformers.{IdentityDenotTransformer, SymTransformer}
import Contexts.Context
import Symbols._
import Scopes._
import Flags._
import StdNames._
import SymDenotations._
import Types._

import collection.mutable
import TreeTransforms._
import Decorators._
import ast.Trees._
import TreeTransforms._
import java.io.File.separatorChar

import ValueClasses._
import dotty.tools.dotc.core.Phases.Phase

/** Make private term members that are accessed from another class
 *  non-private by resetting the Private flag and expanding their name.
 *
 *  Make private accessor in value class not-private. Ihis is necessary to unbox
 *  the value class when accessing it from separate compilation units
 *
 *  Also, make non-private any private parameter forwarders that forward to an inherited
 *  public or protected parameter accessor with the same name as the forwarder.
 *  This is necessary since private methods are not allowed to have the same name
 *  as inherited public ones.
 *
 *  See discussion in https://github.com/lampepfl/dotty/pull/784
 *  and https://github.com/lampepfl/dotty/issues/783
 */
class ExpandPrivate extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform =>
  import ast.tpd._

  override def phaseName: String = "expandPrivate"

  // This phase moves methods around (in infotransform) so it may need to make other methods public
  override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[MoveStatics])

  override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
    tree match {
      case t: DefDef =>
        val sym = t.symbol
        def hasWeakerAccess(other: Symbol) = {
          // public > protected > /* default */ > private
          if (sym.is(Private)) other.is(Private)
          else if (sym.is(Protected)) other.is(Protected | Private)
          else true // sym is public
        }
        val fail = sym.allOverriddenSymbols.findSymbol(x => !hasWeakerAccess(x))
        if (fail.exists) {
          assert(false, i"${sym.showFullName}: ${sym.info} has weaker access than superclass method ${fail.showFullName}: ${fail.info}")
        }
      case _ =>
    }
  }

  private def isVCPrivateParamAccessor(d: SymDenotation)(implicit ctx: Context) =
    d.isTerm && d.is(PrivateParamAccessor) && isDerivedValueClass(d.owner)

  /** Make private terms accessed from different classes non-private.
   *  Note: this happens also for accesses between class and linked module class.
   *  If we change the scheme at one point to make static module class computations
   *  static members of the companion class, we should tighten the condition below.
   */
  private def ensurePrivateAccessible(d: SymDenotation)(implicit ctx: Context) =
    if (isVCPrivateParamAccessor(d))
      d.ensureNotPrivate.installAfter(thisTransform)
    else if (d.is(PrivateTerm) && d.owner != ctx.owner.enclosingClass) {
      // Paths `p1` and `p2` are similar if they have a common suffix that follows
      // possibly different directory paths. That is, their common suffix extends
      // in both cases either to the start of the path or to a file separator character.
      def isSimilar(p1: String, p2: String): Boolean = {
        var i = p1.length - 1
        var j = p2.length - 1
        while (i >= 0 && j >= 0 && p1(i) == p2(j) && p1(i) != separatorChar) {
          i -= 1
          j -= 1
        }
        (i < 0 || p1(i) == separatorChar) &&
        (j < 0 || p1(j) == separatorChar)
      }
      assert(isSimilar(d.symbol.sourceFile.path, ctx.source.file.path),
          i"private ${d.symbol.showLocated} in ${d.symbol.sourceFile} accessed from ${ctx.owner.showLocated} in ${ctx.source.file}")
      d.ensureNotPrivate.installAfter(thisTransform)
    }

  override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = {
    ensurePrivateAccessible(tree.symbol)
    tree
  }

  override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = {
    ensurePrivateAccessible(tree.symbol)
    tree
  }

  override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
    val sym = tree.symbol
    tree.rhs match {
      case Apply(sel @ Select(_: Super, _), _)
      if sym.is(PrivateParamAccessor) && sel.symbol.is(ParamAccessor) && sym.name == sel.symbol.name =>
        sym.ensureNotPrivate.installAfter(thisTransform)
      case _ =>
        if (isVCPrivateParamAccessor(sym))
          sym.ensureNotPrivate.installAfter(thisTransform)
    }
    tree
  }
}