The phase assembly, which is responsible for ensuring correct execution order of internal as well as user-defined compiler components, is severely broken.
I have written a small compiler plugin which can be configured to instantiate an arbitrary number of noop components with user-defined phase constraints. For installing it locally, perform
The syntax for defining phases is: phase1(after;rightAfter;before)|phase2(after;rightAfter;before)|....
Here, after and before are comma-separated lists of phase names. The phase definition is specified as the property dummyphase.phases. Example invocations can be found below (note that the "parser" is extremely fragile).
Every phase should run after "parser" and before "terminal". However, if a component specifies empty lists for both runsAfter and runsBefore, it is ignored entirely.
To make matters worse, a component is considered if it declares that it runs after parser, but not if it declares to run before terminal.
This leads to wrong phase orders without any kind of warning.
The current phase assembly fails if a phase that defines a rightAfter dependency has another outgoing edge. Due to the above, this cannot be shown by specifying an after and a rightAfter dependency, but a rightAfter in combination with a before exposes this.
As can be seen in the attached phase-order.png, the constraint system is satisfiable.
The implementation modifies variable members of case classes (that are used for hashCode calculation), without removing them from containing HashSet s. This leads to erroneous phase graphs. See attached file phase-4.png, where the posterasure node is still present even though it was collapsed into another node.
Note that no user-defined phases are necessary to expose this behavior.