/* NSC -- new Scala compiler * Copyright 2005-2009 LAMP/EPFL * @author Martin Odersky */ // $Id: RefChecks.scala 18589 2009-08-27 14:45:35Z odersky $ package scala.tools.nsc package typechecker import symtab.Flags._ import collection.mutable.{HashSet, HashMap} import transform.InfoTransform import scala.tools.nsc.util.{Position, NoPosition} import scala.collection.mutable.ListBuffer /** <p> * Post-attribution checking and transformation. * </p> * <p> * This phase performs the following checks. * </p> * <ul> * <li>All overrides conform to rules.</li> * <li>All type arguments conform to bounds.</li> * <li>All type variable uses conform to variance annotations.</li> * <li>No forward reference to a term symbol extends beyond a value definition.</li> * </ul> * <p> * It performs the following transformations. * </p> * <ul> * <li>Local modules are replaced by variables and classes</li> * <li>Calls to case factory methods are replaced by new's.</li> * <li>Eliminate branches in a conditional if the condition is a constant</li> * </ul> * * @author Martin Odersky * @version 1.0 * * @todo Check whether we always check type parameter bounds. */ abstract class RefChecks extends InfoTransform { import global._ import definitions._ import typer.{typed, typedOperator, atOwner} /** the following two members override abstract members in Transform */ 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) PolyType(List(), tp) } else tp class RefCheckTransformer(unit: CompilationUnit) extends Transformer { var localTyper: analyzer.Typer = typer; var currentApplication: Tree = EmptyTree var inPattern: Boolean = false // Override checking ------------------------------------------------------------ /** 1. Check all members of class `clazz' for overriding conditions. * That is for overriding member M and overridden member O: * * 1.1. M must have the same or stronger access privileges as O. * 1.2. O must not be final. * 1.3. O is deferred, or M has `override' modifier. * 1.4. If O is stable, then so is M. * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias * 1.6. If O is a type alias, then M is an alias of O. * 1.7. If O is an abstract type then * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds. * or M is a type alias or class which conforms to O's bounds. * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds * 1.8. If O and M are values, then * 1.8.1 M's type is a subtype of O's type, or * 1.8.2 M is of type []S, O is of type ()T and S <: T, or * 1.8.3 M is of type ()S, O is of type []T and S <: T, or * 2. Check that only abstract classes have deferred members * 3. Check that concrete classes do not have deferred definitions * that are not implemented in a subclass. * 4. Check that every member with an `override' modifier * overrides some other member. */ private def checkAllOverrides(clazz: Symbol) { 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 != _).removeDuplicates 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) = // (part of DEVIRTUALIZE) 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), PolyType(List(), rtp2)) => rtp1 <:< rtp2 case (PolyType(List(), rtp1), MethodType(List(), rtp2)) => rtp1 <:< rtp2 case (TypeRef(_, sym, _), _) if (sym.isModuleClass) => overridesType(PolyType(List(), tp1), tp2) case _ => tp1 <:< tp2 } /** Check that all conditions for overriding <code>other</code> by * <code>member</code> are met. */ def checkOverride(clazz: Symbol, member: Symbol, other: Symbol) { def overrideError(msg: String) { if (other.tpe != ErrorType && member.tpe != ErrorType) { 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 (other.tpe != ErrorType && member.tpe != ErrorType) { overrideError("has incompatible type") } } def overrideAccessError() { val pwString = if (other.privateWithin == NoSymbol) "" else other.privateWithin.name.toString val otherAccess = flagsToString(other getFlag (PRIVATE | PROTECTED), pwString) overrideError("has weaker access privileges; it should be "+ (if (otherAccess == "") "public" else "at least "+otherAccess)) } //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG // return if we already checked this combination elsewhere if (member.owner != clazz) { if ((member.owner isSubClass other.owner) && (member.isDeferred || !other.isDeferred)) { //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG return; } if (clazz.info.parents exists (parent => (parent.typeSymbol isSubClass other.owner) && (parent.typeSymbol isSubClass member.owner) && (member.isDeferred || !other.isDeferred))) { //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG return; } if (clazz.info.parents forall (parent => (parent.typeSymbol isSubClass other.owner) == (parent.typeSymbol isSubClass member.owner))) { //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG return; } } if (member hasFlag PRIVATE) { // (1.1) overrideError("has weaker access privileges; it should not be private") } val mb = member.accessBoundary(member.owner) val ob = other.accessBoundary(member.owner) if (mb != RootClass && mb != NoSymbol && // todo: change (ob == RootClass || ob == NoSymbol || !ob.hasTransOwner(mb) || (other hasFlag PROTECTED) && !(member hasFlag PROTECTED))) { 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) { // (1.2) overrideError("cannot override final member"); } else if (!other.isDeferred && !(member hasFlag (OVERRIDE | ABSOVERRIDE | SYNTHETIC))) { // (1.3), SYNTHETIC because of DEVIRTUALIZE 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.hasFlag(LAZY)) { overrideError("cannot override a mutable variable") } else if (other.isStable && !member.isStable) { // (1.4) 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 { if (other.isAliasType) { //if (!member.typeParams.isEmpty) // (1.5) @MAT // overrideError("may not be parameterized"); //if (!other.typeParams.isEmpty) // (1.5) @MAT // overrideError("may not override parameterized type"); // @M: substSym if (!(self.memberType(member).substSym(member.typeParams, other.typeParams) =:= self.memberType(other))) // (1.6) overrideTypeError(); } else if (other.isAbstractType) { //if (!member.typeParams.isEmpty) // (1.7) @MAT // overrideError("may not be parameterized"); var memberTp = self.memberType(member) val otherTp = self.memberInfo(other) if (!(otherTp.bounds containsType memberTp)) { // (1.7.1) overrideTypeError(); // todo: do an explaintypes with bounds here explainTypes(_.bounds containsType _, otherTp, memberTp) } // check overriding (abstract type --> abstract type or abstract type --> concrete type member (a type alias)) // making an abstract type member concrete is like passing a type argument val kindErrors = typer.infer.checkKindBounds(List(other), List(memberTp), self, member.owner) // (1.7.2) 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", ", ", "")) // check a type alias's RHS corresponds to its declaration // this overlaps somewhat with validateVariance 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 (other.isTerm) { if (!overridesType(self.memberInfo(member), self.memberInfo(other))) { // 8 overrideTypeError() explainTypes(self.memberInfo(member), self.memberInfo(other)) } } } } val opc = new overridingPairs.Cursor(clazz) while (opc.hasNext) { //Console.println(opc.overriding/* + ":" + opc.overriding.tpe*/ + " in "+opc.overriding.fullNameString + " overrides " + opc.overridden/* + ":" + opc.overridden.tpe*/ + " in "+opc.overridden.fullNameString + "/"+ opc.overridden.hasFlag(DEFERRED));//debug if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden); opc.next } printMixinOverrideErrors() // 2. Check that only abstract classes have deferred members if (clazz.isClass && !clazz.isTrait) { def abstractClassError(mustBeMixin: Boolean, msg: String) { unit.error(clazz.pos, (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible" else if (mustBeMixin) clazz.toString() + " needs to be a mixin" else clazz.toString() + " needs to be abstract") + ", since " + msg); clazz.setFlag(ABSTRACT) } // Find a concrete Java method that overrides `sym' under the erasure model. // Bridge symbols qualify. // Used as a fall back if no overriding symbol of a Java abstract method can be found def javaErasedOverridingSym(sym: Symbol): Symbol = clazz.tpe.findMember(sym.name, PRIVATE, 0, false)(NoSymbol).filter(other => !other.isDeferred && (other hasFlag JAVA) && { val tp1 = erasure.erasure(clazz.thisType.memberType(sym)) val tp2 = erasure.erasure(clazz.thisType.memberType(other)) atPhase(currentRun.erasurePhase.next)(tp1 matches tp2) }) for (member <- clazz.tpe.nonPrivateMembers) if (member.isDeferred && !(clazz hasFlag ABSTRACT) && !isAbstractTypeWithoutFBound(member) && !((member hasFlag JAVA) && javaErasedOverridingSym(member) != NoSymbol)) { abstractClassError( false, infoString(member) + " is not defined" + analyzer.varNotice(member)) } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) { val other = member.superSymbol(clazz); abstractClassError(true, infoString(member) + " is marked `abstract' and `override'" + (if (other != NoSymbol) " and overrides incomplete superclass member " + infoString(other) else "")) } // 3. Check that concrete classes do not have deferred definitions // that are not implemented in a subclass. // Note that this is not the same as (2); In a situation like // // class C { def m: Int = 0} // class D extends C { def m: Int } // // (3) is violated but not (2). def checkNoAbstractDecls(bc: Symbol) { for (decl <- bc.info.decls.iterator) { if (decl.isDeferred && !isAbstractTypeWithoutFBound(decl)) { val impl = decl.matchingSymbol(clazz.thisType) 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) } if (!(clazz hasFlag ABSTRACT)) checkNoAbstractDecls(clazz) } // 4. Check that every defined member with an `override' modifier overrides some other member. for (member <- clazz.info.decls.toList) if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) && (clazz.info.baseClasses.tail forall { bc => member.matchingSymbol(bc, clazz.thisType) == NoSymbol })) { // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG unit.error(member.pos, member.toString() + " overrides nothing"); member resetFlag OVERRIDE } } // Basetype Checking -------------------------------------------------------- /** <ol> * <li> <!-- 1 --> * Check that later type instances in the base-type sequence * are subtypes of earlier type instances of the same mixin. * </li> * </ol> */ private def validateBaseTypes(clazz: Symbol) { val seenTypes = new Array[List[Type]](clazz.info.baseTypeSeq.length) for (i <- 0 until seenTypes.length) seenTypes(i) = Nil /** validate all base types of a class in reverse linear order. */ def register(tp: Type) { // if (clazz.fullNameString.endsWith("Collection.Projection")) // println("validate base type "+tp) 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))) } } tp.parents 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(_) => ;// OK 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) } } } // Variance Checking -------------------------------------------------------- private val ContraVariance = -1 private val NoVariance = 0 private val CoVariance = 1 private val AnyVariance = 2 private val escapedPrivateLocals = new HashSet[Symbol] val varianceValidator = new Traverser { /** Validate variance of info of symbol `base` */ private def validateVariance(base: Symbol) { def varianceString(variance: Int): String = if (variance == 1) "covariant" else if (variance == -1) "contravariant" else "invariant"; /** The variance of a symbol occurrence of `tvar` * seen at the level of the definition of `base`. * The search proceeds from `base` to the owner of `tvar`. * Initially the state is covariant, but it might change along the search. */ def relativeVariance(tvar: Symbol): Int = { val clazz = tvar.owner var sym = base var state = CoVariance while (sym != clazz && state != AnyVariance) { //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG // Flip occurrences of type parameters and parameters, unless // - it's a constructor, or case class factory or extractor // - it's a type parameter of tvar's owner. if ((sym hasFlag PARAM) && !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) && !(escapedPrivateLocals contains sym))) { // return AnyVariance if `sym` is local to a term // or is private[this] or protected[this] state = AnyVariance } else if (sym.isAliasType) { // return AnyVariance if `sym` is an alias type // that does not override anything. This is OK, because we always // expand aliases for variance checking. // However, if `sym` does override a type in a base class // we have to assume NoVariance, as there might then be // references to the type parameter that are not variance checked. state = if (sym.allOverriddenSymbols.isEmpty) AnyVariance else NoVariance } sym = sym.owner } state } /** Validate that the type `tp` is variance-correct, assuming * the type occurs itself at variance position given by `variance` */ def validateVariance(tp: Type, variance: Int): Unit = tp match { case ErrorType => ; case WildcardType => ; case NoType => ; case NoPrefix => ; case ThisType(_) => ; case ConstantType(_) => ; case DeBruijnIndex(_, _) => ; case SingleType(pre, sym) => validateVariance(pre, variance) case TypeRef(pre, sym, args) => if (sym.isAliasType && relativeVariance(sym) == AnyVariance) validateVariance(tp.normalize, variance) else if (sym.variance != NoVariance) { val v = relativeVariance(sym) if (v != AnyVariance && sym.variance != v * variance) { //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG 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) //@M for higher-kinded typeref, args.isEmpty // However, these args respect variances by construction anyway // -- the interesting case is in type application, see checkKindBounds in Infer 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 PolyType(tparams, result) => // type parameters will be validated separately, because they are defined explicitly. 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) // ModuleDefs need not be considered because they have been eliminated already case ValDef(_, _, _, _) => validateVariance(tree.symbol) case DefDef(_, _, tparams, vparamss, tpt, rhs) => validateVariance(tree.symbol) traverseTrees(tparams); traverseTreess(vparamss) case Template(_, _, _) => super.traverse(tree) case _ => } } } // Forward reference checking --------------------------------------------------- class LevelInfo(val outer: LevelInfo) { val scope: Scope = if (outer eq null) newScope else newScope(outer.scope) var maxindex: Int = Math.MIN_INT var refpos: Position = _ var refsym: Symbol = _ } private var currentLevel: LevelInfo = null private val symIndex = new 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(_, _, _, _) => assert(stat.symbol != NoSymbol, stat);//debug if (stat.symbol.isLocal) { currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope)); symIndex(stat.symbol) = 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 } } } } // Comparison checking ------------------------------------------------------- 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) if (args.length == 1) => def isNew(tree: Tree) = tree match { case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true case _ => false } name match { case nme.EQ | nme.NE | nme.LT | nme.GT | nme.LE | nme.GE => def underlyingClass(tp: Type): Symbol = { var sym = tp.widen.typeSymbol while (sym.isAbstractType) sym = sym.info.bounds.hi.widen.typeSymbol sym } val formal = underlyingClass(fn.tpe.paramTypes.head) val actual = underlyingClass(args.head.tpe) val receiver = underlyingClass(qual.tpe) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield "+ (alwaysEqual == (name == nme.EQ || name == nme.LE || name == nme.GE))) def nonSensible(pre: String, alwaysEqual: Boolean) = nonSensibleWarning(pre+"values of types "+normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen), alwaysEqual) // @MAT normalize for consistency in error message, otherwise part is normalized due to use of `typeSymbol', but the rest isn't def hasObjectEquals = receiver.info.member(nme.equals_) == Object_equals // see if it has any == methods beyond Any's and AnyRef's: ticket #2240 def hasOverloadedEqEq = receiver.info.member(nme.EQ).alternatives.length > 2 if (formal == UnitClass && actual == UnitClass) nonSensible("", true) else if ((receiver == BooleanClass || receiver == UnitClass) && !(receiver isSubClass actual)) nonSensible("", false) else if (isNumericValueClass(receiver) && !isNumericValueClass(actual) && !(forMSIL || (actual isSubClass BoxedNumberClass)) && !(receiver isSubClass actual)) nonSensible("", false) else if ((receiver hasFlag FINAL) && hasObjectEquals && !isValueClass(receiver) && !(receiver isSubClass actual) && receiver != NullClass && actual != NullClass && (name == nme.EQ || name == nme.LE)) nonSensible("non-null ", false) else if ((isNew(qual) || isNew(args.head)) && hasObjectEquals && (name == nme.EQ || name == nme.NE) && !hasOverloadedEqEq) nonSensibleWarning("a fresh object", false) case _ => } case _ => } // Transformation ------------------------------------------------------------ /* Convert a reference to a case factory of type `tpe' to a new of the class it produces. */ 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() enterSyms(stats) var index = -1 val stats1 = stats flatMap { stat => index += 1; transformStat(stat, index) } popLevel() stats1 } /** Implements lazy value accessors: * - for lazy values of type Unit and all lazy fields inside traits, * the rhs is the initializer itself * - for all other lazy values z the accessor is a block of this form: * { z = <rhs>; z } where z can be an identifier or a field. */ def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case ModuleDef(mods, name, impl) => val sym = tree.symbol val cdef = ClassDef(mods | MODULE, name, List(), impl) .setPos(tree.pos) .setSymbol(sym.moduleClass) .setType(NoType); if (sym.isStatic) { if (!sym.allOverriddenSymbols.isEmpty) { val factory = sym.owner.newMethod(sym.pos, sym.name) .setFlag(sym.flags | STABLE).resetFlag(MODULE) .setInfo(PolyType(List(), sym.moduleClass.tpe)) sym.owner.info.decls.enter(factory) val ddef = atPhase(phase.next) { localTyper.typed { gen.mkModuleAccessDef(factory, sym.tpe) } } transformTrees(List(cdef, ddef)) } else { List(transform(cdef)) } } else { val vdef = localTyper.typedPos(tree.pos) { gen.mkModuleVarDef(sym) } val ddef = atPhase(phase.next) { localTyper.typed { if (sym.owner.isTrait) gen.mkModuleAccessDcl(sym) else gen.mkCachedModuleAccessDef(sym, vdef.symbol) } } if (sym.owner.isTrait) transformTrees(List(cdef, ddef)) else transformTrees(List(cdef, vdef, ddef)) } case ValDef(_, _, _, _) => val tree1 = transform(tree); // important to do before forward reference check val ValDef(_, _, _, rhs) = tree1 if (tree.symbol.hasFlag(LAZY)) { assert(tree.symbol.isTerm, tree.symbol) val vsym = tree.symbol val hasUnitType = (tree.symbol.tpe.typeSymbol == definitions.UnitClass) val lazyDefSym = vsym.lazyAccessor assert(lazyDefSym != NoSymbol, vsym) val ownerTransformer = new ChangeOwnerTraverser(vsym, lazyDefSym) val lazyDef = atPos(tree.pos)( DefDef(lazyDefSym, ownerTransformer( if (tree.symbol.owner.isTrait // for traits, this is further tranformed in mixins || hasUnitType) rhs else Block(List( Assign(gen.mkAttributedRef(vsym), rhs)), gen.mkAttributedRef(vsym))))) log("Made lazy def: " + lazyDef) if (hasUnitType) typed(lazyDef) :: Nil else typed(ValDef(vsym, EmptyTree)) :: typed(lazyDef) :: Nil } else { if (tree.symbol.isLocal && index <= currentLevel.maxindex && !tree.symbol.hasFlag(LAZY)) { if (settings.debug.value) Console.println(currentLevel.refsym); unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol); } List(tree1) } case Import(_, _) => List() case _ => List(transform(tree)) } override def transform(tree: Tree): Tree = try { /* Check whether argument types conform to bounds of type parameters */ def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type]): Unit = checkBoundsWithPos(pre, owner, tparams, argtps, tree.pos) def checkBoundsWithPos(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) List.map2(argtps, bounds)((targ, bound) => explainTypes(bound.lo, targ)) List.map2(argtps, bounds)((targ, bound) => explainTypes(targ, bound.hi)) () } } def isIrrefutable(pat: Tree, seltpe: Type): Boolean = { val result = pat match { case Apply(_, args) => val clazz = pat.tpe.typeSymbol; clazz == seltpe.typeSymbol && clazz.isClass && (clazz hasFlag CASE) && List.forall2( args, clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable) case Typed(pat, tpt) => seltpe <:< tpt.tpe case Ident(nme.WILDCARD) => true case Bind(_, pat) => isIrrefutable(pat, seltpe) case _ => false } //Console.println("is irefutable? " + pat + ":" + pat.tpe + " against " + seltpe + ": " + result);//DEBUG result } /** If symbol is deprecated and is not contained in a deprecated definition, * issue a deprecated warning */ def checkDeprecated(sym: Symbol, pos: Position) { if (sym.isDeprecated && !currentOwner.ownerChain.exists(_.isDeprecated)) { val dmsg = sym.deprecationMessage val msg = sym.toString + sym.locationString +" is deprecated"+ (if (dmsg.isDefined) ": "+ dmsg.get else "") unit.deprecationWarning(pos, msg) } } /** Check that a deprecated val or def does not override a * concrete, non-deprecated method. If it does, then * deprecation is meaningless. */ def checkDeprecatedOvers() { 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(_.fullNameString).mkString(" ", ", ", "")) } } def isRepeatedParamArg(tree: Tree) = currentApplication match { case Apply(fn, args) => !args.isEmpty && (args.last eq tree) && fn.tpe.paramTypes.length == args.length && fn.tpe.paramTypes.last.typeSymbol == RepeatedParamClass case _ => false } def isCaseApply(sym : Symbol) = sym.isSourceMethod && sym.hasFlag(CASE) && sym.name == nme.apply def checkTypeRef(tp: Type, pos: Position) = tp match { case TypeRef(pre, sym, args) => checkDeprecated(sym, pos) if (!tp.isHigherKinded) checkBoundsWithPos(pre, sym.owner, sym.typeParams, args, pos) case _ => } def checkAnnotations(tpes: List[(Type, Position)]) { for ((tp, pos) <- tpes) checkTypeRef(tp, pos) } val savedLocalTyper = localTyper val savedCurrentApplication = currentApplication val sym = tree.symbol var result = tree def doTypeTraversal(f: (Type) => Unit) = if (!inPattern) { for (tp <- tree.tpe) f(tp) } // Apply RefChecks to annotations. Makes sure the annotations conform to // type bounds (bug #935), issues deprecation warnings for symbols used // inside annotations. tree match { case m: MemberDef => checkAnnotations(m.symbol.annotations.map(a => (a.atp, tree.pos))) transformTrees(m.symbol.annotations.flatMap(_.args)) case TypeTree() => doTypeTraversal { case AnnotatedType(annots, _, _) => checkAnnotations(annots.map(a => (a.atp, tree.pos))) transformTrees(annots.flatMap(_.args)) case _ => } case _ => } tree match { case DefDef(mods, name, tparams, vparams, tpt, EmptyTree) if tree.symbol.hasAnnotation(definitions.NativeAttr) => tree.symbol.resetFlag(DEFERRED) result = transform(treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt, typed(Apply(gen.mkAttributedRef(definitions.Predef_error), List(Literal("native method stub")))))) case DefDef(_, _, _, _, _, _) => checkDeprecatedOvers() case ValDef(_, _, _, _) => checkDeprecatedOvers() case Template(_, _, _) => localTyper = localTyper.atOwner(tree, currentOwner) validateBaseTypes(currentOwner) checkAllOverrides(currentOwner) case TypeTree() => val existentialParams = new ListBuffer[Symbol] doTypeTraversal { // check all bounds, except those that are // existential type parameters 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 _ => } case TypeApply(fn, args) => checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) if (isCaseApply(sym)) result = toConstructor(tree.pos, tree.tpe) 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)) => result = 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) } } result = localTyper.typedPos(tree.pos) { Apply(Apply(Select(gen.mkAttributedRef(ArrayModule), nme.ofDim), args), List(manif)) } currentApplication = tree case Apply(fn, args) => checkSensible(tree.pos, fn, args) currentApplication = tree case If(cond, thenpart, elsepart) => cond.tpe match { case ConstantType(value) => result = if (value.booleanValue) thenpart else elsepart; if (result == EmptyTree) result = Literal(()).setPos(tree.pos).setType(UnitClass.tpe) case _ => } case New(tpt) => enterReference(tree.pos, tpt.tpe.typeSymbol) case Typed(expr, tpt @ Ident(name)) if (name == nme.WILDCARD_STAR.toTypeName) => if (!isRepeatedParamArg(tree)) unit.error(tree.pos, "no `: _*' annotation allowed here\n"+ "(such annotations are only allowed in arguments to *-parameters)") case Ident(name) => if (isCaseApply(sym)) result = toConstructor(tree.pos, tree.tpe) else if (name != nme.WILDCARD && name != nme.WILDCARD_STAR.toTypeName) { assert(sym != NoSymbol, tree)//debug enterReference(tree.pos, sym) } case Select(qual, name) => checkDeprecated(sym, tree.pos) if (currentClass != sym.owner && (sym hasFlag LOCAL)) { 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 } if (isCaseApply(sym)) result = toConstructor(tree.pos, tree.tpe) else qual match { case Super(qualifier, mix) => val base = qual.symbol; //Console.println("super: " + tree + " in " + base);//DEBUG assert(!(base.isTrait && sym.isTerm && mix == nme.EMPTY.toTypeName)) // term should have been eliminated by super accessors case _ => } case _ => } 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 _ => } localTyper = savedLocalTyper currentApplication = savedCurrentApplication result } catch { case ex: TypeError => if (settings.debug.value) ex.printStackTrace(); unit.error(tree.pos, ex.getMessage()) tree } } }