-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathCompilationUnit.scala
216 lines (179 loc) · 7.84 KB
/
CompilationUnit.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package dotty.tools
package dotc
import core.*
import Contexts.*
import SymDenotations.ClassDenotation
import Symbols.*
import Comments.Comment
import util.{FreshNameCreator, SourceFile, NoSource}
import util.Spans.Span
import ast.{tpd, untpd}
import tpd.{Tree, TreeTraverser}
import ast.Trees.{Import, Ident}
import typer.Nullables
import core.Decorators.*
import config.{SourceVersion, Feature}
import StdNames.nme
import scala.annotation.internal.sharable
import scala.util.control.NoStackTrace
import transform.MacroAnnotations.isMacroAnnotation
class CompilationUnit protected (val source: SourceFile, val info: CompilationUnitInfo | Null) {
override def toString: String = source.toString
var untpdTree: untpd.Tree = untpd.EmptyTree
var tpdTree: tpd.Tree = tpd.EmptyTree
/** Is this the compilation unit of a Java file */
def isJava: Boolean = source.file.ext.isJava
/** Is this the compilation unit of a Java file, or TASTy derived from a Java file */
def typedAsJava =
val ext = source.file.ext
ext.isJavaOrTasty && (ext.isJava || tastyInfo.exists(_.attributes.isJava))
def tastyInfo: Option[TastyInfo] =
val local = info
if local == null then None else local.tastyInfo
/** The source version for this unit, as determined by a language import */
var sourceVersion: Option[SourceVersion] = None
/** Pickled TASTY binaries, indexed by class. */
var pickled: Map[ClassSymbol, () => Array[Byte]] = Map()
/** The fresh name creator for the current unit.
* FIXME(#7661): This is not fine-grained enough to enable reproducible builds,
* see https://github.com/scala/scala/commit/f50ec3c866263448d803139e119b33afb04ec2bc
*/
val freshNames: FreshNameCreator = new FreshNameCreator.Default
/** Will be set to `true` if there are inline call that must be inlined after typer.
* The information is used in phase `Inlining` in order to avoid traversing trees that need no transformations.
*/
var needsInlining: Boolean = false
var hasMacroAnnotations: Boolean = false
def hasUnrollDefs: Boolean = unrolledClasses.nonEmpty
var unrolledClasses: Set[Symbol] = Set.empty
/** Set to `true` if inliner added anonymous mirrors that need to be completed */
var needsMirrorSupport: Boolean = false
/** Will be set to `true` if contains `Quote`.
* The information is used in phase `Staging`/`Splicing`/`PickleQuotes` in order to avoid traversing trees that need no transformations.
*/
var needsStaging: Boolean = false
/** Will be set to true if the unit contains a captureChecking language import */
var needsCaptureChecking: Boolean = false
/** Will be set to true if the unit contains a pureFunctions language import */
var knowsPureFuns: Boolean = false
var suspended: Boolean = false
var suspendedAtInliningPhase: Boolean = false
/** Can this compilation unit be suspended */
def isSuspendable: Boolean = true
/** List of all comments present in this compilation unit */
var comments: List[Comment] = Nil
/** This is used to record dependencies to invalidate during incremental
* compilation, but only if `ctx.runZincPhases` is true.
*/
val depRecorder: sbt.DependencyRecorder = sbt.DependencyRecorder()
/** Suspends the compilation unit by throwing a SuspendException
* and recording the suspended compilation unit
*/
def suspend(hint: => String)(using Context): Nothing =
assert(isSuspendable)
// Clear references to symbols that may become stale. No need to call
// `depRecorder.sendToZinc()` since all compilation phases will be rerun
// when this unit is unsuspended.
depRecorder.clear()
if !suspended then
suspended = true
val currRun = ctx.run.nn
currRun.suspendedUnits += this
val isInliningPhase = ctx.phase == Phases.inliningPhase
if ctx.settings.XprintSuspension.value then
currRun.suspendedHints += (this -> (hint, isInliningPhase))
if isInliningPhase then
suspendedAtInliningPhase = true
else
currRun.suspendedAtTyperPhase = true
throw CompilationUnit.SuspendException()
private var myAssignmentSpans: Map[Int, List[Span]] | Null = null
/** A map from (name-) offsets of all local variables in this compilation unit
* that can be tracked for being not null to the list of spans of assignments
* to these variables.
*/
def assignmentSpans(using Context): Map[Int, List[Span]] =
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
myAssignmentSpans.nn
}
@sharable object NoCompilationUnit extends CompilationUnit(NoSource, info = null) {
override def isJava: Boolean = false
override def suspend(hint: => String)(using Context): Nothing =
throw CompilationUnit.SuspendException()
override def assignmentSpans(using Context): Map[Int, List[Span]] = Map.empty
}
object CompilationUnit {
class SuspendException extends Exception with NoStackTrace
/** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */
def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit =
val compilationUnitInfo = clsd.symbol.compilationUnitInfo.nn
val file = compilationUnitInfo.associatedFile
apply(SourceFile(file, Array.empty[Char]), unpickled, forceTrees, compilationUnitInfo)
/** Make a compilation unit, given picked bytes and unpickled tree */
def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean, info: CompilationUnitInfo)(using Context): CompilationUnit = {
assert(!unpickled.isEmpty, unpickled)
val unit1 = new CompilationUnit(source, info)
unit1.tpdTree = unpickled
if (forceTrees) {
val force = new Force
force.traverse(unit1.tpdTree)
unit1.needsStaging = force.containsQuote
unit1.needsInlining = force.containsInline
unit1.hasMacroAnnotations = force.containsMacroAnnotation
}
unit1
}
/** Create a compilation unit corresponding to an in-memory String.
* Used for `compiletime.testing.typeChecks`.
*/
def apply(name: String, source: String): CompilationUnit = {
val src = SourceFile.virtual(name = name, content = source, maybeIncomplete = false)
new CompilationUnit(src, null)
}
/** Create a compilation unit corresponding to `source`.
* If `mustExist` is true, this will fail if `source` does not exist.
*/
def apply(source: SourceFile, mustExist: Boolean = true)(using Context): CompilationUnit = {
val src =
if (!mustExist)
source
else if (source.file.isDirectory) {
report.error(em"expected file, received directory '${source.file.path}'")
NoSource
}
else if (!source.file.exists) {
report.error(em"source file not found: ${source.file.path}")
NoSource
}
else source
val info = if src.exists then CompilationUnitInfo(src.file) else null
new CompilationUnit(src, info)
}
/** Force the tree to be loaded */
private class Force extends TreeTraverser {
var containsQuote = false
var containsInline = false
var containsCaptureChecking = false
var containsMacroAnnotation = false
def traverse(tree: Tree)(using Context): Unit = {
if tree.symbol.is(Flags.Inline) then
containsInline = true
tree match
case _: tpd.Quote =>
containsQuote = true
case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of =>
containsQuote = true
case Import(qual, selectors) =>
tpd.languageImport(qual) match
case Some(prefix) =>
for case untpd.ImportSelector(untpd.Ident(imported), untpd.EmptyTree, _) <- selectors do
Feature.handleGlobalLanguageImport(prefix, imported)
case _ =>
case _ =>
for annot <- tree.symbol.annotations do
if annot.isMacroAnnotation then
ctx.compilationUnit.hasMacroAnnotations = true
traverseChildren(tree)
}
}
}