Skip to content

Commit 26af65f

Browse files
committed
Better handling of Contexts in RunInfo and Implicits
In several situations, instead of the common pattern in the compiler of using the enclosing Context available coming an implicit method parameter, we used some other Context. So far, this wasn't an issue but starting with the next commit it is important for FunProto#typedArgs to be called in a Context whose owner chain contains the Context where the FunProto was created. This motivated the following changes: - RunInfo does not actually need to keep a reference to the root Context: It was previously used by `ImplicitRunInfo#implicitScope`, but can be replaced by using the Context from the enclosing scope (which was already used in this method with the name `liftingCtx`). - OfTypeImplicits does not need to keep a reference to the root Context either, we can just replace its lazy vals by defs that cache their results. - ContextualImplicits does need to keep track of the Context from which its implicit references come from, but this Context itself does not have to be available implicitly in the class, instead the Context from the enclosing scope is used. This also requires replacing OfTypeImplicits#toString and ContextualImplicits#toString by a toText method in Printer to get access to a Context.
1 parent 223aa2a commit 26af65f

File tree

5 files changed

+68
-59
lines changed

5 files changed

+68
-59
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Run(comp: Compiler, ictx: Context) {
5454
ctx.initialize()(start) // re-initialize the base context with start
5555
def addImport(ctx: Context, refFn: () => TermRef) =
5656
ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx))
57-
(start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport)
57+
(start.setRunInfo(new RunInfo) /: defn.RootImportFns)(addImport)
5858
}
5959

6060
protected[this] implicit val ctx: Context = rootContext(ictx)

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ object Contexts {
218218
else
219219
outer.implicits
220220
if (implicitRefs.isEmpty) outerImplicits
221-
else new ContextualImplicits(implicitRefs, outerImplicits)(this)
221+
else new ContextualImplicits(implicitRefs, outerImplicits, this)
222222
}
223223
implicitsCache
224224
}
@@ -526,7 +526,7 @@ object Contexts {
526526
sstate = settings.defaultState
527527
tree = untpd.EmptyTree
528528
typeAssigner = TypeAssigner
529-
runInfo = new RunInfo(this)
529+
runInfo = new RunInfo
530530
diagnostics = None
531531
freshNames = new FreshNameCreator.Default
532532
moreProperties = Map.empty
@@ -537,7 +537,7 @@ object Contexts {
537537

538538
@sharable object NoContext extends Context {
539539
val base = null
540-
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this)
540+
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null, this)
541541
}
542542

543543
/** A context base defines state and associated methods that exist once per
@@ -690,9 +690,7 @@ object Contexts {
690690
}
691691

692692
/** Info that changes on each compiler run */
693-
class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
694-
implicit val ctx: Context = initctx
695-
}
693+
class RunInfo extends ImplicitRunInfo with ConstraintRunInfo
696694

697695
class GADTMap(initBounds: SimpleMap[Symbol, TypeBounds]) extends util.DotClass {
698696
private var myBounds = initBounds

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,18 @@ class PlainPrinter(_ctx: Context) extends Printer {
498498
}
499499
}.close // todo: override in refined printer
500500

501+
502+
// This is only in Printer to have access to a Context
503+
def toText(irefs: ImplicitRefs): Text = irefs match {
504+
case irefs: OfTypeImplicits =>
505+
s"""OfTypeImplicits(${irefs.tp.show}):
506+
| companions = ${irefs.companionRefs.toList.map(_.show).mkString(", ")}
507+
| refs = ${irefs.refs.map(_.show).mkString(", ")}.""".stripMargin
508+
case irefs: ContextualImplicits =>
509+
val own = s"ContextualImplicits: ${irefs.refs.map(_.show).mkString(", ")}"
510+
if (irefs.isOuterMost) own else own + "\n " + irefs.outerImplicits.show
511+
}
512+
501513
def toText(result: SearchResult): Text = result match {
502514
case result: SearchSuccess =>
503515
"SearchSuccess: " ~ toText(result.ref) ~ " via " ~ toText(result.tree)

compiler/src/dotty/tools/dotc/printing/Printer.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import core._
55
import Texts._, ast.Trees._
66
import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant,
77
Names.Name, Denotations._, Annotations.Annotation
8-
import typer.Implicits.SearchResult
8+
import typer.Implicits.{ImplicitRefs, SearchResult}
99
import typer.ImportInfo
1010

1111
/** The base class of all printers
@@ -96,6 +96,9 @@ abstract class Printer {
9696
/** Textual representation of tree */
9797
def toText[T >: Untyped](tree: Tree[T]): Text
9898

99+
/** Textual representation of implicit references */
100+
def toText(irefs: ImplicitRefs): Text
101+
99102
/** Textual representation of implicit search result */
100103
def toText(result: SearchResult): Text
101104

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,12 @@ object Implicits {
4848
/** A common base class of contextual implicits and of-type implicits which
4949
* represents a set of implicit references.
5050
*/
51-
abstract class ImplicitRefs(initctx: Context) {
52-
implicit val ctx: Context =
53-
if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled
54-
51+
abstract class ImplicitRefs extends Showable {
5552
/** The nesting level of this context. Non-zero only in ContextialImplicits */
5653
def level: Int = 0
5754

5855
/** The implicit references */
59-
def refs: List[TermRef]
56+
def refs(implicit ctx: Context): List[TermRef]
6057

6158
/** Return those references in `refs` that are compatible with type `pt`. */
6259
protected def filterMatching(pt: Type)(implicit ctx: Context): List[Candidate] = track("filterMatching") {
@@ -133,70 +130,80 @@ object Implicits {
133130

134131
if (refs.isEmpty) Nil
135132
else {
136-
val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext)
133+
val nestedCtx = ctx.fresh
134+
.retractMode(Mode.ImplicitsEnabled)
135+
.addMode(Mode.TypevarsMissContext)
137136
refs
138137
.filter(ref => nestedCtx.typerState.test(refMatches(ref)(nestedCtx)))
139138
.map(Candidate(_, level))
140139
}
141140
}
141+
142+
def toText(printer: Printer): Text = printer.toText(this)
142143
}
143144

144145
/** The implicit references coming from the implicit scope of a type.
145146
* @param tp the type determining the implicit scope
146147
* @param companionRefs the companion objects in the implicit scope.
147148
*/
148-
class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
149-
assert(initctx.typer != null)
150-
lazy val refs: List[TermRef] = {
151-
val buf = new mutable.ListBuffer[TermRef]
152-
for (companion <- companionRefs) buf ++= companion.implicitMembers
153-
buf.toList
149+
class OfTypeImplicits(val tp: Type, val companionRefs: TermRefSet) extends ImplicitRefs {
150+
private[this] var myRefs: List[TermRef] = _
151+
def refs(implicit ctx: Context): List[TermRef] = {
152+
if (myRefs == null) {
153+
val buf = new mutable.ListBuffer[TermRef]
154+
for (companion <- companionRefs) buf ++= companion.implicitMembers
155+
myRefs = buf.toList
156+
}
157+
myRefs
154158
}
155159

160+
private[this] var myEligible: List[Candidate] = _
156161
/** The candidates that are eligible for expected type `tp` */
157-
lazy val eligible: List[Candidate] =
158-
/*>|>*/ track("eligible in tpe") /*<|<*/ {
159-
/*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ {
160-
if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length)
161-
filterMatching(tp)
162+
def eligible(implicit ctx: Context): List[Candidate] = {
163+
if (myEligible == null) {
164+
/*>|>*/ track("eligible in tpe") /*<|<*/ {
165+
/*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ {
166+
if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length)
167+
myEligible = filterMatching(tp)
168+
}
162169
}
163170
}
164-
165-
override def toString =
166-
i"OfTypeImplicits($tp), companions = ${companionRefs.toList}%, %; refs = $refs%, %."
171+
myEligible
172+
}
167173
}
168174

169175
/** The implicit references coming from the context.
170-
* @param refs the implicit references made visible by the current context.
176+
* @param myRefs the implicit references made visible by the current context.
171177
* Note: The name of the reference might be different from the name of its symbol.
172178
* In the case of a renaming import a => b, the name of the reference is the renamed
173179
* name, b, whereas the name of the symbol is the original name, a.
174-
* @param outerCtx the next outer context that makes visible further implicits
175180
*/
176-
class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) {
181+
class ContextualImplicits(myRefs: List[TermRef], val outerImplicits: ContextualImplicits, private val ictx: Context) extends ImplicitRefs {
177182
private val eligibleCache = new mutable.AnyRefMap[Type, List[Candidate]]
178183

184+
override def refs(implicit ctx: Context) = myRefs
185+
179186
/** The level increases if current context has a different owner or scope than
180187
* the context of the next-outer ImplicitRefs. This is however disabled under
181188
* Scala2 mode, since we do not want to change the implicit disambiguation then.
182189
*/
183190
override val level: Int =
184191
if (outerImplicits == null) 1
185-
else if (ctx.scala2Mode ||
186-
(ctx.owner eq outerImplicits.ctx.owner) &&
187-
(ctx.scope eq outerImplicits.ctx.scope)) outerImplicits.level
192+
else if (ictx.scala2Mode ||
193+
(ictx.owner eq outerImplicits.ictx.owner) &&
194+
(ictx.scope eq outerImplicits.ictx.scope)) outerImplicits.level
188195
else outerImplicits.level + 1
189196

190197
/** Is this the outermost implicits? This is the case if it either the implicits
191198
* of NoContext, or the last one before it.
192199
*/
193-
private def isOuterMost = {
200+
def isOuterMost = {
194201
val finalImplicits = NoContext.implicits
195202
(this eq finalImplicits) || (outerImplicits eq finalImplicits)
196203
}
197204

198205
/** The implicit references that are eligible for type `tp`. */
199-
def eligible(tp: Type): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ {
206+
def eligible(tp: Type)(implicit ctx: Context): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ {
200207
if (tp.hash == NotCached) computeEligible(tp)
201208
else eligibleCache get tp match {
202209
case Some(eligibles) =>
@@ -208,7 +215,7 @@ object Implicits {
208215
if (monitored) record(s"elided eligible refs", elided(this))
209216
eligibles
210217
case None =>
211-
if (ctx eq NoContext) Nil
218+
if (ictx eq NoContext) Nil
212219
else {
213220
val savedEphemeral = ctx.typerState.ephemeral
214221
ctx.typerState.ephemeral = false
@@ -222,8 +229,8 @@ object Implicits {
222229
}
223230
}
224231

225-
private def computeEligible(tp: Type): List[Candidate] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ {
226-
if (monitored) record(s"check eligible refs in ctx", refs.length)
232+
private def computeEligible(tp: Type)(implicit ctx: Context): List[Candidate] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ {
233+
if (monitored) record(s"check eligible refs in ictx", refs.length)
227234
val ownEligible = filterMatching(tp)
228235
if (isOuterMost) ownEligible
229236
else ownEligible ::: {
@@ -232,21 +239,16 @@ object Implicits {
232239
}
233240
}
234241

235-
override def toString = {
236-
val own = i"(implicits: $refs%, %)"
237-
if (isOuterMost) own else own + "\n " + outerImplicits
238-
}
239-
240242
/** This context, or a copy, ensuring root import from symbol `root`
241243
* is not present in outer implicits.
242244
*/
243245
def exclude(root: Symbol): ContextualImplicits =
244246
if (this == NoContext.implicits) this
245247
else {
246248
val outerExcluded = outerImplicits exclude root
247-
if (ctx.importInfo.site.termSymbol == root) outerExcluded
249+
if (ictx.importInfo.site(ictx).termSymbol(ictx) == root) outerExcluded
248250
else if (outerExcluded eq outerImplicits) this
249-
else new ContextualImplicits(refs, outerExcluded)(ctx)
251+
else new ContextualImplicits(myRefs, outerExcluded, ictx)
250252
}
251253
}
252254

@@ -360,14 +362,8 @@ trait ImplicitRunInfo { self: RunInfo =>
360362

361363
private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]()
362364

363-
/** The implicit scope of a type `tp`
364-
* @param liftingCtx A context to be used when computing the class symbols of
365-
* a type. Types may contain type variables with their instances
366-
* recorded in the current context. To find out the instance of
367-
* a type variable, we need the current context, the current
368-
* runinfo context does not do.
369-
*/
370-
def implicitScope(rootTp: Type, liftingCtx: Context): OfTypeImplicits = {
365+
/** The implicit scope of a type `tp` */
366+
def implicitScope(rootTp: Type)(implicit ctx: Context): OfTypeImplicits = {
371367

372368
val seen: mutable.Set[Type] = mutable.Set()
373369
val incomplete: mutable.Set[Type] = mutable.Set()
@@ -379,7 +375,6 @@ trait ImplicitRunInfo { self: RunInfo =>
379375
* abstract types are eliminated.
380376
*/
381377
object liftToClasses extends TypeMap {
382-
override implicit protected val ctx: Context = liftingCtx
383378
override def stopAtStatic = true
384379
def apply(tp: Type) = tp match {
385380
case tp: TypeRef if tp.symbol.isAbstractOrAliasType =>
@@ -442,7 +437,7 @@ trait ImplicitRunInfo { self: RunInfo =>
442437
if (companion.exists) addRef(companion.termRef)
443438
cls.classParents foreach addParentScope
444439
}
445-
tp.classSymbols(liftingCtx) foreach addClassScope
440+
tp.classSymbols foreach addClassScope
446441
case _ =>
447442
// We exclude lower bounds to conform to SLS 7.2:
448443
// "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound"
@@ -468,7 +463,8 @@ trait ImplicitRunInfo { self: RunInfo =>
468463
iscope(liftedTp, isLifted = true).companionRefs
469464
else
470465
collectCompanions(tp)
471-
val result = new OfTypeImplicits(tp, refs)(ctx)
466+
val result = new OfTypeImplicits(tp, refs)
467+
472468
if (ctx.typerState.ephemeral)
473469
record("ephemeral cache miss: implicitScope")
474470
else if (canCache &&
@@ -606,7 +602,7 @@ trait Implicits { self: Typer =>
606602
def lazyImplicitCtx(lazyImplicit: Symbol): Context = {
607603
val lctx = ctx.fresh
608604
for (delayedRef <- ctx.property(DelayedImplicit))
609-
lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx))
605+
lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits, ctx))
610606
lctx.setProperty(DelayedImplicit, lazyImplicit.termRef)
611607
}
612608

@@ -909,7 +905,7 @@ trait Implicits { self: Typer =>
909905
}
910906
}
911907

912-
def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx)
908+
def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp)
913909
}
914910

915911
final class ExplainedImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context)

0 commit comments

Comments
 (0)