package dotty.tools package dotc package typer import core._ import ast.{Trees, untpd, tpd, TreeInfo} import util.Positions._ import util.Stats.{track, record, monitored} import printing.{Showable, Printer} import printing.Texts._ import Contexts._ import Types._ import Flags._ import TypeErasure.{erasure, hasStableErasure} import Mode.ImplicitsEnabled import Denotations._ import NameOps._ import SymDenotations._ import Symbols._ import Types._ import Decorators._ import Names._ import StdNames._ import Constants._ import Applications._ import ProtoTypes._ import ErrorReporting._ import reporting.diagnostic.MessageContainer import Inferencing.fullyDefinedType import Trees._ import Hashable._ import util.Property import config.Config import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable /** Implicit resolution */ object Implicits { /** A reference to an implicit value to be made visible on the next nested call to * inferImplicitArg with a by-name expected type. */ val DelayedImplicit = new Property.Key[TermRef] /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ case class Candidate(ref: TermRef, level: Int) /** A common base class of contextual implicits and of-type implicits which * represents a set of implicit references. */ abstract class ImplicitRefs(initctx: Context) { implicit val ctx: Context = if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled /** The nesting level of this context. Non-zero only in ContextialImplicits */ def level: Int = 0 /** The implicit references */ def refs: List[TermRef] /** Return those references in `refs` that are compatible with type `pt`. */ protected def filterMatching(pt: Type)(implicit ctx: Context): List[Candidate] = track("filterMatching") { def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ { def discardForView(tpw: Type, argType: Type): Boolean = tpw match { case mt: MethodType => mt.isImplicit || mt.paramInfos.length != 1 || !(argType relaxed_<:< mt.paramInfos.head)(ctx.fresh.setExploreTyperState) case poly: PolyType => // We do not need to call ProtoTypes#constrained on `poly` because // `refMatches` is always called with mode TypevarsMissContext enabled. poly.resultType match { case mt: MethodType => mt.isImplicit || mt.paramInfos.length != 1 || !(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty)(ctx.fresh.setExploreTyperState)) case rtp => discardForView(wildApprox(rtp, null, Set.empty), argType) } case tpw: TermRef => false // can't discard overloaded refs case tpw => // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views. // However, Predef.$conforms is not eligible, because it is a no-op. // // In principle, it would be cleanest if only implicit methods qualified // as implicit conversions. We could achieve that by having standard conversions like // this in Predef: // // implicit def convertIfConforms[A, B](x: A)(implicit ev: A <:< B): B = ev(a) // implicit def convertIfConverter[A, B](x: A)(implicit ev: ImplicitConverter[A, B]): B = ev(a) // // (Once `<:<` inherits from `ImplicitConverter` we only need the 2nd one.) // But clauses like this currently slow down implicit search a lot, because // they are eligible for all pairs of types, and therefore are tried too often. // We emulate instead these conversions directly in the search. // The reason for leaving out `Predef_conforms` is that we know it adds // nothing since it only relates subtype with supertype. // // We keep the old behavior under -language:Scala2. val isFunctionInS2 = ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1)) val isImplicitConverter = tpw.derivesFrom(defn.Predef_ImplicitConverter) val isConforms = tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms !(isFunctionInS2 || isImplicitConverter || isConforms) } def discardForValueType(tpw: Type): Boolean = tpw.stripPoly match { case tpw: MethodType => !tpw.isImplicit case _ => false } def discard = pt match { case pt: ViewProto => discardForView(ref.widen, pt.argType) case _: ValueTypeOrProto => !defn.isFunctionType(pt) && discardForValueType(ref.widen) case _ => false } (ref.symbol isAccessibleFrom ref.prefix) && { if (discard) { record("discarded eligible") false } else NoViewsAllowed.isCompatible(normalize(ref, pt), pt) } } if (refs.isEmpty) Nil else refs.filter(refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution .map(Candidate(_, level)) } } /** The implicit references coming from the implicit scope of a type. * @param tp the type determining the implicit scope * @param companionRefs the companion objects in the implicit scope. */ class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { assert(initctx.typer != null) lazy val refs: List[TermRef] = { val buf = new mutable.ListBuffer[TermRef] for (companion <- companionRefs) buf ++= companion.implicitMembers buf.toList } /** The candidates that are eligible for expected type `tp` */ lazy val eligible: List[Candidate] = /*>|>*/ track("eligible in tpe") /*<|<*/ { /*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ { if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length) filterMatching(tp) } } override def toString = i"OfTypeImplicits($tp), companions = ${companionRefs.toList}%, %; refs = $refs%, %." } /** The implicit references coming from the context. * @param refs the implicit references made visible by the current context. * Note: The name of the reference might be different from the name of its symbol. * In the case of a renaming import a => b, the name of the reference is the renamed * name, b, whereas the name of the symbol is the original name, a. * @param outerCtx the next outer context that makes visible further implicits */ class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) { private val eligibleCache = new mutable.AnyRefMap[Type, List[Candidate]] /** The level increases if current context has a different owner or scope than * the context of the next-outer ImplicitRefs. This is however disabled under * Scala2 mode, since we do not want to change the implicit disambiguation then. */ override val level: Int = if (outerImplicits == null) 1 else if (ctx.scala2Mode || (ctx.owner eq outerImplicits.ctx.owner) && (ctx.scope eq outerImplicits.ctx.scope)) outerImplicits.level else outerImplicits.level + 1 /** Is this the outermost implicits? This is the case if it either the implicits * of NoContext, or the last one before it. */ private def isOuterMost = { val finalImplicits = NoContext.implicits (this eq finalImplicits) || (outerImplicits eq finalImplicits) } /** The implicit references that are eligible for type `tp`. */ def eligible(tp: Type): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { if (tp.hash == NotCached) computeEligible(tp) else eligibleCache get tp match { case Some(eligibles) => def elided(ci: ContextualImplicits): Int = { val n = ci.refs.length if (ci.isOuterMost) n else n + elided(ci.outerImplicits) } if (monitored) record(s"elided eligible refs", elided(this)) eligibles case None => if (ctx eq NoContext) Nil else { val savedEphemeral = ctx.typerState.ephemeral ctx.typerState.ephemeral = false try { val result = computeEligible(tp) if (ctx.typerState.ephemeral) record("ephemeral cache miss: eligible") else eligibleCache(tp) = result result } finally ctx.typerState.ephemeral |= savedEphemeral } } } private def computeEligible(tp: Type): List[Candidate] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { if (monitored) record(s"check eligible refs in ctx", refs.length) val ownEligible = filterMatching(tp) if (isOuterMost) ownEligible else ownEligible ::: { val shadowed = ownEligible.map(_.ref.name).toSet outerImplicits.eligible(tp).filterNot(cand => shadowed.contains(cand.ref.name)) } } override def toString = { val own = i"(implicits: $refs%, %)" if (isOuterMost) own else own + "\n " + outerImplicits } /** This context, or a copy, ensuring root import from symbol `root` * is not present in outer implicits. */ def exclude(root: Symbol): ContextualImplicits = if (this == NoContext.implicits) this else { val outerExcluded = outerImplicits exclude root if (ctx.importInfo.site.termSymbol == root) outerExcluded else if (outerExcluded eq outerImplicits) this else new ContextualImplicits(refs, outerExcluded)(ctx) } } /** The result of an implicit search */ sealed abstract class SearchResult extends Showable { def toText(printer: Printer): Text = printer.toText(this) } /** A successful search * @param ref The implicit reference that succeeded * @param tree The typed tree that needs to be inserted * @param ctx The context after the implicit search */ case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult with Showable { override def toString = s"SearchSuccess($tree, $ref, $level)" } /** A failed search */ abstract class SearchFailure extends SearchResult { /** A note describing the failure in more detail - this * is either empty or starts with a '\n' */ def postscript(implicit ctx: Context): String = "" } /** A "no matching implicit found" failure */ case object NoImplicitMatches extends SearchFailure case object DivergingImplicit extends SearchFailure /** A search failure that can show information about the cause */ abstract class ExplainedSearchFailure extends SearchFailure { protected def pt: Type protected def argument: tpd.Tree protected def qualify(implicit ctx: Context) = if (argument.isEmpty) em"match type $pt" else em"convert from ${argument.tpe} to $pt" /** An explanation of the cause of the failure as a string */ def explanation(implicit ctx: Context): String } /** An ambiguous implicits failure */ class AmbiguousImplicits(val alt1: TermRef, val alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { def explanation(implicit ctx: Context): String = em"both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify" override def postscript(implicit ctx: Context) = "\nNote that implicit conversions cannot be applied because they are ambiguous;" + "\n " + explanation } class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree, trail: List[MessageContainer]) extends ExplainedSearchFailure { private val separator = "\n**** because ****\n" /** Replace repeated parts beginning with `separator` by ... */ private def elideRepeated(str: String): String = { val startIdx = str.indexOfSlice(separator) val nextIdx = str.indexOfSlice(separator, startIdx + separator.length) if (nextIdx < 0) str else { val prefix = str.take(startIdx) val first = str.slice(startIdx, nextIdx) var rest = str.drop(nextIdx) if (rest.startsWith(first)) { rest = rest.drop(first.length) val dots = "\n\n ...\n" if (!rest.startsWith(dots)) rest = dots ++ rest } prefix ++ first ++ rest } } def explanation(implicit ctx: Context): String = { val headMsg = em"${err.refStr(ref)} does not $qualify" val trailMsg = trail.map(mc => i"$separator ${mc.message}").mkString elideRepeated(headMsg ++ trailMsg) } } class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { def explanation(implicit ctx: Context): String = em"${err.refStr(ref)} does $qualify but is shadowed by ${err.refStr(shadowing)}" } class DivergingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { def explanation(implicit ctx: Context): String = em"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" } class FailedImplicit(failures: List[ExplainedSearchFailure], val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { def explanation(implicit ctx: Context): String = if (failures.isEmpty) s" No implicit candidates were found that $qualify" else " " + (failures map (_.explanation) mkString "\n ") override def postscript(implicit ctx: Context): String = i""" |Implicit search failure summary: |$explanation""" } } import Implicits._ /** Info relating to implicits that is kept for one run */ trait ImplicitRunInfo { self: RunInfo => private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() /** The implicit scope of a type `tp` * @param liftingCtx A context to be used when computing the class symbols of * a type. Types may contain type variables with their instances * recorded in the current context. To find out the instance of * a type variable, we need the current context, the current * runinfo context does not do. */ def implicitScope(rootTp: Type, liftingCtx: Context): OfTypeImplicits = { val seen: mutable.Set[Type] = mutable.Set() val incomplete: mutable.Set[Type] = mutable.Set() /** Replace every typeref that does not refer to a class by a conjunction of class types * that has the same implicit scope as the original typeref. The motivation for applying * this map is that it reduces the total number of types for which we need to * compute and cache the implicit scope; all variations wrt type parameters or * abstract types are eliminated. */ object liftToClasses extends TypeMap { override implicit protected val ctx: Context = liftingCtx override def stopAtStatic = true def apply(tp: Type) = tp match { case tp: TypeRef if tp.symbol.isAbstractOrAliasType => val pre = tp.prefix def joinClass(tp: Type, cls: ClassSymbol) = AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner)) val lead = if (tp.prefix eq NoPrefix) defn.AnyType else apply(tp.prefix) (lead /: tp.classSymbols)(joinClass) case tp: TypeVar => apply(tp.underlying) case tp: HKApply => def applyArg(arg: Type) = arg match { case TypeBounds(lo, hi) => AndType.make(lo, hi) case _: WildcardType => defn.AnyType case _ => arg } (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg))) case tp: TypeLambda => apply(tp.resType) case _ => mapOver(tp) } } // todo: compute implicits directly, without going via companionRefs? def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") { ctx.traceIndented(i"collectCompanions($tp)", implicits) { def iscopeRefs(t: Type): TermRefSet = implicitScopeCache.get(t) match { case Some(is) => is.companionRefs case None => if (seen contains t) { incomplete += tp // all references to rootTo will be accounted for in `seen` so we return `EmptySet`. EmptyTermRefSet // on the other hand, the refs of `tp` are now not accurate, so `tp` is marked incomplete. } else { seen += t val is = iscope(t) if (!implicitScopeCache.contains(t)) incomplete += tp is.companionRefs } } val comps = new TermRefSet tp match { case tp: NamedType => val pre = tp.prefix comps ++= iscopeRefs(pre) def addClassScope(cls: ClassSymbol): Unit = { def addRef(companion: TermRef): Unit = { val compSym = companion.symbol if (compSym is Package) addRef(TermRef.withSig(companion, nme.PACKAGE, Signature.NotAMethod)) else if (compSym.exists) comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] } def addParentScope(parent: TypeRef): Unit = { iscopeRefs(parent) foreach addRef for (param <- parent.typeParamSymbols) comps ++= iscopeRefs(tp.member(param.name).info) } val companion = cls.companionModule if (companion.exists) addRef(companion.valRef) cls.classParents foreach addParentScope } tp.classSymbols(liftingCtx) foreach addClassScope case _ => // We exclude lower bounds to conform to SLS 7.2: // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound" for (part <- tp.namedPartsWith(_.isType, excludeLowerBounds = true)) comps ++= iscopeRefs(part) } comps } } /** The implicit scope of type `tp` * @param isLifted Type `tp` is the result of a `liftToClasses` application */ def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = { val canCache = Config.cacheImplicitScopes && tp.hash != NotCached def computeIScope() = { val savedEphemeral = ctx.typerState.ephemeral ctx.typerState.ephemeral = false try { val liftedTp = if (isLifted) tp else liftToClasses(tp) val refs = if (liftedTp ne tp) iscope(liftedTp, isLifted = true).companionRefs else collectCompanions(tp) val result = new OfTypeImplicits(tp, refs)(ctx) if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") else if (canCache && ((tp eq rootTp) || // first type traversed is always cached !incomplete.contains(tp))) // other types are cached if they are not incomplete implicitScopeCache(tp) = result result } finally ctx.typerState.ephemeral |= savedEphemeral } if (canCache) implicitScopeCache.getOrElse(tp, computeIScope()) else computeIScope() } iscope(rootTp) } /** A map that counts the number of times an implicit ref was picked */ val useCount = new mutable.HashMap[TermRef, Int] { override def default(key: TermRef) = 0 } def clear() = implicitScopeCache.clear() } /** The implicit resolution part of type checking */ trait Implicits { self: Typer => import tpd._ override def viewExists(from: Type, to: Type)(implicit ctx: Context): Boolean = ( !from.isError && !to.isError && !ctx.isAfterTyper && (ctx.mode is Mode.ImplicitsEnabled) && from.isValueType && ( from.isValueSubType(to) || inferView(dummyTreeOfType(from), to) (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState) .isInstanceOf[SearchSuccess] ) ) /** Find an implicit conversion to apply to given tree `from` so that the * result is compatible with type `to`. */ def inferView(from: Tree, to: Type)(implicit ctx: Context): SearchResult = track("inferView") { if ( (to isRef defn.AnyClass) || (to isRef defn.ObjectClass) || (to isRef defn.UnitClass) || (from.tpe isRef defn.NothingClass) || (from.tpe isRef defn.NullClass) || !(ctx.mode is Mode.ImplicitsEnabled) || (from.tpe eq NoPrefix)) NoImplicitMatches else { def adjust(to: Type) = to.stripTypeVar.widenExpr match { case SelectionProto(name, memberProto, compat, true) => SelectionProto(name, memberProto, compat, privateOK = false) case tp => tp } try inferImplicit(adjust(to), from, from.pos) catch { case ex: AssertionError => implicits.println(s"view $from ==> $to") implicits.println(ctx.typerState.constraint.show) implicits.println(TypeComparer.explained(implicit ctx => from.tpe <:< to)) throw ex } } } /** Find an implicit argument for parameter `formal`. * @param error An error handler that gets an error message parameter * which is itself parameterized by another string, * indicating where the implicit parameter is needed */ def inferImplicitArg(formal: Type, error: (String => String) => Unit, pos: Position)(implicit ctx: Context): Tree = { /** If `formal` is of the form ClassTag[T], where `T` is a class type, * synthesize a class tag for `T`. */ def synthesizedClassTag(formal: Type, pos: Position)(implicit ctx: Context): Tree = { if (formal.isRef(defn.ClassTagClass)) formal.argTypes match { case arg :: Nil => fullyDefinedType(arg, "ClassTag argument", pos) match { case defn.ArrayOf(elemTp) => val etag = inferImplicitArg(defn.ClassTagType.appliedTo(elemTp), error, pos) if (etag.isEmpty) etag else etag.select(nme.wrap) case tp if hasStableErasure(tp) => if (defn.isBottomClass(tp.typeSymbol)) error(where => i"attempt to take ClassTag of undetermined type for $where") ref(defn.ClassTagModule) .select(nme.apply) .appliedToType(tp) .appliedTo(clsOf(erasure(tp))) .withPos(pos) case tp => EmptyTree } case _ => EmptyTree } else EmptyTree } /** The context to be used when resolving a by-name implicit argument. * This makes any implicit stored under `DelayedImplicit` visible and * stores in turn the given `lazyImplicit` as new `DelayedImplicit`. */ def lazyImplicitCtx(lazyImplicit: Symbol): Context = { val lctx = ctx.fresh for (delayedRef <- ctx.property(DelayedImplicit)) lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx)) lctx.setProperty(DelayedImplicit, lazyImplicit.termRef) } /** formalValue: The value type for which an implicit is searched * lazyImplicit: An implicit symbol to install for nested by-name resolutions * argCtx : The context to be used for searching the implicit argument */ val (formalValue, lazyImplicit, argCtx) = formal match { case ExprType(fv) => val lazyImplicit = ctx.newLazyImplicit(fv) (fv, lazyImplicit, lazyImplicitCtx(lazyImplicit)) case _ => (formal, NoSymbol, ctx) } inferImplicit(formalValue, EmptyTree, pos)(argCtx) match { case SearchSuccess(arg, _, _, _) => def refersToLazyImplicit = arg.existsSubTree { case id: Ident => id.symbol == lazyImplicit case _ => false } if (lazyImplicit.exists && refersToLazyImplicit) Block(ValDef(lazyImplicit.asTerm, arg).withPos(pos) :: Nil, ref(lazyImplicit)) else arg case ambi: AmbiguousImplicits => error(where => s"ambiguous implicits: ${ambi.explanation} of $where") EmptyTree case failure: SearchFailure => val arg = synthesizedClassTag(formalValue, pos) if (!arg.isEmpty) arg else { var msgFn = (where: String) => em"no implicit argument of type $formal found for $where" + failure.postscript for { notFound <- formalValue.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot) Trees.Literal(Constant(raw: String)) <- notFound.argument(0) } { msgFn = where => err.implicitNotFoundString( raw, formalValue.typeSymbol.typeParams.map(_.name.unexpandedName.toString), formalValue.argInfos) } error(msgFn) EmptyTree } } } private def assumedCanEqual(ltp: Type, rtp: Type)(implicit ctx: Context) = { def eqNullable: Boolean = { val other = if (ltp.isRef(defn.NullClass)) rtp else if (rtp.isRef(defn.NullClass)) ltp else NoType (other ne NoType) && !other.derivesFrom(defn.AnyValClass) } val lift = new TypeMap { def apply(t: Type) = t match { case t: TypeRef => t.info match { case TypeBounds(lo, hi) if lo ne hi => hi case _ => t } case _ => if (variance > 0) mapOver(t) else t } } ltp.isError || rtp.isError || ltp <:< lift(rtp) || rtp <:< lift(ltp) || eqNullable } /** Check that equality tests between types `ltp` and `rtp` make sense */ def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = if (!ctx.isAfterTyper && !assumedCanEqual(ltp, rtp)) { val res = inferImplicitArg( defn.EqType.appliedTo(ltp, rtp), msgFun => ctx.error(msgFun(""), pos), pos) implicits.println(i"Eq witness found: $res: ${res.tpe}") } /** Find an implicit parameter or conversion. * @param pt The expected type of the parameter or conversion. * @param argument If an implicit conversion is searched, the argument to which * it should be applied, EmptyTree otherwise. * @param pos The position where errors should be reported. * !!! todo: catch potential cycles */ def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") { assert(!ctx.isAfterTyper, if (argument.isEmpty) i"missing implicit parameter of type $pt after typer" else i"type error: ${argument.tpe} does not conform to $pt${err.whyNoMatchStr(argument.tpe, pt)}") val prevConstr = ctx.typerState.constraint ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) { assert(!pt.isInstanceOf[ExprType]) val isearch = if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos) else new ImplicitSearch(pt, argument, pos) val result = isearch.bestImplicit result match { case result: SearchSuccess => result.tstate.commit() implicits.println(i"committing ${result.tstate.constraint} yielding ${ctx.typerState.constraint} ${ctx.typerState.hashesStr}") result case result: AmbiguousImplicits => val deepPt = pt.deepenProto if (deepPt ne pt) inferImplicit(deepPt, argument, pos) else if (ctx.scala2Mode && !ctx.mode.is(Mode.OldOverloadingResolution)) { inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match { case altResult: SearchSuccess => ctx.migrationWarning( s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}", pos) altResult case _ => result } } else result case _ => assert(prevConstr eq ctx.typerState.constraint) result } } } /** An implicit search; parameters as in `inferImplicit` */ class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) { private def nestedContext = ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled) private def implicitProto(resultType: Type, f: Type => Type) = if (argument.isEmpty) f(resultType) else ViewProto(f(argument.tpe.widen), f(resultType)) // Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though. assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType], em"found: $argument: ${argument.tpe}, expected: $pt") /** The expected type for the searched implicit */ lazy val fullProto = implicitProto(pt, identity) lazy val funProto = fullProto match { case proto: ViewProto => FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self) case proto => proto } /** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */ val wildProto = implicitProto(pt, wildApprox(_, null, Set.empty)) /** Search failures; overridden in ExplainedImplicitSearch */ protected def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]): SearchFailure = NoImplicitMatches protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches protected def failedSearch: SearchFailure = NoImplicitMatches /** Search a list of eligible implicit references */ def searchImplicits(eligible: List[Candidate], contextual: Boolean): SearchResult = { val constr = ctx.typerState.constraint /** Try to typecheck an implicit reference */ def typedImplicit(cand: Candidate)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { assert(constr eq ctx.typerState.constraint) val ref = cand.ref var generated: Tree = tpd.ref(ref).withPos(pos) if (!argument.isEmpty) generated = typedUnadapted( untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil), pt) val generated1 = adapt(generated, pt) lazy val shadowing = typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)( nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState) def refMatches(shadowing: Tree): Boolean = ref.symbol == closureBody(shadowing).symbol || { shadowing match { case Trees.Select(qual, nme.apply) => refMatches(qual) case _ => false } } // Does there exist an implicit value of type `Eq[tp, tp]`? def hasEq(tp: Type): Boolean = new ImplicitSearch(defn.EqType.appliedTo(tp, tp), EmptyTree, pos).bestImplicit match { case result: SearchSuccess => result.ref.symbol != defn.Predef_eqAny case result: AmbiguousImplicits => true case _ => false } def validEqAnyArgs(tp1: Type, tp2: Type) = { List(tp1, tp2).foreach(fullyDefinedType(_, "eqAny argument", pos)) assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) || { implicits.println(i"invalid eqAny[$tp1, $tp2]"); false } } if (ctx.reporter.hasErrors) nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages) else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && !shadowing.tpe.isError && !refMatches(shadowing)) { implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") shadowedImplicit(ref, methPart(shadowing).tpe) } else generated1 match { case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil)) if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) => nonMatchingImplicit(ref, Nil) case _ => SearchSuccess(generated1, ref, cand.level, ctx.typerState) } }} /** Given a list of implicit references, produce a list of all implicit search successes, * where the first is supposed to be the best one. * @param pending The list of implicit references that remain to be investigated * @param acc An accumulator of successful matches found so far. */ def rankImplicits(pending: List[Candidate], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { case cand :: pending1 => val history = ctx.searchHistory nest wildProto val result = if (history eq ctx.searchHistory) divergingImplicit(cand.ref) else typedImplicit(cand)(nestedContext.setNewTyperState.setSearchHistory(history)) result match { case fail: SearchFailure => rankImplicits(pending1, acc) case best: SearchSuccess => if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil else { val newPending = pending1.filter(cand1 => isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext.setExploreTyperState)) rankImplicits(newPending, best :: acc) } } case nil => acc } /** If the (result types of) the expected type, and both alternatives * are all numeric value types, return the alternative which has * the smaller numeric subtype as result type, if it exists. * (This alternative is then discarded). */ def numericValueTieBreak(alt1: SearchSuccess, alt2: SearchSuccess): SearchResult = { def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass def isProperSubType(tp1: Type, tp2: Type) = tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1) val rpt = pt.resultType val rt1 = alt1.ref.widen.resultType val rt2 = alt2.ref.widen.resultType if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2)) if (isProperSubType(rt1, rt2)) alt1 else if (isProperSubType(rt2, rt1)) alt2 else NoImplicitMatches else NoImplicitMatches } /** Convert a (possibly empty) list of search successes into a single search result */ def condense(hits: List[SearchSuccess]): SearchResult = hits match { case best :: alts => alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match { case Some(alt) => typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}") /* !!! DEBUG println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) */ numericValueTieBreak(best, alt) match { case eliminated: SearchSuccess => condense(hits.filter(_ ne eliminated)) case _ => new AmbiguousImplicits(best.ref, alt.ref, pt, argument) } case None => ctx.runInfo.useCount(best.ref) += 1 best } case Nil => failedSearch } def ranking(cand: Candidate) = -ctx.runInfo.useCount(cand.ref) /** Sort list of implicit references according to their popularity * (# of times each was picked in current run). */ def sort(eligible: List[Candidate]) = eligible match { case Nil => eligible case e1 :: Nil => eligible case e1 :: e2 :: Nil => if (ranking(e2) < ranking(e1)) e2 :: e1 :: Nil else eligible case _ => eligible.sortBy(ranking) } condense(rankImplicits(sort(eligible), Nil)) } /** Find a unique best implicit reference */ def bestImplicit: SearchResult = { searchImplicits(ctx.implicits.eligible(wildProto), contextual = true) match { case result: SearchSuccess => result case result: AmbiguousImplicits => result case result: SearchFailure => searchImplicits(implicitScope(wildProto).eligible, contextual = false) } } def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx) } final class ExplainedImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context) extends ImplicitSearch(pt, argument, pos) { private var myFailures = new mutable.ListBuffer[ExplainedSearchFailure] private def record(fail: ExplainedSearchFailure) = { myFailures += fail fail } def failures = myFailures.toList override def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]) = record(new NonMatchingImplicit(ref, pt, argument, trail)) override def divergingImplicit(ref: TermRef) = record(new DivergingImplicit(ref, pt, argument)) override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = record(new ShadowedImplicit(ref, shadowing, pt, argument)) override def failedSearch: SearchFailure = { //println(s"wildProto = $wildProto") //println(s"implicit scope = ${implicitScope(wildProto).companionRefs}") new FailedImplicit(failures, pt, argument) } } } /** Records the history of currently open implicit searches * @param searchDepth The number of open searches. * @param seen A map that records for each class symbol of a type * that's currently searched for the complexity of the * type that is searched for (wrt `typeSize`). The map * is populated only once `searchDepth` is greater than * the threshold given in the `XminImplicitSearchDepth` setting. */ class SearchHistory(val searchDepth: Int, val seen: Map[ClassSymbol, Int]) { /** The number of RefinementTypes in this type, after all aliases are expanded */ private def typeSize(tp: Type)(implicit ctx: Context): Int = { val accu = new TypeAccumulator[Int] { def apply(n: Int, tp: Type): Int = tp match { case tp: RefinedType => foldOver(n + 1, tp) case tp: TypeRef if tp.info.isAlias => apply(n, tp.superType) case _ => foldOver(n, tp) } } accu.apply(0, tp) } /** Check for possible divergence. If one is detected return the current search history * (this will be used as a criterion to abandon the implicit search in rankImplicits). * If no divergence is detected, produce a new search history nested in the current one * which records that we are now also looking for type `proto`. * * As long as `searchDepth` is lower than the `XminImplicitSearchDepth` value * in settings, a new history is always produced, so the implicit search is always * undertaken. If `searchDepth` matches or exceeds the `XminImplicitSearchDepth` value, * we test that the new search is for a class that is either not yet in the set of * `seen` classes, or the complexity of the type `proto` being searched for is strictly * lower than the complexity of the type that was previously encountered and that had * the same class symbol as `proto`. A possible divergence is detected if that test fails. */ def nest(proto: Type)(implicit ctx: Context): SearchHistory = { if (searchDepth < ctx.settings.XminImplicitSearchDepth.value) new SearchHistory(searchDepth + 1, seen) else { val size = typeSize(proto) def updateMap(csyms: List[ClassSymbol], seen: Map[ClassSymbol, Int]): SearchHistory = csyms match { case csym :: csyms1 => seen get csym match { // proto complexity is >= than the last time it was seen → diverge case Some(prevSize) if size >= prevSize => this case _ => updateMap(csyms1, seen.updated(csym, size)) } case _ => new SearchHistory(searchDepth + 1, seen) } if (proto.classSymbols.isEmpty) this else updateMap(proto.classSymbols, seen) } } } /** A set of term references where equality is =:= */ class TermRefSet(implicit ctx: Context) extends mutable.Traversable[TermRef] { import collection.JavaConverters._ private val elems = (new java.util.LinkedHashMap[TermSymbol, List[Type]]).asScala def += (ref: TermRef): Unit = { val pre = ref.prefix val sym = ref.symbol.asTerm elems get sym match { case Some(prefixes) => if (!(prefixes exists (_ =:= pre))) elems(sym) = pre :: prefixes case None => elems(sym) = pre :: Nil } } def ++= (refs: TraversableOnce[TermRef]): Unit = refs foreach += override def foreach[U](f: TermRef => U): Unit = for (sym <- elems.keysIterator) for (pre <- elems(sym)) f(TermRef(pre, sym)) } @sharable object EmptyTermRefSet extends TermRefSet()(NoContext)