Skip to content

Commit f296b40

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 9edc5c5 commit f296b40

File tree

47 files changed

+459
-36
lines changed

Some content is hidden

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

47 files changed

+459
-36
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: 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/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: 4 additions & 1 deletion
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)
@@ -57,14 +58,16 @@ class DottyUnpickler(bytes: Array[Byte], mode: UnpickleMode = UnpickleMode.TopLe
5758
private val treeUnpickler = unpickler.unpickle(treeSectionUnpickler(posUnpicklerOpt, commentUnpicklerOpt, attributeUnpicklerOpt)).get
5859

5960
def tastyAttributes: Attributes =
60-
attributeUnpicklerOpt.map(_.attributes).getOrElse(Attributes(false, false))
61+
attributeUnpicklerOpt.map(_.attributes).getOrElse(Attributes.empty)
6162

6263
/** Enter all toplevel classes and objects into their scopes
6364
* @param roots a set of SymDenotations that should be overwritten by unpickling
6465
*/
6566
def enter(roots: Set[SymDenotation])(using Context): Unit =
6667
treeUnpickler.enter(roots)
6768

69+
def attributes: Option[Attributes] = attributeUnpicklerOpt.map(_.attributes)
70+
6871
protected def treeSectionUnpickler(
6972
posUnpicklerOpt: Option[PositionUnpickler],
7073
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: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ 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
5051

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

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

@@ -115,6 +121,9 @@ class TreeUnpickler(reader: TastyReader,
115121
*/
116122
def enter(roots: Set[SymDenotation])(using Context): Unit = {
117123
this.roots = roots
124+
if isOutline && !unpicklingJava then
125+
// TODO: overly cautious here, eventually we could enable this with 2-pass compilation.
126+
throw UnpickleException("Outline TASTy is not supported for Scala sources.")
118127
val rdr = new TreeReader(reader).fork
119128
ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
120129
if (rdr.isTopLevel)
@@ -612,7 +621,10 @@ class TreeUnpickler(reader: TastyReader,
612621
val rhsIsEmpty = nothingButMods(end)
613622
if (!rhsIsEmpty) skipTree()
614623
val (givenFlags0, annotFns, privateWithin) = readModifiers(end)
615-
val givenFlags = if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty else givenFlags0
624+
val givenFlags =
625+
if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty
626+
else if unpicklingJava then givenFlags0 | JavaDefined
627+
else givenFlags0
616628
pickling.println(i"creating symbol $name at $start with flags ${givenFlags.flagsString}, isAbsType = $isAbsType, $ttag")
617629
val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty)
618630
def adjustIfModule(completer: LazyType) =
@@ -1045,6 +1057,8 @@ class TreeUnpickler(reader: TastyReader,
10451057
val parentReader = fork
10461058
val parents = readParents(withArgs = false)(using parentCtx)
10471059
val parentTypes = parents.map(_.tpe.dealias)
1060+
if cls.is(JavaDefined) && parentTypes.exists(_.derivesFrom(defn.JavaAnnotationClass)) then
1061+
cls.setFlag(JavaAnnotation)
10481062
val self =
10491063
if (nextByte == SELFDEF) {
10501064
readByte()
@@ -1205,7 +1219,12 @@ class TreeUnpickler(reader: TastyReader,
12051219

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

12111230
def readQualId(): (untpd.Ident, TypeRef) =
@@ -1224,6 +1243,11 @@ class TreeUnpickler(reader: TastyReader,
12241243
forkAt(readAddr()).readTree()
12251244
case IDENT =>
12261245
untpd.Ident(readName()).withType(readType())
1246+
case ELIDED =>
1247+
if !isOutline then
1248+
report.error(
1249+
s"Illegal elided tree in unpickler without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}")
1250+
untpd.Ident(nme.WILDCARD).withType(readType())
12271251
case IDENTtpt =>
12281252
untpd.Ident(readName().toTypeName).withType(readType())
12291253
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.YjavaTasty.value then
50+
// filter out Java compilation units if -Yjava-tasty 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
@@ -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
@@ -55,6 +55,9 @@ class ExtractAPI extends Phase {
5555
// Check no needed. Does not transform trees
5656
override def isCheckable: Boolean = false
5757

58+
// when `-Yjava-tasty` is set we actually want to run this phase on Java sources
59+
override def skipIfJava(using Context): Boolean = false
60+
5861
// SuperAccessors need to be part of the API (see the scripted test
5962
// `trait-super` for an example where this matters), this is only the case
6063
// 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)