package scala.tools.nsc
package typechecker
import symtab.Flags._
import scala.collection.mutable.ListBuffer
import annotation.tailrec
trait Contexts { self: Analyzer =>
import global._
val NoContext = new Context {
override def implicitss: List[List[ImplicitInfo]] = List()
outer = this
}
NoContext.enclClass = NoContext
NoContext.enclMethod = NoContext
private val startContext = {
NoContext.make(
global.Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType,
global.definitions.RootClass,
global.definitions.RootClass.info.decls)
}
var lastAccessCheckDetails: String = ""
protected def rootImports(unit: CompilationUnit, tree: Tree): List[Symbol] = {
import definitions._
val imps = new ListBuffer[Symbol]
if (!settings.noimports.value) {
assert(isDefinitionsInitialized)
imps += JavaLangPackage
if (!unit.isJava) {
assert(ScalaPackage ne null, "Scala package is null")
imps += ScalaPackage
if (!(treeInfo.isUnitInScala(unit.body, nme.Predef) ||
treeInfo.isUnitInScala(unit.body, tpnme.ScalaObject) ||
treeInfo.containsLeadingPredefImport(List(unit.body))))
imps += PredefModule
}
}
imps.toList
}
def rootContext(unit: CompilationUnit): Context =
rootContext(unit, EmptyTree, false)
def rootContext(unit: CompilationUnit, tree: Tree, erasedTypes: Boolean): Context = {
import definitions._
var sc = startContext
def addImport(pkg: Symbol) {
assert(pkg ne null)
val qual = gen.mkAttributedStableRef(pkg)
sc = sc.makeNewImport(
Import(qual, List(ImportSelector(nme.WILDCARD, -1, null, -1)))
.setSymbol(NoSymbol.newImport(NoPosition).setFlag(SYNTHETIC).setInfo(ImportType(qual)))
.setType(NoType))
sc.depth += 1
}
for (imp <- rootImports(unit, tree))
addImport(imp)
val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports)
c.reportAmbiguousErrors = !erasedTypes
c.reportGeneralErrors = !erasedTypes
c.implicitsEnabled = !erasedTypes
c
}
def resetContexts() {
var sc = startContext
while (sc != NoContext) {
sc.tree match {
case Import(qual, _) => qual.tpe = singleType(qual.symbol.owner.thisType, qual.symbol)
case _ =>
}
sc = sc.outer
}
}
class Context private[typechecker] {
var unit: CompilationUnit = _
var tree: Tree = _
var owner: Symbol = NoSymbol
var scope: Scope = _
var outer: Context = _
var enclClass: Context = _
var enclMethod: Context = _
var variance: Int = _
private var _undetparams: List[Symbol] = List()
var depth: Int = 0
var imports: List[ImportInfo] = List()
var openImplicits: List[(Type,Symbol)] = List()
var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None
var prefix: Type = NoPrefix
var inConstructorSuffix = false
var returnsSeen = false
var inSelfSuperCall = false
var reportAmbiguousErrors = false
var reportGeneralErrors = false
var diagnostic: List[String] = Nil
var implicitsEnabled = false
var checking = false
var retyping = false
var savedTypeBounds: List[(Symbol, Type)] = List()
var typingIndentLevel: Int = 0
def typingIndent = " " * typingIndentLevel
def undetparamsString = if (undetparams.isEmpty) "" else undetparams.mkString("undetparams=", ", ", "")
def undetparams = _undetparams
def undetparams_=(ps: List[Symbol]) = {
_undetparams = ps
}
def extractUndetparams() = {
val tparams = undetparams
undetparams = List()
tparams
}
def withoutReportingErrors[T](op: => T): T = {
val saved = reportGeneralErrors
reportGeneralErrors = false
try op
finally reportGeneralErrors = saved
}
def withImplicitsDisabled[T](op: => T): T = {
val saved = implicitsEnabled
implicitsEnabled = false
try op
finally implicitsEnabled = saved
}
def make(unit: CompilationUnit, tree: Tree, owner: Symbol,
scope: Scope, imports: List[ImportInfo]): Context = {
val c = new Context
c.unit = unit
c.tree = tree
c.owner = owner
c.scope = scope
c.outer = this
tree match {
case Template(_, _, _) | PackageDef(_, _) =>
c.enclClass = c
c.prefix = c.owner.thisType
c.inConstructorSuffix = false
case _ =>
c.enclClass = this.enclClass
c.prefix =
if (c.owner != this.owner && c.owner.isTerm) NoPrefix
else this.prefix
c.inConstructorSuffix = this.inConstructorSuffix
}
tree match {
case DefDef(_, _, _, _, _, _) =>
c.enclMethod = c
case _ =>
c.enclMethod = this.enclMethod
}
c.variance = this.variance
c.depth = if (scope == this.scope) this.depth else this.depth + 1
c.imports = imports
c.inSelfSuperCall = inSelfSuperCall
c.reportAmbiguousErrors = this.reportAmbiguousErrors
c.reportGeneralErrors = this.reportGeneralErrors
c.diagnostic = this.diagnostic
c.typingIndentLevel = typingIndentLevel
c.implicitsEnabled = this.implicitsEnabled
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
registerContext(c.asInstanceOf[analyzer.Context])
c
}
def make(unit: CompilationUnit): Context = {
val c = make(unit, EmptyTree, owner, scope, imports)
c.reportAmbiguousErrors = true
c.reportGeneralErrors = true
c.implicitsEnabled = true
c
}
def makeNewImport(imp: Import): Context =
make(unit, imp, owner, scope, new ImportInfo(imp, depth) :: imports)
def make(tree: Tree, owner: Symbol, scope: Scope): Context = {
if (tree == this.tree && owner == this.owner && scope == this.scope) this
else make0(tree, owner, scope)
}
private def make0(tree : Tree, owner : Symbol, scope : Scope) : Context = {
make(unit, tree, owner, scope, imports)
}
def makeNewScope(tree: Tree, owner: Symbol): Context =
make(tree, owner, new Scope(scope))
def make(tree: Tree, owner: Symbol): Context =
make0(tree, owner, scope)
def make(tree: Tree): Context =
make(tree, owner)
def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = {
val c = make(newtree)
c.reportGeneralErrors = false
c.reportAmbiguousErrors = reportAmbiguousErrors
c
}
def makeImplicit(reportAmbiguousErrors: Boolean) = {
val c = makeSilent(reportAmbiguousErrors)
c.implicitsEnabled = false
c
}
def makeConstructorContext = {
var baseContext = enclClass.outer
while (baseContext.tree.isInstanceOf[Template])
baseContext = baseContext.outer
val argContext = baseContext.makeNewScope(tree, owner)
argContext.inSelfSuperCall = true
argContext.reportGeneralErrors = this.reportGeneralErrors
argContext.reportAmbiguousErrors = this.reportAmbiguousErrors
def enterElems(c: Context) {
def enterLocalElems(e: ScopeEntry) {
if (e != null && e.owner == c.scope) {
enterLocalElems(e.next)
argContext.scope enter e.sym
}
}
if (c.owner.isTerm && !c.owner.isLocalDummy) {
enterElems(c.outer)
enterLocalElems(c.scope.elems)
}
}
enterElems(this)
argContext
}
private def diagString =
if (diagnostic.isEmpty) ""
else diagnostic.mkString("\n","\n", "")
private def addDiagString(msg: String) = {
val ds = diagString
if (msg endsWith ds) msg else msg + ds
}
private def unitError(pos: Position, msg: String) =
unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg)
def error(pos: Position, err: Throwable) =
if (reportGeneralErrors) unitError(pos, addDiagString(err.getMessage()))
else throw err
def error(pos: Position, msg: String) = {
val msg1 = addDiagString(msg)
if (reportGeneralErrors) unitError(pos, msg1)
else throw new TypeError(pos, msg1)
}
def warning(pos: Position, msg: String) = {
if (reportGeneralErrors) unit.warning(pos, msg)
}
def ambiguousError(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) {
val (reportPos, msg) = (
if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) {
val methodName = nme.defaultGetterToMethod(sym1.name)
(sym1.enclClass.pos,
"in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
" define default arguments")
}
else {
(pos,
("ambiguous reference to overloaded definition,\n" +
"both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
"\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
"\nmatch " + rest)
)
}
)
if (reportAmbiguousErrors) {
if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
unit.error(reportPos, msg)
} else throw new TypeError(pos, msg)
}
def isLocal(): Boolean = tree match {
case Block(_,_) => true
case PackageDef(_, _) => false
case EmptyTree => false
case _ => outer.isLocal()
}
private def nextOuter = {
val scopingCtx =
if (owner.isConstructor) nextEnclosing(c => !c.tree.isInstanceOf[Block])
else this
scopingCtx.outer
}
def nextEnclosing(p: Context => Boolean): Context =
if (this == NoContext || p(this)) this else outer.nextEnclosing(p)
override def toString = (
if (this == NoContext) "NoContext"
else "Context(%s@%s scope=%s)".format(owner.fullName, tree.getClass.getName split "[.$]" last, scope.##)
)
def isSubClassOrCompanion(sub: Symbol, base: Symbol) =
sub.isNonBottomSubClass(base) ||
sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)
def enclosingSuperClassContext(clazz: Symbol): Context = {
var c = this.enclClass
while (c != NoContext &&
!clazz.isNonBottomSubClass(c.owner) &&
!(c.owner.isModuleClass && clazz.isNonBottomSubClass(c.owner.companionClass)))
c = c.outer.enclClass
c
}
def enclosingSubClassContext(clazz: Symbol): Context = {
var c = this.enclClass
while (c != NoContext && !isSubClassOrCompanion(c.owner, clazz))
c = c.outer.enclClass
c
}
def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = {
lastAccessCheckDetails = ""
@inline def accessWithinLinked(ab: Symbol) = {
val linked = ab.linkedClassOfClass
(linked ne NoSymbol) && accessWithin(linked)
}
def accessWithin(ab: Symbol) = {
if (sym.isJavaDefined) {
@tailrec def abEnclosesStopAtPkg(o: Symbol): Boolean =
(o eq ab) || (!o.isPackageClass && (o ne NoSymbol) && abEnclosesStopAtPkg(o.owner))
abEnclosesStopAtPkg(owner)
} else (owner hasTransOwner ab)
}
def isSubClassOfEnclosing(clazz: Symbol): Boolean =
enclosingSuperClassContext(clazz) != NoContext
def isSubThisType(pre: Type, clazz: Symbol): Boolean = pre match {
case ThisType(pclazz) => pclazz isNonBottomSubClass clazz
case _ => false
}
def isProtectedAccessOK(target: Symbol) = {
val c = enclosingSubClassContext(sym.owner)
if (c == NoContext)
lastAccessCheckDetails =
"\n Access to protected "+target+" not permitted because"+
"\n "+"enclosing class "+this.enclClass.owner+this.enclClass.owner.locationString+" is not a subclass of "+
"\n "+sym.owner+sym.owner.locationString+" where target is defined"
c != NoContext && {
val res =
isSubClassOrCompanion(pre.widen.typeSymbol, c.owner) ||
c.owner.isModuleClass &&
isSubClassOrCompanion(pre.widen.typeSymbol, c.owner.linkedClassOfClass)
if (!res)
lastAccessCheckDetails =
"\n Access to protected "+target+" not permitted because"+
"\n prefix type "+pre.widen+" does not conform to"+
"\n "+c.owner+c.owner.locationString+" where the access take place"
res
}
}
(pre == NoPrefix) || {
val ab = sym.accessBoundary(sym.owner)
( (ab.isTerm || ab == definitions.RootClass)
|| (accessWithin(ab) || accessWithinLinked(ab)) &&
( !sym.hasLocalFlag
|| sym.owner.isImplClass
|| sym.isProtected && isSubThisType(pre, sym.owner)
|| pre =:= sym.owner.thisType
)
|| sym.isProtected &&
( superAccess
|| pre.isInstanceOf[ThisType]
|| phase.erasedTypes
|| isProtectedAccessOK(sym)
|| (sym.allOverriddenSymbols exists isProtectedAccessOK)
)
)
}
}
def pushTypeBounds(sym: Symbol) {
savedTypeBounds ::= ((sym, sym.info))
}
def restoreTypeBounds(tp: Type): Type = {
var current = tp
for ((sym, info) <- savedTypeBounds) {
if (settings.debug.value) log("resetting " + sym + " to " + info);
sym.info match {
case TypeBounds(lo, hi) if (hi <:< lo && lo <:< hi) =>
current = current.instantiateTypeParams(List(sym), List(lo))
case _ =>
}
sym.setInfo(info)
}
savedTypeBounds = List()
current
}
private var implicitsCache: List[List[ImplicitInfo]] = null
private var implicitsRunId = NoRunId
def resetCache() {
implicitsRunId = NoRunId
implicitsCache = null
if (outer != null && outer != this) outer.resetCache()
}
private def isQualifyingImplicit(sym: Symbol, pre: Type, imported: Boolean) =
sym.isImplicit &&
isAccessible(sym, pre) &&
!(imported && {
val e = scope.lookupEntry(sym.name)
(e ne null) && (e.owner == scope)
})
private def collectImplicits(syms: List[Symbol], pre: Type, imported: Boolean = false): List[ImplicitInfo] =
for (sym <- syms if isQualifyingImplicit(sym, pre, imported)) yield
new ImplicitInfo(sym.name, pre, sym)
private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = {
val pre = imp.qual.tpe
def collect(sels: List[ImportSelector]): List[ImplicitInfo] = sels match {
case List() =>
List()
case List(ImportSelector(nme.WILDCARD, _, _, _)) =>
collectImplicits(pre.implicitMembers, pre, imported = true)
case ImportSelector(from, _, to, _) :: sels1 =>
var impls = collect(sels1) filter (info => info.name != from)
if (to != nme.WILDCARD) {
for (sym <- imp.importedSymbol(to).alternatives)
if (isQualifyingImplicit(sym, pre, imported = true))
impls = new ImplicitInfo(to, pre, sym) :: impls
}
impls
}
collect(imp.tree.selectors)
}
def implicitss: List[List[ImplicitInfo]] = {
if (implicitsRunId != currentRunId) {
implicitsRunId = currentRunId
implicitsCache = List()
val newImplicits: List[ImplicitInfo] =
if (owner != nextOuter.owner && owner.isClass && !owner.isPackageClass && !inSelfSuperCall) {
if (!owner.isInitialized) return nextOuter.implicitss
val savedEnclClass = enclClass
this.enclClass = this
val res = collectImplicits(owner.thisType.implicitMembers, owner.thisType)
this.enclClass = savedEnclClass
res
} else if (scope != nextOuter.scope && !owner.isPackageClass) {
if (settings.debug.value) log("collect local implicits " + scope.toList)
collectImplicits(scope.toList, NoPrefix)
} else if (imports != nextOuter.imports) {
assert(imports.tail == nextOuter.imports)
collectImplicitImports(imports.head)
} else if (owner.isPackageClass) {
collectImplicits(owner.tpe.implicitMembers, owner.tpe)
} else List()
implicitsCache = if (newImplicits.isEmpty) nextOuter.implicitss
else newImplicits :: nextOuter.implicitss
}
implicitsCache
}
def lookup(name: Name, expectedOwner: Symbol) = {
var res: Symbol = NoSymbol
var ctx = this
while(res == NoSymbol && ctx.outer != ctx) {
val s = ctx.scope.lookup(name)
if (s != NoSymbol && s.owner == expectedOwner)
res = s
else
ctx = ctx.outer
}
res
}
}
class ImportInfo(val tree: Import, val depth: Int) {
def qual: Tree = tree.symbol.info match {
case ImportType(expr) => expr
case ErrorType => tree setType NoType
case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info);
}
def isExplicitImport(name: Name): Boolean =
tree.selectors exists (_.rename == name.toTermName)
def importedSymbol(name: Name): Symbol = {
var result: Symbol = NoSymbol
var renamed = false
var selectors = tree.selectors
while (selectors != Nil && result == NoSymbol) {
if (selectors.head.rename == name.toTermName)
result = qual.tpe.nonLocalMember(
if (name.isTypeName) selectors.head.name.toTypeName else selectors.head.name)
else if (selectors.head.name == name.toTermName)
renamed = true
else if (selectors.head.name == nme.WILDCARD && !renamed)
result = qual.tpe.nonLocalMember(name)
selectors = selectors.tail
}
result
}
def allImportedSymbols: List[Symbol] =
qual.tpe.members flatMap (transformImport(tree.selectors, _))
private def transformImport(selectors: List[ImportSelector], sym: Symbol): List[Symbol] = selectors match {
case List() => List()
case List(ImportSelector(nme.WILDCARD, _, _, _)) => List(sym)
case ImportSelector(from, _, to, _) :: _ if (from == sym.name) =>
if (to == nme.WILDCARD) List()
else { val sym1 = sym.cloneSymbol; sym1.name = to; List(sym1) }
case _ :: rest => transformImport(rest, sym)
}
override def toString() = tree.toString()
}
case class ImportType(expr: Tree) extends Type {
override def safeToString = "ImportType("+expr+")"
}
}