diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 04:46:02 -0700 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 04:46:02 -0700 |
commit | 57db28c55c3610f508b07940f7077cb73932418f (patch) | |
tree | 70b62aa01670cfc4b0877327a96e9d434f152418 /src | |
parent | 0614d2f512ad7b1b3885f81d9e6e779f447a6511 (diff) | |
parent | 211c9620ba83de143ea4776f55a3e0c4de11d002 (diff) | |
download | scala-57db28c55c3610f508b07940f7077cb73932418f.tar.gz scala-57db28c55c3610f508b07940f7077cb73932418f.tar.bz2 scala-57db28c55c3610f508b07940f7077cb73932418f.zip |
Merge pull request #1399 from paulp/210-unchecked
Much better unchecked warnings.
Diffstat (limited to 'src')
10 files changed, 337 insertions, 123 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eaee39d7e6..a7da857429 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1750,7 +1750,7 @@ self => in.nextToken() if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } - case IDENTIFIER if treeInfo.isVariableName(in.name) => + case IDENTIFIER if nme.isVariableName(in.name) => atPos(start) { Bind(identForType(), EmptyTree) } case _ => typ() diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index af77d3fe3f..40e520076a 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -21,7 +21,7 @@ trait Patterns extends ast.TreeDSL { import definitions._ import CODE._ import Debug._ - import treeInfo.{ unbind, isStar, isVarPattern, isVariableName } + import treeInfo.{ unbind, isStar, isVarPattern } type PatternMatch = MatchMatrix#PatternMatch private type PatternVar = MatrixContext#PatternVar @@ -366,7 +366,7 @@ trait Patterns extends ast.TreeDSL { lazy val Select(qualifier, name) = select def pathSegments = getPathSegments(tree) def backticked: Option[String] = qualifier match { - case _: This if isVariableName(name) => Some("`%s`".format(name)) + case _: This if nme.isVariableName(name) => Some("`%s`".format(name)) case _ => None } override def covers(sym: Symbol) = newMatchesPattern(sym, tree.tpe) @@ -388,11 +388,11 @@ trait Patterns extends ast.TreeDSL { lazy val UnApply(unfn, args) = tree lazy val Apply(fn, _) = unfn lazy val MethodType(List(arg, _*), _) = fn.tpe - + // Covers if the symbol matches the unapply method's argument type, // and the return type of the unapply is Some. override def covers(sym: Symbol) = newMatchesPattern(sym, arg.tpe) - + // TODO: for alwaysCovers: // fn.tpe.finalResultType.typeSymbol == SomeClass @@ -451,7 +451,7 @@ trait Patterns extends ast.TreeDSL { (sym.tpe.baseTypeSeq exists (_ matchesPattern pattp)) } } - + def sym = tree.symbol def tpe = tree.tpe def isEmpty = tree.isEmpty @@ -496,4 +496,4 @@ trait Patterns extends ast.TreeDSL { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 5a3db26e30..d3a5cebea0 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -185,7 +185,7 @@ abstract class UnCurry extends InfoTransform * try { * body * } catch { - * case ex: NonLocalReturnControl[_] => + * case ex: NonLocalReturnControl[T @unchecked] => * if (ex.key().eq(key)) ex.value() * else throw ex * } @@ -195,7 +195,8 @@ abstract class UnCurry extends InfoTransform localTyper typed { val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) val ex = meth.newValue(nme.ex, body.pos) setInfo extpe - val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType)) + val argType = meth.tpe.finalResultType withAnnotation (AnnotationInfo marker UncheckedClass.tpe) + val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(argType)) val rhs = ( IF ((ex DOT nme.key)() OBJ_EQ Ident(key)) THEN ((ex DOT nme.value)()) diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala new file mode 100644 index 0000000000..7e15cf91a7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -0,0 +1,284 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2012 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package typechecker + +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import scala.util.control.ControlThrowable +import symtab.Flags._ +import scala.annotation.tailrec +import Checkability._ + +/** On pattern matcher checkability: + * + * Consider a pattern match of this form: (x: X) match { case _: P => } + * + * There are four possibilities to consider: + * [P1] X will always conform to P + * [P2] x will never conform to P + * [P3] X <: P if some runtime test is true + * [P4] X cannot be checked against P + * + * The first two cases correspond to those when there is enough + * static information to say X <: P or that (x ∈ X) ⇒ (x ∉ P). + * The fourth case includes unknown abstract types or structural + * refinements appearing within a pattern. + * + * The third case is the interesting one. We designate another type, XR, + * which is essentially the intersection of X and |P|, where |P| is + * the erasure of P. If XR <: P, then no warning is emitted. + * + * Examples of how this info is put to use: + * sealed trait A[T] ; class B[T] extends A[T] + * def f(x: B[Int]) = x match { case _: A[Int] if true => } + * def g(x: A[Int]) = x match { case _: B[Int] => } + * + * `f` requires no warning because X=B[Int], P=A[Int], and B[Int] <:< A[Int]. + * `g` requires no warning because X=A[Int], P=B[Int], XR=B[Int], and B[Int] <:< B[Int]. + * XR=B[Int] because a value of type A[Int] which is tested to be a B can + * only be a B[Int], due to the definition of B (B[T] extends A[T].) + * + * This is something like asSeenFrom, only rather than asking what a type looks + * like from the point of view of one of its base classes, we ask what it looks + * like from the point of view of one of its subclasses. + */ +trait Checkable { + self: Analyzer => + + import global._ + import definitions._ + import CheckabilityChecker.{ isNeverSubType, isNeverSubClass } + + /** The applied type of class 'to' after inferring anything + * possible from the knowledge that 'to' must also be of the + * type given in 'from'. + */ + def propagateKnownTypes(from: Type, to: Symbol): Type = { + def tparams = to.typeParams + val tvars = tparams map (p => TypeVar(p)) + val tvarType = appliedType(to, tvars: _*) + val bases = from.baseClasses filter (to.baseClasses contains _) + + bases foreach { bc => + val tps1 = (from baseType bc).typeArgs + val tps2 = (tvarType baseType bc).typeArgs + (tps1, tps2).zipped foreach (_ =:= _) + // Alternate, variance respecting formulation causes + // neg/unchecked3.scala to fail (abstract types). TODO - + // figure it out. It seems there is more work to do if I + // allow for variance, because the constraints accumulate + // as bounds and "tvar.instValid" is false. + // + // foreach3(tps1, tps2, bc.typeParams)((tp1, tp2, tparam) => + // if (tparam.initialize.isCovariant) tp1 <:< tp2 + // else if (tparam.isContravariant) tp2 <:< tp1 + // else tp1 =:= tp2 + // ) + } + + val resArgs = tparams zip tvars map { + case (_, tvar) if tvar.instValid => tvar.constr.inst + case (tparam, _) => tparam.tpe + } + appliedType(to, resArgs: _*) + } + + private def isUnwarnableTypeArgSymbol(sym: Symbol) = ( + sym.isTypeParameter // dummy + || (sym.name.toTermName == nme.WILDCARD) // _ + || nme.isVariableName(sym.name) // type variable + ) + private def isUnwarnableTypeArg(arg: Type) = ( + uncheckedOk(arg) // @unchecked T + || isUnwarnableTypeArgSymbol(arg.typeSymbolDirect) // has to be direct: see pos/t1439 + ) + private def uncheckedOk(tp: Type) = tp hasAnnotation UncheckedClass + + private def typeArgsInTopLevelType(tp: Type): List[Type] = { + val tps = tp match { + case RefinedType(parents, _) => parents flatMap typeArgsInTopLevelType + case TypeRef(_, ArrayClass, arg :: Nil) => typeArgsInTopLevelType(arg) + case TypeRef(pre, sym, args) => typeArgsInTopLevelType(pre) ++ args + case ExistentialType(tparams, underlying) => tparams.map(_.tpe) ++ typeArgsInTopLevelType(underlying) + case _ => Nil + } + tps filterNot isUnwarnableTypeArg + } + + private class CheckabilityChecker(val X: Type, val P: Type) { + def Xsym = X.typeSymbol + def Psym = P.typeSymbol + def XR = propagateKnownTypes(X, Psym) + // sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean] + def P1 = X matchesPattern P + def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P) + def P3 = isNonRefinementClassType(P) && (XR matchesPattern P) + def P4 = !(P1 || P2 || P3) + + def summaryString = f""" + |Checking checkability of (x: $X) against pattern $P + |[P1] $P1%-6s X <: P // $X <: $P + |[P2] $P2%-6s x ∉ P // (x ∈ $X) ⇒ (x ∉ $P) + |[P3] $P3%-6s XR <: P // $XR <: $P + |[P4] $P4%-6s None of the above // !(P1 || P2 || P3) + """.stripMargin.trim + + val result = ( + if (X.isErroneous || P.isErroneous) CheckabilityError + else if (P1) StaticallyTrue + else if (P2) StaticallyFalse + else if (P3) RuntimeCheckable + else if (uncheckableType == NoType) { + // Avoid warning (except ourselves) if we can't pinpoint the uncheckable type + debugwarn("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString) + CheckabilityError + } + else Uncheckable + ) + lazy val uncheckableType = if (Psym.isAbstractType) P else { + val possibles = typeArgsInTopLevelType(P).toSet + val opt = possibles find { targ => + // Create a derived type with every possibly uncheckable type replaced + // with a WildcardType, except for 'targ'. If !(XR <: derived) then + // 'targ' is uncheckable. + val derived = P map (tp => if (possibles(tp) && !(tp =:= targ)) WildcardType else tp) + !(XR <:< derived) + } + opt getOrElse NoType + } + + def neverSubClass = isNeverSubClass(Xsym, Psym) + def neverMatches = result == StaticallyFalse + def isUncheckable = result == Uncheckable + def uncheckableMessage = uncheckableType match { + case NoType => "something" + case tp @ RefinedType(_, _) => "refinement " + tp + case TypeRef(_, sym, _) if sym.isAbstractType => "abstract type " + sym.name + case tp => "non-variable type argument " + tp + } + } + + /** X, P, [P1], etc. are all explained at the top of the file. + */ + private object CheckabilityChecker { + /** A knowable class is one which is either effectively final + * itself, or sealed with only knowable children. + */ + def isKnowable(sym: Symbol): Boolean = /*logResult(s"isKnowable($sym)")*/( + sym.initialize.isEffectivelyFinal // pesky .initialize requirement, or we receive lies about isSealed + || sym.isSealed && (sym.children forall isKnowable) + ) + def knownSubclasses(sym: Symbol): List[Symbol] = /*logResult(s"knownSubclasses($sym)")*/(sym :: { + if (sym.isSealed) sym.children.toList flatMap knownSubclasses + else Nil + }) + def excludable(s1: Symbol, s2: Symbol) = /*logResult(s"excludable($s1, $s2)")*/( + isKnowable(s1) + && !(s2 isSubClass s1) + && knownSubclasses(s1).forall(child => !(child isSubClass s2)) + ) + + /** Given classes A and B, can it be shown that nothing which is + * an A will ever be a subclass of something which is a B? This + * entails not only showing that !(A isSubClass B) but that the + * same is true of all their subclasses. Restated for symmetry: + * the same value cannot be a member of both A and B. + * + * 1) A must not be a subclass of B, nor B of A (the trivial check) + * 2) One of A or B must be completely knowable (see isKnowable) + * 3) Assuming A is knowable, the proposition is true if + * !(A' isSubClass B) for all A', where A' is a subclass of A. + * + * Due to symmetry, the last condition applies as well in reverse. + */ + def isNeverSubClass(sym1: Symbol, sym2: Symbol) = /*logResult(s"isNeverSubClass($sym1, $sym2)")*/( + sym1.isClass + && sym2.isClass + && (excludable(sym1, sym2) || excludable(sym2, sym1)) + ) + private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/ { + def isNeverSubArg(t1: Type, t2: Type, variance: Int) = { + if (variance > 0) isNeverSubType(t2, t1) + else if (variance < 0) isNeverSubType(t1, t2) + else isNeverSameType(t1, t2) + } + exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg) + } + private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => + ( isNeverSubClass(sym1, sym2) + || isNeverSubClass(sym2, sym1) + || ((sym1 == sym2) && isNeverSubArgs(args1, args2, sym1.typeParams)) + ) + case _ => + false + } + // Important to dealias at any entry point (this is the only one at this writing.) + def isNeverSubType(tp1: Type, tp2: Type): Boolean = /*logResult(s"isNeverSubType($tp1, $tp2)")*/((tp1.dealias, tp2.dealias) match { + case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => + isNeverSubClass(sym1, sym2) || { + (sym1 isSubClass sym2) && { + val tp1seen = tp1 baseType sym2 + isNeverSubArgs(tp1seen.typeArgs, args2, sym2.typeParams) + } + } + case _ => false + }) + } + + trait InferCheckable { + self: Inferencer => + + /** TODO: much better error positions. + * Kind of stuck right now because they just pass us the one tree. + * TODO: Eliminate inPattern, canRemedy, which have no place here. + */ + def checkCheckable(tree: Tree, P0: Type, X0: Type, inPattern: Boolean, canRemedy: Boolean = false) { + if (uncheckedOk(P0)) return + def where = if (inPattern) "pattern " else "" + + // singleton types not considered here + val P = P0.widen + val X = X0.widen + + P match { + // Prohibit top-level type tests for these, but they are ok nested (e.g. case Foldable[Nothing] => ... ) + case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => + InferErrorGen.TypePatternOrIsInstanceTestError(tree, P) + // If top-level abstract types can be checked using a classtag extractor, don't warn about them + case TypeRef(_, sym, _) if sym.isAbstractType && canRemedy => + ; + // Matching on types like case _: AnyRef { def bippy: Int } => doesn't work -- yet. + case RefinedType(_, decls) if !decls.isEmpty => + getContext.unit.warning(tree.pos, s"a pattern match on a refinement type is unchecked") + case _ => + val checker = new CheckabilityChecker(X, P) + log(checker.summaryString) + if (checker.neverMatches) { + val addendum = if (checker.neverSubClass) "" else " (but still might match its erasure)" + getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $P$addendum") + } + else if (checker.isUncheckable) { + val msg = ( + if (checker.uncheckableType =:= P) s"abstract type $where$P" + else s"${checker.uncheckableMessage} in type $where$P" + ) + getContext.unit.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure") + } + } + } + } +} + +private[typechecker] final class Checkability(val value: Int) extends AnyVal { } +private[typechecker] object Checkability { + val StaticallyTrue = new Checkability(0) + val StaticallyFalse = new Checkability(1) + val RuntimeCheckable = new Checkability(2) + val Uncheckable = new Checkability(3) + val CheckabilityError = new Checkability(4) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 22077303a4..0c3dcb9342 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -17,7 +17,7 @@ import scala.annotation.tailrec * @author Martin Odersky * @version 1.0 */ -trait Infer { +trait Infer extends Checkable { self: Analyzer => import global._ @@ -254,7 +254,7 @@ trait Infer { private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) extends InferencerContextErrors { + class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { import InferErrorGen._ /* -- Error Messages --------------------------------------------------- */ @@ -1341,99 +1341,6 @@ trait Infer { check(tp, Nil) } - // if top-level abstract types can be checked using a classtag extractor, don't warn about them - def checkCheckable(tree: Tree, typeToTest: Type, typeEnsured: Type, inPattern: Boolean, canRemedy: Boolean = false) = { - log(s"checkCheckable($tree, $typeToTest, $typeEnsured, inPattern = $inPattern, canRemedy = $canRemedy") - - sealed abstract class TypeConformance(check: (Type, Type) => Boolean) { - def apply(t1: Type, t2: Type): Boolean = check(t1, t2) && { - log(s"Skipping unchecked for statically verifiable condition $t1 ${this} $t2") - true - } - } - // I tried to use varianceInType to track the variance implications - // but I could not make it work. - case object =:= extends TypeConformance(_ =:= _) - case object <:< extends TypeConformance(_ <:< _) - case object >:> extends TypeConformance((t1, t2) => t2 <:< t1) - case object =!= extends TypeConformance((t1, t2) => false) - - var bound: List[Symbol] = Nil - var warningMessages: List[String] = Nil - - def isLocalBinding(sym: Symbol) = ( - sym.isAbstractType && ( - (bound contains sym) - || (sym.name == tpnme.WILDCARD) - || { - val e = context.scope.lookupEntry(sym.name) - (e ne null) && e.sym == sym && !e.sym.isTypeParameterOrSkolem && e.owner == context.scope - } - ) - ) - def check(tp0: Type, pt: Type, conformance: TypeConformance): Boolean = { - val tp = tp0.normalize - // Set the warning message to be issued when the top-level call fails. - def warn(what: String): Boolean = { - warningMessages ::= what - false - } - def checkArg(param: Symbol, arg: Type) = { - def conforms = ( - if (param.isCovariant) <:< - else if (param.isContravariant) >:> - else =:= - ) - (arg hasAnnotation UncheckedClass) || { - arg.withoutAnnotations match { - case TypeRef(_, sym, args) => - ( isLocalBinding(sym) - || arg.typeSymbol.isTypeParameterOrSkolem - || (sym.name == tpnme.WILDCARD) // avoid spurious warnings on HK types - || check(arg, param.tpeHK, conforms) - || warn("non-variable type argument " + arg) - ) - case _ => - warn("non-variable type argument " + arg) - } - } - } - - // Checking if pt (the expected type of the pattern, and the type - // we are guaranteed) conforms to tp (the type expressed in the pattern's - // type test.) If it does, then even if the type being checked for appears - // to be uncheckable, it is not a warning situation, because it is indeed - // checked: not at runtime, but statically. - conformance.apply(pt, tp) || (tp match { - case SingleType(pre, _) => check(pre, pt, =:=) - case ExistentialType(quantified, tp1) => bound :::= quantified ; check(tp1, pt, <:<) - case ThisType(_) | NoPrefix => true - case RefinedType(parents, decls) if decls.isEmpty => parents forall (p => check(p, pt, <:<)) - case RefinedType(_, _) => warn("refinement " + tp) - case TypeRef(_, ArrayClass, arg :: Nil) => check(arg, NoType, =!=) - case TypeRef(_, NonLocalReturnControlClass, _) => true // no way to suppress unchecked warnings on try/catch - // we only use the extractor for top-level type tests, type arguments remain unchecked - case TypeRef(_, sym, _) if sym.isAbstractType => isLocalBinding(sym) || canRemedy || warn("abstract type " + tp) - case TypeRef(_, _, Nil) => false // leaf node - case TypeRef(pre, sym, args) => forall2(sym.typeParams, args)(checkArg) && check(pre, pt.prefix, =:=) - case _ => warn("type " + tp) - }) - } - typeToTest match { - // Prohibit top-level type tests for these, but they are - // acceptable nested (e.g. case Foldable[Nothing] => ... ) - case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => - TypePatternOrIsInstanceTestError(tree, typeToTest) - case _ => - def where = ( if (inPattern) "pattern " else "" ) + typeToTest - if (check(typeToTest, typeEnsured, =:=)) () - // Note that this is a regular warning, not an uncheckedWarning, - // which is now the province of such notifications as "pattern matcher - // exceeded its analysis budget." - else warningMessages foreach (m => - context.unit.warning(tree.pos, s"$m in type $where is unchecked since it is eliminated by erasure")) - } - } /** Type intersection of simple type tp1 with general type tp2. * The result eliminates some redundancies. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index fa7e756e36..c25b6c3726 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -253,12 +253,6 @@ trait Typers extends Modes with Adaptations with Tags { result } } - def isNonRefinementClassType(tpe: Type) = tpe match { - case SingleType(_, sym) => sym.isModuleClass - case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass - case ErrorType => true - case _ => false - } private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false } private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false } @@ -3768,9 +3762,13 @@ trait Typers extends Modes with Adaptations with Tags { if (fun.symbol == Predef_classOf) typedClassOf(tree, args.head, true) else { - if (!isPastTyper && fun.symbol == Any_isInstanceOf && !targs.isEmpty) - checkCheckable(tree, targs.head, AnyClass.tpe, inPattern = false) - + if (!isPastTyper && fun.symbol == Any_isInstanceOf && targs.nonEmpty) { + val scrutineeType = fun match { + case Select(qual, _) => qual.tpe + case _ => AnyClass.tpe + } + checkCheckable(tree, targs.head, scrutineeType, inPattern = false) + } val resultpe = restpe.instantiateTypeParams(tparams, targs) //@M substitution in instantiateParams needs to be careful! //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int] @@ -3780,7 +3778,8 @@ trait Typers extends Modes with Adaptations with Tags { //println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe) treeCopy.TypeApply(tree, fun, args) setType resultpe } - } else { + } + else { TypedApplyWrongNumberOfTpeParametersError(tree, fun) } case ErrorType => diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 2cdfb05e77..eacbf6a0cc 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -346,6 +346,16 @@ trait StdNames { def isSingletonName(name: Name) = name endsWith SINGLETON_SUFFIX def isModuleName(name: Name) = name endsWith MODULE_SUFFIX_NAME + /** Is name a variable name? */ + def isVariableName(name: Name): Boolean = { + val first = name.startChar + ( ((first.isLower && first.isLetter) || first == '_') + && (name != nme.false_) + && (name != nme.true_) + && (name != nme.null_) + ) + } + def isDeprecatedIdentifierName(name: Name) = name.toTermName match { case nme.`then` | nme.`macro` => true case _ => false diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 6ef4c3f660..68decc27f5 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -247,7 +247,7 @@ abstract class TreeInfo { /** Is tree a variable pattern? */ def isVarPattern(pat: Tree): Boolean = pat match { - case x: Ident => !x.isBackquoted && isVariableName(x.name) + case x: Ident => !x.isBackquoted && nme.isVariableName(x.name) case _ => false } def isDeprecatedIdentifier(tree: Tree): Boolean = tree match { @@ -312,14 +312,6 @@ abstract class TreeInfo { /** Is name a left-associative operator? */ def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.endChar != ':') - private val reserved = Set[Name](nme.false_, nme.true_, nme.null_) - - /** Is name a variable name? */ - def isVariableName(name: Name): Boolean = { - val first = name.startChar - ((first.isLower && first.isLetter) || first == '_') && !reserved(name) - } - /** Is tree a `this` node which belongs to `enclClass`? */ def isSelf(tree: Tree, enclClass: Symbol): Boolean = tree match { case This(_) => tree.symbol == enclClass diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fd5694b599..6b274467fc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -5732,6 +5732,13 @@ trait Types extends api.Types { self: SymbolTable => case _ => false } + def isNonRefinementClassType(tpe: Type) = tpe match { + case SingleType(_, sym) => sym.isModuleClass + case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass + case ErrorType => true + case _ => false + } + // @assume tp1.isHigherKinded || tp2.isHigherKinded def isHKSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = ( tp1.typeSymbol == NothingClass diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 14b5d3003d..201b4dfe0a 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -175,6 +175,20 @@ trait Collections { } false } + final def exists3[A, B, C](xs1: List[A], xs2: List[B], xs3: List[C])(f: (A, B, C) => Boolean): Boolean = { + var ys1 = xs1 + var ys2 = xs2 + var ys3 = xs3 + while (!ys1.isEmpty && !ys2.isEmpty && !ys3.isEmpty) { + if (f(ys1.head, ys2.head, ys3.head)) + return true + + ys1 = ys1.tail + ys2 = ys2.tail + ys3 = ys3.tail + } + false + } final def forall2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Boolean): Boolean = { var ys1 = xs1 var ys2 = xs2 |