ark-lang / ark

A compiled systems programming language written in Go using the LLVM framework
https://ark-lang.github.io/
MIT License
677 stars 47 forks source link

Semantic checks of generic functions invalid. #670

Open kiljacken opened 8 years ago

kiljacken commented 8 years ago

Currently the AST visitor checks the actual body with unresolved generic parameters when doing semantic checks. This will cause errors for otherwise correct programs like the following:

func frobnicate<T>(a: T, b: T) -> T {
    return a + b;
}

pub func main() -> int {
    a: u16 = 1;
    b: u16 = 2;
    c: u16 = frobnicate<u16>(a, b);
    return 0;
}

Solving the problem will require introducing a boolean field to the AST visitor for signalling that we want to visit the function variations instead of the actual function body. This part is simple. Actually traversing and substituting types in the function bodies will be a fair bit of work. Below is the new VisitFunction method I created for checking the available information:

func (v *ASTVisitor) VisitFunction(fn *Function) {
    v.EnterScope()

    if v.VisitFunctionVariations && len(fn.Type.GenericParameters) > 0 {
        log.Debugln("parser", "Not visiting generic function as that is unimplemented :P")
        for _, access := range fn.Accesses {
            log.Debugln("parser", "  Variation: %v", access)
            // Here is where we'd somehow traverse the function body while replacing
            // the types within with the ones we get from the access.
        }
    } else {
        if fn.Type.Receiver != nil {
            fn.Receiver = v.Visit(fn.Receiver).(*VariableDecl)
        }

        for idx, param := range fn.Parameters {
            fn.Parameters[idx] = v.Visit(param).(*VariableDecl)
        }

        if fn.Body != nil {
            fn.Body = v.Visit(fn.Body).(*Block)
        }
    }

    v.ExitScope()
}
MovingtoMars commented 8 years ago

This is working as intented IMO. The generic function doesn't know if the + operator is valid on type T. What is needed is a restriction for types that can be used with arithmetic operators.

kiljacken commented 8 years ago

The problem with that is, that our concept of type restrictions is intended to use interfaces. This would mean that operators would have to be implemented as an interface, or we'd have to special case the heck out of this scenario. Not that doing operator overloading as an interface is impossible, it would just require generic interfaces (Which is a patchwork solution for using implicit interfaces instead of explicit traits)

MovingtoMars commented 8 years ago

I just think special-case "interfaces" for + - * / would be fine.

kiljacken commented 8 years ago

Well, we'd need it for all binary operators and some unary operators. This (should be) the complete list:

That's a lot of interfaces to special case, so I'm not so sure.

MovingtoMars commented 8 years ago

As for ! || &&, I don't see any reason for them in generic functions, since really you should just pass a bool, as we don't allow overloading of these.

As for the other ones, how about:

kiljacken commented 8 years ago

We could probably could it down to around 6 pseudo-interfaces, that's still a lot though:

But I guess it's doable