summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Typers.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala178
1 files changed, 120 insertions, 58 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index b35a23ed50..0fe77f10cc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -16,9 +16,9 @@ import scala.collection.{ mutable, immutable }
import scala.tools.nsc.util.BatchSourceFile
import mutable.ListBuffer
import symtab.Flags._
-
import util.Statistics
import util.Statistics._
+import scala.tools.util.StringOps.{ countAsString, countElementsAsString }
// Suggestion check whether we can do without priming scopes with symbols of outer scopes,
// like the IDE does.
@@ -80,6 +80,7 @@ trait Typers extends Modes {
abstract class Typer(context0: Context) extends TyperDiagnostics {
import context0.unit
+ import typeDebug.{ ptTree, ptBlock, ptLine }
val infer = new Inferencer(context0) {
override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281
@@ -728,7 +729,7 @@ trait Typers extends Modes {
val tree1 = if (tree.isType) tree
else TypeApply(tree, tparams1 map (tparam =>
TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos //@M/tcpolyinfer: changed tparam.tpe to tparam.tpeHK
- context.undetparams = context.undetparams ::: tparams1
+ context.undetparams ++= tparams1
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0`
@@ -743,7 +744,7 @@ trait Typers extends Modes {
// Looking for a manifest of Nil: This has many potential types,
// so we need to instantiate to minimal type List[Nothing].
keepNothings = false, // retract Nothing's that indicate failure, ambiguities in manifests are dealt with in manifestOfType
- checkCompat = isWeaklyCompatible) // #3808
+ useWeaklyCompatible = true) // #3808
}
val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree))
@@ -825,8 +826,8 @@ trait Typers extends Modes {
// Note that we treat Any and Nothing as kind-polymorphic.
// We can't perform this check when typing type arguments to an overloaded method before the overload is resolved
// (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1).
- errorTree(tree, tree.tpe+" takes "+reporter.countElementsAsString(tree.tpe.typeParams.length, "type parameter")+
- ", expected: "+reporter.countAsString(pt.typeParams.length))
+ errorTree(tree, tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+
+ ", expected: "+countAsString(pt.typeParams.length))
tree setType tree.tpe
} else tree match { // (6)
case TypeTree() => tree
@@ -891,10 +892,16 @@ trait Typers extends Modes {
case TypeRef(_, sym, _) =>
// note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially
// infinite expansion if pt is constant type ()
- if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) // (12)
+ if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12)
+ if (settings.warnValueDiscard.value)
+ context.unit.warning(tree.pos, "discarded non-Unit value")
return typed(atPos(tree.pos)(Block(List(tree), Literal(()))), mode, pt)
- else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt))
+ }
+ else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
+ if (settings.warnNumericWiden.value)
+ context.unit.warning(tree.pos, "implicit numeric widening")
return typed(atPos(tree.pos)(Select(tree, "to"+sym.name)), mode, pt)
+ }
case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13)
return typed(adaptAnnotations(tree, mode, pt), mode, pt)
case _ =>
@@ -1315,7 +1322,6 @@ trait Typers extends Modes {
treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
}
-
/** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
* all the time, it is exposed here the module/class typing methods go through it.
*/
@@ -2694,7 +2700,7 @@ trait Typers extends Modes {
(nme.ERROR, None)
} else {
names -= sym
- if(isJava) sym.cookJavaRawInfo() // #3429
+ if (isJava) sym.cookJavaRawInfo() // #3429
val annArg = tree2ConstArg(rhs, sym.tpe.resultType)
(sym.name, annArg)
}
@@ -2780,39 +2786,42 @@ trait Typers extends Modes {
def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type?
sym.isTypeParameter && sym.owner.isJavaDefined
- /** Given a set `rawSyms' of term- and type-symbols, and a type `tp'.
- * produce a set of fresh type parameters and a type so that it can be
- * abstracted to an existential type.
- * Every type symbol `T' in `rawSyms' is mapped to a clone.
- * Every term symbol `x' of type `T' in `rawSyms' is given an
- * associated type symbol of the following form:
+ /** Given a set `rawSyms` of term- and type-symbols, and a type
+ * `tp`, produce a set of fresh type parameters and a type so that
+ * it can be abstracted to an existential type. Every type symbol
+ * `T` in `rawSyms` is mapped to a clone. Every term symbol `x` of
+ * type `T` in `rawSyms` is given an associated type symbol of the
+ * following form:
*
- * type x.type <: T with <singleton>
+ * type x.type <: T with Singleton
*
- * The name of the type parameter is `x.type', to produce nice diagnostics.
- * The <singleton> parent ensures that the type parameter is still seen as a stable type.
- * Type symbols in rawSyms are fully replaced by the new symbols.
- * Term symbols are also replaced, except when they are the term
- * symbol of an Ident tree, in which case only the type of the
- * Ident is changed.
+ * The name of the type parameter is `x.type`, to produce nice
+ * diagnostics. The Singleton parent ensures that the type
+ * parameter is still seen as a stable type. Type symbols in
+ * rawSyms are fully replaced by the new symbols. Term symbols are
+ * also replaced, except for term symbols of an Ident tree, where
+ * only the type of the Ident is changed.
*/
protected def existentialTransform(rawSyms: List[Symbol], tp: Type) = {
val typeParams: List[Symbol] = rawSyms map { sym =>
val name = sym.name match {
case x: TypeName => x
- case x => newTypeName(x+".type")
+ case x => newTypeName(x + ".type")
}
- val bound = sym.existentialBound
- val sowner = if (isRawParameter(sym)) context.owner else sym.owner
- val quantified: Symbol = sowner.newAbstractType(sym.pos, name).setFlag(EXISTENTIAL)
+ val bound = sym.existentialBound
+ val sowner = if (isRawParameter(sym)) context.owner else sym.owner
+ val quantified = sowner.newExistential(sym.pos, name)
- quantified.setInfo(bound.cloneInfo(quantified))
- quantified
+ quantified setInfo bound.cloneInfo(quantified)
}
- val typeParamTypes = typeParams map (_.tpe)
- //println("ex trans "+rawSyms+" . "+tp+" "+typeParamTypes+" "+(typeParams map (_.info)))//DEBUG
- for (tparam <- typeParams) tparam.setInfo(tparam.info.subst(rawSyms, typeParamTypes))
- (typeParams, tp.subst(rawSyms, typeParamTypes))
+ // Higher-kinded existentials are not yet supported, but this is
+ // tpeHK for when they are: "if a type constructor is expected/allowed,
+ // tpeHK must be called instead of tpe."
+ val typeParamTypes = typeParams map (_.tpeHK)
+ (
+ typeParams map (tparam => tparam setInfo tparam.info.subst(rawSyms, typeParamTypes)),
+ tp.subst(rawSyms, typeParamTypes)
+ )
}
/** Compute an existential type from raw hidden symbols `syms' and type `tp'
@@ -2973,16 +2982,17 @@ trait Typers extends Modes {
errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.")
}
- @inline final def deindentTyping() = if (printTypings) context.typingIndent = context.typingIndent.substring(0, context.typingIndent.length() - 2)
- @inline final def indentTyping() = if (printTypings) context.typingIndent += " "
- @inline final def printTyping(s: => String) = if (printTypings) println(context.typingIndent+s)
+ @inline final def deindentTyping() = context.typingIndentLevel -= 2
+ @inline final def indentTyping() = context.typingIndentLevel += 2
+ @inline final def printTyping(s: => String) = {
+ if (printTypings)
+ println(context.typingIndent + s.replaceAll("\n", "\n" + context.typingIndent))
+ }
+ @inline final def printInference(s: => String) = {
+ if (printInfers)
+ println(s)
+ }
- /**
- * @param tree ...
- * @param mode ...
- * @param pt ...
- * @return ...
- */
protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
def isPatternMode = inPatternMode(mode)
@@ -3120,7 +3130,7 @@ trait Typers extends Modes {
return fail
if (treeInfo.mayBeVarGetter(varsym)) {
- lhs1 match {
+ treeInfo.methPart(lhs1) match {
case Select(qual, name) =>
val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos
val app = Apply(sel, List(rhs)) setPos tree.pos
@@ -3187,7 +3197,7 @@ trait Typers extends Modes {
if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) {
context.undetparams = cloneSymbols(tpt0.symbol.typeParams)
TypeTree().setOriginal(tpt0)
- .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpe)))
+ .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347.
} else tpt0
}
@@ -3274,7 +3284,7 @@ trait Typers extends Modes {
}
}
- /** Try to apply function to arguments; if it does not work try to
+ /** Try to apply function to arguments; if it does not work, try to convert Java raw to existentials, or try to
* insert an implicit conversion.
*/
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
@@ -3284,6 +3294,17 @@ trait Typers extends Modes {
t
case ex: TypeError =>
stopTimer(failedApplyNanos, start)
+
+ // If the problem is with raw types, copnvert to existentials and try again.
+ // See #4712 for a case where this situation arises,
+ if ((fun.symbol ne null) && fun.symbol.isJavaDefined) {
+ val newtpe = rawToExistential(fun.tpe)
+ if (fun.tpe ne newtpe) {
+ // println("late cooking: "+fun+":"+fun.tpe) // DEBUG
+ return tryTypedApply(fun setType newtpe, args)
+ }
+ }
+
def treesInResult(tree: Tree): List[Tree] = tree :: (tree match {
case Block(_, r) => treesInResult(r)
case Match(_, cases) => cases
@@ -3295,9 +3316,13 @@ trait Typers extends Modes {
case _ => Nil
})
def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos)
-
- if (fun :: tree :: args exists errorInResult) {
- printTyping("second try for: "+fun+" and "+args)
+ val retry = fun :: tree :: args exists errorInResult
+ 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: " + ex.pos+"!="+tree.pos
+ }
+ if (retry) {
val Select(qual, name) = fun
val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex)
val qual1 =
@@ -3307,8 +3332,7 @@ trait Typers extends Modes {
val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
return typed1(tree1, mode | SNDTRYmode, pt)
}
- } else printTyping("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos)
-
+ }
reportTypeError(tree.pos, ex)
setError(tree)
}
@@ -3593,10 +3617,10 @@ trait Typers extends Modes {
!(List(Any_isInstanceOf, Any_asInstanceOf) contains result.symbol) // null.is/as is not a dereference
}
// unit is null here sometimes; how are we to know when unit might be null? (See bug #2467.)
- if (settings.Xchecknull.value && isPotentialNullDeference && unit != null)
+ if (settings.warnSelectNullable.value && isPotentialNullDeference && unit != null)
unit.warning(tree.pos, "potential null pointer dereference: "+tree)
- result match {
+ val selection = result match {
// could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks
treeCopy.SelectFromTypeTree(
@@ -3616,6 +3640,21 @@ trait Typers extends Modes {
case _ =>
result
}
+ // To fully benefit from special casing the return type of
+ // getClass, we have to catch it immediately so expressions
+ // like x.getClass().newInstance() are typed with the type of x.
+ val isRefinableGetClass = (
+ selection.symbol.name == nme.getClass_
+ && selection.tpe.params.isEmpty
+ // TODO: If the type of the qualifier is inaccessible, we can cause private types
+ // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this
+ // so for now it requires the type symbol be public.
+ && qual.tpe.typeSymbol.isPublic
+ )
+ if (isRefinableGetClass)
+ selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe))
+ else
+ selection
}
}
@@ -4185,7 +4224,9 @@ trait Typers extends Modes {
* @param pt ...
* @return ...
*/
- def typed(tree: Tree, mode: Int, pt: Type): Tree = { indentTyping()
+ def typed(tree: Tree, mode: Int, pt: Type): Tree = {
+ indentTyping()
+
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
if (settings.debug.value)
@@ -4198,6 +4239,7 @@ trait Typers extends Modes {
case _ => tp
}
+ var alreadyTyped = false
try {
if (Statistics.enabled) {
val t = currentTime()
@@ -4212,15 +4254,34 @@ trait Typers extends Modes {
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
- printTyping("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors+", context.owner = "+context.owner) //DEBUG
- var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt))
- printTyping("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG
+ alreadyTyped = tree.tpe ne null
+ var tree1: Tree = if (alreadyTyped) tree else {
+ printTyping(
+ ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
+ "undetparams" -> context.undetparams,
+ "implicitsEnabled" -> context.implicitsEnabled,
+ "silent" -> !context.reportGeneralErrors,
+ "context.owner" -> context.owner
+ )
+ )
+ val tree1 = typed1(tree, mode, dropExistential(pt))
+ printTyping("typed %s: %s%s".format(
+ ptTree(tree1), tree1.tpe,
+ if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")
+ )
+ tree1
+ }
tree1.tpe = addAnnotations(tree1, tree1.tpe)
-
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
- printTyping("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG
+
+ if (!alreadyTyped) {
+ printTyping("adapted %s: %s to %s, %s".format(
+ tree1, tree1.tpe.widen, pt, context.undetparamsString)
+ ) //DEBUG
+ }
+
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
@@ -4228,7 +4289,7 @@ trait Typers extends Modes {
} catch {
case ex: TypeError =>
tree.tpe = null
- printTyping("caught "+ex+" in typed: "+tree) //DEBUG
+ printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Exception =>
@@ -4241,6 +4302,7 @@ trait Typers extends Modes {
}
finally {
deindentTyping()
+
if (Statistics.enabled) {
val t = currentTime()
microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt