Skip to content

Commit 8b8d813

Browse files
committed
Another tweak to overloading resolution
After thinking even more about it, I now believe we should revise the rule. The idea of using implicits as a priority mechanism is so that we can write something like this: ``` class LowPrio class MidPrio extends LowPrio class HighPrio extends MidPrio implied for LowPrio implied for MidPrio implied for HighPrio implied fallback given LowPrio for A implied standard given MidPrio for A implied specialized given HighPrio for A ``` (usually in different modules, of course). This is a good alternative to the previous priorization by owner subclassing since it places no demands on where implied instances are put and is stable under aliasing/forwarding. But to do this reliably it should also work if some of the alternatives take additional implicit parameters. E.g. ``` implied standard given MidPrio given B for A ``` The "more implicit parameters is better" rule would make standard of higher priority than specialized, which defeats the purpose.
1 parent b47d2f2 commit 8b8d813

File tree

4 files changed

+9
-24
lines changed

4 files changed

+9
-24
lines changed

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,9 +1344,6 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13441344
(flip(tp1) relaxed_<:< flip(tp2)) || viewExists(tp1, tp2)
13451345
}
13461346

1347-
// # skipped implicit parameters in tp1 - # skipped implicit parameters in tp2
1348-
var implicitBalance: Int = 0
1349-
13501347
/** Widen the result type of synthetic implied methods from the implementation class to the
13511348
* type that's implemented. Example
13521349
*
@@ -1383,7 +1380,6 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13831380
/** Drop any implicit parameter section */
13841381
def stripImplicit(tp: Type, weight: Int): Type = tp match {
13851382
case mt: MethodType if mt.isImplicitMethod =>
1386-
implicitBalance += mt.paramInfos.length * weight
13871383
resultTypeApprox(mt)
13881384
case pt: PolyType =>
13891385
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType, weight))
@@ -1416,14 +1412,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14161412
val strippedType2 = stripImplicit(fullType2, +1)
14171413

14181414
val result = compareWithTypes(strippedType1, strippedType2)
1419-
if (result != 0)
1420-
result
1421-
else if (implicitBalance != 0)
1422-
implicitBalance.signum
1423-
else if ((strippedType1 `ne` fullType1) || (strippedType2 `ne` fullType2))
1424-
compareWithTypes(fullType1, fullType2)
1425-
else
1426-
0
1415+
if (result != 0) result
1416+
else if (strippedType1 eq fullType1)
1417+
if (strippedType2 eq fullType2) 0 // no implicits either side: its' a draw
1418+
else 1 // prefer 1st alternative with no implicits
1419+
else if (strippedType2 eq fullType2) -1 // prefer 2nd alternative with no implicits
1420+
else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters
14271421
}}
14281422

14291423
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {

docs/docs/reference/changed-features/implicit-resolution.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ affect implicits on the language level.
114114
An alternative A is _more specific_ than an alternative B if
115115

116116
- the relative weight of A over B is greater than the relative weight of B over A, or
117-
- the relative weights are the same, and A takes fewer inferable parameters than B, or
118-
- the relative weights and the number of inferable parameters are the same, and
119-
A is more specific than B if all inferable parameters in either alternative are
117+
- the relative weights are the same, and A takes no implicit parameters but B does, or
118+
- the relative weights are the same, both A and B take implicit parameters, and
119+
A is more specific than B if all implicit parameters in either alternative are
120120
replaced by regular parameters.
121121

122122
[//]: # todo: expand with precise rules

tests/run/implicit-specifity.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,4 @@ object Test extends App {
3535
assert(Show[String] == 1)
3636
assert(Show[Generic] == 1) // showGen loses against fallback due to longer argument list
3737
assert(Show[Generic2] == 2) // ... but the opaque type intersection trick works.
38-
39-
{ import implied Contextual._
40-
assert(Show[Generic] == 4) // shorter, more specific implicit parameter list wins
41-
}
4238
}

tests/run/overloading-specifity.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,11 @@ object Test extends App {
1212
trait Context
1313
implied ctx for Context
1414

15-
object a {
16-
def foo[T](implicit gen: Generic): Show[T] = new Show[T](1)
17-
def foo[T](implicit gen: Generic, ctx: Context): Show[T] = new Show[T](2)
18-
}
1915
object b {
2016
def foo[T](implicit gen: Generic): Show[T] = new Show[T](1)
2117
def foo[T]: Show[T] = new Show[T](2)
2218
}
2319

24-
assert(a.foo[Int].i == 1)
2520
assert(b.foo[Int].i == 2)
2621

2722
def f: Int = 1

0 commit comments

Comments
 (0)