Skip to content

Commit 951eaf4

Browse files
smartermilessabin
authored andcommitted
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 dbac6dc commit 951eaf4

File tree

6 files changed

+46
-6
lines changed

6 files changed

+46
-6
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,11 @@ class Definitions {
10351035
if (n <= MaxImplementedFunctionArity && (!isContextual || ctx.erasedTypes) && !isErased) ImplementedFunctionType(n)
10361036
else FunctionClass(n, isContextual, isErased).typeRef
10371037

1038+
lazy val PolyFunctionClass = ctx.requiredClass("scala.PolyFunction")
1039+
def PolyFunctionType = PolyFunctionClass.typeRef
1040+
1041+
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
1042+
10381043
/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
10391044
def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName =
10401045
if (cls.isClass && cls.owner == ScalaPackageClass) cls.asClass.name else EmptyTypeName

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
383383
* - otherwise, if T is a type parameter coming from Java, []Object
384384
* - otherwise, Object
385385
* - For a term ref p.x, the type <noprefix> # x.
386+
* - For a refined type scala.PolyFunction { def apply[...](x_1, ..., x_N): R }, scala.FunctionN
386387
* - For a typeref scala.Any, scala.AnyVal, scala.Singleton, scala.Tuple, or scala.*: : |java.lang.Object|
387388
* - For a typeref scala.Unit, |scala.runtime.BoxedUnit|.
388389
* - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL
@@ -429,6 +430,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
429430
SuperType(this(thistpe), this(supertpe))
430431
case ExprType(rt) =>
431432
defn.FunctionType(0)
433+
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
434+
assert(refinedInfo.isInstanceOf[PolyType])
435+
val res = refinedInfo.resultType
436+
val paramss = res.paramNamess
437+
assert(paramss.length == 1)
438+
this(defn.FunctionType(paramss.head.length, isContextual = res.isImplicitMethod, isErased = res.isErasedMethod))
432439
case tp: TypeProxy =>
433440
this(tp.underlying)
434441
case AndType(tp1, tp2) =>

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,21 +416,28 @@ object Erasure {
416416
*/
417417
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
418418

419+
val qual1 = typed(tree.qualifier, AnySelectionProto)
420+
419421
def mapOwner(sym: Symbol): Symbol = {
420-
def recur(owner: Symbol): Symbol =
421-
if (defn.specialErasure.contains(owner)) {
422+
def recur(owner: Symbol): Symbol = {
423+
val owner = sym.maybeOwner
424+
if (!owner.exists) {
425+
// Hack for PolyFunction#apply
426+
qual1.tpe.widen.typeSymbol
427+
} else if (defn.specialErasure.contains(owner)) {
422428
assert(sym.isConstructor, s"${sym.showLocated}")
423429
defn.specialErasure(owner)
424430
} else if (defn.isSyntheticFunctionClass(owner))
425431
defn.erasedFunctionClass(owner)
426432
else
427433
owner
428-
recur(sym.owner)
434+
}
435+
recur(sym.maybeOwner)
429436
}
430437

431438
val origSym = tree.symbol
432439
val owner = mapOwner(origSym)
433-
val sym = if (owner eq origSym.owner) origSym else owner.info.decl(origSym.name).symbol
440+
val sym = if (owner eq origSym.maybeOwner) origSym else owner.info.decl(tree.name).symbol
434441
assert(sym.exists, origSym.showLocated)
435442

436443
def select(qual: Tree, sym: Symbol): Tree =
@@ -474,7 +481,7 @@ object Erasure {
474481
}
475482
}
476483

477-
checkNotErased(recur(typed(tree.qualifier, AnySelectionProto)))
484+
checkNotErased(recur(qual1))
478485
}
479486

480487
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
@@ -1271,7 +1271,9 @@ class Typer extends Namer
12711271
typr.println(s"adding refinement $refinement")
12721272
checkRefinementNonCyclic(refinement, refineCls, seen)
12731273
val rsym = refinement.symbol
1274-
if (rsym.info.isInstanceOf[PolyType] && rsym.allOverriddenSymbols.isEmpty)
1274+
val polymorphicRefinementAllowed =
1275+
tpt1.tpe.typeSymbol == defn.PolyFunctionClass && rsym.name == nme.apply
1276+
if (!polymorphicRefinementAllowed && rsym.info.isInstanceOf[PolyType] && rsym.allOverriddenSymbols.isEmpty)
12751277
ctx.error(PolymorphicMethodMissingTypeInParent(rsym, tpt1.symbol), refinement.sourcePos)
12761278

12771279
val member = refineCls.info.member(rsym.name)

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)