Repository navigation
refactor(types): share assignment, argument, and return checks - #1313
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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.