Skip to content

Commit f6d7a92

Browse files
committed
Fix scala#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 d78b8c0 commit f6d7a92

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
@@ -1161,36 +1161,38 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11611161
/** Is given method reference applicable to type arguments `targs` and argument trees `args`?
11621162
* @param resultType The expected result type of the application
11631163
*/
1164-
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
1164+
def isApplicableMethodRef(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
11651165
def isApp(implicit ctx: Context): Boolean =
11661166
new ApplicableToTrees(methRef, targs, args, resultType).success
11671167
if (keepConstraint) isApp else ctx.test(implicit ctx => isApp)
11681168
}
11691169

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

11821176
/** Is given type applicable to type arguments `targs` and argument trees `args`,
11831177
* possibly after inserting an `apply`?
11841178
* @param resultType The expected result type of the application
11851179
*/
1186-
def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
1187-
onMethod(tp, isApplicable(_, targs, args, resultType, keepConstraint))
1180+
def isApplicableType(tp: Type, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
1181+
onMethod(tp, isApplicableMethodRef(_, targs, args, resultType, keepConstraint))
11881182

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

11951197
private def onMethod(tp: Type, p: TermRef => Boolean)(implicit ctx: Context): Boolean = tp match {
11961198
case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] =>
@@ -1206,7 +1208,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12061208
*/
12071209
def hasExtensionMethod(tp: Type, name: TermName, argType: Type, resultType: Type)(implicit ctx: Context) = {
12081210
def qualifies(mbr: Denotation) =
1209-
mbr.exists && isApplicable(tp.select(name, mbr), argType :: Nil, resultType)
1211+
mbr.exists && isApplicableType(tp.select(name, mbr), argType :: Nil, resultType)
12101212
tp.memberBasedOnFlags(name, required = ExtensionMethod) match {
12111213
case mbr: SingleDenotation => qualifies(mbr)
12121214
case mbr => mbr.hasAltWith(qualifies(_))
@@ -1270,7 +1272,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12701272
val formals1 =
12711273
if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle)
12721274
else tp1.paramInfos
1273-
isApplicable(alt2, formals1, WildcardType) ||
1275+
isApplicableMethodRef(alt2, formals1, WildcardType) ||
12741276
tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType]
12751277
case tp1: PolyType => // (2)
12761278
val nestedCtx = ctx.fresh.setExploreTyperState()
@@ -1539,7 +1541,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15391541
}
15401542

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

15441546
val candidates = pt match {
15451547
case pt @ FunProto(args, resultType) =>
@@ -1549,7 +1551,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15491551
case x => x
15501552
}
15511553

1552-
def sizeFits(alt: TermRef, tp: Type): Boolean = tp.stripPoly match {
1554+
def sizeFits(alt: TermRef): Boolean = alt.widen.stripPoly match {
15531555
case tp: MethodType =>
15541556
val ptypes = tp.paramInfos
15551557
val numParams = ptypes.length
@@ -1559,12 +1561,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15591561
else if (numParams < numArgs) isVarArgs
15601562
else if (numParams > numArgs + 1) hasDefault
15611563
else isVarArgs || hasDefault
1562-
case _ =>
1563-
numArgs == 0
1564+
case tp =>
1565+
numArgs == 0 || onMethod(tp, sizeFits)
15641566
}
15651567

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

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

15771579
def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = {
15781580
val alts2 = alts.filter(alt =>
1579-
isDirectlyApplicable(alt, targs, args, resultType)
1581+
isDirectlyApplicableType(alt, targs, args, resultType)
15801582
)
15811583
if (alts2.isEmpty && !ctx.isAfterTyper)
15821584
alts.filter(alt =>
1583-
isApplicable(alt, targs, args, resultType, keepConstraint = false)
1585+
isApplicableType(alt, targs, args, resultType, keepConstraint = false)
15841586
)
15851587
else
15861588
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)