Skip to content

Commit 32d4c62

Browse files
committed
Support the -Yjava-tasty flag.
- Keep Java compilation units up to Pickler phase if -Yjava-tasty. Skip phases for Java when not needed. - 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. - write java tasty files to a special jar, set with -Yjava-tasty-output this option is for testing purposes only.
1 parent 639d8e5 commit 32d4c62

File tree

45 files changed

+452
-33
lines changed

Some content is hidden

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

45 files changed

+452
-33
lines changed

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

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

33+
/** Under `-from-tasty` the attributes associated with the underlying TASTy, otherwise null.
34+
* This is also null until set by ReadTasty.
35+
*/
36+
var tastyAttributes: tasty.Attributes | Null = null
37+
38+
/** Is this the compilation unit of a Java file, or TASTy derived from a Java file */
39+
def typedAsJava = isJava || {
40+
val attrs = tastyAttributes
41+
attrs != null && attrs.isJava
42+
}
43+
44+
3345
/** The source version for this unit, as determined by a language import */
3446
var sourceVersion: Option[SourceVersion] = None
3547

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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import dotty.tools.dotc.config.Settings.{Setting, SettingGroup}
88
import dotty.tools.dotc.config.SourceVersion
99
import dotty.tools.dotc.core.Contexts.*
1010
import dotty.tools.dotc.rewrites.Rewrites
11-
import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory}
11+
import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory, NoAbstractFile}
1212
import Setting.ChoiceWithHelp
1313

1414
import scala.util.chaining.*
@@ -433,4 +433,8 @@ private sealed trait YSettings:
433433
val YforceInlineWhileTyping: Setting[Boolean] = BooleanSetting("-Yforce-inline-while-typing", "Make non-transparent inline methods inline when typing. Emulates the old inlining behavior of 3.0.0-M3.")
434434

