Skip to content

Commit ca2d865

Browse files
committed
Decouple ImplicitFunctionN from FunctionN.
This removes an illegal method override mentioned in #2000.
1 parent 8e2c271 commit ca2d865

File tree

5 files changed

+41
-30
lines changed

5 files changed

+41
-30
lines changed

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

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class Definitions {
104104
*
105105
* ImplicitFunctionN traits follow this template:
106106
*
107-
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
107+
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
108108
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
109109
* }
110110
*/
@@ -118,22 +118,12 @@ class Definitions {
118118
for (i <- List.range(0, arity)) yield
119119
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
120120
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
121-
val (methodType, parentTraits) =
122-
if (name.startsWith(tpnme.ImplicitFunction)) {
123-
val superTrait =
124-
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
125-
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
126-
}
127-
else (MethodType, Nil)
128-
val applyMeth =
129-
decls.enter(
130-
newMethod(cls, nme.apply,
131-
methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
132-
denot.info =
133-
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
121+
val methodType = if (name.isImplicitFunction) ImplicitMethodType else MethodType
122+
decls.enter(newMethod(cls, nme.apply, methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
123+
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls)
134124
}
135125
}
136-
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
126+
newClassSymbol(ScalaPackageClass, name, NoInitsTrait, completer)
137127
}
138128

139129
private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
@@ -822,7 +812,7 @@ class Definitions {
822812
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
823813
* is treated as a NoInit trait.
824814
*/
825-
lazy val NoInitClasses = PhantomClasses + FunctionXXLClass
815+
def isNoInitClass(cls: Symbol) = PhantomClasses.contains(cls) || (cls eq FunctionXXLClass) || isFunctionClass(cls)
826816

827817
def isPolymorphicAfterErasure(sym: Symbol) =
828818
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)

compiler/src/dotty/tools/dotc/transform/Mixin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
175175
case Some(call) =>
176176
if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil
177177
case None =>
178-
if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil
178+
if (baseCls.is(NoInitsTrait) || defn.isNoInitClass(baseCls)) Nil
179179
else {
180180
//println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}")
181181
transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil

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

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,20 +2003,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20032003
// local adaptation makes sure every adapted tree conforms to its pt
20042004
// so will take the code path that decides on inlining
20052005
return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(())))
2006-
// convert function literal to SAM closure
20072006
tree match {
2008-
case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
2009-
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
2010-
pt match {
2011-
case SAMType(meth)
2012-
if wtp <:< meth.info.toFunctionType() =>
2013-
// was ... && isFullyDefined(pt, ForceDegree.noBottom)
2014-
// but this prevents case blocks from implementing polymorphic partial functions,
2015-
// since we do not know the result parameter a priori. Have to wait until the
2016-
// body is typechecked.
2017-
return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt)
2018-
case _ =>
2019-
}
2007+
case Closure(Nil, id @ Ident(nme.ANON_FUN), _) =>
2008+
val adaptToPrototype =
2009+
if (defn.isFunctionType(pt)) {
2010+
// convert implicit function to function
2011+
!defn.isImplicitFunctionType(pt) && defn.isImplicitFunctionType(wtp)
2012+
} else {
2013+
// convert function literal to SAM closure
2014+
defn.isFunctionType(wtp) && {
2015+
pt match {
2016+
case SAMType(meth)
2017+
if wtp <:< meth.info.toFunctionType() =>
2018+
// was ... && isFullyDefined(pt, ForceDegree.noBottom)
2019+
// but this prevents case blocks from implementing polymorphic partial functions,
2020+
// since we do not know the result parameter a priori. Have to wait until the
2021+
// body is typechecked.
2022+
true
2023+
case _ => false
2024+
}
2025+
}
2026+
}
2027+
if (adaptToPrototype)
2028+
return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt)
20202029
case _ =>
20212030
}
20222031
// try an implicit conversion

tests/run/implicitFunctionImpl.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
42

tests/run/implicitFunctionImpl.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println(new Fun().apply(42))
5+
6+
}
7+
}
8+
9+
class Fun extends ImplicitFunction1[Int, Int] {
10+
def apply(implicit v1: Int): Int = v1
11+
}

0 commit comments

Comments
 (0)