Skip to content
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

Drop function 22 limit #1758

Merged
merged 6 commits into from
Dec 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
val externalEqualsNumChar: Symbol = NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private
val externalEqualsNumObject: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject)
val externalEquals: Symbol = defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol
val MaxFunctionArity: Int = Definitions.MaxFunctionArity
val MaxFunctionArity: Int = Definitions.MaxImplementedFunctionArity
val FunctionClass: Array[Symbol] = defn.FunctionClassPerRun()
val AbstractFunctionClass: Array[Symbol] = defn.AbstractFunctionClassPerRun()
val PartialFunctionClass: Symbol = defn.PartialFunctionClass
Expand Down
214 changes: 137 additions & 77 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ object NameOps {
}
}

def functionArity: Int =
if (name.startsWith(tpnme.Function))
try name.drop(tpnme.Function.length).toString.toInt
catch { case ex: NumberFormatException => -1 }
else -1

/** The name of the generic runtime operation corresponding to an array operation */
def genericArrayOp: TermName = name match {
case nme.apply => nme.array_apply
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ object Scopes {

/** Lookup a symbol entry matching given name.
*/
override final def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
var e: ScopeEntry = null
if (hashTable ne null) {
e = hashTable(name.hashCode & (hashTable.length - 1))
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
80 changes: 65 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,23 @@ 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)

def mapOwner(sym: Symbol): Symbol = {
val owner = sym.owner
if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) {
assert(sym.isConstructor, s"${sym.showLocated}")
defn.ObjectClass
}
else if (defn.isUnimplementedFunctionClass(owner))
defn.FunctionXXLClass
else
owner
}

var sym = tree.symbol
val owner = mapOwner(sym)
if (owner ne sym.owner) sym = owner.info.decl(sym.name).symbol
assert(sym.exists, owner)

def select(qual: Tree, sym: Symbol): Tree = {
val name = tree.typeOpt match {
Expand Down Expand Up @@ -366,11 +393,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 +446,9 @@ object Erasure extends TypeTestsCasts{
}
}

/** Besides normal 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)
Expand All @@ -434,7 +460,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 +502,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
21 changes: 0 additions & 21 deletions library/src/scala/Function23.scala

This file was deleted.

21 changes: 0 additions & 21 deletions library/src/scala/Function24.scala

This file was deleted.

21 changes: 0 additions & 21 deletions library/src/scala/Function25.scala

This file was deleted.

20 changes: 0 additions & 20 deletions library/src/scala/Function26.scala

This file was deleted.

20 changes: 0 additions & 20 deletions library/src/scala/Function27.scala

This file was deleted.

20 changes: 0 additions & 20 deletions library/src/scala/Function28.scala

This file was deleted.

Loading