SLC: Extend `typeChecks`/`typeCheckErrors` to run compiler-plugin phases

I’ve been needing this since forever, since I have compiler plugins that perform various changes and checks that I cannot properly verify them with typeCheckErrors. Therefore, I’m proposing to expand both typeChecks and typeCheckErrors with an additional string literal argument (in backward compatible way) to define the compiler phase to stop at and return the checks from.

(additional methods to the existing ones)

transparent inline def typeChecks(inline code: String, inline stopAfterPhase: String): Boolean 
transparent inline def typeCheckErrors(inline code: String, inline stopAfterPhase: String): List[Error]

I created a draft implementation:

This is not super clear, am I understanding this right ?

Currently:

  1. typeChecks(code) only checks up to some phase (or only during a certain set of phases), and so misses some non-type-checking programs
  2. typeCheckErrors(code) only checks the same phases, and so it is impossible to write tests which catch type check errors outside of those phases (or possible, but very undesirable in some way)

With this change:

  1. typeChecks(code) and typeCheckErrors(code) are unchanged, they still only run on a somewhat ad-hoc set of phases
  2. typeChecks(code, stopAfterPhase) and typeCheckErrors(code, stopAfterPhase) are introduced to be able to set a phase which is further than the one without the parameter

This seems backwards to me, I would expect the version without the parameter to go all the way, and the one with to dial it back somewhat
And I would maybe even expect stopAfterPhase to instead be something like raisesDuringPhase, pin-pointing “when” the error is thrown (only works when combined with my other change)

I understand there might be compatibility issues which might make this impossible, but these have not been explained here or in the PR, and I do not have the knowledge required to identify them
(In other words, please explain why I’m wrong ^^)

We could go maybe with a total different approach just for compiler plugins if there is one, and create a different method. But the effort of explaining the difference is annoying if we can “just make it work” in the expanded fashion without harming the previous behavior.

The error trapping is up to a phase and not during a phase. Maybe indeed a better word (e.g., checkUpToPhase) is the right call here. It is possible to apply a filter and return only the error in the specific phase (maybe even with a flag to decide what we want). I’m open to whatever people think is best.

I don’t understand any of this ^^’

If by “just make it work” you mean “typeCheck{Error}s(code) should run on all phases”, then I totally agree, and that is what I tried to explain with:


I think both stopAfterPhase and checkUpToPhase are perfectly acceptable

Yes I understand that is what you are proposing, I’m saying it would be better to

Read the comment on a different PR where I mentioned I wanted compiler-plugin support Extend compiletime.testing.typechecks with certain transform phases by jchyb · Pull Request #21185 · scala/scala3 · GitHub

Yes I understand that is what you are proposing, I’m saying it would be better to

My main issue with user experience is what happens if you have errors from earlier phases and you get Nil in return. It’s less confusing to get the error from previous stage and fix it so you get what you want than trying to dig up what happened.

Hmm I see, makes sense !

It just hit me that I can add a compiler stage within my library to just do what this proposal is doing and get the tests capability. Don’t know how common this requirement is, so that could be a good enough solution for me.