sbt / zinc

Scala incremental compiler library, used by sbt and other build tools
Apache License 2.0
335 stars 121 forks source link

Feature Request: Make zinc Scala 3 macro-aware #1478

Open gzoller opened 1 month ago

gzoller commented 1 month ago

It would be amazing if incremental compilation was aware of macro dependencies—changes in code generated by a Scala 3 macro. The problem is described here: https://github.com/gzoller/scala-reflection#learning-to-drive-with-macros

If this could be made to work, code using macros would be as seamless as non-macro code with respect to compilation.

Friendseeker commented 1 month ago

Since macros are the only access to reflected class information in Scala 3, it's an "ism" we'll all have to live with, until such time as someone invents a way to track macro dependencies in sbt.

Zinc actually do... track macro dependencies (kinda), with a fully fledged development roadmap back in 2014 and partial implementations of the roadmap scattered around the years, with the latest in https://github.com/sbt/zinc/pull/1282, https://github.com/sbt/zinc/pull/1316. The example mentioned in the article

// File1.scala
case  class  Foo(name: String)

// File2.scala
val  fooRType = RType.of[Foo]

actually works if one uses Scala 2.12 and latest sbt version. (assuming RType.of[T] is a macro that reflectively inspect T).

I would say that macro invalidation is practically a solved problem if one sufficiently familiar with Macro Engine is willing to give it a last push. The only puzzle piece missing is tracking symbols touched reflectively during macro reflection, and a 2014 POC showed that it is feasible. (Just that 2014 POC did it in a really hacky way, so it was unmerged).

gzoller commented 1 month ago

You mention Scala 2 macros. Is this partial capability also present for Scala 3 macros? So far I haven’t seen that anything picks up changes in the macro vs generated code, so its very easy to get in a situation where you change the code your macro expands over (ex: RType.of[Foo] where Foo has just changed) yet the macro is not re-expanded. You still have expanded code for the old, pre-change Foo. It’ll all compile fine and explode quite dramatically when run, usually with an obscure error that means nothing to the uninitiated. In an ideal world, zinc would detect Foo’s change and when it happened upon RType.of[Foo] would re-run the macro expansion, giving a seamless experience.

Friendseeker commented 1 month ago

You mention Scala 2 macros. Is this partial capability also present for Scala 3 macros? So far I haven’t seen that anything picks up changes in the macro vs generated code, so its very easy to get in a situation where you change the code your macro expands over (ex: RType.of[Foo] where Foo has just changed) yet the macro is not re-expanded. You still have expanded code for the old, pre-change Foo. It’ll all compile fine and explode quite dramatically when run, usually with an obscure error that means nothing to the uninitiated. In an ideal world, zinc would detect Foo’s change and when it happened upon RType.of[Foo] would re-run the macro expansion, giving a seamless experience.

Not yet. Currently the partial capability is only available for Scala 2.12. Still need to port to Scala 2.13 and Scala 3. I plan to do it soon.