Hi Jochen,

as a precursor: Besides it being really helpful to us, I see this (seemingly innocuous) NULL_TO macro spike as a general check how well existing Groovy macro functionality works in practice. To me, the goal would be that adding a Groovy macro to a project should be similar and nearly as simple as implementing a few methods in a helper class, and far below the level of effort and required understanding of build phases and inner workings of the Groovy compiler to implement a (local) transform for the same functionality.

Your post also seems to cut off abpruptly... (?)

Comments inline.

Cheers,
mg

Am 25.09.2026 um 04:34 schrieb Jochen Theodorou via dev:
On 9/24/26 23:07, MG wrote:
 1. I checked today, and INFERRED_TYPE unfortunately does not seem to be
    filled at this point, in fact all the metaDataMap|s I checked on
    different Expression|s that appear in my test code were null...

yes, I mentioned that may be the case. The macros run before this information is added

Yea, I was confirming your suspicion (could have said "does /in fact/ not //seem to be filled" to make that clearer). Btw, would supporting to be able to choose in what compile phase a macro gets executed make sense in your eyes ?

<zip>


         1. To get around this, I introduced a
            simple NullToMacroWrapper helper class which stores the
            result, with an overridden asBoolean method that checks its
            stored value against null, i.e.:
             1. wrapInNullToMacroWrapper(exp0) ?: exp1
         2. ...but that helper class (which currently is not generic, so
            it can resuse a thread local singleton) is exactly what
            requires the cast (as I saw when rechecking the ternary
            operator NULL_TO  version).

I don't think macros are a good fit for this

Even if one does not expect the RHS to be cast to the LHS type (see below) ... ?-)


 4. So as far as I can see either:
     1. There is a way to store the exp0 evaluation result inside the
        macro, then the rest is trivial.

temporary variables maybe, but frankly that does not look like it will work well as the right side of an assignment/declaration

Are you referring to the local transform approach here ?

Otherwise: How would I introduce a local variable inside a macro (unfortunately Groovy does not accept something like:  final x = (final h=exp0(); h !== null ? h : exp1()) - which, on second thought, would not work anyway, since the local variable h would have type Object, so an explicit cast would again be needed when using NULL_TO under static compilation...).

(As a general remark: Besides being more typed, I have always felt that it would be nice for more things in Groovy to also be usable as an expression).


     2. There is a way to get the right type to do the cast of the Elvis
        operator based result.

the "right" type is the left side type I assume, which you have no access to in the macro

No, instead it should be WideningCategories.lowestUpperBound of the NULL_TO arguments (which is what the ternary operator based implementation seems to do), and compilation should fail if the RHS is not is not assignment compatible with the LHS.


     3. Turning NullToMacroWrapper into a generic class fixes the cast
        problem - but this approach would create (short lived, i.e. gen
        zero) NullToMacroWrapper instances on the heap...

I thought that also created a casting problem?

I had not tried the generic variety when I wrote that, since generic types are so ethereal in Groovy/Java, static compilation of generics in Groovy can sometimes lead to unexpected/incorrect build errors, and creating new objects (even if they are gen zero on the heap) did not seem like the best way forward. I did try it just now, and alas it fails with "... cannot be cast to type T"/"cannot cast type T to ...." (where "T" is the literal generics argument name in the GenericNullToMacroWrapper class) build errors wherever the NULL_TO macro is used - I assume the type T is bound to its actual type in a later compilation phase ? Or maybe not, since the "unexpected/incorrect build errors" I mentioned above look very similar, but outside of macros one can sometimes get around that by "casting to T" ( which should not be a thing afaiaao, since type T does not exist during runtime, but it satisfies the compiler ;-) ).

macros are basically specialized local transforms. You have a slightly different interface and you can choose the compilation phase... but most importantly you have more context to work with and can place them at other source positions.

So yes, they

they... ?-)



Reply via email to