435435
val YdebugMacros: Setting[Boolean] = BooleanSetting("-Ydebug-macros", "Show debug info when quote pattern match fails")
436+
437+
// Pipeline compilation options
438+
val YjavaTasty: Setting[Boolean] = BooleanSetting("-Yjava-tasty", "Pickler phase should compute pickles for .java defined symbols for use by build tools")
439+
val YjavaTastyOutput: Setting[AbstractFile] = OutputSetting("-Yjava-tasty-output", "directory|jar", "(Internal use only!) destination for generated .tasty files containing Java type signatures.", NoAbstractFile)
436440
end YSettings

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@ class Definitions {
992992
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
993993
@tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
994994
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")
995+
@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
995996

996997
// Annotation classes
997998
@tu lazy val AllowConversionsAnnot: ClassSymbol = requiredClass("scala.annotation.allowConversions")

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,16 +333,25 @@ object Phases {
333333
def subPhases: List[Run.SubPhase] = Nil
334334
final def traversals: Int = if subPhases.isEmpty then 1 else subPhases.length
335335

336+
/** skip the phase for a Java compilation unit, may depend on -Yjava-tasty */
337+
def skipIfJava(using Context): Boolean = true
338+
336339
/** @pre `isRunnable` returns true */
337340
def run(using Context): Unit
338341

339342
/** @pre `isRunnable` returns true */
340343
def runOn(units: List[CompilationUnit])(using runCtx: Context): List[CompilationUnit] =
341344
val buf = List.newBuilder[CompilationUnit]
345+
// factor out typedAsJava check when not needed
346+
val doSkipJava = ctx.settings.YjavaTasty.value && this <= picklerPhase && skipIfJava
342347
for unit <- units do
343348
given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
344349
if ctx.run.enterUnit(unit) then
345-
try run
350+
try
351+
if doSkipJava && unit.typedAsJava then
352+
()
353+
else
354+
run
346355
catch case ex: Throwable if !ctx.run.enrichedErrorMessage =>
347356
println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit"))
348357
throw ex

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@ class Attributes private[tasty](
99
) {
1010
def scala2StandardLibrary: Boolean = booleanTags(SCALA2STANDARDLIBRARYattr)
1111
def explicitNulls: Boolean = booleanTags(EXPLICITNULLSattr)
12+
def isJava: Boolean = booleanTags(JAVAattr)
13+
def isOutline: Boolean = booleanTags(OUTLINEattr)
1214
}
1315

1416
object Attributes:
1517
def apply(
1618
scala2StandardLibrary: Boolean,
1719
explicitNulls: Boolean,
20+
isJava: Boolean,
21+
isOutline: Boolean,
1822
): Attributes =
1923
val booleanTags = BitSet.newBuilder
2024
if scala2StandardLibrary then booleanTags += SCALA2STANDARDLIBRARYattr
2125
if explicitNulls then booleanTags += EXPLICITNULLSattr
26+
if isJava then booleanTags += JAVAattr
27+
if isOutline then booleanTags += OUTLINEattr
2228
new Attributes(booleanTags.result())
2329
end apply
2430

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ object DottyUnpickler {
3636
def unpickle(reader: TastyReader, nameAtRef: NameTable): CommentUnpickler =
3737
new CommentUnpickler(reader)
3838
}
39+
3940
class AttributesSectionUnpickler extends SectionUnpickler[AttributeUnpickler](AttributesSection) {
4041
def unpickle(reader: TastyReader, nameAtRef: NameTable): AttributeUnpickler =
4142
new AttributeUnpickler(reader)

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

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

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

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

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

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import dotty.tools.tasty.TastyFormat.*
4646
import scala.annotation.constructorOnly
4747
import scala.annotation.internal.sharable
4848
import scala.compiletime.uninitialized
49+
import dotty.tools.tasty.UnpickleException
4950

5051
/** Unpickler for typed trees
5152
* @param reader the reader from which to unpickle
@@ -106,6 +107,11 @@ class TreeUnpickler(reader: TastyReader,
106107
private val explicitNulls =
107108
attributeUnpicklerOpt.exists(_.attributes.explicitNulls)
108109

110+
private val unpicklingJava =
111+
attributeUnpicklerOpt.exists(_.attributes.isJava)
112+
113+
private val isOutline = attributeUnpicklerOpt.exists(_.attributes.isOutline)
114+
109115
private def registerSym(addr: Addr, sym: Symbol) =
110116
symAtAddr(addr) = sym
111117

@@ -114,6 +120,9 @@ class TreeUnpickler(reader: TastyReader,
114120
*/
115121
def enter(roots: Set[SymDenotation])(using Context): Unit = {
116122
this.roots = roots
123+
if isOutline && !unpicklingJava then
124+
// TODO: overly cautious here, eventually we could enable this with 2-pass compilation.
125+
throw UnpickleException("Outline TASTy is not supported for Scala sources.")
117126
val rdr = new TreeReader(reader).fork
118127
ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
119128
if (rdr.isTopLevel)
@@ -611,7 +620,10 @@ class TreeUnpickler(reader: TastyReader,
611620
val rhsIsEmpty = nothingButMods(end)
612621
if (!rhsIsEmpty) skipTree()
613622
val (givenFlags0, annotFns, privateWithin) = readModifiers(end)
614-
val givenFlags = if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty else givenFlags0
623+
val givenFlags =
624+
if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty
625+
else if unpicklingJava then givenFlags0 | JavaDefined
626+
else givenFlags0
615627
pickling.println(i"creating symbol $name at $start with flags ${givenFlags.flagsString}, isAbsType = $isAbsType, $ttag")
616628
val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty)
617629
def adjustIfModule(completer: LazyType) =
@@ -1044,6 +1056,8 @@ class TreeUnpickler(reader: TastyReader,
10441056
val parentReader = fork
10451057
val parents = readParents(withArgs = false)(using parentCtx)
10461058
val parentTypes = parents.map(_.tpe.dealias)
1059+
if cls.is(JavaDefined) && parentTypes.exists(_.derivesFrom(defn.JavaAnnotationClass)) then
1060+
cls.setFlag(JavaAnnotation)
10471061
val self =
10481062
if (nextByte == SELFDEF) {
10491063
readByte()
@@ -1204,7 +1218,12 @@ class TreeUnpickler(reader: TastyReader,
12041218

12051219
def completeSelect(name: Name, sig: Signature, target: Name): Select =
12061220
val qual = readTree()
1207-
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1221+
val denot0 = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1222+
val denot =
1223+
if unpicklingJava && name == tpnme.Object && denot0.symbol == defn.ObjectClass then
1224+
defn.FromJavaObjectType.denot
1225+
else
1226+
denot0
12081227
makeSelect(qual, name, denot)
12091228

12101229
def readQualId(): (untpd.Ident, TypeRef) =
@@ -1223,6 +1242,11 @@ class TreeUnpickler(reader: TastyReader,
12231242
forkAt(readAddr()).readTree()
12241243
case IDENT =>
12251244
untpd.Ident(readName()).withType(readType())
1245+
case ELIDED =>
1246+
if !isOutline then
1247+
report.error(
1248+
s"Illegal elided tree in unpickler without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}")
1249+
untpd.Ident(nme.WILDCARD).withType(readType())
12261250
case IDENTtpt =>
12271251
untpd.Ident(readName().toTypeName).withType(readType())
12281252
case SELECT =>

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,15 @@ 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+
val attributes = unpickler.tastyAttributes
50+
if attributes.isJava && !ctx.settings.YjavaTasty.value then
51+
// filter out Java compilation units if -Yjava-tasty is not set
52+
None
53+
else
54+
val unit = CompilationUnit(cls, cls.rootTree, forceTrees = true)
55+
unit.pickled += (cls -> (() => unpickler.unpickler.bytes))
56+
unit.tastyAttributes = attributes
57+
Some(unit)
5258
}
5359
case tree: Tree[?] =>
5460
// 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
@@ -97,6 +97,9 @@ object Parsers {
9797
private val InCond: Region => Region = Scanners.InParens(LPAREN, _)
9898
private val InFor : Region => Region = Scanners.InBraces(_)
9999

100+
def unimplementedExpr(using Context): Select =
101+
Select(scalaDot(nme.Predef), nme.???)
102+
100103
abstract class ParserCommon(val source: SourceFile)(using Context) {
101104

102105
val in: ScannerCommon
@@ -164,9 +167,6 @@ object Parsers {
164167
*/
165168
def syntaxError(msg: Message, span: Span): Unit =
166169
report.error(msg, source.atSpan(span))
167-
168-
def unimplementedExpr(using Context): Select =
169-
Select(scalaDot(nme.Predef), nme.???)
170170
}
171171

172172
trait OutlineParserCommon extends ParserCommon {

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import dotty.tools.dotc.core.Flags.*
99
import dotty.tools.dotc.core.Mode
1010
import dotty.tools.dotc.core.Symbols.*
1111
import dotty.tools.dotc.core.Types.*
12-
import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter, TreePickler }
12+
import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter, TreePickler, Attributes }
1313
import dotty.tools.dotc.core.tasty.DottyUnpickler
1414
import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode
1515
import dotty.tools.dotc.report
@@ -217,7 +217,7 @@ object PickledQuotes {
217217
private def pickle(tree: Tree)(using Context): Array[Byte] = {
218218
quotePickling.println(i"**** pickling quote of\n$tree")
219219
val pickler = new TastyPickler(defn.RootClass)
220-
val treePkl = new TreePickler(pickler)
220+
val treePkl = new TreePickler(pickler, Attributes.empty)
221221
treePkl.pickle(tree :: Nil)
222222
treePkl.compactify()
223223
if tree.span.exists then

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class ExtractAPI extends Phase {
5454
// Check no needed. Does not transform trees
5555
override def isCheckable: Boolean = false
5656

57+
// when `-Yjava-tasty` is set we actually want to run this phase on Java sources
58+
override def skipIfJava(using Context): Boolean = false
59+
5760
// SuperAccessors need to be part of the API (see the scripted test
5861
// `trait-super` for an example where this matters), this is only the case
5962
// after `PostTyper` (unlike `ExtractDependencies`, the simplication to trees

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class ExtractDependencies extends Phase {
6464
// Check no needed. Does not transform trees
6565
override def isCheckable: Boolean = false
6666

67+
// when `-Yjava-tasty` is set we actually want to run this phase on Java sources
68+
override def skipIfJava(using Context): Boolean = false
69+
6770
// This phase should be run directly after `Frontend`, if it is run after
6871
// `PostTyper`, some dependencies will be lost because trees get simplified.
6972
// See the scripted test `constants` for an example where this matters.

0 commit comments

Comments
 (0)