aboutsummaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-11-30 14:34:17 +0100
committerMartin Odersky <odersky@gmail.com>2016-11-30 14:34:17 +0100
commitb94e6ea5a54d38470e5793c0084785e2d2f9a819 (patch)
tree6a3f4e83dbf66ad9a2353d4165f6321fede1dceb /compiler
parent3116142d3e0e2d560b2fa79f73e699e1ac000204 (diff)
downloaddotty-b94e6ea5a54d38470e5793c0084785e2d2f9a819.tar.gz
dotty-b94e6ea5a54d38470e5793c0084785e2d2f9a819.tar.bz2
dotty-b94e6ea5a54d38470e5793c0084785e2d2f9a819.zip
Drop function 22 limit.
Functions with more than 22 parameters are now automatically converted to functions taking a single object array parameter. This has been achieved by tweaking erasure. Other things I have tried that did ot work out well: - Use a single function type in typer. The problem with this one which could not be circumvented was that existing higher-kinded code with e.g. Funcor assumes that Functon1 is a binary type constructor. - Have a late phase that converts to FunctonXXL instead of doing it in erasure. The problem with that one was that potentially every type could be affected, which was ill-suited to the architecture of a miniphase.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala13
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/TypeErasure.scala17
-rw-r--r--compiler/src/dotty/tools/dotc/transform/Erasure.scala77
4 files changed, 86 insertions, 22 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index e1a87d1f7..29b7bb60a 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -457,6 +457,9 @@ class Definitions {
def PartialFunctionClass(implicit ctx: Context) = PartialFunctionType.symbol.asClass
lazy val AbstractPartialFunctionType: TypeRef = ctx.requiredClassRef("scala.runtime.AbstractPartialFunction")
def AbstractPartialFunctionClass(implicit ctx: Context) = AbstractPartialFunctionType.symbol.asClass
+ lazy val FunctionXXLType: TypeRef = ctx.requiredClassRef("scala.FunctionXXL")
+ def FunctionXXLClass(implicit ctx: Context) = FunctionXXLType.symbol.asClass
+
lazy val SymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol")
def SymbolClass(implicit ctx: Context) = SymbolType.symbol.asClass
lazy val DynamicType: TypeRef = ctx.requiredClassRef("scala.Dynamic")
@@ -645,6 +648,9 @@ class Definitions {
private lazy val ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0)
def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => ImplementedFunctionType.map(_.symbol.asClass))
+ lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
+ lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
+
def FunctionClass(n: Int)(implicit ctx: Context) =
if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
else ctx.requiredClass("scala.Function" + n.toString)
@@ -652,10 +658,7 @@ class Definitions {
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
- lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
- lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
-
- def FunctionType(n: Int): TypeRef =
+ def FunctionType(n: Int)(implicit ctx: Context): TypeRef =
if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n)
else FunctionClass(n).typeRef
@@ -680,6 +683,8 @@ class Definitions {
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)
def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function)
+ def isUnimplementedFunctionClass(cls: Symbol) =
+ isFunctionClass(cls) && cls.name.functionArity >= MaxImplementedFunctionArity
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index c2a14b36f..741ff8b1f 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -265,6 +265,7 @@ object StdNames {
val THIS: N = "_$this"
val TRAIT_CONSTRUCTOR: N = "$init$"
val U2EVT: N = "u2evt$"
+ val ALLARGS: N = "$allArgs"
final val Nil: N = "Nil"
final val Predef: N = "Predef"
diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala
index abbacee49..57397a8bc 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala
@@ -7,6 +7,7 @@ import Uniques.unique
import dotc.transform.ExplicitOuter._
import dotc.transform.ValueClasses._
import util.DotClass
+import Definitions.MaxImplementedFunctionArity
/** Erased types are:
*
@@ -38,7 +39,10 @@ object TypeErasure {
case _: ErasedValueType =>
true
case tp: TypeRef =>
- tp.symbol.isClass && tp.symbol != defn.AnyClass && tp.symbol != defn.ArrayClass
+ val sym = tp.symbol
+ sym.isClass &&
+ sym != defn.AnyClass && sym != defn.ArrayClass &&
+ !defn.isUnimplementedFunctionClass(sym)
case _: TermRef =>
true
case JavaArrayType(elem) =>
@@ -176,8 +180,13 @@ object TypeErasure {
else if (sym.isAbstractType) TypeAlias(WildcardType)
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
else erase.eraseInfo(tp, sym)(erasureCtx) match {
- case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) =>
- MethodType(Nil, defn.BoxedUnitType)
+ case einfo: MethodType =>
+ if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass))
+ MethodType(Nil, defn.BoxedUnitType)
+ else if (sym.isAnonymousFunction && einfo.paramTypes.length > MaxImplementedFunctionArity)
+ MethodType(nme.ALLARGS :: Nil, JavaArrayType(defn.ObjectType) :: Nil, einfo.resultType)
+ else
+ einfo
case einfo =>
einfo
}
@@ -317,6 +326,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
* - For a term ref p.x, the type <noprefix> # x.
* - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object|
* - For a typeref scala.Unit, |scala.runtime.BoxedUnit|.
+ * - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL
* - For a typeref P.C where C refers to a class, <noprefix> # C.
* - For a typeref P.C where C refers to an alias type, the erasure of C's alias.
* - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound.
@@ -345,6 +355,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
if (!sym.isClass) this(tp.info)
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
+ else if (defn.isUnimplementedFunctionClass(sym)) defn.FunctionXXLType
else eraseNormalClassRef(tp)
case tp: RefinedType =>
val parent = tp.parent
diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala
index 069176111..5dd2e512b 100644
--- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala
@@ -13,6 +13,7 @@ import core.StdNames._
import core.NameOps._
import core.Decorators._
import core.Constants._
+import core.Definitions._
import typer.NoChecking
import typer.ProtoTypes._
import typer.ErrorReporting._
@@ -36,9 +37,17 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case ref: SymDenotation =>
+ def isCompacted(sym: Symbol) =
+ sym.isAnonymousFunction && {
+ sym.info(ctx.withPhase(ctx.phase.next)) match {
+ case MethodType(nme.ALLARGS :: Nil, _) => true
+ case _ => false
+ }
+ }
+
assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}")
if (ref.symbol eq defn.ObjectClass) {
- // Aftre erasure, all former Any members are now Object members
+ // After erasure, all former Any members are now Object members
val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info
val extendedScope = decls.cloneScope
for (decl <- defn.AnyClass.classInfo.decls)
@@ -59,7 +68,10 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
val oldInfo = ref.info
val newInfo = transformInfo(ref.symbol, oldInfo)
val oldFlags = ref.flags
- val newFlags = ref.flags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading
+ val newFlags =
+ if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param
+ else oldFlags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading
+
// TODO: define derivedSymDenotation?
if ((oldSymbol eq newSymbol) && (oldOwner eq newOwner) && (oldInfo eq newInfo) && (oldFlags == newFlags)) ref
else {
@@ -331,8 +343,20 @@ object Erasure extends TypeTestsCasts{
* e.m -> e.[]m if `m` is an array operation other than `clone`.
*/
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
- val sym = tree.symbol
- assert(sym.exists, tree.show)
+ val oldSym = tree.symbol
+ assert(oldSym.exists)
+ val oldOwner = oldSym.owner
+ val owner =
+ if ((oldOwner eq defn.AnyClass) || (oldOwner eq defn.AnyValClass)) {
+ assert(oldSym.isConstructor, s"${oldSym.showLocated}")
+ defn.ObjectClass
+ }
+ else if (defn.isUnimplementedFunctionClass(oldOwner))
+ defn.FunctionXXLClass
+ else
+ oldOwner
+ val sym = if (owner eq oldOwner) oldSym else owner.info.decl(oldSym.name).symbol
+ assert(sym.exists, owner)
def select(qual: Tree, sym: Symbol): Tree = {
val name = tree.typeOpt match {
@@ -366,11 +390,7 @@ object Erasure extends TypeTestsCasts{
def recur(qual: Tree): Tree = {
val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType
val symIsPrimitive = sym.owner.isPrimitiveValueClass
- if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass)) {
- assert(sym.isConstructor, s"${sym.showLocated}")
- select(qual, defn.ObjectClass.info.decl(sym.name).symbol)
- }
- else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
+ if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
recur(box(qual))
else if (!qualIsPrimitive && symIsPrimitive)
recur(unbox(qual, sym.owner.typeRef))
@@ -423,6 +443,9 @@ object Erasure extends TypeTestsCasts{
}
}
+ /** Besides notmal typing, this method collects all arguments
+ * to a compacted function into a single argument of array type.
+ */
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
val Apply(fun, args) = tree
if (fun.symbol == defn.dummyApply)
@@ -434,7 +457,13 @@ object Erasure extends TypeTestsCasts{
fun1.tpe.widen match {
case mt: MethodType =>
val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased
- val args1 = (outers ::: args ++ protoArgs(pt)).zipWithConserve(mt.paramTypes)(typedExpr)
+ var args0 = outers ::: args ++ protoArgs(pt)
+ if (args0.length > MaxImplementedFunctionArity && mt.paramTypes.length == 1) {
+ val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType))
+ .withType(defn.ArrayOf(defn.ObjectType))
+ args0 = bunchedArgs :: Nil
+ }
+ val args1 = args0.zipWithConserve(mt.paramTypes)(typedExpr)
untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType
case _ =>
throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}")
@@ -470,18 +499,36 @@ object Erasure extends TypeTestsCasts{
super.typedValDef(untpd.cpy.ValDef(vdef)(
tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym)
+ /** Besides normal typing, this function also compacts anonymous functions
+ * with more than `MaxImplementedFunctionArity` parameters to ise a single
+ * parameter of type `[]Object`.
+ */
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
val restpe =
if (sym.isConstructor) defn.UnitType
else sym.info.resultType
+ var vparamss1 = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil
+ var rhs1 = ddef.rhs match {
+ case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe))
+ case _ => ddef.rhs
+ }
+ if (sym.isAnonymousFunction && vparamss1.head.length > MaxImplementedFunctionArity) {
+ val bunchedParam = ctx.newSymbol(sym, nme.ALLARGS, Flags.TermParam, JavaArrayType(defn.ObjectType))
+ def selector(n: Int) = ref(bunchedParam)
+ .select(defn.Array_apply)
+ .appliedTo(Literal(Constant(n)))
+ val paramDefs = vparamss1.head.zipWithIndex.map {
+ case (paramDef, idx) =>
+ assignType(untpd.cpy.ValDef(paramDef)(rhs = selector(idx)), paramDef.symbol)
+ }
+ vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil
+ rhs1 = untpd.Block(paramDefs, rhs1)
+ }
val ddef1 = untpd.cpy.DefDef(ddef)(
tparams = Nil,
- vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil,
+ vparamss = vparamss1,
tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)),
- rhs = ddef.rhs match {
- case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe))
- case _ => ddef.rhs
- })
+ rhs = rhs1)
super.typedDefDef(ddef1, sym)
}