Skip to content

Commit d1dd468

Browse files
committed
Factor out logic for scala functions.
1 parent 75bea8d commit d1dd468

File tree

17 files changed

+196
-102
lines changed

17 files changed

+196
-102
lines changed

compiler/sjs/backend/sjs/JSCodeGen.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import SymDenotations._
1616
import Contexts._
1717
import Decorators._
1818
import Flags._
19+
import functions.ScalaFunction
1920
import dotty.tools.dotc.ast.Trees._
2021
import Types._
2122
import Symbols._
@@ -1903,7 +1904,7 @@ class JSCodeGen()(implicit ctx: Context) {
19031904
if (jsdefn.isJSFunctionClass(funInterfaceSym)) {
19041905
closure
19051906
} else {
1906-
assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym),
1907+
assert(!funInterfaceSym.exists || ScalaFunction(funInterfaceSym).exists,
19071908
s"Invalid functional interface $funInterfaceSym reached the back-end")
19081909
val cls = "sjsr_AnonFunction" + formalParams.size
19091910
val ctor = js.Ident("init___sjs_js_Function" + formalParams.size)

compiler/sjs/tools/dotc/config/SJSPlatform.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.dotc.config
22

33
import dotty.tools.dotc.core._
4+
import functions.ScalaFunction
45
import Contexts._
56
import Symbols._
67

@@ -13,6 +14,6 @@ class SJSPlatform()(implicit ctx: Context) extends JavaPlatform {
1314

1415
/** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */
1516
override def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
16-
defn.isFunctionClass(cls) || jsDefinitions.isJSFunctionClass(cls)
17+
ScalaFunction(cls).exists || jsDefinitions.isJSFunctionClass(cls)
1718

1819
}

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

Lines changed: 42 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import scala.collection.{ mutable, immutable }
1010
import PartialFunction._
1111
import collection.mutable
1212
import scala.reflect.api.{ Universe => ApiUniverse }
13+
import functions._
1314

1415
object Definitions {
1516

@@ -90,7 +91,7 @@ class Definitions {
9091
}
9192

9293
/** The trait FunctionN or ImplicitFunctionN, for some N
93-
* @param name The name of the trait to be created
94+
* @param function The function name description of the trait to be created
9495
*
9596
* FunctionN traits follow this template:
9697
*
@@ -108,32 +109,28 @@ class Definitions {
108109
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
109110
* }
110111
*/
111-
private def newFunctionNTrait(name: TypeName) = {
112+
private def newFunctionNTrait(function: ScalaFunction) = {
112113
val completer = new LazyType {
113114
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
114115
val cls = denot.asClass.classSymbol
115116
val decls = newScope
116-
val arity = name.functionArity
117-
val argParams =
118-
for (i <- List.range(0, arity)) yield
119-
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
120-
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
117+
val tParamTpes = List.tabulate(function.arity + 1) { i =>
118+
val variance = if (i < function.arity) Contravariant else Covariant
119+
val paramName = tpnme.scala_ ++ "$" ++ function.name ++ "$$" ++ (if (i != function.arity) "T" + (i + 1) else "R")
120+
enterTypeParam(cls, paramName, variance, decls).typeRef
121+
}
121122
val (methodType, parentTraits) =
122-
if (name.startsWith(tpnme.ImplicitFunction)) {
123-
val superTrait =
124-
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
123+
if (function.isImplicit) {
124+
val superTrait = function.withoutImplicit.typeRef.appliedTo(tParamTpes)
125125
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
126126
}
127127
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)
128+
129+
decls.enter(newMethod(cls, nme.apply, methodType(tParamTpes.init, tParamTpes.last), Deferred))
130+
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
134131
}
135132
}
136-
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
133+
newClassSymbol(ScalaPackageClass, function.name, NoInitsTrait, completer)
137134
}
138135

139136
private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
@@ -631,19 +628,18 @@ class Definitions {
631628
sym.owner.linkedClass.typeRef
632629

633630
object FunctionOf {
634-
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
635-
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
631+
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) = {
632+
ScalaFunction(args, isImplicit).typeRef.appliedTo(args ::: resultType :: Nil)
633+
}
636634
def unapply(ft: Type)(implicit ctx: Context) = {
637635
val tsym = ft.typeSymbol
638-
val isImplicitFun = isImplicitFunctionClass(tsym)
639-
if (isImplicitFun || isFunctionClass(tsym)) {
640-
val targs = ft.argInfos
641-
val numArgs = targs.length - 1
642-
if (numArgs >= 0 && FunctionType(numArgs, isImplicitFun).symbol == tsym)
643-
Some(targs.init, targs.last, isImplicitFun)
644-
else None
636+
val function = ScalaFunction(tsym)
637+
if (function.exists) {
638+
val targs = ft.dealias.argInfos
639+
Some(targs.init, targs.last, function.isImplicit)
640+
} else {
641+
None
645642
}
646-
else None
647643
}
648644
}
649645

@@ -688,26 +684,14 @@ class Definitions {
688684
lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
689685
val AbstractFunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => AbstractFunctionType.map(_.symbol.asClass))
690686
def AbstractFunctionClass(n: Int)(implicit ctx: Context) = AbstractFunctionClassPerRun()(ctx)(n)
691-
private lazy val ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0)
687+
lazy val ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0)
692688
def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => ImplementedFunctionType.map(_.symbol.asClass))
693689

