Skip to content

Prevent TplRefs from escaping into types of global variables #710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

taminomara
Copy link
Contributor

If a global variable is not annotated, and there's an assignment to it in a generic function, there's a chance that references to generic parameters will end up in its type.

This commit fixes the situation by skipping a type from a particular assignment if it contains generic references.

This is a rather simplistic implementation. We could be smarter about it: it's possible to analyze generics that're referenced from a type and replace them with their bounds:

--- @generic T: Bound
--- @param x T
function foo(x)
   -- After this commit, `globalVar` will have type `undefined` in other files;
   -- instead, we can infer it as `Bound`.
   globalVar = x
end

However, this will require saving bounds to a global context, and lazily replacing them later. I've decided to leave it as is for now.

If a global variable is not annotated, and there's an assignment to it
in a generic function, there's a chance that references to generic parameters
will end up in its type.

This commit fixes the situation by skipping a type from a particular assignment
if it contains generic references.

This is a rather simplistic implementation. We could be smarter about it:
it's possible to analyze generics that're referenced from a type and replace
them with their bounds:

```lua
--- @Generic T: Bound
--- @param x T
function foo(x)
   -- After this commit, `globalVar` will have type `undefined` in other files;
   -- instead, we can infer it as `Bound`.
   globalVar = x
end
```

However, this will require saving bounds to a global context,
and lazily replacing them later. I've decided to leave it as is for now.
@taminomara
Copy link
Contributor Author

taminomara commented Aug 12, 2025

A particularly interesting scenario, where TplRefs from one function get evaluated in another:

--- @generic T
--- @param x T
function foo(x)
    -- Type of `globalVar` will be `TplRef(0)`.
    globalVar = x
end

--- @generic U
--- @param x U
function bar(x)
    -- EmmyLua picks up type of `globalVar`, which is `TplRef(0)`.
    -- Hence, function return type is also `TplRef(0)`.
    -- The signature of `bar` is therefore `bar<U>(x: U) -> U`.
   return globalVar
end

local result = bar(10)
--    ^^^^^^ type of `result` is `integer`

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.

1 participant