Skip to content

Commit df9b52e

Browse files
liufengyunallanrenucci
authored andcommitted
add documentation for compiler plugin
1 parent 264cbf8 commit df9b52e

File tree

5 files changed

+138
-44
lines changed

5 files changed

+138
-44
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
layout: doc-page
3+
title: "Changes in Compiler Plugins"
4+
---
5+
6+
Compiler plugins are supported by Dotty since 0.9. Compared to Scalac, there are
7+
two notable changes:
8+
9+
- No more support for analyzer plugins
10+
- Added support for research plugins
11+
12+
[Analyzer plugins][1] in Scalac are executed during type checking to change the
13+
normal type checking. This is a nice feature for doing research, but for
14+
production usage, a predictable and consistent type checker is more important.
15+
16+
For experiments and researches that depend on analyzer plugins in Scalac,
17+
_research plugin_ can be used for the same purpose in Dotty. Research plugins
18+
are more powerful than Scalac analyzers as it enables plugin authors to
19+
customize the whole compiler pipeline. That means, you can easily use your
20+
customized typer to replace the standard typer, or roll your own parser for
21+
your domain-specific language. Research plugins are only enabled for nightly or
22+
snaphot releases of Dotty.
23+
24+
The common plugins that add a new phase to the compiler pipeline are called
25+
_standard plugins_ in Dotty. In terms of features, they are similar to
26+
Scalac plugins, despite minor changes in the API.
27+
28+
## Physical Interface
29+
30+
Both research plugins and standard plugins share the same command line options
31+
as Scalac plugins. You may manually specify a plugin as a compiler option as follows:
32+
33+
```shell
34+
dotc -Xplugin:pluginA.jar -Xplugin:pluginB.jar Test.scala
35+
```
36+
37+
The compiler will examine the jar provided, and look for a property file
38+
`plugin.properties` in the root directory of the jar. The property file
39+
specifies the fully qualified plugin class name. The format of a property file
40+
looks like the following:
41+
42+
```
43+
pluginClass=dividezero.DivideZero
44+
```
45+
46+
The above is a change from Scalac, which depends on an XML file
47+
`scalac-plugin.xml`. Since SBT 1.1.5, SBT also supports Dotty compiler plugins:
48+
49+
```Scala
50+
addCompilerPlugin("org.divbyzero" % "divbyzero" % "1.0")
51+
```
52+
53+
## Standard Plugin
54+
55+
The following code example shows the template for a standard plugin:
56+
57+
```Scala
58+
package dividezero
59+
60+
import dotty.tools.dotc._
61+
import core._
62+
import Contexts.Context
63+
import plugins._
64+
import Phases.Phase
65+
import ast.tpd
66+
import transform.{LinkAll, Pickler}
67+
68+
class DivideZero extends StandardPlugin {
69+
val name: String = "divideZero"
70+
override val description: String = "divide zero check"
71+
72+
def init(options: List[String]): List[PluginPhase] = (new DivideZeroPhase) :: Nil
73+
}
74+
75+
class DivideZeroPhase extends PluginPhase {
76+
val phaseName = "divideZero"
77+
78+
override val runsAfter = Set(Pickler.name)
79+
override val runsBefore = Set(LinkAll.name)
80+
81+
override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = {
82+
// check whether divide by zero here
83+
tree
84+
}
85+
}
86+
```
87+
88+
As you can see from the code above, the plugin main class `DivideZero`
89+
extends the trait `StandardPlugin`. It implements the method `init` which
90+
takes the options for the plugin and return a list of `PluginPhase`s to be
91+
inserted into the compilation pipeline.
92+
93+
The plugin `DivideZero` only adds one compiler phase, `DivideZeroPhase`,
94+
to the compiler pipeline. The compiler phase has to extend the trait
95+
`PluginPhase`. It also needs to tell the compiler the place where it wants to be
96+
in the pipeline by specifying `runsAfter` and `runsBefore` relative to standard
97+
compiler phases. Finally, it can transform the trees of interest by overriding
98+
methods like `transformXXX`.
99+
100+
Usually a compiler plugin requires significant compiler knowledge in order to
101+
maintain invariants of the compiler. It is a good practice to enable
102+
the compiler option `-Ycheck:all` in the test set of your plugin.
103+
104+
## Research Plugin
105+
106+
Research plugins extend the trait `ResearchPlugin` as the following code shows:
107+
108+
```Scala
109+
import dotty.tools.dotc._
110+
import core._
111+
import Contexts.Context
112+
import plugins._
113+
import Phases.Phase
114+
115+
class DummyResearchPlugin extends ResearchPlugin {
116+
val name: String = "dummy"
117+
override val description: String = "dummy research plugin"
118+
119+
def init(options: List[String], phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] =
120+
phases
121+
}
122+
```
123+
124+
Research plugins also define a method `init`, but the signature is different.
125+
Research plugins receive options for the plugin and the whole compiler pipeline as parameters.
126+
Usually, the `init` method replaces some standard phase of the compiler pipeline
127+
with a custom phase, e.g. use a custom frontend. Finally, `init` returns the
128+
updated compiler pipeline.
129+
130+
Note that research plugins are only enabled for nightly or snaphot release of Dotty.
131+
132+
133+
[1]: https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ sidebar:
7979
url: docs/reference/changed/pattern-matching.html
8080
- title: Eta Expansion
8181
url: docs/reference/changed/eta-expansion.html
82+
- title: Compiler Plugins
83+
url: docs/reference/changed/compiler-plugins.html
8284
- title: Dropped Features
8385
subsection:
8486
- title: DelayedInit

sbt-dotty/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class DivideZero extends PluginPhase with StandardPlugin {
2222
override val runsAfter = Set(Pickler.name)
2323
override val runsBefore = Set(LinkAll.name)
2424

25-
override def init(options: List[String]): List[PluginPhase] = this :: Nil
25+
def init(options: List[String]): List[PluginPhase] = this :: Nil
2626

2727
private def isNumericDivide(sym: Symbol)(implicit ctx: Context): Boolean = {
2828
def test(tpe: String): Boolean =

tests/plugins/neg/divideZero-research/plugin_1.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import Symbols.Symbol
1010
import Constants.Constant
1111
import StdNames._
1212

13-
class DivideZero extends MiniPhase with ResearchPlugin {
13+
class DivideZero extends PluginPhase with ResearchPlugin {
1414
val name: String = "divideZero"
1515
override val description: String = "divide zero check"
1616

1717
val phaseName = name
1818

19-
override def init(options: List[String], phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = {
19+
def init(options: List[String], phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = {
2020
val (before, after) = phases.span(ps => !ps.exists(_.phaseName == "pickler"))
2121
before ++ (List(this) :: after)
2222
}

tests/plugins/pos/divideZero/plugin_1.scala

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)