diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9e9e39a8439d..66f571e015ae 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -104,7 +104,7 @@ class Definitions { * * ImplicitFunctionN traits follow this template: * - * trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] { + * trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object { * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R * } */ @@ -113,27 +113,16 @@ class Definitions { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope - val arity = name.functionArity val argParams = - for (i <- List.range(0, arity)) yield - enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls) - val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls) - val (methodType, parentTraits) = - if (name.startsWith(tpnme.ImplicitFunction)) { - val superTrait = - FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) - (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) - } - else (MethodType, Nil) - val applyMeth = - decls.enter( - newMethod(cls, nme.apply, - methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) - denot.info = - ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls) + for (i <- List.range(0, name.functionArity)) yield + enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls).typeRef + val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls).typeRef + val methodType = if (name.isImplicitFunction) ImplicitMethodType else MethodType + decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred)) + denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) } } - newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer) + newClassSymbol(ScalaPackageClass, name, NoInitsTrait, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = @@ -822,7 +811,7 @@ class Definitions { * trait gets screwed up. Therefore, it is mandatory that FunctionXXL * is treated as a NoInit trait. */ - lazy val NoInitClasses = PhantomClasses + FunctionXXLClass + def isNoInitClass(cls: Symbol) = PhantomClasses.contains(cls) || (cls eq FunctionXXLClass) || isFunctionClass(cls) def isPolymorphicAfterErasure(sym: Symbol) = (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index fd4370d3e319..b9298eaa8307 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -175,7 +175,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => case Some(call) => if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil case None => - if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil + if (baseCls.is(NoInitsTrait) || defn.isNoInitClass(baseCls)) Nil else { //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 498fd001bdf8..f9c8dae619e1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2003,20 +2003,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // local adaptation makes sure every adapted tree conforms to its pt // so will take the code path that decides on inlining return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(()))) - // convert function literal to SAM closure tree match { - case Closure(Nil, id @ Ident(nme.ANON_FUN), _) - if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) => - pt match { - case SAMType(meth) - if wtp <:< meth.info.toFunctionType() => - // was ... && isFullyDefined(pt, ForceDegree.noBottom) - // but this prevents case blocks from implementing polymorphic partial functions, - // since we do not know the result parameter a priori. Have to wait until the - // body is typechecked. - return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt) - case _ => - } + case Closure(Nil, id @ Ident(nme.ANON_FUN), _) => + val adaptToPrototype = + if (defn.isFunctionType(pt)) { + // convert implicit function to function + !defn.isImplicitFunctionType(pt) && defn.isImplicitFunctionType(wtp) + } else { + // convert function literal to SAM closure + defn.isFunctionType(wtp) && { + pt match { + case SAMType(meth) + if wtp <:< meth.info.toFunctionType() => + // was ... && isFullyDefined(pt, ForceDegree.noBottom) + // but this prevents case blocks from implementing polymorphic partial functions, + // since we do not know the result parameter a priori. Have to wait until the + // body is typechecked. + true + case _ => false + } + } + } + if (adaptToPrototype) + return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt) case _ => } // try an implicit conversion diff --git a/tests/run/implicitFunctionImpl.check b/tests/run/implicitFunctionImpl.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/run/implicitFunctionImpl.check @@ -0,0 +1 @@ +42 diff --git a/tests/run/implicitFunctionImpl.scala b/tests/run/implicitFunctionImpl.scala new file mode 100644 index 000000000000..2e3124feb17a --- /dev/null +++ b/tests/run/implicitFunctionImpl.scala @@ -0,0 +1,10 @@ + +object Test { + def main(args: Array[String]): Unit = { + println(new Fun().apply(42)) + } +} + +class Fun extends ImplicitFunction1[Int, Int] { + def apply(implicit v1: Int): Int = v1 +}