Skip to content

Commit 417f0c8

Browse files
committed
New interpolation scheme
Major changes from previous one: - We explicitly keep track in typer of which variables should and which should not be interpolated. This replaces searching trees for embedded variable definitions, which is fragile e.g. in the presence of eta expansion. - We compute variances starting with all variables found in the type, not just teh qualifying ones. The previous scheme caused some variance information to be missed, which caused some variables to be mis-classified as non-occurring. i4032.scala is a test case. Unfortunately, fixing this caused several other tricky inference failures because which were previously hidden because some variables were already instantiated prematurely. Examples were hamp.scala, hmap-covariant.scala, and i2300.scala. - We interpolate at the end of typedUnadapted instead of at the beginning of `adapt`. Managing instantiatable variables turned out easier this way.
1 parent d17cab4 commit 417f0c8

11 files changed

+230
-228
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package core
55
import Types._
66
import Flags._
77
import Contexts._
8-
import util.{SimpleIdentityMap, DotClass}
8+
import util.{SimpleIdentityMap, SimpleIdentitySet, DotClass}
99
import reporting._
1010
import printing.{Showable, Printer}
1111
import printing.Texts._
@@ -76,6 +76,11 @@ class TyperState(previous: TyperState /* | Null */) {
7676
/** The uninstantiated variables */
7777
def uninstVars = constraint.uninstVars
7878

79+
/** The set of uninstantiated type varibles which have this state as their owning state */
80+
private[this] var myOwnedVars: TypeVars = SimpleIdentitySet.empty
81+
def ownedVars = myOwnedVars
82+
def ownedVars_=(vs: TypeVars): Unit = myOwnedVars = vs
83+
7984
/** Gives for each instantiated type var that does not yet have its `inst` field
8085
* set, the instance value stored in the constraint. Storing instances in constraints
8186
* is done only in a temporary way for contexts that may be retracted
@@ -154,6 +159,7 @@ class TyperState(previous: TyperState /* | Null */) {
154159
constraint foreachTypeVar { tvar =>
155160
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
156161
}
162+
targetState.ownedVars ++= ownedVars
157163
targetState.gc()
158164
reporter.flush()
159165
isCommitted = true

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import Denotations._
1818
import Periods._
1919
import util.Positions.{Position, NoPosition}
2020
import util.Stats._
21-
import util.DotClass
21+
import util.{DotClass, SimpleIdentitySet}
2222
import reporting.diagnostic.Message
2323
import reporting.diagnostic.messages.CyclicReferenceInvolving
2424
import ast.tpd._
@@ -3369,7 +3369,10 @@ object Types {
33693369
private[core] def inst = myInst
33703370
private[core] def inst_=(tp: Type) = {
33713371
myInst = tp
3372-
if (tp.exists) owningState = null // no longer needed; null out to avoid a memory leak
3372+
if (tp.exists) {
3373+
owningState.get.ownedVars -= this
3374+
owningState = null // no longer needed; null out to avoid a memory leak
3375+
}
33733376
}
33743377

33753378
/** The state owning the variable. This is at first `creatorState`, but it can
@@ -3433,6 +3436,8 @@ object Types {
34333436
}
34343437
}
34353438

3439+
type TypeVars = SimpleIdentitySet[TypeVar]
3440+
34363441
// ------ ClassInfo, Type Bounds ------------------------------------------------------------
34373442

34383443
type TypeOrSymbol = AnyRef /* should be: Type | Symbol */

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -675,14 +675,16 @@ object Erasure {
675675
super.typedStats(stats1, exprOwner).filter(!_.isEmpty)
676676
}
677677

678-
override def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
678+
override def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree =
679679
trace(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) {
680-
assert(ctx.phase == ctx.erasurePhase.next, ctx.phase)
680+
assert(ctx.phase == ctx.erasurePhase || ctx.phase == ctx.erasurePhase.next, ctx.phase)
681681
if (tree.isEmpty) tree
682682
else if (ctx.mode is Mode.Pattern) tree // TODO: replace with assertion once pattern matcher is active
683683
else adaptToType(tree, pt)
684684
}
685-
}
685+
686+
override def simplify(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): tree.type = tree
687+
}
686688

687689
def takesBridges(sym: Symbol)(implicit ctx: Context) =
688690
sym.isClass && !sym.is(Flags.Trait | Flags.Package)

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,14 @@ class TreeChecker extends Phase with SymTransformer {
259259
tpdTree
260260
}
261261

262-
override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context): tpd.Tree = {
262+
override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): tpd.Tree = {
263263
val res = tree match {
264264
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[_] =>
265-
super.typedUnadapted(tree)
265+
super.typedUnadapted(tree, pt, locked)
266266
case _ if tree.isType =>
267267
promote(tree)
268268
case _ =>
269-
val tree1 = super.typedUnadapted(tree, pt)
269+
val tree1 = super.typedUnadapted(tree, pt, locked)
270270
def isSubType(tp1: Type, tp2: Type) =
271271
(tp1 eq tp2) || // accept NoType / NoType
272272
(tp1 <:< tp2)
@@ -435,7 +435,7 @@ class TreeChecker extends Phase with SymTransformer {
435435
override def ensureNoLocalRefs(tree: Tree, pt: Type, localSyms: => List[Symbol])(implicit ctx: Context): Tree =
436436
tree
437437

438-
override def adapt(tree: Tree, pt: Type)(implicit ctx: Context) = {
438+
override def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context) = {
439439
def isPrimaryConstructorReturn =
440440
ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass)
441441
if (ctx.mode.isExpr &&
@@ -449,6 +449,8 @@ class TreeChecker extends Phase with SymTransformer {
449449
})
450450
tree
451451
}
452+
453+
override def simplify(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): tree.type = tree
452454
}
453455

454456
/**

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
546546
init()
547547

548548
def addArg(arg: Tree, formal: Type): Unit =
549-
typedArgBuf += adaptInterpolated(arg, formal.widenExpr)
549+
typedArgBuf += adapt(arg, formal.widenExpr)
550550

551551
def makeVarArg(n: Int, elemFormal: Type): Unit = {
552552
val args = typedArgBuf.takeRight(n).toList
@@ -711,7 +711,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
711711
* part. Return an optional value to indicate success.
712712
*/
713713
def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(implicit ctx: Context): Option[Tree] =
714-
tryInsertImplicitOnQualifier(fun1, proto) flatMap { fun2 =>
714+
tryInsertImplicitOnQualifier(fun1, proto, ctx.typerState.ownedVars) flatMap { fun2 =>
715715
tryEither {
716716
implicit ctx => Some(simpleApply(fun2, proto)): Option[Tree]
717717
} {
@@ -1519,11 +1519,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15191519
* If the resulting trees all have the same type, return them instead of the original ones.
15201520
*/
15211521
def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = {
1522-
def adapt(tree: Tree, pt: Type): Tree = tree match {
1523-
case cdef: CaseDef => tpd.cpy.CaseDef(cdef)(body = adapt(cdef.body, pt))
1524-
case _ => adaptInterpolated(tree, pt)
1522+
def adaptDeep(tree: Tree, pt: Type): Tree = tree match {
1523+
case cdef: CaseDef => tpd.cpy.CaseDef(cdef)(body = adaptDeep(cdef.body, pt))
1524+
case _ => adapt(tree, pt)
15251525
}
1526-
if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt)
1526+
if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adaptDeep)
15271527
}
15281528

15291529
/** Apply a transformation `harmonize` on the results of operation `op`.

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,12 @@ object ErrorReporting {
120120
val found1 = dropJavaMethod(found)
121121
val expected1 = dropJavaMethod(expected)
122122
if ((found1 eq found) != (expected eq expected1) && (found1 <:< expected1))
123-
"\n(Note that Scala's and Java's representation of this type differs)"
123+
i"""
124+
|(Note that Scala's and Java's representation of this type differs)"""
124125
else if (ctx.settings.explainTypes.value)
125-
"\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_))
126+
i"""
127+
|${ctx.typerState.constraint}
128+
|${TypeComparer.explained((found <:< expected)(_))}"""
126129
else
127130
""
128131
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ trait Implicits { self: Typer =>
768768
case result: SearchSuccess =>
769769
result.tstate.commit()
770770
implicits.println(i"success: $result")
771-
implicits.println(i"committing ${result.tstate.constraint} yielding ${ctx.typerState.constraint} ${ctx.typerState.hashesStr}")
771+
implicits.println(i"committing ${result.tstate.constraint} yielding ${ctx.typerState.constraint} in ${ctx.typerState}")
772772
result
773773
case result: SearchFailure if result.isAmbiguous =>
774774
val deepPt = pt.deepenProto
@@ -828,11 +828,12 @@ trait Implicits { self: Typer =>
828828
def typedImplicit(cand: Candidate, contextual: Boolean)(implicit ctx: Context): SearchResult = track("typedImplicit") { trace(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) {
829829
val ref = cand.ref
830830
var generated: Tree = tpd.ref(ref).withPos(pos.startPos)
831+
val locked = ctx.typerState.ownedVars
831832
if (!argument.isEmpty)
832833
generated = typedUnadapted(
833834
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
834-
pt)
835-
val generated1 = adapt(generated, pt)
835+
pt, locked)
836+
val generated1 = adapt(generated, pt, locked)
836837
lazy val shadowing =
837838
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
838839
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())

0 commit comments

Comments
 (0)