Skip to content

Commit f065d75

Browse files
committed
Keep track of enclosing capture sets when comparing refined types
When comparing refined types C{x: T}^cs <:< C{x: T'}^cs' we need to remember the capture set `cs` on the left hand side when we get to ask whether the refined type has a member `x: T'`, so that we can correctly instantiate any local root `cap[C]` in the member type of `x`. Same if the LHS is simply `C^cs`. In both cases we strip the capture set before we get to take the compute `x`, so we have to re-add it at the point of that member computation.
1 parent e7d3b92 commit f065d75

File tree

1 file changed

+52
-18
lines changed

1 file changed

+52
-18
lines changed

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

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
6868
private var myInstance: TypeComparer = this
6969
def currentInstance: TypeComparer = myInstance
7070

71+
/** All capturing types in the original `tp1` enclosing the currently
72+
* compared type.
73+
*/
74+
private var enclosingCapturing1: List[AnnotatedType] = Nil
75+
7176
/** Is a subtype check in progress? In that case we may not
7277
* permanently instantiate type variables, because the corresponding
7378
* constraint might still be retracted and the instantiation should
@@ -256,7 +261,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
256261
report.log(explained(_.isSubType(tp1, tp2, approx)))
257262
}
258263
// Eliminate LazyRefs before checking whether we have seen a type before
259-
val normalize = new TypeMap {
264+
val normalize = new TypeMap with CaptureSet.IdempotentCaptRefMap {
260265
val DerefLimit = 10
261266
var derefCount = 0
262267
def apply(t: Type) = t match {
@@ -538,17 +543,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
538543

539544
res
540545

541-
case CapturingType(parent1, refs1) =>
542-
if tp2.isAny then true
543-
else if subCaptures(refs1, tp2.captureSet, frozenConstraint).isOK && sameBoxed(tp1, tp2, refs1)
544-
|| !ctx.mode.is(Mode.CheckBoundsOrSelfType) && tp1.isAlwaysPure
545-
then
546-
val tp2a =
547-
if tp1.isBoxedCapturing && !parent1.isBoxedCapturing
548-
then tp2.unboxed
549-
else tp2
550-
recur(parent1, tp2a)
551-
else thirdTry
546+
case tp1 @ CapturingType(parent1, refs1) =>
547+
def compareCapturing =
548+
if tp2.isAny then true
549+
else if subCaptures(refs1, tp2.captureSet, frozenConstraint).isOK && sameBoxed(tp1, tp2, refs1)
550+
|| !ctx.mode.is(Mode.CheckBoundsOrSelfType) && tp1.isAlwaysPure
551+
then
552+
val tp2a =
553+
if tp1.isBoxedCapturing && !parent1.isBoxedCapturing
554+
then tp2.unboxed
555+
else tp2
556+
try
557+
enclosingCapturing1 = tp1 :: enclosingCapturing1
558+
recur(parent1, tp2a)
559+
finally
560+
enclosingCapturing1 = enclosingCapturing1.tail
561+
else thirdTry
562+
compareCapturing
552563
case tp1: AnnotatedType if !tp1.isRefining =>
553564
recur(tp1.parent, tp2)
554565
case tp1: MatchType =>
@@ -660,10 +671,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
660671
case (info1: MethodType, info2: MethodType) =>
661672
matchingMethodParams(info1, info2, precise = false)
662673
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1))
663-
case (info1 @ CapturingType(parent1, refs1), info2: Type) =>
674+
case (info1 @ CapturingType(parent1, refs1), info2: Type)
675+
if info2.stripCapturing.isInstanceOf[MethodOrPoly] =>
664676
subCaptures(refs1, info2.captureSet, frozenConstraint).isOK && sameBoxed(info1, info2, refs1)
665677
&& isSubInfo(parent1, info2)
666-
case (info1: Type, CapturingType(parent2, refs2)) =>
678+
case (info1: Type, CapturingType(parent2, refs2))
679+
if info1.stripCapturing.isInstanceOf[MethodOrPoly] =>
667680
val refs1 = info1.captureSet
668681
(refs1.isAlwaysEmpty || subCaptures(refs1, refs2, frozenConstraint).isOK) && sameBoxed(info1, info2, refs1)
669682
&& isSubInfo(info1, parent2)
@@ -672,7 +685,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
672685

673686
if defn.isFunctionType(tp2) then
674687
if tp2.derivesFrom(defn.PolyFunctionClass) then
675-
return isSubInfo(tp1.member(nme.apply).info, tp2.refinedInfo)
688+
return isSubInfo(tp1.ccMember(nme.apply).info, tp2.refinedInfo)
676689
else
677690
tp1w.widenDealias match
678691
case tp1: RefinedType =>
@@ -2028,7 +2041,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
20282041
* rebase both itself and the member info of `tp` on a freshly created skolem type.
20292042
*/
20302043
def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
2031-
trace(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.member(name).info}", subtyping) {
2044+
trace(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.ccMember(name).info}", subtyping) {
20322045

20332046
// If the member is an abstract type and the prefix is a path, compare the member itself
20342047
// instead of its bounds. This case is needed situations like:
@@ -2118,9 +2131,30 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
21182131
|| (tp1.isStable && m.symbol.isStableMember && isSubType(TermRef(tp1, m.symbol), tp2.refinedInfo))
21192132
end qualifies
21202133

2121-
tp1.member(name).hasAltWithInline(qualifies)
2134+
tp1.ccMember(name).hasAltWithInline(qualifies)
21222135
}
21232136

2137+
extension (qual: Type)
2138+
/** Add all directly enclosing capture sets to `qual` and select `name` on the
2139+
* resulting type. A capture set is directly enclosing if there is an enclosing
2140+
* capturing type with the set and all types between `qual` and that type
2141+
* are RefinedTypes or CapturingTypes.
2142+
*/
2143+
def ccMember(name: Name): Denotation =
2144+
def isEnclosing(tp: Type): Boolean = tp match
2145+
case RefinedType(parent, _, _) => isEnclosing(parent)
2146+
case CapturingType(parent, _) => isEnclosing(parent)
2147+
case _ => tp eq qual
2148+
2149+
def addCaptures(tp: Type, encls: List[AnnotatedType]): Type = encls match
2150+
case (ct @ CapturingType(parent, refs)) :: encls1 if isEnclosing(parent) =>
2151+
addCaptures(CapturingType(tp, refs, ct.isBoxedCapturing), encls1)
2152+
case _ =>
2153+
tp
2154+
2155+
addCaptures(qual, enclosingCapturing1).member(name)
2156+
end ccMember
2157+
21242158
final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match {
21252159
case tp: SingletonType if tp.isStable => tp
21262160
case tp: ValueType => SkolemType(tp)
@@ -3397,7 +3431,7 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
33973431
}
33983432

33993433
override def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
3400-
traceIndented(s"hasMatchingMember(${show(tp1)} . $name, ${show(tp2.refinedInfo)}), member = ${show(tp1.member(name).info)}") {
3434+
traceIndented(s"hasMatchingMember(${show(tp1)} . $name, ${show(tp2.refinedInfo)}), member = ${show(tp1.ccMember(name).info)}") {
34013435
super.hasMatchingMember(name, tp1, tp2)
34023436
}
34033437

0 commit comments

Comments
 (0)