Skip to content

Commit 3e8e563

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 7fa6565 commit 3e8e563

File tree

51 files changed

+504
-35
lines changed

Some content is hidden

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

51 files changed

+504
-35
lines changed

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

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

33+
/** Is this the compilation unit of a Java file, or TASTy derived from a Java file */
34+
def typedAsJava = isJava || {
35+
val infoNN = info
36+
infoNN != null && infoNN.tastyInfo.exists(_.attributes.isJava)
37+
}
38+
39+
3340
/** The source version for this unit, as determined by a language import */
3441
var sourceVersion: Option[SourceVersion] = None
3542

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: 6 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,9 @@ 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)
440+
val YallowOutlineFromTasty: Setting[Boolean] = BooleanSetting("-Yallow-outline-from-tasty", "Allow outline TASTy to be loaded with the -from-tasty option.")
436441
end YSettings

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,7 @@ class Definitions {
996996
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
997997
@tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
998998
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")
999+
@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
9991000

10001001
// Annotation classes
10011002
@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
@@ -11,6 +11,8 @@ class Attributes private[tasty](
1111
def explicitNulls: Boolean = booleanTags(EXPLICITNULLSattr)
1212
def captureChecked: Boolean = booleanTags(CAPTURECHECKEDattr)
1313
def withPureFuns: Boolean = booleanTags(WITHPUREFUNSattr)
14+
def isJava: Boolean = booleanTags(JAVAattr)
15+
def isOutline: Boolean = booleanTags(OUTLINEattr)
1416
}
1517

1618
object Attributes:
@@ -19,12 +21,16 @@ object Attributes:
1921
explicitNulls: Boolean,
2022
captureChecked: Boolean,
2123
withPureFuns: Boolean,
24+
isJava: Boolean,
25+
isOutline: Boolean,
2226
): Attributes =
2327
val booleanTags = BitSet.newBuilder
2428
if scala2StandardLibrary then booleanTags += SCALA2STANDARDLIBRARYattr
2529
if explicitNulls then booleanTags += EXPLICITNULLSattr
2630
if captureChecked then booleanTags += CAPTURECHECKEDattr
2731
if withPureFuns then booleanTags += WITHPUREFUNSattr
32+
if isJava then booleanTags += JAVAattr
33+
if isOutline then booleanTags += OUTLINEattr
2834
new Attributes(booleanTags.result())
2935
end apply
3036

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: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ class TreeUnpickler(reader: TastyReader,
104104
private val explicitNulls =
105105
attributeUnpicklerOpt.exists(_.attributes.explicitNulls)
106106

107+
private val unpicklingJava =
108+
attributeUnpicklerOpt.exists(_.attributes.isJava)
109+
110+
private val isOutline = attributeUnpicklerOpt.exists(_.attributes.isOutline)
111+
107112
private def registerSym(addr: Addr, sym: Symbol) =
108113
symAtAddr(addr) = sym
109114

@@ -609,7 +614,10 @@ class TreeUnpickler(reader: TastyReader,
609614
val rhsIsEmpty = nothingButMods(end)
610615
if (!rhsIsEmpty) skipTree()
611616
val (givenFlags0, annotFns, privateWithin) = readModifiers(end)
612-
val givenFlags = if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty else givenFlags0
617+
val givenFlags =
618+
if isClass && unpicklingScala2Library then givenFlags0 | Scala2x | Scala2Tasty
619+
else if unpicklingJava then givenFlags0 | JavaDefined
620+
else givenFlags0
613621
pickling.println(i"creating symbol $name at $start with flags ${givenFlags.flagsString}, isAbsType = $isAbsType, $ttag")
614622
val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty)
615623
def adjustIfModule(completer: LazyType) =
@@ -1037,6 +1045,8 @@ class TreeUnpickler(reader: TastyReader,
10371045
val parentReader = fork
10381046
val parents = readParents(withArgs = false)(using parentCtx)
10391047
val parentTypes = parents.map(_.tpe.dealias)
1048+
if cls.is(JavaDefined) && parentTypes.exists(_.derivesFrom(defn.JavaAnnotationClass)) then
1049+
cls.setFlag(JavaAnnotation)
10401050
val self =
10411051
if (nextByte == SELFDEF) {
10421052
readByte()
@@ -1197,7 +1207,12 @@ class TreeUnpickler(reader: TastyReader,
11971207

11981208
def completeSelect(name: Name, sig: Signature, target: Name): Select =
11991209
val qual = readTree()
1200-
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1210+
val denot0 = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
1211+
val denot =
1212+
if unpicklingJava && name == tpnme.Object && denot0.symbol == defn.ObjectClass then
1213+
defn.FromJavaObjectType.denot
1214+
else
1215+
denot0
12011216
makeSelect(qual, name, denot)
12021217

12031218
def readQualId(): (untpd.Ident, TypeRef) =
@@ -1216,6 +1231,11 @@ class TreeUnpickler(reader: TastyReader,
12161231
forkAt(readAddr()).readTree()
12171232
case IDENT =>
12181233
untpd.Ident(readName()).withType(readType())
1234+
case ELIDED =>
1235+
if !isOutline then
1236+
report.error(
1237+
s"Illegal elided tree in unpickler without ${attributeTagToString(OUTLINEattr)}, ${ctx.source}")
1238+
untpd.Ident(nme.WILDCARD).withType(readType())
12191239
case IDENTtpt =>
12201240
untpd.Ident(readName().toTypeName).withType(readType())
12211241
case SELECT =>

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,16 @@ 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 if attributes.isOutline && !ctx.settings.YallowOutlineFromTasty.value then
54+
cannotUnpickle("it contains outline signatures and -Yallow-outline-from-tasty is not set.")
55+
else
56+
val unit = CompilationUnit(cls, cls.rootTree, forceTrees = true)
57+
unit.pickled += (cls -> (() => unpickler.unpickler.bytes))
58+
Some(unit)
5259
}
5360
case tree: Tree[?] =>
5461
// 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)