package scala.tools.nsc
package typechecker
import symtab.Flags._
import collection.{ mutable, immutable }
import transform.InfoTransform
import scala.collection.mutable.ListBuffer
abstract class RefChecks extends InfoTransform {
import global._
import definitions._
import typer.{typed, typedOperator, atOwner}
val phaseName: String = "refchecks"
override def phaseNewFlags: Long = lateMETHOD
def newTransformer(unit: CompilationUnit): RefCheckTransformer =
new RefCheckTransformer(unit)
override def changesBaseClasses = false
def transformInfo(sym: Symbol, tp: Type): Type =
if (sym.isModule && !sym.isStatic) {
sym setFlag (lateMETHOD | STABLE)
NullaryMethodType(tp)
} else tp
val toJavaRepeatedParam = new TypeMap {
def apply(tp: Type) = tp match {
case TypeRef(pre, RepeatedParamClass, args) =>
typeRef(pre, JavaRepeatedParamClass, args)
case _ =>
mapOver(tp)
}
}
val toScalaRepeatedParam = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, JavaRepeatedParamClass, args) =>
typeRef(pre, RepeatedParamClass, args)
case _ =>
mapOver(tp)
}
}
def accessFlagsToString(sym: Symbol) = flagsToString(
sym getFlag (PRIVATE | PROTECTED),
if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
)
class RefCheckTransformer(unit: CompilationUnit) extends Transformer {
var localTyper: analyzer.Typer = typer;
var currentApplication: Tree = EmptyTree
var inPattern: Boolean = false
var checkedCombinations = Set[List[Type]]()
private def checkOverloadedRestrictions(clazz: Symbol): Unit = {
val defaultGetters = clazz.info.findMember(nme.ANYNAME, 0L, DEFAULTPARAM, false).alternatives
val defaultMethodNames = defaultGetters map (sym => nme.defaultGetterToMethod(sym.name))
defaultMethodNames.distinct foreach { name =>
val methods = clazz.info.findMember(name, 0L, METHOD, false).alternatives
val haveDefaults = methods filter (sym => sym.hasParamWhich(_.hasDefaultFlag) && !nme.isProtectedAccessorName(sym.name))
if (haveDefaults.lengthCompare(1) > 0) {
val owners = haveDefaults map (_.owner)
if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size) {
unit.error(clazz.pos,
"in "+ clazz +
", multiple overloaded alternatives of "+ haveDefaults.head +
" define default arguments" + (
if (owners.forall(_ == clazz)) "."
else ".\nThe members with defaults are defined in "+owners.map(_.fullLocationString).mkString("", " and ", ".")
)
)
}
}
}
clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym =>
val alts = clazz.info.decl(sym.name).alternatives
if (alts.size > 1)
alts foreach (x => unit.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds"))
}
}
def hasRepeatedParam(tp: Type): Boolean = tp match {
case MethodType(formals, restpe) => isScalaVarArgs(formals) || hasRepeatedParam(restpe)
case PolyType(_, restpe) => hasRepeatedParam(restpe)
case _ => false
}
def addVarargBridges(clazz: Symbol): List[Tree] = {
val self = clazz.thisType
val bridges = new ListBuffer[Tree]
def varargBridge(member: Symbol, bridgetpe: Type): Tree = {
val bridge = member.cloneSymbolImpl(clazz)
.setPos(clazz.pos).setFlag(member.flags | VBRIDGE)
bridge.setInfo(bridgetpe.cloneInfo(bridge))
clazz.info.decls enter bridge
val List(params) = bridge.paramss
val TypeRef(_, JavaRepeatedParamClass, List(elemtp)) = params.last.tpe
val (initargs, List(lastarg0)) = (params map Ident) splitAt (params.length - 1)
val lastarg = gen.wildcardStar(gen.mkWrapArray(lastarg0, elemtp))
val body = Apply(Select(This(clazz), member), initargs ::: List(lastarg))
localTyper.typed {
(DefDef(bridge, body))
}
}
for (member <- clazz.info.nonPrivateMembers) {
if (!(member hasFlag DEFERRED) && hasRepeatedParam(member.info)) {
val jtpe = toJavaRepeatedParam(self.memberType(member))
val inherited = clazz.info.nonPrivateMemberAdmitting(member.name, VBRIDGE) filter (
sym => (self.memberType(sym) matches jtpe) && !(sym hasFlag VBRIDGE)
)
if (inherited.exists) {
bridges += varargBridge(member, jtpe)
}
}
}
bridges.toList
}
private def checkAllOverrides(clazz: Symbol, typesOnly: Boolean = false) {
case class MixinOverrideError(member: Symbol, msg: String)
var mixinOverrideErrors = new ListBuffer[MixinOverrideError]()
def printMixinOverrideErrors() {
mixinOverrideErrors.toList match {
case List() =>
case List(MixinOverrideError(_, msg)) =>
unit.error(clazz.pos, msg)
case MixinOverrideError(member, msg) :: others =>
val others1 = others.map(_.member.name.decode).filter(member.name.decode != _).distinct
unit.error(
clazz.pos,
msg+(if (others1.isEmpty) ""
else ";\n other members with override errors are: "+(others1 mkString ", ")))
}
}
val self = clazz.thisType
def isAbstractTypeWithoutFBound(sym: Symbol) =
sym.isAbstractType && !isFBounded(sym)
def isFBounded(tsym: Symbol) =
tsym.info.baseTypeSeq exists (_ contains tsym)
def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz)
def infoStringWithLocation(sym: Symbol) = infoString0(sym, true)
def infoString0(sym: Symbol, showLocation: Boolean) = {
val sym1 = analyzer.underlying(sym)
sym1.toString() +
(if (showLocation)
sym1.locationString +
(if (sym1.isAliasType) ", which equals "+self.memberInfo(sym1)
else if (sym1.isAbstractType) " with bounds "+self.memberInfo(sym1)
else if (sym1.isTerm) " of type "+self.memberInfo(sym1)
else "")
else "")
}
def overridesType(tp1: Type, tp2: Type): Boolean = (tp1.normalize, tp2.normalize) match {
case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
rtp1 <:< rtp2
case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
rtp1 <:< rtp2
case (TypeRef(_, sym, _), _) if (sym.isModuleClass) =>
overridesType(NullaryMethodType(tp1), tp2)
case _ =>
tp1 <:< tp2
}
def checkOverride(clazz: Symbol, member: Symbol, other: Symbol) {
def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType
def isRootOrNone(sym: Symbol) = sym == RootClass || sym == NoSymbol
def overrideError(msg: String) {
if (noErrorType) {
val fullmsg =
"overriding "+infoStringWithLocation(other)+";\n "+
infoString(member)+" "+msg+
(if ((other.owner isSubClass member.owner) && other.isDeferred && !member.isDeferred)
";\n (Note that "+infoStringWithLocation(other)+" is abstract,"+
"\n and is therefore overridden by concrete "+infoStringWithLocation(member)+")"
else "")
if (member.owner == clazz) unit.error(member.pos, fullmsg)
else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
}
}
def overrideTypeError() {
if (noErrorType) {
overrideError("has incompatible type")
}
}
def overrideAccessError() {
val otherAccess = accessFlagsToString(other)
overrideError("has weaker access privileges; it should be "+ (if (otherAccess == "") "public" else "at least "+otherAccess))
}
if (member.owner != clazz) {
if ((member.owner isSubClass other.owner) && (member.isDeferred || !other.isDeferred)) {
return;
}
if (clazz.info.parents exists (parent =>
(parent.typeSymbol isSubClass other.owner) && (parent.typeSymbol isSubClass member.owner) &&
(member.isDeferred || !other.isDeferred))) {
return;
}
if (clazz.info.parents forall (parent =>
(parent.typeSymbol isSubClass other.owner) == (parent.typeSymbol isSubClass member.owner))) {
return;
}
}
def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) =
!(syms1 exists (syms2 contains))
if (typesOnly) checkOverrideTypes()
else {
if (member.isPrivate)
overrideError("has weaker access privileges; it should not be private")
val ob = other.accessBoundary(member.owner)
val mb = member.accessBoundary(member.owner)
def isOverrideAccessOK = member.isPublic || {
(!other.isProtected || member.isProtected) &&
((!isRootOrNone(ob) && ob.hasTransOwner(mb)) ||
other.isJavaDefined)
}
if (!isOverrideAccessOK) {
overrideAccessError()
} else if (other.isClass || other.isModule) {
overrideError("cannot be used here - classes and objects cannot be overridden");
} else if (!other.isDeferred && (member.isClass || member.isModule)) {
overrideError("cannot be used here - classes and objects can only override abstract types");
} else if (other hasFlag FINAL) {
overrideError("cannot override final member");
} else if (!other.isDeferred && !(member hasFlag (OVERRIDE | ABSOVERRIDE | SYNTHETIC))) {
overrideError("needs `override' modifier");
} else if ((other hasFlag ABSOVERRIDE) && other.isIncompleteIn(clazz) && !(member hasFlag ABSOVERRIDE)) {
overrideError("needs `abstract override' modifiers")
} else if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
(other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) {
overrideError("cannot override a mutable variable")
} else if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
!(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) &&
!member.isDeferred && !other.isDeferred &&
intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) {
overrideError("cannot override a concrete member without a third member that's overridden by both "+
"(this rule is designed to prevent ``accidental overrides'')")
} else if (other.isStable && !member.isStable) {
overrideError("needs to be a stable, immutable value")
} else if (member.isValue && (member hasFlag LAZY) &&
other.isValue && !other.isSourceMethod && !other.isDeferred && !(other hasFlag LAZY)) {
overrideError("cannot override a concrete non-lazy value")
} else if (other.isValue && (other hasFlag LAZY) && !other.isSourceMethod && !other.isDeferred &&
member.isValue && !(member hasFlag LAZY)) {
overrideError("must be declared lazy to override a concrete lazy value")
} else {
checkOverrideTypes()
if (settings.warnNullaryOverride.value) {
if (other.paramss.isEmpty && !member.paramss.isEmpty) {
unit.warning(member.pos, "non-nullary method overrides nullary method")
}
}
}
}
def checkOverrideTypes() {
if (other.isAliasType) {
if( !(sameLength(member.typeParams, other.typeParams) && (self.memberType(member).substSym(member.typeParams, other.typeParams) =:= self.memberType(other))) )
overrideTypeError();
} else if (other.isAbstractType) {
val memberTp = self.memberType(member)
val otherTp = self.memberInfo(other)
if (!(otherTp.bounds containsType memberTp)) {
overrideTypeError();
explainTypes(_.bounds containsType _, otherTp, memberTp)
}
val kindErrors = typer.infer.checkKindBounds(List(other), List(memberTp), self, member.owner)
if(!kindErrors.isEmpty)
unit.error(member.pos,
"The kind of "+member.keyString+" "+member.varianceString + member.nameString+
" does not conform to the expected kind of " + other.defString + other.locationString + "." +
kindErrors.toList.mkString("\n", ", ", ""))
if(member.isAliasType) {
val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.normalize), self, member.owner)
if(!kindErrors.isEmpty)
unit.error(member.pos,
"The kind of the right-hand side "+memberTp.normalize+" of "+member.keyString+" "+
member.varianceString + member.nameString+ " does not conform to its expected kind."+
kindErrors.toList.mkString("\n", ", ", ""))
} else if (member.isAbstractType) {
if (memberTp.isVolatile && !otherTp.bounds.hi.isVolatile)
overrideError("is a volatile type; cannot override a type with non-volatile upper bound")
}
} else if (other.isTerm) {
other.cookJavaRawInfo()
val memberTp = self.memberType(member)
val otherTp = self.memberType(other)
if (!overridesType(memberTp, otherTp)) {
overrideTypeError()
explainTypes(memberTp, otherTp)
}
if (member.isStable && !otherTp.isVolatile) {
if (memberTp.isVolatile)
overrideError("has a volatile type; cannot override a member with non-volatile type")
else memberTp.normalize.resultType match {
case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) =>
checkedCombinations += rt.parents
val tsym = rt.typeSymbol;
if (tsym.pos == NoPosition) tsym setPos member.pos
checkAllOverrides(tsym, typesOnly = true)
case _ =>
}
}
}
}
}
val opc = new overridingPairs.Cursor(clazz)
while (opc.hasNext) {
if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden);
opc.next
}
printMixinOverrideErrors()
if (clazz.isClass && !clazz.isTrait && !(clazz hasFlag ABSTRACT) && !typesOnly) {
val abstractErrors = new ListBuffer[String]
def abstractErrorMessage =
if (abstractErrors.size <= 2) abstractErrors mkString " "
else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "")
def abstractClassError(mustBeMixin: Boolean, msg: String) {
def prelude = (
if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible"
else if (mustBeMixin) clazz + " needs to be a mixin"
else clazz + " needs to be abstract"
) + ", since"
if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
else abstractErrors += msg
}
def javaErasedOverridingSym(sym: Symbol): Symbol =
clazz.tpe.nonPrivateMemberAdmitting(sym.name, BRIDGE).filter(other =>
!other.isDeferred && other.isJavaDefined && {
def uncurryAndErase(tp: Type) = erasure.erasure(uncurry.transformInfo(sym, tp))
val tp1 = uncurryAndErase(clazz.thisType.memberType(sym))
val tp2 = uncurryAndErase(clazz.thisType.memberType(other))
atPhase(currentRun.erasurePhase.next)(tp1 matches tp2)
})
def ignoreDeferred(member: Symbol) =
isAbstractTypeWithoutFBound(member) ||
(member.isJavaDefined &&
(currentRun.erasurePhase == NoPhase ||
javaErasedOverridingSym(member) != NoSymbol))
def checkNoAbstractMembers() = {
def memberList = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE)
val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m))
val grouped = missing groupBy (analyzer underlying _ name)
for (member <- missing) {
def undefined(msg: String) = abstractClassError(false, infoString(member) + " is not defined" + msg)
val underlying = analyzer.underlying(member)
if (underlying.isVariable) {
val isMultiple = grouped(underlying.name).size > 1
if (member.isSetter && isMultiple) ()
else undefined(
if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)"
else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)"
else analyzer.varNotice(member)
)
}
else if (underlying.isMethod) {
val abstractParams = underlying.tpe.paramTypes
val matchingName = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE)
val matchingArity = matchingName filter { m =>
!m.isDeferred &&
(m.name == underlying.name) &&
(m.tpe.paramTypes.size == underlying.tpe.paramTypes.size) &&
(m.tpe.typeParams.size == underlying.tpe.typeParams.size)
}
matchingArity match {
case concrete :: Nil =>
val mismatches = abstractParams zip concrete.tpe.paramTypes filterNot { case (x, y) => x =:= y }
mismatches match {
case (pa, pc) :: Nil =>
val addendum =
if (pa.typeSymbol == pc.typeSymbol) {
if (underlying.isJavaDefined && pa.typeArgs.isEmpty && pa.typeSymbol.typeParams.nonEmpty)
". To implement a raw type, use %s[_]".format(pa)
else if (pa.prefix =:= pc.prefix)
": their type parameters differ"
else
": their prefixes (i.e. enclosing instances) differ"
}
else ""
undefined("\n(Note that %s does not match %s%s)".format(pa, pc, addendum))
case xs =>
undefined("")
}
case _ =>
undefined("")
}
}
else undefined("")
}
for (member <- rest ; if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz))) {
val other = member.superSymbol(clazz)
val explanation =
if (other != NoSymbol) " and overrides incomplete superclass member " + infoString(other)
else ", but no concrete implementation could be found in a base class"
abstractClassError(true, infoString(member) + " is marked `abstract' and `override'" + explanation)
}
}
def checkNoAbstractDecls(bc: Symbol) {
for (decl <- bc.info.decls.iterator) {
if (decl.isDeferred && !ignoreDeferred(decl)) {
val impl = decl.matchingSymbol(clazz.thisType, admit = VBRIDGE)
if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) {
abstractClassError(false, "there is a deferred declaration of "+infoString(decl)+
" which is not implemented in a subclass"+analyzer.varNotice(decl))
}
}
}
val parents = bc.info.parents
if (!parents.isEmpty && parents.head.typeSymbol.hasFlag(ABSTRACT))
checkNoAbstractDecls(parents.head.typeSymbol)
}
checkNoAbstractMembers()
if (abstractErrors.isEmpty)
checkNoAbstractDecls(clazz)
if (abstractErrors.nonEmpty)
unit.error(clazz.pos, abstractErrorMessage)
} else if (clazz.isTrait) {
for (decl <- clazz.info.decls.iterator) {
val overridden = decl.overriddenSymbol(ObjectClass)
if (overridden.isFinal)
unit.error(decl.pos, "trait cannot redefine final method from class AnyRef")
}
}
def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = {
val isVarargs = hasRepeatedParam(member.tpe)
lazy val varargsType = toJavaRepeatedParam(member.tpe)
def isSignatureMatch(sym: Symbol) = !sym.isTerm || {
val symtpe = clazz.thisType memberType sym
def matches(tp: Type) = tp matches symtpe
matches(member.tpe) || (isVarargs && matches(varargsType))
}
def javaAccessCheck(sym: Symbol) = (
!inclazz.isJavaDefined
|| !sym.hasAccessBoundary
|| sym.isProtected
|| sym.privateWithin == member.enclosingPackageClass
)
def classDecls = inclazz.info.nonPrivateDecl(member.name)
def matchingSyms = classDecls filter (sym => isSignatureMatch(sym) && javaAccessCheck(sym))
(inclazz != clazz) && (matchingSyms != NoSymbol)
}
for (member <- clazz.info.decls.toList)
if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
!(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
unit.error(member.pos, member.toString() + " overrides nothing");
member resetFlag OVERRIDE
}
}
private def validateBaseTypes(clazz: Symbol) {
val seenParents = mutable.HashSet[Type]()
val seenTypes = new Array[List[Type]](clazz.info.baseTypeSeq.length)
for (i <- 0 until seenTypes.length)
seenTypes(i) = Nil
def register(tp: Type): Unit = {
val baseClass = tp.typeSymbol
if (baseClass.isClass) {
val index = clazz.info.baseTypeIndex(baseClass)
if (index >= 0) {
if (seenTypes(index) forall (tp1 => !(tp1 <:< tp)))
seenTypes(index) =
tp :: (seenTypes(index) filter (tp1 => !(tp <:< tp1)))
}
}
val remaining = tp.parents filterNot seenParents
seenParents ++= remaining
remaining foreach register
}
register(clazz.tpe)
for (i <- 0 until seenTypes.length) {
val baseClass = clazz.info.baseTypeSeq(i).typeSymbol
seenTypes(i) match {
case List() =>
println("??? base "+baseClass+" not found in basetypes of "+clazz)
case List(_) =>
;
case tp1 :: tp2 :: _ =>
unit.error(clazz.pos, "illegal inheritance;\n " + clazz +
" inherits different type instances of " + baseClass +
":\n" + tp1 + " and " + tp2);
explainTypes(tp1, tp2)
explainTypes(tp2, tp1)
}
}
}
private val ContraVariance = -1
private val NoVariance = 0
private val CoVariance = 1
private val AnyVariance = 2
private val escapedPrivateLocals = new mutable.HashSet[Symbol]
val varianceValidator = new Traverser {
private def validateVariance(base: Symbol) {
def varianceString(variance: Int): String =
if (variance == 1) "covariant"
else if (variance == -1) "contravariant"
else "invariant";
def relativeVariance(tvar: Symbol): Int = {
val clazz = tvar.owner
var sym = base
var state = CoVariance
while (sym != clazz && state != AnyVariance) {
if (sym.isParameter && !sym.owner.isConstructor && !sym.owner.isCaseApplyOrUnapply &&
!(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem &&
tvar.owner == sym.owner)) state = -state;
else if (!sym.owner.isClass ||
sym.isTerm && ((sym.isPrivateLocal || sym.isProtectedLocal || sym.isSuperAccessor ) && !(escapedPrivateLocals contains sym))) {
state = AnyVariance
} else if (sym.isAliasType) {
state = if (sym.allOverriddenSymbols.isEmpty) AnyVariance
else NoVariance
}
sym = sym.owner
}
state
}
def validateVariance(tp: Type, variance: Int): Unit = tp match {
case ErrorType => ;
case WildcardType => ;
case NoType => ;
case NoPrefix => ;
case ThisType(_) => ;
case ConstantType(_) => ;
case SingleType(pre, sym) =>
validateVariance(pre, variance)
case TypeRef(pre, sym, args) =>
if (sym.isAliasType)
validateVariance(tp.normalize, variance)
else if (sym.variance != NoVariance) {
val v = relativeVariance(sym)
if (v != AnyVariance && sym.variance != v * variance) {
def tpString(tp: Type) = tp match {
case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner)
case _ => "type "+tp
}
unit.error(base.pos,
varianceString(sym.variance) + " " + sym +
" occurs in " + varianceString(v * variance) +
" position in " + tpString(base.info) + " of " + base);
}
}
validateVariance(pre, variance)
validateVarianceArgs(args, variance, sym.typeParams)
case ClassInfoType(parents, decls, symbol) =>
validateVariances(parents, variance)
case RefinedType(parents, decls) =>
validateVariances(parents, variance)
for (sym <- decls.toList)
validateVariance(sym.info, if (sym.isAliasType) NoVariance else variance)
case TypeBounds(lo, hi) =>
validateVariance(lo, -variance)
validateVariance(hi, variance)
case MethodType(formals, result) =>
validateVariance(result, variance)
case NullaryMethodType(result) =>
validateVariance(result, variance)
case PolyType(tparams, result) =>
validateVariance(result, variance)
case ExistentialType(tparams, result) =>
validateVariances(tparams map (_.info), variance)
validateVariance(result, variance)
case AnnotatedType(annots, tp, selfsym) =>
if (!(annots exists (_.atp.typeSymbol.isNonBottomSubClass(uncheckedVarianceClass))))
validateVariance(tp, variance)
}
def validateVariances(tps: List[Type], variance: Int) {
tps foreach (tp => validateVariance(tp, variance))
}
def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) {
(tps zip tparams) foreach {
case (tp, tparam) => validateVariance(tp, variance * tparam.variance)
}
}
validateVariance(base.info, CoVariance)
}
override def traverse(tree: Tree) {
tree match {
case ClassDef(_, _, _, _) |
TypeDef(_, _, _, _) =>
validateVariance(tree.symbol)
super.traverse(tree)
case ValDef(_, _, _, _) =>
validateVariance(tree.symbol)
case DefDef(_, _, tparams, vparamss, tpt, rhs) =>
validateVariance(tree.symbol)
traverseTrees(tparams); traverseTreess(vparamss)
case Template(_, _, _) =>
super.traverse(tree)
case _ =>
}
}
}
class LevelInfo(val outer: LevelInfo) {
val scope: Scope = if (outer eq null) new Scope else new Scope(outer.scope)
var maxindex: Int = Int.MinValue
var refpos: Position = _
var refsym: Symbol = _
}
private var currentLevel: LevelInfo = null
private val symIndex = new mutable.HashMap[Symbol, Int]
private def pushLevel() {
currentLevel = new LevelInfo(currentLevel)
}
private def popLevel() {
currentLevel = currentLevel.outer
}
private def enterSyms(stats: List[Tree]) {
var index = -1
for (stat <- stats) {
index = index + 1;
stat match {
case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) =>
val sym = stat.symbol.lazyAccessorOrSelf
if (sym.isLocal) {
currentLevel.scope.enter(sym)
symIndex(sym) = index;
}
case _ =>
}
}
}
private def enterReference(pos: Position, sym: Symbol) {
if (sym.isLocal) {
val e = currentLevel.scope.lookupEntry(sym.name)
if ((e ne null) && sym == e.sym) {
var l = currentLevel
while (l.scope != e.owner) l = l.outer;
val symindex = symIndex(sym)
if (l.maxindex < symindex) {
l.refpos = pos
l.refsym = sym
l.maxindex = symindex
}
}
}
}
object normalizeAll extends TypeMap {
def apply(tp: Type) = mapOver(tp).normalize
}
def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match {
case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 =>
def isReferenceOp = name == nme.eq || name == nme.ne
def isNew(tree: Tree) = tree match {
case Function(_, _)
| Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
case _ => false
}
def underlyingClass(tp: Type): Symbol = {
var sym = tp.widen.typeSymbol
while (sym.isAbstractType)
sym = sym.info.bounds.hi.widen.typeSymbol
sym
}
val actual = underlyingClass(args.head.tpe)
val receiver = underlyingClass(qual.tpe)
def onTrees[T](f: List[Tree] => T) = f(List(qual, args.head))
def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual))
def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen)
val isMaybeValue = Set(AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, SerializableClass)
def isUsingDefaultEquals = {
val m = receiver.info.member(nme.equals_)
(m == Object_equals) || (m == Any_equals)
}
def isUsingDefaultScalaOp = {
val s = fn.symbol
(s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=)
}
def isWarnable = isReferenceOp || (isUsingDefaultEquals && isUsingDefaultScalaOp)
def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info)
def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass
def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass
def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass)
def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size)
val nullCount = onSyms(_ filter (_ == NullClass) size)
def nonSensibleWarning(what: String, alwaysEqual: Boolean) = {
val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield " + msg)
}
def nonSensible(pre: String, alwaysEqual: Boolean) =
nonSensibleWarning(pre+"values of types "+typesString, alwaysEqual)
def unrelatedTypes() =
unit.warning(pos, typesString + " are unrelated: should not compare equal")
if (nullCount == 2)
nonSensible("", true)
else if (nullCount == 1) {
if (onSyms(_ exists isValueClass))
nonSensible("", false)
else if (onTrees( _ exists isNew))
nonSensibleWarning("a fresh object", false)
}
else if (isBoolean(receiver)) {
if (!isBoolean(actual) && !isMaybeValue(actual))
nonSensible("", false)
}
else if (isUnit(receiver)) {
if (isUnit(actual))
nonSensible("", true)
else if (!isUnit(actual) && !isMaybeValue(actual))
nonSensible("", false)
}
else if (isNumeric(receiver)) {
if (!isNumeric(actual) && !forMSIL)
if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual))
nonSensible("", false)
}
else if (isWarnable) {
if (isNew(qual))
nonSensibleWarning("a fresh object", false)
else if (isNew(args.head) && (receiver.isFinal || isReferenceOp))
nonSensibleWarning("a fresh object", false)
else if (receiver.isFinal && !(receiver isSubClass actual)) {
if (isEitherNullable)
nonSensible("non-null ", false)
else
nonSensible("", false)
}
}
if (false) {
if (nullCount == 0 && possibleNumericCount < 2 && !(receiver isSubClass actual) && !(actual isSubClass receiver))
unrelatedTypes()
}
case _ =>
}
def toConstructor(pos: Position, tpe: Type): Tree = {
var rtpe = tpe.finalResultType
assert(rtpe.typeSymbol hasFlag CASE, tpe);
localTyper.typedOperator {
atPos(pos) {
Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor)
}
}
}
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
pushLevel()
try {
enterSyms(stats)
var index = -1
stats flatMap { stat => index += 1; transformStat(stat, index) }
}
finally popLevel()
}
private def eliminateModuleDefs(tree: Tree): List[Tree] = {
val ModuleDef(mods, name, impl) = tree
val sym = tree.symbol
val classSym = sym.moduleClass
val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType
def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) {
lazy val createModuleVar = gen.mkModuleVarDef(sym)
sym.owner.info.decl(nme.moduleVarName(sym.name.toTermName)) match {
case NoSymbol => createModuleVar
case vsym => ValDef(vsym)
}
}
def createStaticModuleAccessor() = atPhase(phase.next) {
val method = (
sym.owner.newMethod(sym.pos, sym.name.toTermName)
setFlag (sym.flags | STABLE) resetFlag MODULE setInfo NullaryMethodType(sym.moduleClass.tpe)
)
sym.owner.info.decls enter method
localTyper.typedPos(tree.pos)(gen.mkModuleAccessDef(method, sym))
}
def createInnerModuleAccessor(vdef: Tree) = List(
vdef,
localTyper.typedPos(tree.pos) {
val vsym = vdef.symbol
atPhase(phase.next) {
val rhs = gen.newModule(sym, vsym.tpe)
val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs)
DefDef(sym, body.changeOwner(vsym -> sym))
}
}
)
transformTrees(cdef :: {
if (sym.isStatic)
if (sym.allOverriddenSymbols.isEmpty) Nil
else List(createStaticModuleAccessor())
else createInnerModuleAccessor(findOrCreateModuleVar)
})
}
private def makeLazyAccessor(tree: Tree, rhs: Tree): List[Tree] = {
val vsym = tree.symbol
assert(vsym.isTerm, vsym)
val hasUnitType = vsym.tpe.typeSymbol == UnitClass
val lazySym = vsym.lazyAccessor
assert(lazySym != NoSymbol, vsym)
val body = (
if (tree.symbol.owner.isTrait || hasUnitType) rhs
else gen.mkAssignAndReturn(vsym, rhs)
)
val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym)))
log("Made lazy def: " + lazyDef)
if (hasUnitType) List(typed(lazyDef))
else List(
typed(ValDef(vsym)),
atPhase(phase.next)(typed(lazyDef))
)
}
def transformStat(tree: Tree, index: Int): List[Tree] = tree match {
case ModuleDef(_, _, _) => eliminateModuleDefs(tree)
case ValDef(_, _, _, _) =>
val tree1 @ ValDef(_, _, _, rhs) = transform(tree)
if (tree.symbol.isLazy)
makeLazyAccessor(tree, rhs)
else {
val lazySym = tree.symbol.lazyAccessorOrSelf
if (lazySym.isLocal && index <= currentLevel.maxindex) {
if (settings.debug.value)
Console.println(currentLevel.refsym)
unit.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym)
}
List(tree1)
}
case Import(_, _) => Nil
case _ => List(transform(tree))
}
private def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type], pos: Position): Unit =
try typer.infer.checkBounds(pos, pre, owner, tparams, argtps, "")
catch {
case ex: TypeError =>
unit.error(pos, ex.getMessage());
if (settings.explaintypes.value) {
val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds)
(argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ))
(argtps, bounds).zipped map ((targ, bound) => explainTypes(targ, bound.hi))
()
}
}
private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match {
case Apply(_, args) =>
val clazz = pat.tpe.typeSymbol
clazz == seltpe.typeSymbol &&
clazz.isCaseClass &&
(args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable)
case Typed(pat, tpt) =>
seltpe <:< tpt.tpe
case Ident(tpnme.WILDCARD) =>
true
case Bind(_, pat) =>
isIrrefutable(pat, seltpe)
case _ =>
false
}
private def checkDeprecated(sym: Symbol, pos: Position) {
if (sym.isDeprecated && !currentOwner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) {
unit.deprecationWarning(pos, "%s%s is deprecated%s".format(
sym, sym.locationString, sym.deprecationMessage map (": " + _) getOrElse "")
)
}
}
private def checkMigration(sym: Symbol, pos: Position) = {
for (msg <- sym.migrationMessage)
unit.warning(pos, sym.fullLocationString + " has changed semantics:\n" + msg)
}
private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = (
(otherSym != NoSymbol)
&& !otherSym.isTypeParameterOrSkolem
&& !otherSym.isExistentiallyBound
&& (otherSym isLessAccessibleThan memberSym)
&& (otherSym isLessAccessibleThan memberSym.enclClass)
)
private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = {
val extras = other match {
case TypeRef(pre, _, args) =>
args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym))
case _ =>
Nil
}
if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras
else extras
}
private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) {
val comparison = accessFlagsToString(memberSym) match {
case "" => ""
case acc => " is " + acc + " but"
}
val cannot =
if (memberSym.isDeferred) "may be unable to provide a concrete implementation of"
else "may be unable to override"
unit.warning(memberSym.pos,
"%s%s references %s %s.".format(
memberSym.fullLocationString, comparison,
accessFlagsToString(otherSym), otherSym
) + "\nClasses which cannot access %s %s %s.".format(
otherSym.decodedName, cannot, memberSym.decodedName)
)
}
private def checkAccessibilityOfReferencedTypes(tree: Tree) {
val member = tree.symbol
member.paramss.flatten foreach { p =>
val normalized = p.tpe.normalize
if ((normalized ne p.tpe) && lessAccessibleSymsInType(normalized, member).isEmpty) ()
else lessAccessibleSymsInType(p.tpe, member) foreach (sym => warnLessAccessible(sym, member))
}
member.typeParams.map(_.info.bounds.hi.widen) foreach { tp =>
lessAccessibleSymsInType(tp, member) foreach (sym => warnLessAccessible(sym, member))
}
}
private def checkDeprecatedOvers(tree: Tree) {
val symbol = tree.symbol
if (symbol.isDeprecated) {
val concrOvers =
symbol.allOverriddenSymbols.filter(sym =>
!sym.isDeprecated && !sym.isDeferred)
if(!concrOvers.isEmpty)
unit.deprecationWarning(
tree.pos,
symbol.toString + " overrides concrete, non-deprecated symbol(s):" +
concrOvers.map(_.name.decode).mkString(" ", ", ", ""))
}
}
private def isRepeatedParamArg(tree: Tree) = currentApplication match {
case Apply(fn, args) =>
!args.isEmpty && (args.last eq tree) &&
fn.tpe.params.length == args.length && isRepeatedParamType(fn.tpe.params.last.tpe)
case _ =>
false
}
private def checkTypeRef(tp: Type, pos: Position) = tp match {
case TypeRef(pre, sym, args) =>
checkDeprecated(sym, pos)
if(sym.isJavaDefined)
sym.typeParams foreach (_.cookJavaRawInfo())
if (!tp.isHigherKinded)
checkBounds(pre, sym.owner, sym.typeParams, args, pos)
case _ =>
}
private def checkAnnotations(tpes: List[Type], pos: Position) = tpes foreach (tp => checkTypeRef(tp, pos))
private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
private def applyRefchecksToAnnotations(tree: Tree): Unit = {
def applyChecks(annots: List[AnnotationInfo]) = {
checkAnnotations(annots map (_.atp), tree.pos)
transformTrees(annots flatMap (_.args))
}
tree match {
case m: MemberDef =>
val sym = m.symbol
applyChecks(sym.annotations)
analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
unit.warning(tree.pos, "Invalid implicitNotFound message for %s%s:\n%s".format(sym, sym.locationString, warn))
}
case tpt@TypeTree() =>
if(tpt.original != null) {
tpt.original foreach {
case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check())
case _ =>
}
}
doTypeTraversal(tree) {
case AnnotatedType(annots, _, _) => applyChecks(annots)
case _ =>
}
case _ =>
}
}
private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
val sym = tree.symbol
if (sym.isSourceMethod && sym.isCase && sym.name == nme.apply)
toConstructor(tree.pos, tree.tpe)
else {
ifNot
tree
}
}
private def transformApply(tree: Apply): Tree = tree match {
case Apply(
Select(qual, nme.filter),
List(Function(
List(ValDef(_, pname, tpt, _)),
Match(_, CaseDef(pat1, _, _) :: _))))
if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) &&
isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) =>
transform(qual)
case Apply(Select(New(tpt), name), args)
if (tpt.tpe.typeSymbol == ArrayClass && args.length >= 2) =>
unit.deprecationWarning(tree.pos,
"new Array(...) with multiple dimensions has been deprecated; use Array.ofDim(...) instead")
val manif = {
var etpe = tpt.tpe
for (_ <- args) { etpe = etpe.typeArgs.headOption.getOrElse(NoType) }
if (etpe == NoType) {
unit.error(tree.pos, "too many dimensions for array creation")
Literal(Constant(null))
} else {
localTyper.getManifestTree(tree.pos, etpe, false)
}
}
val newResult = localTyper.typedPos(tree.pos) {
new ApplyToImplicitArgs(Apply(Select(gen.mkAttributedRef(ArrayModule), nme.ofDim), args), List(manif))
}
currentApplication = tree
newResult
case Apply(fn, args) =>
checkSensible(tree.pos, fn, args)
currentApplication = tree
tree
}
private def transformSelect(tree: Select): Tree = {
val Select(qual, name) = tree
val sym = tree.symbol
checkDeprecated(sym, tree.pos)
if (settings.Xmigration28.value)
checkMigration(sym, tree.pos)
if (currentClass != sym.owner && sym.hasLocalFlag) {
var o = currentClass
var hidden = false
while (!hidden && o != sym.owner && o != sym.owner.moduleClass && !o.isPackage) {
hidden = o.isTerm || o.isPrivateLocal
o = o.owner
}
if (!hidden) escapedPrivateLocals += sym
}
def checkSuper(mix: Name) =
assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY))
transformCaseApply(tree,
qual match {
case Super(_, mix) => checkSuper(mix)
case _ =>
}
)
}
private def transformIf(tree: If): Tree = {
val If(cond, thenpart, elsepart) = tree
def unitIfEmpty(t: Tree): Tree =
if (t == EmptyTree) Literal(()).setPos(tree.pos).setType(UnitClass.tpe) else t
cond.tpe match {
case ConstantType(value) =>
val res = if (value.booleanValue) thenpart else elsepart
unitIfEmpty(res)
case _ => tree
}
}
private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match {
case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass =>
if (sym.isGetter || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType))) ()
else unit.warning(sym.pos,
"side-effecting nullary methods are discouraged: suggest defining as `def %s()` instead".format(
sym.name.decode)
)
case _ => ()
}
override def transform(tree: Tree): Tree = {
val savedLocalTyper = localTyper
val savedCurrentApplication = currentApplication
try {
val sym = tree.symbol
applyRefchecksToAnnotations(tree)
var result: Tree = tree match {
case DefDef(mods, name, tparams, vparams, tpt, EmptyTree) if tree.symbol.hasAnnotation(NativeAttr) =>
tree.symbol.resetFlag(DEFERRED)
transform(treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt,
typed(Apply(gen.mkAttributedRef(Predef_error), List(Literal("native method stub"))))))
case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) =>
checkDeprecatedOvers(tree)
if (settings.warnNullaryUnit.value)
checkNullaryMethodReturnType(sym)
if (settings.warnInaccessible.value) {
if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
checkAccessibilityOfReferencedTypes(tree)
}
tree
case Template(parents, self, body) =>
localTyper = localTyper.atOwner(tree, currentOwner)
validateBaseTypes(currentOwner)
checkOverloadedRestrictions(currentOwner)
val bridges = addVarargBridges(currentOwner)
checkAllOverrides(currentOwner)
if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges)
else tree
case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc
case tpt@TypeTree() =>
if(tpt.original != null) {
tpt.original foreach {
case dc@TypeTreeWithDeferredRefCheck() =>
transform(dc.check())
case _ =>
}
}
val existentialParams = new ListBuffer[Symbol]
doTypeTraversal(tree) {
case ExistentialType(tparams, tpe) =>
existentialParams ++= tparams
case t: TypeRef =>
val exparams = existentialParams.toList
val wildcards = exparams map (_ => WildcardType)
checkTypeRef(t.subst(exparams, wildcards), tree.pos)
case _ =>
}
tree
case TypeApply(fn, args) =>
checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe), tree.pos)
transformCaseApply(tree, ())
case x @ Apply(_, _) =>
transformApply(x)
case x @ If(_, _, _) =>
transformIf(x)
case New(tpt) =>
enterReference(tree.pos, tpt.tpe.typeSymbol)
tree
case Typed(_, Ident(tpnme.WILDCARD_STAR)) if !isRepeatedParamArg(tree) =>
unit.error(tree.pos, "no `: _*' annotation allowed here\n"+
"(such annotations are only allowed in arguments to *-parameters)")
tree
case Ident(name) =>
transformCaseApply(tree,
if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) {
assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass)
enterReference(tree.pos, sym)
}
)
case x @ Select(_, _) =>
transformSelect(x)
case UnApply(fun, args) =>
transform(fun)
tree
case _ => tree
}
result = result match {
case CaseDef(pat, guard, body) =>
inPattern = true
val pat1 = transform(pat)
inPattern = false
treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
case _ =>
super.transform(result)
}
result match {
case ClassDef(_, _, _, _)
| TypeDef(_, _, _, _) =>
if (result.symbol.isLocal || result.symbol.owner.isPackageClass)
varianceValidator.traverse(result)
case _ =>
}
result
} catch {
case ex: TypeError =>
if (settings.debug.value) ex.printStackTrace();
unit.error(tree.pos, ex.getMessage())
tree
} finally {
localTyper = savedLocalTyper
currentApplication = savedCurrentApplication
}
}
}
}