teal-language / tl

The compiler for Teal, a typed dialect of Lua
MIT License
2.03k stars 101 forks source link

spurious error "cannot resolve a type" with union #695

Closed fperrad closed 9 months ago

fperrad commented 10 months ago
$ cat union.tl 
global function f1(val: string)
    if val is string then  -- warning: val (of type string) is always a string
        print(val)
    else
        error("string expected")
    end
end

global function f2(val: string|number)
    if val is string then
        print(val)
    elseif val is number then  -- warning: val (of type number (inferred at ...)) is always a number
        print(val)
    else  -- ERROR: cannot resolve a type for val here
        error("string or number expected")
    end
end
$ ./tl check union.tl
========================================
2 warnings:
union.tl:2:12: val (of type string) is always a string
union.tl:12:16: val (of type number (inferred at union.tl:12:5)) is always a number
========================================
1 error:
union.tl:14:5: cannot resolve a type for val here
hishamhm commented 10 months ago

The error is not spurious: if the type is A|B, and you have branches already covering cases for both A and B, the third branch is unreachable: there is no valid type inferrable for var in that branch.

lenscas commented 10 months ago

The error is not spurious: if the type is A|B, and you have branches already covering cases for both A and B, the third branch is unreachable: there is no valid type inferrable for var in that branch.

considering that teal uses lua's type function to work with the is syntax, and that every value in teal can be nil regardless of type, shouldn't the type be nil in the else branch?

fperrad commented 10 months ago

after adding a explicit nil in union, tl gives the same kind of error & warnings

$ cat union2.tl 
global function f1(val: string|nil)
    if val is string then  -- warning: val (of type string | nil) is always a string
        print(val)
    else
        error("string expected")
    end
end

global function f2(val: string|number|nil)
    if val is string then
        print(val)
    elseif val is number then  -- warning: val (of type number (inferred at ...)) is always a number
        print(val)
    else  -- ERROR: cannot resolve a type for val here
        error("string or number expected")
    end
end
$ tl check union2.tl 
========================================
2 warnings:
union2.tl:2:12: val (of type string | nil) is always a string
union2.tl:12:16: val (of type number (inferred at union2.tl:12:5)) is always a number
========================================
1 error:
union2.tl:14:5: cannot resolve a type for val here

but when adding a any, tl is happy.

$ cat union3.tl 
global function f1(val: string|any)
    if val is string then
        print(val)
    else
        error("string expected")
    end
end

global function f2(val: string|number|any)
    if val is string then
        print(val)
    elseif val is number then
        print(val)
    else
        error("string or number expected")
    end
end
$ tl check union3.tl 
========================================
Type checked union3.tl
0 errors detected -- you can use:
...
hishamhm commented 10 months ago

considering that teal uses lua's type function to work with the is syntax, and that every value in teal can be nil regardless of type, shouldn't the type be nil in the else branch?

@lenscas fair point! I stand corrected.

hishamhm commented 10 months ago

after adding a explicit nil in union, tl gives the same kind of error & warnings

Because nil is currently implied in every type, X|nil is currently equivalent(-ish?*) to X, so this result doesn't suprise me.

But I agree that the behavior you described in the issue needs changing, at the very least to make the behavior of f1 and f2 consistent, and ideally, to make the else block of f2 infer val to nil.

(* I admit I don't have the precise semantics for that off the top of my head, which is a strong indicator that the current semantics are not ideal — I know, I know, we'll get rid of nil-everywhere eventually...)