fix: type inference for bitwise operators on integer variables#3394
fix: type inference for bitwise operators on integer variables#3394RomanSpector wants to merge 2 commits intoLuaLS:masterfrom
Conversation
Bitwise operators (<<, >>, &, |, ~) on integer-typed variables returned 'unknown' instead of 'integer'. For example: local a = 1 << 20 -- integer = 1048576 (OK) local b = a << 1 -- was: unknown, now: integer = 2097152 Two fixes: - operator.lua: add integer type fallback for bitwise ops when operands are integer-typed but not literal constants (matching existing behavior for +, -, *, % operators) - value.lua: vm.getInteger now traces through getlocal to the local definition's value when the compiled node doesn't preserve the literal, enabling constant folding for chained expressions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request improves type inference for bitwise operators on integer variables and fixes constant value computation for chained bitwise expressions in hover tooltips. Key changes include updating vm.binarySwitch to explicitly set the result type to 'integer' for bitwise operations and modifying vm.getInteger to resolve local values more effectively. Review feedback identifies a logic error in vm.getInteger regarding the handling of union types (mixed integer and non-integer) and suggests refactoring duplicated operand inference logic into a helper function to improve maintainability.
script/vm/value.lua
Outdated
| if hasNonInteger and not result then | ||
| result = nil | ||
| end |
There was a problem hiding this comment.
The logic for handling mixed types appears to be incorrect. If a variable can be both an integer and another non-integer type (a union), getInteger should return nil. With the current change, if an integer value is found (result is set) and a non-integer type is also found (hasNonInteger is true), the condition hasNonInteger and not result will be false. This causes the function to incorrectly return the integer value instead of nil for a union type.
To fix this, result should be cleared if any non-integer type is found, before attempting the recursive lookup.
if hasNonInteger then
result = nil
end
There was a problem hiding this comment.
Done. Changed to unconditionally clear result when hasNonInteger is true, so union types (e.g. integer|string) correctly return nil.
script/vm/operator.lua
Outdated
| local uri = guide.getUri(source) | ||
| local infer1 = vm.getInfer(source[1]) | ||
| local infer2 = vm.getInfer(source[2]) |
There was a problem hiding this comment.
This block of code to get the URI and inferred types of operands is duplicated in several places within this file (e.g., lines 327-329, 343-345, 353-355, and 398-400). To improve maintainability and reduce redundancy, consider extracting this logic into a helper function.
For example, you could define this function at the top of the file:
local function getInfers(source)
return guide.getUri(source), vm.getInfer(source[1]), vm.getInfer(source[2])
endAnd then replace the duplicated blocks with a single call:
local uri, infer1, infer2 = getInfers(source)
There was a problem hiding this comment.
Done. Extracted getOperandInfers(source) helper, replaced all 5 duplicated blocks.
- value.lua: clear result when any non-integer type is found in node, preventing incorrect integer return for union types (integer|string) - operator.lua: extract getOperandInfers() helper to deduplicate URI/infer retrieval across 5 call sites Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Bitwise operators (
<<,>>,&,|,~) on integer-typed variables returnedunknowninstead ofinteger. Constant folding also failed for chained expressions.Before
After
Changes
script/vm/operator.lua: Add integer type fallback for bitwise operators when operands are integer-typed but not literal constants. This matches existing behavior for arithmetic operators (+,-,*,%).script/vm/value.lua:vm.getIntegernow traces throughgetlocalto the local definition's value expression when the compiled node doesn't preserve the literal integer. This enables constant folding for chained bitwise/arithmetic expressions.Test Plan
a << 1and(a << 1) - 1wherea = 1 << 20