diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 7794d36c87e3..8f49f37b35dd 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -53,7 +53,7 @@ class Run(comp: Compiler, ictx: Context) { ctx.initialize()(start) // re-initialize the base context with start def addImport(ctx: Context, refFn: () => TermRef) = ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx)) - (start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport) + (start.setRunInfo(new RunInfo) /: defn.RootImportFns)(addImport) } protected[this] implicit val ctx: Context = rootContext(ictx) @@ -194,9 +194,7 @@ class Run(comp: Compiler, ictx: Context) { object Run { /** Info that changes on each compiler run */ - class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo { - implicit val ctx: Context = initctx - + class RunInfo extends ImplicitRunInfo with ConstraintRunInfo { private[this] var myUnits: List[CompilationUnit] = _ private[this] var myUnitsCached: List[CompilationUnit] = _ private[this] var myFiles: Set[AbstractFile] = _ diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index fe5f5b6d4c62..09c37fe07968 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -217,7 +217,7 @@ object Contexts { else outer.implicits if (implicitRefs.isEmpty) outerImplicits - else new ContextualImplicits(implicitRefs, outerImplicits)(this) + else new ContextualImplicits(implicitRefs, outerImplicits, this) } implicitsCache } @@ -508,7 +508,7 @@ object Contexts { moreProperties = Map.empty store = initialStore .updated(settingsStateLoc, settings.defaultState) - .updated(runInfoLoc, new RunInfo(this)) + .updated(runInfoLoc, new RunInfo) typeComparer = new TypeComparer(this) searchHistory = new SearchHistory(0, Map()) gadt = EmptyGADTMap @@ -516,7 +516,7 @@ object Contexts { @sharable object NoContext extends Context { val base = null - override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this) + override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null, this) } /** A context base defines state and associated methods that exist once per diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f0298cf63804..4a3de831b02c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2014,7 +2014,7 @@ object Types { /** A reference to an implicit definition. This can be either a TermRef or a * Implicits.RenamedImplicitRef. */ - trait ImplicitRef { + trait ImplicitRef extends printing.Showable { def implicitName(implicit ctx: Context): TermName def underlyingRef: TermRef } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 6cd933a48dc9..50d1c7514a00 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -504,6 +504,21 @@ class PlainPrinter(_ctx: Context) extends Printer { } }.close // todo: override in refined printer + + def toText(ref: RenamedImplicitRef): Text = + "RenamedImplicitRef(" ~ toText(ref.underlyingRef) ~ ", " ~ toText(ref.alias) ~ ")" + + // This is only in Printer to have access to a Context + def toText(irefs: ImplicitRefs): Text = irefs match { + case irefs: OfTypeImplicits => + s"""OfTypeImplicits(${irefs.tp.show}): + | companions = ${irefs.companionRefs.toList.map(_.show).mkString(", ")} + | refs = ${irefs.refs.map(_.show).mkString(", ")}.""".stripMargin + case irefs: ContextualImplicits => + val own = s"ContextualImplicits: ${irefs.refs.map(_.show).mkString(", ")}" + if (irefs.isOuterMost) own else own + "\n " + irefs.outerImplicits.show + } + def toText(result: SearchResult): Text = result match { case result: SearchSuccess => "SearchSuccess: " ~ toText(result.ref) ~ " via " ~ toText(result.tree) diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index e0794627425a..30a29b9ad3f3 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -6,7 +6,7 @@ import core._ import Texts._, ast.Trees._ import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant, Names.Name, Denotations._, Annotations.Annotation -import typer.Implicits.SearchResult +import typer.Implicits.{ImplicitRefs, RenamedImplicitRef, SearchResult} import typer.ImportInfo /** The base class of all printers @@ -97,6 +97,12 @@ abstract class Printer { /** Textual representation of tree */ def toText[T >: Untyped](tree: Tree[T]): Text + /** Textual representation of a renamed implicit reference */ + def toText(irefs: RenamedImplicitRef): Text + + /** Textual representation of implicit references */ + def toText(irefs: ImplicitRefs): Text + /** Textual representation of implicit search result */ def toText(result: SearchResult): Text diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 302d19924d13..644bb49fa708 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -49,8 +49,10 @@ object Implicits { /** An implicit definition `implicitRef` that is visible under a different name, `alias`. * Gets generated if an implicit ref is imported via a renaming import. */ - class RenamedImplicitRef(val underlyingRef: TermRef, val alias: TermName) extends ImplicitRef { + class RenamedImplicitRef(val underlyingRef: TermRef, val alias: TermName) extends ImplicitRef with Showable { def implicitName(implicit ctx: Context): TermName = alias + + def toText(printer: Printer): Text = printer.toText(this) } /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ @@ -61,15 +63,12 @@ object Implicits { /** A common base class of contextual implicits and of-type implicits which * represents a set of references to implicit definitions. */ - abstract class ImplicitRefs(initctx: Context) { - implicit val ctx: Context = - if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled - + abstract class ImplicitRefs extends Showable { /** The nesting level of this context. Non-zero only in ContextialImplicits */ def level: Int = 0 /** The implicit references */ - def refs: List[ImplicitRef] + def refs(implicit ctx: Context): List[ImplicitRef] /** Return those references in `refs` that are compatible with type `pt`. */ protected def filterMatching(pt: Type)(implicit ctx: Context): List[Candidate] = track("filterMatching") { @@ -146,71 +145,85 @@ object Implicits { if (refs.isEmpty) Nil else { - val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) + val nestedCtx = ctx.fresh + .retractMode(Mode.ImplicitsEnabled) + .addMode(Mode.TypevarsMissContext) refs .filter(ref => nestedCtx.typerState.test(refMatches(ref.underlyingRef)(nestedCtx))) .map(Candidate(_, level)) } } + + def toText(printer: Printer): Text = printer.toText(this) } /** The implicit references coming from the implicit scope of a type. * @param tp the type determining the implicit scope * @param companionRefs the companion objects in the implicit scope. */ - class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { - assert(initctx.typer != null) - lazy val refs: List[ImplicitRef] = { - val buf = new mutable.ListBuffer[TermRef] - for (companion <- companionRefs) buf ++= companion.implicitMembers - buf.toList + class OfTypeImplicits(val tp: Type, val companionRefs: TermRefSet) extends ImplicitRefs { + private[this] var myRefs: List[TermRef] = _ + override def refs(implicit ctx: Context): List[ImplicitRef] = { + if (myRefs == null) { + val buf = new mutable.ListBuffer[TermRef] + for (companion <- companionRefs) buf ++= companion.implicitMembers + myRefs = buf.toList + } + myRefs } + private[this] var myEligible: List[Candidate] = _ /** The candidates that are eligible for expected type `tp` */ - lazy val eligible: List[Candidate] = - /*>|>*/ track("eligible in tpe") /*<|<*/ { - /*>|>*/ trace(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ { - if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length) - filterMatching(tp) + def eligible(implicit ctx: Context): List[Candidate] = { + if (myEligible == null) { + /*>|>*/ track("eligible in tpe") /*<|<*/ { + /*>|>*/ trace(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ { + if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length) + myEligible = filterMatching(tp) + } } } - - override def toString = - i"OfTypeImplicits($tp), companions = ${companionRefs.toList}%, %; refs = $refs%, %." + myEligible + } } /** The implicit references coming from the context. - * @param refs the implicit references made visible by the current context. + * @param myRefs the implicit references made visible by the current context. * Note: The name of the reference might be different from the name of its symbol. * In the case of a renaming import a => b, the name of the reference is the renamed * name, b, whereas the name of the symbol is the original name, a. * @param outerCtx the next outer context that makes visible further implicits */ - class ContextualImplicits(val refs: List[ImplicitRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) { + class ContextualImplicits(myRefs: List[ImplicitRef], val outerImplicits: ContextualImplicits, private val ictx: Context) extends ImplicitRefs { private val eligibleCache = new mutable.AnyRefMap[Type, List[Candidate]] + override def refs(implicit ctx: Context) = myRefs + /** The level increases if current context has a different owner or scope than * the context of the next-outer ImplicitRefs. This is however disabled under * Scala2 mode, since we do not want to change the implicit disambiguation then. */ - override val level: Int = + override val level: Int = { + implicit val ctx: Context = ictx + if (outerImplicits == null) 1 - else if (ctx.scala2Mode || - (ctx.owner eq outerImplicits.ctx.owner) && - (ctx.scope eq outerImplicits.ctx.scope) && + else if (ictx.scala2Mode || + (ictx.owner eq outerImplicits.ictx.owner) && + (ictx.scope eq outerImplicits.ictx.scope) && !refs.head.implicitName.is(LazyImplicitName)) outerImplicits.level else outerImplicits.level + 1 + } /** Is this the outermost implicits? This is the case if it either the implicits * of NoContext, or the last one before it. */ - private def isOuterMost = { + def isOuterMost = { val finalImplicits = NoContext.implicits (this eq finalImplicits) || (outerImplicits eq finalImplicits) } /** The implicit references that are eligible for type `tp`. */ - def eligible(tp: Type): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { + def eligible(tp: Type)(implicit ctx: Context): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { if (tp.hash == NotCached) computeEligible(tp) else eligibleCache get tp match { case Some(eligibles) => @@ -222,7 +235,7 @@ object Implicits { if (monitored) record(s"elided eligible refs", elided(this)) eligibles case None => - if (ctx eq NoContext) Nil + if (ictx eq NoContext) Nil else { val savedEphemeral = ctx.typerState.ephemeral ctx.typerState.ephemeral = false @@ -236,7 +249,7 @@ object Implicits { } } - private def computeEligible(tp: Type): List[Candidate] = /*>|>*/ trace(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { + private def computeEligible(tp: Type)(implicit ctx: Context): List[Candidate] = /*>|>*/ trace(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { if (monitored) record(s"check eligible refs in ctx", refs.length) val ownEligible = filterMatching(tp) if (isOuterMost) ownEligible @@ -246,11 +259,6 @@ object Implicits { } } - override def toString = { - val own = i"(implicits: $refs%, %)" - if (isOuterMost) own else own + "\n " + outerImplicits - } - /** This context, or a copy, ensuring root import from symbol `root` * is not present in outer implicits. */ @@ -258,9 +266,12 @@ object Implicits { if (this == NoContext.implicits) this else { val outerExcluded = outerImplicits exclude root - if (ctx.importInfo.site.termSymbol == root) outerExcluded + + implicit val ctx: Context = ictx + + if (ictx.importInfo.site.termSymbol == root) outerExcluded else if (outerExcluded eq outerImplicits) this - else new ContextualImplicits(refs, outerExcluded)(ctx) + else new ContextualImplicits(myRefs, outerExcluded, ictx) } } @@ -376,14 +387,8 @@ trait ImplicitRunInfo { self: RunInfo => private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() - /** The implicit scope of a type `tp` - * @param liftingCtx A context to be used when computing the class symbols of - * a type. Types may contain type variables with their instances - * recorded in the current context. To find out the instance of - * a type variable, we need the current context, the current - * runinfo context does not do. - */ - def implicitScope(rootTp: Type, liftingCtx: Context): OfTypeImplicits = { + /** The implicit scope of a type `tp` */ + def implicitScope(rootTp: Type)(implicit ctx: Context): OfTypeImplicits = { val seen: mutable.Set[Type] = mutable.Set() val incomplete: mutable.Set[Type] = mutable.Set() @@ -395,7 +400,6 @@ trait ImplicitRunInfo { self: RunInfo => * abstract types are eliminated. */ object liftToClasses extends TypeMap { - override implicit protected val ctx: Context = liftingCtx override def stopAtStatic = true def apply(tp: Type) = tp match { case tp: TypeRef if tp.symbol.isAbstractOrAliasType => @@ -458,7 +462,7 @@ trait ImplicitRunInfo { self: RunInfo => if (companion.exists) addRef(companion.termRef) cls.classParents foreach addParentScope } - tp.classSymbols(liftingCtx) foreach addClassScope + tp.classSymbols foreach addClassScope case _ => // We exclude lower bounds to conform to SLS 7.2: // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound" @@ -484,7 +488,7 @@ trait ImplicitRunInfo { self: RunInfo => iscope(liftedTp, isLifted = true).companionRefs else collectCompanions(tp) - val result = new OfTypeImplicits(tp, refs)(ctx) + val result = new OfTypeImplicits(tp, refs) if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") else if (canCache && @@ -613,7 +617,7 @@ trait Implicits { self: Typer => def lazyImplicitCtx(lazyImplicit: Symbol): Context = { val lctx = ctx.fresh for (delayedRef <- ctx.property(DelayedImplicit)) - lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx)) + lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits, ctx)) lctx.setProperty(DelayedImplicit, lazyImplicit.termRef) } @@ -1055,7 +1059,7 @@ trait Implicits { self: Typer => } } - def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx) + def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp) } }