diff --git a/changelog.md b/changelog.md index 343a2e40c..62ad11bd7 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ * `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` diff --git a/script/vm/operator.lua b/script/vm/operator.lua index e621d139a..75defe852 100644 --- a/script/vm/operator.lua +++ b/script/vm/operator.lua @@ -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 @@ -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) @@ -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') diff --git a/script/vm/value.lua b/script/vm/value.lua index ce031357d..f487cda45 100644 --- a/script/vm/value.lua +++ b/script/vm/value.lua @@ -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 @@ -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 diff --git a/test/type_inference/common.lua b/test/type_inference/common.lua index c54c82ee1..0d545b3ab 100644 --- a/test/type_inference/common.lua +++ b/test/type_inference/common.lua @@ -146,6 +146,16 @@ TEST 'integer' [[ = 1 << 2 ]] +TEST 'integer' [[ +local a = 1 << 20 +local = a << 1 +]] + +TEST 'integer' [[ +local a = 1 << 20 +local = (a << 1) - 1 +]] + TEST 'unknown' [[ = a .. b ]]