From b88b958bb7f25ba45a6c15c9f554df2d4c6746ed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 17:06:29 +0200 Subject: Add varargs bridge methods Distinguish translations between Java and Scala varargs methods and add bridges where needed. --- src/dotty/tools/dotc/Compiler.scala | 4 +- src/dotty/tools/dotc/core/Types.scala | 9 +++++ src/dotty/tools/dotc/transform/ElimRepeated.scala | 45 +++++++++++++++++++++-- src/dotty/tools/dotc/transform/Erasure.scala | 18 ++++----- src/dotty/tools/dotc/transform/TreeGen.scala | 26 +++++++++++++ src/dotty/tools/dotc/transform/TypeUtils.scala | 20 ++++++++-- 6 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/TreeGen.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 95f74e290..28b340eb4 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -19,8 +19,10 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new Companions, new ElimRepeated /*, new ElimLocals*/), + List(new Companions), List(new SuperAccessors), + // pickling and refchecks goes here + List(new ElimRepeated/*, new ElimLocals*/), List(new ExtensionMethods), List(new TailRec), List(new PatternMatcher, diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a37b872d2..84c799129 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -158,6 +158,15 @@ object Types { def isRepeatedParam(implicit ctx: Context): Boolean = typeSymbol eq defn.RepeatedParamClass + /** Is this the type of a method that has a repeated parameter type as + * last parameter type? + */ + def isVarArgsMethod(implicit ctx: Context): Boolean = this match { + case tp: PolyType => tp.resultType.isVarArgsMethod + case MethodType(_, paramTypes) => paramTypes.nonEmpty && paramTypes.last.isRepeatedParam + case _ => false + } + /** Is this an alias TypeBounds? */ def isAlias: Boolean = this match { case TypeBounds(lo, hi) => lo eq hi diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index a362aee07..c78c012c3 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -32,10 +32,8 @@ class ElimRepeated extends TreeTransform with InfoTransformer { thisTransformer case tp @ MethodType(paramNames, paramTypes) => val resultType1 = elimRepeated(tp.resultType) val paramTypes1 = - if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { - paramTypes.init :+ - paramTypes.last.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) - } + if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) + paramTypes.init :+ paramTypes.last.underlyingIfRepeated(tp.isJava) else paramTypes tp.derivedMethodType(paramNames, paramTypes1, resultType1) case tp: PolyType => @@ -58,4 +56,43 @@ class ElimRepeated extends TreeTransform with InfoTransformer { thisTransformer override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = transformTypeOfTree(tree) + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + assert(ctx.phase == thisTransformer) + def overridesJava = { + val overridden = tree.symbol.allOverriddenSymbols + overridden.hasNext && overridden.forall(_ is JavaDefined) + } + if (tree.symbol.info.isVarArgsMethod && overridesJava) + addVarArgsBridge(tree)(ctx.withPhase(thisTransformer.next)) + else + tree + } + + /** add varargs bridge method + */ + private def addVarArgsBridge(ddef: DefDef)(implicit ctx: Context): Tree = { + val original = ddef.symbol.asTerm + val bridge = original.copy( + flags = ddef.symbol.flags &~ Private | Artifact, + info = toJavaVarArgs(ddef.symbol.info)).enteredAfter(thisTransformer).asTerm + val bridgeDef = polyDefDef(bridge, trefs => vrefss => { + val (vrefs :+ varArgRef) :: vrefss1 = vrefss + val elemtp = varArgRef.tpe.widen.argTypes.head + ref(original.termRef) + .appliedToTypes(trefs) + .appliedToArgs(vrefs :+ TreeGen.wrapArray(varArgRef, elemtp)) + .appliedToArgss(vrefss1) + }) + Thicket(ddef, bridgeDef) + } + + private def toJavaVarArgs(tp: Type)(implicit ctx: Context): Type = tp match { + case tp: PolyType => + tp.derivedPolyType(tp.paramNames, tp.paramBounds, toJavaVarArgs(tp.resultType)) + case tp: MethodType => + val inits :+ last = tp.paramTypes + val last1 = last.underlyingIfRepeated(isJava = true) + tp.derivedMethodType(tp.paramNames, inits :+ last1, tp.resultType) + } } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 35742ac8c..1cc00bdee 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -23,6 +23,7 @@ import ast.Trees._ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Flags import ValueClasses._ +import TypeUtils._ class Erasure extends Phase with DenotTransformer { @@ -83,7 +84,6 @@ object Erasure { } def isErasedValueType(tpe: Type)(implicit ctx: Context): Boolean = tpe.isInstanceOf[ErasedValueType] - def isPrimitiveValueType(tpe: Type)(implicit ctx: Context): Boolean = tpe.classSymbol.isPrimitiveValueClass def constant(tree: Tree, const: Tree)(implicit ctx: Context) = if (isPureExpr(tree)) Block(tree :: Nil, const) else const @@ -112,7 +112,7 @@ object Erasure { pt match { case ErasedValueType(clazz, underlying) => val tree1 = - if ((tree.tpe isRef defn.NullClass) && isPrimitiveValueType(underlying)) + if ((tree.tpe isRef defn.NullClass) && underlying.isPrimitiveValueType) // convert `null` directly to underlying type, as going // via the unboxed type would yield a NPE (see SI-5866) unbox(tree, underlying) @@ -137,7 +137,7 @@ object Erasure { if (pt isRef defn.UnitClass) unbox(tree, pt) else (tree.tpe, pt) match { case (defn.ArrayType(treeElem), defn.ArrayType(ptElem)) - if isPrimitiveValueType(treeElem.widen) && !isPrimitiveValueType(ptElem) => + if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType => // See SI-2386 for one example of when this might be necessary. cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt) case _ => @@ -157,13 +157,13 @@ object Erasure { def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = if (tree.tpe <:< pt) tree - else if (isErasedValueType(tree.tpe.widen)) + else if (tree.tpe.widen.isErasedValueType) adaptToType(box(tree), pt) - else if (isErasedValueType(pt)) + else if (pt.isErasedValueType) adaptToType(unbox(tree, pt), pt) - else if (isPrimitiveValueType(tree.tpe.widen) && !isPrimitiveValueType(pt)) + else if (tree.tpe.widen.isPrimitiveValueType && !pt.isPrimitiveValueType) adaptToType(box(tree), pt) - else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe.widen)) + else if (pt.isPrimitiveValueType && !tree.tpe.widen.isPrimitiveValueType) adaptToType(unbox(tree, pt), pt) else cast(tree, pt) @@ -211,11 +211,11 @@ object Erasure { else recur(cast(qual, erasedPre)) def recur(qual: Tree): Tree = { - val qualIsPrimitive = isPrimitiveValueType(qual.tpe.widen) + val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType val symIsPrimitive = sym.owner.isPrimitiveValueClass if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass)) select(qual, defn.ObjectClass.info.decl(sym.name).symbol) - else if (qualIsPrimitive && !symIsPrimitive || isErasedValueType(qual.tpe)) + else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.isErasedValueType) recur(box(qual)) else if (!qualIsPrimitive && symIsPrimitive) recur(unbox(qual, sym.owner.typeRef)) diff --git a/src/dotty/tools/dotc/transform/TreeGen.scala b/src/dotty/tools/dotc/transform/TreeGen.scala new file mode 100644 index 000000000..7997aa308 --- /dev/null +++ b/src/dotty/tools/dotc/transform/TreeGen.scala @@ -0,0 +1,26 @@ +package dotty.tools.dotc +package transform + +import core._ +import Symbols._, Contexts._, Types._, Names._, StdNames._ +import ast._ +import Trees._ +import TypeUtils._ + +object TreeGen { + + import tpd._ + + def wrapArrayMethodName(elemtp: Type)(implicit ctx: Context): TermName = { + val elemCls = elemtp.classSymbol + if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name) + else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isPhantomClass) nme.wrapRefArray + else nme.genericWrapArray + } + + def wrapArray(tree: Tree, elemtp: Type)(implicit ctx: Context): Tree = + ref(defn.ScalaPredefModule) + .select(wrapArrayMethodName(elemtp)) + .appliedToTypes(if (elemtp.isPrimitiveValueType) Nil else elemtp :: Nil) + .appliedTo(tree) +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index a26660092..d07930661 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -1,9 +1,15 @@ package dotty.tools.dotc package transform -import dotty.tools.dotc.core.Types._ - -import scala.language.implicitConversions +import core._ +import core.transform.Erasure.ErasedValueType +import Types._ +import Contexts._ +import Symbols._ +import Decorators._ +import StdNames.nme +import NameOps._ +import language.implicitConversions object TypeUtils { implicit def decorateTypeUtils(tpe: Type): TypeUtils = new TypeUtils(tpe) @@ -15,4 +21,10 @@ object TypeUtils { */ class TypeUtils(val self: Type) extends AnyVal { -} \ No newline at end of file + def isErasedValueType(implicit ctx: Context): Boolean = + self.isInstanceOf[ErasedValueType] + + def isPrimitiveValueType(implicit ctx: Context): Boolean = + self.classSymbol.isPrimitiveValueClass + + } -- cgit v1.2.3