Skip to content

Drop function 22 limit #1758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 3, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -645,17 +648,17 @@ 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)

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

Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be strict equality instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, indeed.

def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction)
def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple)
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
17 changes: 14 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Uniques.unique
import dotc.transform.ExplicitOuter._
import dotc.transform.ValueClasses._
import util.DotClass
import Definitions.MaxImplementedFunctionArity

/** Erased types are:
*
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we implement isImplementedFunctionClass instead to avoid the double negation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just one occurrence, so probably not.

case _: TermRef =>
true
case JavaArrayType(elem) =>
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
77 changes: 62 additions & 15 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -423,6 +443,9 @@ object Erasure extends TypeTestsCasts{
}
}

/** Besides notmal typing, this method collects all arguments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is off

* 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)
Expand All @@ -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}")
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/to ise/into/?

* 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)
}

Expand Down
72 changes: 72 additions & 0 deletions tests/pos/functionXXL.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
object Test {

val f = (x1: Int,
x2: Int,
x3: Int,
x4: Int,
x5: Int,
x6: Int,
x7: Int,
x8: Int,
x9: Int,
x10: Int,
x11: Int,
x12: Int,
x13: Int,
x14: Int,
x15: Int,
x16: Int,
x17: Int,
x18: Int,
x19: Int,
x20: Int,
x21: Int,
x22: Int,
x23: Int,
x24: Int,
x25: Int,
x26: Int) => 42

def main(args: Array[String]) = {
val g = (x1: Int,
x2: Int,
x3: Int,
x4: Int,
x5: Int,
x6: Int,
x7: Int,
x8: Int,
x9: Int,
x10: Int,
x11: Int,
x12: Int,
x13: Int,
x14: Int,
x15: Int,
x16: Int,
x17: Int,
x18: Int,
x19: Int,
x20: Int,
x21: Int,
x22: Int,
x23: Int,
x24: Int,
x25: Int,
x26: Int) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
x11, x12, x13, x14, x15, x16, x17, x18, x19, x20,
x21, x22, x23, x24, x25, x26)



println(f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26))


println(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26))
}

}
2 changes: 1 addition & 1 deletion tests/pos/lazyValsSepComp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ import dotty.tools.dotc.core.Contexts._
object Foo {
val definitions: Definitions = null
def defn = definitions
def go = defn.FunctionType(0)
def go = defn.FunctionClassPerRun
}