@@ -1247,53 +1247,102 @@ trait Implicits:
1247
1247
|Consider using the scala.util.NotGiven class to implement similar functionality. """ ,
1248
1248
ctx.source.atSpan(span))
1249
1249
1250
- /** A relation that influences the order in which implicits are tried.
1250
+ /** Compare the length of the baseClasses of two symbols (except for objects,
1251
+ * where we use the length of the companion class instead if it's bigger).
1252
+ *
1253
+ * This relation is meant to approximate `Applications#compareOwner` while also
1254
+ * inducing a total ordering: `compareOwner` returns `0` for unrelated symbols
1255
+ * and therefore only induces a partial ordering, meaning it cannot be used
1256
+ * as a sorting function (see `java.util.Comparator#compare`).
1257
+ */
1258
+ def compareBaseClassesLength (sym1 : Symbol , sym2 : Symbol ): Int =
1259
+ def len (sym : Symbol ) =
1260
+ if sym.is(ModuleClass ) && sym.companionClass.exists then
1261
+ Math .max(sym.asClass.baseClassesLength, sym.companionClass.asClass.baseClassesLength)
1262
+ else if sym.isClass then
1263
+ sym.asClass.baseClassesLength
1264
+ else
1265
+ 0
1266
+ len(sym1) - len(sym2)
1267
+
1268
+ /** A relation that influences the order in which eligible implicits are tried.
1269
+ *
1251
1270
* We prefer (in order of importance)
1252
1271
* 1. more deeply nested definitions
1253
1272
* 2. definitions with fewer implicit parameters
1254
- * 3. definitions in subclasses
1273
+ * 3. definitions whose owner has more parents (see `compareBaseClassesLength`)
1255
1274
* The reason for (2) is that we want to fail fast if the search type
1256
1275
* is underconstrained. So we look for "small" goals first, because that
1257
1276
* will give an ambiguity quickly.
1258
1277
*/
1259
- def prefer (cand1 : Candidate , cand2 : Candidate ): Boolean =
1260
- val level1 = cand1.level
1261
- val level2 = cand2.level
1262
- if level1 > level2 then return true
1263
- if level1 < level2 then return false
1264
- val sym1 = cand1.ref.symbol
1265
- val sym2 = cand2.ref.symbol
1278
+ def compareEligibles (e1 : Candidate , e2 : Candidate ): Int =
1279
+ if e1 eq e2 then return 0
1280
+ val cmpLevel = e1.level - e2.level
1281
+ if cmpLevel != 0 then return - cmpLevel // 1.
1282
+ val sym1 = e1.ref.symbol
1283
+ val sym2 = e2.ref.symbol
1266
1284
val arity1 = sym1.info.firstParamTypes.length
1267
1285
val arity2 = sym2.info.firstParamTypes.length
1268
- if arity1 < arity2 then return true
1269
- if arity1 > arity2 then return false
1270
- compareOwner(sym1.owner, sym2.owner) == 1
1286
+ val cmpArity = arity1 - arity2
1287
+ if cmpArity != 0 then return cmpArity // 2.
1288
+ val cmpBcs = compareBaseClassesLength(sym1.owner, sym2.owner)
1289
+ - cmpBcs // 3.
1271
1290
1272
- /** Sort list of implicit references according to `prefer `.
1291
+ /** Sort list of implicit references according to `compareEligibles `.
1273
1292
* This is just an optimization that aims at reducing the average
1274
1293
* number of candidates to be tested.
1275
1294
*/
1276
- def sort (eligible : List [Candidate ]) = eligible match {
1295
+ def sort (eligible : List [Candidate ]) = eligible match
1277
1296
case Nil => eligible
1278
1297
case e1 :: Nil => eligible
1279
1298
case e1 :: e2 :: Nil =>
1280
- if (prefer( e2, e1)) e2 :: e1 :: Nil
1299
+ if compareEligibles( e2, e1) < 0 then e2 :: e1 :: Nil
1281
1300
else eligible
1282
1301
case _ =>
1283
- try eligible.sortWith(prefer )
1302
+ try eligible.sorted( using (a, b) => compareEligibles(a, b) )
1284
1303
catch case ex : IllegalArgumentException =>
1285
- // diagnostic to see what went wrong
1304
+ // Check if we violated the contract of java.util.Comparator#compare
1286
1305
for
1287
- e1 <- eligible
1288
- e2 <- eligible
1289
- if prefer(e1, e2)
1290
- e3 <- eligible
1291
- if prefer(e2, e3) && ! prefer(e1, e3)
1306
+ x <- eligible
1307
+ y <- eligible
1308
+ cmpXY = Integer .signum(compareEligibles(x, y))
1309
+ cmpYX = Integer .signum(compareEligibles(y, x))
1310
+ z <- eligible
1311
+ cmpXZ = Integer .signum(compareEligibles(x, z))
1312
+ cmpYZ = Integer .signum(compareEligibles(y, z))
1292
1313
do
1293
- val es = List (e1, e2, e3)
1294
- println(i " transitivity violated for $es%, % \n ${es.map(_.implicitRef.underlyingRef.symbol.showLocated)}%, % " )
1314
+ def reportViolation (msg : String ): Unit =
1315
+ Console .err.println(s " Internal error: comparison function violated ${msg.stripMargin}" )
1316
+ def showCandidate (c : Candidate ): String =
1317
+ s " $c ( ${c.ref.symbol.showLocated}) "
1318
+
1319
+ if cmpXY != - cmpYX then
1320
+ reportViolation(
1321
+ s """ signum(cmp(x, y)) == -signum(cmp(y, x)) given:
1322
+ |x = ${showCandidate(x)}
1323
+ |y = ${showCandidate(y)}
1324
+ |cmpXY = $cmpXY
1325
+ |cmpYX = $cmpYX""" )
1326
+ if cmpXY != 0 && cmpXY == cmpYZ && cmpXZ != cmpXY then
1327
+ reportViolation(
1328
+ s """ transitivity given:
1329
+ |x = ${showCandidate(x)}
1330
+ |y = ${showCandidate(y)}
1331
+ |z = ${showCandidate(z)}
1332
+ |cmpXY = $cmpXY
1333
+ |cmpXZ = $cmpXZ
1334
+ |cmpYZ = $cmpYZ""" )
1335
+ if cmpXY == 0 && cmpXZ != cmpYZ then
1336
+ reportViolation(
1337
+ s """ cmp(x, y) == 0 implies that signum(cmp(x, z)) == signum(cmp(y, z)) given:
1338
+ |x = ${showCandidate(x)}
1339
+ |y = ${showCandidate(y)}
1340
+ |z = ${showCandidate(z)}
1341
+ |cmpXY = $cmpXY
1342
+ |cmpXZ = $cmpXZ
1343
+ |cmpYZ = $cmpYZ""" )
1344
+ end for
1295
1345
throw ex
1296
- }
1297
1346
1298
1347
rank(sort(eligible), NoMatchingImplicitsFailure , Nil )
1299
1348
end searchImplicit
0 commit comments