|
Scala example source code file (Typers.scala)
This example Scala source code file (Typers.scala) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Scala Typers.scala source code
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
* @author Martin Odersky
*/
// Added: Sat Oct 7 16:08:21 2006
//todo: use inherited type info also for vars and values
// Added: Thu Apr 12 18:23:58 2007
//todo: disallow C#D in superclass
//todo: treat :::= correctly
package scala.tools.nsc
package typechecker
import scala.collection.{ mutable, immutable }
import scala.tools.nsc.util.BatchSourceFile
import mutable.ListBuffer
import symtab.Flags._
import util.Statistics
import util.Statistics._
import scala.tools.util.StringOps.{ countAsString, countElementsAsString }
// Suggestion check whether we can do without priming scopes with symbols of outer scopes,
// like the IDE does.
/** This trait provides methods to assign types to trees.
*
* @author Martin Odersky
* @version 1.0
*/
trait Typers extends Modes {
self: Analyzer =>
import global._
import definitions._
final def forArgMode(fun: Tree, mode: Int) =
if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode
else mode
// namer calls typer.computeType(rhs) on DefDef / ValDef when tpt is empty. the result
// is cached here and re-used in typedDefDef / typedValDef
// Also used to cache imports type-checked by namer.
val transformed = new mutable.HashMap[Tree, Tree]
final val shortenImports = false
def resetTyper() {
//println("resetTyper called")
resetContexts()
resetNamer()
resetImplicits()
transformed.clear()
resetSynthetics()
}
object UnTyper extends Traverser {
override def traverse(tree: Tree) = {
if (tree != EmptyTree) tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
super.traverse(tree)
}
}
/* needed for experimental version where eraly types can be type arguments
class EarlyMap(clazz: Symbol) extends TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(NoPrefix, sym, List()) if (sym hasFlag PRESUPER) =>
TypeRef(ThisType(clazz), sym, List())
case _ =>
mapOver(tp)
}
}
*/
def newTyper(context: Context): Typer = new NormalTyper(context)
private class NormalTyper(context : Context) extends Typer(context)
// A transient flag to mark members of anonymous classes
// that are turned private by typedBlock
private final val SYNTHETIC_PRIVATE = TRANS_FLAG
abstract class Typer(context0: Context) extends TyperDiagnostics {
import context0.unit
import typeDebug.{ ptTree, ptBlock, ptLine }
val infer = new Inferencer(context0) {
override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281
tp.isError || pt.isError ||
context0.implicitsEnabled && // this condition prevents chains of views
inferView(EmptyTree, tp, pt, false) != EmptyTree
}}
/** Find implicit arguments and pass them to given tree.
*/
def applyImplicitArgs(fun: Tree): Tree = fun.tpe match {
case MethodType(params, _) =>
val argResultsBuff = new ListBuffer[SearchResult]()
val argBuff = new ListBuffer[Tree]()
def mkPositionalArg(argTree: Tree, paramName: Name) = argTree
def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree)))
var mkArg: (Tree, Name) => Tree = mkPositionalArg
def errorMessage(paramName: Name, paramTp: Type) =
paramTp.typeSymbol match {
case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
case _ =>
"could not find implicit value for "+
(if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type "
else "parameter "+paramName+": ")+paramTp
}
// DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1))
//
// apply the substitutions (undet type param -> type) that were determined
// by implicit resolution of implicit arguments on the left of this argument
for(param <- params) {
var paramTp = param.tpe
for(ar <- argResultsBuff)
paramTp = paramTp.subst(ar.subst.from, ar.subst.to)
val res = inferImplicit(fun, paramTp, true, false, context)
argResultsBuff += res
if (res != SearchFailure) {
argBuff += mkArg(res.tree, param.name)
} else {
mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
if (!param.hasDefault)
context.error(fun.pos, errorMessage(param.name, param.tpe))
/* else {
TODO: alternative (to expose implicit search failure more) -->
resolve argument, do type inference, keep emitting positional args, infer type params based on default value for arg
for (ar <- argResultsBuff) ar.subst traverse defaultVal
val targs = exprTypeArgs(context.undetparams, defaultVal.tpe, paramTp)
substExpr(tree, tparams, targs, pt)
}*/
}
}
val args = argBuff.toList
for (ar <- argResultsBuff) {
ar.subst traverse fun
for (arg <- args) ar.subst traverse arg
}
new ApplyToImplicitArgs(fun, args) setPos fun.pos
case ErrorType =>
fun
}
/** Infer an implicit conversion (``view'') between two types.
* @param tree The tree which needs to be converted.
* @param from The source type of the conversion
* @param to The target type of the conversion
* @param reportAmbiguous Should ambiguous implicit errors be reported?
* False iff we search for a view to find out
* whether one type is coercible to another.
*/
def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = {
if (settings.debug.value) log("infer view from "+from+" to "+to)//debug
if (phase.id > currentRun.typerPhase.id) EmptyTree
else from match {
case MethodType(_, _) => EmptyTree
case OverloadedType(_, _) => EmptyTree
case PolyType(_, _) => EmptyTree
case _ =>
def wrapImplicit(from: Type): Tree = {
val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context)
if (result.subst != EmptyTreeTypeSubstituter) result.subst traverse tree
result.tree
}
val result = wrapImplicit(from)
if (result != EmptyTree) result
else wrapImplicit(appliedType(ByNameParamClass.typeConstructor, List(from)))
}
}
import infer._
private var namerCache: Namer = null
def namer = {
if ((namerCache eq null) || namerCache.context != context)
namerCache = newNamer(context)
namerCache
}
var context = context0
def context1 = context
/** Check that <code>tree is a stable expression.
*
* @param tree ...
* @return ...
*/
def checkStable(tree: Tree): Tree =
if (treeInfo.isPureExpr(tree)) tree
else errorTree(
tree,
"stable identifier required, but "+tree+" found."+
(if (isStableExceptVolatile(tree)) {
val tpe = tree.symbol.tpe match {
case PolyType(_, rtpe) => rtpe
case t => t
}
"\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile."
} else ""))
/** Would tree be a stable (i.e. a pure expression) if the type
* of its symbol was not volatile?
*/
private def isStableExceptVolatile(tree: Tree) = {
tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile &&
{ val savedTpe = tree.symbol.info
val savedSTABLE = tree.symbol getFlag STABLE
tree.symbol setInfo AnyRefClass.tpe
tree.symbol setFlag STABLE
val result = treeInfo.isPureExpr(tree)
tree.symbol setInfo savedTpe
tree.symbol setFlag savedSTABLE
result
}
}
/** Check that `tpt' refers to a non-refinement class type */
def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean) {
def check(tpe: Type): Unit = tpe.normalize match {
case TypeRef(pre, sym, _) if sym.isClass && !sym.isRefinementClass =>
if (stablePrefix && phase.id <= currentRun.typerPhase.id && !pre.isStable)
error(tpt.pos, "type "+pre+" is not a stable prefix")
case ErrorType => ;
case PolyType(_, restpe) => check(restpe)
case ExistentialType(_, restpe) if existentialOK => check(restpe)
case AnnotatedType(_, underlying, _) => check(underlying)
case t => error(tpt.pos, "class type required but "+t+" found")
}
check(tpt.tpe)
}
/** Check that type <code>tp is not a subtype of itself.
*
* @param pos ...
* @param tp ...
* @return <code>true if tp is not a subtype of itself.
*/
def checkNonCyclic(pos: Position, tp: Type): Boolean = {
def checkNotLocked(sym: Symbol): Boolean = {
sym.initialize
sym.lockOK || {error(pos, "cyclic aliasing or subtyping involving "+sym); false}
}
tp match {
case TypeRef(pre, sym, args) =>
(checkNotLocked(sym)) && (
!sym.isNonClassType ||
checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym) // @M! info for a type ref to a type parameter now returns a polytype
// @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym)
)
case SingleType(pre, sym) =>
checkNotLocked(sym)
/*
case TypeBounds(lo, hi) =>
var ok = true
for (t <- lo) ok = ok & checkNonCyclic(pos, t)
ok
*/
case st: SubType =>
checkNonCyclic(pos, st.supertype)
case ct: CompoundType =>
ct.parents forall (x => checkNonCyclic(pos, x))
case _ =>
true
}
}
def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try {
lockedSym.lock {
throw new TypeError("illegal cyclic reference involving " + lockedSym)
}
checkNonCyclic(pos, tp)
} finally {
lockedSym.unlock()
}
def checkNonCyclic(sym: Symbol) {
if (!checkNonCyclic(sym.pos, sym.tpe)) sym.setInfo(ErrorType)
}
def checkNonCyclic(defn: Tree, tpt: Tree) {
if (!checkNonCyclic(defn.pos, tpt.tpe, defn.symbol)) {
tpt.tpe = ErrorType
defn.symbol.setInfo(ErrorType)
}
}
def checkParamsConvertible(pos: Position, tpe: Type) {
tpe match {
case MethodType(formals, restpe) =>
/*
if (formals.exists(_.typeSymbol == ByNameParamClass) && formals.length != 1)
error(pos, "methods with `=>'-parameter can be converted to function values only if they take no other parameters")
if (formals exists (isRepeatedParamType(_)))
error(pos, "methods with `*'-parameters cannot be converted to function values");
*/
if (restpe.isDependent)
error(pos, "method with dependent type "+tpe+" cannot be converted to function value")
checkParamsConvertible(pos, restpe)
case _ =>
}
}
def checkStarPatOK(pos: Position, mode: Int) =
if ((mode & STARmode) == 0 && phase.id <= currentRun.typerPhase.id)
error(pos, "star patterns must correspond with varargs parameters")
/** Check that type of given tree does not contain local or private
* components.
*/
object checkNoEscaping extends TypeMap {
private var owner: Symbol = _
private var scope: Scope = _
private var hiddenSymbols: List[Symbol] = _
/** Check that type <code>tree does not refer to private
* components unless itself is wrapped in something private
* (<code>owner tells where the type occurs).
*
* @param owner ...
* @param tree ...
* @return ...
*/
def privates[T <: Tree](owner: Symbol, tree: T): T =
check(owner, EmptyScope, WildcardType, tree)
/** Check that type <code>tree does not refer to entities
* defined in scope <code>scope.
*
* @param scope ...
* @param pt ...
* @param tree ...
* @return ...
*/
def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T =
check(NoSymbol, scope, pt, tree)
def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = {
this.owner = owner
this.scope = scope
hiddenSymbols = List()
val tp1 = apply(tree.tpe)
if (hiddenSymbols.isEmpty) tree setType tp1
else if (hiddenSymbols exists (_.isErroneous)) setError(tree)
else if (isFullyDefined(pt)) tree setType pt //todo: eliminate
else if (tp1.typeSymbol.isAnonymousClass) // todo: eliminate
check(owner, scope, pt, tree setType tp1.typeSymbol.classBound)
else if (owner == NoSymbol)
tree setType packSymbols(hiddenSymbols.reverse, tp1)
else if (!phase.erasedTypes) { // privates
val badSymbol = hiddenSymbols.head
error(tree.pos,
(if (badSymbol.isPrivate) "private " else "") + badSymbol +
" escapes its defining scope as part of type "+tree.tpe)
setError(tree)
} else tree
}
def addHidden(sym: Symbol) =
if (!(hiddenSymbols contains sym)) hiddenSymbols = sym :: hiddenSymbols
override def apply(t: Type): Type = {
def checkNoEscape(sym: Symbol) {
if (sym.isPrivate && !sym.hasFlag(SYNTHETIC_PRIVATE)) {
var o = owner
while (o != NoSymbol && o != sym.owner && o != sym.owner.linkedClassOfClass &&
!o.isLocal && !o.isPrivate &&
!o.privateWithin.hasTransOwner(sym.owner))
o = o.owner
if (o == sym.owner || o == sym.owner.linkedClassOfClass)
addHidden(sym)
} else if (sym.owner.isTerm && !sym.isTypeParameterOrSkolem) {
var e = scope.lookupEntry(sym.name)
var found = false
while (!found && (e ne null) && e.owner == scope) {
if (e.sym == sym) {
found = true
addHidden(sym)
} else {
e = scope.lookupNextEntry(e)
}
}
}
}
mapOver(
t match {
case TypeRef(_, sym, args) =>
checkNoEscape(sym)
if (!hiddenSymbols.isEmpty && hiddenSymbols.head == sym &&
sym.isAliasType && sameLength(sym.typeParams, args)) {
hiddenSymbols = hiddenSymbols.tail
t.normalize
} else t
case SingleType(_, sym) =>
checkNoEscape(sym)
t
case _ =>
t
})
}
}
def reenterValueParams(vparamss: List[List[ValDef]]) {
for (vparams <- vparamss)
for (vparam <- vparams)
vparam.symbol = context.scope enter vparam.symbol
}
def reenterTypeParams(tparams: List[TypeDef]): List[Symbol] =
for (tparam <- tparams) yield {
tparam.symbol = context.scope enter tparam.symbol
tparam.symbol.deSkolemize
}
/** The qualifying class
* of a this or super with prefix <code>qual.
*/
def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean): Symbol =
context.enclClass.owner.ownerChain.find(o => qual.isEmpty || o.isClass && o.name == qual) match {
case Some(c) if packageOK || !c.isPackageClass =>
c
case _ =>
error(
tree.pos,
if (qual.isEmpty) tree+" can be used only in a class, object, or template"
else qual+" is not an enclosing class")
NoSymbol
}
/** The typer for an expression, depending on where we are. If we are before a superclass
* call, this is a typer over a constructor context; otherwise it is the current typer.
*/
def constrTyperIf(inConstr: Boolean): Typer =
if (inConstr) {
assert(context.undetparams.isEmpty)
newTyper(context.makeConstructorContext)
} else this
/** The typer for a label definition. If this is part of a template we
* first have to enter the label definition.
*/
def labelTyper(ldef: LabelDef): Typer =
if (ldef.symbol == NoSymbol) { // labeldef is part of template
val typer1 = newTyper(context.makeNewScope(ldef, context.owner))
typer1.enterLabelDef(ldef)
typer1
} else this
final val xtypes = false
/** Is symbol defined and not stale?
*/
def reallyExists(sym: Symbol) = {
if (isStale(sym)) sym.setInfo(NoType)
sym.exists
}
/** A symbol is stale if it is toplevel, to be loaded from a classfile, and
* the classfile is produced from a sourcefile which is compiled in the current run.
*/
def isStale(sym: Symbol): Boolean = {
sym.rawInfo.isInstanceOf[loaders.ClassfileLoader] && {
sym.rawInfo.load(sym)
(sym.sourceFile ne null) &&
(currentRun.compiledFiles contains sym.sourceFile.path)
}
}
/** Does the context of tree <code>tree require a stable type?
*/
private def isStableContext(tree: Tree, mode: Int, pt: Type) =
isNarrowable(tree.tpe) && ((mode & (EXPRmode | LHSmode)) == EXPRmode) &&
(xtypes ||
(pt.isStable ||
(mode & QUALmode) != 0 && !tree.symbol.isConstant ||
pt.typeSymbol.isAbstractType && pt.bounds.lo.isStable && !(tree.tpe <:< pt)) ||
pt.typeSymbol.isRefinementClass && !(tree.tpe <:< pt))
/** Make symbol accessible. This means:
* If symbol refers to package object, insert `.package` as second to last selector.
* (exception for some symbols in scala package which are dealiased immediately)
* Call checkAccessible, which sets tree's attributes.
* Also note that checkAccessible looks up sym on pre without checking that pre is well-formed
* (illegal type applications in pre will be skipped -- that's why typedSelect wraps the resulting tree in a TreeWithDeferredChecks)
* @return modified tree and new prefix type
*/
private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) =
if (isInPackageObject(sym, pre.typeSymbol)) {
if (pre.typeSymbol == ScalaPackageClass && sym.isTerm) {
// short cut some aliases. It seems that without that pattern matching
// fails to notice exhaustiveness and to generate good code when
// List extractors are mixed with :: patterns. See Test5 in lists.scala.
def dealias(sym: Symbol) =
({ val t = gen.mkAttributedRef(sym) ; t.setPos(tree.pos) ; t }, sym.owner.thisType)
sym.name match {
case nme.List => return dealias(ListModule)
case nme.Seq => return dealias(SeqModule)
case nme.Nil => return dealias(NilModule)
case _ =>
}
}
val qual = typedQualifier { atPos(tree.pos.focusStart) {
tree match {
case Ident(_) => Ident(nme.PACKAGEkw)
case Select(qual, _) => Select(qual, nme.PACKAGEkw)
case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw)
}
}}
val tree1 = atPos(tree.pos) {
tree match {
case Ident(name) => Select(qual, name)
case Select(_, name) => Select(qual, name)
case SelectFromTypeTree(_, name) => SelectFromTypeTree(qual, name)
}
}
(checkAccessible(tree1, sym, qual.tpe, qual), qual.tpe)
} else {
(checkAccessible(tree, sym, pre, site), pre)
}
/** Is `sym` defined in package object of package `pkg`?
*/
private def isInPackageObject(sym: Symbol, pkg: Symbol) =
pkg.isPackageClass && {
sym.alternatives forall { sym =>
!sym.owner.isPackage && {
sym.owner.isPackageObjectClass &&
sym.owner.owner == pkg ||
pkg.isInitialized && {
// need to be careful here to not get a cyclic reference during bootstrap
val pkgobj = pkg.info.member(nme.PACKAGEkw)
pkgobj.isInitialized &&
(pkgobj.info.member(sym.name).alternatives contains sym)
}
}
}
}
/** Post-process an identifier or selection node, performing the following:
* 1. Check that non-function pattern expressions are stable
* 2. Check that packages and static modules are not used as values
* 3. Turn tree type into stable type if possible and required by context.
*/
private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = {
if (tree.symbol.isOverloaded && !inFunMode(mode))
inferExprAlternative(tree, pt)
val sym = tree.symbol
if (tree.tpe.isError) tree
else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1)
if (sym.isValue) checkStable(tree)
else errorTree(tree, sym+" is not a value")
} else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2)
errorTree(tree, sym+" is not a value")
} else {
if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) &&
(isStableContext(tree, mode, pt) || sym.isModule && !sym.isMethod))
tree.setType(singleType(pre, sym))
else tree
}
}
private def isNarrowable(tpe: Type): Boolean = tpe match {
case TypeRef(_, _, _) | RefinedType(_, _) => true
case ExistentialType(_, tpe1) => isNarrowable(tpe1)
case AnnotatedType(_, tpe1, _) => isNarrowable(tpe1)
case PolyType(_, tpe1) => isNarrowable(tpe1)
case NullaryMethodType(tpe1) => isNarrowable(tpe1)
case _ => !phase.erasedTypes
}
/**
* @param tree ...
* @param mode ...
* @param pt ...
* @return ...
*/
def stabilizeFun(tree: Tree, mode: Int, pt: Type): Tree = {
val sym = tree.symbol
val pre = tree match {
case Select(qual, _) => qual.tpe
case _ => NoPrefix
}
if (tree.tpe.isInstanceOf[MethodType] && pre.isStable && sym.tpe.params.isEmpty &&
(isStableContext(tree, mode, pt) || sym.isModule))
tree.setType(MethodType(List(), singleType(pre, sym)))
else tree
}
/** The member with given name of given qualifier tree */
def member(qual: Tree, name: Name) = {
def callSiteWithinClass(clazz: Symbol) = context.enclClass.owner hasTransOwner clazz
val includeLocals = qual.tpe match {
case ThisType(clazz) if callSiteWithinClass(clazz) => true
case SuperType(clazz, _) if callSiteWithinClass(clazz.typeSymbol) => true
case _ => phase.next.erasedTypes
}
if (includeLocals) qual.tpe member name
else qual.tpe nonLocalMember name
}
def silent[T](op: Typer => T,
reportAmbiguousErrors: Boolean = context.reportAmbiguousErrors,
newtree: Tree = context.tree): Any /* in fact, TypeError or T */ = {
val rawTypeStart = startCounter(rawTypeFailed)
val findMemberStart = startCounter(findMemberFailed)
val subtypeStart = startCounter(subtypeFailed)
val failedSilentStart = startTimer(failedSilentNanos)
try {
if (context.reportGeneralErrors ||
reportAmbiguousErrors != context.reportAmbiguousErrors ||
newtree != context.tree) {
val context1 = context.makeSilent(reportAmbiguousErrors, newtree)
context1.undetparams = context.undetparams
context1.savedTypeBounds = context.savedTypeBounds
context1.namedApplyBlockInfo = context.namedApplyBlockInfo
val typer1 = newTyper(context1)
val result = op(typer1)
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
result
} else {
op(this)
}
} catch {
case ex: CyclicReference => throw ex
case ex: TypeError =>
stopCounter(rawTypeFailed, rawTypeStart)
stopCounter(findMemberFailed, findMemberStart)
stopCounter(subtypeFailed, subtypeStart)
stopTimer(failedSilentNanos, failedSilentStart)
ex
}
}
/** Utility method: Try op1 on tree. If that gives an error try op2 instead.
*/
def tryBoth(tree: Tree)(op1: (Typer, Tree) => Tree)(op2: (Typer, Tree) => Tree): Tree =
silent(op1(_, tree)) match {
case result1: Tree =>
result1
case ex1: TypeError =>
silent(op2(_, resetAllAttrs(tree))) match {
case result2: Tree =>
// println("snd succeeded: "+result2)
result2
case ex2: TypeError =>
reportTypeError(tree.pos, ex1)
setError(tree)
}
}
/** Perform the following adaptations of expression, pattern or type `tree' wrt to
* given mode `mode' and given prototype `pt':
* (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
* (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
* (1) Resolve overloading, unless mode contains FUNmode
* (2) Apply parameterless functions
* (3) Apply polymorphic types to fresh instances of their type parameters and
* store these instances in context.undetparams,
* unless followed by explicit type application.
* (4) Do the following to unapplied methods used as values:
* (4.1) If the method has only implicit parameters pass implicit arguments
* (4.2) otherwise, if `pt' is a function type and method is not a constructor,
* convert to function by eta-expansion,
* (4.3) otherwise, if the method is nullary with a result type compatible to `pt'
* and it is not a constructor, apply it to ()
* otherwise issue an error
* (5) Convert constructors in a pattern as follows:
* (5.1) If constructor refers to a case class factory, set tree's type to the unique
* instance of its primary constructor that is a subtype of the expected type.
* (5.2) If constructor refers to an extractor, convert to application of
* unapply or unapplySeq method.
*
* (6) Convert all other types to TypeTree nodes.
* (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized
* (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity
* (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
* (9) If there are undetermined type variables and not POLYmode, infer expression instance
* Then, if tree's type is not a subtype of expected type, try the following adaptations:
* (10) If the expected type is Byte, Short or Char, and the expression
* is an integer fitting in the range of that type, convert it to that type.
* (11) Widen numeric literals to their expected type, if necessary
* (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
* (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
* (14) When in mode EXPRmode, apply a view
* If all this fails, error
*/
protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = tree.tpe match {
case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1)
adaptAnnotations(tree, mode, pt)
case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0)
val sym = tree.symbol
if (sym != null && sym.isDeprecated) {
val msg = sym.toString + sym.locationString +" is deprecated: "+ sym.deprecationMessage.getOrElse("")
unit.deprecationWarning(tree.pos, msg)
}
treeCopy.Literal(tree, value)
case OverloadedType(pre, alts) if !inFunMode(mode) => // (1)
inferExprAlternative(tree, pt)
adapt(tree, mode, pt, original)
case NullaryMethodType(restpe) => // (2)
adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, ByNameParamClass, List(arg))
if ((mode & EXPRmode) != 0) => // (2)
adapt(tree setType arg, mode, pt, original)
case tr @ TypeRef(_, sym, _)
if sym.isAliasType && tr.normalize.isInstanceOf[ExistentialType] &&
((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt, original)
case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original)
case PolyType(tparams, restpe) if inNoModes(mode, TAPPmode | PATTERNmode | HKmode) => // (3)
// assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
// we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params!
// ticket #2197 triggered turning the assert into a guard
// I guess this assert wasn't violated before because type aliases weren't expanded as eagerly
// (the only way to get a PolyType for an anonymous type function is by normalisation, which applies eta-expansion)
// -- are we sure we want to expand aliases this early?
// -- what caused this change in behaviour??
val tparams1 = cloneSymbols(tparams)
val tree1 = if (tree.isType) tree
else TypeApply(tree, tparams1 map (tparam =>
TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos //@M/tcpolyinfer: changed tparam.tpe to tparam.tpeHK
context.undetparams ++= tparams1
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0`
// dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed
// needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition?
context.undetparams =
inferExprInstance(tree, context.extractUndetparams(), pt,
// approximate types that depend on arguments since dependency on implicit argument is like dependency on type parameter
if(settings.YdepMethTpes.value) mt.approximate else mt,
// if we are looking for a manifest, instantiate type to Nothing anyway,
// as we would get ambiguity errors otherwise. Example
// Looking for a manifest of Nil: This has many potential types,
// so we need to instantiate to minimal type List[Nothing].
keepNothings = false, // retract Nothing's that indicate failure, ambiguities in manifests are dealt with in manifestOfType
useWeaklyCompatible = true) // #3808
}
val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree))
if (original != EmptyTree && pt != WildcardType)
typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match {
case result: Tree => result
case ex: TypeError =>
if (settings.debug.value) log("fallback on implicits: "+tree+"/"+resetAllAttrs(original))
val tree1 = typed(resetAllAttrs(original), mode, WildcardType)
tree1.tpe = addAnnotations(tree1, tree1.tpe)
if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree)
}
else
typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
case mt: MethodType
if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) &&
(context.undetparams.isEmpty || inPolyMode(mode))) =>
val meth = tree match {
// a partial named application is a block (see comment in EtaExpansion)
case Block(_, tree1) => tree1.symbol
case _ => tree.symbol
}
if (!meth.isConstructor && isFunctionType(pt)) { // (4.2)
if (settings.debug.value) log("eta-expanding "+tree+":"+tree.tpe+" to "+pt)
checkParamsConvertible(tree.pos, tree.tpe)
val tree0 = etaExpand(context.unit, tree)
// println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode))
if (meth.typeParams.nonEmpty) {
// #2624: need to infer type arguments for eta expansion of a polymorphic method
// context.undetparams contains clones of meth.typeParams (fresh ones were generated in etaExpand)
// need to run typer on tree0, since etaExpansion sets the tpe's of its subtrees to null
// can't type with the expected type, as we can't recreate the setup in (3) without calling typed
// (note that (3) does not call typed to do the polymorphic type instantiation --
// it is called after the tree has been typed with a polymorphic expected result type)
instantiate(typed(tree0, mode, WildcardType), mode, pt)
} else
typed(tree0, mode, pt)
} else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3)
adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original)
} else if (context.implicitsEnabled) {
errorTree(tree, "missing arguments for "+meth+meth.locationString+
(if (meth.isConstructor) ""
else ";\nfollow this method with `_' if you want to treat it as a partially applied function"))
} else {
setError(tree)
}
case _ =>
def applyPossible = {
def applyMeth = member(adaptToName(tree, nme.apply), nme.apply)
if ((mode & TAPPmode) != 0)
tree.tpe.typeParams.isEmpty && applyMeth.filter(! _.tpe.typeParams.isEmpty) != NoSymbol
else
applyMeth.filter(_.tpe.paramSectionCount > 0) != NoSymbol
}
if (tree.isType) {
if (inFunMode(mode)) {
tree
} else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty && !inHKMode(mode) &&
!(tree.symbol.isJavaDefined && context.unit.isJava)) { // (7)
// @M When not typing a higher-kinded type ((mode & HKmode) == 0)
// or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *,
// and thus parameterized types must be applied to their type arguments
// @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't?
errorTree(tree, tree.symbol+" takes type parameters")
tree setType tree.tpe
} else if ( // (7.1) @M: check kind-arity
// @M: removed check for tree.hasSymbol and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol)
(inHKMode(mode)) &&
// @M: don't check tree.tpe.symbol.typeParams. check tree.tpe.typeParams!!!
// (e.g., m[Int] --> tree.tpe.symbol.typeParams.length == 1, tree.tpe.typeParams.length == 0!)
!sameLength(tree.tpe.typeParams, pt.typeParams) &&
!(tree.tpe.typeSymbol==AnyClass ||
tree.tpe.typeSymbol==NothingClass ||
pt == WildcardType )) {
// Check that the actual kind arity (tree.symbol.typeParams.length) conforms to the expected
// kind-arity (pt.typeParams.length). Full checks are done in checkKindBounds in Infer.
// Note that we treat Any and Nothing as kind-polymorphic.
// We can't perform this check when typing type arguments to an overloaded method before the overload is resolved
// (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1).
errorTree(tree, tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+
", expected: "+countAsString(pt.typeParams.length))
tree setType tree.tpe
} else tree match { // (6)
case TypeTree() => tree
case _ => TypeTree(tree.tpe) setOriginal(tree)
}
} else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) { // (5)
val extractor = tree.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe)))
if (extractor != NoSymbol) {
tree setSymbol extractor
val unapply = unapplyMember(extractor.tpe)
val clazz = unapplyParameterType(unapply)
if (unapply.isCase && clazz.isCase && !(clazz.ancestors exists (_.isCase))) {
// convert synthetic unapply of case class to case class constructor
val prefix = tree.tpe.prefix
val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner))
.setOriginal(tree)
inferConstructorInstance(tree1, clazz.typeParams, pt)
tree1
} else {
tree
}
} else {
errorTree(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method")
}
} else if (inAllModes(mode, EXPRmode | FUNmode) &&
!tree.tpe.isInstanceOf[MethodType] &&
!tree.tpe.isInstanceOf[OverloadedType] &&
applyPossible) {
assert(!inHKMode(mode)) //@M
val qual = adaptToName(tree, nme.apply) match {
case id @ Ident(_) =>
val pre = if (id.symbol.owner.isPackageClass) id.symbol.owner.thisType
else if (id.symbol.owner.isClass)
context.enclosingSubClassContext(id.symbol.owner).prefix
else NoPrefix
stabilize(id, pre, EXPRmode | QUALmode, WildcardType)
case sel @ Select(qualqual, _) =>
stabilize(sel, qualqual.tpe, EXPRmode | QUALmode, WildcardType)
case other =>
other
}
typed(atPos(tree.pos)(Select(qual, nme.apply)), mode, pt)
} else if (!context.undetparams.isEmpty && !inPolyMode(mode)) { // (9)
assert(!inHKMode(mode)) //@M
instantiate(tree, mode, pt)
} else if (tree.tpe <:< pt) {
tree
} else {
if (inPatternMode(mode)) {
if ((tree.symbol ne null) && tree.symbol.isModule)
inferModulePattern(tree, pt)
if (isPopulated(tree.tpe, approximateAbstracts(pt)))
return tree
}
val tree1 = constfold(tree, pt) // (10) (11)
if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original)
else {
if ((mode & (EXPRmode | FUNmode)) == EXPRmode) {
pt.normalize match {
case TypeRef(_, sym, _) =>
// note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially
// infinite expansion if pt is constant type ()
if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12)
if (settings.warnValueDiscard.value)
context.unit.warning(tree.pos, "discarded non-Unit value")
return typed(atPos(tree.pos)(Block(List(tree), Literal(()))), mode, pt)
}
else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
if (settings.warnNumericWiden.value)
context.unit.warning(tree.pos, "implicit numeric widening")
return typed(atPos(tree.pos)(Select(tree, "to"+sym.name)), mode, pt)
}
case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13)
return typed(adaptAnnotations(tree, mode, pt), mode, pt)
case _ =>
}
if (!context.undetparams.isEmpty) {
return instantiate(tree, mode, pt)
}
if (context.implicitsEnabled && !tree.tpe.isError && !pt.isError) {
// (14); the condition prevents chains of views
if (settings.debug.value) log("inferring view from "+tree.tpe+" to "+pt)
val coercion = inferView(tree, tree.tpe, pt, true)
// convert forward views of delegate types into closures wrapped around
// the delegate's apply method (the "Invoke" method, which was translated into apply)
if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) {
val meth: Symbol = tree.tpe.member(nme.apply)
if(settings.debug.value)
log("replacing forward delegate view with: " + meth + ":" + meth.tpe)
return typed(Select(tree, meth), mode, pt)
}
if (coercion != EmptyTree) {
if (settings.debug.value) log("inferred view from "+tree.tpe+" to "+pt+" = "+coercion+":"+coercion.tpe)
return newTyper(context.makeImplicit(context.reportAmbiguousErrors)).typed(
new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt)
}
}
}
if (settings.debug.value) {
log("error tree = "+tree)
if (settings.explaintypes.value) explainTypes(tree.tpe, pt)
}
try {
typeErrorTree(tree, tree.tpe, pt)
} catch {
case ex: TypeError =>
if (phase.id > currentRun.typerPhase.id &&
pt.existentialSkolems.nonEmpty) {
// Ignore type errors raised in later phases that are due to mismatching types with existential skolems
// We have lift crashing in 2.9 with an adapt failure in the pattern matcher.
// Here's my hypothsis why this happens. The pattern matcher defines a variable of type
//
// val x: T = expr
//
// where T is the type of expr, but T contains existential skolems ts.
// In that case, this value definition does not typecheck.
// The value definition
//
// val x: T forSome { ts } = expr
//
// would typecheck. Or one can simply leave out the type of the `val':
//
// val x = expr
context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n"+tree+"\nwith type "+tree.tpe+"\n expected type = "+pt+"\n context = "+context.tree)
adapt(tree, mode, pt.subst(pt.existentialSkolems, pt.existentialSkolems map (_ => WildcardType)))
} else
throw ex
}
}
}
}
/**
* @param tree ...
* @param mode ...
* @param pt ...
* @return ...
*/
def instantiate(tree: Tree, mode: Int, pt: Type): Tree = {
inferExprInstance(tree, context.extractUndetparams(), pt)
adapt(tree, mode, pt)
}
def adaptToMember(qual: Tree, searchTemplate: Type): Tree = {
var qtpe = qual.tpe.widen
if (qual.isTerm &&
((qual.symbol eq null) || !qual.symbol.isTerm || qual.symbol.isValue) &&
phase.id <= currentRun.typerPhase.id && !qtpe.isError &&
qtpe.typeSymbol != NullClass && qtpe.typeSymbol != NothingClass && qtpe != WildcardType &&
!qual.isInstanceOf[ApplyImplicitView] && // don't chain views
context.implicitsEnabled) { // don't try to adapt a top-level type that's the subject of an implicit search
// this happens because, if isView, typedImplicit tries to apply the "current" implicit value to
// a value that needs to be coerced, so we check whether the implicit value has an `apply` method
// (if we allow this, we get divergence, e.g., starting at `conforms` during ant quick.bin)
// note: implicit arguments are still inferred (this kind of "chaining" is allowed)
if (qtpe.normalize.isInstanceOf[ExistentialType]) {
qtpe = qtpe.normalize.skolemizeExistential(context.owner, qual) // open the existential
qual setType qtpe
}
val coercion = inferView(qual, qual.tpe, searchTemplate, true)
if (coercion != EmptyTree)
typedQualifier(atPos(qual.pos)(new ApplyImplicitView(coercion, List(qual))))
else
qual
} else {
qual
}
}
/** Try to apply an implicit conversion to `qual' to that it contains
* a method `name` which can be applied to arguments `args' with expected type `pt'.
* If `pt' is defined, there is a fallback to try again with pt = ?.
* This helps avoiding propagating result information too far and solves
* #1756.
* If no conversion is found, return `qual' unchanged.
*
*/
def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type): Tree = {
def doAdapt(restpe: Type) =
//util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ")
adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe))
if (pt != WildcardType) {
silent(_ => doAdapt(pt)) match {
case result: Tree if result != qual =>
result
case _ =>
if (settings.debug.value) log("fallback on implicits in adaptToArguments: "+qual+" . "+name)
doAdapt(WildcardType)
}
} else
doAdapt(pt)
}
/** Try o apply an implicit conversion to `qual' to that it contains
* a method `name`. If that's ambiguous try taking arguments into account using `adaptToArguments`.
*/
def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int): Tree = {
try {
adaptToMember(qual, HasMember(name))
} catch {
case ex: TypeError =>
// this happens if implicits are ambiguous; try again with more context info.
// println("last ditch effort: "+qual+" . "+name)
context.tree match {
case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => // try handling the arguments
// println("typing args: "+args)
silent(_.typedArgs(args, mode)) match {
case args: List[_] =>
adaptToArguments(qual, name, args.asInstanceOf[List[Tree]], WildcardType)
case _ =>
throw ex
}
case _ =>
// println("not in an apply: "+context.tree+"/"+tree)
throw ex
}
}
}
/** Try to apply an implicit conversion to `qual' to that it contains a
* member `name` of arbitrary type.
* If no conversion is found, return `qual' unchanged.
*/
def adaptToName(qual: Tree, name: Name) =
if (member(qual, name) != NoSymbol) qual
else adaptToMember(qual, HasMember(name))
private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = {
// XXX: see about using the class's symbol....
enclTparams foreach (sym => context.scope.enter(sym))
namer.enterValueParams(context.owner, vparamss)
typed(cbody)
}
private def validateNoCaseAncestor(clazz: Symbol) = {
if (!phase.erasedTypes) {
for (ancestor <- clazz.ancestors find (_.isCase)) {
unit.deprecationWarning(clazz.pos, (
"case class `%s' has case ancestor `%s'. Case-to-case inheritance has potentially "+
"dangerous bugs which are unlikely to be fixed. You are strongly encouraged to "+
"instead use extractors to pattern match on non-leaf nodes."
).format(clazz, ancestor))
}
}
}
def parentTypes(templ: Template): List[Tree] =
if (templ.parents.isEmpty) List()
else try {
val clazz = context.owner
// Normalize supertype and mixins so that supertype is always a class, not a trait.
var supertpt = typedTypeConstructor(templ.parents.head)
val firstParent = supertpt.tpe.typeSymbol
var mixins = templ.parents.tail map typedType
// If first parent is a trait, make it first mixin and add its superclass as first parent
while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) {
val supertpt1 = typedType(supertpt)
if (!supertpt1.tpe.isError) {
mixins = supertpt1 :: mixins
supertpt = TypeTree(supertpt1.tpe.parents.head) setPos supertpt.pos.focus
}
}
// Determine
// - supertparams: Missing type parameters from supertype
// - supertpe: Given supertype, polymorphic in supertparams
val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List()
var supertpe = supertpt.tpe
if (!supertparams.isEmpty)
supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpe)))
// A method to replace a super reference by a New in a supercall
def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match {
case Apply(fn, args) =>
treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate))
case Select(Super(_, _), nme.CONSTRUCTOR) =>
treeCopy.Select(
scall,
atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe),
nme.CONSTRUCTOR)
}
treeInfo.firstConstructor(templ.body) match {
case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) =>
// Convert constructor body to block in environment and typecheck it
val (preSuperStats, rest) = cstats span (!treeInfo.isSuperConstrCall(_))
val (scall, upToSuperStats) =
if (rest.isEmpty) (EmptyTree, preSuperStats)
else (rest.head, preSuperStats :+ rest.head)
val cstats1: List[Tree] = upToSuperStats map (_.duplicate)
val cbody1 = scall match {
case Apply(_, _) =>
treeCopy.Block(cbody, cstats1.init,
if (supertparams.isEmpty) cunit.duplicate
else transformSuperCall(scall))
case _ =>
treeCopy.Block(cbody, cstats1, cunit.duplicate)
}
val outercontext = context.outer
assert(clazz != NoSymbol)
val cscope = outercontext.makeNewScope(constr, outercontext.owner)
val cbody2 = newTyper(cscope) // called both during completion AND typing.
.typePrimaryConstrBody(clazz,
cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate)))
scall match {
case Apply(_, _) =>
val sarg = treeInfo.firstArgument(scall)
if (sarg != EmptyTree && supertpe.typeSymbol != firstParent)
error(sarg.pos, firstParent+" is a trait; does not take constructor arguments")
if (!supertparams.isEmpty) supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus
case _ =>
if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments")
}
(cstats1, treeInfo.preSuperFields(templ.body)).zipped map {
(ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe
}
case _ =>
if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments")
}
/* experimental: early types as type arguments
val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef)
val earlyMap = new EarlyMap(clazz)
List.mapConserve(supertpt :: mixins){ tpt =>
val tpt1 = checkNoEscaping.privates(clazz, tpt)
if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe)
}
*/
//Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG
supertpt :: mixins mapConserve (tpt => checkNoEscaping.privates(clazz, tpt))
} catch {
case ex: TypeError =>
templ.tpe = null
reportTypeError(templ.pos, ex)
List(TypeTree(AnyRefClass.tpe))
}
/** <p>Check that
* <ul>
* <li>all parents are class types,
* <li>first parent class is not a mixin; following classes are mixins,
* <li>final classes are not inherited,
* <li>
* sealed classes are only inherited by classes which are
* nested within definition of base class, or that occur within same
* statement sequence,
* </li>
* <li>self-type of current class is a subtype of self-type of each parent class.
* <li>no two parents define same symbol.
* </ul>
*/
def validateParentClasses(parents: List[Tree], selfType: Type) {
def validateParentClass(parent: Tree, superclazz: Symbol) {
if (!parent.tpe.isError) {
val psym = parent.tpe.typeSymbol.initialize
checkClassType(parent, false, true)
if (psym != superclazz) {
if (psym.isTrait) {
val ps = psym.info.parents
if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol))
error(parent.pos, "illegal inheritance; super"+superclazz+
"\n is not a subclass of the super"+ps.head.typeSymbol+
"\n of the mixin " + psym)
} else {
error(parent.pos, psym+" needs to be a trait to be mixed in")
}
}
if (psym.isFinal) {
error(parent.pos, "illegal inheritance from final "+psym)
}
if (psym.isSealed && !phase.erasedTypes) {
// AnyVal is sealed, but we have to let the value classes through manually
if (context.unit.source.file == psym.sourceFile || isValueClass(context.owner))
psym addChild context.owner
else
error(parent.pos, "illegal inheritance from sealed "+psym)
}
if (!(selfType <:< parent.tpe.typeOfThis) &&
!phase.erasedTypes &&
!context.owner.isSynthetic && // don't check synthetic concrete classes for virtuals (part of DEVIRTUALIZE)
!settings.noSelfCheck.value && // setting to suppress this very check
!selfType.isErroneous &&
!parent.tpe.isErroneous)
{
//Console.println(context.owner);//DEBUG
//Console.println(context.owner.unsafeTypeParams);//DEBUG
//Console.println(List.fromArray(context.owner.info.closure));//DEBUG
error(parent.pos, "illegal inheritance;\n self-type "+
selfType+" does not conform to "+parent +
"'s selftype "+parent.tpe.typeOfThis)
if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis)
}
if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError))
error(parent.pos, psym+" is inherited twice")
}
}
if (!parents.isEmpty && !parents.head.tpe.isError)
for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol)
/*
if (settings.Xshowcls.value != "" &&
settings.Xshowcls.value == context.owner.fullName)
println("INFO "+context.owner+
", baseclasses = "+(context.owner.info.baseClasses map (_.fullName))+
", lin = "+(context.owner.info.baseClasses map (context.owner.thisType.baseType)))
*/
}
def checkFinitary(classinfo: ClassInfoType) {
val clazz = classinfo.typeSymbol
for (tparam <- clazz.typeParams) {
if (classinfo.expansiveRefs(tparam) contains tparam) {
error(tparam.pos, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive")
val newinfo = ClassInfoType(
classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefClass.tpe))),
classinfo.decls,
clazz)
clazz.setInfo {
clazz.info match {
case PolyType(tparams, _) => PolyType(tparams, newinfo)
case _ => newinfo
}
}
}
}
}
/**
* @param cdef ...
* @return ...
*/
def typedClassDef(cdef: ClassDef): Tree = {
// attributes(cdef)
val clazz = cdef.symbol
val typedMods = removeAnnotations(cdef.mods)
assert(clazz != NoSymbol)
reenterTypeParams(cdef.tparams)
val tparams1 = cdef.tparams mapConserve (typedTypeDef)
val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope))
.typedTemplate(cdef.impl, parentTypes(cdef.impl))
val impl2 = typerAddSyntheticMethods(impl1, clazz, context)
if ((clazz != ClassfileAnnotationClass) &&
(clazz isNonBottomSubClass ClassfileAnnotationClass))
restrictionWarning(cdef.pos, unit,
"subclassing Classfile does not\n"+
"make your annotation visible at runtime. If that is what\n"+
"you want, you must write the annotation class in Java.")
if (phase.id <= currentRun.typerPhase.id) {
for (ann <- clazz.getAnnotation(DeprecatedAttr)) {
val m = companionModuleOf(clazz, context)
if (m != NoSymbol)
m.moduleClass.addAnnotation(AnnotationInfo(ann.atp, ann.args, List()))
}
}
treeCopy.ClassDef(cdef, typedMods, cdef.name, tparams1, impl2)
.setType(NoType)
}
/**
* @param mdef ...
* @return ...
*/
def typedModuleDef(mdef: ModuleDef): Tree = {
//Console.println("sourcefile of " + mdef.symbol + "=" + mdef.symbol.sourceFile)
// attributes(mdef)
// initialize all constructors of the linked class: the type completer (Namer.methodSig)
// might add default getters to this object. example: "object T; class T(x: Int = 1)"
val linkedClass = companionClassOf(mdef.symbol, context)
if (linkedClass != NoSymbol)
for (c <- linkedClass.info.decl(nme.CONSTRUCTOR).alternatives)
c.initialize
val clazz = mdef.symbol.moduleClass
val maybeAddSerializable = (l: List[Tree]) =>
if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) l
else {
clazz.makeSerializable()
l :+ TypeTree(SerializableClass.tpe)
}
val typedMods = removeAnnotations(mdef.mods)
assert(clazz != NoSymbol)
val impl1 = newTyper(context.make(mdef.impl, clazz, new Scope))
.typedTemplate(mdef.impl, maybeAddSerializable(parentTypes(mdef.impl)))
val impl2 = typerAddSyntheticMethods(impl1, clazz, context)
treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
}
/** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
* all the time, it is exposed here the module/class typing methods go through it.
*/
protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = {
addSyntheticMethods(templ, clazz, context)
}
/**
* @param stat ...
* @return ...
*/
def addGetterSetter(stat: Tree): List[Tree] = stat match {
case ValDef(mods, name, tpt, rhs)
// PRIVATE | LOCAL are fields generated for primary constructor arguments
if !mods.isPrivateLocal && !stat.symbol.isModuleVar =>
val isDeferred = mods.isDeferred
val value = stat.symbol
val allAnnots = value.annotations
if (!isDeferred)
// keepClean: by default annotations go to the field, except if the field is
// generated for a class parameter (PARAMACCESSOR).
value.setAnnotations(memberAnnots(allAnnots, FieldTargetClass, keepClean = !mods.isParamAccessor))
val getter = if (isDeferred) value else value.getter(value.owner)
assert(getter != NoSymbol, stat)
if (getter.isOverloaded)
error(getter.pos, getter+" is defined twice")
getter.setAnnotations(memberAnnots(allAnnots, GetterTargetClass))
if (value.isLazy) List(stat)
else {
val vdef = treeCopy.ValDef(stat, mods | PRIVATE | LOCAL, nme.getterToLocal(name), tpt, rhs)
val getterDef: DefDef = atPos(vdef.pos.focus) {
if (isDeferred) {
val r = DefDef(getter, EmptyTree)
r.tpt.asInstanceOf[TypeTree].setOriginal(tpt) // keep type tree of original abstract field
r
} else {
val rhs = gen.mkCheckInit(Select(This(value.owner), value))
val r = typed {
atPos(getter.pos.focus) {
DefDef(getter, rhs)
}
}.asInstanceOf[DefDef]
r.tpt.setPos(tpt.pos.focus)
r
}
}
checkNoEscaping.privates(getter, getterDef.tpt)
def setterDef(setter: Symbol, isBean: Boolean = false): DefDef = {
setter setAnnotations memberAnnots(allAnnots, if (isBean) BeanSetterTargetClass else SetterTargetClass)
val defTree =
if ((mods hasFlag DEFERRED) || (setter hasFlag OVERLOADED)) EmptyTree
else Assign(Select(This(value.owner), value), Ident(setter.paramss.head.head))
typedPos(vdef.pos.focus)(DefDef(setter, defTree)).asInstanceOf[DefDef]
}
val gs = new ListBuffer[DefDef]
gs.append(getterDef)
if (mods.isMutable) {
val setter = getter.setter(value.owner)
gs.append(setterDef(setter))
}
if (!forMSIL && (value.hasAnnotation(BeanPropertyAttr) ||
value.hasAnnotation(BooleanBeanPropertyAttr))) {
val nameSuffix = name.toString().capitalize
val beanGetterName =
(if (value.hasAnnotation(BooleanBeanPropertyAttr)) "is" else "get") +
nameSuffix
val beanGetter = value.owner.info.decl(beanGetterName)
if (beanGetter == NoSymbol) {
// the namer decides wether to generate these symbols or not. at that point, we don't
// have symbolic information yet, so we only look for annotations named "BeanProperty".
unit.error(stat.pos, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import")
}
beanGetter.setAnnotations(memberAnnots(allAnnots, BeanGetterTargetClass))
if (mods.isMutable && beanGetter != NoSymbol) {
val beanSetterName = "set" + nameSuffix
val beanSetter = value.owner.info.decl(beanSetterName)
// unlike for the beanGetter, the beanSetter body is generated here. see comment in Namers.
gs.append(setterDef(beanSetter, isBean = true))
}
}
if (mods.isDeferred) gs.toList else vdef :: gs.toList
}
case dd @ DocDef(comment, defn) =>
addGetterSetter(defn) map (stat => DocDef(comment, stat) setPos dd.pos)
case Annotated(annot, defn) =>
addGetterSetter(defn) map (stat => Annotated(annot, stat))
case _ =>
List(stat)
}
/**
* The annotations amongst `annots` that should go on a member of class
* `memberClass` (field, getter, setter, beanGetter, beanSetter, param)
* If 'keepClean' is true, annotations without any meta-annotation are kept
*/
protected def memberAnnots(annots: List[AnnotationInfo], memberClass: Symbol, keepClean: Boolean = false) = {
def hasMatching(metaAnnots: List[AnnotationInfo], orElse: => Boolean) = {
// either one of the meta-annotations matches the `memberClass`
metaAnnots.exists(_.atp.typeSymbol == memberClass) ||
// else, if there is no `target` meta-annotation at all, use the default case
(metaAnnots.forall(ann => {
val annClass = ann.atp.typeSymbol
annClass != FieldTargetClass && annClass != GetterTargetClass &&
annClass != SetterTargetClass && annClass != BeanGetterTargetClass &&
annClass != BeanSetterTargetClass && annClass != ParamTargetClass
}) && orElse)
}
// there was no meta-annotation on `ann`. Look if the class annotations of
// `ann` has a `target` annotation, otherwise put `ann` only on fields.
def noMetaAnnot(ann: AnnotationInfo) = {
hasMatching(ann.atp.typeSymbol.annotations, keepClean)
}
annots.filter(ann => ann.atp match {
// the annotation type has meta-annotations, e.g. @(foo @getter)
case AnnotatedType(metaAnnots, _, _) =>
hasMatching(metaAnnots, noMetaAnnot(ann))
// there are no meta-annotations, e.g. @foo
case _ => noMetaAnnot(ann)
})
}
protected def enterSyms(txt: Context, trees: List[Tree]) = {
var txt0 = txt
for (tree <- trees) txt0 = enterSym(txt0, tree)
}
protected def enterSym(txt: Context, tree: Tree): Context =
if (txt eq context) namer.enterSym(tree)
else newNamer(txt).enterSym(tree)
/**
* @param templ ...
* @param parents1 ...
* <li>
* Check that inner classes do not inherit from Annotation
* </li>
* @return ...
*/
def typedTemplate(templ: Template, parents1: List[Tree]): Template = {
val clazz = context.owner
// complete lazy annotations
val annots = clazz.annotations
if (templ.symbol == NoSymbol)
templ setSymbol clazz.newLocalDummy(templ.pos)
val self1 = templ.self match {
case vd @ ValDef(mods, name, tpt, EmptyTree) =>
val tpt1 =
checkNoEscaping.privates(
clazz.thisSym,
treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe)
treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType
}
// was:
// val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt))
// treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType
// but this leads to cycles for existential self types ==> #2545
if (self1.name != nme.WILDCARD) context.scope enter self1.symbol
val selfType =
if (clazz.isAnonymousClass && !phase.erasedTypes)
intersectionType(clazz.info.parents, clazz.owner)
else clazz.typeOfThis
// the following is necessary for templates generated later
assert(clazz.info.decls != EmptyScope)
enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body)
validateParentClasses(parents1, selfType)
if (clazz.isCase)
validateNoCaseAncestor(clazz)
if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass)
unit.error(clazz.pos, "inner classes cannot be classfile annotations")
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
val body =
if (phase.id <= currentRun.typerPhase.id && !reporter.hasErrors)
templ.body flatMap addGetterSetter
else templ.body
val body1 = typedStats(body, templ.symbol)
treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe
}
/** Remove definition annotations from modifiers (they have been saved
* into the symbol's ``annotations'' in the type completer / namer)
*/
def removeAnnotations(mods: Modifiers): Modifiers =
mods.copy(annotations = Nil)
/**
* @param vdef ...
* @return ...
*/
def typedValDef(vdef: ValDef): ValDef = {
// attributes(vdef)
val sym = vdef.symbol
val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor)
val typedMods = removeAnnotations(vdef.mods)
// complete lazy annotations
val annots = sym.annotations
var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt))
checkNonCyclic(vdef, tpt1)
if (sym.hasAnnotation(definitions.VolatileAttr)) {
if (!sym.isMutable)
error(vdef.pos, "values cannot be volatile")
else if (sym.isFinal)
error(vdef.pos, "final vars cannot be volatile")
}
val rhs1 =
if (vdef.rhs.isEmpty) {
if (sym.isVariable && sym.owner.isTerm && phase.id <= currentRun.typerPhase.id)
error(vdef.pos, "local variables must be initialized")
vdef.rhs
} else {
val tpt2 = if (sym.hasDefault) {
// When typechecking default parameter, replace all type parameters in the expected type by Wildcard.
// This allows defining "def foo[T](a: T = 1)"
val tparams =
if (sym.owner.isConstructor) sym.owner.owner.info.typeParams
else sym.owner.tpe.typeParams
val subst = new SubstTypeMap(tparams, tparams map (_ => WildcardType)) {
override def matches(sym: Symbol, sym1: Symbol) =
if (sym.isSkolem) matches(sym.deSkolemize, sym1)
else if (sym1.isSkolem) matches(sym, sym1.deSkolemize)
else super[SubstTypeMap].matches(sym, sym1)
}
// allow defaults on by-name parameters
if (sym hasFlag BYNAMEPARAM)
if (tpt1.tpe.typeArgs.isEmpty) WildcardType // during erasure tpt1 is Function0
else subst(tpt1.tpe.typeArgs(0))
else subst(tpt1.tpe)
} else tpt1.tpe
newTyper(typer1.context.make(vdef, sym)).transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2)
}
treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType
}
/** Enter all aliases of local parameter accessors.
*
* @param clazz ...
* @param vparamss ...
* @param rhs ...
*/
def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) {
if (settings.debug.value) log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug
def decompose(call: Tree): (Tree, List[Tree]) = call match {
case Apply(fn, args) =>
val (superConstr, args1) = decompose(fn)
val params = fn.tpe.params
val args2 = if (params.isEmpty || !isRepeatedParamType(params.last.tpe)) args
else args.take(params.length - 1) :+ EmptyTree
assert(sameLength(args2, params), "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2)//debug
(superConstr, args1 ::: args2)
case Block(stats, expr) if !stats.isEmpty =>
decompose(stats.last)
case _ =>
(call, List())
}
val (superConstr, superArgs) = decompose(rhs)
assert(superConstr.symbol ne null)//debug
// an object cannot be allowed to pass a reference to itself to a superconstructor
// because of initialization issues; bug #473
for (arg <- superArgs ; tree <- arg) {
val sym = tree.symbol
if (sym != null && (sym.info.baseClasses contains clazz)) {
if (sym.isModule)
error(tree.pos, "super constructor cannot be passed a self reference unless parameter is declared by-name")
tree match {
case This(qual) =>
error(tree.pos, "super constructor arguments cannot reference unconstructed `this`")
case _ => ()
}
}
}
if (superConstr.symbol.isPrimaryConstructor) {
val superClazz = superConstr.symbol.owner
if (!superClazz.isJavaDefined) {
val superParamAccessors = superClazz.constrParamAccessors
if (sameLength(superParamAccessors, superArgs)) {
(superParamAccessors, superArgs).zipped map { (superAcc, superArg) =>
superArg match {
case Ident(name) =>
if (vparamss.exists(_.exists(_.symbol == superArg.symbol))) {
var alias = superAcc.initialize.alias
if (alias == NoSymbol)
alias = superAcc.getter(superAcc.owner)
if (alias != NoSymbol &&
superClazz.info.nonPrivateMember(alias.name) != alias)
alias = NoSymbol
if (alias != NoSymbol) {
var ownAcc = clazz.info.decl(name).suchThat(_.isParamAccessor)
if ((ownAcc hasFlag ACCESSOR) && !ownAcc.isDeferred)
ownAcc = ownAcc.accessed
if (!ownAcc.isVariable && !alias.accessed.isVariable) {
if (settings.debug.value)
log("" + ownAcc + " has alias "+alias + alias.locationString) //debug
ownAcc.asInstanceOf[TermSymbol].setAlias(alias)
}
}
}
case _ =>
}
()
}
}
}
}
}
/** Check if a method is defined in such a way that it can be called.
* A method cannot be called if it is a non-private member of a structural type
* and if its parameter's types are not one of
* - this.type
* - a type member of the structural type
* - an abstract type declared outside of the structural type. */
def checkMethodStructuralCompatible(meth: Symbol): Unit =
if (meth.owner.isStructuralRefinement && meth.allOverriddenSymbols.isEmpty && !(meth.isPrivate || meth.hasAccessBoundary)) {
val tp: Type = meth.tpe match {
case mt: MethodType => mt
case NullaryMethodType(res) => res
// TODO_NMT: drop NullaryMethodType from resultType?
case pt: PolyType => pt.resultType
case _ => NoType
}
for (paramType <- tp.paramTypes) {
if (paramType.typeSymbol.isAbstractType && !(paramType.typeSymbol.hasTransOwner(meth.owner)))
unit.error(meth.pos,"Parameter type in structural refinement may not refer to an abstract type defined outside that refinement")
else if (paramType.typeSymbol.isAbstractType && !(paramType.typeSymbol.hasTransOwner(meth)))
unit.error(meth.pos,"Parameter type in structural refinement may not refer to a type member of that refinement")
else if (paramType.isInstanceOf[ThisType] && paramType.typeSymbol == meth.owner)
unit.error(meth.pos,"Parameter type in structural refinement may not refer to the type of that refinement (self type)")
}
}
def typedUseCase(useCase: UseCase) {
def stringParser(str: String): syntaxAnalyzer.Parser = {
val file = new BatchSourceFile(context.unit.source.file, str) {
override def positionInUltimateSource(pos: Position) = {
pos.withSource(context.unit.source, useCase.pos.start)
}
}
val unit = new CompilationUnit(file)
new syntaxAnalyzer.UnitParser(unit)
}
val trees = stringParser(useCase.body+";").nonLocalDefOrDcl
val enclClass = context.enclClass.owner
def defineAlias(name: Name) =
if (context.scope.lookup(name) == NoSymbol) {
lookupVariable(name.toString.substring(1), enclClass) match {
case Some(repl) =>
silent(_.typedTypeConstructor(stringParser(repl).typ())) match {
case tpt: Tree =>
val alias = enclClass.newAliasType(useCase.pos, name.toTypeName)
val tparams = cloneSymbols(tpt.tpe.typeSymbol.typeParams, alias)
alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe)))
context.scope.enter(alias)
case _ =>
}
case _ =>
}
}
for (tree <- trees; t <- tree)
t match {
case Ident(name) if name startsWith '$' => defineAlias(name)
case _ =>
}
useCase.aliases = context.scope.toList
namer.enterSyms(trees)
typedStats(trees, NoSymbol)
useCase.defined = context.scope.toList filterNot (useCase.aliases contains _)
if (settings.debug.value)
useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe)))
}
/**
* @param ddef ...
* @return ...
*/
def typedDefDef(ddef: DefDef): DefDef = {
val meth = ddef.symbol
reenterTypeParams(ddef.tparams)
reenterValueParams(ddef.vparamss)
// for `val` and `var` parameter, look at `target` meta-annotation
if (phase.id <= currentRun.typerPhase.id && meth.isPrimaryConstructor) {
for (vparams <- ddef.vparamss; vd <- vparams) {
if (vd.mods.isParamAccessor) {
val sym = vd.symbol
sym.setAnnotations(memberAnnots(sym.annotations, ParamTargetClass, keepClean = true))
}
}
}
val tparams1 = ddef.tparams mapConserve typedTypeDef
val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef)
// complete lazy annotations
val annots = meth.annotations
for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1)
if (isRepeatedParamType(vparam1.symbol.tpe))
error(vparam1.pos, "*-parameter must come last")
var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt))
if (!settings.YdepMethTpes.value) {
for (vparams <- vparamss1; vparam <- vparams) {
checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
}
checkNoEscaping.locals(context.scope, WildcardType, tpt1)
}
checkNonCyclic(ddef, tpt1)
ddef.tpt.setType(tpt1.tpe)
val typedMods = removeAnnotations(ddef.mods)
var rhs1 =
if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors
if (!meth.isPrimaryConstructor &&
(!meth.owner.isClass ||
meth.owner.isModuleClass ||
meth.owner.isAnonOrRefinementClass))
error(ddef.pos, "constructor definition not allowed here")
typed(ddef.rhs)
} else {
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
}
if (meth.isPrimaryConstructor && meth.isClassConstructor &&
phase.id <= currentRun.typerPhase.id && !reporter.hasErrors)
computeParamAliases(meth.owner, vparamss1, rhs1)
if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
rhs1 = checkDead(rhs1)
if (phase.id <= currentRun.typerPhase.id && meth.owner.isClass &&
meth.paramss.exists(ps => ps.exists(_.hasDefaultFlag) && isRepeatedParamType(ps.last.tpe)))
error(meth.pos, "a parameter section with a `*'-parameter is not allowed to have default arguments")
if (phase.id <= currentRun.typerPhase.id) {
val allParams = meth.paramss.flatten
for (p <- allParams) {
deprecatedName(p).foreach(n => {
if (allParams.exists(p1 => p1.name == n || (p != p1 && deprecatedName(p1) == Some(n))))
error(p.pos, "deprecated parameter name "+ n +" has to be distinct from any other parameter name (deprecated or not).")
})
}
}
checkMethodStructuralCompatible(meth)
treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType
}
def typedTypeDef(tdef: TypeDef): TypeDef = {
def typeDefTyper = {
if(tdef.tparams isEmpty) Typer.this
else newTyper(context.makeNewScope(tdef, tdef.symbol))
}
typeDefTyper.typedTypeDef0(tdef)
}
// call typedTypeDef instead
// a TypeDef with type parameters must always be type checked in a new scope
private def typedTypeDef0(tdef: TypeDef): TypeDef = {
tdef.symbol.initialize
reenterTypeParams(tdef.tparams)
val tparams1 = tdef.tparams mapConserve {typedTypeDef(_)}
val typedMods = removeAnnotations(tdef.mods)
// complete lazy annotations
val annots = tdef.symbol.annotations
// @specialized should not be pickled when compiling with -no-specialize
if (settings.nospecialization.value && currentRun.compiles(tdef.symbol)) {
tdef.symbol.removeAnnotation(definitions.SpecializedClass)
tdef.symbol.deSkolemize.removeAnnotation(definitions.SpecializedClass)
}
val rhs1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.rhs))
checkNonCyclic(tdef.symbol)
if (tdef.symbol.owner.isType)
rhs1.tpe match {
case TypeBounds(lo1, hi1) =>
if (!(lo1 <:< hi1))
error(tdef.pos, "lower bound "+lo1+" does not conform to upper bound "+hi1)
case _ =>
}
treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType
}
private def enterLabelDef(stat: Tree) {
stat match {
case ldef @ LabelDef(_, _, _) =>
if (ldef.symbol == NoSymbol)
ldef.symbol = namer.enterInScope(
context.owner.newLabel(ldef.pos, ldef.name) setInfo MethodType(List(), UnitClass.tpe))
case _ =>
}
}
def typedLabelDef(ldef: LabelDef): LabelDef = {
if (!nme.isLoopHeaderLabel(ldef.symbol.name) || phase.id > currentRun.typerPhase.id) {
val restpe = ldef.symbol.tpe.resultType
val rhs1 = typed(ldef.rhs, restpe)
ldef.params foreach (param => param.tpe = param.symbol.tpe)
treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe
} else {
val initpe = ldef.symbol.tpe.resultType
val rhs1 = typed(ldef.rhs)
val restpe = rhs1.tpe
if (restpe == initpe) { // stable result, no need to check again
ldef.params foreach (param => param.tpe = param.symbol.tpe)
treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe
} else {
context.scope.unlink(ldef.symbol)
val sym2 = namer.enterInScope(
context.owner.newLabel(ldef.pos, ldef.name) setInfo MethodType(List(), restpe))
val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe)
ldef.params foreach (param => param.tpe = param.symbol.tpe)
treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs2) setSymbol sym2 setType restpe
}
}
}
/**
* @param block ...
* @param mode ...
* @param pt ...
* @return ...
*/
def typedBlock(block: Block, mode: Int, pt: Type): Block = {
val syntheticPrivates = new ListBuffer[Symbol]
try {
namer.enterSyms(block.stats)
for (stat <- block.stats) enterLabelDef(stat)
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
// This is very tricky stuff, because we are navigating
// the Skylla and Charybdis of anonymous classes and what to return
// from them here. On the one hand, we cannot admit
// every non-private member of an anonymous class as a part of
// the structural type of the enclosing block. This runs afoul of
// the restriction that a structural type may not refer to an enclosing
// type parameter or abstract types (which in turn is necessitated
// by what can be done in Java reflection. On the other hand,
// making every term member private conflicts with private escape checking
// see ticket #3174 for an example.
// The cleanest way forward is if we would find a way to suppress
// structural type checking for these members and maybe defer
// type errors to the places where members are called. But that would
// be a big refactoring and also a big departure from existing code.
// The probably safest fix for 2.8 is to keep members of an anonymous
// class that are not mentioned in a parent type private (as before)
// but to disable escape checking for code that's in the same anonymous class.
// That's what's done here.
// We really should go back and think hard whether we find a better
// way to address the problem of escaping idents on the one hand and well-formed
// structural types on the other.
block match {
case block @ Block(List(classDef @ ClassDef(_, _, _, _)), newInst @ Apply(Select(New(_), _), _)) =>
// The block is an anonymous class definitions/instantiation pair
// -> members that are hidden by the type of the block are made private
val visibleMembers = pt match {
case WildcardType => classDef.symbol.info.decls.toList
case BoundedWildcardType(TypeBounds(lo, hi)) => lo.members
case _ => pt.members
}
for (member <- classDef.symbol.info.decls.toList
if member.isTerm && !member.isConstructor &&
member.allOverriddenSymbols.isEmpty &&
(!member.isPrivate && !member.hasAccessBoundary) &&
!(visibleMembers exists { visible =>
visible.name == member.name &&
member.tpe <:< visible.tpe.substThis(visible.owner, ThisType(classDef.symbol))
})
) {
member.resetFlag(PROTECTED)
member.resetFlag(LOCAL)
member.setFlag(PRIVATE | SYNTHETIC_PRIVATE)
syntheticPrivates += member
member.privateWithin = NoSymbol
}
case _ =>
}
}
val stats1 = typedStats(block.stats, context.owner)
val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt)
treeCopy.Block(block, stats1, expr1)
.setType(if (treeInfo.isPureExpr(block)) expr1.tpe else expr1.tpe.deconst)
} finally {
// enable escaping privates checking from the outside and recycle
// transient flag
for (sym <- syntheticPrivates) sym resetFlag SYNTHETIC_PRIVATE
}
}
/**
* @param cdef ...
* @param pattpe ...
* @param pt ...
* @return ...
*/
def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = {
// verify no _* except in last position
for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x)
error(x.pos, "_* may only come last")
val pat1: Tree = typedPattern(cdef.pat, pattpe)
// When case classes have more than two parameter lists, the pattern ends
// up typed as a method. We only pattern match on the first parameter
// list, so substitute the final result type of the method, i.e. the type
// of the case class.
if (pat1.tpe.paramSectionCount > 0)
pat1 setType pat1.tpe.finalResultType
if (forInteractive) {
for (bind @ Bind(name, _) <- cdef.pat)
if (name.toTermName != nme.WILDCARD && bind.symbol != null && bind.symbol != NoSymbol)
namer.enterIfNotThere(bind.symbol)
}
val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree
else typed(cdef.guard, BooleanClass.tpe)
var body1: Tree = typed(cdef.body, pt)
if (!context.savedTypeBounds.isEmpty) {
body1.tpe = context.restoreTypeBounds(body1.tpe)
if (isFullyDefined(pt) && !(body1.tpe <:< pt)) {
body1 =
typed {
atPos(body1.pos) {
TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) // @M no need for pt.normalize here, is done in erasure
}
}
}
}
// body1 = checkNoEscaping.locals(context.scope, pt, body1)
treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
}
def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
cases mapConserve { cdef =>
newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)
}
/**
* @param fun ...
* @param mode ...
* @param pt ...
* @return ...
*/
def typedFunction(fun: Function, mode: Int, pt: Type): Tree = {
val numVparams = fun.vparams.length
val codeExpected = !forMSIL && (pt.typeSymbol isNonBottomSubClass CodeClass)
if (numVparams > definitions.MaxFunctionArity)
return errorTree(fun, "implementation restricts functions to " + definitions.MaxFunctionArity + " parameters")
def decompose(pt: Type): (Symbol, List[Type], Type) =
if ((isFunctionType(pt)
||
pt.typeSymbol == PartialFunctionClass &&
numVparams == 1 && fun.body.isInstanceOf[Match])
&& // see bug901 for a reason why next conditions are needed
(pt.normalize.typeArgs.length - 1 == numVparams
||
fun.vparams.exists(_.tpt.isEmpty)))
(pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last)
else
(FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType)
val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt)
if (argpts.lengthCompare(numVparams) != 0)
errorTree(fun, "wrong number of parameters; expected = " + argpts.length)
else {
val vparamSyms = (fun.vparams, argpts).zipped map { (vparam, argpt) =>
if (vparam.tpt.isEmpty) {
vparam.tpt.tpe =
if (isFullyDefined(argpt)) argpt
else {
fun match {
case etaExpansion(vparams, fn, args) if !codeExpected =>
silent(_.typed(fn, forFunMode(mode), pt)) match {
case fn1: Tree if context.undetparams.isEmpty =>
// if context,undetparams is not empty, the function was polymorphic,
// so we need the missing arguments to infer its type. See #871
//println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams)
val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams)
if (isFunctionType(ftpe) && isFullyDefined(ftpe))
return typedFunction(fun, mode, ftpe)
case _ =>
}
case _ =>
}
error(vparam.pos, missingParameterTypeMsg(fun, vparam, pt))
ErrorType
}
if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus
}
enterSym(context, vparam)
if (context.retyping) context.scope enter vparam.symbol
vparam.symbol
}
val vparams = fun.vparams mapConserve (typedValDef)
// for (vparam <- vparams) {
// checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); ()
// }
var body = typed(fun.body, respt)
val formals = vparamSyms map (_.tpe)
val restpe = packedType(body, fun.symbol).deconst
val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
// body = checkNoEscaping.locals(context.scope, restpe, body)
val fun1 = treeCopy.Function(fun, vparams, body).setType(funtpe)
if (codeExpected) {
val liftPoint = Apply(Select(Ident(CodeModule), nme.lift_), List(fun1))
typed(atPos(fun.pos)(liftPoint))
} else fun1
}
}
def typedRefinement(stats: List[Tree]) {
namer.enterSyms(stats)
// need to delay rest of typedRefinement to avoid cyclic reference errors
unit.toCheck += { () =>
// go to next outer context which is not silent, see #3614
var c = context
while (!c.reportGeneralErrors) c = c.outer
val stats1 = newTyper(c).typedStats(stats, NoSymbol)
for (stat <- stats1 if stat.isDef) {
val member = stat.symbol
if (!(context.owner.ancestors forall
(bc => member.matchingSymbol(bc, context.owner.thisType) == NoSymbol))) {
member setFlag OVERRIDE
}
}
}
}
def typedImport(imp : Import) : Import = (transformed remove imp) match {
case Some(imp1: Import) => imp1
case None => log("unhandled import: "+imp+" in "+unit); imp
}
def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
val inBlock = exprOwner == context.owner
def includesTargetPos(tree: Tree) =
tree.pos.isRange && context.unit != null && (tree.pos includes context.unit.targetPos)
val localTarget = stats exists includesTargetPos
def typedStat(stat: Tree): Tree = {
if (context.owner.isRefinementClass && !treeInfo.isDeclaration(stat))
errorTree(stat, "only declarations allowed here")
else
stat match {
case imp @ Import(_, _) =>
context = context.makeNewImport(imp)
imp.symbol.initialize
typedImport(imp)
case _ =>
if (localTarget && !includesTargetPos(stat)) {
// skip typechecking of statements in a sequence where some other statement includes
// the targetposition
stat
} else {
val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this
else newTyper(context.make(stat, exprOwner))
val result = checkDead(localTyper.typed(stat, EXPRmode | BYVALmode, WildcardType))
if (treeInfo.isSelfOrSuperConstrCall(result)) {
context.inConstructorSuffix = true
if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0))
error(stat.pos, "called constructor's definition must precede calling constructor's definition")
}
result
}
}
}
/** 'accessor' and 'accessed' are so similar it becomes very difficult to
* follow the logic, so I renamed one to something distinct.
*/
def accesses(looker: Symbol, accessed: Symbol) = accessed.hasLocalFlag && (
accessed.isParamAccessor || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
)
def checkNoDoubleDefsAndAddSynthetics(stats: List[Tree]): List[Tree] = {
val scope = if (inBlock) context.scope else context.owner.info.decls
var newStats = new ListBuffer[Tree]
var needsCheck = true
var moreToAdd = true
while (moreToAdd) {
val initSize = scope.size
var e = scope.elems
while ((e ne null) && e.owner == scope) {
// check no double def
if (needsCheck) {
var e1 = scope.lookupNextEntry(e)
while ((e1 ne null) && e1.owner == scope) {
if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) &&
(e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe)))
// default getters are defined twice when multiple overloads have defaults. an
// error for this is issued in RefChecks.checkDefaultsInOverloaded
if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag &&
!e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) {
error(e.sym.pos, e1.sym+" is defined twice"+
{if(!settings.debug.value) "" else " in "+unit.toString})
scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
}
e1 = scope.lookupNextEntry(e1)
}
}
// add synthetics
context.unit.synthetics get e.sym foreach { tree =>
newStats += typedStat(tree) // might add even more synthetics to the scope
context.unit.synthetics -= e.sym
}
e = e.next
}
needsCheck = false
// the type completer of a synthetic might add more synthetics. example: if the
// factory method of a case class (i.e. the constructor) has a default.
moreToAdd = initSize != scope.size
}
if (newStats.isEmpty) stats
else {
// put default getters next to the method they belong to,
// same for companion objects. fixes #2489 and #4036.
def matches(stat: Tree, synt: Tree) = (stat, synt) match {
case (DefDef(_, statName, _, _, _, _), DefDef(mods, syntName, _, _, _, _)) =>
mods.hasDefaultFlag && syntName.toString.startsWith(statName.toString)
case (ClassDef(_, className, _, _), ModuleDef(_, moduleName, _)) =>
className.toTermName == moduleName
case _ => false
}
def matching(stat: Tree): List[Tree] = {
val (pos, neg) = newStats.partition(synt => matches(stat, synt))
newStats = neg
pos.toList
}
(stats foldRight List[Tree]())((stat, res) => {
stat :: matching(stat) ::: res
}) ::: newStats.toList
}
}
val result = stats mapConserve (typedStat)
if (phase.erasedTypes) result
else checkNoDoubleDefsAndAddSynthetics(result)
}
def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = {
val typedMode = onlyStickyModes(mode) | newmode
val t = constrTyperIf((mode & SCCmode) != 0).typed(arg, typedMode, pt)
checkDead.inMode(typedMode, t)
}
def typedArgs(args: List[Tree], mode: Int) =
args mapConserve (arg => typedArg(arg, mode, 0, WildcardType))
def typedArgs(args: List[Tree], mode: Int, originalFormals: List[Type], adaptedFormals: List[Type]) = {
var newmodes = originalFormals map (tp => if (isByNameParamType(tp)) 0 else BYVALmode)
if (isVarArgTypes(originalFormals)) // TR check really necessary?
newmodes = newmodes.init ++ List.fill(args.length - originalFormals.length + 1)(STARmode | BYVALmode)
(args, adaptedFormals, newmodes).zipped map { (arg, formal, m) =>
typedArg(arg, mode, m, formal)
}
}
/** Does function need to be instantiated, because a missing parameter
* in an argument closure overlaps with an uninstantiated formal?
*/
def needsInstantiation(tparams: List[Symbol], formals: List[Type], args: List[Tree]) = {
def isLowerBounded(tparam: Symbol) = {
val losym = tparam.info.bounds.lo.typeSymbol
losym != NothingClass && losym != NullClass
}
(formals, args).zipped exists {
case (formal, Function(vparams, _)) =>
(vparams exists (_.tpt.isEmpty)) &&
vparams.length <= MaxFunctionArity &&
(formal baseType FunctionClass(vparams.length) match {
case TypeRef(_, _, formalargs) =>
(formalargs, vparams).zipped.exists ((formalarg, vparam) =>
vparam.tpt.isEmpty && (tparams exists (formalarg contains))) &&
(tparams forall isLowerBounded)
case _ =>
false
})
case _ =>
false
}
}
/** Is `tree' a block created by a named application?
*/
def isNamedApplyBlock(tree: Tree) =
context.namedApplyBlockInfo exists (_._1 == tree)
def callToCompanionConstr(context: Context, calledFun: Symbol) = {
calledFun.isConstructor && {
val methCtx = context.enclMethod
(methCtx != NoContext) && {
val contextFun = methCtx.tree.symbol
contextFun.isPrimaryConstructor && contextFun.owner.isModuleClass &&
companionModuleOf(calledFun.owner, context).moduleClass == contextFun.owner
}
}
}
def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = {
// TODO_NMT: check the assumption that args nonEmpty
var fun = fun0
if (fun.hasSymbol && fun.symbol.isOverloaded) {
// remove alternatives with wrong number of parameters without looking at types.
// less expensive than including them in inferMethodAlternatvie (see below).
def shapeType(arg: Tree): Type = arg match {
case Function(vparams, body) =>
functionType(vparams map (vparam => AnyClass.tpe), shapeType(body))
case AssignOrNamedArg(Ident(name), rhs) =>
NamedType(name, shapeType(rhs))
case _ =>
NothingClass.tpe
}
val argtypes = args map shapeType
val pre = fun.symbol.tpe.prefix
var sym = fun.symbol filter { alt =>
// must use pt as expected type, not WildcardType (a tempting quick fix to #2665)
// now fixed by using isWeaklyCompatible in exprTypeArgs
// TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed)
// (I had expected inferMethodAlternative to pick up the slack introduced by using WildcardType here)
isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt)
}
if (sym.isOverloaded) {
val sym1 = sym filter (alt => {
// eliminate functions that would result from tupling transforms
// keeps alternatives with repeated params
hasExactlyNumParams(followApply(alt.tpe), argtypes.length) ||
// also keep alts which define at least one default
alt.tpe.paramss.exists(_.exists(_.hasDefault))
})
if (sym1 != NoSymbol) sym = sym1
}
if (sym != NoSymbol)
fun = adapt(fun setSymbol sym setType pre.memberType(sym), forFunMode(mode), WildcardType)
}
fun.tpe match {
case OverloadedType(pre, alts) =>
val undetparams = context.extractUndetparams()
val argtpes = new ListBuffer[Type]
val amode = forArgMode(fun, mode)
val args1 = args map {
case arg @ AssignOrNamedArg(Ident(name), rhs) =>
// named args: only type the righthand sides ("unknown identifier" errors otherwise)
val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType)
argtpes += NamedType(name, rhs1.tpe.deconst)
// the assign is untyped; that's ok because we call doTypedApply
atPos(arg.pos) { new AssignOrNamedArg(arg.lhs , rhs1) }
case arg =>
val arg1 = typedArg(arg, amode, BYVALmode, WildcardType)
argtpes += arg1.tpe.deconst
arg1
}
context.undetparams = undetparams
inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args))
doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt)
case mt @ MethodType(params, _) =>
val paramTypes = mt.paramTypes
// repeat vararg as often as needed, remove by-name
val formals = formalTypes(paramTypes, args.length)
/** Try packing all arguments into a Tuple and apply `fun'
* to that. This is the last thing which is tried (after
* default arguments)
*/
def tryTupleApply: Option[Tree] = {
// if 1 formal, 1 arg (a tuple), otherwise unmodified args
val tupleArgs = actualArgs(tree.pos.makeTransparent, args, formals.length)
if (!sameLength(tupleArgs, args) && !isUnitForVarArgs(args, params)) {
// expected one argument, but got 0 or >1 ==> try applying to tuple
// the inner "doTypedApply" does "extractUndetparams" => restore when it fails
val savedUndetparams = context.undetparams
silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) match {
case t: Tree =>
// println("tuple conversion to "+t+" for "+mt)//DEBUG
Some(t)
case ex =>
context.undetparams = savedUndetparams
None
}
} else None
}
/** Treats an application which uses named or default arguments.
* Also works if names + a vararg used: when names are used, the vararg
* parameter has to be specified exactly once. Note that combining varargs
* and defaults is ruled out by typedDefDef.
*/
def tryNamesDefaults: Tree = {
val lencmp = compareLengths(args, formals)
if (mt.isErroneous) setError(tree)
else if (inPatternMode(mode))
// #2064
errorTree(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun))
else if (lencmp > 0) {
tryTupleApply getOrElse errorTree(tree, "too many arguments for "+treeSymTypeMsg(fun))
} else if (lencmp == 0) {
// we don't need defaults. names were used, so this application is transformed
// into a block (@see transformNamedApplication in NamesDefaults)
val (namelessArgs, argPos) = removeNames(Typer.this)(args, params)
if (namelessArgs exists (_.isErroneous)) {
setError(tree)
} else if (!isIdentity(argPos) && !sameLength(formals, params))
// !isIdentity indicates that named arguments are used to re-order arguments
errorTree(tree, "when using named arguments, the vararg parameter "+
"has to be specified exactly once")
else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) {
// if there's no re-ordering, and fun is not transformed, no need to transform
// more than an optimization, e.g. important in "synchronized { x = update-x }"
doTypedApply(tree, fun, namelessArgs, mode, pt)
} else {
transformNamedApplication(Typer.this, mode, pt)(
treeCopy.Apply(tree, fun, namelessArgs), argPos)
}
} else {
// defaults are needed. they are added to the argument list in named style as
// calls to the default getters. Example:
// foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
if (fun1.isErroneous) setError(tree)
else {
assert(isNamedApplyBlock(fun1), fun1)
val NamedApplyInfo(qual, targs, previousArgss, _) = context.namedApplyBlockInfo.get._2
val blockIsEmpty = fun1 match {
case Block(Nil, _) =>
// if the block does not have any ValDef we can remove it. Note that the call to
// "transformNamedApplication" is always needed in order to obtain targs/previousArgss
context.namedApplyBlockInfo = None
true
case _ => false
}
val (allArgs, missing) = addDefaults(args, qual, targs, previousArgss, params, fun.pos.focus, context)
val funSym = fun1 match { case Block(_, expr) => expr.symbol }
val lencmp2 = compareLengths(allArgs, formals)
if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) {
errorTree(tree, "module extending its companion class cannot use default constructor arguments")
} else if (lencmp2 > 0) {
removeNames(Typer.this)(allArgs, params) // #3818
setError(tree)
} else if (lencmp2 == 0) {
// useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
val note = "Error occurred in an application involving default arguments."
if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
tryTupleApply getOrElse errorTree(tree, notEnoughArgumentsMsg(fun, missing))
}
}
}
}
if (!sameLength(formals, args) || // wrong nb of arguments
(args exists isNamed) || // uses a named argument
isNamedApplyBlock(fun)) { // fun was transformed to a named apply block =>
// integrate this application into the block
tryNamesDefaults
} else {
val tparams = context.extractUndetparams()
if (tparams.isEmpty) { // all type params are defined
// In order for checkDead not to be misled by the unfortunate special
// case of AnyRef#synchronized (which is implemented with signature T => T
// but behaves as if it were (=> T) => T) we need to know what is the actual
// target of a call. Since this information is no longer available from
// typedArg, it is recorded here.
checkDead.updateExpr(fun)
val args1 = typedArgs(args, forArgMode(fun, mode), paramTypes, formals)
// instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case:
// val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo)
// precise(foo) : foo.type => foo.type
val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe))
def ifPatternSkipFormals(tp: Type) = tp match {
case MethodType(_, rtp) if (inPatternMode(mode)) => rtp
case _ => tp
}
// Replace the Delegate-Chainer methods += and -= with corresponding
// + and - calls, which are translated in the code generator into
// Combine and Remove
if (forMSIL) {
fun match {
case Select(qual, name) =>
if (isSubType(qual.tpe, DelegateClass.tpe)
&& (name == encode("+=") || name == encode("-=")))
{
val n = if (name == encode("+=")) nme.PLUS else nme.MINUS
val f = Select(qual, n)
// the compiler thinks, the PLUS method takes only one argument,
// but he thinks it's an instance method -> still two ref's on the stack
// -> translated by backend
val rhs = treeCopy.Apply(tree, f, args)
return typed(Assign(qual, rhs))
}
case _ => ()
}
}
/** This is translating uses of List() into Nil. This is less
* than ideal from a consistency standpoint, but it shouldn't be
* altered without due caution.
*/
if (fun.symbol == List_apply && args.isEmpty && !forInteractive)
atPos(tree.pos)(gen.mkNil setType restpe)
else
constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe))
} else if (needsInstantiation(tparams, formals, args)) {
//println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info)))
inferExprInstance(fun, tparams)
doTypedApply(tree, fun, args, mode, pt)
} else {
assert(!inPatternMode(mode)) // this case cannot arise for patterns
val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt)
val strictTargs = (lenientTargs, tparams).zipped map ((targ, tparam) =>
if (targ == WildcardType) tparam.tpe else targ) //@M TODO: should probably be .tpeHK
var remainingParams = paramTypes
def typedArgToPoly(arg: Tree, formal: Type): Tree = { //TR TODO: cleanup
val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs)
val newmode =
if (isByNameParamType(remainingParams.head)) POLYmode
else POLYmode | BYVALmode
if (remainingParams.tail.nonEmpty) remainingParams = remainingParams.tail
val arg1 = typedArg(arg, forArgMode(fun, mode), newmode, lenientPt)
val argtparams = context.extractUndetparams()
if (!argtparams.isEmpty) {
val strictPt = formal.instantiateTypeParams(tparams, strictTargs)
inferArgumentInstance(arg1, argtparams, strictPt, lenientPt)
}
arg1
}
val args1 = (args, formals).zipped map typedArgToPoly
if (args1 exists (_.tpe.isError)) setError(tree)
else {
if (settings.debug.value) log("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug
// define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun"
// returns those undetparams which have not been instantiated.
val undetparams = inferMethodInstance(fun, tparams, args1, pt)
val result = doTypedApply(tree, fun, args1, mode, pt)
context.undetparams = undetparams
result
}
}
}
case SingleType(_, _) =>
doTypedApply(tree, fun setType fun.tpe.widen, args, mode, pt)
case ErrorType =>
setError(treeCopy.Apply(tree, fun, args))
/* --- begin unapply --- */
case otpe if inPatternMode(mode) && unapplyMember(otpe).exists =>
if (args.length > MaxTupleArity)
error(fun.pos, "too many arguments for unapply pattern, maximum = "+MaxTupleArity)
def freshArgType(tp: Type): (Type, List[Symbol]) = (tp: @unchecked) match {
case MethodType(param :: _, _) =>
(param.tpe, Nil)
case PolyType(tparams, restype) =>
val tparams1 = cloneSymbols(tparams)
(freshArgType(restype)._1.substSym(tparams, tparams1), tparams1)
case OverloadedType(_, _) =>
error(fun.pos, "cannot resolve overloaded unapply")
(ErrorType, Nil)
}
val unapp = unapplyMember(otpe)
val unappType = otpe.memberType(unapp)
val argDummy = context.owner.newValue(fun.pos, nme.SELECTOR_DUMMY) setFlag SYNTHETIC setInfo pt
val arg = Ident(argDummy) setType pt
if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) {
//Console.println("UNAPP: need to typetest, arg.tpe = "+arg.tpe+", unappType = "+unappType)
val (unappFormal, freeVars) = freshArgType(unappType.skolemizeExistential(context.owner, tree))
val context1 = context.makeNewScope(context.tree, context.owner)
freeVars foreach context1.scope.enter
val typer1 = newTyper(context1)
val pattp = typer1.infer.inferTypedPattern(tree.pos, unappFormal, arg.tpe)
// turn any unresolved type variables in freevars into existential skolems
val skolems = freeVars map { fv =>
val skolem = new TypeSkolem(context1.owner, fun.pos, fv.name.toTypeName, fv)
skolem.setInfo(fv.info.cloneInfo(skolem))
.setFlag(fv.flags | EXISTENTIAL).resetFlag(PARAM)
skolem
}
arg.tpe = pattp.substSym(freeVars, skolems)
argDummy setInfo arg.tpe
}
// setType null is necessary so that ref will be stabilized; see bug 881
val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg)))
if (fun1.tpe.isErroneous) setError(tree)
else {
val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe)
val formals1 = formalTypes(formals0, args.length)
if (sameLength(formals1, args)) {
val args1 = typedArgs(args, mode, formals0, formals1)
// This used to be the following (failing) assert:
// assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt)
// I modified as follows. See SI-1048.
val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt)
val itype = glb(List(pt1, arg.tpe))
arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking)
UnApply(fun1, args1) setPos tree.pos setType itype
}
else {
errorTree(tree, "wrong number of arguments for "+treeSymTypeMsg(fun))
}
}
/* --- end unapply --- */
case _ =>
errorTree(tree, fun.tpe+" does not take parameters")
}
}
/**
* Convert an annotation constructor call into an AnnotationInfo.
*
* @param annClass the expected annotation class
*/
def typedAnnotation(ann: Tree, mode: Int = EXPRmode, selfsym: Symbol = NoSymbol, annClass: Symbol = AnnotationClass, requireJava: Boolean = false): AnnotationInfo = {
lazy val annotationError = AnnotationInfo(ErrorType, Nil, Nil)
var hasError: Boolean = false
def error(pos: Position, msg: String) = {
context.error(pos, msg)
hasError = true
annotationError
}
/** Calling constfold right here is necessary because some trees (negated
* floats and literals in particular) are not yet folded.
*/
def tryConst(tr: Tree, pt: Type): Option[LiteralAnnotArg] = {
val const: Constant = typed(constfold(tr), EXPRmode, pt) match {
case l @ Literal(c) if !l.isErroneous => c
case tree => tree.tpe match {
case ConstantType(c) => c
case tpe => null
}
}
def fail(msg: String) = { error(tr.pos, msg) ; None }
if (const == null)
fail("annotation argument needs to be a constant; found: " + tr)
else if (const.value == null)
fail("annotation argument cannot be null")
else
Some(LiteralAnnotArg(const))
}
/** Converts an untyped tree to a ClassfileAnnotArg. If the conversion fails,
* an error message is reported and None is returned.
*/
def tree2ConstArg(tree: Tree, pt: Type): Option[ClassfileAnnotArg] = tree match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) if (pt.typeSymbol == ArrayClass) =>
error(tree.pos, "Array constants have to be specified using the `Array(...)' factory method")
None
case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
val annInfo = typedAnnotation(ann, mode, NoSymbol, pt.typeSymbol, true)
if (annInfo.atp.isErroneous) {
// recursive typedAnnotation call already printed an error, so don't call "error"
hasError = true
None
} else Some(NestedAnnotArg(annInfo))
// use of Array.apply[T: ClassManifest](xs: T*): Array[T]
// and Array.apply(x: Int, xs: Int*): Array[Int] (and similar)
case Apply(fun, args) =>
val typedFun = typed(fun, forFunMode(mode), WildcardType)
if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply)
pt match {
case TypeRef(_, ArrayClass, targ :: _) =>
trees2ConstArg(args, targ)
case _ =>
// For classfile annotations, pt can only be T:
// BT = Int, .., String, Class[_], JavaAnnotClass
// T = BT | Array[BT]
// So an array literal as argument can only be valid if pt is Array[_]
error(tree.pos, "found array constant, expected argument of type "+ pt)
None
}
else
tryConst(tree, pt)
case Typed(t, _) => tree2ConstArg(t, pt)
case tree =>
tryConst(tree, pt)
}
def trees2ConstArg(trees: List[Tree], pt: Type): Option[ArrayAnnotArg] = {
val args = trees.map(tree2ConstArg(_, pt))
if (args.exists(_.isEmpty)) None
else Some(ArrayAnnotArg(args.flatten.toArray))
}
// begin typedAnnotation
val (fun, argss) = {
def extract(fun: Tree, outerArgss: List[List[Tree]]):
(Tree, List[List[Tree]]) = fun match {
case Apply(f, args) =>
extract(f, args :: outerArgss)
case Select(New(tpt), nme.CONSTRUCTOR) =>
(fun, outerArgss)
case _ =>
error(fun.pos, "unexpected tree in annotation: "+ fun)
(setError(fun), outerArgss)
}
extract(ann, List())
}
if (fun.isErroneous) annotationError
else {
val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType)
val annType = tpt.tpe
if (typedFun.isErroneous) annotationError
else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) {
// annotation to be saved as java classfile annotation
val isJava = typedFun.symbol.owner.isJavaDefined
if (!annType.typeSymbol.isNonBottomSubClass(annClass)) {
error(tpt.pos, "expected annotation of type "+ annClass.tpe +", found "+ annType)
} else if (argss.length > 1) {
error(ann.pos, "multiple argument lists on classfile annotation")
} else {
val args =
if (argss.head.length == 1 && !isNamed(argss.head.head))
List(new AssignOrNamedArg(Ident(nme.value), argss.head.head))
else argss.head
val annScope = annType.decls
.filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
val names = new collection.mutable.HashSet[Symbol]
names ++= (if (isJava) annScope.iterator
else typedFun.tpe.params.iterator)
val nvPairs = args map {
case arg @ AssignOrNamedArg(Ident(name), rhs) =>
val sym = if (isJava) annScope.lookup(name)
else typedFun.tpe.params.find(p => p.name == name).getOrElse(NoSymbol)
if (sym == NoSymbol) {
error(arg.pos, "unknown annotation argument name: " + name)
(nme.ERROR, None)
} else if (!names.contains(sym)) {
error(arg.pos, "duplicate value for annotation argument " + name)
(nme.ERROR, None)
} else {
names -= sym
if (isJava) sym.cookJavaRawInfo() // #3429
val annArg = tree2ConstArg(rhs, sym.tpe.resultType)
(sym.name, annArg)
}
case arg =>
error(arg.pos, "classfile annotation arguments have to be supplied as named arguments")
(nme.ERROR, None)
}
for (name <- names) {
if (!name.annotations.contains(AnnotationInfo(AnnotationDefaultAttr.tpe, List(), List())) &&
!name.hasDefaultFlag)
error(ann.pos, "annotation " + annType.typeSymbol.fullName + " is missing argument " + name.name)
}
if (hasError) annotationError
else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos)
}
} else if (requireJava) {
error(ann.pos, "nested classfile annotations must be defined in java; found: "+ annType)
} else {
val typedAnn = if (selfsym == NoSymbol) {
typed(ann, mode, annClass.tpe)
} else {
// Since a selfsym is supplied, the annotation should have
// an extra "self" identifier in scope for type checking.
// This is implemented by wrapping the rhs
// in a function like "self => rhs" during type checking,
// and then stripping the "self =>" and substituting
// in the supplied selfsym.
val funcparm = ValDef(NoMods, nme.self, TypeTree(selfsym.info), EmptyTree)
val func = Function(List(funcparm), ann.duplicate)
// The .duplicate of annot.constr
// deals with problems that
// accur if this annotation is
// later typed again, which
// the compiler sometimes does.
// The problem is that "self"
// ident's within annot.constr
// will retain the old symbol
// from the previous typing.
val fun1clazz = FunctionClass(1)
val funcType = typeRef(fun1clazz.tpe.prefix,
fun1clazz,
List(selfsym.info, annClass.tpe))
(typed(func, mode, funcType): @unchecked) match {
case t @ Function(List(arg), rhs) =>
val subs =
new TreeSymSubstituter(List(arg.symbol),List(selfsym))
subs(rhs)
}
}
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
AnnotationInfo(annType, args, List()).setPos(t.pos)
case Block(stats, expr) =>
context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+
"constructor call into a block. The corresponding AnnotationInfo\n"+
"will contain references to local values and default getters instead\n"+
"of the actual argument trees")
annInfo(expr)
case Apply(fun, args) =>
context.warning(t.pos, "Implementation limitation: multiple argument lists on annotations are\n"+
"currently not supported; ignoring arguments "+ args)
annInfo(fun)
case _ =>
error(t.pos, "unexpected tree after typing annotation: "+ typedAnn)
}
if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2)
unit.deprecationWarning(ann.pos, "@deprecated now takes two arguments; see the scaladoc.")
if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) annotationError
else annInfo(typedAnn)
}
}
}
def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type?
sym.isTypeParameter && sym.owner.isJavaDefined
/** Given a set `rawSyms` of term- and type-symbols, and a type
* `tp`, produce a set of fresh type parameters and a type so that
* it can be abstracted to an existential type. Every type symbol
* `T` in `rawSyms` is mapped to a clone. Every term symbol `x` of
* type `T` in `rawSyms` is given an associated type symbol of the
* following form:
*
* type x.type <: T with Singleton
*
* The name of the type parameter is `x.type`, to produce nice
* diagnostics. The Singleton parent ensures that the type
* parameter is still seen as a stable type. Type symbols in
* rawSyms are fully replaced by the new symbols. Term symbols are
* also replaced, except for term symbols of an Ident tree, where
* only the type of the Ident is changed.
*/
protected def existentialTransform(rawSyms: List[Symbol], tp: Type) = {
val typeParams: List[Symbol] = rawSyms map { sym =>
val name = sym.name match {
case x: TypeName => x
case x => newTypeName(x + ".type")
}
val bound = sym.existentialBound
val sowner = if (isRawParameter(sym)) context.owner else sym.owner
val quantified = sowner.newExistential(sym.pos, name)
quantified setInfo bound.cloneInfo(quantified)
}
// Higher-kinded existentials are not yet supported, but this is
// tpeHK for when they are: "if a type constructor is expected/allowed,
// tpeHK must be called instead of tpe."
val typeParamTypes = typeParams map (_.tpeHK)
(
typeParams map (tparam => tparam setInfo tparam.info.subst(rawSyms, typeParamTypes)),
tp.subst(rawSyms, typeParamTypes)
)
}
/** Compute an existential type from raw hidden symbols `syms' and type `tp'
*/
def packSymbols(hidden: List[Symbol], tp: Type): Type =
if (hidden.isEmpty) tp
else {
// Console.println("original type: "+tp)
// Console.println("hidden symbols: "+hidden)
val (tparams, tp1) = existentialTransform(hidden, tp)
// Console.println("tparams: "+tparams+", result: "+tp1)
val res = existentialAbstraction(tparams, tp1)
// Console.println("final result: "+res)
res
}
/** convert skolems to existentials */
def packedType(tree: Tree, owner: Symbol): Type = {
def defines(tree: Tree, sym: Symbol) =
sym.isExistentialSkolem && sym.unpackLocation == tree ||
tree.isDef && tree.symbol == sym
def isVisibleParameter(sym: Symbol) =
sym.isParameter && (sym.owner == owner) && (sym.isType || !owner.isAnonymousFunction)
def containsDef(owner: Symbol, sym: Symbol): Boolean =
(!sym.hasPackageFlag) && {
var o = sym.owner
while (o != owner && o != NoSymbol && !o.hasPackageFlag) o = o.owner
o == owner && !isVisibleParameter(sym)
}
var localSyms = collection.immutable.Set[Symbol]()
var boundSyms = collection.immutable.Set[Symbol]()
def isLocal(sym: Symbol): Boolean =
if (sym == NoSymbol || sym.isRefinementClass || sym.isLocalDummy) false
else if (owner == NoSymbol) tree exists (defines(_, sym))
else containsDef(owner, sym) || isRawParameter(sym)
def containsLocal(tp: Type): Boolean =
tp exists (t => isLocal(t.typeSymbol) || isLocal(t.termSymbol))
val normalizeLocals = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) =>
if (sym.isAliasType && containsLocal(tp)) apply(tp.normalize)
else {
if (pre.isVolatile)
context.error(tree.pos, "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre)
mapOver(tp)
}
case _ =>
mapOver(tp)
}
}
// add all local symbols of `tp' to `localSyms'
// TODO: expand higher-kinded types into individual copies for each instance.
def addLocals(tp: Type) {
val remainingSyms = new ListBuffer[Symbol]
def addIfLocal(sym: Symbol, tp: Type) {
if (isLocal(sym) && !localSyms(sym) && !boundSyms(sym)) {
if (sym.typeParams.isEmpty) {
localSyms += sym
remainingSyms += sym
} else {
unit.error(tree.pos,
"can't existentially abstract over parameterized type " + tp)
}
}
}
for (t <- tp) {
t match {
case ExistentialType(tparams, _) =>
boundSyms ++= tparams
case AnnotatedType(annots, _, _) =>
for (annot <- annots; arg <- annot.args) {
arg match {
case Ident(_) =>
// Check the symbol of an Ident, unless the
// Ident's type is already over an existential.
// (If the type is already over an existential,
// then remap the type, not the core symbol.)
if (!arg.tpe.typeSymbol.hasFlag(EXISTENTIAL))
addIfLocal(arg.symbol, arg.tpe)
case _ => ()
}
}
case _ =>
}
addIfLocal(t.termSymbol, t)
addIfLocal(t.typeSymbol, t)
}
for (sym <- remainingSyms) addLocals(sym.existentialBound)
}
val normalizedTpe = normalizeLocals(tree.tpe)
addLocals(normalizedTpe)
packSymbols(localSyms.toList, normalizedTpe)
}
protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = {
for (wc <- tree.whereClauses)
if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL }
else context.scope enter wc.symbol
val whereClauses1 = typedStats(tree.whereClauses, context.owner)
for (vd @ ValDef(_, _, _, _) <- tree.whereClauses)
if (vd.symbol.tpe.isVolatile)
error(vd.pos, "illegal abstraction from value with volatile type "+vd.symbol.tpe)
val tpt1 = typedType(tree.tpt, mode)
val (typeParams, tpe) = existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe)
//println(tpe + ": " + tpe.getClass )
TypeTree(ExistentialType(typeParams, tpe)) setOriginal tree
}
// lifted out of typed1 because it's needed in typedImplicit0
protected def typedTypeApply(tree: Tree, mode: Int, fun: Tree, args: List[Tree]): Tree = fun.tpe match {
case OverloadedType(pre, alts) =>
inferPolyAlternatives(fun, args map (_.tpe))
val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
val args1 = if (sameLength(args, tparams)) {
//@M: in case TypeApply we can't check the kind-arities of the type arguments,
// as we don't know which alternative to choose... here we do
map2Conserve(args, tparams) {
//@M! the polytype denotes the expected kind
(arg, tparam) => typedHigherKindedType(arg, mode, polyType(tparam.typeParams, AnyClass.tpe))
}
} else // @M: there's probably something wrong when args.length != tparams.length... (triggered by bug #320)
// Martin, I'm using fake trees, because, if you use args or arg.map(typedType),
// inferPolyAlternatives loops... -- I have no idea why :-(
// ...actually this was looping anyway, see bug #278.
return errorTree(fun, "wrong number of type parameters for "+treeSymTypeMsg(fun))
typedTypeApply(tree, mode, fun, args1)
case SingleType(_, _) =>
typedTypeApply(tree, mode, fun setType fun.tpe.widen, args)
case PolyType(tparams, restpe) if tparams.nonEmpty =>
if (sameLength(tparams, args)) {
val targs = args map (_.tpe)
checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "")
if (fun.symbol == Predef_classOf) {
checkClassType(args.head, true, false)
atPos(tree.pos) { gen.mkClassOf(targs.head) }
} else {
if (phase.id <= currentRun.typerPhase.id &&
fun.symbol == Any_isInstanceOf && !targs.isEmpty)
checkCheckable(tree.pos, targs.head, "")
val resultpe = restpe.instantiateTypeParams(tparams, targs)
//@M substitution in instantiateParams needs to be careful!
//@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int]
//@M --> first, m[a] gets changed to m[Int], then m gets substituted for List,
// this must preserve m's type argument, so that we end up with List[Int], and not List[a]
//@M related bug: #1438
//println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe)
treeCopy.TypeApply(tree, fun, args) setType resultpe
}
} else {
errorTree(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun))
}
case ErrorType =>
setError(tree)
case _ =>
errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.")
}
@inline final def deindentTyping() = context.typingIndentLevel -= 2
@inline final def indentTyping() = context.typingIndentLevel += 2
@inline final def printTyping(s: => String) = {
if (printTypings)
println(context.typingIndent + s.replaceAll("\n", "\n" + context.typingIndent))
}
@inline final def printInference(s: => String) = {
if (printInfers)
println(s)
}
protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
def isPatternMode = inPatternMode(mode)
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst))
//@M! get the type of the qualifier in a Select tree, otherwise: NoType
def prefixType(fun: Tree): Type = fun match {
case Select(qualifier, _) => qualifier.tpe
// case Ident(name) => ??
case _ => NoType
}
def typedAnnotated(ann: Tree, arg1: Tree): Tree = {
/** mode for typing the annotation itself */
val annotMode = mode & ~TYPEmode | EXPRmode
if (arg1.isType) {
// make sure the annotation is only typechecked once
if (ann.tpe == null) {
// an annotated type
val selfsym =
if (!settings.selfInAnnots.value)
NoSymbol
else
arg1.tpe.selfsym match {
case NoSymbol =>
/* Implementation limitation: Currently this
* can cause cyclical reference errors even
* when the self symbol is not referenced at all.
* Surely at least some of these cases can be
* fixed by proper use of LazyType's. Lex tinkered
* on this but did not succeed, so is leaving
* it alone for now. Example code with the problem:
* class peer extends Annotation
* class NPE[T <: NPE[T] @peer]
*
* (Note: -Yself-in-annots must be on to see the problem)
* */
val sym =
context.owner.newLocalDummy(ann.pos)
.newValue(ann.pos, nme.self)
sym.setInfo(arg1.tpe.withoutAnnotations)
sym
case sym => sym
}
val ainfo = typedAnnotation(ann, annotMode, selfsym)
val atype0 = arg1.tpe.withAnnotation(ainfo)
val atype =
if ((selfsym != NoSymbol) && (ainfo.refsSymbol(selfsym)))
atype0.withSelfsym(selfsym)
else
atype0 // do not record selfsym if
// this annotation did not need it
if (ainfo.isErroneous)
arg1 // simply drop erroneous annotations
else {
ann.tpe = atype
TypeTree(atype) setOriginal tree
}
} else {
// the annotation was typechecked before
TypeTree(ann.tpe) setOriginal tree
}
} else {
if (ann.tpe == null) {
val annotInfo = typedAnnotation(ann, annotMode)
ann.tpe = arg1.tpe.withAnnotation(annotInfo)
}
val atype = ann.tpe
Typed(arg1, TypeTree(atype) setOriginal tree setPos tree.pos.focus) setPos tree.pos setType atype
}
}
def typedBind(name: Name, body: Tree) = {
var vble = tree.symbol
def typedBindType(name: TypeName) = {
assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass)
if (vble == NoSymbol)
vble =
if (isFullyDefined(pt))
context.owner.newAliasType(tree.pos, name) setInfo pt
else
context.owner.newAbstractType(tree.pos, name) setInfo TypeBounds.empty
val rawInfo = vble.rawInfo
vble = if (vble.name == tpnme.WILDCARD) context.scope.enter(vble)
else namer.enterInScope(vble)
tree setSymbol vble setType vble.tpe
}
def typedBindTerm(name: TermName) = {
if (vble == NoSymbol)
vble = context.owner.newValue(tree.pos, name)
if (vble.name.toTermName != nme.WILDCARD) {
if ((mode & ALTmode) != 0)
error(tree.pos, "illegal variable in pattern alternative")
vble = namer.enterInScope(vble)
}
val body1 = typed(body, mode, pt)
vble.setInfo(
if (treeInfo.isSequenceValued(body)) seqType(body1.tpe)
else body1.tpe)
treeCopy.Bind(tree, name, body1) setSymbol vble setType body1.tpe // burak, was: pt
}
name match {
case x: TypeName => typedBindType(x)
case x: TermName => typedBindTerm(x)
}
}
def typedArrayValue(elemtpt: Tree, elems: List[Tree]) = {
val elemtpt1 = typedType(elemtpt, mode)
val elems1 = elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe))
treeCopy.ArrayValue(tree, elemtpt1, elems1)
.setType(
(if (isFullyDefined(pt) && !phase.erasedTypes) pt
else appliedType(ArrayClass.typeConstructor, List(elemtpt1.tpe))).notNull)
}
def typedAssign(lhs: Tree, rhs: Tree): Tree = {
val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType)
val varsym = lhs1.symbol
def failMsg =
if (varsym != null && varsym.isValue) "reassignment to val"
else "assignment to non variable"
def fail = {
if (!lhs1.tpe.isError)
error(tree.pos, failMsg)
setError(tree)
}
if (varsym == null)
return fail
if (treeInfo.mayBeVarGetter(varsym)) {
treeInfo.methPart(lhs1) match {
case Select(qual, name) =>
val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos
val app = Apply(sel, List(rhs)) setPos tree.pos
return typed(app, mode, pt)
case _ =>
}
}
if (varsym.isVariable || varsym.isValue && phase.erasedTypes) {
val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe)
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe
}
else fail
}
def typedIf(cond: Tree, thenp: Tree, elsep: Tree) = {
val cond1 = checkDead(typed(cond, EXPRmode | BYVALmode, BooleanClass.tpe))
if (elsep.isEmpty) { // in the future, should be unnecessary
val thenp1 = typed(thenp, UnitClass.tpe)
treeCopy.If(tree, cond1, thenp1, elsep) setType thenp1.tpe
} else {
var thenp1 = typed(thenp, pt)
var elsep1 = typed(elsep, pt)
val (owntype, needAdapt) = ptOrLub(List(thenp1.tpe, elsep1.tpe))
if (needAdapt) { //isNumericValueType(owntype)) {
thenp1 = adapt(thenp1, mode, owntype)
elsep1 = adapt(elsep1, mode, owntype)
}
treeCopy.If(tree, cond1, thenp1, elsep1) setType owntype
}
}
def typedReturn(expr: Tree) = {
val enclMethod = context.enclMethod
if (enclMethod == NoContext ||
enclMethod.owner.isConstructor ||
context.enclClass.enclMethod == enclMethod // i.e., we are in a constructor of a local class
) {
errorTree(tree, "return outside method definition")
} else {
val DefDef(_, name, _, _, restpt, _) = enclMethod.tree
if (restpt.tpe eq null)
errorTree(tree, enclMethod.owner + " has return statement; needs result type")
else {
context.enclMethod.returnsSeen = true
val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe)
// Warn about returning a value if no value can be returned.
if (restpt.tpe.typeSymbol == UnitClass) {
// The typing in expr1 says expr is Unit (it has already been coerced if
// it is non-Unit) so we have to retype it. Fortunately it won't come up much
// unless the warning is legitimate.
if (typed(expr).tpe.typeSymbol != UnitClass)
unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded")
}
treeCopy.Return(tree, checkDead(expr1)) setSymbol enclMethod.owner setType NothingClass.tpe
}
}
}
def typedNew(tpt: Tree) = {
val tpt1 = {
val tpt0 = typedTypeConstructor(tpt)
checkClassType(tpt0, false, true)
if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) {
context.undetparams = cloneSymbols(tpt0.symbol.typeParams)
TypeTree().setOriginal(tpt0)
.setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347.
} else tpt0
}
/** If current tree <tree> appears in >
* return `tp with x.type' else return `tp'.
*/
def narrowRhs(tp: Type) = { val sym = context.tree.symbol
context.tree match {
case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol =>
val sym1 = if (sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) sym.getter(sym.owner)
else sym.lazyAccessorOrSelf
val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix
intersectionType(List(tp, singleType(pre, sym1)))
case _ => tp
}}
val tp = tpt1.tpe
val sym = tp.typeSymbol
if (sym.isAbstractType || sym.hasAbstractFlag)
error(tree.pos, sym + " is abstract; cannot be instantiated")
else if (!( tp == sym.initialize.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable
// sym.thisSym.tpe == tp.typeOfThis (except for objects)
|| narrowRhs(tp) <:< tp.typeOfThis
|| phase.erasedTypes
)) {
error(tree.pos, sym +
" cannot be instantiated because it does not conform to its self-type "+
tp.typeOfThis)
}
treeCopy.New(tree, tpt1).setType(tp)
}
def typedEta(expr1: Tree): Tree = expr1.tpe match {
case TypeRef(_, ByNameParamClass, _) =>
val expr2 = Function(List(), expr1) setPos expr1.pos
new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
typed1(expr2, mode, pt)
case NullaryMethodType(restpe) =>
val expr2 = Function(List(), expr1) setPos expr1.pos
new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
typed1(expr2, mode, pt)
case PolyType(_, MethodType(formals, _)) =>
if (isFunctionType(pt)) expr1
else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType))
case MethodType(formals, _) =>
if (isFunctionType(pt)) expr1
else expr1 match {
case Select(qual, name) if (forMSIL &&
pt != WildcardType &&
pt != ErrorType &&
isSubType(pt, DelegateClass.tpe)) =>
val scalaCaller = newScalaCaller(pt)
addScalaCallerInfo(scalaCaller, expr1.symbol)
val n: Name = scalaCaller.name
val del = Ident(DelegateClass) setType DelegateClass.tpe
val f = Select(del, n)
//val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt))
val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null)))
else List(qual) // where the scala-method is located
val rhs = Apply(f, args)
typed(rhs)
case _ =>
adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType))
}
case ErrorType =>
expr1
case _ =>
errorTree(expr1, "_ must follow method; cannot follow " + expr1.tpe)
}
/**
* @param args ...
* @return ...
*/
def tryTypedArgs(args: List[Tree], mode: Int, other: TypeError): List[Tree] = {
val c = context.makeSilent(false)
c.retyping = true
try {
newTyper(c).typedArgs(args, mode)
} catch {
case ex: CyclicReference => throw ex
case ex: TypeError =>
null
}
}
/** Try to apply function to arguments; if it does not work, try to convert Java raw to existentials, or try to
* insert an implicit conversion.
*/
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
val start = startTimer(failedApplyNanos)
silent(_.doTypedApply(tree, fun, args, mode, pt)) match {
case t: Tree =>
t
case ex: TypeError =>
stopTimer(failedApplyNanos, start)
// If the problem is with raw types, copnvert to existentials and try again.
// See #4712 for a case where this situation arises,
if ((fun.symbol ne null) && fun.symbol.isJavaDefined) {
val newtpe = rawToExistential(fun.tpe)
if (fun.tpe ne newtpe) {
// println("late cooking: "+fun+":"+fun.tpe) // DEBUG
return tryTypedApply(fun setType newtpe, args)
}
}
def treesInResult(tree: Tree): List[Tree] = tree :: (tree match {
case Block(_, r) => treesInResult(r)
case Match(_, cases) => cases
case CaseDef(_, _, r) => treesInResult(r)
case Annotated(_, r) => treesInResult(r)
case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
case Try(b, catches, _) => treesInResult(b) ++ catches
case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
case _ => Nil
})
def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos)
val retry = fun :: tree :: args exists errorInResult
printTyping {
val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ")
if (retry) "second try: " + funStr
else "no second try: " + funStr + " because error not in result: " + ex.pos+"!="+tree.pos
}
if (retry) {
val Select(qual, name) = fun
val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex)
val qual1 =
if ((args1 ne null) && !pt.isError) adaptToArguments(qual, name, args1, pt)
else qual
if (qual1 ne qual) {
val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
return typed1(tree1, mode | SNDTRYmode, pt)
}
}
reportTypeError(tree.pos, ex)
setError(tree)
}
}
def typedApply(fun: Tree, args: List[Tree]) = {
val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable
if (stableApplication && isPatternMode) {
// treat stable function applications f() as expressions.
typed1(tree, mode & ~PATTERNmode | EXPRmode, pt)
} else {
val funpt = if (isPatternMode) pt else WildcardType
val appStart = startTimer(failedApplyNanos)
val opeqStart = startTimer(failedOpEqNanos)
silent(_.typed(fun, forFunMode(mode), funpt),
if ((mode & EXPRmode) != 0) false else context.reportAmbiguousErrors,
if ((mode & EXPRmode) != 0) tree else context.tree) match {
case fun1: Tree =>
val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1
incCounter(typedApplyCount)
def isImplicitMethod(tpe: Type) = tpe match {
case mt: MethodType => mt.isImplicit
case _ => false
}
val res =
if (phase.id <= currentRun.typerPhase.id &&
fun2.isInstanceOf[Select] &&
!isImplicitMethod(fun2.tpe) &&
((fun2.symbol eq null) || !fun2.symbol.isConstructor) &&
(mode & (EXPRmode | SNDTRYmode)) == EXPRmode) {
tryTypedApply(fun2, args)
} else {
doTypedApply(tree, fun2, args, mode, pt)
}
/*
if (fun2.hasSymbol && fun2.symbol.isConstructor && (mode & EXPRmode) != 0) {
res.tpe = res.tpe.notNull
}
*/
// TODO: In theory we should be able to call:
//if (fun2.hasSymbol && fun2.symbol.name == nme.apply && fun2.symbol.owner == ArrayClass) {
// But this causes cyclic reference for Array class in Cleanup. It is easy to overcome this
// by calling ArrayClass.info here (or some other place before specialize).
if (fun2.symbol == Array_apply) {
val checked = gen.mkCheckInit(res)
// this check is needed to avoid infinite recursion in Duplicators
// (calling typed1 more than once for the same tree)
if (checked ne res) typed { atPos(tree.pos)(checked) }
else res
} else res
case ex: TypeError =>
fun match {
case Select(qual, name)
if !isPatternMode && nme.isOpAssignmentName(name.decode) =>
val qual1 = typedQualifier(qual)
if (treeInfo.isVariableOrGetter(qual1)) {
stopTimer(failedOpEqNanos, opeqStart)
convertToAssignment(fun, qual1, name, args, ex)
} else {
stopTimer(failedApplyNanos, appStart)
if ((qual1.symbol ne null) && qual1.symbol.isValue)
error(tree.pos, "reassignment to val")
else
reportTypeError(fun.pos, ex)
setError(tree)
}
case _ =>
stopTimer(failedApplyNanos, appStart)
reportTypeError(fun.pos, ex)
setError(tree)
}
}
}
}
def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree], ex: TypeError): Tree = {
val prefix = name.subName(0, name.length - nme.EQL.length)
def mkAssign(vble: Tree): Tree =
Assign(
vble,
Apply(
Select(vble.duplicate, prefix) setPos fun.pos.focus, args) setPos tree.pos.makeTransparent
) setPos tree.pos
def mkUpdate(table: Tree, indices: List[Tree]) = {
gen.evalOnceAll(table :: indices, context.owner, context.unit) { ts =>
val tab = ts.head
val is = ts.tail
Apply(
Select(tab(), nme.update) setPos table.pos,
((is map (i => i())) ::: List(
Apply(
Select(
Apply(
Select(tab(), nme.apply) setPos table.pos,
is map (i => i())) setPos qual.pos,
prefix) setPos fun.pos,
args) setPos tree.pos)
)
) setPos tree.pos
}
}
val tree1 = qual match {
case Ident(_) =>
mkAssign(qual)
case Select(qualqual, vname) =>
gen.evalOnce(qualqual, context.owner, context.unit) { qq =>
val qq1 = qq()
mkAssign(Select(qq1, vname) setPos qual.pos)
}
case Apply(fn, indices) =>
treeInfo.methPart(fn) match {
case Select(table, nme.apply) => mkUpdate(table, indices)
case _ => errorTree(qual, "Unexpected tree during assignment conversion.")
}
}
typed1(tree1, mode, pt)
/*
if (settings.debug.value) log("retry assign: "+tree1)
silent(_.typed1(tree1, mode, pt)) match {
case t: Tree =>
t
case _ =>
reportTypeError(tree.pos, ex)
setError(tree)
}
*/
}
def qualifyingClassSym(qual: Name): Symbol =
if (tree.symbol != NoSymbol) tree.symbol else qualifyingClass(tree, qual, false)
def typedSuper(qual: Tree, mix: TypeName) = {
val qual1 = typed(qual)
val clazz = qual1 match {
case This(_) => qual1.symbol
case _ => qual1.tpe.typeSymbol
}
//println(clazz+"/"+qual1.tpe.typeSymbol+"/"+qual1)
def findMixinSuper(site: Type): Type = {
var ps = site.parents filter (_.typeSymbol.name == mix)
if (ps.isEmpty)
ps = site.parents filter (_.typeSymbol.toInterface.name == mix)
if (ps.isEmpty) {
if (settings.debug.value)
Console.println(site.parents map (_.typeSymbol.name))//debug
if (phase.erasedTypes && context.enclClass.owner.isImplClass) {
// println(qual1)
// println(clazz)
// println(site)
// println(site.parents)
// println(mix)
// the reference to super class got lost during erasure
restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class")
} else {
error(tree.pos, mix+" does not name a parent class of "+clazz)
}
ErrorType
} else if (!ps.tail.isEmpty) {
error(tree.pos, "ambiguous parent class qualifier")
ErrorType
} else {
ps.head
}
}
val owntype =
if (mix.isEmpty) {
if ((mode & SUPERCONSTRmode) != 0)
if (clazz.info.parents.isEmpty) AnyRefClass.tpe // can happen due to cyclic references ==> #1036
else clazz.info.parents.head
else intersectionType(clazz.info.parents)
} else {
findMixinSuper(clazz.tpe)
}
treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype)
}
def typedThis(qual: Name) = {
val clazz = qualifyingClassSym(qual)
if (clazz == NoSymbol) setError(tree)
else {
tree setSymbol clazz setType clazz.thisType.underlying
if (isStableContext(tree, mode, pt)) tree setType clazz.thisType
tree
}
}
/** Attribute a selection where <code>tree is qual.name .
* <code>qual is already attributed.
*
* @param qual ...
* @param name ...
* @return ...
*/
def typedSelect(qual: Tree, name: Name): Tree = {
val sym =
if (tree.symbol != NoSymbol) {
if (phase.erasedTypes && qual.isInstanceOf[Super])
qual.tpe = tree.symbol.owner.tpe
if (false && settings.debug.value) { // todo: replace by settings.check.value?
val alts = qual.tpe.member(tree.symbol.name).alternatives
if (!(alts exists (alt =>
alt == tree.symbol || alt.isTerm && (alt.tpe matches tree.symbol.tpe))))
assert(false, "symbol "+tree.symbol+tree.symbol.locationString+" not in "+alts+" of "+qual.tpe+
"\n members = "+qual.tpe.members+
"\n type history = "+qual.tpe.termSymbol.infosString+
"\n phase = "+phase)
}
tree.symbol
} else {
member(qual, name)
}
if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) {
val qual1 =
if (member(qual, name) != NoSymbol) qual
else adaptToMemberWithArgs(tree, qual, name, mode)
if (qual1 ne qual) return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
if (!reallyExists(sym)) {
if (context.owner.toplevelClass.isJavaDefined && name.isTypeName) {
val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) }
if (tree1 != EmptyTree) return typed1(tree1, mode, pt)
}
// try to expand according to Dynamic rules.
if (settings.Xexperimental.value && (qual.tpe.widen.typeSymbol isNonBottomSubClass DynamicClass)) {
var dynInvoke = Apply(Select(qual, nme.applyDynamic), List(Literal(Constant(name.decode))))
context.tree match {
case Apply(tree1, args) if tree1 eq tree =>
;
case _ =>
dynInvoke = Apply(dynInvoke, List())
}
return typed1(util.trace("dynatype: ")(dynInvoke), mode, pt)
}
if (settings.debug.value) {
log(
"qual = "+qual+":"+qual.tpe+
"\nSymbol="+qual.tpe.termSymbol+"\nsymbol-info = "+qual.tpe.termSymbol.info+
"\nscope-id = "+qual.tpe.termSymbol.info.decls.hashCode()+"\nmembers = "+qual.tpe.members+
"\nname = "+name+"\nfound = "+sym+"\nowner = "+context.enclClass.owner
)
}
def makeErrorTree = {
val tree1 = tree match {
case Select(_, _) => treeCopy.Select(tree, qual, name)
case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name)
}
setError(tree1)
}
if (name == nme.ERROR && forInteractive)
return makeErrorTree
if (!qual.tpe.widen.isErroneous)
notAMember(tree, qual, name)
if (forInteractive) makeErrorTree else setError(tree)
} else {
val tree1 = tree match {
case Select(_, _) => treeCopy.Select(tree, qual, name)
case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name)
}
val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual)
val result = stabilize(tree2, pre2, mode, pt)
def isPotentialNullDeference() = {
phase.id <= currentRun.typerPhase.id &&
!sym.isConstructor &&
!(qual.tpe <:< NotNullClass.tpe) && !qual.tpe.isNotNull &&
!(List(Any_isInstanceOf, Any_asInstanceOf) contains result.symbol) // null.is/as is not a dereference
}
// unit is null here sometimes; how are we to know when unit might be null? (See bug #2467.)
if (settings.warnSelectNullable.value && isPotentialNullDeference && unit != null)
unit.warning(tree.pos, "potential null pointer dereference: "+tree)
val selection = result match {
// could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks
treeCopy.SelectFromTypeTree(
result,
(TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect
// will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one?
checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "")
qual // you only get to see the wrapped tree after running this check :-p
}) setType qual.tpe,
name)
case accErr: Inferencer#AccessError =>
val qual1 =
try adaptToMemberWithArgs(tree, qual, name, mode)
catch { case _: TypeError => qual }
if (qual1 ne qual) typed(Select(qual1, name) setPos tree.pos, mode, pt)
else accErr.emit()
case _ =>
result
}
// To fully benefit from special casing the return type of
// getClass, we have to catch it immediately so expressions
// like x.getClass().newInstance() are typed with the type of x.
val isRefinableGetClass = (
selection.symbol.name == nme.getClass_
&& selection.tpe.params.isEmpty
// TODO: If the type of the qualifier is inaccessible, we can cause private types
// to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this
// so for now it requires the type symbol be public.
&& qual.tpe.typeSymbol.isPublic
)
if (isRefinableGetClass)
selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe))
else
selection
}
}
/** Attribute an identifier consisting of a simple name or an outer reference.
*
* @param tree The tree representing the identifier.
* @param name The name of the identifier.
* Transformations: (1) Prefix class members with this.
* (2) Change imported symbols to selections
*/
def typedIdent(name: Name): Tree = {
def ambiguousError(msg: String) =
error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg)
var defSym: Symbol = tree.symbol // the directly found symbol
var pre: Type = NoPrefix // the prefix type of defSym, if a class member
var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select
var inaccessibleSym: Symbol = NoSymbol // the first symbol that was found but that was discarded
// for being inaccessible; used for error reporting
var inaccessibleExplanation: String = ""
// If a special setting is given, the empty package will be checked as a
// last ditch effort before failing. This method sets defSym and returns
// true if a member of the given name exists.
def checkEmptyPackage(): Boolean = {
defSym = EmptyPackageClass.tpe.nonPrivateMember(name)
defSym != NoSymbol
}
// A symbol qualifies if it exists and is not stale. Stale symbols
// are made to disappear here. In addition,
// if we are in a constructor of a pattern, we ignore all definitions
// which are methods (note: if we don't do that
// case x :: xs in class List would return the :: method)
// unless they are stable or are accessors (the latter exception is for better error messages).
def qualifies(sym: Symbol): Boolean = {
reallyExists(sym) &&
((mode & PATTERNmode | FUNmode) != (PATTERNmode | FUNmode) || !sym.isSourceMethod || sym.hasFlag(ACCESSOR))
}
if (defSym == NoSymbol) {
var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope
var cx = context
if ((mode & (PATTERNmode | TYPEPATmode)) != 0) {
// println("ignoring scope: "+name+" "+cx.scope+" "+cx.outer.scope)
// ignore current variable scope in patterns to enforce linearity
cx = cx.outer
}
while (defSym == NoSymbol && cx != NoContext) {
currentRun.compileSourceFor(context.asInstanceOf[analyzer.Context], name)
pre = cx.enclClass.prefix
defEntry = cx.scope.lookupEntry(name)
if ((defEntry ne null) && qualifies(defEntry.sym)) {
defSym = defEntry.sym
}
else {
cx = cx.enclClass
val foundSym = pre.member(name) filter qualifies
defSym = foundSym filter (context.isAccessible(_, pre, false))
if (defSym == NoSymbol) {
if ((foundSym ne NoSymbol) && (inaccessibleSym eq NoSymbol)) {
inaccessibleSym = foundSym
inaccessibleExplanation = analyzer.lastAccessCheckDetails
}
cx = cx.outer
}
}
}
val symDepth = if (defEntry eq null) cx.depth
else cx.depth - (cx.scope.nestingLevel - defEntry.owner.nestingLevel)
var impSym: Symbol = NoSymbol // the imported symbol
var imports = context.imports // impSym != NoSymbol => it is imported from imports.head
while (!reallyExists(impSym) && !imports.isEmpty && imports.head.depth > symDepth) {
impSym = imports.head.importedSymbol(name)
if (!impSym.exists) imports = imports.tail
}
// detect ambiguous definition/import,
// update `defSym' to be the final resolved symbol,
// update `pre' to be `sym's prefix type in case it is an imported member,
// and compute value of:
if (defSym.exists && impSym.exists) {
// imported symbols take precedence over package-owned symbols in different
// compilation units. Defined symbols take precedence over erroneous imports.
if (defSym.definedInPackage &&
(!currentRun.compiles(defSym) ||
(context.unit ne null) && defSym.sourceFile != context.unit.source.file))
defSym = NoSymbol
else if (impSym.isError || impSym.name == nme.CONSTRUCTOR)
impSym = NoSymbol
}
if (defSym.exists) {
if (impSym.exists)
ambiguousError(
"it is both defined in "+defSym.owner +
" and imported subsequently by \n"+imports.head)
else if (!defSym.owner.isClass || defSym.owner.isPackageClass || defSym.isTypeParameterOrSkolem)
pre = NoPrefix
else
qual = atPos(tree.pos.focusStart)(gen.mkAttributedQualifier(pre))
} else {
if (impSym.exists) {
var impSym1 = NoSymbol
var imports1 = imports.tail
def ambiguousImport() = {
if (!(imports.head.qual.tpe =:= imports1.head.qual.tpe))
ambiguousError(
"it is imported twice in the same scope by\n"+imports.head + "\nand "+imports1.head)
}
while (!imports1.isEmpty &&
(!imports.head.isExplicitImport(name) ||
imports1.head.depth == imports.head.depth)) {
var impSym1 = imports1.head.importedSymbol(name)
if (reallyExists(impSym1)) {
if (imports1.head.isExplicitImport(name)) {
if (imports.head.isExplicitImport(name) ||
imports1.head.depth != imports.head.depth) ambiguousImport()
impSym = impSym1
imports = imports1
} else if (!imports.head.isExplicitImport(name) &&
imports1.head.depth == imports.head.depth) ambiguousImport()
}
imports1 = imports1.tail
}
defSym = impSym
val qual0 = imports.head.qual
if (!(shortenImports && qual0.symbol.isPackage)) // optimization: don't write out package prefixes
qual = atPos(tree.pos.focusStart)(resetPos(qual0.duplicate))
pre = qual.tpe
}
else if (settings.exposeEmptyPackage.value && checkEmptyPackage())
log("Allowing empty package member " + name + " due to settings.")
else {
if (settings.debug.value) {
log(context.imports)//debug
}
if (inaccessibleSym eq NoSymbol) {
error(tree.pos, "not found: "+decodeWithKind(name, context.owner))
}
else new AccessError(
tree, inaccessibleSym, context.enclClass.owner.thisType,
inaccessibleExplanation
).emit()
defSym = context.owner.newErrorSymbol(name)
}
}
}
if (defSym.owner.isPackageClass) pre = defSym.owner.thisType
if (defSym.isThisSym) {
typed1(This(defSym.owner) setPos tree.pos, mode, pt)
} else {
val tree1 = if (qual == EmptyTree) tree
else atPos(tree.pos)(Select(qual, name))
// atPos necessary because qualifier might come from startContext
val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual)
// assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right?
stabilize(tree2, pre2, mode, pt) match {
case accErr: Inferencer#AccessError => accErr.emit()
case result => result
}
}
}
def typedCompoundTypeTree(templ: Template) = {
val parents1 = templ.parents mapConserve (typedType(_, mode))
if (parents1 exists (_.tpe.isError)) tree setType ErrorType
else {
val decls = new Scope
//Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id)
val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos)
newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ.body)
tree setType self
}
}
def typedAppliedTypeTree(tpt: Tree, args: List[Tree]) = {
val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType)
if (tpt1.tpe.isError) {
setError(tree)
} else if (!tpt1.hasSymbol) {
errorTree(tree, tpt1.tpe+" does not take type parameters")
} else {
val tparams = tpt1.symbol.typeParams
if (sameLength(tparams, args)) {
// @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
val args1 =
if(!tpt1.symbol.rawInfo.isComplete)
args mapConserve (typedHigherKindedType(_, mode))
// if symbol hasn't been fully loaded, can't check kind-arity
else map2Conserve(args, tparams) {
(arg, tparam) =>
typedHigherKindedType(arg, mode, polyType(tparam.typeParams, AnyClass.tpe))
//@M! the polytype denotes the expected kind
}
val argtypes = args1 map (_.tpe)
(args, tparams).zipped foreach { (arg, tparam) => arg match {
// note: can't use args1 in selector, because Bind's got replaced
case Bind(_, _) =>
if (arg.symbol.isAbstractType)
arg.symbol setInfo // XXX, feedback. don't trackSymInfo here!
TypeBounds(
lub(List(arg.symbol.info.bounds.lo, tparam.info.bounds.lo.subst(tparams, argtypes))),
glb(List(arg.symbol.info.bounds.hi, tparam.info.bounds.hi.subst(tparams, argtypes))))
case _ =>
}}
val original = treeCopy.AppliedTypeTree(tree, tpt1, args1)
val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal original
if(tpt1.tpe.isInstanceOf[PolyType]) // did the type application (performed by appliedType) involve an unchecked beta-reduction?
(TypeTreeWithDeferredRefCheck(){ () =>
// wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap
// we can't simply use original in refchecks because it does not contains types
// (and the only typed trees we have have been mangled so they're not quite the original tree anymore)
checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "")
result // you only get to see the wrapped tree after running this check :-p
}).setType(result.tpe)
else result
} else if (tparams.isEmpty) {
errorTree(tree, tpt1.tpe+" does not take type parameters")
} else {
//Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}")
if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug
errorTree(tree, "wrong number of type arguments for "+tpt1.tpe+", should be "+tparams.length)
}
}
}
def adaptCase(cdef: CaseDef, tpe: Type): CaseDef =
treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, adapt(cdef.body, mode, tpe))
// begin typed1
val sym: Symbol = tree.symbol
if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
//if (settings.debug.value && tree.isDef) log("typing definition of "+sym);//DEBUG
tree match {
case PackageDef(pid, stats) =>
val pid1 = typedQualifier(pid).asInstanceOf[RefTree]
assert(sym.moduleClass ne NoSymbol, sym)
// complete lazy annotations
val annots = sym.annotations
val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls))
.typedStats(stats, NoSymbol)
treeCopy.PackageDef(tree, pid1, stats1) setType NoType
case tree @ ClassDef(_, _, _, _) =>
newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree)
case tree @ ModuleDef(_, _, _) =>
newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree)
case vdef @ ValDef(_, _, _, _) =>
typedValDef(vdef)
case ddef @ DefDef(_, _, _, _, _, _) =>
newTyper(context.makeNewScope(tree, sym)).typedDefDef(ddef)
case tdef @ TypeDef(_, _, _, _) =>
typedTypeDef(tdef)
case ldef @ LabelDef(_, _, _) =>
labelTyper(ldef).typedLabelDef(ldef)
case ddef @ DocDef(comment, defn) =>
if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) {
docComments(sym) = comment
comment.defineVariables(sym)
val typer1 = newTyper(context.makeNewScope(tree, context.owner))
for (useCase <- comment.useCases) {
typer1.silent(_.typedUseCase(useCase)) match {
case ex: TypeError =>
unit.warning(useCase.pos, ex.msg)
case _ =>
}
for (useCaseSym <- useCase.defined) {
if (sym.name != useCaseSym.name)
unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode)
}
}
}
typed(defn, mode, pt)
case Annotated(constr, arg) =>
typedAnnotated(constr, typed(arg, mode, pt))
case tree @ Block(_, _) =>
newTyper(context.makeNewScope(tree, context.owner))
.typedBlock(tree, mode, pt)
case Alternative(alts) =>
val alts1 = alts mapConserve (alt => typed(alt, mode | ALTmode, pt))
treeCopy.Alternative(tree, alts1) setType pt
case Star(elem) =>
checkStarPatOK(tree.pos, mode)
val elem1 = typed(elem, mode, pt)
treeCopy.Star(tree, elem1) setType makeFullyDefined(pt)
case Bind(name, body) =>
typedBind(name, body)
case UnApply(fun, args) =>
val fun1 = typed(fun)
val tpes = formalTypes(unapplyTypeList(fun.symbol, fun1.tpe), args.length)
val args1 = (args, tpes).zipped map typedPattern
treeCopy.UnApply(tree, fun1, args1) setType pt
case ArrayValue(elemtpt, elems) =>
typedArrayValue(elemtpt, elems)
case tree @ Function(_, _) =>
if (tree.symbol == NoSymbol)
tree.symbol = context.owner.newValue(tree.pos, nme.ANON_FUN_NAME)
.setFlag(SYNTHETIC).setInfo(NoType)
newTyper(context.makeNewScope(tree, tree.symbol)).typedFunction(tree, mode, pt)
case Assign(lhs, rhs) =>
typedAssign(lhs, rhs)
case AssignOrNamedArg(lhs, rhs) => // called by NamesDefaults in silent typecheck
typedAssign(lhs, rhs)
case If(cond, thenp, elsep) =>
typedIf(cond, thenp, elsep)
case tree @ Match(selector, cases) =>
if (selector == EmptyTree) {
val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1
val params = for (i <- List.range(0, arity)) yield
atPos(tree.pos.focusStart) {
ValDef(Modifiers(PARAM | SYNTHETIC),
unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
}
val ids = for (p <- params) yield Ident(p.name)
val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
val body = treeCopy.Match(tree, selector1, cases)
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
} else {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
var cases1 = typedCases(tree, cases, selector1.tpe.widen, pt)
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
if (needAdapt) {
cases1 = cases1 map (adaptCase(_, owntype))
}
treeCopy.Match(tree, selector1, cases1) setType owntype
}
case Return(expr) =>
typedReturn(expr)
case Try(block, catches, finalizer) =>
var block1 = typed(block, pt)
var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt)
val finalizer1 = if (finalizer.isEmpty) finalizer
else typed(finalizer, UnitClass.tpe)
val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
if (needAdapt) {
block1 = adapt(block1, mode, owntype)
catches1 = catches1 map (adaptCase(_, owntype))
}
treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype
case Throw(expr) =>
val expr1 = typed(expr, EXPRmode | BYVALmode, ThrowableClass.tpe)
treeCopy.Throw(tree, expr1) setType NothingClass.tpe
case New(tpt: Tree) =>
typedNew(tpt)
case Typed(expr, Function(List(), EmptyTree)) =>
typedEta(checkDead(typed1(expr, mode, pt)))
case Typed(expr, tpt @ Ident(tpnme.WILDCARD_STAR)) =>
val expr0 = typed(expr, onlyStickyModes(mode), WildcardType)
def subArrayType(pt: Type) =
if (isValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt)
else {
val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt)
ExistentialType(List(tparam), arrayType(tparam.tpe))
}
val (expr1, baseClass) = expr0.tpe.typeSymbol match {
case ArrayClass => (adapt(expr0, onlyStickyModes(mode), subArrayType(pt)), ArrayClass)
case _ => (adapt(expr0, onlyStickyModes(mode), seqType(pt)), SeqClass)
}
expr1.tpe.baseType(baseClass) match {
case TypeRef(_, _, List(elemtp)) =>
treeCopy.Typed(tree, expr1, tpt setType elemtp) setType elemtp
case _ =>
setError(tree)
}
case Typed(expr, tpt) =>
val tpt1 = typedType(tpt, mode)
val expr1 = typed(expr, onlyStickyModes(mode), tpt1.tpe.deconst)
val owntype =
if (isPatternMode) inferTypedPattern(tpt1.pos, tpt1.tpe, pt)
else tpt1.tpe
//Console.println(typed pattern: "+tree+":"+", tp = "+tpt1.tpe+", pt = "+pt+" ==> "+owntype)//DEBUG
treeCopy.Typed(tree, expr1, tpt1) setType owntype
case TypeApply(fun, args) =>
// @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
//@M! we must type fun in order to type the args, as that requires the kinds of fun's type parameters.
// However, args should apparently be done first, to save context.undetparams. Unfortunately, the args
// *really* have to be typed *after* fun. We escape from this classic Catch-22 by simply saving&restoring undetparams.
// @M TODO: the compiler still bootstraps&all tests pass when this is commented out..
//val undets = context.undetparams
// @M: fun is typed in TAPPmode because it is being applied to its actual type parameters
val fun1 = typed(fun, forFunMode(mode) | TAPPmode, WildcardType)
val tparams = fun1.symbol.typeParams
//@M TODO: val undets_fun = context.undetparams ?
// "do args first" (by restoring the context.undetparams) in order to maintain context.undetparams on the function side.
// @M TODO: the compiler still bootstraps when this is commented out.. TODO: run tests
//context.undetparams = undets
// @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds?
val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) {
//@M! the polytype denotes the expected kind
(arg, tparam) => typedHigherKindedType(arg, mode, polyType(tparam.typeParams, AnyClass.tpe))
} else {
//@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases.
// Until the right alternative for an overloaded method is known, be very liberal,
// typedTypeApply will find the right alternative and then do the same check as
// in the then-branch above. (see pos/tcpoly_overloaded.scala)
// this assert is too strict: be tolerant for errors like trait A { def foo[m[x], g]=error(""); def x[g] = foo[g/*ERR: missing argument type*/] }
//assert(fun1.symbol.info.isInstanceOf[OverloadedType] || fun1.symbol.isError) //, (fun1.symbol,fun1.symbol.info,fun1.symbol.info.getClass,args,tparams))
args mapConserve (typedHigherKindedType(_, mode))
}
//@M TODO: context.undetparams = undets_fun ?
typedTypeApply(tree, mode, fun1, args1)
case Apply(Block(stats, expr), args) =>
typed1(atPos(tree.pos)(Block(stats, Apply(expr, args))), mode, pt)
case Apply(fun, args) =>
typedApply(fun, args) match {
case Apply(Select(New(tpt), name), args)
if (tpt.tpe != null &&
tpt.tpe.typeSymbol == ArrayClass &&
args.length == 1 &&
erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor
// convert new Array[T](len) to evidence[ClassManifest[T]].newArray(len)
// convert new Array^N[T](len) for N > 1 to evidence[ClassManifest[T]].newArrayN(len)
val Some((level, manifType)) = erasure.GenericArray.unapply(tpt.tpe)
if (level > MaxArrayDims)
error(tree.pos, "cannot create a generic multi-dimensional array of more than "+MaxArrayDims+" dimensions")
val newArrayApp = atPos(tree.pos) {
val manif = getManifestTree(tree.pos, manifType, false)
new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args)
}
typed(newArrayApp, mode, pt)
case tree1 =>
tree1
}
case ApplyDynamic(qual, args) =>
val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic")
val qual1 = typed(qual, AnyRefClass.tpe)
val args1 = args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg))
treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType)
case Super(qual, mix) =>
typedSuper(qual, mix)
case This(qual) =>
typedThis(qual)
case Select(qual @ Super(_, _), nme.CONSTRUCTOR) =>
val qual1 =
typed(qual, EXPRmode | QUALmode | POLYmode | SUPERCONSTRmode, WildcardType)
// the qualifier type of a supercall constructor is its first parent class
typedSelect(qual1, nme.CONSTRUCTOR)
case Select(qual, name) =>
incCounter(typedSelectCount)
var qual1 = checkDead(typedQualifier(qual, mode))
if (name.isTypeName) qual1 = checkStable(qual1)
val tree1 = // temporarily use `filter' and an alternative for `withFilter'
if (name == nme.withFilter)
silent(_ => typedSelect(qual1, name)) match {
case result1: Tree =>
result1
case ex1: TypeError =>
silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match {
case result2: Tree =>
unit.deprecationWarning(
tree.pos, "`withFilter' method does not yet exist on "+qual1.tpe.widen+
", using `filter' method instead")
result2
case ex2: TypeError =>
reportTypeError(tree.pos, ex1)
setError(tree)
}
}
else
typedSelect(qual1, name)
if (qual1.symbol == RootPackage) treeCopy.Ident(tree1, name)
else tree1
case Ident(name) =>
incCounter(typedIdentCount)
if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) ||
(name == tpnme.WILDCARD && (mode & TYPEmode) != 0))
tree setType makeFullyDefined(pt)
else
typedIdent(name)
case Literal(value) =>
tree setType (
if (value.tag == UnitTag) UnitClass.tpe
else ConstantType(value))
case SingletonTypeTree(ref) =>
val ref1 = checkStable(
typed(ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe))
tree setType ref1.tpe.resultType
case SelectFromTypeTree(qual, selector) =>
val qual1 = typedType(qual, mode)
if (qual1.tpe.isVolatile) error(tree.pos, "illegal type selection from volatile type "+qual.tpe)
typedSelect(qual1, selector)
case CompoundTypeTree(templ) =>
typedCompoundTypeTree(templ)
case AppliedTypeTree(tpt, args) =>
typedAppliedTypeTree(tpt, args)
case TypeBoundsTree(lo, hi) =>
val lo1 = typedType(lo, mode)
val hi1 = typedType(hi, mode)
treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe)
case etpt @ ExistentialTypeTree(_, _) =>
newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode)
case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure)
case tpt @ TypeTree() =>
if (tpt.original != null)
tree setType typedType(tpt.original, mode).tpe
else
// we should get here only when something before failed
// and we try again (@see tryTypedApply). In that case we can assign
// whatever type to tree; we just have to survive until a real error message is issued.
tree setType AnyClass.tpe
case Import(expr, selectors) =>
assert(forInteractive) // should not happen in normal circumstances.
tree setType tree.symbol.tpe
case _ =>
abort("unexpected tree: " + tree.getClass + "\n" + tree)//debug
}
}
/**
* @param tree ...
* @param mode ...
* @param pt ...
* @return ...
*/
def typed(tree: Tree, mode: Int, pt: Type): Tree = {
indentTyping()
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
if (settings.debug.value)
log("Dropping existential: " + tree + " " + tp)
new SubstWildcardMap(tparams).apply(tp)
case TypeRef(_, sym, _) if sym.isAliasType =>
val tp0 = tp.normalize
val tp1 = dropExistential(tp0)
if (tp1 eq tp0) tp else tp1
case _ => tp
}
var alreadyTyped = false
try {
if (Statistics.enabled) {
val t = currentTime()
if (pendingTreeTypes.nonEmpty) {
microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt
}
typerTime = t
pendingTreeTypes = tree.getClass :: pendingTreeTypes
}
if (context.retyping &&
(tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) {
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
alreadyTyped = tree.tpe ne null
var tree1: Tree = if (alreadyTyped) tree else {
printTyping(
ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
"undetparams" -> context.undetparams,
"implicitsEnabled" -> context.implicitsEnabled,
"silent" -> !context.reportGeneralErrors,
"context.owner" -> context.owner
)
)
val tree1 = typed1(tree, mode, dropExistential(pt))
printTyping("typed %s: %s%s".format(
ptTree(tree1), tree1.tpe,
if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")
)
tree1
}
tree1.tpe = addAnnotations(tree1, tree1.tpe)
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
if (!alreadyTyped) {
printTyping("adapted %s: %s to %s, %s".format(
tree1, tree1.tpe.widen, pt, context.undetparamsString)
) //DEBUG
}
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
result
} catch {
case ex: TypeError =>
tree.tpe = null
printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Exception =>
if (settings.debug.value) // @M causes cyclic reference error
Console.println("exception when typing "+tree+", pt = "+pt)
if ((context ne null) && (context.unit ne null) &&
(context.unit.source ne null) && (tree ne null))
logError("AT: " + (tree.pos).dbgString, ex)
throw ex
}
finally {
deindentTyping()
if (Statistics.enabled) {
val t = currentTime()
microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt
visitsByType(pendingTreeTypes.head) += 1
typerTime = t
pendingTreeTypes = pendingTreeTypes.tail
}
}
}
def atOwner(owner: Symbol): Typer =
newTyper(context.make(context.tree, owner))
def atOwner(tree: Tree, owner: Symbol): Typer =
newTyper(context.make(tree, owner))
/** Types expression or definition <code>tree.
*
* @param tree ...
* @return ...
*/
def typed(tree: Tree): Tree = {
val ret = typed(tree, EXPRmode, WildcardType)
ret
}
def typedPos(pos: Position)(tree: Tree) = typed(atPos(pos)(tree))
/** Types expression <code>tree with given prototype pt .
*
* @param tree ...
* @param pt ...
* @return ...
*/
def typed(tree: Tree, pt: Type): Tree =
typed(tree, EXPRmode, pt)
/** Types qualifier <code>tree of a select node.
* E.g. is tree occurs in a context like <code>tree.m.
*/
def typedQualifier(tree: Tree, mode: Int, pt: Type): Tree =
typed(tree, EXPRmode | QUALmode | POLYmode | mode & TYPEPATmode, pt) // TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit
/** Types qualifier <code>tree of a select node.
* E.g. is tree occurs in a context like <code>tree.m.
*/
def typedQualifier(tree: Tree, mode: Int): Tree =
typedQualifier(tree, mode, WildcardType)
def typedQualifier(tree: Tree): Tree = typedQualifier(tree, NOmode, WildcardType)
/** Types function part of an application */
def typedOperator(tree: Tree): Tree =
typed(tree, EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType)
/** Types a pattern with prototype <code>pt */
def typedPattern(tree: Tree, pt: Type): Tree = {
// We disable implicits because otherwise some constructs will
// type check which should not. The pattern matcher does not
// perform implicit conversions in an attempt to consummate a match.
context.withImplicitsDisabled(typed(tree, PATTERNmode, pt))
}
/** Types a (fully parameterized) type tree */
def typedType(tree: Tree, mode: Int): Tree =
typed(tree, forTypeMode(mode), WildcardType)
/** Types a (fully parameterized) type tree */
def typedType(tree: Tree): Tree = typedType(tree, NOmode)
/** Types a higher-kinded type tree -- pt denotes the expected kind*/
def typedHigherKindedType(tree: Tree, mode: Int, pt: Type): Tree =
if (pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's *
else typed(tree, HKmode, pt)
def typedHigherKindedType(tree: Tree, mode: Int): Tree =
typed(tree, HKmode, WildcardType)
def typedHigherKindedType(tree: Tree): Tree = typedHigherKindedType(tree, NOmode)
/** Types a type constructor tree used in a new or supertype */
def typedTypeConstructor(tree: Tree, mode: Int): Tree = {
val result = typed(tree, forTypeMode(mode) | FUNmode, WildcardType)
val restpe = result.tpe.normalize // normalize to get rid of type aliases for the following check (#1241)
if (!phase.erasedTypes && restpe.isInstanceOf[TypeRef] && !restpe.prefix.isStable && !context.unit.isJava) {
// The isJava exception if OK only because the only type constructors scalac gets
// to see are those in the signatures. These do not need a unique object as a prefix.
// The situation is different for new's and super's, but scalac does not look deep
// enough to see those. See #3938
error(tree.pos, restpe.prefix+" is not a legal prefix for a constructor")
}
//@M fix for #2208
// if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef
if(result.tpe.typeArgs.isEmpty) {
// minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) {
// must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not
// designed to deal with the cycles in the scala package (ScalaObject extends
// AnyRef, but the AnyRef type alias is entered after the scala package is
// loaded and completed, so that ScalaObject is unpickled while AnyRef is not
// yet defined )
result setType(restpe)
} else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208
// during uncurry (after refchecks), all types are normalized
result
}
}
def typedTypeConstructor(tree: Tree): Tree = typedTypeConstructor(tree, NOmode)
def computeType(tree: Tree, pt: Type): Type = {
val tree1 = typed(tree, pt)
transformed(tree) = tree1
packedType(tree1, context.owner)
}
def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match {
case Some(tree1) => transformed -= tree; tree1
case None => typed(tree, mode, pt)
}
def findManifest(tp: Type, full: Boolean) = atPhase(currentRun.typerPhase) {
inferImplicit(
EmptyTree,
appliedType((if (full) FullManifestClass else PartialManifestClass).typeConstructor, List(tp)),
true, false, context)
}
def getManifestTree(pos: Position, tp: Type, full: Boolean): Tree = {
val manifestOpt = findManifest(tp, full)
if (manifestOpt.tree.isEmpty) {
error(pos, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp)
Literal(Constant(null))
} else {
manifestOpt.tree
}
}
/*
def convertToTypeTree(tree: Tree): Tree = tree match {
case TypeTree() => tree
case _ => TypeTree(tree.tpe)
}
*/
}
}
Other Scala examples (source code examples)
Here is a short list of links related to this Scala Typers.scala source code file:
|