Skip to content

Commit bd04177

Browse files
committed
Fix #876: Allow implicit conversions from singleton types
I found a way to make this efficient (hopefully).
1 parent 71f3aca commit bd04177

File tree

3 files changed

+29
-17
lines changed

3 files changed

+29
-17
lines changed

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -598,15 +598,6 @@ trait Checking {
598598
defn.ObjectType
599599
}
600600

601-
/** Check that a non-implicit parameter making up the first parameter section of an
602-
* implicit conversion is not a singleton type.
603-
*/
604-
def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
605-
case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) =>
606-
checkNotSingleton(vparam.tpt, " to be parameter type of an implicit conversion")
607-
case _ =>
608-
}
609-
610601
/** If `sym` is an implicit conversion, check that implicit conversions are enabled.
611602
* @pre sym.is(Implicit)
612603
*/
@@ -948,7 +939,6 @@ trait NoChecking extends ReChecking {
948939
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
949940
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
950941
override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
951-
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
952942
override def checkImplicitConversionDefOK(sym: Symbol)(implicit ctx: Context): Unit = ()
953943
override def checkImplicitConversionUseOK(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = ()
954944
override def checkFeasibleParent(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,17 @@ object Implicits {
8080
case mt: MethodType =>
8181
mt.isImplicitMethod ||
8282
mt.paramInfos.lengthCompare(1) != 0 ||
83-
!ctx.test(implicit ctx => argType relaxed_<:< mt.paramInfos.head)
83+
!ctx.test(implicit ctx =>
84+
argType relaxed_<:< mt.paramInfos.head.widenSingleton)
8485
case poly: PolyType =>
8586
// We do not need to call ProtoTypes#constrained on `poly` because
8687
// `refMatches` is always called with mode TypevarsMissContext enabled.
8788
poly.resultType match {
8889
case mt: MethodType =>
8990
mt.isImplicitMethod ||
9091
mt.paramInfos.length != 1 ||
91-
!ctx.test(implicit ctx => argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty))
92+
!ctx.test(implicit ctx =>
93+
argType relaxed_<:< wildApprox(mt.paramInfos.head.widenSingleton, null, Set.empty))
9294
case rtp =>
9395
discardForView(wildApprox(rtp, null, Set.empty), argType)
9496
}
@@ -132,14 +134,37 @@ object Implicits {
132134
case _ => false
133135
}
134136

137+
/** Widen singleton arguments of implicit conversions to their underlying type.
138+
* This is necessary so that they can be found eligible for the argument type.
139+
* Note that we always take the underlying type of a singleton type as the argument
140+
* type, so that we get a reasonable implicit cache hit ratio.
141+
*/
142+
def adjustSingletonArg(tp: Type): Type = tp match {
143+
case tp: PolyType =>
144+
val res = adjustSingletonArg(tp.resType)
145+
if (res `eq` tp.resType) tp else tp.derivedLambdaType(resType = res)
146+
case tp: MethodType =>
147+
tp.paramInfos match {
148+
case (single: SingletonType) :: Nil =>
149+
tp.derivedLambdaType(paramInfos = single.widenSingleton :: Nil)
150+
case _ =>
151+
tp
152+
}
153+
case _ => tp
154+
}
155+
135156
(ref.symbol isAccessibleFrom ref.prefix) && {
136157
if (discard) {
137158
record("discarded eligible")
138159
false
139160
}
140161
else {
141162
val ptNorm = normalize(pt, pt) // `pt` could be implicit function types, check i2749
142-
NoViewsAllowed.isCompatible(normalize(ref, pt), ptNorm)
163+
val refAdjusted =
164+
if (pt.isInstanceOf[ViewProto]) adjustSingletonArg(ref.widenSingleton)
165+
else ref
166+
val refNorm = normalize(refAdjusted, pt)
167+
NoViewsAllowed.isCompatible(refNorm, ptNorm)
143168
}
144169
}
145170
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,10 +1413,7 @@ class Typer extends Namer
14131413
val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef])
14141414
val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef])
14151415
vparamss1.foreach(checkNoForwardDependencies)
1416-
if (sym is Implicit) {
1417-
checkImplicitParamsNotSingletons(vparamss1)
1418-
checkImplicitConversionDefOK(sym)
1419-
}
1416+
if (sym is Implicit) checkImplicitConversionDefOK(sym)
14201417
val tpt1 = checkSimpleKinded(typedType(tpt))
14211418

14221419
var rhsCtx = ctx

0 commit comments

Comments
 (0)