Skip to content

Commit 1e5b8fa

Browse files
committed
elide fake java primary constructor
introduce SPLITCLAUSE at the end of the template parents, if present, then there is no primary constructor. We assert that we have the JAVAattr and reconstruct the fake primary constructor to satisfy the compiler.
1 parent 1e2ee33 commit 1e5b8fa

File tree

6 files changed

+70
-27
lines changed

6 files changed

+70
-27
lines changed

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
361361
else
362362
throw ex
363363
if sym.is(Method) && sym.owner.isClass then
364-
profile.recordMethodSize(sym, currentAddr.index - addr.index, mdef.span)
364+
profile.recordMethodSize(sym, (currentAddr.index - addr.index) max 1, mdef.span)
365365
for docCtx <- ctx.docCtx do
366366
val comment = docCtx.docstrings.lookup(sym)
367367
if comment != null then
@@ -617,7 +617,17 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
617617
}
618618
}
619619
}
620-
pickleStats(tree.constr :: rest)
620+
if isJavaPickle then
621+
val rest0 = rest.dropWhile:
622+
case stat: ValOrDefDef => stat.symbol.is(Flags.Invisible)
623+
case _ => false
624+
if tree.constr.symbol.is(Flags.Invisible) then
625+
writeByte(SPLITCLAUSE)
626+
pickleStats(rest0)
627+
else
628+
pickleStats(tree.constr :: rest0)
629+
else
630+
pickleStats(tree.constr :: rest)
621631
}
622632
case Import(expr, selectors) =>
623633
writeByte(IMPORT)

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ class TreeUnpickler(reader: TastyReader,
168168
def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr))
169169
def fork: TreeReader = forkAt(currentAddr)
170170

171+
def skipParentTree(tag: Int): Unit = {
172+
if tag == SPLITCLAUSE then ()
173+
else skipTree(tag)
174+
}
175+
def skipParentTree(): Unit = skipParentTree(readByte())
171176
def skipTree(tag: Int): Unit = {
172177
if (tag >= firstLengthTreeTag) goto(readEnd())
173178
else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() }
@@ -1016,7 +1021,7 @@ class TreeUnpickler(reader: TastyReader,
10161021
* parsed in this way as InferredTypeTrees.
10171022
*/
10181023
def readParents(withArgs: Boolean)(using Context): List[Tree] =
1019-
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
1024+
collectWhile({val tag = nextByte; tag != SELFDEF && tag != DEFDEF && tag != SPLITCLAUSE}) {
10201025
nextUnsharedTag match
10211026
case APPLY | TYPEAPPLY | BLOCK =>
10221027
if withArgs then readTree()
@@ -1043,7 +1048,8 @@ class TreeUnpickler(reader: TastyReader,
10431048
val bodyFlags = {
10441049
val bodyIndexer = fork
10451050
// The first DEFDEF corresponds to the primary constructor
1046-
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree()
1051+
while ({val tag = bodyIndexer.reader.nextByte; tag != DEFDEF && tag != SPLITCLAUSE}) do
1052+
bodyIndexer.skipParentTree()
10471053
bodyIndexer.indexStats(end)
10481054
}
10491055
val parentReader = fork
@@ -1062,7 +1068,38 @@ class TreeUnpickler(reader: TastyReader,
10621068
cls.owner.thisType, cls, parentTypes, cls.unforcedDecls,
10631069
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe
10641070
).integrateOpaqueMembers
1065-
val constr = readIndexedDef().asInstanceOf[DefDef]
1071+
1072+
val (constr, stats0) =
1073+
if nextByte == SPLITCLAUSE then
1074+
assert(unpicklingJava, s"unexpected SPLITCLAUSE at $start")
1075+
val tag = readByte()
1076+
def ta = ctx.typeAssigner
1077+
val flags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible
1078+
val pflags = Flags.JavaDefined | Flags.Param
1079+
val tdefRefs = tparams.map(_.symbol.asType)
1080+
val ctorCompleter = new LazyType {
1081+
def complete(denot: SymDenotation)(using Context) =
1082+
val sym = denot.symbol
1083+
lazy val tparamSyms: List[TypeSymbol] = tparams.map: tdef =>
1084+
val completer = new LazyType {
1085+
def complete(denot: SymDenotation)(using Context) =
1086+
denot.info = tdef.symbol.asType.info.subst(tdefRefs, tparamSyms.map(_.typeRef))
1087+
}
1088+
newSymbol(sym, tdef.name, pflags, completer, coord = cls.coord)
1089+
val paramSym =
1090+
newSymbol(sym, nme.syntheticParamName(1), pflags, defn.UnitType, coord = cls.coord)
1091+
val paramSymss = tparamSyms :: List(paramSym) :: Nil
1092+
val res = effectiveResultType(sym, paramSymss)
1093+
denot.info = methodType(paramSymss, res)
1094+
denot.setParamss(paramSymss)
1095+
}
1096+
val ctorSym = newSymbol(ctx.owner, nme.CONSTRUCTOR, flags, ctorCompleter, coord = coordAt(start))
1097+
val accSym = newSymbol(cls, nme.syntheticParamName(1), flags, defn.UnitType, coord = ctorSym.coord)
1098+
val ctorDef = tpd.DefDef(ctorSym, EmptyTree)
1099+
val accessor = tpd.ValDef(accSym, ElidedTree(accSym.info))
1100+
(ctorDef.setDefTree, accessor.setDefTree :: Nil)
1101+
else
1102+
readIndexedDef().asInstanceOf[DefDef] -> Nil
10661103
val mappedParents: LazyTreeList =
10671104
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
10681105
// parents were not read fully, will need to be read again later on demand
@@ -1073,7 +1110,7 @@ class TreeUnpickler(reader: TastyReader,
10731110

10741111
val lazyStats = readLater(end, rdr => {
10751112
val stats = rdr.readIndexedStats(localDummy, end)
1076-
tparams ++ vparams ++ stats
1113+
tparams ++ vparams ++ stats0 ++ stats
10771114
})
10781115
defn.patchStdLibClass(cls)
10791116
NamerOps.addConstructorProxies(cls)
@@ -1183,6 +1220,9 @@ class TreeUnpickler(reader: TastyReader,
11831220

11841221
// ------ Reading trees -----------------------------------------------------
11851222

1223+
private def ElidedTree(tpe: Type)(using Context): Tree =
1224+
untpd.Ident(nme.WILDCARD).withType(tpe)
1225+
11861226
def readTree()(using Context): Tree = {
11871227
val sctx = sourceChangeContext()
11881228
if (sctx `ne` ctx) return readTree()(using sctx)
@@ -1239,7 +1279,7 @@ class TreeUnpickler(reader: TastyReader,
12391279
val msg =
12401280
s"Illegal elided tree in unpickler at $start without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}"
12411281
report.error(msg)
1242-
untpd.Ident(nme.WILDCARD).withType(readType())
1282+
ElidedTree(readType())
12431283
case IDENTtpt =>
12441284
untpd.Ident(readName().toTypeName).withType(readType())
12451285
case SELECT =>

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ object JavaParsers {
120120
// can call it.
121121
// This also avoids clashes between the constructor parameter names and member names.
122122
if (needsDummyConstr) {
123+
val fakeFlags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible
123124
if (constr1 == EmptyTree) constr1 = makeConstructor(List(), Nil, Parsers.unimplementedExpr)
124125
stats1 = constr1 :: stats1
125-
constr1 =
126-
makeConstructor(List(scalaDot(tpnme.Unit)), tparams, EmptyTree, Flags.JavaDefined | Flags.PrivateLocal)
126+
constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, EmptyTree, fakeFlags)
127127
}
128128
else if (constr1 == EmptyTree) {
129129
constr1 = makeConstructor(List(), tparams, EmptyTree)

compiler/src/dotty/tools/dotc/printing/OutlinePrinter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ object OutlinePrinter:
1717

1818
/** A printer that elides known standard tree forms from the rhs of def and val.
1919
* Typically used for printing Java trees which elide the rhs.
20+
* Note that there may still be some differences if you compare before and after pickling.
2021
*/
2122
class OutlinePrinter private (_ctx: Context) extends RefinedPrinter(_ctx) {
2223

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

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,6 @@ class Pickler extends Phase {
8787
Pickler.ParallelPickling && !ctx.settings.YtestPickler.value &&
8888
!ctx.settings.YjavaTasty.value // disable parallel pickling when `-Yjava-tasty` is set (internal testing only)
8989

90-
private def adjustPrinter(ictx: Context, isOutline: Boolean): Context =
91-
if isOutline then
92-
// use special printer because Java parser will use `Predef.???` as rhs,
93-
// which conflicts with the unpickling of ELIDED as `Ident(nme.WILDCARD).withType(tpe)`
94-
// In the future we could modify the typer/parser to elide the rhs in the same way.
95-
ictx.fresh.setPrinterFn(OutlinePrinter(_))
96-
else
97-
ictx
98-
9990
override def run(using Context): Unit = {
10091
val unit = ctx.compilationUnit
10192
pickling.println(i"unpickling in run ${ctx.runId}")
@@ -104,8 +95,7 @@ class Pickler extends Phase {
10495
cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree))
10596
tree <- sliceTopLevel(unit.tpdTree, cls)
10697
do
107-
if ctx.settings.YtestPickler.value then
108-
beforePickling(cls) = tree.show(using adjustPrinter(ctx, unit.typedAsJava))
98+
if ctx.settings.YtestPickler.value then beforePickling(cls) = tree.show
10999

110100
val sourceRelativePath =
111101
val reference = ctx.settings.sourceroot.value
@@ -256,12 +246,13 @@ class Pickler extends Phase {
256246
if unit.typedAsJava then
257247
if unpickler.unpickler.nameAtRef.contents.exists(_ == nme.FromJavaObject) then
258248
report.error(em"Pickled reference to FromJavaObject in Java defined $cls in ${cls.source}")
259-
val unpickled = unpickler.rootTrees
260-
val freshUnit = CompilationUnit(rootCtx.compilationUnit.source)
261-
freshUnit.needsCaptureChecking = unit.needsCaptureChecking
262-
freshUnit.knowsPureFuns = unit.knowsPureFuns
263-
inContext(adjustPrinter(rootCtx.fresh.setCompilationUnit(freshUnit), unit.typedAsJava)):
264-
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
249+
else
250+
val unpickled = unpickler.rootTrees
251+
val freshUnit = CompilationUnit(rootCtx.compilationUnit.source)
252+
freshUnit.needsCaptureChecking = unit.needsCaptureChecking
253+
freshUnit.knowsPureFuns = unit.knowsPureFuns
254+
inContext(rootCtx.fresh.setCompilationUnit(freshUnit)):
255+
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
265256

266257
private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(using Context) =
267258
import java.nio.charset.StandardCharsets.UTF_8

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ Standard-Section: "ASTs" TopLevelStat*
8181
Param = TypeParam
8282
TermParam
8383
Template = TEMPLATE Length TypeParam* TermParam* parent_Term* Self?
84-
Stat* -- [typeparams] paramss extends parents { self => stats }, where Stat* always starts with the primary constructor.
84+
EndParents? Stat* -- [typeparams] paramss extends parents { self => stats }, where Stat* always starts with the primary constructor.
8585
Self = SELFDEF selfName_NameRef selfType_Term -- selfName : selfType
86+
EndParents = SPLITCLAUSE -- explicitly end the template header, e.g. if there is no primary constructor
8687
8788
Term = Path -- Paths represent both types and terms
8889
IDENT NameRef Type -- Used when term ident’s type is not a TermRef

0 commit comments

Comments
 (0)