luau-lang / luau

A fast, small, safe, gradually typed embeddable scripting language derived from Lua
https://luau.org
MIT License
4.05k stars 381 forks source link

Intersections of specific function types and the top function type do not normalize correctly #1401

Open Daw588 opened 1 month ago

Daw588 commented 1 month ago

I have a Main function that will be called twice, and the second time it gets called it will print 64. However the type solver throws unclear type error.

type Func = () -> number

local FuncThatReturnsNumber: Func? = nil

local function GetFuncThatReturnsNumber(): Func
    return function()
        return 64
    end
end

local function Main()
    if FuncThatReturnsNumber then
        local position = FuncThatReturnsNumber() -- TypeError: Cannot call a value of type ((() -> number)?) & (buffer | class | function | number | string | table | thread | true) Luau(1020)
        -- local position: 'a
        print(position)
    else
        FuncThatReturnsNumber = GetFuncThatReturnsNumber()
    end
end

Main() -- Prints nothing
Main() -- Prints 64

This does not happen when the if statement is not wrapped inside a function.

type Func = () -> number

local FuncThatReturnsNumber: Func? = nil

local function GetFuncThatReturnsNumber(): Func
    return function()
        return 64
    end
end

if FuncThatReturnsNumber then
    local position = FuncThatReturnsNumber() -- number
    print(position)
else
    FuncThatReturnsNumber = GetFuncThatReturnsNumber()
end
aatxe commented 1 month ago

The problem here is that intersections of the function type (or unions including it) with individual specific function types do not correctly simplify. So, (() -> number) | nil intersected with ~(false | nil) (which is what the refinement for a truthy test does) doesn't correctly simplify to () -> number. We might be able to fix this sooner, but this will definitely be addressed by the upcoming work on integrated e-graphs (#1285, but there's follow-up integration work that is under way).