694690
lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
695691
lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
696692

697-
def FunctionClass(n: Int)(implicit ctx: Context) =
698-
if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
699-
else ctx.requiredClass("scala.Function" + n.toString)
700-
701-
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
702-
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
703-
704-
def ImplicitFunctionClass(n: Int)(implicit ctx: Context) =
705-
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
706-
707-
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
708-
if (isImplicit && !ctx.erasedTypes) ImplicitFunctionClass(n).typeRef
709-
else if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n)
710-
else FunctionClass(n).typeRef
693+
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
694+
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
711695

712696
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
713697
private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet
@@ -731,10 +715,6 @@ class Definitions {
731715
def isBottomType(tp: Type) =
732716
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
733717

734-
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
735-
def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction)
736-
/** Is a class that will be erased to FunctionXXL */
737-
def isXXLFunctionClass(cls: Symbol) = cls.name.functionArity > MaxImplementedFunctionArity
738718
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
739719
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
740720
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
@@ -780,7 +760,8 @@ class Definitions {
780760
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
781761
* is treated as a NoInit trait.
782762
*/
783-
lazy val NoInitClasses = PhantomClasses + FunctionXXLClass
763+
def isNoInitClass(cls: Symbol) =
764+
cls.is(NoInitsTrait) || PhantomClasses(cls) || ScalaFunction(cls).exists
784765

785766
def isPolymorphicAfterErasure(sym: Symbol) =
786767
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -807,18 +788,17 @@ class Definitions {
807788

808789
/** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN ? */
809790
def isFunctionType(tp: Type)(implicit ctx: Context) = {
810-
val arity = functionArity(tp)
811-
val sym = tp.dealias.typeSymbol
812-
arity >= 0 && (
813-
isFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = false).typeSymbol) ||
814-
isImplicitFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = true).typeSymbol)
815-
)
791+
val function = ScalaFunction(tp.dealias.typeSymbol)
792+
function.exists && tp.isRef(function.symbol)
816793
}
817794

818-
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
795+
def functionArity(tp: Type)(implicit ctx: Context) =
796+
ScalaFunction(tp.dealias.typeSymbol).arity
819797

820-
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) =
821-
isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction)
798+
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) = {
799+
val function = ScalaFunction(tp.dealias.typeSymbol)
800+
function.isImplicit && tp.isRef(function.symbol)
801+
}
822802

823803
// ----- primitive value class machinery ------------------------------------------
824804

