Skip to content

Commit 989163e

Browse files
committed
Fix #6450: Don't drop apply alternatives in overloading resolution
The overloading resolution algorithm dropped alternatives reachable through applys when testing for applicability. It did so in three situations - when comparing the arities of alternatives - when testing via isDirectlyApplicable - when testing via isApplicable The third problem stemmed from confusion between overloaded versions of `isApplicable` (it's ironical that overzealous use of overloading broke overloading resolution in the compiler!). The version for TermRefs would not consider `apply` insertion, but the version for Types would. This means that the overloading broke the Liskov substitution principle. If a general type happened to be a TermRef the more specific isApplicable version would kick in, which however did less than the original.
1 parent 9ed471c commit 989163e

File tree

3 files changed

+51
-23
lines changed

3 files changed

+51
-23
lines changed

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

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,36 +1163,38 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11631163
/** Is given method reference applicable to type arguments `targs` and argument trees `args`?
11641164
* @param resultType The expected result type of the application
11651165
*/
1166-
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
1166+
def isApplicableMethodRef(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
11671167
def isApp(implicit ctx: Context): Boolean =
11681168
new ApplicableToTrees(methRef, targs, args, resultType).success
11691169
if (keepConstraint) isApp else ctx.test(implicit ctx => isApp)
11701170
}
11711171

1172-
/** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views?
1173-
* @param resultType The expected result type of the application
1174-
*/
1175-
def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1176-
ctx.test(implicit ctx => new ApplicableToTreesDirectly(methRef, targs, args, resultType).success)
1177-
11781172
/** Is given method reference applicable to argument types `args`?
11791173
* @param resultType The expected result type of the application
11801174
*/
1181-
def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
1175+
def isApplicableMethodRef(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
11821176
ctx.test(implicit ctx => new ApplicableToTypes(methRef, args, resultType).success)
11831177

11841178
/** Is given type applicable to type arguments `targs` and argument trees `args`,
11851179
* possibly after inserting an `apply`?
11861180
* @param resultType The expected result type of the application
11871181
*/
1188-
def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
1189-
onMethod(tp, isApplicable(_, targs, args, resultType, keepConstraint))
1182+
def isApplicableType(tp: Type, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
1183+
onMethod(tp, isApplicableMethodRef(_, targs, args, resultType, keepConstraint))
11901184

11911185
/** Is given type applicable to argument types `args`, possibly after inserting an `apply`?
11921186
* @param resultType The expected result type of the application
11931187
*/
1194-
def isApplicable(tp: Type, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
1195-
onMethod(tp, isApplicable(_, args, resultType))
1188+
def isApplicableType(tp: Type, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
1189+
onMethod(tp, isApplicableMethodRef(_, args, resultType))
1190+
1191+
/** Is given method type applicable to type arguments `targs` and argument trees `args` without inferring views,
1192+
* possibly after inserting an `apply`?
1193+
* @param resultType The expected result type of the application
1194+
*/
1195+
def isDirectlyApplicableType(tp: Type, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1196+
onMethod(tp, methRef =>
1197+
ctx.test(implicit ctx => new ApplicableToTreesDirectly(methRef, targs, args, resultType).success))
11961198

11971199
private def onMethod(tp: Type, p: TermRef => Boolean)(implicit ctx: Context): Boolean = tp match {
11981200
case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] =>
@@ -1208,7 +1210,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12081210
*/
12091211
def hasExtensionMethod(tp: Type, name: TermName, argType: Type, resultType: Type)(implicit ctx: Context) = {
12101212
def qualifies(mbr: Denotation) =
1211-
mbr.exists && isApplicable(tp.select(name, mbr), argType :: Nil, resultType)
1213+
mbr.exists && isApplicableType(tp.select(name, mbr), argType :: Nil, resultType)
12121214
tp.memberBasedOnFlags(name, required = ExtensionMethod) match {
12131215
case mbr: SingleDenotation => qualifies(mbr)
12141216
case mbr => mbr.hasAltWith(qualifies(_))
@@ -1272,7 +1274,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12721274
val formals1 =
12731275
if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle)
12741276
else tp1.paramInfos
1275-
isApplicable(alt2, formals1, WildcardType) ||
1277+
isApplicableMethodRef(alt2, formals1, WildcardType) ||
12761278
tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType]
12771279
case tp1: PolyType => // (2)
12781280
val nestedCtx = ctx.fresh.setExploreTyperState()
@@ -1541,7 +1543,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15411543
}
15421544

15431545
def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
1544-
alts filter (isApplicable(_, argTypes, resultType))
1546+
alts filter (isApplicableType(_, argTypes, resultType))
15451547

15461548
val candidates = pt match {
15471549
case pt @ FunProto(args, resultType) =>
@@ -1551,7 +1553,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15511553
case x => x
15521554
}
15531555

1554-
def sizeFits(alt: TermRef, tp: Type): Boolean = tp.stripPoly match {
1556+
def sizeFits(alt: TermRef): Boolean = alt.widen.stripPoly match {
15551557
case tp: MethodType =>
15561558
val ptypes = tp.paramInfos
15571559
val numParams = ptypes.length
@@ -1561,12 +1563,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15611563
else if (numParams < numArgs) isVarArgs
15621564
else if (numParams > numArgs + 1) hasDefault
15631565
else isVarArgs || hasDefault
1564-
case _ =>
1565-
numArgs == 0
1566+
case tp =>
1567+
numArgs == 0 || onMethod(tp, sizeFits)
15661568
}
15671569

15681570
def narrowBySize(alts: List[TermRef]): List[TermRef] =
1569-
alts filter (alt => sizeFits(alt, alt.widen))
1571+
alts.filter(sizeFits)
15701572

15711573
def narrowByShapes(alts: List[TermRef]): List[TermRef] = {
15721574
if (normArgs exists untpd.isFunctionWithUnknownParamType)
@@ -1578,11 +1580,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15781580

15791581
def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = {
15801582
val alts2 = alts.filter(alt =>
1581-
isDirectlyApplicable(alt, targs, args, resultType)
1583+
isDirectlyApplicableType(alt, targs, args, resultType)
15821584
)
15831585
if (alts2.isEmpty && !ctx.isAfterTyper)
15841586
alts.filter(alt =>
1585-
isApplicable(alt, targs, args, resultType, keepConstraint = false)
1587+
isApplicableType(alt, targs, args, resultType, keepConstraint = false)
15861588
)
15871589
else
15881590
alts2

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ object ProtoTypes {
253253
def isPoly(tree: Tree) = tree.tpe.widenSingleton.isInstanceOf[PolyType]
254254
// See remark in normalizedCompatible for why we can't keep the constraint
255255
// if one of the arguments has a PolyType.
256-
typer.isApplicable(tp, Nil, args, resultType, keepConstraint && !args.exists(isPoly))
256+
typer.isApplicableType(tp, Nil, args, resultType, keepConstraint && !args.exists(isPoly))
257257
}
258258

259259
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer): FunProto =
@@ -399,7 +399,7 @@ object ProtoTypes {
399399
override def resultType(implicit ctx: Context): Type = resType
400400

401401
def isMatchedBy(tp: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
402-
ctx.typer.isApplicable(tp, argType :: Nil, resultType) || {
402+
ctx.typer.isApplicableType(tp, argType :: Nil, resultType) || {
403403
resType match {
404404
case SelectionProto(name: TermName, mbrType, _, _) =>
405405
ctx.typer.hasExtensionMethod(tp, name, argType, mbrType)

tests/pos/i6450.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Foo {
2+
def A(i: Double) = i
3+
object A {
4+
def apply(i: Int): Int = i+1
5+
}
6+
def B(i: Any) = i
7+
object B {
8+
def apply(i: Int) = i+1
9+
}
10+
def C(i: Int) = i + 1
11+
object C {
12+
def apply(i: Double): Double = i
13+
}
14+
def foo = A(0)
15+
def bar = B(1)
16+
def baz = C(2)
17+
}
18+
19+
object Test {
20+
val x = new Foo().foo
21+
val y = new Foo().bar
22+
val z = new Foo().baz
23+
val x1: Int = x
24+
val y1: Int = y
25+
val z1: Int = z
26+
}

0 commit comments

Comments
 (0)