Skip to content

Commit 940acf6

Browse files
committed
make scheduling deterministic
1 parent 1f2759e commit 940acf6

File tree

2 files changed

+91
-56
lines changed

2 files changed

+91
-56
lines changed

compiler/src/dotty/tools/dotc/plugins/Plugins.scala

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import config.PathResolver
77
import dotty.tools.io._
88
import Phases._
99

10+
import scala.collection.mutable.ListBuffer
11+
1012
/** Support for run-time loading of compiler plugins.
1113
*
1214
* @author Lex Spoon
@@ -125,7 +127,8 @@ trait Plugins {
125127
}
126128

127129
// schedule plugins according to ordering constraints
128-
val updatedPlan = Plugins.schedule(plan, plugins.filter(!_.research), options)
130+
val pluginPhases = plugins.filter(!_.research).flatMap(plug => plug.init(options(plug)))
131+
val updatedPlan = Plugins.schedule(plan, pluginPhases)
129132

130133
// add research plugins
131134
plugins.filter(_.research).foldRight(updatedPlan) { (plug, plan) => plug.init(options(plug), plan) }
@@ -140,7 +143,7 @@ object Plugins {
140143
*
141144
* Note: this algorithm is factored out for unit test.
142145
*/
143-
def schedule(plan: List[List[Phase]], plugins: List[Plugin], optionFn: Plugin => List[String]): List[List[Phase]] = {
146+
def schedule(plan: List[List[Phase]], pluginPhases: List[PluginPhase]): List[List[Phase]] = {
144147
import scala.collection.mutable.{ Set => MSet, Map => MMap }
145148
type OrderingReq = (MSet[Class[_]], MSet[Class[_]])
146149

@@ -178,39 +181,36 @@ object Plugins {
178181
}
179182
}
180183

184+
pluginPhases.foreach(updateOrdering)
185+
181186
var updatedPlan = plan
182-
plugins.foreach { plug =>
183-
plug.init(optionFn(plug)).foreach { phase =>
184-
updateOrdering(phase)
187+
pluginPhases.sortBy(_.phaseName).foreach { phase =>
188+
val (runsBefore1, runsAfter1) = orderRequirements(phase.getClass)
189+
val runsBefore = runsBefore1 & existingPhases
190+
val runsAfter = runsAfter1 & existingPhases
191+
192+
// beforeReq met after the split
193+
val (before, after) = updatedPlan.span { ps =>
194+
val classes = ps.map(_.getClass)
195+
val runsAfterSat = runsAfter.isEmpty
196+
runsAfter --= classes
197+
// Prefer the point immediately before the first beforePhases.
198+
// If beforePhases not specified, insert at the point immediately
199+
// after the last afterPhases.
200+
!classes.exists(runsBefore.contains) &&
201+
!(runsBefore.isEmpty && runsAfterSat)
202+
}
185203

186-
val (runsBefore1, runsAfter1) = orderRequirements(phase.getClass)
187-
val runsBefore: MSet[Class[_]] = runsBefore1 & existingPhases // MSet(runsBefore1.filter(existingPhases.contains).toSeq: _*)
188-
val runsAfter: MSet[Class[_]] = runsAfter1 & existingPhases // MSet(runsAfter1.filter(existingPhases.contains).toSeq: _*)
189-
190-
// beforeReq met after the split
191-
val (before, after) = updatedPlan.span { ps =>
192-
val classes = ps.map(_.getClass)
193-
val runsAfterSat = runsAfter.isEmpty
194-
runsAfter --= classes
195-
// Prefer the point immediately before the first beforePhases.
196-
// If beforePhases not specified, insert at the point immediately
197-
// after the last afterPhases.
198-
!classes.exists(runsBefore.contains) &&
199-
!(runsBefore.isEmpty && runsAfterSat)
200-
}
201-
202-
// check afterReq
203-
// error can occur if: a < b, b < c, c < a
204-
after.foreach { ps =>
205-
val classes = ps.map(_.getClass)
206-
if (classes.exists(runsAfter)) // afterReq satisfied
207-
throw new Exception(s"Ordering conflict for plugin ${plug.name}")
208-
}
209-
210-
211-
existingPhases += phase.getClass
212-
updatedPlan = before ++ (List(phase) :: after)
204+
// check afterReq
205+
// error can occur if: a < b, b < c, c < a
206+
after.foreach { ps =>
207+
val classes = ps.map(_.getClass)
208+
if (classes.exists(runsAfter)) // afterReq satisfied
209+
throw new Exception(s"Ordering conflict for phase ${phase.phaseName}")
213210
}
211+
212+
existingPhases += phase.getClass
213+
updatedPlan = before ++ (List(phase) :: after)
214214
}
215215

216216
updatedPlan

compiler/test/dotty/tools/dotc/plugins/PluginsTest.scala

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ class PluginsTest {
2525
class P7 extends TestPhase
2626
class P8 extends TestPhase
2727

28-
class TestPlugin extends TestPhase with Plugin {
29-
def name = this.getClass.getName
30-
override def description = ""
31-
32-
override def init(options: List[String]): List[PluginPhase] = this :: Nil
33-
}
34-
3528
val basicPlan = List(
3629
List(new P1),
3730
List(new P2),
@@ -45,45 +38,45 @@ class PluginsTest {
4538

4639
@Test
4740
def insertAfter = {
48-
object M1 extends TestPlugin {
41+
object M1 extends TestPhase {
4942
override val runsAfter = Set(classOf[P3d])
5043
}
5144

52-
val updatedPlan = Plugins.schedule(basicPlan, M1 :: Nil, _ => Nil)
45+
val updatedPlan = Plugins.schedule(basicPlan, M1 :: Nil)
5346
assert(updatedPlan(3)(0) eq M1)
5447
}
5548

5649
@Test
5750
def insertBefore = {
58-
object ConstFold extends TestPlugin {
51+
object ConstFold extends TestPhase {
5952
override val runsBefore = Set(classOf[P7])
6053
}
6154

62-
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
55+
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil)
6356
assert(updatedPlan(6)(0) eq ConstFold)
6457
}
6558

6659
@Test
6760
def insertBeforeAfter = {
68-
object ConstFold extends TestPlugin {
61+
object ConstFold extends TestPhase {
6962
override val runsAfter = Set(classOf[P3d])
7063
override val runsBefore = Set(classOf[P7], classOf[P8])
7164
}
7265

7366
// prefers the runsBefore
74-
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
67+
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil)
7568
assert(updatedPlan(6)(0) eq ConstFold)
7669
}
7770

7871
@Test
7972
def constraintUnsatisfiable = {
80-
object ConstFold extends TestPlugin {
73+
object ConstFold extends TestPhase {
8174
override val runsAfter = Set(classOf[P6d])
8275
override val runsBefore = Set(classOf[P2], classOf[P8])
8376
}
8477

8578
try {
86-
Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
79+
Plugins.schedule(basicPlan, ConstFold :: Nil)
8780
assert(false, "unsatisfiable constraint should throw exception, but not")
8881
} catch {
8982
case _: Exception =>
@@ -92,44 +85,86 @@ class PluginsTest {
9285

9386
@Test
9487
def orderingTwoPlugins1 = {
95-
object M1 extends TestPlugin {
88+
object M1 extends TestPhase {
9689
override val runsAfter = Set(classOf[P3d])
9790
override val runsBefore = Set(M2.getClass, classOf[P7], classOf[P8])
9891
}
99-
object M2 extends TestPlugin {
92+
object M2 extends TestPhase {
10093
override val runsAfter = Set(classOf[P3d])
10194
override val runsBefore = Set(classOf[P7], classOf[P8])
10295
}
10396

10497
// M1 inserted to plan first
105-
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil, _ => Nil)
98+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil)
10699
assert(updatedPlan1(6)(0) eq M1)
107100
assert(updatedPlan1(7)(0) eq M2)
108101

109102
// M2 inserted to plan first
110-
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil, _ => Nil)
103+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil)
111104
assert(updatedPlan2(6)(0) eq M1)
112105
assert(updatedPlan2(7)(0) eq M2)
113106
}
114107

115108
@Test
116109
def orderingTwoPlugins2 = {
117-
object M1 extends TestPlugin {
110+
object M1 extends TestPhase {
118111
override val runsAfter = Set(classOf[P3d], M2.getClass)
119112
}
120-
object M2 extends TestPlugin {
113+
object M2 extends TestPhase {
121114
override val runsAfter = Set(classOf[P3d])
122115
override val runsBefore = Set(classOf[P7], classOf[P8])
123116
}
124117

125118
// M1 inserted to plan first
126-
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil, _ => Nil)
119+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil)
127120
assert(updatedPlan1(4)(0) eq M1)
128121
assert(updatedPlan1(3)(0) eq M2)
129122

130123
// M2 inserted to plan first
131-
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil, _ => Nil)
124+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil)
125+
assert(updatedPlan2(4)(0) eq M1)
126+
assert(updatedPlan2(3)(0) eq M2)
127+
}
128+
129+
@Test
130+
def orderingTwoPlugins3 = {
131+
object M1 extends TestPhase {
132+
override val runsAfter = Set(classOf[P3d], M2.getClass)
133+
override val runsBefore = Set(classOf[P7], classOf[P8])
134+
}
135+
object M2 extends TestPhase {
136+
override val runsAfter = Set(classOf[P3d])
137+
override val runsBefore = Set(classOf[P5])
138+
}
139+
140+
// M1 inserted to plan first
141+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil)
142+
assert(updatedPlan1(7)(0) eq M1)
143+
assert(updatedPlan1(4)(0) eq M2)
144+
145+
// M2 inserted to plan first
146+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil)
132147
assert(updatedPlan2(7)(0) eq M1)
133-
assert(updatedPlan2(6)(0) eq M2)
148+
assert(updatedPlan2(4)(0) eq M2)
149+
}
150+
151+
@Test
152+
def deterministic = {
153+
object M1 extends TestPhase {
154+
override val runsAfter = Set(classOf[P3d])
155+
override val runsBefore = Set(classOf[P7], classOf[P8])
156+
}
157+
object M2 extends TestPhase {
158+
override val runsAfter = Set(classOf[P3d])
159+
override val runsBefore = Set(classOf[P7], classOf[P8])
160+
}
161+
162+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil)
163+
assert(updatedPlan1(6)(0) eq M1)
164+
assert(updatedPlan1(7)(0) eq M2)
165+
166+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil)
167+
assert(updatedPlan1(6)(0) eq M1)
168+
assert(updatedPlan1(7)(0) eq M2)
134169
}
135170
}

0 commit comments

Comments
 (0)