aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala46
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala19
-rw-r--r--src/dotty/tools/dotc/core/Scopes.scala15
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala6
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala9
-rw-r--r--src/dotty/tools/dotc/core/Types.scala80
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala253
-rw-r--r--src/dotty/tools/dotc/typer/ImportInfo.scala66
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala6
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala23
-rw-r--r--src/dotty/tools/dotc/util/Set.scala4
11 files changed, 485 insertions, 42 deletions
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 27de2c4de..d92ed35fc 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -16,6 +16,7 @@ import ast.Trees._
import ast.untpd
import util.{FreshNameCreator, SimpleMap}
import typer._
+import Implicits.ContextualImplicits
import config.Settings._
import reporting._
import collection.mutable
@@ -140,6 +141,11 @@ object Contexts {
protected def reporter_=(reporter: Reporter) = _reporter = reporter
def reporter: Reporter = _reporter
+ /** The current compiler-run specific Info */
+ private[this] var _runInfo: RunInfo = _
+ protected def runInfo_=(runInfo: RunInfo) = _runInfo = runInfo
+ def runInfo: RunInfo = _runInfo
+
/** An optional diagostics buffer than is used by some checking code
* to provide more information in the buffer if it exists.
*/
@@ -159,7 +165,23 @@ object Contexts {
_typeComparer
}
- /** If -Ydebug is on, the top of the stack trace where this context
+ /** The new implicit references that are introduces by this scope */
+ private var implicitsCache: ContextualImplicits = null
+ def implicits: ContextualImplicits = {
+ if (implicitsCache == null )
+ implicitsCache = {
+ val implicitRefs: Set[TermRef] =
+ if (isClassDefContext) owner.thisType.implicitMembers
+ else if (isImportContext) importInfo.importedImplicits
+ else if (isNonEmptyScopeContext) scope.implicitDecls
+ else Set()
+ if (implicitRefs.isEmpty) outer.implicits
+ else new ContextualImplicits(implicitRefs, outer.implicits.ctx)
+ }
+ implicitsCache
+ }
+
+ /** If -Ydebug is on, the top of the stack trace where this context
* was created, otherwise `null`.
*/
private var creationTrace: Array[StackTraceElement] = _
@@ -180,6 +202,18 @@ object Contexts {
println("=== end context creation trace ===")
}
+ /** Is this a context for the members of a class definition? */
+ def isClassDefContext: Boolean =
+ owner.isClass && (owner ne outer.owner)
+
+ /** Is this a context that introduces an import clause? */
+ def isImportContext: Boolean =
+ (this ne NoContext) && (this.importInfo ne outer.importInfo)
+
+ /** Is this a context that introduces a non-empty scope? */
+ def isNonEmptyScopeContext: Boolean =
+ (this.scope ne outer.scope) && this.scope.nonEmpty
+
/** Leave message in diagnostics buffer if it exists */
def diagnose(str: => String) =
for (sb <- diagnostics) {
@@ -224,6 +258,7 @@ object Contexts {
.withSettings(sstate)
// tree is not preserved in condensed
.withReporter(reporter)
+ .withRunInfo(runInfo)
.withDiagnostics(diagnostics)
.withMoreProperties(moreProperties)
_condensed
@@ -235,6 +270,7 @@ object Contexts {
def fresh: FreshContext = {
val newctx = super.clone.asInstanceOf[FreshContext]
newctx.outer = this
+ newctx.implicitsCache = null
newctx.setCreationTrace()
newctx
}
@@ -266,6 +302,7 @@ object Contexts {
def withTyper(typer: Typer): this.type = { this.typer = typer; this.scope = typer.scope; this }
def withImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this }
def withReporter(reporter: Reporter): this.type = { this.reporter = reporter; this }
+ def withRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this }
def withDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this }
def withMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this }
@@ -278,6 +315,7 @@ object Contexts {
def withDebug = withSetting(base.settings.debug, true)
def withImplicitsDisabled: this.type = ???
+ def silent: this.type = ???
}
/** A class defining the initial context with given context base
@@ -294,8 +332,11 @@ object Contexts {
sstate = settings.defaultState
tree = untpd.EmptyTree
reporter = new ConsoleReporter()(this)
+ runInfo = new RunInfo
diagnostics = None
moreProperties = Map.empty
+
+ override val implicits = new ContextualImplicits(Set(), NoContext)(this)
}
object NoContext extends Context {
@@ -418,6 +459,9 @@ object Contexts {
val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normallly cannot access a context
}
+ /** Info that changes on each compiler run */
+ class RunInfo(implicit val ctx: Context) extends ImplicitRunInfo
+
/** Initial size of superId table */
private final val InitialSuperIdsSize = 4096
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index 82fd86c05..045a511de 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -203,18 +203,14 @@ object Flags {
final val TypeParam = Param.toTypeFlags
/** Labeled with `implicit` modifier (implicit value) */
- final val Implicit = termFlag(9, "implicit")
-
- /** A trait */
- final val Trait = typeFlag(9, "<trait>")
+ final val ImplicitCommon = commonFlag(9, "implicit")
+ final val Implicit = ImplicitCommon.toTermFlags
/** Labeled with `lazy` (a lazy val). */
final val Lazy = termFlag(10, "lazy")
- /** A trait that has only abstract methods as members
- * (and therefore can be represented by a Java interface
- */
- final val Interface = typeFlag(10, "interface")
+ /** A trait */
+ final val Trait = typeFlag(10, "<trait>")
/** A value or variable accessor (getter or setter) */
final val Accessor = termFlag(11, "<accessor>")
@@ -273,6 +269,11 @@ object Flags {
/** Method is a label. */
final val Label = termFlag(22, "<label>")
+ /** A trait that has only abstract methods as members
+ * (and therefore can be represented by a Java interface
+ */
+ final val Interface = typeFlag(22, "interface")
+
/** Labeled with `abstract` modifier (an abstract class)
* Note: You should never see Abstract on any symbol except a class.
* Note: the flag counts as common, because it can be combined with OVERRIDE in a term.
@@ -380,7 +381,7 @@ object Flags {
final val FromStartFlags =
AccessFlags | Module | Package | Deferred | Param | Scala2ExistentialCommon | Touched |
Static | CovariantCommon | ContravariantCommon | ExpandedName | AccessorOrSealed | Frozen |
- Erroneous
+ Erroneous | ImplicitCommon
assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags)
// TODO: Should check that FromStartFlags do not change in completion
diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala
index 82ec0a30a..b622af6ac 100644
--- a/src/dotty/tools/dotc/core/Scopes.scala
+++ b/src/dotty/tools/dotc/core/Scopes.scala
@@ -7,6 +7,8 @@ package dotty.tools.dotc
package core
import Symbols._
+import Types.{TermRef, NoPrefix}
+import Flags.Implicit
import Names._
import Periods._
import Decorators._
@@ -109,6 +111,8 @@ object Scopes {
syms
}
+ def implicitDecls(implicit ctx: Context): Set[TermRef] = Set()
+
final def toText(printer: Printer): Text = printer.toText(this)
}
@@ -283,6 +287,17 @@ object Scopes {
elemsCache
}
+ override def implicitDecls(implicit ctx: Context): Set[TermRef] = {
+ var irefs: Set[TermRef] = Set()
+ var e = lastEntry
+ while (e ne null) {
+ if (e.sym is Implicit)
+ irefs += TermRef(NoPrefix, e.sym.name.asTermName).withDenot(e.sym.denot)
+ e = e.prev
+ }
+ irefs
+ }
+
/** Vanilla scope - symbols are stored in declaration order.
*/
final def sorted: List[Symbol] = toList
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 9d89ac5c1..258604df4 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -30,10 +30,6 @@ trait SymDenotations { this: Context =>
result.validFor = stablePeriod
result
}
-
- def lookup(name: Name): PreDenotation =
- if (owner.isClass && (owner ne outer.owner)) owner.asClass.membersNamed(name)
- else scope.denotsNamed(name)
}
object SymDenotations {
@@ -700,7 +696,7 @@ object SymDenotations {
* because the latter does not ensure that the `parentDenots` key
* is up-to-date, which might lead to invalid caches later on.
*/
- private def classParents(implicit ctx: Context) = {
+ def classParents(implicit ctx: Context) = {
val ps = classInfo.classParents
if (parentDenots == null) parentDenots = ps map (_.denot)
ps
diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala
index e90070253..76787e070 100644
--- a/src/dotty/tools/dotc/core/TyperState.scala
+++ b/src/dotty/tools/dotc/core/TyperState.scala
@@ -23,6 +23,8 @@ class TyperState extends DotClass {
def diagnostics_=(ds: List[Diagnostic]): Unit = unsupported("diagnostics_=")
def fresh: TyperState = this
+
+ def copyFrom(tp: TyperState): Unit = unsupported("copyFrom")
}
class MutableTyperState (previous: TyperState) extends TyperState {
@@ -40,4 +42,11 @@ class MutableTyperState (previous: TyperState) extends TyperState {
override def diagnostics_=(ds: List[Diagnostic]) = myDiagnostics = ds
override def fresh: TyperState = new MutableTyperState(this)
+
+ override def copyFrom(state: TyperState): Unit = {
+ constraint = state.constraint
+ undetVars = state.undetVars
+ // todo: do something about diagnostics (put reporter in state?)
+ }
+
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 508bf53b0..e66d86a40 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -59,6 +59,7 @@ object Types {
* | +- TypeBounds
* | +- ExprType
* | +- AnnotatedType
+ * | +- TypeVar
* |
* +- GroundType -+- AndType
* +- OrType
@@ -165,6 +166,10 @@ object Types {
*/
final def forallParts(p: Type => Boolean): Boolean = !existsPart(!p(_))
+ /** The parts of this type which are type or term refs */
+ final def namedParts(implicit ctx: Context): Set[NamedType] =
+ new PartsAccumulator().apply(Set(), this)
+
/** Map function over elements of an AndType, rebuilding with & */
def mapAnd(f: Type => Type)(implicit ctx: Context): Type = thisInstance match {
case AndType(tp1, tp2) => tp1.mapAnd(f) & tp2.mapAnd(f)
@@ -213,6 +218,21 @@ object Types {
NoSymbol
}
+ /** The least (wrt <:<) set of class symbols of which this type is a subtype
+ */
+ final def classSymbols(implicit ctx: Context): Set[ClassSymbol] = this match {
+ case tp: ClassInfo =>
+ Set(tp.cls)
+ case tp: TypeProxy =>
+ tp.underlying.classSymbols
+ case AndType(l, r) =>
+ l.classSymbols | r.classSymbols
+ case OrType(l, r) =>
+ l.classSymbols & r.classSymbols
+ case _ =>
+ Set()
+ }
+
/** The term symbol associated with the type */
final def termSymbol(implicit ctx: Context): Symbol = this match {
case tp: TermRef => tp.symbol
@@ -399,6 +419,13 @@ object Types {
final def typeMembers(implicit ctx: Context): Set[SingleDenotation] =
memberNames(typeNameFilter).map(member(_).asInstanceOf[SingleDenotation])
+ /** The set of implicit members of this type */
+ final def implicitMembers(implicit ctx: Context): Set[TermRef] =
+ memberNames(implicitFilter)
+ .flatMap(name => member(name)
+ .altsWith(_ is Implicit)
+ .map(TermRef(this, name.asTermName).withDenot(_)))
+
/** The info of `sym`, seen as a member of this type. */
final def memberInfo(sym: Symbol)(implicit ctx: Context): Type =
sym.info.asSeenFrom(this, sym.owner)
@@ -572,6 +599,11 @@ object Types {
final def bounds(implicit ctx: Context): TypeBounds = this match {
case tp: TypeBounds => tp
case ci: ClassInfo => TypeAlias(ci.typeConstructor)
+ case wc: WildcardType =>
+ wc.optBounds match {
+ case bounds: TypeBounds => bounds
+ case NoType => TypeBounds.empty
+ }
case _ => TypeAlias(this)
}
@@ -1728,7 +1760,8 @@ object Types {
/** An annotated type tpe @ annot */
case class AnnotatedType(annot: Annotation, tpe: Type)
- extends UncachedProxyType with ValueType { // todo: cache them?
+ extends UncachedProxyType with ValueType {
+ // todo: cache them? but this makes only sense if annotations and trees are also cached.
override def underlying(implicit ctx: Context): Type = tpe
def derivedAnnotatedType(annot: Annotation, tpe: Type) =
if ((annot eq this.annot) && (tpe eq this.tpe)) this
@@ -1761,7 +1794,16 @@ object Types {
object ErrorType extends ErrorType
- case object WildcardType extends UncachedGroundType
+ /** Wildcard type, possibly with bounds */
+ abstract case class WildcardType(optBounds: Type) extends CachedGroundType {
+ override def computeHash = doHash(optBounds)
+ }
+
+ final class CachedWildcardType(optBounds: Type) extends WildcardType(optBounds)
+
+ object WildcardType extends WildcardType(NoType) {
+ def apply(bounds: TypeBounds)(implicit ctx: Context) = unique(new CachedWildcardType(bounds))
+ }
/** An extractor for single abstract method types.
* A type is a SAM type if it is a reference to a class or trait, which
@@ -1892,6 +1934,22 @@ object Types {
}
}
+ /** Approximate occurrences of paremter types and uninstantiated typevars
+ * by wildcard types
+ */
+ class WildApprox(implicit ctx: Context) extends TypeMap {
+ override def apply(tp: Type) = tp match {
+ case PolyParam(pt, pnum) =>
+ WildcardType(apply(pt.paramBounds(pnum)).bounds)
+ case MethodParam(mt, pnum) =>
+ WildcardType(TypeBounds.upper(apply(mt.paramTypes(pnum))))
+ case tp: TypeVar =>
+ apply(tp.underlying)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
// ----- TypeAccumulators ----------------------------------------------------
abstract class TypeAccumulator[T] extends ((T, Type) => T) {
@@ -1939,6 +1997,19 @@ object Types {
def apply(x: Boolean, tp: Type) = x || p(tp) || foldOver(x, tp)
}
+ class PartsAccumulator(implicit ctx: Context) extends TypeAccumulator[Set[NamedType]] {
+ def apply(x: Set[NamedType], tp: Type): Set[NamedType] = tp match {
+ case tp: NamedType =>
+ foldOver(x + tp, tp)
+ case tp: ThisType =>
+ apply(x, tp.underlying)
+ case tp: TypeVar =>
+ apply(x, tp.underlying)
+ case _ =>
+ foldOver(x, tp)
+ }
+ }
+
// ----- Name Filters --------------------------------------------------
/** A name filter selects or discards a member name of a type `pre`.
@@ -1972,6 +2043,11 @@ object Types {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
}
+ object implicitFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ (pre member name).hasAltWith(_ is Implicit)
+ }
+
// ----- Exceptions -------------------------------------------------------------
class TypeError(msg: String) extends Exception(msg)
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index f3a9ff0cf..cedeb6af9 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -10,6 +10,7 @@ import Types._
import Flags._
import Denotations._
import NameOps._
+import SymDenotations._
import Symbols._
import Types._
import Decorators._
@@ -17,22 +18,264 @@ import Names._
import StdNames._
import Constants._
import Inferencing._
+import Applications._
import collection.mutable
+/** Implicit resolution */
+object Implicits {
+
+ /** A common base class of contextual implicits and of-type implicits which
+ * represents as set of implicit references.
+ */
+ abstract class ImplicitRefs extends Compatibility with Normalizing {
+
+ /** The implicit references */
+ def refs: Set[TermRef]
+
+ /** Return those references in `refs` that are compatible with type `pt`. */
+ protected def filterMatching(pt: Type)(implicit ctx: Context) =
+ refs.toList filter (ref => isCompatible(normalize(ref), pt))
+
+ /** No further implicit conversions can be applied when searching for implicits. */
+ override def viewExists(tp: Type, pt: Type)(implicit ctx: Context) = false
+ }
+
+ /** 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: Set[TermRef])(implicit ctx: Context)
+ extends ImplicitRefs{
+ val refs: Set[TermRef] = companionRefs 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 outerCtx the next outer context that makes visible further implicits
+ */
+ class ContextualImplicits(val refs: Set[TermRef], val outerCtx: Context)(implicit val ctx: Context) extends ImplicitRefs {
+ private val eligibleCache = new mutable.HashMap[Type, List[TermRef]]
+
+ /** The implicit references that are eligible for type `tp`. */
+ def eligible(tp: Type): List[TermRef] =
+ if (tp.hash == NotCached) computeEligible(tp)
+ else eligibleCache.getOrElseUpdate(tp, computeEligible(tp))
+
+ private def computeEligible(tp: Type): List[TermRef] = {
+ val ownEligible = filterMatching(tp)
+ if (outerCtx == NoContext) ownEligible
+ else ownEligible ::: {
+ val shadowed = (ownEligible map (_.name)).toSet
+ outerCtx.implicits.eligible(tp).filter(ref => !(shadowed contains ref.name))
+ }
+ }
+ }
+
+ /** The result of an implicit search */
+ abstract class SearchResult
+
+ /** A successful search
+ * @param ref The implicit reference that succeeeded
+ * @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, ctx: Context) extends SearchResult
+
+ /** A failed search */
+ abstract class SearchFailure extends SearchResult
+
+ /** An ambiguous implicits failure */
+ case class AmbiguousImplicits(alt1: TermRef, alt2: TermRef) extends SearchFailure
+
+ /** A "no matching implicit found" failure */
+ case object NoImplicitMatches extends SearchFailure
+}
+
+import Implicits._
+
+/** Info relating to implicits that is kept for one run */
+trait ImplicitRunInfo { self: RunInfo =>
+
+ private val implicitScopeCache = mutable.HashMap[Type, OfTypeImplicits]()
+
+ /** 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.
+ */
+ private val liftToClasses = new TypeMap {
+ def apply(tp: Type) = tp match {
+ case tp: TypeRef if !tp.symbol.isClass =>
+ val pre = tp.prefix
+ def joinClass(tp: Type, cls: ClassSymbol) =
+ AndType(tp, cls.symTypeRef.asSeenFrom(pre, cls.owner))
+ (apply(tp.prefix) /: tp.classSymbols)(joinClass)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
+ 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)
+ }
+ (implicitScope(pre).companionRefs /: tp.classSymbols)(addClassScope)
+ case _ =>
+ tp.namedParts.flatMap(implicitScope(_).companionRefs)
+ })
+
+ /** The implicit scope of a type
+ * @param isLifted Type `tp` is the result of a `liftToClasses` application
+ */
+ def implicitScope(tp: Type, isLifted: Boolean = false): OfTypeImplicits =
+ if (tp.hash == NotCached) computeImplicitScope(tp)
+ else implicitScopeCache.getOrElseUpdate(tp, {
+ val liftedTp = if (isLifted) tp else liftToClasses(tp)
+ if (liftedTp ne tp) implicitScope(liftedTp, isLifted = true)
+ else computeImplicitScope(tp)
+ })
+
+ /** 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
+ }
+}
+
+/** The implicit resolution part of type checking */
trait Implicits { self: Typer =>
import tpd._
- def viewExists(from: Type, to: Type)(implicit ctx: Context): Boolean = (
+ override def viewExists(from: Type, to: Type)(implicit ctx: Context): Boolean = (
!from.isError
&& !to.isError
&& ctx.implicitsEnabled
- && inferView(EmptyTree, from, to, reportAmbiguous = false) != EmptyTree
+ && inferView(dummyTreeOfType(from), to) != EmptyTree
)
- def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean)(implicit ctx: Context): Tree =
- inferImplicit(tree, defn.FunctionType(from :: Nil, to), isView = true, reportAmbiguous)
+ /** 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)
+
+ /** 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 = {
+ new ImplicitSearch(pt, argument, pos).bestImplicit match {
+ case SearchSuccess(_, tree, newCtx) =>
+ ctx.typerState.copyFrom(newCtx.typerState)
+ 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
+ }
+ }
+
+ /** 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, Mode.Expr, 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)
+ }
+
+ /** 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.silent) match {
+ case fail: SearchFailure =>
+ rankImplicits(pending1, acc)
+ case best @ SearchSuccess(bestRef, _, _) =>
+ val newPending = pending filterNot (isAsGood(_, bestRef))
+ 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) =>
+ AmbiguousImplicits(best.ref, alt.ref)
+ case None =>
+ ctx.runInfo.useCount(best.ref) += 1
+ best
+ }
+ case Nil =>
+ 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 = {
+ condense(rankImplicits(sort(eligible), Nil))
+ }
+
+ /** The expected type where parameters and uninstantiated typevars
+ * are replaced by wildcard types
+ */
+ val wildPt: Type = (new WildApprox) apply pt
+
+ /** Find a unique best implicit reference */
+ def bestImplicit: SearchResult = {
+ searchImplicits(ctx.implicits.eligible(wildPt)) match {
+ case result: SearchSuccess => result
+ case result: AmbiguousImplicits => result
+ case NoImplicitMatches => searchImplicits(implicitScope(wildPt).eligible)
+ }
+ }
- def inferImplicit(tree: Tree, pt: Type, isView: Boolean, reportAmbiguous: Boolean)(implicit ctx: Context): Tree = ???
+ def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp)
+ }
} \ 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 3f668ecb6..4d173e10e 100644
--- a/src/dotty/tools/dotc/typer/ImportInfo.scala
+++ b/src/dotty/tools/dotc/typer/ImportInfo.scala
@@ -2,14 +2,74 @@ package dotty.tools
package dotc
package typer
-import ast.untpd._
+import ast.untpd
+import ast.Trees._
import core._
-import Symbols._, Names._, Denotations._, Types._, Contexts._
+import util.SimpleMap
+import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._
+
+/** Info relating to an import clause */
+case class ImportInfo(sym: Symbol, selectors: List[untpd.Tree], scopeNestingLevel: Int)(implicit ctx: Context) {
-case class ImportInfo(sym: Symbol, selectors: List[Tree], scopeNestingLevel: Int) {
/** The (TermRef) type of the qualifier of the import clause */
def site(implicit ctx: Context): Type = {
val ImportType(expr) = sym.info
expr.tpe
}
+
+ /** The names that are excluded from any wildcard import */
+ def excluded: Set[TermName] = { ensureInitialized(); myExcluded }
+
+ /** A mapping from renamed to original names */
+ def reverseMapping: SimpleMap[TermName, TermName] = { ensureInitialized(); myMapped }
+
+ /** The original names imported by-name before renaming */
+ def originals: Set[TermName] = { ensureInitialized(); myOriginals }
+
+ /** Does the import clause end with wildcard? */
+ def wildcardImport = { ensureInitialized(); myWildcardImport }
+
+ private var myExcluded: Set[TermName] = null
+ private var myMapped: SimpleMap[TermName, TermName] = null
+ private var myOriginals: Set[TermName] = null
+ private var myWildcardImport: Boolean = false
+
+ /** Compute info relating to the selector list */
+ private def ensureInitialized(): Unit = if (myExcluded == null) {
+ myExcluded = Set()
+ myMapped = SimpleMap.Empty
+ myOriginals = Set()
+ def recur(sels: List[untpd.Tree]): Unit = sels match {
+ case sel :: sels1 =>
+ sel match {
+ case Pair(Ident(name: TermName), Ident(nme.WILDCARD)) =>
+ myExcluded += name
+ case Pair(Ident(from: TermName), Ident(to: TermName)) =>
+ myMapped = myMapped.updated(to, from)
+ myOriginals += from
+ case Ident(nme.WILDCARD) =>
+ myWildcardImport = true
+ case Ident(name: TermName) =>
+ myMapped = myMapped.updated(name, name)
+ myOriginals += name
+ }
+ recur(sels1)
+ case nil =>
+ }
+ recur(selectors)
+ }
+
+ /** The implicit references imported by this import clause */
+ def importedImplicits: Set[TermRef] = {
+ val pre = site
+ if (wildcardImport) {
+ val refs = pre.implicitMembers
+ if (excluded.isEmpty) refs
+ 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
+ }
} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 43589aac5..56469e604 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -4,7 +4,7 @@ package typer
import core._
import ast._
-import Trees._, Constants._, StdNames._, Scopes._
+import Trees._, Constants._, StdNames._, Scopes._, Denotations._
import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
import ast.desugar, ast.desugar._
import util.Positions._
@@ -22,6 +22,10 @@ trait NamerContextOps { this: Context =>
}
sym
}
+
+ def lookup(name: Name): PreDenotation =
+ if (isClassDefContext) owner.asClass.membersNamed(name)
+ else scope.denotsNamed(name)
}
/** This class attaches creates symbols from definitions and imports and gives them
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 1dd49ca9f..3054ba9cd 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -167,21 +167,16 @@ class Typer extends Namer with Applications with Implicits {
}
/** The type representing a wildcard import with enclosing name when imported
- * from given `site` and `selectors`.
+ * from given import info
*/
- def wildImportRef(site: Type, selectors: List[untpd.Tree]): Type = {
- def wildPermitted(selectors: List[untpd.Tree]): Boolean = selectors match {
- case Trees.Pair(Trees.Ident(`name`), Trees.Ident(nme.WILDCARD)) :: _ => false
- case Trees.Ident(nme.WILDCARD) :: _ => true
- case _ :: rest => wildPermitted(rest)
- case nil => false
- }
- if (wildPermitted(selectors)) {
- val denot = site.member(name)
- if (denot.exists) return NamedType(site, name).withDenot(denot)
+ def wildImportRef(imp: ImportInfo): Type =
+ if (imp.wildcardImport && !(imp.excluded contains name.toTermName)) {
+ val pre = imp.site
+ val denot = pre.member(name)
+ if (denot.exists) return NamedType(pre, name).withDenot(denot)
+ else NoType
}
- NoType
- }
+ else NoType
/** Is (some alternative of) the given predenotation `denot`
* defined in current compilation unit?
@@ -213,7 +208,7 @@ class Typer extends Namer with Applications with Implicits {
if (namedImp.exists)
return findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
if (prevPrec < wildImport) {
- val wildImp = wildImportRef(curImport.site, curImport.selectors)
+ val wildImp = wildImportRef(curImport)
if (wildImp.exists)
return findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
}
diff --git a/src/dotty/tools/dotc/util/Set.scala b/src/dotty/tools/dotc/util/Set.scala
index 4183186ed..231d862f9 100644
--- a/src/dotty/tools/dotc/util/Set.scala
+++ b/src/dotty/tools/dotc/util/Set.scala
@@ -6,7 +6,7 @@ package dotty.tools.dotc.util
/** A common class for lightweight sets.
*/
-abstract class Set[T <: AnyRef] {
+abstract class Set[T >: Null] {
def findEntry(x: T): T
@@ -19,7 +19,7 @@ abstract class Set[T <: AnyRef] {
def apply(x: T): Boolean = contains(x)
def contains(x: T): Boolean =
- findEntry(x) ne null
+ findEntry(x) != null
def toList = iterator.toList