Skip to content

Commit f1b8d1e

Browse files
smarterLinyxus
authored andcommitted
Merge the regular and capture-checking version of isSubInfo
The isSubInfo for capture-checking introduced in scala#15877 had the following TODO: // A relaxed version of subtyping for dependent functions where method types // are treated as contravariant. // TODO: Merge with isSubInfo in hasMatchingMember. Currently, we can't since // the isSubinfo of hasMatchingMember has problems dealing with PolyTypes // (---> orphan params during pickling) The orphan params error was due to the recursion on the result of the PolyTypes being done without first calling `compareTypeLambda`. After fixing this we can safely merge the two versions while keeping the new behavior for dependent and polymorphic function types hidden under the `captureChecking` feature since they're language changes. I'll open a separate PR to create a `relaxedSubtyping` feature for these improvements since their usefulness is independent of capture checking. The isSubInfo for capture-checking got two additional cases involving CapturingTypes in 3e690a8, I don't know what they're supposed to do, but moving those to the regular isSubInfo breaks various capture-checking tests: -- [E007] Type Mismatch Error: tests/pos-custom-args/captures/classes.scala:11:32 -------------------------------------- 11 | val c1: C{val n: B^{x}}^{x} = c0 | ^^ | Found: (c0 : C{val n: B^}^{x}) | Required: C{val n: B^{x}}^{x} So this commit breaks capture-checking and I don't know enough about what this code is supposed to do to fix it.
1 parent 5ab80b4 commit f1b8d1e

File tree

1 file changed

+32
-36
lines changed

1 file changed

+32
-36
lines changed

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

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import TypeOps.refineUsingParent
1010
import collection.mutable
1111
import util.{Stats, NoSourcePosition, EqHashMap}
1212
import config.Config
13-
import config.Feature.migrateTo3
13+
import config.Feature.{ccEnabled, migrateTo3}
1414
import config.Printers.{subtyping, gadts, matchTypes, noPrinter}
1515
import TypeErasure.{erasedLub, erasedGlb}
1616
import TypeApplications._
@@ -641,36 +641,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
641641
def compareRefined: Boolean =
642642
val tp1w = tp1.widen
643643

644-
if ctx.phase == Phases.checkCapturesPhase then
645-
646-
// A relaxed version of subtyping for dependent functions where method types
647-
// are treated as contravariant.
648-
// TODO: Merge with isSubInfo in hasMatchingMember. Currently, we can't since
649-
// the isSubinfo of hasMatchingMember has problems dealing with PolyTypes
650-
// (---> orphan params during pickling)
651-
def isSubInfo(info1: Type, info2: Type): Boolean = (info1, info2) match
652-
case (info1: PolyType, info2: PolyType) =>
653-
info1.paramNames.hasSameLengthAs(info2.paramNames)
654-
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1))
655-
case (info1: MethodType, info2: MethodType) =>
656-
matchingMethodParams(info1, info2, precise = false)
657-
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1))
658-
case (info1 @ CapturingType(parent1, refs1), info2: Type) =>
659-
subCaptures(refs1, info2.captureSet, frozenConstraint).isOK && sameBoxed(info1, info2, refs1)
660-
&& isSubInfo(parent1, info2)
661-
case (info1: Type, CapturingType(parent2, refs2)) =>
662-
val refs1 = info1.captureSet
663-
(refs1.isAlwaysEmpty || subCaptures(refs1, refs2, frozenConstraint).isOK) && sameBoxed(info1, info2, refs1)
664-
&& isSubInfo(info1, parent2)
665-
case _ =>
666-
isSubType(info1, info2)
667-
668-
if defn.isFunctionType(tp2) then
669-
tp1w.widenDealias match
670-
case tp1: RefinedType =>
671-
return isSubInfo(tp1.refinedInfo, tp2.refinedInfo)
672-
case _ =>
673-
674644
val skipped2 = skipMatching(tp1w, tp2)
675645
if (skipped2 eq tp2) || !Config.fastPathForRefinedSubtype then
676646
if containsAnd(tp1) then
@@ -2001,8 +1971,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
20011971
// conceivably dispatch without knowing precise parameter signatures. One can signal
20021972
// this by inheriting from the `scala.reflect.SignatureCanBeImprecise` marker trait,
20031973
// in which case the signature test is elided.
1974+
//
1975+
// Additionally, for refined function types we do not need to check
1976+
// signatures since they're erased in a special way, so we skip the
1977+
// signature check but only under `ccEnabled` for now.
1978+
// TODO: add a new relaxedSubtyping feature for this (and make a SIP)
1979+
// since this is useful in general, but is a language change.
20041980
def sigsOK(symInfo: Type, info2: Type) =
2005-
tp2.underlyingClassRef(refinementOK = true).member(name).exists
1981+
(ccEnabled && defn.isFunctionType(tp2))
1982+
|| tp2.underlyingClassRef(refinementOK = true).member(name).exists
20061983
|| tp2.derivesFrom(defn.WithoutPreciseParameterTypesClass)
20071984
|| symInfo.isInstanceOf[MethodType]
20081985
&& symInfo.signature.consistentParams(info2.signature)
@@ -2014,17 +1991,36 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
20141991
// but under the condition that signatures might have to match (see sigsOK)
20151992
// This relaxed version is needed to correctly compare dependent function types.
20161993
// See pos/i12211.scala.
2017-
def isSubInfo(info1: Type, info2: Type, symInfo: Type): Boolean =
1994+
def isSubInfo(info1: Type, info2: Type, symInfo1: Type): Boolean =
1995+
def fallback = inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
20181996
info2 match
1997+
case info2: PolyType if ccEnabled => // See TODO about `ccEnabled` in `sigsOK`, this should also go under `relaxedSubtyping`.
1998+
info1 match
1999+
case info1: PolyType =>
2000+
comparingTypeLambdas(info1, info2):
2001+
info1.paramNames.hasSameLengthAs(info2.paramNames)
2002+
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
2003+
// Signature checks are never necessary because polymorphic
2004+
// refinements are only allowed for the `apply` method of a
2005+
// PolyFunction.
2006+
case _ => fallback
20192007
case info2: MethodType =>
20202008
info1 match
20212009
case info1: MethodType =>
2022-
val symInfo1 = symInfo.stripPoly
20232010
matchingMethodParams(info1, info2, precise = false)
20242011
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
20252012
&& sigsOK(symInfo1, info2)
2026-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
2027-
case _ => inFrozenGadtIf(tp1IsSingleton) { isSubType(info1, info2) }
2013+
case _ => fallback
2014+
case info2 @ CapturingType(parent2, refs2) if ctx.phase == Phases.checkCapturesPhase =>
2015+
val refs1 = info1.captureSet
2016+
(refs1.isAlwaysEmpty || subCaptures(refs1, refs2, frozenConstraint).isOK) && sameBoxed(info1, info2, refs1)
2017+
&& isSubInfo(info1, parent2, symInfo1)
2018+
case _ =>
2019+
info1 match
2020+
case info1 @ CapturingType(parent1, refs1) if ctx.phase == Phases.checkCapturesPhase =>
2021+
subCaptures(refs1, info2.captureSet, frozenConstraint).isOK && sameBoxed(info1, info2, refs1)
2022+
&& isSubInfo(parent1, info2, symInfo1)
2023+
case _ => fallback
20282024

20292025
def qualifies(m: SingleDenotation): Boolean =
20302026
val info2 = tp2.refinedInfo

0 commit comments

Comments
 (0)