-
Notifications
You must be signed in to change notification settings - Fork 213
How do we handle access to global mutable state in macros? #1917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
My thoughts: Option 1It feels both too restrictive and not worth the effort of trying to enforce Option 2I can't imagine this is fast enough - it seems that our time budget per macro application is <1ms. Even that budget could represent a significant total increase in compile time if macros were used at a large scale. Option 3I think the cost/benefit analysis just doesn't work out. Option 4This is my choice. Ya, its not perfect, but it will result in the fastest compilation and there are legitimate use cases that don't violate the rules. The build_runner package gets away with this approach, as does bazel (local workers in bazel are long running, have persistent state, and bazel just trusts them to do no wrong). |
I see the appeal of 4, but I'm very hesitant to commit to it. JavaScript never specified object property iteration order and left it up to implementations. Chrome chose an iteration order different from insertion order and it became a never-ending series of bug reports because users expected it to work the same across all browsers. Eventually, ECMAScript conceded defeat and specified an iteration order in ES 6. The Go team didn't want users to inadvertently rely on map iteration order so they actually randomize it every time to prevent users from relying on it. This is why the default map type in Dart is LinkedHashMap (specified iteration order) and not HashMap (unspecified). I worry very much that if the macro expansion order is ever visible, we will be forced to maintain that order in perpetuity and that could make it harder to optimize the compiler. Maybe the right answer is to do what Go does and randomize the order. |
I would be fine with randomizing macro ordering or something in order to try and prevent users from doing weird things, that should be pretty cheap to do. |
We could also try and do something where we periodically (maybe randomly) do reset the state (hot restart the macro isolate or something randomly). |
I am going to close this issue - the spec currently has this:
Which is Option 4. I think that is fine. |
@jakemac53 Just to be clear, that doesn't prevent macros from defining global state if there's no phase issue, right? For example what if two macros depend on a package which has defines a global variable. Assuming both macros are correctly in the same phase, is it legal and is the state shared between macros? The point being that we'd want to enable to cache some logic across macros when possible. |
Correct, package:logging for instance uses factory constructors and re-uses loggers based on the name. This type of behavior is allowed, but the behavior is ultimately undefined and a macro should not be written in a way that requires the caching. |
Actually resolve #1917 - I missed this section in the proposal. We already chose option 4 so I removed the other options and elaborated on the chosen strategy.
We've discussed this in meetings and the proposal lays out several options, but I didn't see a tracking bug. Filing one now. Options from the proposal:
Don't allow macro code to mutate global state at all. This is probably
overly-restrictive, and may be hard to enforce. There are legitimate use
cases for this like like using
package:logging
.Run each macro application in a completely new isolate. Each application
has its own independent global mutable state. This is permissive in macros
while keeping them isolated, but may be slow.
Reset all static state between macro application executions. If this is
feasible to implement and fast enough, it could work.
Document that mutating global state is a bad practice, but don't block it.
Give no guarantees around static state persistence between macro
applications.
In practice, most macros won't access any global state, so this is harmless.
But if macros do exploit this (deliberately or inadvertently) then it could
force implementations to be stuck with a specific execution order in order
to not break existing code. This is the easiest and fastest solution, but
the least safe.
The text was updated successfully, but these errors were encountered: