Skip to content

Commit d313143

Browse files
committed
SI-7455 Drop dummy param for synthetic access constructor
Adapted from scalac commit 050b4c951c838699c2fe30cbf01b63942c63a299 by Jason Zaugg: Java synthesizes public constructors in private classes to allow access from inner classes. The signature of that synthetic constructor (known as a "access constructor") has a dummy parameter appended to avoid overloading clashes. javac chooses the type "Enclosing$1" for the dummy parameter (called the "access constructor tag") which is either an existing anonymous class or a synthesized class for this purpose. In OpenJDK, this transformation is performed in: langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java (Incidentally, scalac would just emits a byte-code public constructor in this situation, rather than a private constructor / access constructor pair.) Scala parses the signature of the access contructor, and drops the $outer parameter, but retains the dummy parameter. This causes havoc when it tries to parse the bytecode for that anonymous class; the class file parser doesn't have the enclosing type parameters of Vector in scope and crash ensues. In any case, we shouldn't allow user code to see that constructor; it should only be called from within its own compilation unit. This commit drops the dummy parameter from access constructor signatures in class file parsing.
1 parent 5a1bc13 commit d313143

File tree

1 file changed

+13
-5
lines changed

1 file changed

+13
-5
lines changed

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,21 @@ class ClassfileParser(
194194
val name = pool.getName(in.nextChar)
195195
val isConstructor = name eq nme.CONSTRUCTOR
196196

197-
/** Strip leading outer param from constructor.
198-
* Todo: Also strip trailing access tag for private inner constructors?
197+
/** Strip leading outer param from constructor and trailing access tag for
198+
* private inner constructors.
199199
*/
200-
def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match {
200+
def normalizeConstructorParams() = innerClasses.get(currentClassName) match {
201201
case Some(entry) if !isStatic(entry.jflags) =>
202202
val mt @ MethodTpe(paramNames, paramTypes, resultType) = denot.info
203-
denot.info = mt.derivedLambdaType(paramNames.tail, paramTypes.tail, resultType)
203+
var normalizedParamNames = paramNames.tail
204+
var normalizedParamTypes = paramTypes.tail
205+
if ((jflags & JAVA_ACC_SYNTHETIC) != 0) {
206+
// SI-7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which
207+
// are added when an inner class needs to access a private constructor.
208+
normalizedParamNames = paramNames.dropRight(1)
209+
normalizedParamTypes = paramTypes.dropRight(1)
210+
}
211+
denot.info = mt.derivedLambdaType(normalizedParamNames, normalizedParamTypes, resultType)
204212
case _ =>
205213
}
206214

@@ -216,7 +224,7 @@ class ClassfileParser(
216224

217225
denot.info = pool.getType(in.nextChar)
218226
if (isEnum) denot.info = ConstantType(Constant(sym))
219-
if (isConstructor) stripOuterParamFromConstructor()
227+
if (isConstructor) normalizeConstructorParams()
220228
setPrivateWithin(denot, jflags)
221229
denot.info = translateTempPoly(parseAttributes(sym, denot.info))
222230
if (isConstructor) normalizeConstructorInfo()

0 commit comments

Comments
 (0)