diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 178 |
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 |