Skip to content

Commit fa6a965

Browse files
committed
Refactor ClassSymbol#tree
Use less state: just a single field that can be either a tree or a tree provider This should make it easy to integrate sourfe files in IDE. The refactoring required a major refactoring in ReadTastyTreesFromClasses, which was a mess before. Hopefully it's not right. There are still "spurious" warnings coming from classes loaded twice. These will require a refactoring of FromTastyTest to avoid. They do not happen if the FromTasty frontend is invoked stand-alone.
1 parent 745a80f commit fa6a965

File tree

10 files changed

+88
-78
lines changed

10 files changed

+88
-78
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object CompilationUnit {
3535
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit =
3636
mkCompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()), unpickled, forceTrees)
3737

38-
/** Make a compilation unit the given unpickled tree */
38+
/** Make a compilation unit, given picked bytes and unpickled tree */
3939
def mkCompilationUnit(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
4040
assert(!unpickled.isEmpty, unpickled)
4141
val unit1 = new CompilationUnit(source)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
873873
def tpes: List[Type] = xs map (_.tpe)
874874
}
875875

876+
trait TreeProvider {
877+
def getTree(implicit ctx: Context): Tree
878+
}
879+
876880
// convert a numeric with a toXXX method
877881
def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = {
878882
val mname = ("to" + numericCls.name).toTermName

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ object Config {
160160
final val showCompletions = false
161161

162162
/** If set, enables tracing */
163-
final val tracingEnabled = false
163+
final val tracingEnabled = true
164164

165165
/** Initial capacity of uniques HashMap.
166166
* Note: This MUST BE a power of two to work with util.HashSet

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ object SymDenotations {
205205
* Uncompleted denotations set myInfo to a LazyType.
206206
*/
207207
final def info(implicit ctx: Context): Type = {
208-
def completeInfo = {
208+
def completeInfo = { // Written this way so that `info` is small enough to be inlined
209209
completeFrom(myInfo.asInstanceOf[LazyType]); info
210210
}
211211
if (myInfo.isInstanceOf[LazyType]) completeInfo else myInfo

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,8 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
379379
if (mayLoadTreesFromTasty) {
380380
result match {
381381
case Some(unpickler: tasty.DottyUnpickler) =>
382-
classRoot.symbol.asClass.unpickler = unpickler
383-
moduleRoot.symbol.asClass.unpickler = unpickler
382+
classRoot.classSymbol.treeOrProvider = unpickler
383+
moduleRoot.classSymbol.treeOrProvider = unpickler
384384
case _ =>
385385
}
386386
}

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import StdNames._
2121
import NameOps._
2222
import NameKinds.LazyImplicitName
2323
import ast.tpd
24-
import tpd.Tree
24+
import tpd.{Tree, TreeProvider}
2525
import ast.TreeTypeMap
2626
import Constants.Constant
2727
import reporting.diagnostic.Message
@@ -615,31 +615,32 @@ object Symbols {
615615

616616
type ThisName = TypeName
617617

618+
type TreeOrProvider = AnyRef /* tpd.TreeProvider | tpd.PackageDef | tpd.TypeDef | tpd.EmptyTree | Null */
619+
620+
private[this] var myTree: TreeOrProvider = tpd.EmptyTree
621+
618622
/** If this is either:
619623
* - a top-level class and `-Yretain-trees` is set
620624
* - a top-level class loaded from TASTY and `-tasty` or `-Xlink` is set
621625
* then return the TypeDef tree (possibly wrapped inside PackageDefs) for this class, otherwise EmptyTree.
622626
* This will force the info of the class.
623627
*/
624-
def tree(implicit ctx: Context): tpd.Tree /* tpd.PackageDef | tpd.TypeDef | tpd.EmptyTree */ = {
625-
denot.info
626-
// TODO: Consider storing this tree like we store lazy trees for inline functions
627-
if (unpickler != null && !denot.isAbsent) {
628-
assert(myTree.isEmpty)
629-
val body = unpickler.body(ctx.addMode(Mode.ReadPositions))
630-
myTree = body.headOption.getOrElse(tpd.EmptyTree)
631-
if (!ctx.settings.fromTasty.value)
632-
unpickler = null
633-
}
634-
myTree
628+
def tree(implicit ctx: Context): Tree = denot.infoOrCompleter match {
629+
case _: NoCompleter =>
630+
tpd.EmptyTree
631+
case _ =>
632+
denot.ensureCompleted()
633+
myTree match {
634+
case fn: TreeProvider => myTree = fn.getTree(ctx)
635+
case _ =>
636+
}
637+
myTree.asInstanceOf[Tree]
635638
}
636-
private[this] var myTree: tpd.Tree /* tpd.PackageDef | tpd.TypeDef | tpd.EmptyTree */ = tpd.EmptyTree
637-
private[dotc] var unpickler: tasty.DottyUnpickler = _
638639

639-
private[dotc] def registerTree(tree: tpd.TypeDef)(implicit ctx: Context): Unit = {
640-
if (ctx.settings.YretainTrees.value)
641-
myTree = tree
642-
}
640+
def treeOrProvider: TreeOrProvider = myTree
641+
642+
private[dotc] def treeOrProvider_=(t: TreeOrProvider)(implicit ctx: Context): Unit =
643+
myTree = t
643644

644645
/** The source or class file from which this class was generated, null if not applicable. */
645646
override def associatedFile(implicit ctx: Context): AbstractFile =

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object DottyUnpickler {
3232
/** A class for unpickling Tasty trees and symbols.
3333
* @param bytes the bytearray containing the Tasty file from which we unpickle
3434
*/
35-
class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
35+
class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded with tpd.TreeProvider {
3636
import tpd._
3737
import DottyUnpickler._
3838

@@ -52,7 +52,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
5252

5353
/** Only used if `-Yretain-trees` is set. */
5454
private[this] var myBody: List[Tree] = _
55-
/** The unpickled trees, and the source file they come from. */
55+
56+
/** The unpickled trees */
5657
def body(implicit ctx: Context): List[Tree] = {
5758
def computeBody() = treeUnpickler.unpickle()
5859
if (ctx.settings.YretainTrees.value) {
@@ -61,4 +62,6 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded {
6162
myBody
6263
} else computeBody()
6364
}
65+
66+
def getTree(implicit ctx: Context): Tree = body.headOption.getOrElse(tpd.EmptyTree)
6467
}

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

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ package dotty.tools
22
package dotc
33
package fromtasty
44

5-
import dotty.tools.dotc.ast.tpd
6-
import dotty.tools.dotc.core.Contexts.Context
7-
import dotty.tools.dotc.core.Decorators._
8-
import dotty.tools.dotc.core.Names._
9-
import dotty.tools.dotc.core.NameOps._
10-
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
11-
import dotty.tools.dotc.core._
12-
import dotty.tools.dotc.typer.FrontEnd
5+
import core._
6+
import Decorators._
7+
import Contexts.Context
8+
import Symbols.{Symbol, ClassSymbol}
9+
import SymDenotations.{SymDenotation, ClassDenotation, LazyType}
10+
import typer.FrontEnd
11+
import Names.TypeName
12+
import NameOps._
13+
import Types.Type
14+
import ast.tpd
15+
import ast.Trees.Tree
16+
import util.SourceFile
17+
import CompilationUnit.mkCompilationUnit
1318

1419
class ReadTastyTreesFromClasses extends FrontEnd {
1520

@@ -21,57 +26,53 @@ class ReadTastyTreesFromClasses extends FrontEnd {
2126
def readTASTY(unit: CompilationUnit)(implicit ctx: Context): Option[CompilationUnit] = unit match {
2227
case unit: TASTYCompilationUnit =>
2328
val className = unit.className.toTypeName
24-
def compilationUnit(className: TypeName): Option[CompilationUnit] = {
25-
tree(className).flatMap {
26-
case (clsd, unpickled) =>
27-
if (unpickled.isEmpty) None
28-
else {
29-
val unit = CompilationUnit.mkCompilationUnit(clsd, unpickled, forceTrees = true)
30-
val cls = clsd.symbol.asClass
31-
if (cls.unpickler == null) {
32-
ctx.error(s"Error: Already loaded ${cls.showFullName}")
33-
None
34-
}
29+
30+
def cannotUnpickle(reason: String): None.type = {
31+
ctx.error(s"class $className cannot be unpickled because $reason")
32+
None
33+
}
34+
35+
def alreadyLoaded(): None.type = {
36+
ctx.warning("sclass $className cannot be unpickled because it is already loaded")
37+
None
38+
}
39+
40+
def compilationUnit(cls: Symbol): Option[CompilationUnit] = cls match {
41+
case cls: ClassSymbol =>
42+
(cls.treeOrProvider: @unchecked) match {
43+
case unpickler: tasty.DottyUnpickler =>
44+
//println(i"unpickling $cls, info = ${cls.infoOrCompleter.getClass}, tree = ${cls.treeOrProvider}")
45+
if (cls.tree.isEmpty) None
3546
else {
36-
unit.pickled += (cls -> cls.unpickler.unpickler.bytes)
37-
cls.unpickler = null
47+
val source = SourceFile(cls.associatedFile, Array())
48+
val unit = mkCompilationUnit(source, cls.tree, forceTrees = true)
49+
unit.pickled += (cls -> unpickler.unpickler.bytes)
3850
Some(unit)
3951
}
40-
}
41-
}
52+
case tree: Tree[_] =>
53+
alreadyLoaded()
54+
case _ =>
55+
cannotUnpickle(s"its class file does not have a TASTY attribute")
56+
}
57+
case _ => None
4258
}
59+
4360
// The TASTY section in a/b/C.class may either contain a class a.b.C, an object a.b.C, or both.
4461
// We first try to load the class and fallback to loading the object if the class doesn't exist.
4562
// Note that if both the class and the object are present, then loading the class will also load
4663
// the object, this is why we use orElse here, otherwise we could load the object twice and
4764
// create ambiguities!
48-
compilationUnit(className).orElse(compilationUnit(className.moduleClassName))
49-
}
50-
51-
private def tree(className: TypeName)(implicit ctx: Context): Option[(ClassDenotation, tpd.Tree)] = {
52-
val clsd = ctx.base.staticRef(className)
53-
ctx.base.staticRef(className) match {
54-
case clsd: ClassDenotation =>
55-
val cls = clsd.symbol.asClass
56-
def cannotUnpickle(reason: String) =
57-
ctx.error(s"class $className cannot be unpickled because $reason")
58-
def tryToLoad = clsd.infoOrCompleter match {
59-
case info: ClassfileLoader =>
60-
info.load(clsd)
61-
Option(cls.tree).orElse {
62-
cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
63-
None
64-
}
65-
66-
case info =>
67-
cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader")
68-
None
69-
}
70-
Option(cls.tree).orElse(tryToLoad).map(tree => (clsd, tree))
71-
72-
case _ =>
73-
ctx.error(s"class not found: $className")
74-
None
75-
}
65+
ctx.staticRef(className) match {
66+
case clsd: ClassDenotation =>
67+
clsd.infoOrCompleter match {
68+
case info: ClassfileLoader =>
69+
info.load(clsd) // sets cls.treeOrProvider and cls.moduleClass.treeProvider as a side-effect
70+
compilationUnit(clsd.classSymbol).orElse(compilationUnit(clsd.moduleClass))
71+
case _ =>
72+
alreadyLoaded()
73+
}
74+
case _ =>
75+
cannotUnpickle(s"no class file was found")
76+
}
7677
}
7778
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@ class Typer extends Namer
15341534
// check value class constraints
15351535
checkDerivedValueClass(cls, body1)
15361536

1537-
cls.registerTree(cdef1)
1537+
if (ctx.settings.YretainTrees.value) cls.treeOrProvider = cdef1
15381538

15391539
cdef1
15401540

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class DottyLanguageServer extends LanguageServer
6565
myDrivers = new mutable.HashMap
6666
for (config <- configs) {
6767
val classpathFlags = List("-classpath", (config.classDirectory +: config.dependencyClasspath).mkString(File.pathSeparator))
68-
val sourcepathFlags = List("-sourcepath", config.sourceDirectories.mkString(File.pathSeparator), "-scansource")
68+
val sourcepathFlags = List("-sourcepath", config.sourceDirectories.mkString(File.pathSeparator))
69+
System.out.println(s"sourcepath = $sourcepathFlags")
6970
val settings = defaultFlags ++ config.compilerArguments.toList ++ classpathFlags ++ sourcepathFlags
7071
myDrivers.put(config, new InteractiveDriver(settings))
7172
}

0 commit comments

Comments
 (0)