@@ -1169,6 +1169,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1169
1169
if (keepConstraint) isApp else ctx.test(implicit ctx => isApp)
1170
1170
}
1171
1171
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 isDirectlyApplicableMethodRef (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
+
1172
1178
/** Is given method reference applicable to argument types `args`?
1173
1179
* @param resultType The expected result type of the application
1174
1180
*/
@@ -1192,27 +1198,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1192
1198
isApplicableMethodRef(_, args, resultType)
1193
1199
}
1194
1200
1195
- /** Is given method type applicable to type arguments `targs` and argument trees `args` without inferring views,
1196
- * possibly after inserting an `apply`?
1197
- * @param resultType The expected result type of the application
1198
- */
1199
- def isDirectlyApplicableType (tp : Type , targs : List [Type ], args : List [Tree ], resultType : Type )(implicit ctx : Context ): Boolean =
1200
- onMethod(tp, targs.nonEmpty || args.nonEmpty) { methRef =>
1201
- ctx.test(implicit ctx => new ApplicableToTreesDirectly (methRef, targs, args, resultType).success)
1202
- }
1203
-
1204
1201
private def onMethod (tp : Type , followApply : Boolean )(p : TermRef => Boolean )(implicit ctx : Context ): Boolean = tp match {
1205
1202
case methRef : TermRef if methRef.widenSingleton.isInstanceOf [MethodicType ] =>
1206
1203
p(methRef)
1207
1204
case mt : MethodicType =>
1208
1205
p(mt.narrow)
1209
1206
case _ =>
1210
- followApply && hasApplyWith(tp)(p )
1207
+ followApply && tp.member(nme.apply).hasAltWith(d => p( TermRef (tp, nme.apply, d)) )
1211
1208
}
1212
1209
1213
- private def hasApplyWith (tp : Type )(p : TermRef => Boolean )(implicit ctx : Context ): Boolean =
1214
- tp.member(nme.apply).hasAltWith(d => p(TermRef (tp, nme.apply, d)))
1215
-
1216
1210
/** Does `tp` have an extension method named `name` with this-argument `argType` and
1217
1211
* result matching `resultType`?
1218
1212
*/
@@ -1257,10 +1251,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1257
1251
* If that tournament yields a draw, a tiebreak is applied where
1258
1252
* an alternative that takes more implicit parameters wins over one
1259
1253
* that takes fewer.
1260
- *
1261
- * @param followApply if true consider `apply` members when comparing with a method reference
1262
1254
*/
1263
- def compare (alt1 : TermRef , alt2 : TermRef , followApply : Boolean )(implicit ctx : Context ): Int = track(" compare" ) { trace(i " compare( $alt1, $alt2) " , overload) {
1255
+ def compare (alt1 : TermRef , alt2 : TermRef )(implicit ctx : Context ): Int = track(" compare" ) { trace(i " compare( $alt1, $alt2) " , overload) {
1264
1256
1265
1257
assert(alt1 ne alt2)
1266
1258
@@ -1275,78 +1267,44 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1275
1267
* assumption that for i = 1,...,n each ai is an abstract type name bounded
1276
1268
* from below by Li and from above by Ui.
1277
1269
* 3. A member of any other type `tp1` is:
1278
- * a. always as specific as a method or a polymorphic method
1270
+ * a. always as specific as a method or a polymorphic method.
1279
1271
* b. as specific as a member of any other type `tp2` if `tp1` is compatible
1280
1272
* with `tp2`.
1281
- *
1282
- * If followApply is true, and one of the alternatives is not a method, we test instead
1283
- * whether isAsSpecific is true for all apply methods in that alternative.
1284
- * Note that this is errs on the side of not being comparable in the following case:
1285
- *
1286
- * Alternatives
1287
- *
1288
- * val x: A
1289
- * def x(y: S): B
1290
- *
1291
- * where A has members
1292
- *
1293
- * def apply(y: S1): B1
1294
- * def apply(y: S2): B2
1295
- *
1296
- * and only one of the two `apply` methods (say the first) is applicable. If the first `apply`
1297
- * is as specific as the method `x`, but not the second, we still judge the two `x`'s
1298
- * as incomparable. If we had used an "exists an apply method" instead of the "forall"
1299
- * then value `x` would be picked over method `x` instead. On the other hand, if
1300
- * the first `apply` was NOT applicable but the second one was, then we would still pick
1301
- * pick value `x` over method `x` even though the applicable second apply method was is not
1302
- * more specific than the `x` method. So in going with "forall" instead of "exists" we
1303
- * err on the side of treating alternatives as incomparable, instead of potentially
1304
- * picking the wrong one.
1305
1273
*/
1306
- def isAsSpecific (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type , followApply : Boolean ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) {
1307
- if (followApply) {
1308
- val isMethod1 = tp1.stripPoly.isInstanceOf [MethodType ]
1309
- val isMethod2 = tp2.stripPoly.isInstanceOf [MethodType ]
1310
- if (! isMethod1 && isMethod2)
1311
- return ! hasApplyWith(tp1)(alt1app => ! isAsSpecific(alt1app, alt1app.widen, alt2, tp2, false ))
1312
- if (! isMethod2 && isMethod1)
1313
- return ! hasApplyWith(tp2)(alt2app => ! isAsSpecific(alt1, tp1, alt2app, alt2app.widen, false ))
1314
- }
1315
- tp1 match {
1316
- case tp1 : MethodType => // (1)
1317
- val formals1 =
1318
- if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle)
1319
- else tp1.paramInfos
1320
- val isAsSpecificMethod = isApplicableMethodRef(alt2, formals1, WildcardType )
1321
- isAsSpecificMethod || tp1.paramInfos.isEmpty && tp2.isInstanceOf [LambdaType ]
1322
- case tp1 : PolyType => // (2)
1323
- val nestedCtx = ctx.fresh.setExploreTyperState()
1324
-
1325
- {
1326
- implicit val ctx = nestedCtx
1327
-
1328
- // Fully define the PolyType parameters so that the infos of the
1329
- // tparams created below never contain TypeRefs whose underling types
1330
- // contain uninstantiated TypeVars, this could lead to cycles in
1331
- // `isSubType` as a TypeVar might get constrained by a TypeRef it's
1332
- // part of.
1333
- val tp1Params = tp1.newLikeThis(tp1.paramNames, tp1.paramInfos, defn.AnyType )
1334
- fullyDefinedType(tp1Params, " type parameters of alternative" , alt1.symbol.span)
1335
-
1336
- val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags , tp1.instantiateParamInfos(_))
1337
- isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2, followApply)
1338
- }
1339
- case _ => // (3)
1340
- tp2 match {
1341
- case tp2 : MethodType => true
1342
- case tp2 : PolyType if tp2.resultType.isInstanceOf [MethodType ] => true
1343
- case tp2 : PolyType => // (3b)
1344
- ctx.test(implicit ctx => isAsSpecificValueType(tp1, constrained(tp2).resultType))
1345
- case _ => // (3b)
1346
- isAsSpecificValueType(tp1, tp2)
1347
- }
1348
- }
1349
- }
1274
+ def isAsSpecific (alt1 : TermRef , tp1 : Type , alt2 : TermRef , tp2 : Type ): Boolean = trace(i " isAsSpecific $tp1 $tp2" , overload) { tp1 match {
1275
+ case tp1 : MethodType => // (1)
1276
+ val formals1 =
1277
+ if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle)
1278
+ else tp1.paramInfos
1279
+ isApplicableMethodRef(alt2, formals1, WildcardType ) ||
1280
+ tp1.paramInfos.isEmpty && tp2.isInstanceOf [LambdaType ]
1281
+ case tp1 : PolyType => // (2)
1282
+ val nestedCtx = ctx.fresh.setExploreTyperState()
1283
+
1284
+ {
1285
+ implicit val ctx = nestedCtx
1286
+
1287
+ // Fully define the PolyType parameters so that the infos of the
1288
+ // tparams created below never contain TypeRefs whose underling types
1289
+ // contain uninstantiated TypeVars, this could lead to cycles in
1290
+ // `isSubType` as a TypeVar might get constrained by a TypeRef it's
1291
+ // part of.
1292
+ val tp1Params = tp1.newLikeThis(tp1.paramNames, tp1.paramInfos, defn.AnyType )
1293
+ fullyDefinedType(tp1Params, " type parameters of alternative" , alt1.symbol.span)
1294
+
1295
+ val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags , tp1.instantiateParamInfos(_))
1296
+ isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2)
1297
+ }
1298
+ case _ => // (3)
1299
+ tp2 match {
1300
+ case tp2 : MethodType => true // (3a)
1301
+ case tp2 : PolyType if tp2.resultType.isInstanceOf [MethodType ] => true // (3a)
1302
+ case tp2 : PolyType => // (3b)
1303
+ ctx.test(implicit ctx => isAsSpecificValueType(tp1, constrained(tp2).resultType))
1304
+ case _ => // (3b)
1305
+ isAsSpecificValueType(tp1, tp2)
1306
+ }
1307
+ }}
1350
1308
1351
1309
/** Test whether value type `tp1` is as specific as value type `tp2`.
1352
1310
* Let's abbreviate this to `tp1 <:s tp2`.
@@ -1437,8 +1395,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1437
1395
1438
1396
def compareWithTypes (tp1 : Type , tp2 : Type ) = {
1439
1397
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
1440
- def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2, followApply )
1441
- def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1, followApply )
1398
+ def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
1399
+ def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
1442
1400
1443
1401
overload.println(i " compare( $alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2" )
1444
1402
if (ownerScore == 1 )
@@ -1465,20 +1423,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1465
1423
else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters
1466
1424
}}
1467
1425
1468
- def narrowMostSpecific (alts : List [TermRef ], followApply : Boolean )(implicit ctx : Context ): List [TermRef ] = track(" narrowMostSpecific" ) {
1426
+ def narrowMostSpecific (alts : List [TermRef ])(implicit ctx : Context ): List [TermRef ] = track(" narrowMostSpecific" ) {
1469
1427
alts match {
1470
1428
case Nil => alts
1471
1429
case _ :: Nil => alts
1472
1430
case alt1 :: alt2 :: Nil =>
1473
- compare(alt1, alt2, followApply ) match {
1431
+ compare(alt1, alt2) match {
1474
1432
case 1 => alt1 :: Nil
1475
1433
case - 1 => alt2 :: Nil
1476
1434
case 0 => alts
1477
1435
}
1478
1436
case alt :: alts1 =>
1479
1437
def survivors (previous : List [TermRef ], alts : List [TermRef ]): List [TermRef ] = alts match {
1480
1438
case alt :: alts1 =>
1481
- compare(previous.head, alt, followApply ) match {
1439
+ compare(previous.head, alt) match {
1482
1440
case 1 => survivors(previous, alts1)
1483
1441
case - 1 => survivors(alt :: previous.tail, alts1)
1484
1442
case 0 => survivors(alt :: previous, alts1)
@@ -1488,7 +1446,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1488
1446
val best :: rest = survivors(alt :: Nil , alts1)
1489
1447
def asGood (alts : List [TermRef ]): List [TermRef ] = alts match {
1490
1448
case alt :: alts1 =>
1491
- if (compare(alt, best, followApply ) < 0 ) asGood(alts1) else alt :: asGood(alts1)
1449
+ if (compare(alt, best) < 0 ) asGood(alts1) else alt :: asGood(alts1)
1492
1450
case nil =>
1493
1451
Nil
1494
1452
}
@@ -1507,21 +1465,17 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1507
1465
/** Is `alt` a method or polytype whose result type after the first value parameter
1508
1466
* section conforms to the expected type `resultType`? If `resultType`
1509
1467
* is a `IgnoredProto`, pick the underlying type instead.
1510
- * If `alt`does not have method or poly type, and `followApply` is true, consider
1511
- * all apply members instead. In all other cases return `true`.
1512
1468
*/
1513
- def resultConforms (altSym : Symbol , altType : Type , resultType : Type )(implicit ctx : Context ): Boolean = {
1514
- def recur ( altType : Type , followApply : Boolean ) : Boolean = resultType.revealIgnored match {
1469
+ def resultConforms (altSym : Symbol , altType : Type , resultType : Type )(implicit ctx : Context ): Boolean =
1470
+ resultType.revealIgnored match {
1515
1471
case resultType : ValueType =>
1516
1472
altType.widen match {
1517
- case tp : PolyType => recur( constrained(tp).resultType, followApply )
1473
+ case tp : PolyType => resultConforms(altSym, constrained(tp).resultType, resultType )
1518
1474
case tp : MethodType => constrainResult(altSym, tp.resultType, resultType)
1519
- case _ => ! followApply || hasApplyWith(altType)(recur(_, followApply = false ))
1475
+ case _ => true
1520
1476
}
1521
1477
case _ => true
1522
1478
}
1523
- recur(altType, followApply = true )
1524
- }
1525
1479
1526
1480
/** If the `chosen` alternative has a result type incompatible with the expected result
1527
1481
* type `pt`, run overloading resolution again on all alternatives that do match `pt`.
@@ -1535,7 +1489,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1535
1489
* probability of pruning the search. result type comparisons are neither cheap nor
1536
1490
* do they prune much, on average.
1537
1491
*/
1538
- def adaptByResult (chosen : TermRef ) = pt match {
1492
+ def adaptByResult (chosen : TermRef , alts : List [ TermRef ] ) = pt match {
1539
1493
case pt : FunProto if ! ctx.test(implicit ctx => resultConforms(chosen.symbol, chosen, pt.resultType)) =>
1540
1494
val conformingAlts = alts.filter(alt =>
1541
1495
(alt ne chosen) && ctx.test(implicit ctx => resultConforms(alt.symbol, alt, pt.resultType)))
@@ -1551,13 +1505,42 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1551
1505
case _ => chosen
1552
1506
}
1553
1507
1554
- var found = resolveOverloaded(alts, pt, Nil )(ctx.retractMode(Mode .ImplicitsEnabled ))
1555
- if (found.isEmpty && ctx.mode.is(Mode .ImplicitsEnabled ))
1556
- found = resolveOverloaded(alts, pt, Nil )
1557
- found match {
1558
- case alt :: Nil => adaptByResult(alt) :: Nil
1559
- case _ => found
1508
+ def resolve (alts : List [TermRef ]) = {
1509
+ var found = resolveOverloaded(alts, pt, Nil )(ctx.retractMode(Mode .ImplicitsEnabled ))
1510
+ if (found.isEmpty && ctx.mode.is(Mode .ImplicitsEnabled ))
1511
+ found = resolveOverloaded(alts, pt, Nil )
1512
+ found match {
1513
+ case alt :: Nil => adaptByResult(alt, alts) :: Nil
1514
+ case _ => found
1515
+ }
1516
+ }
1517
+
1518
+ /** Try an apply method, if
1519
+ * - the result is applied to value arguments and alternative is not a method, or
1520
+ * - the result is applied to type arguments and alternatuve is not polymorphic
1521
+ */
1522
+ val tryApply : Type => Boolean = alt => pt match {
1523
+ case pt : FunProto => ! alt.widen.stripPoly.isInstanceOf [MethodType ]
1524
+ case pt : PolyProto => ! alt.widen.isInstanceOf [PolyType ]
1525
+ case _ => false
1560
1526
}
1527
+
1528
+ /** Replace each alternative by its apply members where necesssary */
1529
+ def applyMembers (alt : TermRef ): List [TermRef ] =
1530
+ if (tryApply(alt)) alt.member(nme.apply).alternatives.map(TermRef (alt, nme.apply, _))
1531
+ else alt :: Nil
1532
+
1533
+ /** Fall back from an apply method to its original alternative */
1534
+ def retract (alt : TermRef ): TermRef =
1535
+ if (alt.name == nme.apply && ! alts.contains(alt))
1536
+ alts.find(_.symbol == alt.prefix.termSymbol).getOrElse(alt)
1537
+ else alt
1538
+
1539
+ if (alts.exists(tryApply)) {
1540
+ val expanded = alts.flatMap(applyMembers)
1541
+ resolve(expanded).map(retract)
1542
+ }
1543
+ else resolve(alts)
1561
1544
}
1562
1545
1563
1546
/** This private version of `resolveOverloaded` does the bulk of the work of
@@ -1591,21 +1574,17 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1591
1574
}
1592
1575
1593
1576
def narrowByTypes (alts : List [TermRef ], argTypes : List [Type ], resultType : Type ): List [TermRef ] =
1594
- alts filter (isApplicableType(_, argTypes, resultType))
1595
-
1596
- val numArgs = pt match {
1597
- case pt @ FunProto (args, resultType) => args.length
1598
- case _ => 0
1599
- }
1577
+ alts filter (isApplicableMethodRef(_, argTypes, resultType))
1600
1578
1601
1579
val candidates = pt match {
1602
1580
case pt @ FunProto (args, resultType) =>
1581
+ val numArgs = args.length
1603
1582
val normArgs = args.mapConserve {
1604
1583
case Block (Nil , expr) => expr
1605
1584
case x => x
1606
1585
}
1607
1586
1608
- def sizeFits (alt : TermRef , followApply : Boolean ): Boolean = alt.widen.stripPoly match {
1587
+ def sizeFits (alt : TermRef ): Boolean = alt.widen.stripPoly match {
1609
1588
case tp : MethodType =>
1610
1589
val ptypes = tp.paramInfos
1611
1590
val numParams = ptypes.length
@@ -1615,13 +1594,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1615
1594
else if (numParams < numArgs) isVarArgs
1616
1595
else if (numParams > numArgs + 1 ) hasDefault
1617
1596
else isVarArgs || hasDefault
1618
- case tp =>
1619
- numArgs == 0 ||
1620
- followApply && hasApplyWith(tp)(sizeFits(_, followApply = false ))
1597
+ case _ =>
1598
+ numArgs == 0
1621
1599
}
1622
1600
1623
1601
def narrowBySize (alts : List [TermRef ]): List [TermRef ] =
1624
- alts.filter(sizeFits(_, followApply = true ))
1602
+ alts.filter(sizeFits(_))
1625
1603
1626
1604
def narrowByShapes (alts : List [TermRef ]): List [TermRef ] = {
1627
1605
if (normArgs exists untpd.isFunctionWithUnknownParamType)
@@ -1633,11 +1611,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1633
1611
1634
1612
def narrowByTrees (alts : List [TermRef ], args : List [Tree ], resultType : Type ): List [TermRef ] = {
1635
1613
val alts2 = alts.filter(alt =>
1636
- isDirectlyApplicableType (alt, targs, args, resultType)
1614
+ isDirectlyApplicableMethodRef (alt, targs, args, resultType)
1637
1615
)
1638
1616
if (alts2.isEmpty && ! ctx.isAfterTyper)
1639
1617
alts.filter(alt =>
1640
- isApplicableType (alt, targs, args, resultType, keepConstraint = false )
1618
+ isApplicableMethodRef (alt, targs, args, resultType, keepConstraint = false )
1641
1619
)
1642
1620
else
1643
1621
alts2
@@ -1708,7 +1686,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1708
1686
candidates.flatMap(cloneCandidate)
1709
1687
}
1710
1688
1711
- val found = narrowMostSpecific(candidates, followApply = numArgs != 0 )
1689
+ val found = narrowMostSpecific(candidates)
1712
1690
if (found.length <= 1 ) found
1713
1691
else pt match {
1714
1692
case pt @ FunProto (_, resType : FunProto ) =>
@@ -1880,4 +1858,3 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
1880
1858
app
1881
1859
}
1882
1860
}
1883
-
0 commit comments