@@ -7,94 +7,46 @@ previous-page: arch-types
7
7
next-page : arch-symbols
8
8
---
9
9
10
- > (The following is work in progress), adapted from dotty.epfl.ch
11
-
12
- Conceptually, the ` dotc ` compiler's job is to maintain views of various
13
- artifacts associated with source code at all points in time. But what is
14
- * time* for ` dotc ` ? In fact, it is a combination of compiler runs and compiler
15
- phases.
16
-
17
- The * hours* of the compiler's clocks are measured in compiler [ runs] . Every run
18
- creates a new hour, which follows all the compiler runs (hours) that happened
19
- before. ` dotc ` is designed to be used as an incremental compiler that can
20
- support incremental builds, as well as interactions in an IDE and a REPL. This
21
- means that new runs can occur quite frequently. At the extreme, every
22
- keystroke in an editor or REPL can potentially launch a new compiler run, so
23
- potentially an "hour" of compiler time might take only a fraction of a second
24
- in real time.
25
-
26
- The * minutes* of the compiler's clocks are measured in phases. At every
27
- compiler run, the compiler cycles through a number of [ phases] . The list of
28
- phases is defined in the [ Compiler] object There are currently about 60 phases
29
- per run, so the minutes/hours analogy works out roughly. After every phase the
30
- view the compiler has of the world changes: trees are transformed, types are
31
- gradually simplified from Scala types to JVM types, definitions are rearranged,
32
- and so on.
33
-
34
- Many pieces in the information compiler are time-dependent. For instance, a
35
- Scala symbol representing a definition has a type, but that type will usually
36
- change as one goes from the higher-level Scala view of things to the
37
- lower-level JVM view. There are different ways to deal with this. Many
38
- compilers change the type of a symbol destructively according to the "current
39
- phase". Another, more functional approach might be to have different symbols
40
- representing the same definition at different phases, which each symbol
41
- carrying a different immutable type. ` dotc ` employs yet another scheme, which
42
- is inspired by functional reactive programming (FRP): Symbols carry not a
43
- single type, but a function from compiler phase to type. So the type of a
44
- symbol is a time-indexed function, where time ranges over compiler phases.
45
-
46
- Typically, the definition of a symbol or other quantity remains stable for a
47
- number of phases. This leads us to the concept of a [ period] . Conceptually,
48
- period is an interval of some given phases in a given compiler run. Periods
49
- are conceptually represented by three pieces of information
50
-
51
- * the ID of the current run,
52
- * the ID of the phase starting the period
53
- * the number of phases in the period
54
-
55
- All three pieces of information are encoded in a value class over a 32 bit
56
- integer. Here's the API for class ` Period ` :
57
-
58
- ``` scala
59
- class Period (val code : Int ) extends AnyVal {
60
- def runId : RunId // The run identifier of this period.
61
- def firstPhaseId : PhaseId // The first phase of this period
62
- def lastPhaseId : PhaseId // The last phase of this period
63
- def phaseId : PhaseId // The phase identifier of this single-phase period
64
-
65
- def containsPhaseId (id : PhaseId ): Boolean
66
- def contains (that : Period ): Boolean
67
- def overlaps (that : Period ): Boolean
68
-
69
- def & (that : Period ): Period
70
- def | (that : Period ): Period
71
- }
72
- ```
73
-
74
- We can access the parts of a period using ` runId ` , ` firstPhaseId ` ,
75
- ` lastPhaseId ` , or using ` phaseId ` for periods consisting only of a single
76
- phase. They return ` RunId ` or ` PhaseId ` values, which are aliases of ` Int ` .
77
- ` containsPhaseId ` , ` contains ` and ` overlaps ` test whether a period contains a
78
- phase or a period as a sub-interval, or whether the interval overlaps with
79
- another period. Finally, ` & ` and ` | ` produce the intersection and the union of
80
- two period intervals (the union operation ` | ` takes as ` runId ` the ` runId ` of
81
- its left operand, as periods spanning different ` runId ` s cannot be constructed.
82
-
83
- Periods are constructed using two ` apply ` methods:
84
-
85
- ``` scala
86
- object Period {
87
- /** The single-phase period consisting of given run id and phase id */
88
- def apply (rid : RunId , pid : PhaseId ): Period
89
-
90
- /** The period consisting of given run id, and lo/hi phase ids */
91
- def apply (rid : RunId , loPid : PhaseId , hiPid : PhaseId ): Period
92
- }
93
- ```
94
-
95
- As a sentinel value there's ` Nowhere ` , a period that is empty.
10
+ As discussed in the [ lifecycle] of ` dotc ` , the compiler is designed to be interactive,
11
+ and so can answer questions about entities as they come into existance and change throughout time,
12
+ for example:
13
+ - which new definitions were added in a REPL session?
14
+ - which definitions were replaced in an incremental build?
15
+ - how are definitions simplified as they are adapted to the runtime system?
16
+
17
+ ## Hours, Minutes, and Periods
18
+
19
+ For the compiler to be able to resolve the above temporal questions, and more, it maintains
20
+ a concept of time. Additionally, because interactions are frequent, it is important to
21
+ persist knowledge of entities between interactions, allowing the compiler to remain performant;
22
+ knowing about time allows the compiler to efficiently mark entities as being outdated.
23
+
24
+ Conceptually, ` dotc ` works like a clock, where its minutes are represented by [ phases] ,
25
+ and its hours by [ runs] . Like a clock, each run passes once each of its phases have completed
26
+ sequentially, and then a new run can begin. Phases are further grouped into [ periods] , where
27
+ during a period certain entities of the compiler remain stable.
28
+
29
+ The [ Compiler] class is responsible for creating new runs, and also maintains the list of
30
+ phases that make each run.
31
+
32
+ ## Time Dependency
33
+
34
+ During a run, each phase can transform the world as the compiler sees it, for example:
35
+ - to transform trees,
36
+ - to gradually simplify type from Scala types to JVM types,
37
+ - to move definitions out of inner scopes to outer ones, fitting the JVM's model,
38
+ - and so on.
39
+
40
+ A significant consequence of this is that a definition's associated [ Symbol] has a type that
41
+ changes over time. Indeed, a cross module reference is always typed as either a ` TermRef `
42
+ or ` TypeRef ` , these reference types contain a prefix type and a name, which is resolved dynamically
43
+ to a [ Denotation] : a time-indexed function from phase to type. The type of a denotation can then
44
+ be recovered at any moment by passing the relevant phase in question.
96
45
97
46
[ runs ] : https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/Run.scala
98
- [ phases ] : https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Phases.scala
99
- [ period ] : https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Periods.scala
47
+ [ periods ] : https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Periods.scala
100
48
[ Compiler ] : https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala
49
+ [ lifecycle] : {% link _ overviews/scala3-contribution/arch-lifecycle.md %}#introducing-the-compilers-lifecycle
50
+ [ phases] : {% link _ overviews/scala3-contribution/arch-phases.md %}
51
+ [ Symbol] : {% link _ overviews/scala3-contribution/arch-symbols.md %}
52
+ [ Denotation ] : https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Denotations.scala
0 commit comments