Skip to content

Commit 0938fe5

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 c12ed63 commit 0938fe5

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
@@ -163,6 +163,11 @@ class TreeUnpickler(reader: TastyReader,
163163
def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr))
164164
def fork: TreeReader = forkAt(currentAddr)
165165

166+
def skipParentTree(tag: Int): Unit = {
167+
if tag == SPLITCLAUSE then ()
168+
else skipTree(tag)
169+
}
170+
def skipParentTree(): Unit = skipParentTree(readByte())
166171
def skipTree(tag: Int): Unit = {
167172
if (tag >= firstLengthTreeTag) goto(readEnd())
168173
else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() }
@@ -1011,7 +1016,7 @@ class TreeUnpickler(reader: TastyReader,
10111016
* parsed in this way as InferredTypeTrees.
10121017
*/
10131018
def readParents(withArgs: Boolean)(using Context): List[Tree] =
1014-
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
1019+
collectWhile({val tag = nextByte; tag != SELFDEF && tag != DEFDEF && tag != SPLITCLAUSE}) {
10151020
nextUnsharedTag match
10161021
case APPLY | TYPEAPPLY | BLOCK =>
10171022
if withArgs then readTree()
@@ -1038,7 +1043,8 @@ class TreeUnpickler(reader: TastyReader,
10381043
val bodyFlags = {
10391044
val bodyIndexer = fork
10401045
// The first DEFDEF corresponds to the primary constructor
1041-
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree()
1046+
while ({val tag = bodyIndexer.reader.nextByte; tag != DEFDEF && tag != SPLITCLAUSE}) do
1047+
bodyIndexer.skipParentTree()
10421048
bodyIndexer.indexStats(end)
10431049
}
10441050
val parentReader = fork
@@ -1057,7 +1063,38 @@ class TreeUnpickler(reader: TastyReader,
10571063
cls.owner.thisType, cls, parentTypes, cls.unforcedDecls,
10581064
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe
10591065
).integrateOpaqueMembers
1060-
val constr = readIndexedDef().asInstanceOf[DefDef]
1066+
1067+
val (constr, stats0) =
1068+
if nextByte == SPLITCLAUSE then
1069+
assert(unpicklingJava, s"unexpected SPLITCLAUSE at $start")
1070+
val tag = readByte()
1071+
def ta = ctx.typeAssigner
1072+
val flags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible
1073+
val pflags = Flags.JavaDefined | Flags.Param
1074+
val tdefRefs = tparams.map(_.symbol.asType)
1075+
val ctorCompleter = new LazyType {
1076+
def complete(denot: SymDenotation)(using Context) =
1077+
val sym = denot.symbol
1078+
lazy val tparamSyms: List[TypeSymbol] = tparams.map: tdef =>
1079+
val completer = new LazyType {
1080+
def complete(denot: SymDenotation)(using Context) =
1081+
denot.info = tdef.symbol.asType.info.subst(tdefRefs, tparamSyms.map(_.typeRef))
1082+
}
1083+
newSymbol(sym, tdef.name, pflags, completer, coord = cls.coord)
1084+
val paramSym =
1085+
newSymbol(sym, nme.syntheticParamName(1), pflags, defn.UnitType, coord = cls.coord)
1086+
val paramSymss = tparamSyms :: List(paramSym) :: Nil
1087+
val res = effectiveResultType(sym, paramSymss)
1088+
denot.info = methodType(paramSymss, res)
1089+
denot.setParamss(paramSymss)
1090+
}
1091+
val ctorSym = newSymbol(ctx.owner, nme.CONSTRUCTOR, flags, ctorCompleter, coord = coordAt(start))
1092+
val accSym = newSymbol(cls, nme.syntheticParamName(1), flags, defn.UnitType, coord = ctorSym.coord)
1093+
val ctorDef = tpd.DefDef(ctorSym, EmptyTree)
1094+
val accessor = tpd.ValDef(accSym, ElidedTree(accSym.info))
1095+
(ctorDef.setDefTree, accessor.setDefTree :: Nil)
1096+
else
1097+
readIndexedDef().asInstanceOf[DefDef] -> Nil
10611098
val mappedParents: LazyTreeList =
10621099
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
10631100
// parents were not read fully, will need to be read again later on demand
@@ -1068,7 +1105,7 @@ class TreeUnpickler(reader: TastyReader,
10681105

10691106
val lazyStats = readLater(end, rdr => {
10701107
val stats = rdr.readIndexedStats(localDummy, end)
1071-
tparams ++ vparams ++ stats
1108+
tparams ++ vparams ++ stats0 ++ stats
10721109
})
10731110
defn.patchStdLibClass(cls)
10741111
NamerOps.addConstructorProxies(cls)
@@ -1178,6 +1215,9 @@ class TreeUnpickler(reader: TastyReader,
11781215

11791216
// ------ Reading trees -----------------------------------------------------
11801217

1218+
private def ElidedTree(tpe: Type)(using Context): Tree =
1219+
untpd.Ident(nme.WILDCARD).withType(tpe)
1220+
11811221
def readTree()(using Context): Tree = {
11821222
val sctx = sourceChangeContext()
11831223
if (sctx `ne` ctx) return readTree()(using sctx)
@@ -1234,7 +1274,7 @@ class TreeUnpickler(reader: TastyReader,
12341274
val msg =
12351275
s"Illegal elided tree in unpickler at $start without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}"
12361276
report.error(msg)
1237-
untpd.Ident(nme.WILDCARD).withType(readType())
1277+
ElidedTree(readType())
12381278
case IDENTtpt =>
12391279
untpd.Ident(readName().toTypeName).withType(readType())
12401280
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)