Skip to content

Commit 1502307

Browse files
committed
Support the -Ypickle-java flag.
- Add JAVAattr and OUTLINEattr TASTy attributes, ELIDED tree tag. ELIDED trees are pickled as rhs of java term definitions. ELIDED trees can only be unpickled if OUTLINEattr is present. For now OUTLINEattr implies JAVAattr. In the future we might expand OUTLINEattr to include outline Scala typing. - Keep java mode compilation units up to ExtractAPI phase if pipeline.
1 parent 9ea5ff0 commit 1502307

File tree

55 files changed

+423
-31
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+423
-31
lines changed

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class CompilationUnit protected (val source: SourceFile) {
3131
/** Is this the compilation unit of a Java file */
3232
def isJava: Boolean = source.file.name.endsWith(".java")
3333

34+
/** Is this the compilation unit of a Java file, or TASTy derived from a Java file */
35+
def typedAsJava = isJava || tastyAttributes.exists(_.isJava)
36+
37+
var tastyAttributes: Option[tasty.Attributes] = None
38+
3439
/** The source version for this unit, as determined by a language import */
3540
var sourceVersion: Option[SourceVersion] = None
3641

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import reporting.*
1717
import annotation.constructorOnly
1818
import printing.Formatting.hl
1919
import config.Printers
20+
import parsing.Parsers
2021

2122
import scala.annotation.internal.sharable
2223
import scala.annotation.threadUnsafe
@@ -143,8 +144,13 @@ object desugar {
143144

144145
/** A value definition copied from `vdef` with a tpt typetree derived from it */
145146
def derivedTermParam(vdef: ValDef)(using Context): ValDef =
147+
derivedTermParam(vdef, vdef.unforcedRhs)
148+
149+
def derivedTermParam(vdef: ValDef, rhs: LazyTree)(using Context): ValDef =
146150
cpy.ValDef(vdef)(
147-
tpt = DerivedFromParamTree().withSpan(vdef.tpt.span).watching(vdef))
151+
tpt = DerivedFromParamTree().withSpan(vdef.tpt.span).watching(vdef),
152+
rhs = rhs
153+
)
148154

149155
// ----- Desugar methods -------------------------------------------------
150156

@@ -544,8 +550,11 @@ object desugar {
544550
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
545551
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
546552
val derivedVparamss =
547-
constrVparamss.nestedMap(vparam =>
548-
derivedTermParam(vparam).withAnnotations(Nil))
553+
constrVparamss.nestedMap: vparam =>
554+
val derived =
555+
if ctx.compilationUnit.isJava then derivedTermParam(vparam, Parsers.unimplementedExpr)
556+
else derivedTermParam(vparam)
557+
derived.withAnnotations(Nil)
549558

550559
val constr = cpy.DefDef(constr1)(paramss = joinParams(constrTparams, constrVparamss))
551560

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,6 @@ private sealed trait YSettings:
436436
val YdebugMacros: Setting[Boolean] = BooleanSetting("-Ydebug-macros", "Show debug info when quote pattern match fails")
437437

438438
// Pipeline compilation options
439-
val YpickleJava: Setting[Boolean] = BooleanSetting("-Ypickle-java", "Pickler phase should compute pickles for .java defined symbols for use by build tools")
439+
val YpickleJava: Setting[Boolean] = BooleanSetting("-Ypickle-java", "Pickler phase should compute pickles for .java defined symbols for use by build tools", aliases = List("-Yjava-tasty"))
440440
val YpickleWrite: Setting[AbstractFile] = OutputSetting("-Ypickle-write", "directory|jar", "destination for generated .sig files containing type signatures.", NoAbstractFile, aliases = List("-Yearly-tasty"))
441441
end YSettings

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ class Definitions {
10591059
@tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName")
10601060

10611061
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")
1062+
@tu lazy val JavaAnnotationAnnot: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
10621063

10631064
// Initialization annotations
10641065
@tu lazy val InitModule: Symbol = requiredModule("scala.annotation.init")

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ object Phases {
211211
private var myPostTyperPhase: Phase = uninitialized
212212
private var mySbtExtractDependenciesPhase: Phase = uninitialized
213213
private var myPicklerPhase: Phase = uninitialized
214+
private var mySbtExtractApiPhase: Phase = uninitialized
214215
private var myInliningPhase: Phase = uninitialized
215216
private var myStagingPhase: Phase = uninitialized
216217
private var mySplicingPhase: Phase = uninitialized
@@ -236,6 +237,7 @@ object Phases {
236237
final def postTyperPhase: Phase = myPostTyperPhase
237238
final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase
238239
final def picklerPhase: Phase = myPicklerPhase
240+
final def sbtExtractApiPhase: Phase = mySbtExtractApiPhase
239241
final def inliningPhase: Phase = myInliningPhase
240242
final def stagingPhase: Phase = myStagingPhase
241243
final def splicingPhase: Phase = mySplicingPhase
@@ -264,6 +266,7 @@ object Phases {
264266
myPostTyperPhase = phaseOfClass(classOf[PostTyper])
265267
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
266268
myPicklerPhase = phaseOfClass(classOf[Pickler])
269+
mySbtExtractApiPhase = phaseOfClass(classOf[sbt.ExtractAPI])
267270
myInliningPhase = phaseOfClass(classOf[Inlining])
268271
myStagingPhase = phaseOfClass(classOf[Staging])
269272
mySplicingPhase = phaseOfClass(classOf[Splicing])
@@ -333,16 +336,25 @@ object Phases {
333336
def subPhases: List[Run.SubPhase] = Nil
334337
final def traversals: Int = if subPhases.isEmpty then 1 else subPhases.length
335338

339+
/** skip the phase for a Java compilation unit, may depend on -Ypickle-java */
340+
def skipIfJava(using Context): Boolean = true
341+
336342
/** @pre `isRunnable` returns true */
337343
def run(using Context): Unit
338344

339345
/** @pre `isRunnable` returns true */
340346
def runOn(units: List[CompilationUnit])(using runCtx: Context): List[CompilationUnit] =
341347
val buf = List.newBuilder[CompilationUnit]
348+
val skipJavaUnits =
349+
ctx.settings.YpickleJava.value && ctx.phase <= sbtExtractApiPhase && skipIfJava
342350
for unit <- units do
343351
given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
344352
if ctx.run.enterUnit(unit) then
345-
try run
353+
try
354+
if skipJavaUnits && unit.typedAsJava then
355+
()
356+
else
357+
run
346358
catch case ex: Throwable if !ctx.run.enrichedErrorMessage =>
347359
println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit"))
348360
throw ex
@@ -495,6 +507,7 @@ object Phases {
495507
def postTyperPhase(using Context): Phase = ctx.base.postTyperPhase
496508
def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase
497509
def picklerPhase(using Context): Phase = ctx.base.picklerPhase
510+
def sbtExtractApiPhase(using Context): Phase = ctx.base.sbtExtractApiPhase
498511
def inliningPhase(using Context): Phase = ctx.base.inliningPhase
499512
def stagingPhase(using Context): Phase = ctx.base.stagingPhase
500513
def splicingPhase(using Context): Phase = ctx.base.splicingPhase

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1696,7 +1696,7 @@ object SymDenotations {
16961696
c.ensureCompleted()
16971697
end completeChildrenIn
16981698

1699-
if is(Sealed) || isAllOf(JavaEnumTrait) then
1699+
if is(Sealed) || isAllOf(JavaEnumTrait) && isClass then
17001700
if !is(ChildrenQueried) then
17011701
// Make sure all visible children are completed, so that
17021702
// they show up in Child annotations. A possible child is visible if it

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ object AttributePickler:
1414
pickler: TastyPickler,
1515
buf: TastyBuffer
1616
): Unit =
17-
if attributes.scala2StandardLibrary || attributes.explicitNulls then // or any other attribute is set
17+
if attributes.nonEmpty then
1818
pickler.newSection(AttributesSection, buf)
1919
// Pickle attributes
2020
if attributes.scala2StandardLibrary then buf.writeNat(TastyFormat.SCALA2STANDARDLIBRARYattr)
2121
if attributes.explicitNulls then buf.writeNat(TastyFormat.EXPLICITNULLSattr)
22+
if attributes.isJava then buf.writeNat(TastyFormat.JAVAattr)
23+
if attributes.isOutline then buf.writeNat(TastyFormat.OUTLINEattr)
2224
end if
2325

2426
end pickleAttributes

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,21 @@ class AttributeUnpickler(reader: TastyReader):
1818
lazy val attributes: Attributes = {
1919
var scala2StandardLibrary = false
2020
var explicitNulls = false
21+
var isJava = false
22+
var isOutline = false
2123
for attributeTag <- attributeTags do
2224
attributeTag match
2325
case TastyFormat.SCALA2STANDARDLIBRARYattr => scala2StandardLibrary = true
2426
case TastyFormat.EXPLICITNULLSattr => explicitNulls = true
27+
case TastyFormat.JAVAattr => isJava = true
28+
case TastyFormat.OUTLINEattr => isOutline = true
2529
case attribute =>
2630
assert(false, "Unexpected attribute value: " + attribute)
2731
Attributes(
2832
scala2StandardLibrary,
2933
explicitNulls,
34+
isJava,
35+
isOutline,
3036
)
3137
}
3238

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@ package dotty.tools.dotc.core.tasty
33
class Attributes(
44
val scala2StandardLibrary: Boolean,
55
val explicitNulls: Boolean,
6-
)
6+
val isJava: Boolean,
7+
val isOutline: Boolean,
8+
) {
9+
def nonEmpty: Boolean = scala2StandardLibrary || explicitNulls || isJava || isOutline
10+
}
11+
12+
object Attributes {
13+
lazy val empty = new Attributes(false, false, false, false)
14+
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ object DottyUnpickler {
2020
/** Exception thrown if classfile is corrupted */
2121
class BadSignature(msg: String) extends RuntimeException(msg)
2222

23-
class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], commentUnpickler: Option[CommentUnpickler], attributeUnpickler: Option[AttributeUnpickler])
24-
extends SectionUnpickler[TreeUnpickler](ASTsSection) {
23+
class TreeSectionUnpickler(
24+
posUnpickler: Option[PositionUnpickler],
25+
commentUnpickler: Option[CommentUnpickler],
26+
attributeUnpickler: Option[AttributeUnpickler]
27+
) extends SectionUnpickler[TreeUnpickler](ASTsSection) {
2528
def unpickle(reader: TastyReader, nameAtRef: NameTable): TreeUnpickler =
2629
new TreeUnpickler(reader, nameAtRef, posUnpickler, commentUnpickler, attributeUnpickler)
2730
}
@@ -35,6 +38,7 @@ object DottyUnpickler {
3538
def unpickle(reader: TastyReader, nameAtRef: NameTable): CommentUnpickler =
3639
new CommentUnpickler(reader)
3740
}
41+
3842
class AttributesSectionUnpickler extends SectionUnpickler[AttributeUnpickler](AttributesSection) {
3943
def unpickle(reader: TastyReader, nameAtRef: NameTable): AttributeUnpickler =
4044
new AttributeUnpickler(reader)
@@ -61,6 +65,8 @@ class DottyUnpickler(bytes: Array[Byte], mode: UnpickleMode = UnpickleMode.TopLe
6165
def enter(roots: Set[SymDenotation])(using Context): Unit =
6266
treeUnpickler.enter(roots)
6367

68+
def attributes: Option[Attributes] = attributeUnpicklerOpt.map(_.attributes)
69+
6470
protected def treeSectionUnpickler(
6571
posUnpicklerOpt: Option[PositionUnpickler],
6672
commentUnpicklerOpt: Option[CommentUnpickler],

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import quoted.QuotePatterns
2424
object TreePickler:
2525
class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception
2626

27-
class TreePickler(pickler: TastyPickler) {
27+
class TreePickler(pickler: TastyPickler, attributes: Attributes) {
2828
val buf: TreeBuffer = new TreeBuffer
2929
pickler.newSection(ASTsSection, buf)
3030
import buf.*
@@ -323,6 +323,11 @@ class TreePickler(pickler: TastyPickler) {
323323
if (!tree.isEmpty) pickleTree(tree)
324324
}
325325

326+
def pickleElidedUnlessEmpty(tree: Tree, tp: Type)(using Context): Unit =
327+
if !tree.isEmpty then
328+
writeByte(ELIDED)
329+
pickleType(tp)
330+
326331
def pickleDef(tag: Int, mdef: MemberDef, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(using Context): Unit = {
327332
val sym = mdef.symbol
328333

@@ -338,7 +343,12 @@ class TreePickler(pickler: TastyPickler) {
338343
case _: Template | _: Hole => pickleTree(tpt)
339344
case _ if tpt.isType => pickleTpt(tpt)
340345
}
341-
pickleTreeUnlessEmpty(rhs)
346+
if attributes.isOutline && sym.isTerm && attributes.isJava then
347+
// TODO: if we introduce outline typing for Scala definitions
348+
// then we will need to update the check here
349+
pickleElidedUnlessEmpty(rhs, tpt.tpe)
350+
else
351+
pickleTreeUnlessEmpty(rhs)
342352
pickleModifiers(sym, mdef)
343353
}
344354
catch

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import dotty.tools.tasty.TastyFormat.*
4747
import scala.annotation.constructorOnly
4848
import scala.annotation.internal.sharable
4949
import scala.compiletime.uninitialized
50+
import dotty.tools.tasty.UnpickleException
51+
import dotty.tools.tasty.TastyFormat
5052

5153
/** Unpickler for typed trees
5254
* @param reader the reader from which to unpickle
@@ -107,6 +109,11 @@ class TreeUnpickler(reader: TastyReader,
107109
private val explicitNulls =
108110
attributeUnpicklerOpt.exists(_.attributes.explicitNulls)
109111

112+
private val unpicklingJava =
113+
attributeUnpicklerOpt.exists(_.attributes.isJava)
114+
115+
private val isOutline = attributeUnpicklerOpt.exists(_.attributes.isOutline)
116+
110117
private def registerSym(addr: Addr, sym: Symbol) =
111118
symAtAddr(addr) = sym
112119

@@ -115,6 +122,13 @@ class TreeUnpickler(reader: TastyReader,
115122
*/
116123
def enter(roots: Set[SymDenotation])(using Context): Unit = {
117124
this.roots = roots
125+
if isOutline && !unpicklingJava then
126+
// TODO: overly cautious here, eventually we could enable this with 2-pass compilation.
127+
throw UnpickleException("Outline TASTy is not supported for Scala sources.")
128+
if unpicklingJava && !ctx.settings.YpickleJava.value then
129+
throw UnpickleException(
130+
"Top level class comes from Java sources. " +
131+
"TASTy signatures are not supported for Java without the -Ypickle-java flag.")
118132
val rdr = new TreeReader(reader).fork
119133
ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
120134
if (rdr.isTopLevel)
@@ -612,7 +626,10 @@ class TreeUnpickler(reader: TastyReader,
612626
val rhsIsEmpty = nothingButMods(end)
613627
if (!rhsIsEmpty) skipTree()
614628
val (givenFlags0, annotFns, privateWithin) = readModifiers(end)
615-
val givenFlags = if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty else givenFlags0
629+
val givenFlags =
630+
if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty
631+
else if unpicklingJava then givenFlags0 | JavaDefined
632+
else givenFlags0
616633
pickling.println(i"creating symbol $name at $start with flags ${givenFlags.flagsString}, isAbsType = $isAbsType, $ttag")
617634
val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty)
618635
def adjustIfModule(completer: LazyType) =
@@ -867,6 +884,7 @@ class TreeUnpickler(reader: TastyReader,
867884
def readRhs(using Context): LazyTree =
868885
if (nothingButMods(end))
869886
EmptyTree
887+
870888
else if sym.isInlineMethod && !sym.is(Deferred) then
871889
// The body of an inline method is stored in an annotation, so no need to unpickle it again
872890
new Trees.Lazy[Tree] {
@@ -1045,6 +1063,8 @@ class TreeUnpickler(reader: TastyReader,
10451063
val parentReader = fork
10461064
val parents = readParents(withArgs = false)(using parentCtx)
10471065
val parentTypes = parents.map(_.tpe.dealias)
1066+
if cls.is(JavaDefined) && parentTypes.exists(_.derivesFrom(defn.JavaAnnotationAnnot)) then
1067+
cls.setFlag(JavaAnnotation)
10481068
val self =
10491069
if (nextByte == SELFDEF) {
10501070
readByte()
@@ -1205,7 +1225,12 @@ class TreeUnpickler(reader: TastyReader,
12051225

12061226
def completeSelect(name: Name, sig: Signature, target: Name): Select =
12071227
val qual = readTree()
1208-
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1228+
val denot0 = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1229+
val denot =
1230+
if unpicklingJava && name == tpnme.Object && denot0.symbol == defn.ObjectClass then
1231+
defn.FromJavaObjectType.denot
1232+
else
1233+
denot0
12091234
makeSelect(qual, name, denot)
12101235

12111236
def readQualId(): (untpd.Ident, TypeRef) =
@@ -1224,6 +1249,11 @@ class TreeUnpickler(reader: TastyReader,
12241249
forkAt(readAddr()).readTree()
12251250
case IDENT =>
12261251
untpd.Ident(readName()).withType(readType())
1252+
case ELIDED =>
1253+
if !isOutline then
1254+
report.error(
1255+
s"Illegal elided tree in unpickler without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}")
1256+
untpd.Ident(nme.WILDCARD).withType(readType())
12271257
case IDENTtpt =>
12281258
untpd.Ident(readName().toTypeName).withType(readType())
12291259
case SELECT =>

compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ class ReadTasty extends Phase {
4646
case unpickler: tasty.DottyUnpickler =>
4747
if (cls.rootTree.isEmpty) None
4848
else {
49-
val unit = CompilationUnit(cls, cls.rootTree, forceTrees = true)
50-
unit.pickled += (cls -> (() => unpickler.unpickler.bytes))
51-
Some(unit)
49+
if unpickler.attributes.exists(_.isJava) && !ctx.settings.YpickleJava.value then
50+
// filter out Java compilation units if -Ypickle-java is not set
51+
None
52+
else
53+
val unit = CompilationUnit(cls, cls.rootTree, forceTrees = true)
54+
unit.pickled += (cls -> (() => unpickler.unpickler.bytes))
55+
unit.tastyAttributes = unpickler.attributes
56+
Some(unit)
5257
}
5358
case tree: Tree[?] =>
5459
// TODO handle correctly this case correctly to get the tree or avoid it completely.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ object JavaParsers {
136136
ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.Param))
137137

138138
def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined): DefDef = {
139-
val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p) }
139+
val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p).withMods(Modifiers(flags)) }
140140
DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(vparams)), TypeTree(), EmptyTree).withMods(Modifiers(flags))
141141
}
142142

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ object Parsers {
8181
private val InCond: Region => Region = Scanners.InParens(LPAREN, _)
8282
private val InFor : Region => Region = Scanners.InBraces(_)
8383

84+
def unimplementedExpr(using Context): Select =
85+
Select(scalaDot(nme.Predef), nme.???)
86+
8487
abstract class ParserCommon(val source: SourceFile)(using Context) {
8588

8689
val in: ScannerCommon
@@ -148,9 +151,6 @@ object Parsers {
148151
*/
149152
def syntaxError(msg: Message, span: Span): Unit =
150153
report.error(msg, source.atSpan(span))
151-
152-
def unimplementedExpr(using Context): Select =
153-
Select(scalaDot(nme.Predef), nme.???)
154154
}
155155

156156
trait OutlineParserCommon extends ParserCommon {

0 commit comments

Comments
 (0)