HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.16k stars 656 forks source link

GADT type params and inlining #9989

Open nadako opened 3 years ago

nadako commented 3 years ago

I extracted this from tink_sql:

enum ExprData<T> {
    EBinOp<A, B, Ret>(op:BinOp<A, B, Ret>, a:Expr<A>, b:Expr<B>):ExprData<Ret>;
}

enum BinOp<A, B, Ret> {
    Like<T:String>:BinOp<T, T, Bool>;
}

abstract Expr<T>(ExprData<T>) {
    inline function new(e) {
        this = e;
    }

    @:from static function ofData<T>(d:ExprData<T>):Expr<T> {
        return new Expr(d);
    }

    public function like(b:Expr<String>):Expr<Bool> {
        return EBinOp(Like, this, b);
    }
}

My first question is whether the like method should compile? Because within that function this is ExprData<T>, where T is supposed to be an unconstrained type parameter and it doesn't sound like it should pass the T:String constraint check of Like.

Secondly, if we add inline to the ofData method, we actually get an error talking about this:

src/Main.hx:19: characters 23-27 : error: Expr.T should be String
src/Main.hx:19: characters 23-27 : ... have: (ExprData<String>) -> ...
src/Main.hx:19: characters 23-27 : ... want: (ExprData<Expr.T>) -> ...
src/Main.hx:19: characters 23-27 : ... For function argument 'a'

And thirdly, looks like have and want are swapped in this error message?

Simn commented 3 years ago

I have removed some distractions so that we can look at the actual issue:

enum ExprData<T> {
    EBinOp<A>(op : BinOp<A>, a : Expr<A>);
}

enum BinOp<A> {
    Like<T:String>:BinOp<T>;
}

abstract Expr<T>(ExprData<T>) {
    @:from
    static inline function ofData<T>(d:ExprData<T>):Expr<T> {
        return null;
    }

    public function like():Expr<Bool> {
        return EBinOp(Like, this);
    }
}

This seems to be mostly about the @:from cast because it fails properly if we use from ExprData<T> instead.