MilesCranmer / DispatchDoctor.jl

The dispatch doctor prescribes type stability
Apache License 2.0
128 stars 6 forks source link

Fix macro stacking #23

Closed MilesCranmer closed 1 month ago

MilesCranmer commented 1 month ago

Propagating macros is uber difficult. This took me nearly six hours of just staring at expressions to fix. (I hope you get some use out of this @matthias314!)

Basically this completely refactors the macro propagation functionality to fix some issues. Everything is immutable so there is less spooky action at a distance. And seems like no more issues because of that more robust approach.

How it works is for any macros

@stable @macro1 @macro2 option1 option2 @macro3 ... function f(x)
    x
end

It will aggregate all macros into a stack, like

@macro1
@macro2 option1 option2
@macro3

If it keeps encountering macros, it will keep accumulating them in the stack.

Once it either:

  1. Encounters an expression that is neither a macro or a function. Then it will discard the stack, restart a new stack of macros in a new call to _stabilize_all. Then once that exits, it will send the previous stack of macros back "upwards" in the recursion, at which point the original calls of _stabilize_all can each re-paste those macros back on the code. They know they can do this because the upwards pass is not empty.

  2. Encounters a function. Then it will consume the stack, applying all macros to both the function simulator as well as the normal function.

Note that you can always customize behavior for a particular macro with register_macro! – including having it be pasted on each function (default), only be pasted over the entire begin...end block (DontPropagateMacro), or to prevent @stable from applying at all (IncompatibleMacro).

[!WARNING] Macros which apply to a function and do not have that function in their final argument are incompatible with @stable and may result in it breaking their behavior. This is likely to be a loud breakage but in any case just note it might cause weird bugs. To get around this, either register_macro! on any macros which apply to a function, or (2) declare such blocks @unstable, or (3) don't apply @stable to those parts of code containing those non-standard macros.

coveralls commented 1 month ago

Coverage Status

coverage: 96.679% (-0.4%) from 97.059% when pulling 508845db53b034747577c81a76b3b833e7b88751 on fix-macro-stacking into 1d497fdf68196a90a799682d21041931be91f347 on main.