aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Scopes.scala
blob: 205798474c809394ff501e7c9d225ebedf5414b3 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                
                                
                     



                   
                    
                       
                       
                       
                    
                    
                                  
                         






                                                                           


                                                                            
     
                                  





                                                                


                                                                                     











                                                





                                                                    
                                                                

                                                                              












                                                                      
                                                   


                                                                                       






















                                                                                     
 

                                                                         
 




                                                                             
 













                                                                                                     



                                                                                 
                                                                                                                             


                                            

                                          




                              













                                                                                   
                                                                 
 
                                                                        
 
                                                                   

                                                     





                                                                        




                                                                                                                     


                                                                                    

                                                                                                       
 
                                            

                                                                      
                                                                                                                             








                                                       
                                   









                                                                

                                                         








                                                                                          
                                    





                                                
 

                                                                                               

                                                                                     

                        
                                           

                       


       



                                                                                 
                                                                           
                                                        


                             

     

                                                                      
                                                           
                                            
                                                                                                     
       



                        
                                                                                  
                                                                       
                                                                                           


                
                                                                             
                                                                   





                                                                         
                                     

       
                                                                                          















                                                
                                                                         
                                                                    







                                         
                                                            



                                   
                                           






                          
                                                         
                                                                  

                                   
                                   



                              









                                                                                         
                       

     
                                                  
       
                                                                               

                              

                                                             



                     
                                               






                                                       
                                                                                                

                            
                                                                     
          
                                                                     


       

                                                                                   
       
                                                                      
                               
                        









                                                
                                                                        
                      
                                                 

                         

                                
                                                                                       
         

                  
                  

     

                                                                
                                                                  
 
                                                      


                                                                                       
                      







                                                                  


                           
                                                 

                                                                                   
                                                                                                 

                                                       
                                                                           




                             


                                                                                             
                                                                           
 
                                                      

                                                                                         
 


                                   
                                               

                                 
                                                    
                                                                                            

                                                                                             

   
                                        
     
                                                      
 
/* NSC -- new Scala compiler
 * Copyright 2005-2012 LAMP/EPFL
 * @author  Martin Odersky
 */

package dotty.tools.dotc
package core

import Symbols._
import Types.{TermRef, NoPrefix}
import Flags.Implicit
import Names._
import Periods._
import Decorators._
import Contexts._
import Denotations._
import SymDenotations._
import printing.Texts._
import printing.Printer
import util.common._
import util.DotClass
import SymDenotations.NoDenotation
import collection.mutable

object Scopes {

  /** Maximal fill factor of hash table */
  private final val FillFactor = 2.0/3.0

  /** A hashtable is created once current size exceeds MinHash * FillFactor
   *  The initial hash table has twice that size (i.e 16).
   *  This value must be a power of two, so that the index of an element can
   *  be computed as element.hashCode & (hashTable.length - 1)
   */
  final val MinHashedScopeSize = 8

  /** The maximal permissible number of recursions when creating
   *  a hashtable
   */
  private final val MaxRecursions = 1000

  class ScopeEntry private[Scopes] (val name: Name, _sym: Symbol, val owner: Scope) {

    var sym: Symbol = _sym

    /** the next entry in the hash bucket
     */
    var tail: ScopeEntry = null

    /** the preceding entry in this scope
     */
    var prev: ScopeEntry = null

    override def toString: String = sym.toString
  }

  /** A scope contains a set of symbols. It can be an extension
   *  of some outer scope, from which it inherits all symbols.
   *  This class does not have any methods to add symbols to a scope
   *  or to delete them. These methods are provided by subclass
   *  MutableScope.
   */
  abstract class Scope extends DotClass with printing.Showable {

    /** The last scope-entry from which all others are reachable via `prev` */
    private[dotc] def lastEntry: ScopeEntry

    /** The number of symbols in this scope (including inherited ones
     *  from outer scopes).
     */
    def size: Int

    /** The number of outer scopes from which symbols are inherited */
    def nestingLevel: Int

    /** The symbols in this scope in the order they were entered;
     *  inherited from outer ones first.
     */
    def toList(implicit ctx: Context): List[Symbol]

    /** Return all symbols as an iterator in the order they were entered in this scope.
     */
    def iterator(implicit ctx: Context): Iterator[Symbol] = toList.iterator

    /** Is the scope empty? */
    def isEmpty: Boolean = lastEntry eq null

    def foreach[U](p: Symbol => U)(implicit ctx: Context): Unit = toList foreach p

    def filter(p: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = {
      ensureComplete()
      var syms: List[Symbol] = Nil
      var e = lastEntry
      while ((e ne null) && e.owner == this) {
        val sym = e.sym
        if (p(sym)) syms = sym :: syms
        e = e.prev
      }
      syms
    }

    def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match {
      case sym :: _ => sym
      case _ => NoSymbol
    }

    /** Returns a new mutable scope with the same content as this one. */
    def cloneScope(implicit ctx: Context): MutableScope

    /** Lookup a symbol entry matching given name. */
    def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry

    /** Lookup next entry with same name as this one */
    def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry

    /** Lookup a symbol */
    final def lookup(name: Name)(implicit ctx: Context): Symbol = {
      val e = lookupEntry(name)
      if (e eq null) NoSymbol else e.sym
    }

    /** Returns an iterator yielding every symbol with given name in this scope.
     */
    final def lookupAll(name: Name)(implicit ctx: Context): Iterator[Symbol] = new Iterator[Symbol] {
      var e = lookupEntry(name)
      def hasNext: Boolean = e ne null
      def next(): Symbol = { val r = e.sym; e = lookupNextEntry(e); r }
    }

    /** The denotation set of all the symbols with given name in this scope
     *  Symbols occur in the result in reverse order relative to their occurrence
     *  in `this.toList`.
     */
    final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(implicit ctx: Context): PreDenotation = {
      var syms: PreDenotation = NoDenotation
      var e = lookupEntry(name)
      while (e != null) {
        val d = e.sym.denot
        if (select(d)) syms = syms union d
        e = lookupNextEntry(e)
      }
      syms
    }

    /** The scope that keeps only those symbols from this scope that match the
     *  given predicates. If all symbols match, returns the scope itself, otherwise
     *  a copy with the matching symbols.
     */
    final def filteredScope(p: Symbol => Boolean)(implicit ctx: Context): Scope = {
      var result: MutableScope = null
      for (sym <- iterator)
        if (!p(sym)) {
          if (result == null) result = cloneScope
          result.unlink(sym)
        }
      if (result == null) this else result
    }

    def implicitDecls(implicit ctx: Context): List[TermRef] = Nil

    def openForMutations: MutableScope = unsupported("openForMutations")

    final def toText(printer: Printer): Text = printer.toText(this)

    def checkConsistent()(implicit ctx: Context) = ()

    /** Ensure that all elements of this scope have been entered.
     *  Overridden by SymbolLoaders.PackageLoader#PackageScope, where it
     *  makes sure that all names with `$`'s have been added.
     */
    protected def ensureComplete()(implicit ctx: Context): Unit = ()
  }

  /** A subclass of Scope that defines methods for entering and
   *  unlinking entries.
   *  Note: constructor is protected to force everyone to use the factory methods newScope or newNestedScope instead.
   *  This is necessary because when run from reflection every scope needs to have a
   *  SynchronizedScope as mixin.
   */
  class MutableScope protected[Scopes](initElems: ScopeEntry, initSize: Int, val nestingLevel: Int = 0)
      extends Scope {

    /** Scope shares elements with `base` */
    protected[Scopes] def this(base: Scope)(implicit ctx: Context) = {
      this(base.lastEntry, base.size, base.nestingLevel + 1)
      ensureCapacity(MinHashedScopeSize)(ctx) // WTH? it seems the implicit is not in scope for a secondary constructor call.
    }

    def this() = this(null, 0, 0)

    private[dotc] var lastEntry: ScopeEntry = initElems

    /** The size of the scope */
    private[this] var _size = initSize

    override final def size = _size
    private def size_= (x: Int) = _size = x

    /** the hash table
     */
    private var hashTable: Array[ScopeEntry] = null

    /** a cache for all elements, to be used by symbol iterator.
     */
    private var elemsCache: List[Symbol] = null

    protected def newScopeLikeThis() = new MutableScope()

    /** Clone scope, taking care not to force the denotations of any symbols in the scope.
     */
    def cloneScope(implicit ctx: Context): MutableScope = {
      val entries = new mutable.ArrayBuffer[ScopeEntry]
      var e = lastEntry
      while ((e ne null) && e.owner == this) {
        entries += e
        e = e.prev
      }
      val scope = newScopeLikeThis()
      for (i <- entries.length - 1 to 0 by -1) {
        val e = entries(i)
        scope.newScopeEntry(e.name, e.sym)
      }
      scope
    }

    /** create and enter a scope entry with given name and symbol */
    protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = {
      ensureCapacity(if (hashTable ne null) hashTable.length else MinHashedScopeSize)
      val e = new ScopeEntry(name, sym, this)
      e.prev = lastEntry
      lastEntry = e
      if (hashTable ne null) enterInHash(e)
      size += 1
      elemsCache = null
      e
    }

    /** create and enter a scope entry */
    protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry =
      newScopeEntry(sym.name, sym)

    private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = {
      val idx = e.name.hashCode & (hashTable.length - 1)
      e.tail = hashTable(idx)
      assert(e.tail != e)
      hashTable(idx) = e
    }

    /** enter a symbol in this scope. */
    final def enter[T <: Symbol](sym: T)(implicit ctx: Context): T = {
      if (sym.isType && ctx.phaseId <= ctx.typerPhase.id) {
        assert(lookup(sym.name) == NoSymbol,
          s"duplicate ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG
      }
      newScopeEntry(sym)
      sym
    }

    /** enter a symbol, asserting that no symbol with same name exists in scope */
    final def enterUnique(sym: Symbol)(implicit ctx: Context): Unit = {
      assert(lookup(sym.name) == NoSymbol, (sym.showLocated, lookup(sym.name).showLocated))
      enter(sym)
    }

    private def ensureCapacity(tableSize: Int)(implicit ctx: Context): Unit =
      if (size >= tableSize * FillFactor) createHash(tableSize * 2)

    private def createHash(tableSize: Int)(implicit ctx: Context): Unit =
      if (size > tableSize * FillFactor) createHash(tableSize * 2)
      else {
        hashTable = new Array[ScopeEntry](tableSize)
        enterAllInHash(lastEntry)
        // checkConsistent() // DEBUG
      }

    private def enterAllInHash(e: ScopeEntry, n: Int = 0)(implicit ctx: Context): Unit = {
      if (e ne null) {
        if (n < MaxRecursions) {
          enterAllInHash(e.prev, n + 1)
          enterInHash(e)
        } else {
          var entries: List[ScopeEntry] = List()
          var ee = e
          while (ee ne null) {
            entries = ee :: entries
            ee = ee.prev
          }
          entries foreach enterInHash
        }
      }
    }

    /** Remove entry from this scope (which is required to be present) */
    final def unlink(e: ScopeEntry)(implicit ctx: Context): Unit = {
      if (lastEntry == e) {
        lastEntry = e.prev
      } else {
        var e1 = lastEntry
        while (e1.prev != e) e1 = e1.prev
        e1.prev = e.prev
      }
      if (hashTable ne null) {
        val index = e.name.hashCode & (hashTable.length - 1)
        var e1 = hashTable(index)
        if (e1 == e)
          hashTable(index) = e.tail
        else {
          while (e1.tail != e) e1 = e1.tail
          e1.tail = e.tail
        }
      }
      elemsCache = null
      size -= 1
    }

    /** remove symbol from this scope if it is present */
    final def unlink(sym: Symbol)(implicit ctx: Context): Unit = {
      var e = lookupEntry(sym.name)
      while (e ne null) {
        if (e.sym == sym) unlink(e)
        e = lookupNextEntry(e)
      }
    }

    /** Replace symbol `prev` (if it exists in current scope) by symbol `replacement`.
     *  @pre `prev` and `replacement` have the same name.
     */
    final def replace(prev: Symbol, replacement: Symbol)(implicit ctx: Context): Unit = {
      require(prev.name == replacement.name)
      var e = lookupEntry(prev.name)
      while (e ne null) {
        if (e.sym == prev) e.sym = replacement
        e = lookupNextEntry(e)
      }
      elemsCache = null
    }

    /** Lookup a symbol entry matching given name.
     */
    override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
      var e: ScopeEntry = null
      if (hashTable ne null) {
        e = hashTable(name.hashCode & (hashTable.length - 1))
        while ((e ne null) && e.name != name) {
          e = e.tail
        }
      } else {
        e = lastEntry
        while ((e ne null) && e.name != name) {
          e = e.prev
        }
      }
      e
    }

    /** lookup next entry with same name as this one */
    override final def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = {
      var e = entry
      if (hashTable ne null)
        do { e = e.tail } while ((e ne null) && e.name != entry.name)
      else
        do { e = e.prev } while ((e ne null) && e.name != entry.name)
      e
    }

    /** Returns all symbols as a list in the order they were entered in this scope.
     *  Does _not_ include the elements of inherited scopes.
     */
    override final def toList(implicit ctx: Context): List[Symbol] = {
      if (elemsCache eq null) {
        ensureComplete()
        elemsCache = Nil
        var e = lastEntry
        while ((e ne null) && e.owner == this) {
          elemsCache = e.sym :: elemsCache
          e = e.prev
        }
      }
      elemsCache
    }

    override def implicitDecls(implicit ctx: Context): List[TermRef] = {
      ensureComplete()
      var irefs = new mutable.ListBuffer[TermRef]
      var e = lastEntry
      while (e ne null) {
        if (e.sym is Implicit) {
          val d = e.sym.denot
          irefs += TermRef.withSigAndDenot(NoPrefix, d.name.asTermName, d.signature, d)
        }
        e = e.prev
      }
      irefs.toList
    }

    /** Vanilla scope - symbols are stored in declaration order.
     */
    final def sorted(implicit ctx: Context): List[Symbol] = toList

    override def openForMutations: MutableScope = this

    /** Check that all symbols in this scope are in their correct hashtable buckets. */
    override def checkConsistent()(implicit ctx: Context) = {
      ensureComplete()
      var e = lastEntry
      while (e != null) {
        var e1 = lookupEntry(e.name)
        while (e1 != e && e1 != null) e1 = lookupNextEntry(e1)
        assert(e1 == e, s"PANIC: Entry ${e.name} is badly linked")
        e = e.prev
      }
    }
  }

  /** Create a new scope */
  def newScope: MutableScope = new MutableScope()

  /** Create a new scope nested in another one with which it shares its elements */
  def newNestedScope(outer: Scope)(implicit ctx: Context): MutableScope = new MutableScope(outer)

  /** Create a new scope with given initial elements */
  def newScopeWith(elems: Symbol*)(implicit ctx: Context): MutableScope = {
    val scope = newScope
    elems foreach scope.enter
    scope
  }

  /** Transform scope of members of `owner` using operation `op`
   *  This is overridden by the reflective compiler to avoid creating new scopes for packages
   */
  def scopeTransform(owner: Symbol)(op: => MutableScope): MutableScope = op

  val selectAll: SymDenotation => Boolean = alwaysTrue
  val selectPrivate: SymDenotation => Boolean    = d => (d.flagsUNSAFE is Flags.Private)
  val selectNonPrivate: SymDenotation => Boolean = d => !(d.flagsUNSAFE is Flags.Private)

  /** The empty scope (immutable).
   */
  object EmptyScope extends Scope {
    override private[dotc] def lastEntry = null
    override def size = 0
    override def nestingLevel = 0
    override def toList(implicit ctx: Context) = Nil
    override def cloneScope(implicit ctx: Context): MutableScope = unsupported("cloneScope")
    override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = null
    override def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = null
  }

  /** A class for error scopes (mutable)
   */
  class ErrorScope(owner: Symbol) extends MutableScope
}