Skip to content

Commit c22bb88

Browse files
committed
Widen result types of implied methods for specificity testing
When testing specificity of methods, widen result type of synthetic implied methods. I tried widening the inferred result type of these methods already in Namer instead, but this causes problems since it can hide extension methods.
1 parent f4e6e53 commit c22bb88

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,10 @@ object Flags {
612612
final val InlineParam: FlagConjunction = allOf(Inline, Param)
613613

614614
/** An extension method */
615-
final val ExtensionMethod = allOf(Method, Extension)
615+
final val ExtensionMethod = allOf(Extension, Method)
616+
617+
/** An implied method */
618+
final val SyntheticImpliedMethod: FlagConjunction = allOf(Synthetic, Implied, Method)
616619

617620
/** An enum case */
618621
final val EnumCase: FlagConjunction = allOf(Enum, Case)

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

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,23 +1297,50 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12971297
// # skipped implicit parameters in tp1 - # skipped implicit parameters in tp2
12981298
var implicitBalance: Int = 0
12991299

1300+
/** Widen the type of synthetic implied methods from the implementation class to the
1301+
* type that's implemented. Example
1302+
*
1303+
* implied I[X] for T { ... }
1304+
*
1305+
* This desugars to
1306+
*
1307+
* class I[X] extends T { ... }
1308+
* implied def I[X]: I[X] = new I[X]
1309+
*
1310+
* To compare specificity we should compare with `T`, not with its implementation `I[X]`.
1311+
* No such widening is performed for implied aliases, which are not synthetic. E.g.
1312+
*
1313+
* implied J[X] for T = rhs
1314+
*
1315+
* already has the right result type `T`. Neither is widening performed for implied
1316+
* objects, since these are anyway taken to be more specific than methods
1317+
* (by condition 3a above).
1318+
*/
1319+
def widenImplied(tp: Type, alt: TermRef): Type =
1320+
if (alt.symbol.is(SyntheticImpliedMethod))
1321+
tp.parents match {
1322+
case Nil => tp
1323+
case ps => ps.reduceLeft(AndType(_, _))
1324+
}
1325+
else tp
1326+
13001327
/** Drop any implicit parameter section */
1301-
def stripImplicit(tp: Type, weight: Int): Type = tp match {
1328+
def stripImplicit(tp: Type, alt: TermRef, weight: Int): Type = tp match {
13021329
case mt: MethodType if mt.isImplicitMethod =>
13031330
implicitBalance += mt.paramInfos.length * weight
1304-
resultTypeApprox(mt)
1331+
widenImplied(resultTypeApprox(mt), alt)
13051332
case pt: PolyType =>
1306-
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType, weight))
1333+
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType, alt, weight))
13071334
case _ =>
1308-
tp
1335+
widenImplied(tp, alt)
13091336
}
13101337

13111338
val owner1 = if (alt1.symbol.exists) alt1.symbol.owner else NoSymbol
13121339
val owner2 = if (alt2.symbol.exists) alt2.symbol.owner else NoSymbol
13131340
val ownerScore = compareOwner(owner1, owner2)
13141341

1315-
val tp1 = stripImplicit(alt1.widen, -1)
1316-
val tp2 = stripImplicit(alt2.widen, +1)
1342+
val tp1 = stripImplicit(alt1.widen, alt1, -1)
1343+
val tp2 = stripImplicit(alt2.widen, alt2, +1)
13171344
def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
13181345
def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
13191346

tests/run/implied-specifity.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
case class Show[T](val i: Int)
2+
object Show {
3+
def apply[T](implicit st: Show[T]): Int = st.i
4+
5+
implied showInt for Show[Int](0)
6+
implied fallback[T] for Show[T](1)
7+
}
8+
9+
class Generic
10+
object Generic {
11+
implied gen for Generic
12+
implied showGen[T] given Generic for Show[T](2)
13+
}
14+
15+
object Contextual {
16+
trait Context
17+
implied ctx for Context
18+
implied showGen2[T] given Generic for Show[T](2)
19+
implied showGen3[T] given Generic, Context for Show[T](3)
20+
}
21+
22+
object Test extends App {
23+
assert(Show[Int] == 0)
24+
assert(Show[String] == 1)
25+
assert(Show[Generic] == 2) // showGen beats fallback due to longer argument list
26+
27+
{ import implied Contextual._
28+
assert(Show[Generic] == 3)
29+
}
30+
}

0 commit comments

Comments
 (0)