Skip to content

Commit adc4a54

Browse files
committed
Merge pull request #805 from dotty-staging/change-freeze-bounds
Change : freeze bounds
2 parents 5f7e290 + 3b60c3b commit adc4a54

15 files changed

+360
-54
lines changed

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
626626
loop(from.owner, from :: froms, to :: tos)
627627
else {
628628
//println(i"change owner ${from :: froms}%, % ==> $tos of $tree")
629-
new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree)
629+
new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.addMode(Mode.FutureDefsOK)).apply(tree)
630630
}
631631
}
632632
loop(from, Nil, to :: Nil)
@@ -652,7 +652,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
652652
traverseChildren(tree)
653653
}
654654
}
655-
traverser.traverse(tree)(ctx.withMode(Mode.FutureDefsOK))
655+
traverser.traverse(tree)(ctx.addMode(Mode.FutureDefsOK))
656656
tree
657657
}
658658

src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ trait ConstraintHandling {
2323
implicit val ctx: Context
2424

2525
protected def isSubType(tp1: Type, tp2: Type): Boolean
26+
protected def isSameType(tp1: Type, tp2: Type): Boolean
2627

2728
val state: TyperState
2829
import state.constraint
@@ -104,13 +105,20 @@ trait ConstraintHandling {
104105
up.forall(addOneBound(_, lo, isUpper = false))
105106
}
106107

107-
protected final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
108+
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
108109
val saved = frozenConstraint
109110
frozenConstraint = true
110111
try isSubType(tp1, tp2)
111112
finally frozenConstraint = saved
112113
}
113114

115+
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
116+
val saved = frozenConstraint
117+
frozenConstraint = true
118+
try isSameType(tp1, tp2)
119+
finally frozenConstraint = saved
120+
}
121+
114122
/** Test whether the lower bounds of all parameters in this
115123
* constraint are a solution to the constraint.
116124
*/

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,12 @@ object Contexts {
439439
}
440440

441441
implicit class ModeChanges(val c: Context) extends AnyVal {
442-
final def withMode(mode: Mode): Context =
442+
final def withModeBits(mode: Mode): Context =
443443
if (mode != c.mode) c.fresh.setMode(mode) else c
444444

445-
final def addMode(mode: Mode): Context = withMode(c.mode | mode)
446-
final def maskMode(mode: Mode): Context = withMode(c.mode & mode)
447-
final def retractMode(mode: Mode): Context = withMode(c.mode &~ mode)
445+
final def addMode(mode: Mode): Context = withModeBits(c.mode | mode)
446+
final def maskMode(mode: Mode): Context = withModeBits(c.mode & mode)
447+
final def retractMode(mode: Mode): Context = withModeBits(c.mode &~ mode)
448448
}
449449

450450
implicit class FreshModeChanges(val c: FreshContext) extends AnyVal {

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ object Denotations {
339339
val info1 = denot1.info
340340
val info2 = denot2.info
341341
val sameSym = sym1 eq sym2
342-
if (sameSym && info1 <:< info2) denot2
343-
else if (sameSym && info2 <:< info1) denot1
342+
if (sameSym && (info1 frozen_<:< info2)) denot2
343+
else if (sameSym && (info2 frozen_<:< info1)) denot1
344344
else {
345345
val jointSym =
346346
if (sameSym) sym1

src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
129129

130130
type This = OrderingConstraint
131131

132-
133132
// ----------- Basic indices --------------------------------------------------
134133

135134
/** The number of type parameters in the given entry array */
@@ -576,4 +575,22 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
576575
}
577576
Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")"))
578577
}
578+
579+
override def toString: String = {
580+
def entryText(tp: Type): String = tp match {
581+
case tp: TypeBounds => tp.toString
582+
case _ =>" := " + tp
583+
}
584+
val constrainedText =
585+
" constrained types = " + domainPolys.mkString("\n")
586+
val boundsText =
587+
" bounds = " + {
588+
val assocs =
589+
for (param <- domainParams)
590+
yield
591+
param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param))
592+
assocs.mkString("\n")
593+
}
594+
constrainedText + "\n" + boundsText
595+
}
579596
}

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ object SymDenotations {
15871587
if (isCachable(tp)) baseTypeRefCache.put(tp, basetp)
15881588
else baseTypeRefCache.remove(tp)
15891589
} else if (basetp == NoPrefix) {
1590+
baseTypeRefCache.put(tp, null)
15901591
throw CyclicReference(this)
15911592
}
15921593
basetp

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ class TypeApplications(val self: Type) extends AnyVal {
497497
val boundss = new mutable.ListBuffer[TypeBounds]
498498
for (sym <- boundSyms) {
499499
val bounds = sym.info.bounds
500-
if (!(TypeBounds.empty <:< bounds)) {
500+
if (!(TypeBounds.empty frozen_<:< bounds)) {
501501
boundNames += sym.name
502502
boundss += bounds
503503
}
@@ -574,7 +574,7 @@ class TypeApplications(val self: Type) extends AnyVal {
574574
// we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`.
575575
val pcore = etaCore(tp.parent, otherParams)
576576
val hkBounds = self.member(rname).info.bounds
577-
if (TypeBounds.empty <:< hkBounds) pcore
577+
if (TypeBounds.empty frozen_<:< hkBounds) pcore
578578
else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds)
579579
case _ =>
580580
val pcore = etaCore(tp.parent, tparams)

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -154,24 +154,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
154154
case tp1: NamedType =>
155155
val sym1 = tp1.symbol
156156
(if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol))
157-
ctx.erasedTypes || sym1.isStaticOwner ||
158-
{ // Implements: A # X <: B # X
159-
// if either A =:= B (i.e. A <: B and B <: A), or the following three conditions hold:
160-
// 1. X is a class type,
161-
// 2. B is a class type without abstract type members.
162-
// 3. A <: B.
163-
// Dealiasing is taken care of elsewhere.
164-
val pre1 = tp1.prefix
165-
val pre2 = tp2.prefix
166-
isSameType(pre1, pre2) ||
167-
sym1.isClass &&
168-
pre2.classSymbol.exists &&
169-
pre2.abstractTypeMembers.isEmpty &&
170-
isSubType(pre1, pre2)
171-
}
157+
ctx.erasedTypes || sym1.isStaticOwner || isSubType(tp1.prefix, tp2.prefix)
172158
else
173159
(tp1.name eq tp2.name) &&
174-
isSameType(tp1.prefix, tp2.prefix) &&
160+
isSubType(tp1.prefix, tp2.prefix) &&
175161
(tp1.signature == tp2.signature) &&
176162
!tp1.isInstanceOf[WithFixedSym] &&
177163
!tp2.isInstanceOf[WithFixedSym]
@@ -229,6 +215,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
229215
compareSuper
230216
case AndType(tp21, tp22) =>
231217
isSubType(tp1, tp21) && isSubType(tp1, tp22)
218+
case OrType(tp21, tp22) =>
219+
if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21)
220+
else secondTry(tp1, tp2)
232221
case TypeErasure.ErasedValueType(cls2, underlying2) =>
233222
def compareErasedValueType = tp1 match {
234223
case TypeErasure.ErasedValueType(cls1, underlying1) =>
@@ -291,6 +280,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
291280
isSubType(tp1.ref, tp2)
292281
case tp1: AnnotatedType =>
293282
isSubType(tp1.tpe, tp2)
283+
case AndType(tp11, tp12) =>
284+
if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2)
285+
else thirdTry(tp1, tp2)
294286
case OrType(tp11, tp12) =>
295287
isSubType(tp11, tp2) && isSubType(tp12, tp2)
296288
case ErrorType =>
@@ -353,6 +345,21 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
353345
}
354346
compareRefined
355347
case OrType(tp21, tp22) =>
348+
// Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22)
349+
// and analogously for T1 <: T21 | (T221 & T222)
350+
// `|' types to the right of <: are problematic, because
351+
// we have to choose one constraint set or another, which might cut off
352+
// solutions. The rewriting delays the point where we have to choose.
353+
tp21 match {
354+
case AndType(tp211, tp212) =>
355+
return isSubType(tp1, OrType(tp211, tp22)) && isSubType(tp1, OrType(tp212, tp22))
356+
case _ =>
357+
}
358+
tp22 match {
359+
case AndType(tp221, tp222) =>
360+
return isSubType(tp1, OrType(tp21, tp221)) && isSubType(tp1, OrType(tp21, tp222))
361+
case _ =>
362+
}
356363
eitherIsSubType(tp1, tp21, tp1, tp22) || fourthTry(tp1, tp2)
357364
case tp2 @ MethodType(_, formals2) =>
358365
def compareMethod = tp1 match {
@@ -454,6 +461,21 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
454461
isNewSubType(tp1.parent, tp2) ||
455462
needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil)
456463
case AndType(tp11, tp12) =>
464+
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
465+
// and analogously for T11 & (T121 | T122) & T12 <: T2
466+
// `&' types to the left of <: are problematic, because
467+
// we have to choose one constraint set or another, which might cut off
468+
// solutions. The rewriting delays the point where we have to choose.
469+
tp11 match {
470+
case OrType(tp111, tp112) =>
471+
return isSubType(AndType(tp111, tp12), tp2) && isSubType(AndType(tp112, tp12), tp2)
472+
case _ =>
473+
}
474+
tp12 match {
475+
case OrType(tp121, tp122) =>
476+
return isSubType(AndType(tp11, tp121), tp2) && isSubType(AndType(tp11, tp122), tp2)
477+
case _ =>
478+
}
457479
eitherIsSubType(tp11, tp2, tp12, tp2)
458480
case JavaArrayType(elem1) =>
459481
def compareJavaArray = tp2 match {
@@ -693,7 +715,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
693715
case formal1 :: rest1 =>
694716
formals2 match {
695717
case formal2 :: rest2 =>
696-
(isSameType(formal1, formal2)
718+
(isSameTypeWhenFrozen(formal1, formal2)
697719
|| isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass)
698720
|| isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) &&
699721
matchingParams(rest1, rest2, isJava1, isJava2)
@@ -1067,7 +1089,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10671089
case tp1: ClassInfo =>
10681090
tp2 match {
10691091
case tp2: ClassInfo =>
1070-
isSubType(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner)
1092+
isSubTypeWhenFrozen(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner)
10711093
case _ =>
10721094
false
10731095
}
@@ -1083,7 +1105,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10831105
tp2 match {
10841106
case tp2: MethodType =>
10851107
def asGoodParams(formals1: List[Type], formals2: List[Type]) =
1086-
(formals2 corresponds formals1)(isSubType)
1108+
(formals2 corresponds formals1)(isSubTypeWhenFrozen)
10871109
asGoodParams(tp1.paramTypes, tp2.paramTypes) &&
10881110
(!asGoodParams(tp2.paramTypes, tp1.paramTypes) ||
10891111
isAsGood(tp1.resultType, tp2.resultType))

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,11 @@ object Types {
595595
ctx.typeComparer.topLevelSubType(this, that)
596596
}
597597

598+
/** Is this type a subtype of that type? */
599+
final def frozen_<:<(that: Type)(implicit ctx: Context): Boolean = track("frozen_<:<") {
600+
ctx.typeComparer.isSubTypeWhenFrozen(this, that)
601+
}
602+
598603
/** Is this type the same as that type?
599604
* This is the case iff `this <:< that` and `that <:< this`.
600605
*/
@@ -625,9 +630,9 @@ object Types {
625630
case ExprType(_) | MethodType(Nil, _) => tp.resultType
626631
case _ => tp
627632
}
628-
this <:< that || {
633+
(this frozen_<:< that) || {
629634
val rthat = result(that)
630-
(rthat ne that) && result(this) <:< rthat
635+
(rthat ne that) && (result(this) frozen_<:< rthat)
631636
}
632637
}
633638

@@ -801,12 +806,18 @@ object Types {
801806
case _ => NoType
802807
}
803808

804-
/** The chain of underlying types as long as type is a TypeProxy.
809+
/** The iterator of underlying types as long as type is a TypeProxy.
805810
* Useful for diagnostics
806811
*/
807-
def underlyingChain(implicit ctx: Context): List[Type] = this match {
808-
case tp: TypeProxy => tp :: tp.underlying.underlyingChain
809-
case _ => Nil
812+
def underlyingIterator(implicit ctx: Context): Iterator[Type] = new Iterator[Type] {
813+
var current = Type.this
814+
var hasNext = true
815+
def next = {
816+
val res = current
817+
hasNext = current.isInstanceOf[TypeProxy]
818+
if (hasNext) current = current.asInstanceOf[TypeProxy].underlying
819+
res
820+
}
810821
}
811822

812823
/** A prefix-less refined this or a termRef to a new skolem symbol
@@ -2652,13 +2663,13 @@ object Types {
26522663
}
26532664

26542665
def & (that: TypeBounds)(implicit ctx: Context): TypeBounds =
2655-
if (this.lo <:< that.lo && that.hi <:< this.hi) that
2656-
else if (that.lo <:< this.lo && this.hi <:< that.hi) this
2666+
if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that
2667+
else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this
26572668
else TypeBounds(this.lo | that.lo, this.hi & that.hi)
26582669

26592670
def | (that: TypeBounds)(implicit ctx: Context): TypeBounds =
2660-
if (this.lo <:< that.lo && that.hi <:< this.hi) this
2661-
else if (that.lo <:< this.lo && this.hi <:< that.hi) that
2671+
if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this
2672+
else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that
26622673
else TypeBounds(this.lo & that.lo, this.hi | that.hi)
26632674

26642675
override def & (that: Type)(implicit ctx: Context) = that match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
166166
else {
167167
val constr = ctx.typerState.constraint
168168
val bounds =
169-
if (constr.contains(tp)) constr.fullBounds(tp.origin)
169+
if (constr.contains(tp)) constr.fullBounds(tp.origin)(ctx.addMode(Mode.Printing))
170170
else TypeBounds.empty
171171
"(" ~ toText(tp.origin) ~ "?" ~ toText(bounds) ~ ")"
172172
}

src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,18 @@ trait Reporting { this: Context =>
149149
else traceIndented[T](s"==> $question?", (res: Any) => s"<== $question = ${resStr(res)}")(op)
150150
}
151151

152-
def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = {
153-
var finalized = false
154-
var logctx = this
155-
while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer
156-
def finalize(result: Any, note: String) =
157-
if (!finalized) {
158-
base.indent -= 1
159-
logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note")
160-
finalized = true
161-
}
152+
def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T =
153+
if (ctx.mode.is(Mode.Printing)) op
154+
else {
155+
var finalized = false
156+
var logctx = this
157+
while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer
158+
def finalize(result: Any, note: String) =
159+
if (!finalized) {
160+
base.indent -= 1
161+
logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note")
162+
finalized = true
163+
}
162164
try {
163165
logctx.log(s"${base.indentTab * base.indent}$leading")
164166
base.indent += 1

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class TreeChecker extends Phase with SymTransformer {
6666
val cur = symd.linkedClass
6767
val prev = ctx.atPhase(ctx.phase.prev) {
6868
ct => {
69-
implicit val ctx: Context = ct.withMode(Mode.FutureDefsOK)
69+
implicit val ctx: Context = ct.addMode(Mode.FutureDefsOK)
7070
symd.symbol.linkedClass
7171
}
7272
}

test/test/DeSugarTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class DeSugarTest extends ParserTest {
3535
def transform(trees: List[Tree], mode: Mode)(implicit ctx: Context): List[Tree] = withMode(mode) { transform(trees) }
3636

3737
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
38-
val tree1 = desugar(tree)(ctx.withMode(curMode))
38+
val tree1 = desugar(tree)(ctx.withModeBits(curMode))
3939
tree1 match {
4040
case TypedSplice(t) =>
4141
tree1
File renamed without changes.

0 commit comments

Comments
 (0)