diff options
author | Martin Odersky <odersky@gmail.com> | 2016-11-30 14:34:17 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-11-30 14:34:17 +0100 |
commit | b94e6ea5a54d38470e5793c0084785e2d2f9a819 (patch) | |
tree | 6a3f4e83dbf66ad9a2353d4165f6321fede1dceb /compiler/src/dotty/tools/dotc/transform/Erasure.scala | |
parent | 3116142d3e0e2d560b2fa79f73e699e1ac000204 (diff) | |
download | dotty-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/src/dotty/tools/dotc/transform/Erasure.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/Erasure.scala | 77 |
1 files changed, 62 insertions, 15 deletions
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) } |