Skip to content

Commit 972470e

Browse files
committed
Always insert apply for expressions of implicit function type
1 parent 6435375 commit 972470e

File tree

3 files changed

+43
-13
lines changed

3 files changed

+43
-13
lines changed

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,25 @@ class Definitions {
8686
newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered
8787
}
8888

89-
/** The trait FunctionN, for some N */
89+
/** The trait FunctionN or ImplicitFunctionN, for some N
90+
* @param name The name of the trait to be created
91+
*
92+
* FunctionN traits follow this template:
93+
*
94+
* trait FunctionN[T0,...T{N-1}, R] extends Object {
95+
* def apply($x0: T0, ..., $x{N_1}: T{N-1}): R
96+
* }
97+
*
98+
* That is, they follow the template given for Function2..Function22 in the
99+
* standard library, but without `tupled` and `curried` methods and without
100+
* a `toString`.
101+
*
102+
* ImplicitFunctionN traits follow this template:
103+
*
104+
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
105+
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
106+
* }
107+
*/
90108
private def newFunctionNTrait(name: TypeName) = {
91109
val completer = new LazyType {
92110
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
@@ -97,17 +115,17 @@ class Definitions {
97115
for (i <- List.range(0, arity)) yield
98116
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
99117
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
100-
val (implicitFlag, parentTraits) =
118+
val (methodType, parentTraits) =
101119
if (name.startsWith(tpnme.ImplicitFunction)) {
102120
val superTrait =
103121
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
104-
(Implicit, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
122+
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
105123
}
106-
else (EmptyFlags, Nil)
124+
else (MethodType, Nil)
107125
val applyMeth =
108126
decls.enter(
109127
newMethod(cls, nme.apply,
110-
MethodType(argParams.map(_.typeRef), resParam.typeRef), Deferred | implicitFlag))
128+
methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
111129
denot.info =
112130
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
113131
}
@@ -696,6 +714,7 @@ class Definitions {
696714
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
697715

698716
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
717+
def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction)
699718
def isUnimplementedFunctionClass(cls: Symbol) =
700719
isFunctionClass(cls) && cls.name.functionArity > MaxImplementedFunctionArity
701720
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
16121612
}
16131613
}
16141614

1615+
/** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */
1616+
def isApplyProto(pt: Type)(implicit ctx: Context): Boolean = pt match {
1617+
case pt: SelectionProto => pt.name == nme.apply
1618+
case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType)
1619+
case pt: IgnoredProto => isApplyProto(pt.ignored)
1620+
case _ => false
1621+
}
1622+
16151623
/** Add apply node or implicit conversions. Two strategies are tried, and the first
16161624
* that is successful is picked. If neither of the strategies are successful, continues with
16171625
* `fallBack`.
@@ -1625,14 +1633,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
16251633
*/
16261634
def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: => Tree)(implicit ctx: Context): Tree = {
16271635

1628-
/** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */
1629-
def isApplyProto(pt: Type): Boolean = pt match {
1630-
case pt: SelectionProto => pt.name == nme.apply
1631-
case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType)
1632-
case pt: IgnoredProto => isApplyProto(pt.ignored)
1633-
case _ => false
1634-
}
1635-
16361636
def tryApply(implicit ctx: Context) = {
16371637
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
16381638
if (sel.tpe.isError) sel else adapt(sel, pt)
@@ -1874,6 +1874,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
18741874
err.typeMismatch(tree, pt)
18751875
else
18761876
missingArgs
1877+
case wtp: RefinedType
1878+
if defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
1879+
!isApplyProto(pt) =>
1880+
typr.println(i"insert apply on implicit $tree")
1881+
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
18771882
case _ =>
18781883
ctx.typeComparer.GADTused = false
18791884
if (ctx.mode is Mode.Pattern) {

tests/pos/implicitFuns.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ object Test2 {
1919

2020
val yy: (String, Int) => Any = xx
2121

22+
implicit val world: String = "world!"
23+
2224
val b = x("hello")
2325

2426
val b1: Boolean = b
2527

28+
val bi = x
29+
30+
val bi1: Boolean = bi
31+
2632
val c = xx("hh", 22)
2733

2834
val c1: Int = c

0 commit comments

Comments
 (0)