Skip to content

refactor(types): share assignment, argument, and return checks - #1313

Merged
CppCXY merged 1 commit into
EmmyLuaLs:mainfrom
lewis6991:rewrite-shared-compatibility
Oct 20, 2026
Merged

CppCXY merged 1 commit into
EmmyLuaLs:mainfrom
lewis6991:rewrite-shared-compatibility

Conversation

@lewis6991

Copy link
Copy Markdown
Collaborator

Assignments, arguments, and returns use different rules to decide whether a value fits an expected type. Local exceptions duplicate logic and can accept invalid values. A union can pass even when some of its possible values do not fit.

For example, these three uses must all fail because value can be an integer:

---@param value string | integer
---@param consume fun(text: string)
---@return string
local function example(value, consume)
    ---@type string
    local text = value -- error: assignment
    consume(value)     -- error: argument
    return value       -- error: return
end

Use one directional compatibility check for all three contexts. Every source union member must fit at least one target alternative. Check callbacks by the arguments they must accept and the results they can produce, including optional and variadic slots. Keep strict subtype checks separate for casts and conditional types. This gives the checkers one place to define and enforce value compatibility.

The shared check needs accurate input types. Preserve integer results and complete return sequences, including an expanding final call. Retain object fields from annotations and resolve inherited index types with their generic arguments and key domains. Widen inferred literals for reassignment, while keeping declared literal types exact. These changes let valid code pass without local checker exceptions.

Member guards also need to describe the correct object. Track object identity through local aliases and discard a guard after a write can change the guarded value. This prevents a nil check on one object from being reused for another object or after an affected field changes.

Reuse resolved syntax nodes and cache cast paths to avoid repeated tree walks during flow analysis of long expressions.

Assignments, arguments, and returns use different rules to decide
whether a value fits an expected type. Local exceptions duplicate logic
and can accept invalid values. A union can pass even when some of its
possible values do not fit.

For example, these three uses must all fail because value can be an
integer:

    ---@PARAM value string | integer
    ---@PARAM consume fun(text: string)
    ---@return string
    local function example(value, consume)
        ---@type string
        local text = value -- error: assignment
        consume(value)     -- error: argument
        return value       -- error: return
    end

Use one directional compatibility check for all three contexts. Every
source union member must fit at least one target alternative. Check
callbacks by the arguments they must accept and the results they can
produce, including optional and variadic slots. Keep strict subtype
checks separate for casts and conditional types. This gives the checkers
one place to define and enforce value compatibility.

The shared check needs accurate input types. Preserve integer results
and complete return sequences, including an expanding final call. Retain
object fields from annotations and resolve inherited index types with
their generic arguments and key domains. Widen inferred literals for
reassignment, while keeping declared literal types exact. These changes
let valid code pass without local checker exceptions.

Member guards also need to describe the correct object. Track object
identity through local aliases and discard a guard after a write can
change the guarded value. This prevents a nil check on one object from
being reused for another object or after an affected field changes.

Reuse resolved syntax nodes and cache cast paths to avoid repeated tree
walks during flow analysis of long expressions.

Cover the shared rules across assignments, arguments, and returns, with
regressions for the inference and flow information they use.
@CppCXY
CppCXY merged commit ef11775 into EmmyLuaLs:main Oct 20, 2026
16 of 17 checks passed
@lewis6991
lewis6991 deleted the rewrite-shared-compatibility branch October 20, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants