diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Implicits.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 228 |
1 files changed, 155 insertions, 73 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 53b4f0dac6..da07723b89 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -28,9 +28,8 @@ trait Implicits { import global._ import definitions._ - - def traceImplicits = printTypings - import global.typer.{printTyping, deindentTyping, indentTyping} + import typeDebug.{ ptTree, ptBlock, ptLine } + import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` * for more info how the search is conducted. @@ -46,16 +45,29 @@ trait Implicits { * @return A search result */ def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = { - printTyping("Beginning implicit search for "+ tree +" expecting "+ pt + (if(isView) " looking for a view" else "")) + printInference("[inferImplicit%s] pt = %s".format( + if (isView) " view" else "", pt) + ) + printTyping( + ptBlock("infer implicit" + (if (isView) " view" else ""), + "tree" -> tree, + "pt" -> pt, + "undetparams" -> context.outer.undetparams + ) + ) indentTyping() - val rawTypeStart = startCounter(rawTypeImpl) + + val rawTypeStart = startCounter(rawTypeImpl) val findMemberStart = startCounter(findMemberImpl) - val subtypeStart = startCounter(subtypeImpl) + val subtypeStart = startCounter(subtypeImpl) val start = startTimer(implicitNanos) - if (traceImplicits && !tree.isEmpty && !context.undetparams.isEmpty) - println("typing implicit with undetermined type params: "+context.undetparams+"\n"+tree) + if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) + printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) + val result = new ImplicitSearch(tree, pt, isView, context.makeImplicit(reportAmbiguous)).bestImplicit + printInference("[inferImplicit] result: " + result) context.undetparams = context.undetparams filterNot result.subst.fromContains + stopTimer(implicitNanos, start) stopCounter(rawTypeImpl, rawTypeStart) stopCounter(findMemberImpl, findMemberStart) @@ -87,7 +99,8 @@ trait Implicits { * that were instantiated by the winning implicit. */ class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter) { - override def toString = "SearchResult("+tree+", "+subst+")" + override def toString = "SearchResult(%s, %s)".format(tree, + if (subst.isEmpty) "" else subst) } lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) @@ -122,12 +135,9 @@ trait Implicits { tp.isError } - def isCyclicOrErroneous = try { - containsError(tpe) - } catch { - case ex: CyclicReference => - true - } + def isCyclicOrErroneous = + try containsError(tpe) + catch { case _: CyclicReference => true } override def equals(other: Any) = other match { case that: ImplicitInfo => @@ -137,7 +147,7 @@ trait Implicits { case _ => false } override def hashCode = name.## + pre.## + sym.## - override def toString = "ImplicitInfo(" + name + "," + pre + "," + sym + ")" + override def toString = name + ": " + tpe } /** A sentinel indicating no implicit was found */ @@ -222,7 +232,15 @@ trait Implicits { */ class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) extends Typer(context0) { - printTyping("begin implicit search: "+(tree, pt, isView, context.outer.undetparams)) + printTyping( + ptBlock("new ImplicitSearch", + "tree" -> tree, + "pt" -> pt, + "isView" -> isView, + "context0" -> context0, + "undetparams" -> context.outer.undetparams + ) + ) // assert(tree.isEmpty || tree.pos.isDefined, tree) import infer._ @@ -324,20 +342,29 @@ trait Implicits { if (isView) { val found = pt.typeArgs(0) val req = pt.typeArgs(1) + def defaultExplanation = + "Note that implicit conversions are not applicable because they are ambiguous:\n "+ + coreMsg+"are possible conversion functions from "+ found+" to "+req - /** A nice spot to explain some common situations a little - * less confusingly. - */ def explanation = { - if ((found =:= AnyClass.tpe) && (AnyRefClass.tpe <:< req)) - "Note: Any is not implicitly converted to AnyRef. You can safely\n" + - "pattern match x: AnyRef or cast x.asInstanceOf[AnyRef] to do so." - else if ((found <:< AnyValClass.tpe) && (AnyRefClass.tpe <:< req)) - "Note: primitive types are not implicitly converted to AnyRef.\n" + - "You can safely force boxing by casting x.asInstanceOf[AnyRef]." - else - "Note that implicit conversions are not applicable because they are ambiguous:\n "+ - coreMsg+"are possible conversion functions from "+ found+" to "+req + val sym = found.typeSymbol + // Explain some common situations a bit more clearly. + if (AnyRefClass.tpe <:< req) { + if (sym == AnyClass || sym == UnitClass) { + "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + + "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." + } + else boxedClass get sym match { + case Some(boxed) => + "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + + "methods inherited from Object are rendered ambiguous. This is to avoid\n" + + "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + + "You may wish to use a type ascription: `x: " + boxed.fullName + "`." + case _ => + defaultExplanation + } + } + else defaultExplanation } typeErrorMsg(found, req) + "\n" + explanation @@ -350,7 +377,6 @@ trait Implicits { /** The type parameters to instantiate */ val undetParams = if (isView) List() else context.outer.undetparams - /** Replace undetParams in type `tp` by Any/Nothing, according to variance */ def approximate(tp: Type) = if (undetParams.isEmpty) tp else tp.instantiateTypeParams(undetParams, undetParams map (_ => WildcardType)) @@ -364,7 +390,8 @@ trait Implicits { * @param info The given implicit info describing the implicit definition * @pre <code>info.tpe</code> does not contain an error */ - private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = + private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { + printInference("[typedImplicit] " + info) (context.openImplicits find { case (tp, sym) => sym == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => // println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG @@ -390,6 +417,7 @@ trait Implicits { context.openImplicits = context.openImplicits.tail } } + } /** Todo reconcile with definition of stability given in Types.scala */ private def isStable(tp: Type): Boolean = tp match { @@ -443,9 +471,23 @@ trait Implicits { private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { incCounter(plausiblyCompatibleImplicits) - - printTyping("typed impl for "+wildPt+"? "+info.name +":"+ depoly(info.tpe)+ " orig info= "+ info.tpe +"/"+undetParams+"/"+isPlausiblyCompatible(info.tpe, wildPt)+"/"+matchesPt(depoly(info.tpe), wildPt, List())+"/"+info.pre+"/"+isStable(info.pre)) - if (ptChecked || matchesPt(depoly(info.tpe), wildPt, List()) && isStable(info.pre)) + printTyping( + ptBlock("typedImplicit0", + "info.name" -> info.name, + "info.tpe" -> depoly(info.tpe), + "ptChecked" -> ptChecked, + "pt" -> wildPt, + "orig" -> ptBlock("info", + "matchesPt" -> matchesPt(depoly(info.tpe), wildPt, Nil), + "undetParams" -> undetParams, + "isPlausiblyCompatible" -> isPlausiblyCompatible(info.tpe, wildPt), + "info.pre" -> info.pre, + "isStable" -> isStable(info.pre) + ).replaceAll("\\n", "\n ") + ) + ) + + if (ptChecked || matchesPt(depoly(info.tpe), wildPt, Nil) && isStable(info.pre)) typedImplicit1(info) else SearchFailure @@ -458,7 +500,10 @@ trait Implicits { if (info.pre == NoPrefix) Ident(info.name) else Select(gen.mkAttributedQualifier(info.pre), info.name) } - printTyping("typedImplicit0 typing"+ itree +" with wildpt = "+ wildPt +" from implicit "+ info.name+":"+info.tpe) + printTyping("typedImplicit1 %s, pt=%s, from implicit %s:%s".format( + typeDebug.ptTree(itree), wildPt, info.name, info.tpe) + ) + def fail(reason: String): SearchResult = { if (settings.XlogImplicits.value) inform(itree+" is not a valid implicit value for "+pt+" because:\n"+reason) @@ -479,10 +524,14 @@ trait Implicits { incCounter(typedImplicits) - printTyping("typed implicit "+itree1+":"+itree1.tpe+", pt = "+wildPt) + printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun } else adapt(itree1, EXPRmode, wildPt) - printTyping("adapted implicit "+itree1.symbol+":"+itree2.tpe+" to "+wildPt) + + printTyping("adapted implicit %s:%s to %s".format( + itree1.symbol, itree2.tpe, wildPt) + ) + def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || { tree match { case Apply(fun, _) => hasMatchingSymbol(fun) @@ -492,11 +541,26 @@ trait Implicits { } } - if (itree2.tpe.isError) SearchFailure - else if (hasMatchingSymbol(itree1)) { + if (itree2.tpe.isError) + SearchFailure + else if (!hasMatchingSymbol(itree1)) + fail("candidate implicit %s is shadowed by other implicit %s".format( + info.sym + info.sym.locationString, itree1.symbol + itree1.symbol.locationString)) + else { val tvars = undetParams map freshVar + if (matchesPt(itree2.tpe, pt.instantiateTypeParams(undetParams, tvars), undetParams)) { - printTyping("tvars = "+tvars+"/"+(tvars map (_.constr))) + printInference( + ptBlock("matchesPt", + "itree1" -> itree1, + "tvars" -> tvars, + "undetParams" -> undetParams + ) + ) + + if (tvars.nonEmpty) + printTyping(ptLine("" + info.sym, "tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) + val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), false, lubDepth(List(itree2.tpe, pt))) @@ -505,39 +569,42 @@ trait Implicits { // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams - val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs) // prototype == WildcardType: want to remove all inferred Nothing's - var subst = EmptyTreeTypeSubstituter - if (okParams.nonEmpty) { - subst = new TreeTypeSubstituter(okParams, okArgs) - subst traverse itree2 - } + // prototype == WildcardType: want to remove all inferred Nothings + val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs) + val subst: TreeTypeSubstituter = + if (okParams.isEmpty) EmptyTreeTypeSubstituter + else { + val subst = new TreeTypeSubstituter(okParams, okArgs) + subst traverse itree2 + subst + } - // #2421b: since type inference (which may have been performed during implicit search) - // does not check whether inferred arguments meet the bounds of the corresponding parameter (see note in solvedTypes), - // must check again here: - // TODO: I would prefer to just call typed instead of duplicating the code here, but this is probably a hotspot (and you can't just call typed, need to force re-typecheck) + // #2421b: since type inference (which may have been + // performed during implicit search) does not check whether + // inferred arguments meet the bounds of the corresponding + // parameter (see note in solvedTypes), must check again + // here: + // TODO: I would prefer to just call typed instead of + // duplicating the code here, but this is probably a + // hotspot (and you can't just call typed, need to force + // re-typecheck) + // TODO: the return tree is ignored. This seems to make + // no difference, but it's bad practice regardless. itree2 match { - case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) + case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c - case _ => + case t => t } - val result = new SearchResult(itree2, subst) incCounter(foundImplicits) - if (traceImplicits) println("RESULT = "+result) - // println("RESULT = "+itree+"///"+itree1+"///"+itree2)//DEBUG + printInference("[typedImplicit1] SearchResult: " + result) result - } else { - printTyping("incompatible: "+itree2.tpe+" does not match "+pt.instantiateTypeParams(undetParams, tvars)) - - SearchFailure } + else fail("incompatible: %s does not match expected type %s".format( + itree2.tpe, pt.instantiateTypeParams(undetParams, tvars))) } - else if (settings.XlogImplicits.value) - fail("candidate implicit "+info.sym+info.sym.locationString+ - " is shadowed by other implicit: "+itree1.symbol+itree1.symbol.locationString) - else SearchFailure - } catch { + } + catch { case ex: TypeError => fail(ex.getMessage()) } } @@ -655,6 +722,16 @@ trait Implicits { // most frequent one first matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg) } + def eligibleString = { + val args = List( + "search" -> pt, + "target" -> tree, + "isView" -> isView + ) ++ eligible.map("eligible" -> _) + + ptBlock("Implicit search in " + context, args: _*) + } + printInference(eligibleString) /** Faster implicit search. Overall idea: * - prune aggressively @@ -855,7 +932,7 @@ trait Implicits { val infoMap = new InfoMap getParts(tp)(infoMap, new mutable.HashSet(), Set()) - if (traceImplicits) println("companion implicits of "+tp+" = "+infoMap) + printInference("[companionImplicitMap] "+tp+" = "+infoMap) infoMap } @@ -996,7 +1073,7 @@ trait Implicits { inferImplicit(tree, appliedType(manifestClass.typeConstructor, List(tp)), true, false, context).tree def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass) - def mot(tp0: Type)(implicit from: List[Symbol] = List(), to: List[Type] = List()): SearchResult = { + def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = { implicit def wrapResult(tree: Tree): SearchResult = if (tree == EmptyTree) SearchFailure else new SearchResult(tree, new TreeTypeSubstituter(from, to)) @@ -1032,24 +1109,29 @@ trait Implicits { } else if (sym.isExistentiallyBound && full) { manifestFactoryCall("wildcardType", tp, findManifest(tp.bounds.lo), findManifest(tp.bounds.hi)) - } else if(undetParams contains sym) { // looking for a manifest of a type parameter that hasn't been inferred by now, can't do much, but let's not fail - mot(NothingClass.tpe)(sym :: from, NothingClass.tpe :: to) // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult + } + // looking for a manifest of a type parameter that hasn't been inferred by now, + // can't do much, but let's not fail + else if (undetParams contains sym) { + // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult + mot(NothingClass.tpe, sym :: from, NothingClass.tpe :: to) } else { - EmptyTree // a manifest should have been found by normal searchImplicit + // a manifest should have been found by normal searchImplicit + EmptyTree } case RefinedType(parents, decls) => // refinement is not generated yet if (hasLength(parents, 1)) findManifest(parents.head) - else if (full) manifestFactoryCall("intersectionType", tp, parents map (findSubManifest(_)): _*) - else mot(erasure.erasure.intersectionDominator(parents)) + else if (full) manifestFactoryCall("intersectionType", tp, parents map findSubManifest: _*) + else mot(erasure.erasure.intersectionDominator(parents), from, to) case ExistentialType(tparams, result) => - mot(tp1.skolemizeExistential) + mot(tp1.skolemizeExistential, from, to) case _ => EmptyTree } } - mot(tp) + mot(tp, Nil, Nil) } def wrapResult(tree: Tree): SearchResult = |