Skip to content

Commit 8cfc30a

Browse files
committed
Add marker trait for polymorphic function types
This is the only trait that can be refined with a polymorphic method, as long as that method is called `apply`, e.g.: PolyFunction { def apply[T_1, ..., T_M](x_1: P_1, ..., x_N: P_N): R } This type will be erased to FunctionN.
1 parent 76b110b commit 8cfc30a

File tree

6 files changed

+48
-12
lines changed

6 files changed

+48
-12
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,9 @@ class Definitions {
882882
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes) && !isErased) ImplementedFunctionType(n)
883883
else FunctionClass(n, isImplicit, isErased).typeRef
884884

885+
lazy val PolyFunctionClass = ctx.requiredClass("scala.PolyFunction")
886+
def PolyFunctionType = PolyFunctionClass.typeRef
887+
885888
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
886889

887890
/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
353353
* - otherwise, if T is a type paramter coming from Java, []Object
354354
* - otherwise, Object
355355
* - For a term ref p.x, the type <noprefix> # x.
356+
* - For a refined type scala.PolyFunction { def apply[...](x_1, ..., x_N): R }, scala.FunctionN
356357
* - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object|
357358
* - For a typeref scala.Unit, |scala.runtime.BoxedUnit|.
358359
* - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL
@@ -397,6 +398,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
397398
SuperType(this(thistpe), this(supertpe))
398399
case ExprType(rt) =>
399400
defn.FunctionType(0)
401+
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
402+
assert(refinedInfo.isInstanceOf[PolyType])
403+
val res = refinedInfo.resultType
404+
val paramss = res.paramNamess
405+
assert(paramss.length == 1)
406+
this(defn.FunctionType(paramss.head.length, isImplicit = res.isImplicitMethod, isErased = res.isErasedMethod))
400407
case tp: TypeProxy =>
401408
this(tp.underlying)
402409
case AndType(tp1, tp2) =>

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -378,21 +378,26 @@ object Erasure {
378378
*/
379379
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
380380

381+
val qual1 = typed(tree.qualifier, AnySelectionProto)
382+
381383
def mapOwner(sym: Symbol): Symbol = {
382-
def recur(owner: Symbol): Symbol =
383-
if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) {
384-
assert(sym.isConstructor, s"${sym.showLocated}")
385-
defn.ObjectClass
386-
} else if (defn.isSyntheticFunctionClass(owner))
387-
defn.erasedFunctionClass(owner)
388-
else
389-
owner
390-
recur(sym.owner)
384+
val owner = sym.maybeOwner
385+
if (!owner.exists) {
386+
// Hack for PolyFunction#apply
387+
qual1.tpe.widen.typeSymbol
388+
} else if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) {
389+
assert(sym.isConstructor, s"${sym.showLocated}")
390+
defn.ObjectClass
391+
} else if (defn.isSyntheticFunctionClass(owner)) {
392+
defn.erasedFunctionClass(owner)
393+
}
394+
else
395+
owner
391396
}
392397

393398
val origSym = tree.symbol
394399
val owner = mapOwner(origSym)
395-
val sym = if (owner eq origSym.owner) origSym else owner.info.decl(origSym.name).symbol
400+
val sym = if (owner eq origSym.maybeOwner) origSym else owner.info.decl(tree.name).symbol
396401
assert(sym.exists, origSym.showLocated)
397402

398403
def select(qual: Tree, sym: Symbol): Tree =
@@ -436,7 +441,7 @@ object Erasure {
436441
}
437442
}
438443

439-
recur(typed(tree.qualifier, AnySelectionProto))
444+
recur(qual1)
440445
}
441446

442447
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1200,7 +1200,9 @@ class Typer extends Namer
12001200
typr.println(s"adding refinement $refinement")
12011201
checkRefinementNonCyclic(refinement, refineCls, seen)
12021202
val rsym = refinement.symbol
1203-
if (rsym.info.isInstanceOf[PolyType] && rsym.allOverriddenSymbols.isEmpty)
1203+
val polymorphicRefinementAllowed =
1204+
tpt1.tpe.typeSymbol == defn.PolyFunctionClass && rsym.name == nme.apply
1205+
if (!polymorphicRefinementAllowed && rsym.info.isInstanceOf[PolyType] && rsym.allOverriddenSymbols.isEmpty)
12041206
ctx.error(PolymorphicMethodMissingTypeInParent(rsym, tpt1.symbol), refinement.pos)
12051207
}
12061208
assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls)

library/src/scala/PolyFunction.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package scala
2+
3+
/** Marker trait for polymorphic function types.
4+
*
5+
* This is the only trait that can be refined with a polymorphic method,
6+
* as long as that method is called `apply`, e.g.:
7+
* PolyFunction { def apply[T_1, ..., T_M](x_1: P_1, ..., x_N: P_N): R }
8+
* This type will be erased to FunctionN.
9+
*/
10+
trait PolyFunction

tests/run/polymorphic-functions.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test {
2+
def test1(f: PolyFunction { def apply[T <: AnyVal](x: List[T]): List[(T, T)] }) = {
3+
f(List(1, 2, 3))
4+
}
5+
6+
def main(args: Array[String]): Unit = {
7+
//test1(...)
8+
}
9+
}

0 commit comments

Comments
 (0)