@@ -892,9 +872,6 @@ class Definitions {
892872

893873
// ----- Initialization ---------------------------------------------------
894874

895-
private def maxImplemented(name: Name) =
896-
if (name `startsWith` tpnme.Function) MaxImplementedFunctionArity else 0
897-
898875
/** Give the scala package a scope where a FunctionN trait is automatically
899876
* added when someone looks for it.
900877
*/
@@ -904,8 +881,9 @@ class Definitions {
904881
val newDecls = new MutableScope(oldDecls) {
905882
override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
906883
val res = super.lookupEntry(name)
907-
if (res == null && name.isTypeName && name.functionArity > maxImplemented(name))
908-
newScopeEntry(newFunctionNTrait(name.asTypeName))
884+
lazy val function = ScalaFunction(name.asTypeName)
885+
if (res == null && name.isTypeName && function.isSynthetic)
886+
newScopeEntry(newFunctionNTrait(function))
909887
else res
910888
}
911889
}
@@ -928,6 +906,9 @@ class Definitions {
928906
EmptyPackageVal,
929907
OpsPackageClass)
930908

909+
def isSyntheticScalaClass(sym: Symbol): Boolean =
910+
syntheticScalaClasses.contains(sym) || ScalaFunction(sym).isSynthetic
911+
931912
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
932913
lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)
933914

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,6 @@ object NameOps {
231231
}
232232
}
233233

