Conversation
integer? stores nil as a node flag, so ?? never saw a nil object and copied the optional type unchanged. Treat isOptional() as hasNil so the existing removeOptional() branch runs.
There was a problem hiding this comment.
中文:变更说明:script/vm/operator.lua 中将 hasNil 初始化为 node1:isOptional(),用于在 ?? 运算符推断时剥离左侧类型的可选 ?。需要确认 isOptional() 的语义是否与原先逐对象检测 nil 的逻辑等价,尤其是当 node1 为联合类型(如 integer?|string)或包含 global/type 形式的 nil 时,isOptional() 是否仍能正确返回 true,否则可能引入回归(原本能识别 nil 的场景变为识别不到,导致 ?? 推断结果错误地保留 nil)。建议补充针对联合类型、别名类型以及 nil 通过 global/type 表示等边界情况的测试用例。
English: In script/vm/operator.lua, hasNil is now initialized to node1:isOptional() to strip the optional ? from the left-hand type during ?? inference. Please verify that isOptional() is semantically equivalent to the previous per-object nil detection, especially when node1 is a union type (e.g. integer?|string) or when nil is represented via global/type. If isOptional() does not cover those cases, this could be a regression where previously-detected nil cases are missed and the ?? result incorrectly retains nil. Recommend adding tests for union types, alias types, and nil represented through global/type.
中文:测试用例中通过 config.set(nil, 'Lua.runtime.nonstandardSymbol', { '??' }) 启用 ?? 后,紧接着用 config.set(nil, 'Lua.runtime.nonstandardSymbol', {}) 重置。需确认该重置不会影响后续测试(例如其他依赖 nonstandardSymbol 的测试),且测试框架是否在用例间自动隔离配置;若不会自动隔离,建议使用更明确的保存/恢复方式,避免测试间状态泄漏。
English: In the test, ?? is enabled via config.set(nil, 'Lua.runtime.nonstandardSymbol', { '??' }) and then reset with config.set(nil, 'Lua.runtime.nonstandardSymbol', {}). Please confirm this reset does not affect subsequent tests (e.g. others relying on nonstandardSymbol) and whether the test framework isolates config between cases. If it does not, prefer an explicit save/restore to avoid cross-test state leakage.
中文:测试用例 ---@type integer?\nlocal foo = 1 中,foo 被显式赋值为 1,其推断类型可能已经是 integer 而非 integer?,因此该用例可能无法真正覆盖“剥离可选 ?”的路径。建议改为不赋具体值(如 ---@type integer?\nlocal foo)或使用函数返回值,以确保左侧类型确实带有可选标记,从而有效验证修复。
English: In the test ---@type integer?\nlocal foo = 1, foo is explicitly assigned 1, so its inferred type may already be integer rather than integer?. This test may therefore not actually exercise the "strip optional ?" path. Consider removing the concrete assignment (e.g. ---@type integer?\nlocal foo) or using a function return value so the left-hand type genuinely carries the optional marker, ensuring the fix is properly validated.
中文:变更说明(changelog)描述为“strips the optional ? from the left-hand type”,但代码实现只是将 hasNil 初始值改为 node1:isOptional(),并未显式“剥离”类型标记。请确认最终推断结果确实去除了 ?(即 integer? ?? 2 推断为 integer 而非 integer?),并在实现或注释中体现该行为,避免文档与实现不一致。
English: The changelog states the change "strips the optional ? from the left-hand type", but the implementation only changes the initial value of hasNil to node1:isOptional() and does not explicitly strip the type marker. Please confirm the final inferred result actually removes the ? (i.e. integer? ?? 2 infers integer, not integer?), and reflect this behavior in the implementation or comments to avoid a mismatch between documentation and code.
LuaJIT
??left aninteger?result when the left-hand side was annotatedinteger?. The same expression withoralready inferredinteger.T?stores nil as the node optional flag, not as a nil object ineachObject(). The??arm only sethasNilfrom nil/unknown objects, so it treatedinteger?as definitely non-nil and copied it unchanged.This seeds
hasNilfromnode1:isOptional()so the existingcopy():removeOptional()branch runs. Explicitinteger|nilalready took that branch.What I chose, and the alternative: use
isOptional(), notcopy():setTruthy()likeor, and notisNullable().oralso dropsfalse; LuaJIT??must keep afalseleft-hand side (see the comment on the??case).isNullable()would also treatanyas possibly-nil; this change does not expand that. Happy to switch if a maintainer prefers the broader check.Fixes #3459