diff options
author | Martin Odersky <odersky@gmail.com> | 2013-09-06 22:15:47 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-09-06 22:15:47 +0200 |
commit | 2aa0615594744a7fd92f5f4d017b47e5c42a793a (patch) | |
tree | 90e4458cf2cbfdc03152588eebacb031513197ac /src/dotty/tools | |
parent | f7ab848229e8b9b0de1b719725816209aa1271c8 (diff) | |
download | dotty-2aa0615594744a7fd92f5f4d017b47e5c42a793a.tar.gz dotty-2aa0615594744a7fd92f5f4d017b47e5c42a793a.tar.bz2 dotty-2aa0615594744a7fd92f5f4d017b47e5c42a793a.zip |
Improvements in implicits error reporting
Plus a few bugfixes for implicits
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Constraint.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 12 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Scopes.scala | 13 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 20 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ErrorReporting.scala | 7 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 288 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ImportInfo.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 30 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/SimpleMap.scala | 4 |
12 files changed, 243 insertions, 152 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 50d703a28..181660ab0 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -20,14 +20,13 @@ class Compiler { ctx.usePhases(phases) val start = ctx.fresh .withPeriod(Period(ctx.runId + 1, FirstPhaseId)) - .withRunInfo(new RunInfo) .withOwner(defn.RootClass) .withTyper(new Typer) .withMode(Mode.ImplicitsEnabled) .withTyperState(new MutableTyperState(ctx.typerState, new ConsoleReporter()(ctx))) def addImport(ctx: Context, sym: Symbol) = ctx.fresh.withImportInfo(ImportInfo.rootImport(sym)(ctx)) - (start /: rootImports)(addImport) + (start.withRunInfo(new RunInfo(start)) /: rootImports)(addImport) } def newRun(implicit ctx: Context): Run = diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index 26ab00340..c74428be5 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -98,11 +98,11 @@ class Constraint(val map: SimpleMap[PolyType, Array[Type]]) extends AnyVal with new Constraint((this - param).map mapValues subst) } - def domainPolys: List[PolyType] = map.map2((k, v) => k) + def domainPolys: List[PolyType] = map.keys def domainParams: List[PolyParam] = for { - (poly, entries) <- map.map2((k, v) => (k, v)) + (poly, entries) <- map.toList n <- 0 until entries.length if entries(n).exists } yield PolyParam(poly, n) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 0898321e5..4307c81d4 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -158,11 +158,11 @@ object Contexts { def implicits: ContextualImplicits = { if (implicitsCache == null ) implicitsCache = { - val implicitRefs: Set[TermRef] = + val implicitRefs: List[TermRefBySym] = if (isClassDefContext) owner.thisType.implicitMembers else if (isImportContext) importInfo.importedImplicits else if (isNonEmptyScopeContext) scope.implicitDecls - else Set() + else Nil if (implicitRefs.isEmpty) outer.implicits else new ContextualImplicits(implicitRefs, outer.implicits.ctx)(this) } @@ -325,7 +325,7 @@ object Contexts { owner = NoSymbol sstate = settings.defaultState tree = untpd.EmptyTree - runInfo = new RunInfo + runInfo = new RunInfo(this) diagnostics = None moreProperties = Map.empty typeComparer = new TypeComparer(this) @@ -333,7 +333,7 @@ object Contexts { object NoContext extends Context { lazy val base = unsupported("base") - override def implicits: ContextualImplicits = new ContextualImplicits(Set(), this)(this) + override def implicits: ContextualImplicits = new ContextualImplicits(Nil, this)(this) } /** A context base defines state and associated methods that exist once per @@ -453,7 +453,9 @@ object Contexts { } /** Info that changes on each compiler run */ - class RunInfo(implicit val ctx: Context) extends ImplicitRunInfo + class RunInfo(initctx: Context) extends ImplicitRunInfo { + implicit val ctx = initctx + } /** Initial size of superId table */ private final val InitialSuperIdsSize = 4096 diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 3748801b9..a308adf79 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -7,7 +7,7 @@ package dotty.tools.dotc package core import Symbols._ -import Types.{TermRef, NoPrefix} +import Types.{TermRef, TermRefBySym, NoPrefix} import Flags.Implicit import Names._ import Periods._ @@ -17,6 +17,7 @@ import Denotations._ import printing.Texts._ import printing.Printer import SymDenotations.NoDenotation +import collection.mutable.ListBuffer object Scopes { @@ -111,7 +112,7 @@ object Scopes { syms } - def implicitDecls(implicit ctx: Context): Set[TermRef] = Set() + def implicitDecls(implicit ctx: Context): List[TermRefBySym] = Nil final def toText(printer: Printer): Text = printer.toText(this) } @@ -287,15 +288,15 @@ object Scopes { elemsCache } - override def implicitDecls(implicit ctx: Context): Set[TermRef] = { - var irefs: Set[TermRef] = Set() + override def implicitDecls(implicit ctx: Context): List[TermRefBySym] = { + var irefs = new ListBuffer[TermRefBySym] var e = lastEntry while (e ne null) { if (e.sym is Implicit) - irefs += TermRef(NoPrefix, e.sym.name.asTermName).withDenot(e.sym.denot) + irefs += TermRef.withSym(NoPrefix, e.sym.asTerm).withDenot(e.sym.denot) e = e.prev } - irefs + irefs.toList } /** Vanilla scope - symbols are stored in declaration order. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 88a52fa12..135dfe148 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -617,13 +617,13 @@ object SymDenotations { /** The symbolic typeref representing the type constructor for this type. * @throws ClassCastException is this is not a type */ - final def symTypeRef(implicit ctx: Context): TypeRef = + final def symTypeRef(implicit ctx: Context): TypeRefBySym = TypeRef.withSym(owner.thisType, symbol.asType) /** The symbolic termref pointing to this termsymbol * @throws ClassCastException is this is not a term */ - def symTermRef(implicit ctx: Context): TermRef = + def symTermRef(implicit ctx: Context): TermRefBySym = TermRef.withSym(owner.thisType, symbol.asTerm) def symRef(implicit ctx: Context): NamedType = diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 518b13dbe..a95eddfc7 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -171,7 +171,7 @@ object Types { final def namedParts(implicit ctx: Context): Set[NamedType] = namedPartsWith(Function.const(true)) - final def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): Set[NamedType] = + def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): Set[NamedType] = new NamedPartsAccumulator(p).apply(Set(), this) final def foreach(f: Type => Unit): Unit = ??? @@ -227,17 +227,17 @@ object Types { /** The least (wrt <:<) set of class symbols of which this type is a subtype */ - final def classSymbols(implicit ctx: Context): Set[ClassSymbol] = this match { + final def classSymbols(implicit ctx: Context): List[ClassSymbol] = this match { case tp: ClassInfo => - Set(tp.cls) + tp.cls :: Nil case tp: TypeProxy => tp.underlying.classSymbols case AndType(l, r) => - l.classSymbols | r.classSymbols + l.classSymbols union r.classSymbols case OrType(l, r) => - l.classSymbols & r.classSymbols + l.classSymbols intersect r.classSymbols case _ => - Set() + Nil } /** The term symbol associated with the type */ @@ -436,11 +436,11 @@ object Types { memberNames(typeNameFilter).map(member(_).asInstanceOf[SingleDenotation]) /** The set of implicit members of this type */ - final def implicitMembers(implicit ctx: Context): Set[TermRef] = - memberNames(implicitFilter) + final def implicitMembers(implicit ctx: Context): List[TermRefBySym] = + memberNames(implicitFilter).toList .flatMap(name => member(name) .altsWith(_ is Implicit) - .map(TermRef(this, name.asTermName).withDenot(_))) + .map(d => TermRef.withSym(this, d.symbol.asTerm).withDenot(d))) /** The info of `sym`, seen as a member of this type. */ final def memberInfo(sym: Symbol)(implicit ctx: Context): Type = @@ -2213,7 +2213,7 @@ object Types { } class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[Set[NamedType]] { - def apply(x: Set[NamedType], tp: Type): Set[NamedType] = tp match { + def apply(x: Set[NamedType], tp: Type): Set[NamedType] = tp match { // !!! make set linked!!! case tp: NamedType if (p(tp)) => foldOver(x + tp, tp) case tp: ThisType => diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 79688c584..57ea5ebda 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -6,7 +6,7 @@ import ast._ import core._ import Trees._ import Types._, Inferencing._, Contexts._, Decorators._, Denotations._, Symbols._ -import Applications._ +import Applications._, Implicits._ import util.Positions._ import printing.Showable import reporting.Reporter.SuppressedMessage @@ -63,8 +63,9 @@ object ErrorReporting { def patternConstrStr(tree: Tree): String = ??? - def typeMismatch(tree: Tree, pt: Type): Tree = - errorTree(tree, typeMismatchStr(tree.tpe, pt)) + def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree = { + errorTree(tree, typeMismatchStr(tree.tpe, pt) + implicitFailure.postscript) + } def typeMismatchStr(found: Type, expected: Type) = { if (ctx.settings.explaintypes.value) diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 043ecd29f..cb028a080 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -20,6 +20,7 @@ import StdNames._ import Constants._ import Inferencing._ import Applications._ +import ErrorReporting._ import collection.mutable /** Implicit resolution */ @@ -31,13 +32,13 @@ object Implicits { abstract class ImplicitRefs extends Compatibility { /** The implicit references */ - def refs: Set[TermRef] + def refs: List[TermRef] /** Return those references in `refs` that are compatible with type `pt`. */ - protected def filterMatching(pt: Type)(implicit ctx: Context) = { + protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = { def result(implicit ctx: Context) = - refs.toList filter (ref => isCompatible(normalize(ref), pt)) - result(ctx.fresh.withNewTyperState) + refs filter (ref => isCompatible(normalize(ref), pt)) + result(ctx.fresh.withNewTyperState) // create a defensive copy of ctx to avoid constraint pollution } /** No further implicit conversions can be applied when searching for implicits. */ @@ -48,20 +49,24 @@ object Implicits { * @param tp the type determining the implicit scope * @param companionRefs the companion objects in the implicit scope. */ - class OfTypeImplicits(tp: Type, val companionRefs: Set[TermRef])(initctx: Context) + class OfTypeImplicits(tp: Type, val companionRefs: collection.Set[TermRefBySym])(initctx: Context) extends ImplicitRefs { + assert(initctx.typer != null) implicit val ctx: Context = initctx retractMode ImplicitsEnabled - val refs: Set[TermRef] = companionRefs flatMap (_.implicitMembers) + val refs: List[TermRef] = companionRefs.toList flatMap (_.implicitMembers) /** The implicit references that are eligible for expected type `tp` */ lazy val eligible: List[TermRef] = filterMatching(tp) } /** The implicit references coming from the context. - * @param refs the implicit references made visible by the current 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: Set[TermRef], val outerCtx: Context)(initctx: Context) extends ImplicitRefs { + class ContextualImplicits(val refs: List[TermRefBySym], val outerCtx: Context)(initctx: Context) extends ImplicitRefs { implicit val ctx: Context = if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled @@ -95,16 +100,57 @@ object Implicits { * @param tree The typed tree that can needs to be inserted * @param ctx The context after the implicit search */ - case class SearchSuccess(ref: TermRef, tree: tpd.Tree, tstate: TyperState) extends SearchResult + case class SearchSuccess(tree: tpd.Tree)(val ref: TermRef, val tstate: TyperState) extends SearchResult /** A failed search */ - abstract class SearchFailure extends SearchResult - - /** An ambiguous implicits failure */ - case class AmbiguousImplicits(alt1: TermRef, alt2: TermRef) extends SearchFailure + 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 + + /** 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) i"match type $pt" + else i"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(alt1: TermRef, alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { + def explanation(implicit ctx: Context): String = + i"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) extends ExplainedSearchFailure { + def explanation(implicit ctx: Context): String = + i"${err.refStr(ref)} does not $qualify" + } + + class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { + def explanation(implicit ctx: Context): String = + i"${err.refStr(ref)} does $qualify but is shadowed by ${err.refStr(shadowing)}" + } + + 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 = + "\nImplicit search failure summary:\n" + explanation + } } import Implicits._ @@ -132,26 +178,34 @@ trait ImplicitRunInfo { self: RunInfo => } } - private def computeImplicitScope(tp: Type): OfTypeImplicits = - new OfTypeImplicits(tp, - tp match { - case tp: NamedType => - val pre = tp.prefix - def addClassScope(comps: Set[TermRef], cls: ClassSymbol): Set[TermRef] = { - def addRef(comps: Set[TermRef], comp: TermRef): Set[TermRef] = - comps + comp.asSeenFrom(pre, comp.symbol.owner).asInstanceOf[TermRef] - def addInheritedScope(comps: Set[TermRef], parent: TypeRef): Set[TermRef] = { - val baseTp = cls.thisType.baseType(parent.symbol) - (comps /: implicitScope(baseTp).companionRefs)(addRef) - } - val companion = cls.companionModule - val comps1 = if (companion.exists) addRef(comps, companion.symTermRef) else comps - (comps1 /: cls.classParents)(addInheritedScope) + // todo: compute implicits directly, without going via companionRefs + private def computeImplicitScope(tp: Type): OfTypeImplicits = { + val comps = new mutable.LinkedHashSet[TermRefBySym]() + tp match { + case tp: NamedType => + val pre = tp.prefix + comps ++= implicitScope(pre).companionRefs + def addClassScope(cls: ClassSymbol): Unit = { + def addRef(companion: TermRefBySym): Unit = { + val comp1 @ TermRef(p, _) = companion.asSeenFrom(pre, companion.symbol.owner) + comps += TermRef.withSym(p, comp1.symbol.asTerm).withDenot(comp1.denot) + } + def addParentScope(parent: TypeRef): Unit = { + implicitScope(parent).companionRefs foreach addRef + for (param <- parent.typeParams) + comps ++= implicitScope(pre.member(param.name).info).companionRefs } - (implicitScope(pre).companionRefs /: tp.classSymbols)(addClassScope) - case _ => - tp.namedParts.flatMap(implicitScope(_).companionRefs) - })(ctx) + val companion = cls.companionModule + if (companion.exists) addRef(companion.symTermRef) + cls.classParents foreach addParentScope + } + tp.classSymbols foreach addClassScope + case _ => + for (part <- tp.namedParts) + comps ++= implicitScope(part).companionRefs + } + new OfTypeImplicits(tp, comps)(ctx) + } /** The implicit scope of a type * @param isLifted Type `tp` is the result of a `liftToClasses` application @@ -179,98 +233,99 @@ trait Implicits { self: Typer => !from.isError && !to.isError && (ctx.mode is Mode.ImplicitsEnabled) - && (inferView(dummyTreeOfType(from), to) ne EmptyTree)) + && (inferView(dummyTreeOfType(from), to).isInstanceOf[SearchSuccess])) /** Find an implicit conversion to apply to given tree `from` so that the * result is compatible with type `to`. */ - def inferView(from: tpd.Tree, to: Type)(implicit ctx: Context): Tree = - inferImplicit(to, from, from.pos, reportAmbiguous = false) + def inferView(from: tpd.Tree, to: Type)(implicit ctx: Context): SearchResult = + inferImplicit(to, from, from.pos) /** 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. - * @param reportAmbiguous Should ambiguity errors be reported? False when called from `viewExists`. */ - def inferImplicit(pt: Type, argument: Tree, pos: Position, reportAmbiguous: Boolean = true)(implicit ctx: Context): Tree = - ctx.traceIndented(s"search implicit $pt, arg = ${argument.show}, reportAmbiguous = $reportAmbiguous", show = true) { - new ImplicitSearch(pt, argument, pos).bestImplicit match { - case SearchSuccess(_, tree, tstate) => - tstate.commit() - tree - case NoImplicitMatches => - EmptyTree - case AmbiguousImplicits(alt1, alt2) => - if (reportAmbiguous) { - val qualify = - if (argument.isEmpty) s"match type $pt" - else s"can convert from ${argument.tpe} to $pt" - ctx.error(s"ambiguous implicits; both $alt1 and $alt2 $qualify") - } - EmptyTree + def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = + ctx.traceIndented(s"search implicit $pt, arg = ${argument.show}", show = true) { + 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 success: SearchSuccess => success.tstate.commit() + case _ => } + result } /** An implicit search; parameters as in `inferImplicit` */ - class ImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context) { - - /** Try to typecheck an implicit reference */ - def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = { - val id = Ident(ref).withPos(pos) - val tree = - if (argument.isEmpty) adapt(id, pt) - else typedApply(id, ref, argument :: Nil, pt) - if (tree.tpe.isError) NoImplicitMatches // todo: replace by checking if local errors were reported in ctx. - else SearchSuccess(ref, tree, 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 - * @param acc An accumulator of successful matches found so far. - */ - private def rankImplicits(pending: List[TermRef], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { - case ref :: pending1 => - typedImplicit(ref)(ctx.fresh.withNewTyperState.retractMode(ImplicitsEnabled)) match { - case fail: SearchFailure => - rankImplicits(pending1, acc) - case best @ SearchSuccess(bestRef, _, _) => - val newPending = pending filterNot (isAsGood(_, bestRef)) - rankImplicits(newPending, best :: acc) - } - case nil => acc - } + class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) { - /** 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)) match { - case Some(alt) => - AmbiguousImplicits(best.ref, alt.ref) - case None => - ctx.runInfo.useCount(best.ref) += 1 - best - } - case Nil => - NoImplicitMatches - } + protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches + protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches + protected def failedSearch: SearchFailure = NoImplicitMatches - /** Sort list of implicit references according to their popularity - * (# of times each was picked in current run). - */ - def sort(eligible: List[TermRef]) = eligible match { - case Nil => eligible - case e1 :: Nil => eligible - case e1 :: e2 :: Nil => - if (ctx.runInfo.useCount(e1) < ctx.runInfo.useCount(e2)) e2 :: e1 :: Nil - else eligible - case _ => eligible.sortBy(-ctx.runInfo.useCount(_)) - } /** Search a list of eligible implicit references */ - def searchImplicits(eligible: List[TermRef]): SearchResult = { + def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = { + + /** Try to typecheck an implicit reference */ + def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = { + val id = Ident(ref).withPos(pos) + val tree = + if (argument.isEmpty) adapt(id, pt) + else typedApply(id, ref, argument :: Nil, pt) + lazy val shadowing = typed(untpd.Ident(ref.name), ref).tpe + if (ctx.typerState.reporter.hasErrors) nonMatchingImplicit(ref) + else if (contextual && !(shadowing =:= ref)) shadowedImplicit(ref, shadowing) + else SearchSuccess(tree)(ref, 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 + * @param acc An accumulator of successful matches found so far. + */ + def rankImplicits(pending: List[TermRef], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { + case ref :: pending1 => + typedImplicit(ref)(ctx.fresh.withNewTyperState.retractMode(ImplicitsEnabled)) match { + case fail: SearchFailure => + rankImplicits(pending1, acc) + case best: SearchSuccess => + val newPending = pending filterNot (isAsGood(_, best.ref)) + rankImplicits(newPending, best :: acc) + } + case nil => acc + } + + /** 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)) match { + case Some(alt) => + new AmbiguousImplicits(best.ref, alt.ref, pt, argument) + case None => + ctx.runInfo.useCount(best.ref) += 1 + best + } + case Nil => + failedSearch + } + + /** Sort list of implicit references according to their popularity + * (# of times each was picked in current run). + */ + def sort(eligible: List[TermRef]) = eligible match { + case Nil => eligible + case e1 :: Nil => eligible + case e1 :: e2 :: Nil => + if (ctx.runInfo.useCount(e1) < ctx.runInfo.useCount(e2)) e2 :: e1 :: Nil + else eligible + case _ => eligible.sortBy(-ctx.runInfo.useCount(_)) + } + condense(rankImplicits(sort(eligible), Nil)) } @@ -285,14 +340,33 @@ trait Implicits { self: Typer => /** Find a unique best implicit reference */ def bestImplicit: SearchResult = { - searchImplicits(ctx.implicits.eligible(wildPt)) match { + searchImplicits(ctx.implicits.eligible(wildPt), contextual = true) match { case result: SearchSuccess => result case result: AmbiguousImplicits => result - case NoImplicitMatches => searchImplicits(implicitScope(wildPt).eligible) + case result: SearchFailure => + searchImplicits(implicitScope(wildPt).eligible, contextual = false) } } def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp) } + 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) = + record(new NonMatchingImplicit(ref, pt, argument)) + override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = + record(new ShadowedImplicit(ref, shadowing, pt, argument)) + override def failedSearch: SearchFailure = { + println(s"wildPt = $wildPt") + println(s"implicit scope = ${implicitScope(wildPt).companionRefs}") + new FailedImplicit(failures, pt, argument) + } + } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/ImportInfo.scala b/src/dotty/tools/dotc/typer/ImportInfo.scala index 89ffc1f29..92b980c01 100644 --- a/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -76,7 +76,7 @@ class ImportInfo(val sym: Symbol, val selectors: List[untpd.Tree], val rootImpor } /** The implicit references imported by this import clause */ - def importedImplicits: Set[TermRef] = { + def importedImplicits: List[TermRefBySym] = { val pre = site if (wildcardImport) { val refs = pre.implicitMembers @@ -84,9 +84,9 @@ class ImportInfo(val sym: Symbol, val selectors: List[untpd.Tree], val rootImpor else refs filterNot (ref => excluded contains ref.name.toTermName) } else for { - name <- originals - denot <- pre.member(name).altsWith(_ is Implicit) - } yield TermRef(pre, name) withDenot denot + renamed <- reverseMapping.keys + denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit) + } yield TermRef.withSym(pre, renamed, denot.symbol.asTerm).withDenot(denot) } override def toString = { diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 7ae20062b..58e5f73c9 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -79,6 +79,8 @@ object Inferencing { case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) extends CachedGroundType with ProtoType { def isMatchedBy(tp: Type)(implicit ctx: Context) = ctx.typer.isApplicableToTypes(tp, argType :: Nil, resultType) + override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): Set[NamedType] = + argType.namedPartsWith(p) | resultType.namedPartsWith(p) override def computeHash = doHash(argType, resultType) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 9815015c1..7519cdd73 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -26,6 +26,7 @@ import util.Positions._ import util.SourcePosition import collection.mutable import annotation.tailrec +import Implicits._ import language.implicitConversions trait TyperContextOps { ctx: Context => } @@ -946,7 +947,7 @@ class Typer extends Namer with Applications with Implicits { def adaptOverloaded(ref: TermRef) = { val altDenots = ref.denot.alternatives val alts = altDenots map (alt => - TermRef.withSym(ref.prefix, alt.symbol.asTerm)) + TermRef.withSym(ref.prefix, alt.symbol.asTerm).withDenot(alt)) def expectedStr = err.expectedTypeStr(pt) resolveOverloaded(alts, pt) match { case alt :: Nil => @@ -982,14 +983,23 @@ class Typer extends Namer with Applications with Implicits { case wtp: ExprType => adapt(tree.withType(wtp.resultType), pt) case wtp: ImplicitMethodType => + def implicitArgError(msg: => String): Tree = { + ctx.error(msg, tree.pos.endPos) + EmptyTree + } val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) => - val arg = inferImplicit(formal, EmptyTree, tree.pos.endPos) - if (arg.isEmpty) - ctx.error(i"no implicit argument of type $formal found for parameter $pname of $methodStr", tree.pos.endPos) - arg + def where = i"parameter $pname of $methodStr" + inferImplicit(formal, EmptyTree, tree.pos.endPos) match { + case SearchSuccess(arg) => + arg + case ambi: AmbiguousImplicits => + implicitArgError(s"ambiguous implicits: ${ambi.explanation} of $where") + case failure: SearchFailure => + implicitArgError(i"no implicit argument of type $formal found for $where" + failure.postscript) + } } adapt(tpd.Apply(tree, args), pt) - case wtp: MethodType => + case wtp: MethodType if !pt.isInstanceOf[SingletonType] => if ((defn.isFunctionType(pt) || (pt eq AnyFunctionProto)) && !tree.symbol.isConstructor) etaExpand(tree, wtp) @@ -1026,10 +1036,10 @@ class Typer extends Namer with Applications with Implicits { case _ => } // try an implicit conversion - val adapted = inferView(tree, pt) - if (adapted ne EmptyTree) return adapted - // if everything fails issue a type error - err.typeMismatch(tree, pt) + inferView(tree, pt) match { + case SearchSuccess(adapted) => adapted + case failure: SearchFailure => err.typeMismatch(tree, pt, failure) + } } tree match { diff --git a/src/dotty/tools/dotc/util/SimpleMap.scala b/src/dotty/tools/dotc/util/SimpleMap.scala index f4f238c5e..e467df2c2 100644 --- a/src/dotty/tools/dotc/util/SimpleMap.scala +++ b/src/dotty/tools/dotc/util/SimpleMap.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc.util import collection.mutable.ListBuffer -abstract class SimpleMap[K, +V >: Null] { +abstract class SimpleMap[K, +V >: Null] extends (K => V) { def apply(k: K): V def remove(k: K): SimpleMap[K, V] def updated[V1 >: V](k: K, v: V1): SimpleMap[K, V1] @@ -17,6 +17,8 @@ abstract class SimpleMap[K, +V >: Null] { } buf.toList } + def keys: List[K] = map2((k, v) => k) + def toList: List[(K, V)] = map2((k, v) => (k, v)) override def toString = { def assocToString(key: K, value: V) = s"$key -> $value" map2(assocToString) mkString ("(", ", ", ")") |