diff options
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 46 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Flags.scala | 19 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Scopes.scala | 15 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TyperState.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 80 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 253 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ImportInfo.scala | 66 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 23 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/Set.scala | 4 |
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 |