Skip to content

Commit fa3fc6d

Browse files
committed
Do uncurrying "bottom up"
A re-implementation of the uncurrying part of erasure. It now goes "bottom-up" by temporarily admitting Apply's that dop not yet have all their arguments. The previous implementation was "top-down" by looking at the prototypes. # Conflicts: # compiler/src/dotty/tools/dotc/transform/Erasure.scala
1 parent c5a3b5c commit fa3fc6d

File tree

2 files changed

+57
-52
lines changed

2 files changed

+57
-52
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,23 @@ object TypeErasure {
129129
erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK))
130130

131131
/** The current context with a phase no later than erasure */
132-
private def erasureCtx(implicit ctx: Context) =
132+
def preErasureCtx(implicit ctx: Context) =
133133
if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx
134134

135135
/** The standard erasure of a Scala type. Value classes are erased as normal classes.
136136
*
137137
* @param tp The type to erase.
138138
*/
139139
def erasure(tp: Type)(implicit ctx: Context): Type =
140-
erasureFn(isJava = false, semiEraseVCs = false, isConstructor = false, wildcardOK = false)(tp)(erasureCtx)
140+
erasureFn(isJava = false, semiEraseVCs = false, isConstructor = false, wildcardOK = false)(tp)(preErasureCtx)
141141

142142
/** The value class erasure of a Scala type, where value classes are semi-erased to
143143
* ErasedValueType (they will be fully erased in [[ElimErasedValueType]]).
144144
*
145145
* @param tp The type to erase.
146146
*/
147147
def valueErasure(tp: Type)(implicit ctx: Context): Type =
148-
erasureFn(isJava = false, semiEraseVCs = true, isConstructor = false, wildcardOK = false)(tp)(erasureCtx)
148+
erasureFn(isJava = false, semiEraseVCs = true, isConstructor = false, wildcardOK = false)(tp)(preErasureCtx)
149149

150150
/** Like value class erasure, but value classes erase to their underlying type erasure */
151151
def fullErasure(tp: Type)(implicit ctx: Context): Type =
@@ -156,7 +156,7 @@ object TypeErasure {
156156
def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = {
157157
val normTp = tp.underlyingIfRepeated(isJava)
158158
val erase = erasureFn(isJava, semiEraseVCs = false, isConstructor = false, wildcardOK = true)
159-
erase.sigName(normTp)(erasureCtx)
159+
erase.sigName(normTp)(preErasureCtx)
160160
}
161161

162162
/** The erasure of a top-level reference. Differs from normal erasure in that
@@ -195,9 +195,9 @@ object TypeErasure {
195195

196196
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
197197
else if (sym.isAbstractType) TypeAlias(WildcardType)
198-
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
199-
else if (sym.is(Label)) erase.eraseResult(sym.info)(erasureCtx)
200-
else erase.eraseInfo(tp, sym)(erasureCtx) match {
198+
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(preErasureCtx))
199+
else if (sym.is(Label)) erase.eraseResult(sym.info)(preErasureCtx)
200+
else erase.eraseInfo(tp, sym)(preErasureCtx) match {
201201
case einfo: MethodType =>
202202
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass))
203203
MethodType(Nil, defn.BoxedUnitClass.typeRef)

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import core.Phases._
@@ -299,9 +300,9 @@ object Erasure {
299300
* e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type
300301
* e -> cast(e, PT) otherwise
301302
*/
302-
def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
303-
if (pt.isInstanceOf[FunProto]) tree
304-
else tree.tpe.widen match {
303+
def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = pt match
304+
case _: FunProto | AnyFunctionProto => tree
305+
case _ => tree.tpe.widen match {
305306
case MethodType(Nil) if tree.isTerm =>
306307
adaptToType(tree.appliedToNone, pt)
307308
case tpw =>
@@ -461,7 +462,7 @@ object Erasure {
461462

462463
def selectArrayMember(qual: Tree, erasedPre: Type): Tree =
463464
if erasedPre.isAnyRef then
464-
runtimeCallWithProtoArgs(tree.name.genericArrayOp, pt, qual)
465+
runtimeCall(tree.name.genericArrayOp, qual :: Nil)
465466
else if !(qual.tpe <:< erasedPre) then
466467
selectArrayMember(cast(qual, erasedPre), erasedPre)
467468
else
@@ -507,20 +508,9 @@ object Erasure {
507508
outer.path(toCls = tree.symbol)
508509
}
509510

510-
private def runtimeCallWithProtoArgs(name: Name, pt: Type, args: Tree*)(implicit ctx: Context): Tree = {
511+
private def runtimeCall(name: Name, args: List[Tree])(implicit ctx: Context): Tree =
511512
val meth = defn.runtimeMethodRef(name)
512-
val followingParams = meth.symbol.info.firstParamTypes.drop(args.length)
513-
val followingArgs = protoArgs(pt, meth.widen).zipWithConserve(followingParams)(typedExpr).asInstanceOf[List[tpd.Tree]]
514-
ref(meth).appliedToArgs(args.toList ++ followingArgs)
515-
}
516-
517-
private def protoArgs(pt: Type, methTp: Type): List[untpd.Tree] = (pt, methTp) match {
518-
case (pt: FunProto, methTp: MethodType) if methTp.isErasedMethod =>
519-
protoArgs(pt.resType, methTp.resType)
520-
case (pt: FunProto, methTp: MethodType) =>
521-
pt.args ++ protoArgs(pt.resType, methTp.resType)
522-
case _ => Nil
523-
}
513+
untpd.Apply(ref(meth), args.toList).withType(applyResultType(meth.widen.asInstanceOf[MethodType], args))
524514

525515
override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = {
526516
val ntree = interceptTypeApply(tree.asInstanceOf[TypeApply])(ctx.withPhase(ctx.erasurePhase)).withSpan(tree.span)
@@ -538,36 +528,51 @@ object Erasure {
538528
}
539529
}
540530

541-
/** Besides normal typing, this method collects all arguments
542-
* to a compacted function into a single argument of array type.
543-
*/
544-
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
531+
private def applyResultType(mt: MethodType, args: List[Tree], bunchArgs: Boolean = false)(using Context): Type =
532+
if bunchArgs || mt.paramNames.length <= args.length then
533+
mt.resultType
534+
else
535+
MethodType(mt.paramInfos.drop(args.length), mt.resultType)
536+
537+
/** Besides normal typing, this method does uncurrying and collects parameters
538+
* to anonymous functions of arity > 22.
539+
*
540+
*/
541+
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
545542
val Apply(fun, args) = tree
546543
if (fun.symbol == defn.cbnArg)
547544
typedUnadapted(args.head, pt)
548-
else typedExpr(fun, FunProto(args, pt)(this, isUsingApply = false)) match {
549-
case fun1: Apply => // arguments passed in prototype were already passed
550-
fun1
551-
case fun1 =>
552-
fun1.tpe.widen match {
553-
case mt: MethodType =>
554-
val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased
555-
val ownArgs = if (mt.paramNames.nonEmpty && !mt.isErasedMethod) args else Nil
556-
var args0 = outers ::: ownArgs ::: protoArgs(pt, tree.typeOpt)
557-
558-
if (args0.length > MaxImplementedFunctionArity && mt.paramInfos.length == 1) {
559-
val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType))
560-
.withType(defn.ArrayOf(defn.ObjectType))
561-
args0 = bunchedArgs :: Nil
562-
}
563-
assert(args0 hasSameLengthAs mt.paramInfos)
564-
val args1 = args0.zipWithConserve(mt.paramInfos)(typedExpr)
565-
untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType
566-
case _ =>
567-
throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}")
568-
}
569-
}
570-
}
545+
else
546+
val origFun = fun.asInstanceOf[tpd.Tree]
547+
val origFunType = origFun.tpe.widen(using preErasureCtx)
548+
val outers = outer.args(origFun)
549+
val ownArgs = if origFunType.isErasedMethod then Nil else args
550+
val args0 = outers ::: ownArgs
551+
val fun1 = typedExpr(fun, AnyFunctionProto)
552+
fun1.tpe.widen match
553+
case mt: MethodType =>
554+
val bunchArgs = mt.paramInfos match
555+
case JavaArrayType(elemType) :: Nil => // pre-test for efficiency
556+
elemType.isRef(defn.ObjectClass) // pre-test for efficiency
557+
&& origFunType.paramInfoss.flatten.length > MaxImplementedFunctionArity //
558+
case _ => false
559+
val args1 =
560+
if bunchArgs then args0.map(typedExpr(_, defn.ObjectType))
561+
else args0.zipWithConserve(mt.paramInfos)(typedExpr).asInstanceOf[List[Tree]]
562+
val (fun2, args2) = fun1 match
563+
case Apply(fun2, SeqLiteral(prevArgs, argTpt) :: _) if bunchArgs =>
564+
(fun2, JavaSeqLiteral(prevArgs ++ args1, argTpt) :: Nil)
565+
case Apply(fun2, prevArgs) =>
566+
(fun2, prevArgs ++ args1)
567+
case _ if bunchArgs =>
568+
(fun1, JavaSeqLiteral(args1, TypeTree(defn.ObjectType)) :: Nil)
569+
case _ =>
570+
(fun1, args1)
571+
untpd.cpy.Apply(tree)(fun2, args2).withType(applyResultType(mt, args1, bunchArgs))
572+
case t =>
573+
if args0.isEmpty then fun1
574+
else throw new MatchError(i"tree $tree has unexpected type of function $fun1: $t, was $origFunType, args = $args0")
575+
end typedApply
571576

572577
// The following four methods take as the proto-type the erasure of the pre-existing type,
573578
// if the original proto-type is not a value type.

0 commit comments

Comments
 (0)