haxetink / tink_macro

The macro toolkit
MIT License
57 stars 17 forks source link

BuildCache's treatments for type wrapped by Null #18

Closed kevinresol closed 4 years ago

kevinresol commented 6 years ago
#if macro
import haxe.macro.Context;
import tink.macro.BuildCache;
using tink.MacroApi;
#else
@:genericBuild(Macro.build())
#end
class Wrapper<T> {}

class Macro {
    #if macro
    public static function build() {
        return BuildCache.getType('Wrapper', function(ctx) {
            trace('build');
            var name = ctx.name;
            return macro class $name {}
        });
    }
    #end

    public macro static function test() {

        function t(ct1, ct2) {
            var t1 = macro:Wrapper<$ct1>;
            var t2 = macro:Wrapper<$ct2>;
            trace(Context.unify(t1.toType().sure(), t2.toType().sure()));
        }

        t(macro:Null<Int>, macro:Null<Null<Int>>); // build() run once, trace true
        t(macro:{i:Null<Int>}, macro:{i:Null<Null<Int>>}); // build() run twice, traces false
        return macro null;
    }
}

class Main {
    static function main() {
        Macro.test();
    }
}

In short, build cache uses the same cached type for Null<Int> and Null<Null<Int>> but not when the types are {i:Null<Int>} and {i:Null<Null<Int>>}, and I hope it does.

I am experiencing this issue when trying to use the new tink_json parser with tink web. In the router macro, after some weird transformations,
{@:optional var i:Null<Int>;} becomes {@:optional var i:Null<Null<Int>>;} (actually it is Null-wrapped as many as 5 times) probably due to some back and forth transformation between Type and ComplexType, and at each conversion the type is wrapped by an additional Null.

As a result, build cache generated two types (Parsed0 and Parsed1) and compiler thinks they are incompatible.

back2dos commented 4 years ago

I've shied away from making it the default, but you can pass TypeMap.keepNull as the last argument to BuildCache.getType, in which case it respects nulls.