Skip to content

Commit f6ff1fc

Browse files
committed
Direct comparisons of dependent function types
1 parent 1c4fef2 commit f6ff1fc

File tree

1 file changed

+78
-70
lines changed

1 file changed

+78
-70
lines changed

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

Lines changed: 78 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -605,10 +605,24 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
605605
isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2) &&
606606
recur(tp1, skipped2)
607607

608-
if ctx.phase == Phases.checkCapturesPhase && defn.isFunctionOrPolyType(tp2) then
609-
hasMatchingMember(nme.apply, tp1, tp2)
610-
else
611-
compareRefined
608+
def isSubInfo(info1: Type, info2: Type): Boolean = (info1, info2) match
609+
case (info1: PolyType, info2: PolyType) =>
610+
sameLength(info1.paramNames, info2.paramNames)
611+
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1))
612+
case (info1: MethodType, info2: MethodType) =>
613+
matchingMethodParams(info1, info2, precise = false)
614+
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1))
615+
case _ =>
616+
isSubType(info1, info2)
617+
618+
tp1 match
619+
case tp1: RefinedType
620+
if ctx.phase == Phases.checkCapturesPhase
621+
&& defn.isFunctionOrPolyType(tp1)
622+
&& defn.isFunctionOrPolyType(tp2) =>
623+
isSubInfo(tp1.refinedInfo, tp2.refinedInfo)
624+
case _ =>
625+
compareRefined
612626
case tp2: RecType =>
613627
def compareRec = tp1.safeDealias match {
614628
case tp1: RecType =>
@@ -1793,69 +1807,68 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
17931807
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
17941808
trace(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.member(name).info}", subtyping) {
17951809

1796-
def qualifies(m: SingleDenotation): Boolean =
1797-
// If the member is an abstract type and the prefix is a path, compare the member itself
1798-
// instead of its bounds. This case is needed situations like:
1799-
//
1800-
// class C { type T }
1801-
// val foo: C
1802-
// foo.type <: C { type T {= , <: , >:} foo.T }
1803-
//
1804-
// or like:
1805-
//
1806-
// class C[T]
1807-
// C[?] <: C[TV]
1808-
//
1809-
// where TV is a type variable. See i2397.scala for an example of the latter.
1810-
def matchAbstractTypeMember(info1: Type): Boolean = info1 match {
1811-
case TypeBounds(lo, hi) if lo ne hi =>
1812-
tp2.refinedInfo match {
1813-
case rinfo2: TypeBounds if tp1.isStable =>
1814-
val ref1 = tp1.widenExpr.select(name)
1815-
isSubType(rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
1816-
case _ =>
1817-
false
1818-
}
1819-
case _ => false
1820-
}
1810+
// If the member is an abstract type and the prefix is a path, compare the member itself
1811+
// instead of its bounds. This case is needed situations like:
1812+
//
1813+
// class C { type T }
1814+
// val foo: C
1815+
// foo.type <: C { type T {= , <: , >:} foo.T }
1816+
//
1817+
// or like:
1818+
//
1819+
// class C[T]
1820+
// C[?] <: C[TV]
1821+
//
1822+
// where TV is a type variable. See i2397.scala for an example of the latter.
1823+
def matchAbstractTypeMember(info1: Type): Boolean = info1 match {
1824+
case TypeBounds(lo, hi) if lo ne hi =>
1825+
tp2.refinedInfo match {
1826+
case rinfo2: TypeBounds if tp1.isStable =>
1827+
val ref1 = tp1.widenExpr.select(name)
1828+
isSubType(rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
1829+
case _ =>
1830+
false
1831+
}
1832+
case _ => false
1833+
}
18211834

1822-
// An additional check for type member matching: If the refinement of the
1823-
// supertype `tp2` does not refer to a member symbol defined in the parent of `tp2`.
1824-
// then the symbol referred to in the subtype must have a signature that coincides
1825-
// in its parameters with the refinement's signature. The reason for the check
1826-
// is that if the refinement does not refer to a member symbol, we will have to
1827-
// resort to reflection to invoke the member. And Java reflection needs to know exact
1828-
// erased parameter types. See neg/i12211.scala. Other reflection algorithms could
1829-
// conceivably dispatch without knowning precise parameter signatures. One can signal
1830-
// this by inheriting from the `scala.reflect.SignatureCanBeImprecise` marker trait,
1831-
// in which case the signature test is elided.
1832-
def sigsOK(symInfo: Type, info2: Type) =
1833-
tp2.underlyingClassRef(refinementOK = true).member(name).exists
1834-
|| tp2.derivesFrom(defn.WithoutPreciseParameterTypesClass)
1835-
|| symInfo.isInstanceOf[MethodType]
1836-
&& symInfo.signature.consistentParams(info2.signature)
1837-
1838-
// A relaxed version of isSubType, which compares method types
1839-
// under the standard arrow rule which is contravarient in the parameter types,
1840-
// but under the condition that signatures might have to match (see sigsOK)
1841-
// This relaxed version is needed to correctly compare dependent function types.
1842-
// See pos/i12211.scala.
1843-
def isSubInfo(info1: Type, info2: Type, symInfo: Type): Boolean =
1844-
info2 match
1845-
case info2: MethodType =>
1846-
info1 match
1847-
case info1: MethodType =>
1848-
val symInfo1 = symInfo.stripPoly
1849-
matchingMethodParams(info1, info2, precise = false)
1850-
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
1851-
&& sigsOK(symInfo1, info2)
1852-
case _ => isSubType(info1, info2)
1853-
case _ => isSubType(info1, info2)
1835+
// An additional check for type member matching: If the refinement of the
1836+
// supertype `tp2` does not refer to a member symbol defined in the parent of `tp2`.
1837+
// then the symbol referred to in the subtype must have a signature that coincides
1838+
// in its parameters with the refinement's signature. The reason for the check
1839+
// is that if the refinement does not refer to a member symbol, we will have to
1840+
// resort to reflection to invoke the member. And Java reflection needs to know exact
1841+
// erased parameter types. See neg/i12211.scala. Other reflection algorithms could
1842+
// conceivably dispatch without knowning precise parameter signatures. One can signal
1843+
// this by inheriting from the `scala.reflect.SignatureCanBeImprecise` marker trait,
1844+
// in which case the signature test is elided.
1845+
def sigsOK(symInfo: Type, info2: Type) =
1846+
tp2.underlyingClassRef(refinementOK = true).member(name).exists
1847+
|| tp2.derivesFrom(defn.WithoutPreciseParameterTypesClass)
1848+
|| symInfo.isInstanceOf[MethodType]
1849+
&& symInfo.signature.consistentParams(info2.signature)
1850+
1851+
// A relaxed version of isSubType, which compares method types
1852+
// under the standard arrow rule which is contravarient in the parameter types,
1853+
// but under the condition that signatures might have to match (see sigsOK)
1854+
// This relaxed version is needed to correctly compare dependent function types.
1855+
// See pos/i12211.scala.
1856+
def isSubInfo(info1: Type, info2: Type, symInfo: Type): Boolean =
1857+
info2 match
1858+
case info2: MethodType =>
1859+
info1 match
1860+
case info1: MethodType =>
1861+
val symInfo1 = symInfo.stripPoly
1862+
matchingMethodParams(info1, info2, precise = false)
1863+
&& isSubInfo(info1.resultType, info2.resultType.subst(info2, info1), symInfo1.resultType)
1864+
&& sigsOK(symInfo1, info2)
1865+
case _ => isSubType(info1, info2)
1866+
case _ => isSubType(info1, info2)
18541867

1868+
def qualifies(m: SingleDenotation): Boolean =
18551869
val info1 = m.info.widenExpr
18561870
isSubInfo(info1, tp2.refinedInfo.widenExpr, m.symbol.info.orElse(info1))
18571871
|| matchAbstractTypeMember(m.info)
1858-
end qualifies
18591872

18601873
tp1.member(name) match // inlined hasAltWith for performance
18611874
case mbr: SingleDenotation => qualifies(mbr)
@@ -1980,15 +1993,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
19801993
case formal2 :: rest2 =>
19811994
val formal2a = if (tp2.isParamDependent) formal2.subst(tp2, tp1) else formal2
19821995
val paramsMatch =
1983-
if ctx.phase == Phases.checkCapturesPhase then
1984-
// ^^^ TODO: make this more robust. We want isSubtypeWhenFrozen for the type parts
1985-
// of formals but isSameType for the capture sets. We cannot use isSubType for
1986-
// capture sets since that constrains inferred arguments not enough, and we
1987-
// cannot constrain them later since we would run into the "cannot constrain mapped
1988-
// type from new source" problem.
1989-
isSubType(formal2a, formal1)
1990-
else if precise then
1996+
if precise then
19911997
isSameTypeWhenFrozen(formal1, formal2a)
1998+
else if ctx.phase == Phases.checkCapturesPhase then
1999+
isSubType(formal2a, formal1)
19922000
else
19932001
isSubTypeWhenFrozen(formal2a, formal1)
19942002
paramsMatch && loop(rest1, rest2)

0 commit comments

Comments
 (0)