Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<!-- Add all new changes here. They will be moved under a version at release -->
* `CHG` Modified the `ResolveRequire` function to pass the source URI as a third argument.
* `CHG` Improved the output of test failures during development
* `FIX` Fix type inference for bitwise operators (`<<`, `>>`, `&`, `|`, `~`) on integer variables
* `FIX` Fix constant value computation for chained bitwise expressions in hover tooltips

## 3.17.1
`2026-01-20`
Expand Down
17 changes: 14 additions & 3 deletions script/vm/operator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ vm.OP_UNARY_MAP = util.revertMap(unaryMap)
vm.OP_BINARY_MAP = util.revertMap(binaryMap)
vm.OP_OTHER_MAP = util.revertMap(otherMap)

---@param source parser.object
---@return uri, vm.infer, vm.infer
local function getOperandInfers(source)
return guide.getUri(source), vm.getInfer(source[1]), vm.getInfer(source[2])
end

---@param operators parser.object[]
---@param op string
---@param value? parser.object
Expand Down Expand Up @@ -267,6 +273,13 @@ vm.binarySwitch = util.switch()
end
if node then
vm.setNode(source, node)
return
end
-- Bitwise ops on integers always produce integer
local uri, infer1, infer2 = getOperandInfers(source)
if infer1:hasType(uri, 'integer')
and infer2:hasType(uri, 'integer') then
vm.setNode(source, vm.declareGlobal('type', 'integer'))
end
end
end)
Expand Down Expand Up @@ -386,9 +399,7 @@ vm.binarySwitch = util.switch()
[1] = a .. b,
})
else
local uri = guide.getUri(source)
local infer1 = vm.getInfer(source[1])
local infer2 = vm.getInfer(source[2])
local uri, infer1, infer2 = getOperandInfers(source)
if (
infer1:hasType(uri, 'integer')
or infer1:hasType(uri, 'number')
Expand Down
14 changes: 13 additions & 1 deletion script/vm/value.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ function vm.getInteger(v)
end
local node = vm.compileNode(v)
local result
local hasNonInteger = false
for n in node:eachObject() do
if n.type == 'integer' then
if result then
Expand All @@ -135,7 +136,18 @@ function vm.getInteger(v)
end
elseif n.type ~= 'local'
and n.type ~= 'global' then
return nil
hasNonInteger = true
end
end
if hasNonInteger then
result = nil
end
-- If value not found via compiled node, try the local's
-- definition value directly (tracer may not preserve literals)
if result == nil and v.type == 'getlocal' then
local loc = v.node
if loc and loc.value then
return vm.getInteger(loc.value)
end
end
return result
Expand Down
10 changes: 10 additions & 0 deletions test/type_inference/common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ TEST 'integer' [[
<?x?> = 1 << 2
]]

TEST 'integer' [[
local a = 1 << 20
local <?b?> = a << 1
]]

TEST 'integer' [[
local a = 1 << 20
local <?b?> = (a << 1) - 1
]]

TEST 'unknown' [[
<?x?> = a .. b
]]
Expand Down
Loading