aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-09-06 22:15:47 +0200
committerMartin Odersky <odersky@gmail.com>2013-09-06 22:15:47 +0200
commit2aa0615594744a7fd92f5f4d017b47e5c42a793a (patch)
tree90e4458cf2cbfdc03152588eebacb031513197ac /src/dotty/tools
parentf7ab848229e8b9b0de1b719725816209aa1271c8 (diff)
downloaddotty-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.scala3
-rw-r--r--src/dotty/tools/dotc/core/Constraint.scala4
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala12
-rw-r--r--src/dotty/tools/dotc/core/Scopes.scala13
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala4
-rw-r--r--src/dotty/tools/dotc/core/Types.scala20
-rw-r--r--src/dotty/tools/dotc/typer/ErrorReporting.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala288
-rw-r--r--src/dotty/tools/dotc/typer/ImportInfo.scala8
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala30
-rw-r--r--src/dotty/tools/dotc/util/SimpleMap.scala4
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 ("(", ", ", ")")