From 9835d33da7c98feb2f1917b0694d1628d675262a Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 3 Oct 2013 08:30:15 -0400 Subject: Describe type parameter interpolation in @implicitNotFound documentation Using this feature is necessary for helpful error messages, so it should be documented. Thank you to @adriaanm for recommending the this description. --- src/library/scala/annotation/implicitNotFound.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/library/scala/annotation/implicitNotFound.scala b/src/library/scala/annotation/implicitNotFound.scala index bbde90cebb..eeedcb014e 100644 --- a/src/library/scala/annotation/implicitNotFound.scala +++ b/src/library/scala/annotation/implicitNotFound.scala @@ -9,8 +9,11 @@ package scala.annotation /** - * An annotation that specifies the error message that is emitted when the compiler - * cannot find an implicit value of the annotated type. + * To customize the error message that's emitted when an implicit of type + * C[T1,..., TN] cannot be found, annotate the class C with @implicitNotFound. + * Assuming C has type parameters X1,..., XN, the error message will be the + * result of replacing all occurrences of ${Xi} in the string msg with the + * string representation of the corresponding type argument Ti. * * * @author Adriaan Moors * @since 2.8.1 -- cgit v1.2.3 From e609f1f20b0dce4905271b92aebd0298c7862859 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 5 Oct 2013 16:46:46 -0700 Subject: Generalize OverridingPairs to SymbolPairs. Increases your chance of knowing what is going on in OverridingPairs. Introduces some new abstractions which I hope for your own sakes you will put to use in some way: RelativeTo: operations relative to a prefix SymbolPair: two symbols being compared for something, and the enclosing class where the comparison is being performed Fixed a minor bug with access by accident by way of more principled pair analysis. See run/private-override.scala. Upgraded the error message issued on certain conflicts to give the line numbers of both conflicting methods, as opposed to just one and you go hunting. --- src/compiler/scala/tools/nsc/Global.scala | 2 + .../scala/tools/nsc/transform/Erasure.scala | 136 +++++----- .../tools/nsc/transform/OverridingPairs.scala | 224 ++------------- .../scala/tools/nsc/typechecker/RefChecks.scala | 144 +++++----- .../scala/reflect/internal/SymbolPairs.scala | 302 +++++++++++++++++++++ src/reflect/scala/reflect/internal/Symbols.scala | 4 +- test/files/neg/accesses.check | 6 +- test/files/neg/accesses2.check | 4 + test/files/neg/accesses2.scala | 11 + test/files/neg/t0259.check | 4 +- test/files/neg/t3653.check | 6 +- test/files/neg/t588.check | 10 +- test/files/neg/t6443c.check | 4 +- test/files/neg/t663.check | 6 +- test/files/neg/valueclasses-doubledefs.check | 4 +- test/files/neg/valueclasses-pavlov.check | 4 +- test/files/res/t687.check | 6 +- test/files/run/private-override.check | 1 + test/files/run/private-override.scala | 17 ++ 19 files changed, 514 insertions(+), 381 deletions(-) create mode 100644 src/reflect/scala/reflect/internal/SymbolPairs.scala create mode 100644 test/files/neg/accesses2.check create mode 100644 test/files/neg/accesses2.scala create mode 100644 test/files/run/private-override.check create mode 100644 test/files/run/private-override.scala (limited to 'src') 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 .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 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/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/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 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/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/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/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()) + } +} -- cgit v1.2.3 From 3e6b82c59c75e7ea925a015e33aaac95f7a56b60 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 5 Oct 2013 17:06:27 -0700 Subject: Extract isStable and isVolatile from Type. As with finalResultType, this is easier to control and WAY easier to understand. Look, all the relevant code fits on half a screenful rather than being spread out over 5000 lines and you have to be constantly conscious of what is overriding what. Truly it would be hard to damn the indiscriminate use of subtype polymorphism any more soundly than does this, by way of contrast: 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 } That's all of it! If there are bugs in it (of course there are) some might even be found now. --- .../scala/reflect/internal/Definitions.scala | 75 +++++++++++++++++++ src/reflect/scala/reflect/internal/Types.scala | 85 +++------------------- .../scala/reflect/internal/tpe/TypeComparers.scala | 2 +- 3 files changed, 85 insertions(+), 77 deletions(-) (limited to 'src') 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/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 = "" 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] -- cgit v1.2.3 From 8ed7099f7db6a1060a490a594c958ca9a8b41487 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 7 Oct 2013 15:06:22 +0200 Subject: SI-7899 Don't infer by-name types during, e.g. eta-expansion Given: def id[A](a: A): A = a def foo(f: (=> Int) => Int) = () foo(id) We eta-expanded `id` and inferred `A` to be `=> Int` to satisfy the expected type set forth by the formal parameter `f`. We really shouldn't go about inferring types that we can't *write*. Our attempt to do so led promptly into a `ClassCastException` in the enclosed test. This commit: - drops by-name-ness during `inferExprInstance` - tests that this results in a type error for the reported bug (neg/t7899) - tests that a method with a by-name parameter can still be eta expanded to match function with a corresponding by-name parameter (run/t7899) - discovers the same latent CCE in pos/t7584 - now that would be a type error - so we compensate by using placeholder functions rather than eta-expansion. - and move that that test to `run` for good measure. --- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 5 +++-- test/files/neg/t7899.check | 6 ++++++ test/files/neg/t7899.scala | 7 +++++++ test/files/pos/t7584.scala | 11 ----------- test/files/run/t7584b.scala | 14 ++++++++++++++ test/files/run/t7899.scala | 5 +++++ 6 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 test/files/neg/t7899.check create mode 100644 test/files/neg/t7899.scala delete mode 100644 test/files/pos/t7584.scala create mode 100644 test/files/run/t7584b.scala create mode 100644 test/files/run/t7899.scala (limited to 'src') 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/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/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/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 ??? +} -- cgit v1.2.3 From d0af55ce1ffb9d77e6a604edb41cda2b955e9f02 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 8 Oct 2013 21:20:23 +0200 Subject: SI-7895 Avoid cascade of "symbol not found" in pattern matches If we can't type check the `Foo` in `case Foo(a, b) => (a, b)`, we should enter error symbols for `a` and `b` to avoid further errors being reported in the case body. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 13 ++++++++++--- test/files/neg/t0418.check | 5 +---- test/files/neg/t418.check | 5 +---- test/files/neg/t545.check | 5 +---- test/files/neg/t5903a.check | 5 +---- test/files/neg/t5903b.check | 5 +---- test/files/neg/t5903c.check | 5 +---- test/files/neg/t5903d.check | 5 +---- test/files/neg/t7895.check | 4 ++++ test/files/neg/t7895.scala | 6 ++++++ test/files/neg/t997.check | 5 +---- 11 files changed, 28 insertions(+), 35 deletions(-) create mode 100644 test/files/neg/t7895.check create mode 100644 test/files/neg/t7895.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ecd987b031..e7371fa26b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2392,9 +2392,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (pat1.tpe.paramSectionCount > 0) pat1 modifyType (_.finalResultType) - for (bind @ Bind(name, _) <- cdef.pat) - if (name.toTermName != nme.WILDCARD && bind.symbol != null && bind.symbol != NoSymbol) - namer.enterIfNotThere(bind.symbol) + for (bind @ Bind(name, _) <- cdef.pat) { + val sym = bind.symbol + if (name.toTermName != nme.WILDCARD && sym != null) { + if (sym == NoSymbol) { + if (context.scope.lookup(name) == NoSymbol) + namer.enterInScope(context.owner.newErrorSymbol(name)) + } else + namer.enterIfNotThere(sym) + } + } val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree else typed(cdef.guard, BooleanTpe) diff --git a/test/files/neg/t0418.check b/test/files/neg/t0418.check index 4e9ad2f9ae..b95f8e4e1b 100644 --- a/test/files/neg/t0418.check +++ b/test/files/neg/t0418.check @@ -1,7 +1,4 @@ t0418.scala:2: error: not found: value Foo12340771 null match { case Foo12340771.Bar(x) => x } ^ -t0418.scala:2: error: not found: value x - null match { case Foo12340771.Bar(x) => x } - ^ -two errors found +one error found diff --git a/test/files/neg/t418.check b/test/files/neg/t418.check index 1489547823..1b99717b82 100644 --- a/test/files/neg/t418.check +++ b/test/files/neg/t418.check @@ -1,7 +1,4 @@ t418.scala:2: error: not found: value Foo12340771 null match { case Foo12340771.Bar(x) => x } ^ -t418.scala:2: error: not found: value x - null match { case Foo12340771.Bar(x) => x } - ^ -two errors found +one error found diff --git a/test/files/neg/t545.check b/test/files/neg/t545.check index 8ebbf9bdf6..aae575fa96 100644 --- a/test/files/neg/t545.check +++ b/test/files/neg/t545.check @@ -1,7 +1,4 @@ t545.scala:4: error: value blah is not a member of Test.Foo val x = foo.blah match { ^ -t545.scala:5: error: recursive value x needs type - case List(x) => x - ^ -two errors found +one error found diff --git a/test/files/neg/t5903a.check b/test/files/neg/t5903a.check index cbdcfd1bdd..2e5cc87167 100644 --- a/test/files/neg/t5903a.check +++ b/test/files/neg/t5903a.check @@ -1,7 +1,4 @@ Test_2.scala:4: error: wrong number of patterns for <$anon: AnyRef> offering (SomeTree.type, SomeTree.type): expected 2, found 3 case nq"$x + $y + $z" => println((x, y)) ^ -Test_2.scala:4: error: not found: value x - case nq"$x + $y + $z" => println((x, y)) - ^ -two errors found +one error found diff --git a/test/files/neg/t5903b.check b/test/files/neg/t5903b.check index faeb73ad03..e7637d3edb 100644 --- a/test/files/neg/t5903b.check +++ b/test/files/neg/t5903b.check @@ -3,7 +3,4 @@ Test_2.scala:4: error: type mismatch; required: String case t"$x" => println(x) ^ -Test_2.scala:4: error: not found: value x - case t"$x" => println(x) - ^ -two errors found +one error found diff --git a/test/files/neg/t5903c.check b/test/files/neg/t5903c.check index c9476edd11..05bd775d30 100644 --- a/test/files/neg/t5903c.check +++ b/test/files/neg/t5903c.check @@ -1,7 +1,4 @@ Test_2.scala:4: error: String is not supported case t"$x" => println(x) ^ -Test_2.scala:4: error: not found: value x - case t"$x" => println(x) - ^ -two errors found +one error found diff --git a/test/files/neg/t5903d.check b/test/files/neg/t5903d.check index d5d3fdcc28..9b8526b7f5 100644 --- a/test/files/neg/t5903d.check +++ b/test/files/neg/t5903d.check @@ -1,7 +1,4 @@ Test_2.scala:4: error: extractor macros can only expand into extractor calls case t"$x" => println(x) ^ -Test_2.scala:4: error: not found: value x - case t"$x" => println(x) - ^ -two errors found +one error found diff --git a/test/files/neg/t7895.check b/test/files/neg/t7895.check new file mode 100644 index 0000000000..1a58e24b77 --- /dev/null +++ b/test/files/neg/t7895.check @@ -0,0 +1,4 @@ +t7895.scala:4: error: not found: value Goop + case Goop(a, b, c) => Tuple2(a, b) + ^ +one error found diff --git a/test/files/neg/t7895.scala b/test/files/neg/t7895.scala new file mode 100644 index 0000000000..87a586a82d --- /dev/null +++ b/test/files/neg/t7895.scala @@ -0,0 +1,6 @@ +class A { + (null: Any) match { + // We don't want "symbol not found errors" for `a` and `b` in the case body. + case Goop(a, b, c) => Tuple2(a, b) + } +} diff --git a/test/files/neg/t997.check b/test/files/neg/t997.check index be1e92c369..8c41060ba2 100644 --- a/test/files/neg/t997.check +++ b/test/files/neg/t997.check @@ -4,7 +4,4 @@ t997.scala:13: error: wrong number of patterns for object Foo offering (String, t997.scala:13: error: wrong number of patterns for object Foo offering (String, String): expected 2, found 3 "x" match { case Foo(a, b, c) => Console.println((a,b,c)) } ^ -t997.scala:13: error: not found: value a -"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } - ^ -three errors found +two errors found -- cgit v1.2.3 From 7e4a97e532a9adcd0a6d014d948702aebec3541f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 8 Oct 2013 22:09:01 +0200 Subject: SI-7895 Issue all buffered errors after silent mode. Rather than just the first. For example, `foo(wizzle, wuzzle, woggle)` should report all three not-found symbols. --- .../scala/tools/nsc/typechecker/Contexts.scala | 1 + .../scala/tools/nsc/typechecker/Typers.scala | 44 ++++++++++++---------- test/files/neg/names-defaults-neg.check | 16 +++++++- test/files/neg/t4515.check | 12 +++++- test/files/neg/t556.check | 5 ++- test/files/neg/t5572.check | 7 +++- test/files/neg/t6829.check | 22 ++++++++++- test/files/neg/t7895b.check | 7 ++++ test/files/neg/t7895b.scala | 5 +++ test/files/neg/typeerror.check | 7 +++- 10 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 test/files/neg/t7895b.check create mode 100644 test/files/neg/t7895b.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index abc5363423..5e5619d034 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -329,6 +329,7 @@ trait Contexts { self: Analyzer => /** The first error, if any, in the report buffer */ def firstError: Option[AbsTypeError] = reportBuffer.firstError + def errors: Seq[AbsTypeError] = reportBuffer.errors /** Does the report buffer contain any errors? */ def hasErrors = reportBuffer.hasErrors diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e7371fa26b..0e4797d011 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -79,12 +79,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case SilentResultValue(value) if !p(value) => SilentTypeError(TypeErrorWrapper(new TypeError(NoPosition, "!p"))) case _ => this } - @inline final def orElse[T1 >: T](f: AbsTypeError => T1): T1 = this match { + @inline final def orElse[T1 >: T](f: Seq[AbsTypeError] => T1): T1 = this match { case SilentResultValue(value) => value - case SilentTypeError(err) => f(err) + case s : SilentTypeError => f(s.errors) } } - case class SilentTypeError(err: AbsTypeError) extends SilentResult[Nothing] { } + class SilentTypeError private(val errors: Seq[AbsTypeError]) extends SilentResult[Nothing] { + def err: AbsTypeError = errors.head + } + object SilentTypeError { + def apply(errors: AbsTypeError*): SilentTypeError = new SilentTypeError(errors) + def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption + } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { } def newTyper(context: Context): Typer = new NormalTyper(context) @@ -682,14 +689,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - context1.firstError match { - case Some(err) => - stopStats() - SilentTypeError(err) - case None => - // If we have a successful result, emit any warnings it created. - context1.flushAndIssueWarnings() - SilentResultValue(result) + if (context1.hasErrors) { + stopStats() + SilentTypeError(context1.errors: _*) + } else { + // If we have a successful result, emit any warnings it created. + context1.flushAndIssueWarnings() + SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") @@ -1258,9 +1264,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper reportError } - silent(_.adaptToMember(qual, HasMember(name), reportAmbiguous = false)) orElse (err => + silent(_.adaptToMember(qual, HasMember(name), reportAmbiguous = false)) orElse (errs => onError { - if (reportAmbiguous) context issue err + if (reportAmbiguous) errs foreach (context issue _) setError(tree) } ) @@ -3786,7 +3792,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } } - def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = silent(typeTree) orElse (err => DynamicRewriteError(tree, err)) + def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = silent(typeTree) orElse (err => DynamicRewriteError(tree, err.head)) } def typed1(tree: Tree, mode: Mode, pt: Type): Tree = { @@ -4173,7 +4179,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { val start = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null - def onError(typeError: AbsTypeError): Tree = { + def onError(typeErrors: Seq[AbsTypeError]): Tree = { if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. @@ -4198,13 +4204,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case TypeApply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult) case _ => Nil }) - def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos) + def errorInResult(tree: Tree) = treesInResult(tree) exists (err => typeErrors.exists(_.errPos == err.pos)) - val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult) + val retry = (typeErrors.forall(_.errPos != null)) && (fun :: tree :: args exists errorInResult) typingStack.printTyping({ val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ") if (retry) "second try: " + funStr - else "no second try: " + funStr + " because error not in result: " + typeError.errPos+"!="+tree.pos + else "no second try: " + funStr + " because error not in result: " + typeErrors.head.errPos+"!="+tree.pos }) if (retry) { val Select(qual, name) = fun @@ -4220,7 +4226,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => () } } - issue(typeError) + typeErrors foreach issue setError(treeCopy.Apply(tree, fun, args)) } diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index cdc12c2490..880ddc4327 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -7,6 +7,11 @@ names-defaults-neg.scala:5: error: type mismatch; required: Int test1(b = 2, a = "#") ^ +names-defaults-neg.scala:5: error: type mismatch; + found : Int(2) + required: String + test1(b = 2, a = "#") + ^ names-defaults-neg.scala:8: error: positional after named argument. test1(b = "(*", 23) ^ @@ -122,6 +127,12 @@ names-defaults-neg.scala:131: error: reference to var2 is ambiguous; it is both names-defaults-neg.scala:134: error: missing parameter type for expanded function ((x$1) => a = x$1) val taf2: Int => Unit = testAnnFun(a = _, b = get("+")) ^ +names-defaults-neg.scala:134: error: not found: value a + val taf2: Int => Unit = testAnnFun(a = _, b = get("+")) + ^ +names-defaults-neg.scala:134: error: not found: value get + val taf2: Int => Unit = testAnnFun(a = _, b = get("+")) + ^ names-defaults-neg.scala:135: error: parameter 'a' is already specified at parameter position 1 val taf3 = testAnnFun(b = _: String, a = get(8)) ^ @@ -131,6 +142,9 @@ names-defaults-neg.scala:136: error: missing parameter type for expanded functio names-defaults-neg.scala:136: error: missing parameter type for expanded function ((x$4) => b = x$4) val taf4: (Int, String) => Unit = testAnnFun(_, b = _) ^ +names-defaults-neg.scala:136: error: not found: value b + val taf4: (Int, String) => Unit = testAnnFun(_, b = _) + ^ names-defaults-neg.scala:144: error: variable definition needs type because 'x' is used as a named argument in its body. def t3 { var x = t.f(x = 1) } ^ @@ -168,4 +182,4 @@ names-defaults-neg.scala:180: error: reference to x is ambiguous; it is both a m class u18 { var x: Int = u.f(x = 1) } ^ four warnings found -42 errors found +46 errors found diff --git a/test/files/neg/t4515.check b/test/files/neg/t4515.check index 64e7cc1ca7..708fcfbd29 100644 --- a/test/files/neg/t4515.check +++ b/test/files/neg/t4515.check @@ -3,4 +3,14 @@ t4515.scala:37: error: type mismatch; required: _$2 handler.onEvent(target, ctx.getEvent, node, ctx) ^ -one error found +t4515.scala:37: error: type mismatch; + found : Main.DerivedPushNode[_$1] where type _$1 + required: Main.PushNode[_$2] + handler.onEvent(target, ctx.getEvent, node, ctx) + ^ +t4515.scala:37: error: type mismatch; + found : Main.PushEventContext[_$1] where type _$1 + required: Main.PushEventContext[_$2] + handler.onEvent(target, ctx.getEvent, node, ctx) + ^ +three errors found diff --git a/test/files/neg/t556.check b/test/files/neg/t556.check index 5135dc92ef..30cc296b35 100644 --- a/test/files/neg/t556.check +++ b/test/files/neg/t556.check @@ -1,4 +1,7 @@ t556.scala:3: error: missing parameter type def g:Int = f((x,y)=>x) ^ -one error found +t556.scala:3: error: missing parameter type + def g:Int = f((x,y)=>x) + ^ +two errors found diff --git a/test/files/neg/t5572.check b/test/files/neg/t5572.check index 7b1e290861..3c9adf41cd 100644 --- a/test/files/neg/t5572.check +++ b/test/files/neg/t5572.check @@ -3,9 +3,14 @@ t5572.scala:16: error: type mismatch; required: A Z.transf(a, b) match { ^ +t5572.scala:16: error: type mismatch; + found : A + required: B + Z.transf(a, b) match { + ^ t5572.scala:18: error: type mismatch; found : A required: B run(sth, b) ^ -two errors found +three errors found diff --git a/test/files/neg/t6829.check b/test/files/neg/t6829.check index c7c641844e..a0b43e3b52 100644 --- a/test/files/neg/t6829.check +++ b/test/files/neg/t6829.check @@ -20,11 +20,31 @@ t6829.scala:50: error: type mismatch; required: _53.State where val _53: G val r = rewards(agent).r(s,a,s2) ^ +t6829.scala:50: error: type mismatch; + found : a.type (with underlying type T2) + required: _53.Action where val _53: G + val r = rewards(agent).r(s,a,s2) + ^ +t6829.scala:50: error: type mismatch; + found : s2.type (with underlying type T3) + required: _53.State where val _53: G + val r = rewards(agent).r(s,a,s2) + ^ t6829.scala:51: error: type mismatch; found : s.type (with underlying type T1) required: _50.State agent.learn(s,a,s2,r): G#Agent ^ +t6829.scala:51: error: type mismatch; + found : a.type (with underlying type T2) + required: _50.Action + agent.learn(s,a,s2,r): G#Agent + ^ +t6829.scala:51: error: type mismatch; + found : s2.type (with underlying type T3) + required: _50.State + agent.learn(s,a,s2,r): G#Agent + ^ t6829.scala:53: error: not found: value nextState Error occurred in an application involving default arguments. copy(agents = updatedAgents, state = nextState, pastHistory = currentHistory) @@ -33,4 +53,4 @@ t6829.scala:53: error: not found: value currentHistory Error occurred in an application involving default arguments. copy(agents = updatedAgents, state = nextState, pastHistory = currentHistory) ^ -9 errors found +13 errors found diff --git a/test/files/neg/t7895b.check b/test/files/neg/t7895b.check new file mode 100644 index 0000000000..87ea72704e --- /dev/null +++ b/test/files/neg/t7895b.check @@ -0,0 +1,7 @@ +t7895b.scala:4: error: not found: value a + foo(a, b) + ^ +t7895b.scala:4: error: not found: value b + foo(a, b) + ^ +two errors found diff --git a/test/files/neg/t7895b.scala b/test/files/neg/t7895b.scala new file mode 100644 index 0000000000..1603027446 --- /dev/null +++ b/test/files/neg/t7895b.scala @@ -0,0 +1,5 @@ +object Test { + def foo(a: Any*) = () + + foo(a, b) +} diff --git a/test/files/neg/typeerror.check b/test/files/neg/typeerror.check index 3ce11dad8a..f117e702f0 100644 --- a/test/files/neg/typeerror.check +++ b/test/files/neg/typeerror.check @@ -3,4 +3,9 @@ typeerror.scala:6: error: type mismatch; required: scala.Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ -one error found +typeerror.scala:6: error: type mismatch; + found : Long(in method add) + required: scala.Long + else add2(x.head, y.head) :: add(x.tail, y.tail) + ^ +two errors found -- cgit v1.2.3 From 03a06e02483eaf442158339c2edd6bcfd99847a3 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 9 Oct 2013 13:10:28 +0200 Subject: SI-7902 Fix spurious kind error due to an unitialized symbol Tracked down this error: is invariant, but type Y2 is declared covariant 's bounds are stricter than type X2's declared bounds >: Nothing <: Any, 's bounds are stricter than type Y2's declared bounds >: Nothing <: Any to `Symbol#typeParams` returning `List(NoSymbol)` if the symbol was not initialized. This happends in the enclosed test for: // checkKindBoundsHK() hkArgs = List(type M3) hkParams = List(type M2) This commit forces the symbol of the higher-kinded type argument before checking kind conformance. A little backstory: The `List(NoSymbol)` arises from: class PolyTypeCompleter... { // @M. If `owner` is an abstract type member, `typeParams` are all NoSymbol (see comment in `completerOf`), // otherwise, the non-skolemized (external) type parameter symbols override val typeParams = tparams map (_.symbol) The variation that triggers this problem gets into the kind conformance checks quite early on, during naming of: private[this] val x = ofType[InSeq] The inferred type of which is forced during: def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations --- src/reflect/scala/reflect/internal/Kinds.scala | 1 + test/files/pos/t7902.scala | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/files/pos/t7902.scala (limited to 'src') 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/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] + } +} -- cgit v1.2.3 From 83feb8609161bf19247a8a310f8c5a9e3d8469f5 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 8 Oct 2013 22:28:00 +0200 Subject: SI-7985 Typecheck args after failure to typecheck function `missing1.foo(missing2)` now reports `missing1` and `missing2` as not found. Previously, only the first was reported. The arguments are typed with an expected type ErrorType. We propagate this through as the inferred type of anonymous function parameters to avoid issuing cascading "missing parameter type" errors in code like: scala> Nil.mapp(x => abracadabra) :8: error: value mapp is not a member of object Nil Nil.mapp(x => abracadabra) ^ :8: error: not found: value abracadabra Nil.mapp(x => abracadabra) ^ This was in response to unwanted changes in the output of existing neg tests; no new test is added. Similarly, we refine the errors in neg/t6436b.scala by to avoid cascaded errors after: type mismatch; found: StringContext, required: ?{def q: ?} --- .../scala/tools/nsc/typechecker/Typers.scala | 22 ++++++++++++++++------ test/files/neg/any-vs-anyref.check | 18 +++++++++++++++++- test/files/neg/applydynamic_sip.check | 17 ++++++++++++++++- test/files/neg/macro-basic-mamdmi.check | 4 ++-- test/files/neg/reflection-names-neg.check | 5 ++++- test/files/neg/t512.check | 5 ++++- test/files/neg/t5761.check | 5 ++++- test/files/neg/t7895c.check | 13 +++++++++++++ test/files/neg/t7895c.scala | 3 +++ test/files/run/constrained-types.check | 9 +++++++++ test/files/run/repl-reset.check | 6 ++++++ 11 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 test/files/neg/t7895c.check create mode 100644 test/files/neg/t7895c.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0e4797d011..7bf342f475 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -81,14 +81,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } @inline final def orElse[T1 >: T](f: Seq[AbsTypeError] => T1): T1 = this match { case SilentResultValue(value) => value - case s : SilentTypeError => f(s.errors) + case s : SilentTypeError => f(s.reportableErrors) } } - class SilentTypeError private(val errors: Seq[AbsTypeError]) extends SilentResult[Nothing] { + class SilentTypeError private(val errors: List[AbsTypeError]) extends SilentResult[Nothing] { def err: AbsTypeError = errors.head + def reportableErrors = errors match { + case (e1: AmbiguousImplicitTypeError) +: _ => + List(e1) // DRYer error reporting for neg/t6436b.scala + case all => + all + } } object SilentTypeError { - def apply(errors: AbsTypeError*): SilentTypeError = new SilentTypeError(errors) + def apply(errors: AbsTypeError*): SilentTypeError = new SilentTypeError(errors.toList) def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption } @@ -2714,7 +2720,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val FunctionSymbol = FunctionClass(numVparams) val (argpts, respt) = pt baseType FunctionSymbol match { case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => NoType), WildcardType) + case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) } if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) @@ -4278,8 +4284,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tryTypedApply(fun2, args) else doTypedApply(tree, fun2, args, mode, pt) - case SilentTypeError(err) => - onError({ issue(err); setError(tree) }) + case err: SilentTypeError => + onError({ + err.reportableErrors foreach issue + args foreach (arg => typed(arg, mode, ErrorType)) + setError(tree) + }) } } diff --git a/test/files/neg/any-vs-anyref.check b/test/files/neg/any-vs-anyref.check index 63c4853130..7378f0495f 100644 --- a/test/files/neg/any-vs-anyref.check +++ b/test/files/neg/any-vs-anyref.check @@ -36,12 +36,28 @@ Such types can participate in value classes, but instances cannot appear in singleton types or in reference comparisons. def foo5(x: Quux with Product) = (x eq "abc") && ("abc" eq x) ^ +any-vs-anyref.scala:10: error: type mismatch; + found : Quux with Product + required: AnyRef +Note that the parents of this type (Quux, Product) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo5(x: Quux with Product) = (x eq "abc") && ("abc" eq x) + ^ any-vs-anyref.scala:11: error: value eq is not a member of Quux with Product{def f: Int} Note that the parents of this type (Quux, Product) extend Any, not AnyRef. Such types can participate in value classes, but instances cannot appear in singleton types or in reference comparisons. def foo6(x: Quux with Product { def f: Int }) = (x eq "abc") && ("abc" eq x) ^ +any-vs-anyref.scala:11: error: type mismatch; + found : Quux with Product{def f: Int} + required: AnyRef +Note that the parents of this type (Quux, Product) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo6(x: Quux with Product { def f: Int }) = (x eq "abc") && ("abc" eq x) + ^ any-vs-anyref.scala:12: error: type mismatch; found : Quux with Product{def eq(other: String): Boolean} required: AnyRef @@ -61,4 +77,4 @@ any-vs-anyref.scala:27: error: type mismatch; required: Quux{def g(x: Int): Int} f(new Quux { def g(x: String) = x }) ^ -9 errors found +11 errors found diff --git a/test/files/neg/applydynamic_sip.check b/test/files/neg/applydynamic_sip.check index dcf97b29fc..f28f26c147 100644 --- a/test/files/neg/applydynamic_sip.check +++ b/test/files/neg/applydynamic_sip.check @@ -4,9 +4,18 @@ applydynamic_sip.scala:7: error: applyDynamic does not support passing a vararg applydynamic_sip.scala:8: error: applyDynamicNamed does not support passing a vararg parameter qual.sel(arg = a, a2: _*) ^ +applydynamic_sip.scala:8: error: not found: value arg + qual.sel(arg = a, a2: _*) + ^ applydynamic_sip.scala:9: error: applyDynamicNamed does not support passing a vararg parameter qual.sel(arg, arg2 = "a2", a2: _*) ^ +applydynamic_sip.scala:9: error: not found: value arg + qual.sel(arg, arg2 = "a2", a2: _*) + ^ +applydynamic_sip.scala:9: error: not found: value arg2 + qual.sel(arg, arg2 = "a2", a2: _*) + ^ applydynamic_sip.scala:18: error: type mismatch; found : String("sel") required: Int @@ -28,6 +37,9 @@ error after rewriting to Test.this.bad1.applyDynamicNamed("sel") possible cause: maybe a wrong Dynamic method signature? bad1.sel(a = 1) ^ +applydynamic_sip.scala:20: error: reassignment to val + bad1.sel(a = 1) + ^ applydynamic_sip.scala:21: error: type mismatch; found : String("sel") required: Int @@ -50,9 +62,12 @@ error after rewriting to Test.this.bad2.applyDynamicNamed("sel") possible cause: maybe a wrong Dynamic method signature? bad2.sel(a = 1) ^ +applydynamic_sip.scala:31: error: reassignment to val + bad2.sel(a = 1) + ^ applydynamic_sip.scala:32: error: Int does not take parameters error after rewriting to Test.this.bad2.updateDynamic("sel") possible cause: maybe a wrong Dynamic method signature? bad2.sel = 1 ^ -11 errors found +16 errors found diff --git a/test/files/neg/macro-basic-mamdmi.check b/test/files/neg/macro-basic-mamdmi.check index 621d318ceb..9328fbd51c 100644 --- a/test/files/neg/macro-basic-mamdmi.check +++ b/test/files/neg/macro-basic-mamdmi.check @@ -1,5 +1,5 @@ -Impls_Macros_Test_1.scala:36: error: macro implementation not found: foo +Impls_Macros_Test_1.scala:36: error: macro implementation not found: quux (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them) println(foo(2) + Macros.bar(2) * new Macros().quux(4)) - ^ + ^ one error found diff --git a/test/files/neg/reflection-names-neg.check b/test/files/neg/reflection-names-neg.check index a56a19e7fd..f941ec8dc1 100644 --- a/test/files/neg/reflection-names-neg.check +++ b/test/files/neg/reflection-names-neg.check @@ -7,4 +7,7 @@ Note that implicit conversions are not applicable because they are ambiguous: are possible conversion functions from String("abc") to reflect.runtime.universe.Name val x2 = ("abc": Name) drop 1 // error ^ -one error found +reflection-names-neg.scala:5: error: value drop is not a member of reflect.runtime.universe.Name + val x2 = ("abc": Name) drop 1 // error + ^ +two errors found diff --git a/test/files/neg/t512.check b/test/files/neg/t512.check index 814e65e405..051e5ee19f 100644 --- a/test/files/neg/t512.check +++ b/test/files/neg/t512.check @@ -1,4 +1,7 @@ t512.scala:3: error: not found: value something val xxx = something || ^ -one error found +t512.scala:4: error: not found: value something_else + something_else; + ^ +two errors found diff --git a/test/files/neg/t5761.check b/test/files/neg/t5761.check index 89d766fe34..2d66af26f6 100644 --- a/test/files/neg/t5761.check +++ b/test/files/neg/t5761.check @@ -13,4 +13,7 @@ Unspecified value parameter x. t5761.scala:13: error: not found: type Tread new Tread("sth") { }.run() ^ -four errors found +t5761.scala:13: error: value run is not a member of AnyRef + new Tread("sth") { }.run() + ^ +5 errors found diff --git a/test/files/neg/t7895c.check b/test/files/neg/t7895c.check new file mode 100644 index 0000000000..d4745b1f4b --- /dev/null +++ b/test/files/neg/t7895c.check @@ -0,0 +1,13 @@ +t7895c.scala:2: error: not found: value bong + def booboo = bong + booble + bippity - bazingo + ^ +t7895c.scala:2: error: not found: value booble + def booboo = bong + booble + bippity - bazingo + ^ +t7895c.scala:2: error: not found: value bippity + def booboo = bong + booble + bippity - bazingo + ^ +t7895c.scala:2: error: not found: value bazingo + def booboo = bong + booble + bippity - bazingo + ^ +four errors found diff --git a/test/files/neg/t7895c.scala b/test/files/neg/t7895c.scala new file mode 100644 index 0000000000..53d2a8672e --- /dev/null +++ b/test/files/neg/t7895c.scala @@ -0,0 +1,3 @@ +class A { + def booboo = bong + booble + bippity - bazingo +} diff --git a/test/files/run/constrained-types.check b/test/files/run/constrained-types.check index f022aac1b2..d965d8a2ff 100644 --- a/test/files/run/constrained-types.check +++ b/test/files/run/constrained-types.check @@ -138,6 +138,15 @@ scala> val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message :8: error: not found: value e val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message ^ +:8: error: not found: value f + val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message + ^ +:8: error: not found: value g + val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message + ^ +:8: error: not found: value h + val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message + ^ scala> diff --git a/test/files/run/repl-reset.check b/test/files/run/repl-reset.check index c6e147977a..ed95c7b8ff 100644 --- a/test/files/run/repl-reset.check +++ b/test/files/run/repl-reset.check @@ -33,6 +33,12 @@ scala> x1 + x2 + x3 :8: error: not found: value x1 x1 + x2 + x3 ^ +:8: error: not found: value x2 + x1 + x2 + x3 + ^ +:8: error: not found: value x3 + x1 + x2 + x3 + ^ scala> val x1 = 4 x1: Int = 4 -- cgit v1.2.3