Skip to content

Commit 0d0cdef

Browse files
committed
Parse varargs in a more direct manner
In bytecode, vararg parameters are encoded as arrays with the ACC_VARARGS flag set on the method. Previously we first parsed them as arrays then translated the array into a repeated param. With this commit we directly parse them as array, this isn't really any simpler since we need to keep track of the fact that we're in a varargs method, but it will become useful in the next commit where we start treating arrays differently from varargs (`T[]` will still be translated as `Array[T & Object]` whereas `T...` will be translated as `T*` and handled specially in ElimRepeated to preserve soundness). Similarly for joint compilation, we now directly desugar varargs as `RepeatedParam[T]` instead of `Array[T] @Repeated`, this doesn't change anything currently because: - When giving a type to the method, we call `annotatedToRepeated` on each parameter type - `typedAppliedTypeTree` takes care of adding `& Object` for both `Array[T]` and `RepeatedParam[T]` coming From Java (this is what the next commit will change).
1 parent fd18546 commit 0d0cdef

File tree

3 files changed

+46
-33
lines changed

3 files changed

+46
-33
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,10 +1684,12 @@ object desugar {
16841684
Apply(Select(Apply(scalaDot(nme.StringContext), strs), id).withSpan(tree.span), elems)
16851685
case PostfixOp(t, op) =>
16861686
if ((ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR) {
1687-
val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType
1688-
Annotated(
1689-
AppliedTypeTree(ref(seqType), t),
1690-
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
1687+
if ctx.compilationUnit.isJava then
1688+
AppliedTypeTree(ref(defn.RepeatedParamType), t)
1689+
else
1690+
Annotated(
1691+
AppliedTypeTree(ref(defn.SeqType), t),
1692+
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
16911693
}
16921694
else {
16931695
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -280,15 +280,13 @@ class ClassfileParser(
280280
addConstructorTypeParams(denot)
281281
}
282282

283-
denot.info = pool.getType(in.nextChar)
283+
val isVarargs = denot.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0
284+
denot.info = pool.getType(in.nextChar, isVarargs)
284285
if (isEnum) denot.info = ConstantType(Constant(sym))
285286
if (isConstructor) normalizeConstructorParams()
286-
denot.info = translateTempPoly(parseAttributes(sym, denot.info))
287+
denot.info = translateTempPoly(parseAttributes(sym, denot.info, isVarargs))
287288
if (isConstructor) normalizeConstructorInfo()
288289

289-
if (denot.is(Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0)
290-
denot.info = arrayToRepeated(denot.info)
291-
292290
if (ctx.explicitNulls) denot.info = JavaNullInterop.nullifyMember(denot.symbol, denot.info, isEnum)
293291

294292
// seal java enums
@@ -324,7 +322,7 @@ class ClassfileParser(
324322
case BOOL_TAG => defn.BooleanType
325323
}
326324

327-
private def sigToType(sig: SimpleName, owner: Symbol = null)(using Context): Type = {
325+
private def sigToType(sig: SimpleName, owner: Symbol = null, isVarargs: Boolean = false)(using Context): Type = {
328326
var index = 0
329327
val end = sig.length
330328
def accept(ch: Char): Unit = {
@@ -395,13 +393,41 @@ class ClassfileParser(
395393
val elemtp = sig2type(tparams, skiptvs)
396394
defn.ArrayOf(elemtp.translateJavaArrayElementType)
397395
case '(' =>
398-
// we need a method symbol. given in line 486 by calling getType(methodSym, ..)
396+
def isMethodEnd(i: Int) = sig(i) == ')'
397+
def isArray(i: Int) = sig(i) == '['
398+
399+
/** Is this a repeated parameter type?
400+
* This is true if we're in a vararg method and this is the last parameter.
401+
*/
402+
def isRepeatedParam(i: Int): Boolean =
403+
if !isVarargs then return false
404+
var cur = i
405+
// Repeated parameters are represented as arrays
406+
if !isArray(cur) then return false
407+
// Handle nested arrays: int[]...
408+
while isArray(cur) do
409+
cur += 1
410+
// Simple check to see if we're the last parameter: there should be no
411+
// array in the signature until the method end.
412+
while !isMethodEnd(cur) do
413+
if isArray(cur) then return false
414+
cur += 1
415+
true
416+
end isRepeatedParam
417+
399418
val paramtypes = new ListBuffer[Type]()
400419
var paramnames = new ListBuffer[TermName]()
401-
while (sig(index) != ')') {
420+
while !isMethodEnd(index) do
402421
paramnames += nme.syntheticParamName(paramtypes.length)
403-
paramtypes += objToAny(sig2type(tparams, skiptvs))
404-
}
422+
paramtypes += {
423+
if isRepeatedParam(index) then
424+
index += 1
425+
val elemType = sig2type(tparams, skiptvs)
426+
defn.RepeatedParamType.appliedTo(elemType.translateJavaArrayElementType)
427+
else
428+
objToAny(sig2type(tparams, skiptvs))
429+
}
430+
405431
index += 1
406432
val restype = sig2type(tparams, skiptvs)
407433
JavaMethodType(paramnames.toList, paramtypes.toList, restype)
@@ -574,7 +600,7 @@ class ClassfileParser(
574600
None // ignore malformed annotations
575601
}
576602

577-
def parseAttributes(sym: Symbol, symtype: Type)(using Context): Type = {
603+
def parseAttributes(sym: Symbol, symtype: Type, isVarargs: Boolean = false)(using Context): Type = {
578604
var newType = symtype
579605

580606
def parseAttribute(): Unit = {
@@ -584,7 +610,7 @@ class ClassfileParser(
584610
attrName match {
585611
case tpnme.SignatureATTR =>
586612
val sig = pool.getExternalName(in.nextChar)
587-
newType = sigToType(sig, sym)
613+
newType = sigToType(sig, sym, isVarargs)
588614
if (ctx.debug && ctx.verbose)
589615
println("" + sym + "; signature = " + sig + " type = " + newType)
590616
case tpnme.SyntheticATTR =>
@@ -1103,8 +1129,8 @@ class ClassfileParser(
11031129
c
11041130
}
11051131

1106-
def getType(index: Int)(using Context): Type =
1107-
sigToType(getExternalName(index))
1132+
def getType(index: Int, isVarargs: Boolean = false)(using Context): Type =
1133+
sigToType(getExternalName(index), isVarargs = isVarargs)
11081134

11091135
def getSuperClass(index: Int)(using Context): Symbol = {
11101136
assert(index != 0, "attempt to parse java.lang.Object from classfile")

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,6 @@ object Scala2Unpickler {
5959
denot.info = PolyType.fromParams(denot.owner.typeParams, denot.info)
6060
}
6161

62-
/** Convert array parameters denoting a repeated parameter of a Java method
63-
* to `RepeatedParamClass` types.
64-
*/
65-
def arrayToRepeated(tp: Type)(using Context): Type = tp match {
66-
case tp: MethodType =>
67-
val lastArg = tp.paramInfos.last
68-
assert(lastArg isRef defn.ArrayClass)
69-
tp.derivedLambdaType(
70-
tp.paramNames,
71-
tp.paramInfos.init :+ lastArg.translateParameterized(defn.ArrayClass, defn.RepeatedParamClass),
72-
tp.resultType)
73-
case tp: PolyType =>
74-
tp.derivedLambdaType(tp.paramNames, tp.paramInfos, arrayToRepeated(tp.resultType))
75-
}
76-
7762
def ensureConstructor(cls: ClassSymbol, scope: Scope)(using Context): Unit = {
7863
if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) {
7964
val constr = newDefaultConstructor(cls)

0 commit comments

Comments
 (0)