234-
def functionArity: Int = {
235-
def test(prefix: Name): Int =
236-
if (name.startsWith(prefix))
237-
try name.drop(prefix.length).toString.toInt
238-
catch { case ex: NumberFormatException => -1 }
239-
else -1
240-
test(tpnme.Function) max test(tpnme.ImplicitFunction)
241-
}
242-
243234
/** The name of the generic runtime operation corresponding to an array operation */
244235
def genericArrayOp: TermName = name match {
245236
case nme.apply => nme.array_apply

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ object StdNames {
183183
final val ExprApi: N = "ExprApi"
184184
final val Function: N = "Function"
185185
final val ImplicitFunction: N = "ImplicitFunction"
186+
final val FunctionXXL: N = "FunctionXXL"
186187
final val Mirror: N = "Mirror"
187188
final val Nothing: N = "Nothing"
188189
final val Null: N = "Null"

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotc.transform.ExplicitOuter._
1010
import dotc.transform.ValueClasses._
1111
import util.DotClass
1212
import Definitions.MaxImplementedFunctionArity
13+
import dotty.tools.dotc.core.functions.ScalaFunction
1314

1415
/** Erased types are:
1516
*
@@ -44,7 +45,7 @@ object TypeErasure {
4445
val sym = tp.symbol
4546
sym.isClass &&
4647
sym != defn.AnyClass && sym != defn.ArrayClass &&
47-
!defn.isXXLFunctionClass(sym) && !defn.isImplicitFunctionClass(sym)
48+
!ScalaFunction(sym).isSynthetic
4849
case _: TermRef =>
4950
true
5051
case JavaArrayType(elem) =>
@@ -355,11 +356,11 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
355356
tp
356357
case tp: TypeRef =>
357358
val sym = tp.symbol
359+
lazy val function = ScalaFunction(sym)
358360
if (!sym.isClass) this(tp.info)
359361
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
360362
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
361-
else if (defn.isXXLFunctionClass(sym)) defn.FunctionXXLType
362-
else if (defn.isImplicitFunctionClass(sym)) apply(defn.FunctionType(sym.name.functionArity))
363+
else if (function.exists) function.erased.typeRef
363364
else eraseNormalClassRef(tp)
364365
case tp: RefinedType =>
365366
val parent = tp.parent
@@ -370,7 +371,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
370371
case SuperType(thistpe, supertpe) =>
371372
SuperType(this(thistpe), this(supertpe))
372373
case ExprType(rt) =>
373-
defn.FunctionClass(0).typeRef
374+
defn.ImplementedFunctionType(0)
374375
case AndType(tp1, tp2) =>
375376
erasedGlb(this(tp1), this(tp2), isJava)
376377
case OrType(tp1, tp2) =>
@@ -496,8 +497,9 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
496497
val erasedVCRef = eraseDerivedValueClassRef(tp)
497498
if (erasedVCRef.exists) return sigName(erasedVCRef)
498499
}
499-
if (defn.isImplicitFunctionClass(sym))
500-
sigName(defn.FunctionType(sym.name.functionArity))
500+
val function = ScalaFunction(sym)
501+
if (function.isSynthetic)
502+
sigName(function.erased.typeRef)
501503
else
502504
normalizeClass(sym.asClass).fullName.asTypeName
503505
case defn.ArrayOf(elem) =>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package dotty.tools.dotc.core.functions
2+
3+
import dotty.tools.dotc.core.Contexts._
4+
import dotty.tools.dotc.core.Definitions._
5+
import dotty.tools.dotc.core.Names._
6+
import dotty.tools.dotc.core.StdNames._
7+
import dotty.tools.dotc.core.Symbols._
8+
import dotty.tools.dotc.core.Types._
9+
10+
trait ScalaFunction {
11+
def arity: Int
12+
def isImplicit: Boolean
13+
14+
def symbol(implicit ctx: Context): Symbol
15+
def typeRef(implicit ctx: Context): TypeRef
16+
17+
def name: TypeName
18+
protected def fullName: TypeName = tpnme.scala_ ++ "." ++ name
19+
20+
override def toString(): String = s"ScalaFunction($name)"
21+
}
22+
23+
object ScalaFunction {
24+
25+
def apply(sym: Symbol)(implicit ctx: Context): ScalaFunction = apply(defn.scalaClassName(sym))
26+
27+
def apply(name: TypeName)(implicit ctx: Context): ScalaFunction = {
28+
val functionArity = testArity(tpnme.Function, name)
29+
lazy val implicitFunctionArity = testArity(tpnme.ImplicitFunction, name)
30+
if (functionArity != -1)
31+
new SimpleFunction(functionArity, isImplicit = false)
32+
else if (implicitFunctionArity != -1)
33+
new SimpleFunction(implicitFunctionArity, isImplicit = true)
34+
else if (name == tpnme.FunctionXXL) ScalaFunctionXXL
35+
else new NotAFunctionName(name)
36+
}
37+
38+
def apply(args: List[Type], isImplicit: Boolean)(implicit ctx: Context): ScalaFunction = {
39+
new SimpleFunction(args.size, isImplicit)
40+
}
41+
42+
def apply(arity: Int, isImplicit: Boolean = false): SimpleFunction =
43+
new SimpleFunction(arity, isImplicit)
44+
45+
private class NotAFunctionName(val name: TypeName) extends ScalaFunction {
46+
override def symbol(implicit ctx: Context): Symbol = NoSymbol
47+
def typeRef(implicit ctx: Context): TypeRef = throw new UnsupportedOperationException()
48+
def arity: Int = -1
49+
def isImplicit: Boolean = false
50+
override def toString(): String = "NotAFunctionName"
51+
}
52+
53+
implicit class Ops(fun: ScalaFunction) {
54+
def exists: Boolean = !fun.isInstanceOf[NotAFunctionName]
55+
def isFunctionXXL: Boolean = fun eq ScalaFunctionXXL
56+
def isSynthetic: Boolean = exists && (fun.isImplicit || fun.arity > MaxImplementedFunctionArity)
57+
58+
def erased: ScalaFunction = {
59+
if (fun.arity > MaxImplementedFunctionArity) ScalaFunctionXXL
60+
else fun.withoutImplicit
61+
}
62+
63+
def withoutImplicit: ScalaFunction = fun match {
64+
case f: SimpleFunction if f.isImplicit => new SimpleFunction(f.arity, isImplicit = false)
65+
case _ => fun
66+
}
67+
}
68+
69+
private def testArity(prefix: Name, name: Name): Int = {
70+
if (name.startsWith(prefix))
71+
try name.toString.substring(prefix.length).toInt
72+
catch { case ex: NumberFormatException => -1 }
73+
else -1
74+
}
75+
76+
}

0 commit comments

Comments
 (0)