diff options
30 files changed, 652 insertions, 471 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 92bfe6e0fe..1cd3e0ec4b 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -135,6 +135,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val global: Global.this.type = Global.this } with OverridingPairs + type SymbolPair = overridingPairs.SymbolPair + // Optimizer components /** ICode analysis for optimization */ diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 2d005b26e3..e2902a74b8 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -371,17 +371,14 @@ abstract class Erasure extends AddInterfaces val opc = enteringExplicitOuter { new overridingPairs.Cursor(root) { override def parents = List(root.info.firstParent) - override def exclude(sym: Symbol) = !sym.isMethod || sym.isPrivate || super.exclude(sym) + override def exclude(sym: Symbol) = !sym.isMethod || super.exclude(sym) } } def compute(): (List[Tree], immutable.Set[Symbol]) = { while (opc.hasNext) { - val member = opc.overriding - val other = opc.overridden - //println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG - if (enteringExplicitOuter(!member.isDeferred)) - checkPair(member, other) + if (enteringExplicitOuter(!opc.low.isDeferred)) + checkPair(opc.currentPair) opc.next() } @@ -438,8 +435,15 @@ abstract class Erasure extends AddInterfaces noclash } - def checkPair(member: Symbol, other: Symbol) { - val otpe = specialErasure(root)(other.tpe) + /** TODO - work through this logic with a fine-toothed comb, incorporating + * into SymbolPairs where appropriate. + */ + def checkPair(pair: SymbolPair) { + import pair._ + val member = low + val other = high + val otpe = highErased + val bridgeNeeded = exitingErasure ( !member.isMacro && !(other.tpe =:= member.tpe) && @@ -844,88 +848,75 @@ abstract class Erasure extends AddInterfaces /** The erasure transformer */ class ErasureTransformer(unit: CompilationUnit) extends Transformer { - /** Emit an error if there is a double definition. This can happen if: - * - * - A template defines two members with the same name and erased type. - * - A template defines and inherits two members `m` with different types, - * but their erased types are the same. - * - A template inherits two members `m` with different types, - * but their erased types are the same. - */ - private def checkNoDoubleDefs(root: Symbol) { - def doubleDefError(sym1: Symbol, sym2: Symbol) { - // the .toString must also be computed at the earlier phase - val tpe1 = exitingRefchecks(root.thisType.memberType(sym1)) - val tpe2 = exitingRefchecks(root.thisType.memberType(sym2)) - if (!tpe1.isErroneous && !tpe2.isErroneous) - unit.error( - if (sym1.owner == root) sym1.pos else root.pos, - (if (sym1.owner == sym2.owner) "double definition:\n" - else if (sym1.owner == root) "name clash between defined and inherited member:\n" - else "name clash between inherited members:\n") + - sym1 + ":" + exitingRefchecks(tpe1.toString) + - (if (sym1.owner == root) "" else sym1.locationString) + " and\n" + - sym2 + ":" + exitingRefchecks(tpe2.toString) + - (if (sym2.owner == root) " at line " + (sym2.pos).line else sym2.locationString) + - "\nhave same type" + - (if (exitingRefchecks(tpe1 =:= tpe2)) "" else " after erasure: " + exitingPostErasure(sym1.tpe))) - sym1.setInfo(ErrorType) + import overridingPairs.Cursor + + private def doubleDefError(pair: SymbolPair) { + import pair._ + + if (!pair.isErroneous) { + val what = ( + if (low.owner == high.owner) "double definition" + else if (low.owner == base) "name clash between defined and inherited member" + else "name clash between inherited members" + ) + val when = if (exitingRefchecks(lowType matches highType)) "" else " after erasure: " + exitingPostErasure(highType) + + unit.error(pos, + s"""|$what: + |${exitingRefchecks(highString)} and + |${exitingRefchecks(lowString)} + |have same type$when""".trim.stripMargin + ) } + low setInfo ErrorType + } - val decls = root.info.decls + /** TODO - adapt SymbolPairs so it can be used here. */ + private def checkNoDeclaredDoubleDefs(base: Symbol) { + val decls = base.info.decls var e = decls.elems while (e ne null) { if (e.sym.isTerm) { - var e1 = decls.lookupNextEntry(e) + var e1 = decls lookupNextEntry e while (e1 ne null) { - if (exitingPostErasure(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym) - e1 = decls.lookupNextEntry(e1) + if (exitingPostErasure(e1.sym.info =:= e.sym.info)) + doubleDefError(new SymbolPair(base, e.sym, e1.sym)) + + e1 = decls lookupNextEntry e1 } } e = e.next } + } - val opc = new overridingPairs.Cursor(root) { + /** Emit an error if there is a double definition. This can happen if: + * + * - A template defines two members with the same name and erased type. + * - A template defines and inherits two members `m` with different types, + * but their erased types are the same. + * - A template inherits two members `m` with different types, + * but their erased types are the same. + */ + private def checkNoDoubleDefs(root: Symbol) { + checkNoDeclaredDoubleDefs(root) + object opc extends Cursor(root) { + // specialized members have no type history before 'specialize', causing double def errors for curried defs override def exclude(sym: Symbol): Boolean = ( - !sym.isTerm || sym.isPrivate || super.exclude(sym) - // specialized members have no type history before 'specialize', causing double def errors for curried defs + sym.isType + || sym.isPrivate + || super.exclude(sym) || !sym.hasTypeAt(currentRun.refchecksPhase.id) ) - - override def matches(sym1: Symbol, sym2: Symbol): Boolean = - exitingPostErasure(sym1.tpe =:= sym2.tpe) + override def matches(sym1: Symbol, sym2: Symbol) = true } - while (opc.hasNext) { - if (!exitingRefchecks( - root.thisType.memberType(opc.overriding) matches - root.thisType.memberType(opc.overridden))) { - debuglog("" + opc.overriding.locationString + " " + - opc.overriding.infosString + - opc.overridden.locationString + " " + - opc.overridden.infosString) - doubleDefError(opc.overriding, opc.overridden) - } - opc.next() + def sameTypeAfterErasure(pair: SymbolPair) = { + import pair._ + log(s"Considering for erasure clash:\n$pair") + !exitingRefchecks(lowType matches highType) && exitingPostErasure(low.tpe =:= high.tpe) } + opc.iterator filter sameTypeAfterErasure foreach doubleDefError } -/* - for (bc <- root.info.baseClasses.tail; other <- bc.info.decls.toList) { - if (other.isTerm && !other.isConstructor && !(other hasFlag (PRIVATE | BRIDGE))) { - for (member <- root.info.nonPrivateMember(other.name).alternatives) { - if (member != other && - !(member hasFlag BRIDGE) && - exitingErasure(member.tpe =:= other.tpe) && - !exitingRefchecks( - root.thisType.memberType(member) matches root.thisType.memberType(other))) { - debuglog("" + member.locationString + " " + member.infosString + other.locationString + " " + other.infosString); - doubleDefError(member, other) - } - } - } - } -*/ - /** Add bridge definitions to a template. This means: * * If there is a concrete member `m` which overrides a member in a base @@ -940,7 +931,6 @@ abstract class Erasure extends AddInterfaces */ private def bridgeDefs(owner: Symbol): (List[Tree], immutable.Set[Symbol]) = { assert(phase == currentRun.erasurePhase, phase) - debuglog("computing bridges for " + owner) new ComputeBridges(unit, owner) compute() } diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 2610679542..4222c4d8c8 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -6,221 +6,33 @@ package scala.tools.nsc package transform -import scala.collection.mutable import symtab.Flags._ -import util.HashSet -import scala.annotation.tailrec +import scala.reflect.internal.SymbolPairs /** A class that yields a kind of iterator (`Cursor`), - * which yields all pairs of overriding/overridden symbols - * that are visible in some baseclass, unless there's a parent class - * that already contains the same pairs. - * @author Martin Odersky - * @version 1.0 + * which yields pairs of corresponding symbols visible in some base class, + * unless there's a parent class that already contains the same pairs. + * Most of the logic is in SymbolPairs, which contains generic + * pair-oriented traversal logic. */ -abstract class OverridingPairs { - - val global: Global +abstract class OverridingPairs extends SymbolPairs { import global._ - /** The cursor class - * @param base the base class that contains the overriding pairs - */ - class Cursor(base: Symbol) { - - private val self = base.thisType - - /** Symbols to exclude: Here these are constructors, private locals, - * and hidden symbols, including bridges. But it may be refined in subclasses. - * - */ - protected def exclude(sym: Symbol): Boolean = - sym.isConstructor || sym.isPrivateLocal || sym.isArtifact - - /** The parents of base (may also be refined). - */ - protected def parents: List[Type] = base.info.parents - - /** Does `sym1` match `sym2` so that it qualifies as overriding. - * Types always match. Term symbols match if their membertypes - * relative to <base>.this do - */ - protected def matches(sym1: Symbol, sym2: Symbol): Boolean = { - def tp_s(s: Symbol) = self.memberType(s) + "/" + self.memberType(s).getClass - val result = sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) - debuglog("overriding-pairs? %s matches %s (%s vs. %s) == %s".format( - sym1.fullLocationString, sym2.fullLocationString, tp_s(sym1), tp_s(sym2), result)) - - result - } + class Cursor(base: Symbol) extends super.Cursor(base) { + lazy val relatively = new RelativeTo(base.thisType) - /** An implementation of BitSets as arrays (maybe consider collection.BitSet - * for that?) The main purpose of this is to implement - * intersectionContainsElement efficiently. + /** Symbols to exclude: Here these are constructors and private/artifact symbols, + * including bridges. But it may be refined in subclasses. */ - private type BitSet = Array[Int] - - private def include(bs: BitSet, n: Int) { - val nshifted = n >> 5 - val nmask = 1 << (n & 31) - bs(nshifted) = bs(nshifted) | nmask - } - - /** Implements `bs1 * bs2 * {0..n} != 0. - * Used in hasCommonParentAsSubclass */ - private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, n: Int): Boolean = { - val nshifted = n >> 5 - val nmask = 1 << (n & 31) - var i = 0 - while (i < nshifted) { - if ((bs1(i) & bs2(i)) != 0) return true - i += 1 - } - (bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0 - } - - /** The symbols that can take part in an overriding pair */ - private val decls = newScope + override protected def exclude(sym: Symbol) = (sym hasFlag PRIVATE | ARTIFACT) || sym.isConstructor - // fill `decls` with overriding shadowing overridden */ - { def fillDecls(bcs: List[Symbol], deferredflag: Int) { - if (!bcs.isEmpty) { - fillDecls(bcs.tail, deferredflag) - var e = bcs.head.info.decls.elems - while (e ne null) { - if (e.sym.getFlag(DEFERRED) == deferredflag.toLong && !exclude(e.sym)) - decls enter e.sym - e = e.next - } - } - } - // first, deferred (this wil need to change if we change lookup rules! - fillDecls(base.info.baseClasses, DEFERRED) - // then, concrete. - fillDecls(base.info.baseClasses, 0) - } - - private val size = base.info.baseClasses.length - - /** A map from baseclasses of <base> to ints, with smaller ints meaning lower in - * linearization order. - * symbols that are not baseclasses map to -1. + /** Types always match. Term symbols match if their member types + * relative to `self` match. */ - private val index = new mutable.HashMap[Symbol, Int] { - override def default(key: Symbol) = -1 - } - - // Note: overridingPairs can be called at odd instances by the Eclipse plugin - // Soemtimes symbols are not yet defined and we get missing keys. - // The implementation here is hardened so that it does not crash on a missing key. - - { var i = 0 - for (bc <- base.info.baseClasses) { - index(bc) = i - i += 1 - } - } - - /** A mapping from all base class indices to a bitset - * which indicates whether parents are subclasses. - * - * i \in subParents(j) iff - * exists p \in parents, b \in baseClasses: - * i = index(p) - * j = index(b) - * p isSubClass b - * p.baseType(b) == self.baseType(b) - */ - private val subParents = new Array[BitSet](size) - - { for (i <- List.range(0, size)) - subParents(i) = new BitSet(size) - for (p <- parents) { - val pIndex = index(p.typeSymbol) - if (pIndex >= 0) - for (bc <- p.baseClasses) - if (p.baseType(bc) =:= self.baseType(bc)) { - val bcIndex = index(bc) - if (bcIndex >= 0) - include(subParents(bcIndex), pIndex) - } - } - } - - /** Do `sym1` and `sym2` have a common subclass in `parents`? - * In that case we do not follow their overriding pairs - */ - private def hasCommonParentAsSubclass(sym1: Symbol, sym2: Symbol) = { - val index1 = index(sym1.owner) - (index1 >= 0) && { - val index2 = index(sym2.owner) - (index2 >= 0) && { - intersectionContainsElementLeq( - subParents(index1), subParents(index2), index1 min index2) - } - } - } - - /** The scope entries that have already been visited as overridden - * (maybe excluded because of hasCommonParentAsSubclass). - * These will not appear as overriding - */ - private val visited = HashSet[ScopeEntry]("visited", 64) - - /** The current entry candidate for overriding - */ - private var curEntry = decls.elems - - /** The current entry candidate for overridden */ - private var nextEntry = curEntry - - /** The current candidate symbol for overriding */ - var overriding: Symbol = _ - - /** If not null: The symbol overridden by overriding */ - var overridden: Symbol = _ - - //@M: note that next is called once during object initialization - def hasNext: Boolean = curEntry ne null - - @tailrec - final def next() { - if (curEntry ne null) { - overriding = curEntry.sym - if (nextEntry ne null) { - do { - do { - nextEntry = decls.lookupNextEntry(nextEntry) - /* DEBUG - if ((nextEntry ne null) && - !(nextEntry.sym hasFlag PRIVATE) && - !(overriding.owner == nextEntry.sym.owner) && - !matches(overriding, nextEntry.sym)) - println("skipping "+overriding+":"+self.memberType(overriding)+overriding.locationString+" to "+nextEntry.sym+":"+self.memberType(nextEntry.sym)+nextEntry.sym.locationString) - */ - } while ((nextEntry ne null) && - ((nextEntry.sym hasFlag PRIVATE) || - (overriding.owner == nextEntry.sym.owner) || - (!matches(overriding, nextEntry.sym)) || - (exclude(overriding)))) - if (nextEntry ne null) visited addEntry nextEntry - // skip nextEntry if a class in `parents` is a subclass of the owners of both - // overriding and nextEntry.sym - } while ((nextEntry ne null) && (hasCommonParentAsSubclass(overriding, nextEntry.sym))) - if (nextEntry ne null) { - overridden = nextEntry.sym - //Console.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG - } else { - do { - curEntry = curEntry.next - } while ((curEntry ne null) && (visited contains curEntry)) - nextEntry = curEntry - next() - } - } - } - } - - next() + override protected def matches(sym1: Symbol, sym2: Symbol) = sym1.isType || ( + (sym1.owner != sym2.owner) + && !exclude(sym2) + && relatively.matches(sym1, sym2) + ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 03f680525c..ccf4a5e46f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -913,12 +913,13 @@ trait Infer extends Checkable { val targs = exprTypeArgs(tvars, tparams, treeTp, pt, useWeaklyCompatible) def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString "," printTyping(tree, s"infer expr instance from pt=$pt, $infer_s") + def targsStrict = if (targs eq null) null else targs mapConserve dropByName if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226 - substExpr(tree, tparams, targs, pt) + substExpr(tree, tparams, targsStrict, pt) List() } else { - val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targs) + val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targsStrict) def solved_s = map2(okParams, okArgs)((p, a) => s"$p=$a") mkString "," def undet_s = leftUndet match { case Nil => "" diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index d60e61df1b..a9a7f6a954 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -305,18 +305,23 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans /* Check that all conditions for overriding `other` by `member` * of class `clazz` are met. */ - def checkOverride(member: Symbol, other: Symbol) { + def checkOverride(pair: SymbolPair) { + import pair._ + val member = low + val other = high + def memberTp = lowType + def otherTp = highType + debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString)) - def memberTp = self.memberType(member) - def otherTp = self.memberType(other) - def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType + def noErrorType = !pair.isErroneous def isRootOrNone(sym: Symbol) = sym != null && sym.isRoot || sym == NoSymbol - def isNeitherInClass = (member.owner != clazz) && (other.owner != clazz) + def isNeitherInClass = member.owner != pair.base && other.owner != pair.base + def objectOverrideErrorMsg = ( - "overriding " + other.fullLocationString + " with " + member.fullLocationString + ":\n" + + "overriding " + high.fullLocationString + " with " + low.fullLocationString + ":\n" + "an overriding object must conform to the overridden object's class bound" + - analyzer.foundReqMsg(classBoundAsSeen(member.tpe), classBoundAsSeen(other.tpe)) + analyzer.foundReqMsg(pair.lowClassBound, pair.highClassBound) ) def overrideErrorMsg(msg: String): String = { @@ -460,79 +465,72 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } - def checkOverrideTypes() { - 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( !(sameLength(member.typeParams, other.typeParams) && (memberTp.substSym(member.typeParams, other.typeParams) =:= otherTp)) ) // (1.6) - overrideTypeError() + //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 + def checkOverrideAlias() { + if (pair.sameKind && lowType.substSym(low.typeParams, high.typeParams) =:= highType) () + else overrideTypeError() // (1.6) + } + //if (!member.typeParams.isEmpty) // (1.7) @MAT + // overrideError("may not be parameterized"); + def checkOverrideAbstract() { + if (!(highInfo.bounds containsType lowType)) { // (1.7.1) + overrideTypeError(); // todo: do an explaintypes with bounds here + explainTypes(_.bounds containsType _, highInfo, lowType) } - else if (other.isAbstractType) { - //if (!member.typeParams.isEmpty) // (1.7) @MAT - // overrideError("may not be parameterized"); - 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) + // 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 + typer.infer.checkKindBounds(high :: Nil, lowType :: Nil, rootType, low.owner) match { // (1.7.2) + case Nil => + case kindErrors => 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) { - // println("checkKindBounds" + ((List(member), List(memberTp.dealiasWiden), self, member.owner))) - val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.dealiasWiden), self, member.owner) - - if(!kindErrors.isEmpty) + } + // check a type alias's RHS corresponds to its declaration + // this overlaps somewhat with validateVariance + if (low.isAliasType) { + typer.infer.checkKindBounds(low :: Nil, lowType.normalize :: Nil, rootType, low.owner) match { + case Nil => + case kindErrors => unit.error(member.pos, - "The kind of the right-hand side "+memberTp.dealiasWiden+" of "+member.keyString+" "+ - member.varianceString + member.nameString+ " does not conform to its expected kind."+ + "The kind of the right-hand side "+lowType.normalize+" of "+low.keyString+" "+ + low.varianceString + low.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() // #2454 - val memberTp = self.memberType(member) - val otherTp = self.memberType(other) - if (!overridesTypeInPrefix(memberTp, otherTp, self)) { // 8 - overrideTypeError() - explainTypes(memberTp, otherTp) - } - - if (member.isStable && !otherTp.isVolatile) { - // (1.4), pt 2 -- member.isStable && memberTp.isVolatile started being possible after SI-6815 - // (before SI-6815, !symbol.tpe.isVolatile was implied by symbol.isStable) - // TODO: allow overriding when @uncheckedStable? - if (memberTp.isVolatile) - overrideError("has a volatile type; cannot override a member with non-volatile type") - else memberTp.dealiasWiden.resultType match { - case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) => - // might mask some inconsistencies -- check overrides - checkedCombinations += rt.parents - val tsym = rt.typeSymbol - if (tsym.pos == NoPosition) tsym setPos member.pos - checkAllOverrides(tsym, typesOnly = true) - case _ => - } + } + else if (low.isAbstractType && lowType.isVolatile && !highInfo.bounds.hi.isVolatile) + overrideError("is a volatile type; cannot override a type with non-volatile upper bound") + } + def checkOverrideTerm() { + other.cookJavaRawInfo() // #2454 + if (!overridesTypeInPrefix(lowType, highType, rootType)) { // 8 + overrideTypeError() + explainTypes(lowType, highType) + } + if (low.isStable && !highType.isVolatile) { + if (lowType.isVolatile) + overrideError("has a volatile type; cannot override a member with non-volatile type") + else lowType.normalize.resultType match { + case rt: RefinedType if !(rt =:= highType) && !(checkedCombinations contains rt.parents) => + // might mask some inconsistencies -- check overrides + checkedCombinations += rt.parents + val tsym = rt.typeSymbol + if (tsym.pos == NoPosition) tsym setPos member.pos + checkAllOverrides(tsym, typesOnly = true) + case _ => } } } + def checkOverrideTypes() { + if (high.isAliasType) checkOverrideAlias() + else if (high.isAbstractType) checkOverrideAbstract() + else if (high.isTerm) checkOverrideTerm() + } def checkOverrideDeprecated() { if (other.hasDeprecatedOverridingAnnotation) { @@ -545,8 +543,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val opc = new overridingPairs.Cursor(clazz) while (opc.hasNext) { - //Console.println(opc.overriding/* + ":" + opc.overriding.tpe*/ + " in "+opc.overriding.fullName + " overrides " + opc.overridden/* + ":" + opc.overridden.tpe*/ + " in "+opc.overridden.fullName + "/"+ opc.overridden.hasFlag(DEFERRED));//debug - if (!opc.overridden.isClass) checkOverride(opc.overriding, opc.overridden) + if (!opc.high.isClass) + checkOverride(opc.currentPair) opc.next() } @@ -585,10 +583,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def ignoreDeferred(member: Symbol) = ( (member.isAbstractType && !member.isFBounded) || ( - member.isJavaDefined && // the test requires exitingErasure so shouldn't be // done if the compiler has no erasure phase available - (currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol) + member.isJavaDefined + && (currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol) ) ) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7b2e40b59c..4d8ae73bcc 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -709,7 +709,82 @@ trait Definitions extends api.StandardDefinitions { case NullaryMethodType(restpe) => finalResultType(restpe) case _ => tp } + /** Similarly, putting all the isStable logic in one place. + * This makes it like 1000x easier to see the overall logic + * of the method. + */ + def isStable(tp: Type): Boolean = tp match { + case _: SingletonType => true + case NoPrefix => true + case TypeRef(_, NothingClass | SingletonClass, _) => true + case TypeRef(_, sym, _) if sym.isAbstractType => tp.bounds.hi.typeSymbol isSubClass SingletonClass + case TypeRef(pre, sym, _) if sym.isModuleClass => isStable(pre) + case TypeRef(_, _, _) if tp ne tp.dealias => isStable(tp.dealias) + case TypeVar(origin, _) => isStable(origin) + case AnnotatedType(_, atp, _) => isStable(atp) // Really? + case _: SimpleTypeProxy => isStable(tp.underlying) + case _ => false + } + def isVolatile(tp: Type): Boolean = { + // need to be careful not to fall into an infinite recursion here + // because volatile checking is done before all cycles are detected. + // the case to avoid is an abstract type directly or + // indirectly upper-bounded by itself. See #2918 + def isVolatileAbstractType: Boolean = { + def sym = tp.typeSymbol + def volatileUpperBound = isVolatile(tp.bounds.hi) + def safeIsVolatile = ( + if (volatileRecursions < TypeConstants.LogVolatileThreshold) + volatileUpperBound + // we can return true when pendingVolatiles contains sym, because + // a cycle will be detected afterwards and an error will result anyway. + else pendingVolatiles(sym) || { + pendingVolatiles += sym + try volatileUpperBound finally pendingVolatiles -= sym + } + ) + volatileRecursions += 1 + try safeIsVolatile finally volatileRecursions -= 1 + } + /** A refined type P1 with ... with Pn { decls } is volatile if + * one of the parent types Pi is an abstract type, and + * either i > 1, or decls or a following parent Pj, j > 1, contributes + * an abstract member. + * A type contributes an abstract member if it has an abstract member which + * is also a member of the whole refined type. A scope `decls` contributes + * an abstract member if it has an abstract definition which is also + * a member of the whole type. + */ + def isVolatileRefinedType: Boolean = { + val RefinedType(parents, decls) = tp + def isVisibleDeferred(m: Symbol) = m.isDeferred && ((tp nonPrivateMember m.name).alternatives contains m) + def contributesAbstractMembers(p: Type) = p.deferredMembers exists isVisibleDeferred + def dropConcreteParents = parents dropWhile (p => !p.typeSymbol.isAbstractType) + + (parents exists isVolatile) || { + dropConcreteParents match { + case Nil => false + case ps => (ps ne parents) || (ps.tail exists contributesAbstractMembers) || (decls exists isVisibleDeferred) + } + } + } + + tp match { + case ThisType(_) => false + case SingleType(_, sym) => isVolatile(tp.underlying) && (sym.hasVolatileType || !sym.isStable) + case NullaryMethodType(restpe) => isVolatile(restpe) + case PolyType(_, restpe) => isVolatile(restpe) + case TypeRef(_, _, _) if tp ne tp.dealias => isVolatile(tp.dealias) + case TypeRef(_, sym, _) if sym.isAbstractType => isVolatileAbstractType + case RefinedType(_, _) => isVolatileRefinedType + case TypeVar(origin, _) => isVolatile(origin) + case _: SimpleTypeProxy => isVolatile(tp.underlying) + case _ => false + } + } + private[this] var volatileRecursions: Int = 0 + private[this] val pendingVolatiles = mutable.HashSet[Symbol]() def abstractFunctionForFunctionType(tp: Type) = { assert(isFunctionType(tp), tp) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index d1c215713e..d48a6c6322 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -185,6 +185,7 @@ trait Kinds { ) } else { + hkarg.initialize // SI-7902 otherwise hkarg.typeParams yields List(NoSymbol)! debuglog("checkKindBoundsHK recursing to compare params of "+ hkparam +" with "+ hkarg) kindErrors ++= checkKindBoundsHK( hkarg.typeParams, diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala new file mode 100644 index 0000000000..b538648b36 --- /dev/null +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -0,0 +1,302 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect +package internal + +import scala.collection.mutable +import Flags._ +import util.HashSet +import scala.annotation.tailrec + +/** An abstraction for considering symbol pairs. + * One of the greatest sources of compiler bugs is that symbols can + * trivially lose their prefixes and turn into some completely different + * type with the smallest of errors. It is the exception not the rule + * that type comparisons are done correctly. + * + * This offers a small step toward coherence with two abstractions + * which come up over and over again: + * + * RelativeTo: operations relative to a prefix + * SymbolPair: two symbols being related somehow, plus the class + * in which the relation is being performed + * + * This is only a start, but it is a start. + */ +abstract class SymbolPairs { + val global: SymbolTable + import global._ + + /** Type operations relative to a prefix. All operations work on Symbols, + * and the types are the member types of those symbols in the prefix. + */ + class RelativeTo(val prefix: Type) { + def this(clazz: Symbol) = this(clazz.thisType) + import scala.language.implicitConversions // geez, it even has to hassle me when it's private + private implicit def symbolToType(sym: Symbol): Type = prefix memberType sym + + def erasureOf(sym: Symbol): Type = erasure.erasure(sym)(sym: Type) + def signature(sym: Symbol): String = sym defStringSeenAs (sym: Type) + def erasedSignature(sym: Symbol): String = sym defStringSeenAs erasureOf(sym) + + def isSameType(sym1: Symbol, sym2: Symbol): Boolean = sym1 =:= sym2 + def isSubType(sym1: Symbol, sym2: Symbol): Boolean = sym1 <:< sym2 + def isSuperType(sym1: Symbol, sym2: Symbol): Boolean = sym2 <:< sym1 + def isSameErasure(sym1: Symbol, sym2: Symbol): Boolean = erasureOf(sym1) =:= erasureOf(sym2) + def matches(sym1: Symbol, sym2: Symbol): Boolean = (sym1: Type) matches (sym2: Type) + + override def toString = s"RelativeTo($prefix)" + } + + /** Are types tp1 and tp2 equivalent seen from the perspective + * of `baseClass`? For instance List[Int] and Seq[Int] are =:= + * when viewed from IterableClass. + */ + def sameInBaseClass(baseClass: Symbol)(tp1: Type, tp2: Type) = + (tp1 baseType baseClass) =:= (tp2 baseType baseClass) + + case class SymbolPair(base: Symbol, low: Symbol, high: Symbol) { + def pos = if (low.owner == base) low.pos else if (high.owner == base) high.pos else base.pos + def self: Type = base.thisType + def rootType: Type = base.thisType + + def lowType: Type = self memberType low + def lowErased: Type = erasure.specialErasure(base)(low.tpe) + def lowClassBound: Type = classBoundAsSeen(low.tpe.typeSymbol) + + def highType: Type = self memberType high + def highInfo: Type = self memberInfo high + def highErased: Type = erasure.specialErasure(base)(high.tpe) + def highClassBound: Type = classBoundAsSeen(high.tpe.typeSymbol) + + def isErroneous = low.tpe.isErroneous || high.tpe.isErroneous + def sameKind = sameLength(low.typeParams, high.typeParams) + + private def classBoundAsSeen(tsym: Symbol) = + tsym.classBound.asSeenFrom(rootType, tsym.owner) + + private def memberDefString(sym: Symbol, where: Boolean) = { + val def_s = ( + if (sym.isConstructor) s"$sym: ${self memberType sym}" + else sym defStringSeenAs (self memberType sym) + ) + def_s + whereString(sym) + } + /** A string like ' at line 55' if the symbol is defined in the class + * under consideration, or ' in trait Foo' if defined elsewhere. + */ + private def whereString(sym: Symbol) = + if (sym.owner == base) " at line " + sym.pos.line else sym.locationString + + def lowString = memberDefString(low, where = true) + def highString = memberDefString(high, where = true) + + override def toString = sm""" + |Cursor(in $base) { + | high $highString + | erased $highErased + | infos ${high.infosString} + | low $lowString + | erased $lowErased + | infos ${low.infosString} + |}""".trim + } + + /** The cursor class + * @param base the base class containing the participating symbols + */ + abstract class Cursor(val base: Symbol) { + cursor => + + final val self = base.thisType // The type relative to which symbols are seen. + private val decls = newScope // all the symbols which can take part in a pair. + private val size = bases.length + + /** A symbol for which exclude returns true will not appear as + * either end of a pair. + */ + protected def exclude(sym: Symbol): Boolean + + /** Does `sym1` match `sym2` such that (sym1, sym2) should be + * considered as a (lo, high) pair? Types always match. Term symbols + * match if their member types relative to `self` match. + */ + protected def matches(sym1: Symbol, sym2: Symbol): Boolean + + /** The parents and base classes of `base`. Can be refined in subclasses. + */ + protected def parents: List[Type] = base.info.parents + protected def bases: List[Symbol] = base.info.baseClasses + + /** An implementation of BitSets as arrays (maybe consider collection.BitSet + * for that?) The main purpose of this is to implement + * intersectionContainsElement efficiently. + */ + private type BitSet = Array[Int] + + /** A mapping from all base class indices to a bitset + * which indicates whether parents are subclasses. + * + * i \in subParents(j) iff + * exists p \in parents, b \in baseClasses: + * i = index(p) + * j = index(b) + * p isSubClass b + * p.baseType(b) == self.baseType(b) + */ + private val subParents = new Array[BitSet](size) + + /** A map from baseclasses of <base> to ints, with smaller ints meaning lower in + * linearization order. Symbols that are not baseclasses map to -1. + */ + private val index = new mutable.HashMap[Symbol, Int] { override def default(key: Symbol) = -1 } + + /** The scope entries that have already been visited as highSymbol + * (but may have been excluded via hasCommonParentAsSubclass.) + * These will not appear as lowSymbol. + */ + private val visited = HashSet[ScopeEntry]("visited", 64) + + /** Initialization has to run now so decls is populated before + * the declaration of curEntry. + */ + init() + + // The current low and high symbols; the high may be null. + private[this] var lowSymbol: Symbol = _ + private[this] var highSymbol: Symbol = _ + + // The current entry candidates for low and high symbol. + private[this] var curEntry = decls.elems + private[this] var nextEntry = curEntry + + // These fields are initially populated with a call to next(). + next() + + // populate the above data structures + private def init() { + // Fill `decls` with lower symbols shadowing higher ones + def fillDecls(bcs: List[Symbol], deferred: Boolean) { + if (!bcs.isEmpty) { + fillDecls(bcs.tail, deferred) + var e = bcs.head.info.decls.elems + while (e ne null) { + if (e.sym.initialize.isDeferred == deferred && !exclude(e.sym)) + decls enter e.sym + e = e.next + } + } + } + var i = 0 + for (bc <- bases) { + index(bc) = i + subParents(i) = new BitSet(size) + i += 1 + } + for (p <- parents) { + val pIndex = index(p.typeSymbol) + if (pIndex >= 0) + for (bc <- p.baseClasses ; if sameInBaseClass(bc)(p, self)) { + val bcIndex = index(bc) + if (bcIndex >= 0) + include(subParents(bcIndex), pIndex) + } + } + // first, deferred (this will need to change if we change lookup rules!) + fillDecls(bases, deferred = true) + // then, concrete. + fillDecls(bases, deferred = false) + } + + private def include(bs: BitSet, n: Int) { + val nshifted = n >> 5 + val nmask = 1 << (n & 31) + bs(nshifted) |= nmask + } + + /** Implements `bs1 * bs2 * {0..n} != 0. + * Used in hasCommonParentAsSubclass */ + private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, n: Int): Boolean = { + val nshifted = n >> 5 + val nmask = 1 << (n & 31) + var i = 0 + while (i < nshifted) { + if ((bs1(i) & bs2(i)) != 0) return true + i += 1 + } + (bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0 + } + + /** Do `sym1` and `sym2` have a common subclass in `parents`? + * In that case we do not follow their pairs. + */ + private def hasCommonParentAsSubclass(sym1: Symbol, sym2: Symbol) = { + val index1 = index(sym1.owner) + (index1 >= 0) && { + val index2 = index(sym2.owner) + (index2 >= 0) && { + intersectionContainsElementLeq( + subParents(index1), subParents(index2), index1 min index2) + } + } + } + + @tailrec private def advanceNextEntry() { + if (nextEntry ne null) { + nextEntry = decls lookupNextEntry nextEntry + if (nextEntry ne null) { + val high = nextEntry.sym + val isMatch = matches(lowSymbol, high) && { visited addEntry nextEntry ; true } // side-effect visited on all matches + + // skip nextEntry if a class in `parents` is a subclass of the + // owners of both low and high. + if (isMatch && !hasCommonParentAsSubclass(lowSymbol, high)) + highSymbol = high + else + advanceNextEntry() + } + } + } + @tailrec private def advanceCurEntry() { + if (curEntry ne null) { + curEntry = curEntry.next + if (curEntry ne null) { + if (visited(curEntry) || exclude(curEntry.sym)) + advanceCurEntry() + else + nextEntry = curEntry + } + } + } + + /** The `low` and `high` symbol. In the context of overriding pairs, + * low == overriding and high == overridden. + */ + def low = lowSymbol + def high = highSymbol + + def hasNext = curEntry ne null + def currentPair = new SymbolPair(base, low, high) + def iterator = new Iterator[SymbolPair] { + def hasNext = cursor.hasNext + def next() = try cursor.currentPair finally cursor.next() + } + + // Note that next is called once during object initialization to + // populate the fields tracking the current symbol pair. + def next() { + if (curEntry ne null) { + lowSymbol = curEntry.sym + advanceNextEntry() // sets highSymbol + if (nextEntry eq null) { + advanceCurEntry() + next() + } + } + } + } +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 14d742fc2c..efdc8f7435 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3471,8 +3471,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert((prev eq null) || phaseId(validFrom) > phaseId(prev.validFrom), this) assert(validFrom != NoPeriod, this) - override def toString() = - "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" + private def phaseString = "%s: %s".format(phaseOf(validFrom), info) + override def toString = toList reverseMap (_.phaseString) mkString ", " def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 4367e51041..85dfa037ec 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -18,6 +18,7 @@ import util.Statistics import util.ThreeValues._ import Variance._ import Depth._ +import TypeConstants._ /* A standard type pattern match: case ErrorType => @@ -90,10 +91,6 @@ trait Types private var explainSwitch = false private final val emptySymbolSet = immutable.Set.empty[Symbol] - protected[internal] final val DefaultLogThreshhold = 50 - private final val LogPendingBaseTypesThreshold = DefaultLogThreshhold - private final val LogVolatileThreshold = DefaultLogThreshhold - private final val traceTypeVars = sys.props contains "scalac.debug.tvar" private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn off type parameter bounds being used @@ -142,8 +139,6 @@ trait Types override def typeConstructor: Type = underlying.typeConstructor override def isError = underlying.isError override def isErroneous = underlying.isErroneous - override def isStable: Boolean = underlying.isStable - override def isVolatile = underlying.isVolatile override def paramSectionCount = underlying.paramSectionCount override def paramss = underlying.paramss override def params = underlying.params @@ -270,7 +265,7 @@ trait Types def takesTypeArgs: Boolean = this.isHigherKinded /** Does this type denote a stable reference (i.e. singleton type)? */ - def isStable: Boolean = false + final def isStable: Boolean = definitions isStable this /** Is this type dangerous (i.e. it might contain conflicting * type information when empty, so that it can be constructed @@ -278,7 +273,7 @@ trait Types * type of the form T_1 with T_n { decls }, where one of the * T_i (i > 1) is an abstract type. */ - def isVolatile: Boolean = false + final def isVolatile: Boolean = definitions isVolatile this /** Is this type a structural refinement type (it ''refines'' members that have not been inherited) */ def isStructuralRefinement: Boolean = false @@ -1221,8 +1216,6 @@ trait Types abstract class SingletonType extends SubType with SimpleTypeProxy { def supertype = underlying override def isTrivial = false - override def isStable = true - override def isVolatile = underlying.isVolatile override def widen: Type = underlying.widen override def baseTypeSeq: BaseTypeSeq = { if (Statistics.canEnable) Statistics.incCounter(singletonBaseTypeSeqCount) @@ -1300,7 +1293,6 @@ trait Types /** An object representing a non-existing prefix */ case object NoPrefix extends Type { override def isTrivial: Boolean = true - override def isStable: Boolean = true override def prefixString = "" override def safeToString: String = "<noprefix>" override def kind = "NoPrefixType" @@ -1318,7 +1310,6 @@ trait Types override def isTrivial: Boolean = sym.isPackageClass override def typeSymbol = sym override def underlying: Type = sym.typeOfThis - override def isVolatile = false override def isHigherKinded = sym.isRefinementClass && underlying.isHigherKinded override def prefixString = if (settings.debug) sym.nameString + ".this." @@ -1367,8 +1358,6 @@ trait Types // more precise conceptually, but causes cyclic errors: (paramss exists (_ contains sym)) override def isImmediatelyDependent = (sym ne NoSymbol) && (sym.owner.isMethod && sym.isValueParameter) - - override def isVolatile : Boolean = underlying.isVolatile && (sym.hasVolatileType || !sym.isStable) /* override def narrow: Type = { if (phase.erasedTypes) this @@ -1764,33 +1753,6 @@ trait Types typeSymbol)) } else super.normalize } - - /** A refined type P1 with ... with Pn { decls } is volatile if - * one of the parent types Pi is an abstract type, and - * either i > 1, or decls or a following parent Pj, j > 1, contributes - * an abstract member. - * A type contributes an abstract member if it has an abstract member which - * is also a member of the whole refined type. A scope `decls` contributes - * an abstract member if it has an abstract definition which is also - * a member of the whole type. - */ - override def isVolatile = { - def isVisible(m: Symbol) = - this.nonPrivateMember(m.name).alternatives contains m - def contributesAbstractMembers(p: Type) = - p.deferredMembers exists isVisible - - ((parents exists (_.isVolatile)) - || - (parents dropWhile (! _.typeSymbol.isAbstractType) match { - case ps @ (_ :: ps1) => - (ps ne parents) || - (ps1 exists contributesAbstractMembers) || - (decls.iterator exists (m => m.isDeferred && isVisible(m))) - case _ => - false - })) - } override def kind = "RefinedType" } @@ -2026,7 +1988,6 @@ trait Types class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { require(sym.isModuleClass, sym) private[this] var narrowedCache: Type = _ - override def isStable = pre.isStable override def narrow = { if (narrowedCache eq null) narrowedCache = singleType(pre, sym.sourceModule) @@ -2041,7 +2002,6 @@ trait Types } class PackageTypeRef(pre0: Type, sym0: Symbol) extends ModuleTypeRef(pre0, sym0) { require(sym.isPackageClass, sym) - override def isStable = true override protected def finishPrefix(rest: String) = packagePrefix + rest } class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { @@ -2148,8 +2108,6 @@ trait Types require(sym.isAliasType, sym) override def dealias = if (typeParamsMatchArgs) betaReduce.dealias else super.dealias - override def isStable = normalize.isStable - override def isVolatile = normalize.isVolatile override def narrow = normalize.narrow override def thisInfo = normalize override def prefix = if (this ne normalize) normalize.prefix else pre @@ -2204,30 +2162,6 @@ trait Types private var symInfoCache: Type = _ private var thisInfoCache: Type = _ - override def isVolatile = { - // need to be careful not to fall into an infinite recursion here - // because volatile checking is done before all cycles are detected. - // the case to avoid is an abstract type directly or - // indirectly upper-bounded by itself. See #2918 - try { - volatileRecursions += 1 - if (volatileRecursions < LogVolatileThreshold) - bounds.hi.isVolatile - else if (pendingVolatiles(sym)) - true // we can return true here, because a cycle will be detected - // here afterwards and an error will result anyway. - else - try { - pendingVolatiles += sym - bounds.hi.isVolatile - } finally { - pendingVolatiles -= sym - } - } finally { - volatileRecursions -= 1 - } - } - override def thisInfo = { val symInfo = sym.info if (thisInfoCache == null || (symInfo ne symInfoCache)) { @@ -2242,7 +2176,6 @@ trait Types } thisInfoCache } - override def isStable = bounds.hi.typeSymbol isSubClass SingletonClass override def bounds = thisInfo.bounds override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this override def kind = "AbstractTypeRef" @@ -2328,7 +2261,6 @@ trait Types override def baseClasses = thisInfo.baseClasses override def baseTypeSeqDepth = baseTypeSeq.maxDepth - override def isStable = (sym eq NothingClass) || (sym eq SingletonClass) override def prefix = pre override def termSymbol = super.termSymbol override def termSymbolDirect = super.termSymbol @@ -2595,7 +2527,6 @@ trait Types override def baseClasses: List[Symbol] = resultType.baseClasses override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def boundSyms = resultType.boundSyms - override def isVolatile = resultType.isVolatile override def safeToString: String = "=> "+ resultType override def kind = "NullaryMethodType" } @@ -2634,7 +2565,6 @@ trait Types override def baseClasses: List[Symbol] = resultType.baseClasses override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def narrow: Type = resultType.narrow - override def isVolatile = resultType.isVolatile /** @M: typeDefSig wraps a TypeBounds in a PolyType * to represent a higher-kinded type parameter @@ -2679,7 +2609,6 @@ trait Types override protected def rewrap(newtp: Type) = existentialAbstraction(quantified, newtp) override def isTrivial = false - override def isStable: Boolean = false override def bounds = TypeBounds(maybeRewrap(underlying.bounds.lo), maybeRewrap(underlying.bounds.hi)) override def parents = underlying.parents map maybeRewrap override def boundSyms = quantified.toSet @@ -3239,8 +3168,6 @@ trait Types else super.normalize ) override def typeSymbol = origin.typeSymbol - override def isStable = origin.isStable - override def isVolatile = origin.isVolatile private def tparamsOfSym(sym: Symbol) = sym.info match { case PolyType(tparams, _) if tparams.nonEmpty => @@ -4651,6 +4578,12 @@ trait Types } +object TypeConstants { + final val DefaultLogThreshhold = 50 + final val LogPendingBaseTypesThreshold = DefaultLogThreshhold + final val LogVolatileThreshold = DefaultLogThreshhold +} + object TypesStats { import BaseTypeSeqsStats._ val rawTypeCount = Statistics.newCounter ("#raw type creations") diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index 152c689c56..6532bce9f0 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -12,7 +12,7 @@ trait TypeComparers { import definitions._ import TypesStats._ - private final val LogPendingSubTypesThreshold = DefaultLogThreshhold + private final val LogPendingSubTypesThreshold = TypeConstants.DefaultLogThreshhold private val pendingSubTypes = new mutable.HashSet[SubTypePair] diff --git a/test/files/neg/accesses.check b/test/files/neg/accesses.check index db58af12ce..5a5e03233e 100644 --- a/test/files/neg/accesses.check +++ b/test/files/neg/accesses.check @@ -1,7 +1,3 @@ -accesses.scala:23: error: overriding method f2 in class A of type ()Unit; - method f2 has weaker access privileges; it should not be private - private def f2(): Unit = () - ^ accesses.scala:24: error: overriding method f3 in class A of type ()Unit; method f3 has weaker access privileges; it should be at least protected private[p2] def f3(): Unit = () @@ -14,4 +10,4 @@ accesses.scala:26: error: overriding method f5 in class A of type ()Unit; method f5 has weaker access privileges; it should be at least protected[p1] protected[p2] def f5(): Unit ^ -four errors found +three errors found diff --git a/test/files/neg/accesses2.check b/test/files/neg/accesses2.check new file mode 100644 index 0000000000..554a7b4c81 --- /dev/null +++ b/test/files/neg/accesses2.check @@ -0,0 +1,4 @@ +accesses2.scala:5: error: class B1 needs to be abstract, since method f2 in class A of type ()Int is not defined + class B1 extends A { + ^ +one error found diff --git a/test/files/neg/accesses2.scala b/test/files/neg/accesses2.scala new file mode 100644 index 0000000000..c7640f84b5 --- /dev/null +++ b/test/files/neg/accesses2.scala @@ -0,0 +1,11 @@ +package p2 { + abstract class A { + private[p2] def f2(): Int + } + class B1 extends A { + private def f2(): Int = 1 + } + abstract class B2 extends A { + private def f2(): Int = 1 + } +} diff --git a/test/files/neg/t0259.check b/test/files/neg/t0259.check index 24e35e6176..8c15d98419 100644 --- a/test/files/neg/t0259.check +++ b/test/files/neg/t0259.check @@ -1,6 +1,6 @@ t0259.scala:4: error: double definition: -constructor TestCase3:(groups: String*)test.TestCase3 and -constructor TestCase3:(groups: (String, Int)*)test.TestCase3 at line 3 +constructor TestCase3: (groups: (String, Int)*)test.TestCase3 at line 3 and +constructor TestCase3: (groups: String*)test.TestCase3 at line 4 have same type after erasure: (groups: Seq)test.TestCase3 def this( groups: String*) = this() ^ diff --git a/test/files/neg/t3653.check b/test/files/neg/t3653.check index ac6e2ca9dc..ad68e29fb4 100644 --- a/test/files/neg/t3653.check +++ b/test/files/neg/t3653.check @@ -1,7 +1,7 @@ t3653.scala:3: error: double definition: -method x:(implicit x: Int)Int and -method x:(i: Int)Int at line 2 -have same type after erasure: (x: Int)Int +def x(i: Int): Int at line 2 and +def x(implicit x: Int): Int at line 3 +have same type after erasure: (i: Int)Int def x(implicit x: Int) = 5 ^ one error found diff --git a/test/files/neg/t588.check b/test/files/neg/t588.check index f8b5516fdc..ff08f77a6f 100644 --- a/test/files/neg/t588.check +++ b/test/files/neg/t588.check @@ -1,13 +1,13 @@ t588.scala:3: error: double definition: -method visit:(f: Int => String)Boolean and -method visit:(f: Int => Unit)Boolean at line 2 +def visit(f: Int => Unit): Boolean at line 2 and +def visit(f: Int => String): Boolean at line 3 have same type after erasure: (f: Function1)Boolean def visit(f: Int => String): Boolean ^ t588.scala:10: error: double definition: -method f:(brac: Test.this.TypeB)Unit and -method f:(node: Test.this.TypeA)Unit at line 9 -have same type after erasure: (brac: Test#TraitA)Unit +def f(node: Test.this.TypeA): Unit at line 9 and +def f(brac: Test.this.TypeB): Unit at line 10 +have same type after erasure: (node: Test#TraitA)Unit def f(brac : TypeB) : Unit; ^ two errors found diff --git a/test/files/neg/t6443c.check b/test/files/neg/t6443c.check index 7cf8d23f4b..7b7f419f6c 100644 --- a/test/files/neg/t6443c.check +++ b/test/files/neg/t6443c.check @@ -1,6 +1,6 @@ t6443c.scala:16: error: double definition: -method foo:(d: B.D)(a: Any)(d2: d.type)Unit and -method foo:(d: B.D)(a: Any, d2: d.type)Unit at line 11 +def foo(d: B.D)(a: Any,d2: d.type): Unit at line 11 and +def foo(d: B.D)(a: Any)(d2: d.type): Unit at line 16 have same type after erasure: (d: B.D, a: Object, d2: B.D)Unit def foo(d: D)(a: Any)(d2: d.type): Unit = () ^ diff --git a/test/files/neg/t663.check b/test/files/neg/t663.check index 40161fb3e3..633e27ee12 100644 --- a/test/files/neg/t663.check +++ b/test/files/neg/t663.check @@ -1,7 +1,7 @@ t663.scala:11: error: name clash between defined and inherited member: -method asMatch:(m: Test.this.Node)Any and -method asMatch:(node: Test.this.Matchable)Any in trait MatchableImpl -have same type after erasure: (m: test.Test#NodeImpl)Object +def asMatch(node: Test.this.Matchable): Any in trait MatchableImpl and +def asMatch(m: Test.this.Node): Any at line 11 +have same type after erasure: (node: test.Test#NodeImpl)Object def asMatch(m : Node) : Any = { ^ one error found diff --git a/test/files/neg/t7899.check b/test/files/neg/t7899.check new file mode 100644 index 0000000000..febfe76b8a --- /dev/null +++ b/test/files/neg/t7899.check @@ -0,0 +1,6 @@ +t7899.scala:5: error: type mismatch; + found : Int => Int + required: (=> Int) => ? + foo(identity)() + ^ +one error found diff --git a/test/files/neg/t7899.scala b/test/files/neg/t7899.scala new file mode 100644 index 0000000000..f2dea3ab1f --- /dev/null +++ b/test/files/neg/t7899.scala @@ -0,0 +1,7 @@ +object Test { + def foo[B](f: (=> Int) => B): () => B = () => f(0) + + def main(args: Array[String]) { + foo(identity)() + } +} diff --git a/test/files/neg/valueclasses-doubledefs.check b/test/files/neg/valueclasses-doubledefs.check index 556d7a0900..ec513aca6b 100644 --- a/test/files/neg/valueclasses-doubledefs.check +++ b/test/files/neg/valueclasses-doubledefs.check @@ -1,6 +1,6 @@ valueclasses-doubledefs.scala:5: error: double definition: -method apply:(x: Meter)String and -method apply:(x: Double)String at line 4 +def apply(x: Double): String at line 4 and +def apply(x: Meter): String at line 5 have same type after erasure: (x: Double)String def apply(x: Meter) = x.toString ^ diff --git a/test/files/neg/valueclasses-pavlov.check b/test/files/neg/valueclasses-pavlov.check index 031589edad..17102a0c68 100644 --- a/test/files/neg/valueclasses-pavlov.check +++ b/test/files/neg/valueclasses-pavlov.check @@ -1,6 +1,6 @@ valueclasses-pavlov.scala:8: error: double definition: -method foo:(x: Box2)String and -method foo:(x: String)String at line 7 +def foo(x: String): String at line 7 and +def foo(x: Box2): String at line 8 have same type after erasure: (x: String)String def foo(x: Box2) = "foo(Box2): ok" ^ diff --git a/test/files/pos/t7584.scala b/test/files/pos/t7584.scala deleted file mode 100644 index 52d127ecb9..0000000000 --- a/test/files/pos/t7584.scala +++ /dev/null @@ -1,11 +0,0 @@ -object Test { - def fold[A, B](f: (A, => B) => B) = ??? - def f[A, B](x: A, y: B): B = ??? - def bip[A, B] = fold[A, B]((x, y) => f(x, y)) - def bop[A, B] = fold[A, B](f) - - // these work: - fold[Int, Int]((x, y) => f(x, y)) - fold[Int, Int](f) -} - diff --git a/test/files/pos/t7902.scala b/test/files/pos/t7902.scala new file mode 100644 index 0000000000..47c525c179 --- /dev/null +++ b/test/files/pos/t7902.scala @@ -0,0 +1,17 @@ +import scala.language.higherKinds + +object Bug { + class Tag[W[M1[X1]]] + + def ofType[W[M2[X2]]]: Tag[W] = ??? + type InSeq [M3[X3]] = Some[M3[Any]] + + // fail + val x = ofType[InSeq] + + // okay + val y: Any = ofType[InSeq] + object T { + val z = ofType[InSeq] + } +} diff --git a/test/files/res/t687.check b/test/files/res/t687.check index b741b262b9..5f72c98636 100644 --- a/test/files/res/t687.check +++ b/test/files/res/t687.check @@ -1,8 +1,8 @@ nsc> nsc> t687/QueryB.scala:3: error: name clash between defined and inherited member: -method equals:(o: Object)Boolean and -method equals:(x$1: Any)Boolean in class Any -have same type after erasure: (o: Object)Boolean +def equals(x$1: Any): Boolean in class Any and +override def equals(o: Object): Boolean at line 3 +have same type after erasure: (x$1: Object)Boolean override def equals(o : Object) = false; ^ diff --git a/test/files/run/private-override.check b/test/files/run/private-override.check new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/files/run/private-override.check @@ -0,0 +1 @@ +3 diff --git a/test/files/run/private-override.scala b/test/files/run/private-override.scala new file mode 100644 index 0000000000..0a3f57f97c --- /dev/null +++ b/test/files/run/private-override.scala @@ -0,0 +1,17 @@ +package test.p1.p2 { + abstract class A { + private[p2] def f2(): Int = 1 + } + abstract class Other extends A { + // It's a private method - not a private[p2] method. Not a failed + // "weaker access privileges" override, a different namespace. + private def f2(): Int = super.f2() + 2 + def go() = f2() + } +} + +object Test extends test.p1.p2.Other { + def main(args: Array[String]): Unit = { + println(go()) + } +} diff --git a/test/files/run/t7584b.scala b/test/files/run/t7584b.scala new file mode 100644 index 0000000000..fd560f0418 --- /dev/null +++ b/test/files/run/t7584b.scala @@ -0,0 +1,14 @@ +object Test extends App { + def fold[A, B](f: (A, => B) => B) = (b: B) => f(null.asInstanceOf[A], b) + def f[A, B](x: A, y: B): B = y + def bip[A, B] = fold[A, B]((x, y) => f(x, y)) + def bop[A, B] = fold[A, B](f(_, _)) + + // these work: + fold[Int, Int]((x, y) => f(x, y))(0) + fold[Int, Int](f(_, _))(0) + + // Used to throw a ClassCastException. Since the fix for SI-7899, these issue type errors. + // fold[Int, Int](f _)(0) + // fold[Int, Int](f)(0) +} diff --git a/test/files/run/t7899.scala b/test/files/run/t7899.scala new file mode 100644 index 0000000000..5879d4b9fe --- /dev/null +++ b/test/files/run/t7899.scala @@ -0,0 +1,5 @@ +object Test extends App { + def id[A](a: => A): A = null.asInstanceOf[A] + def foo(f: (=> Int) => Int) = () => f(???) + foo(id)() // should be allowed and not throw ??? +} |