Skip to content

Commit

Permalink
make v is T and v or e imply not-T for expression e
Browse files Browse the repository at this point in the history
Only for "truthy types" (i.e. all except boolean and nil).

Closes #878.
  • Loading branch information
hishamhm committed Jan 4, 2025
1 parent 8d9a26f commit 5631a99
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
15 changes: 15 additions & 0 deletions spec/lang/inference/or_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

local util = require("spec.util")

describe("inference in 'or' expressions", function()
it("`v is T and v or _` for a truthy T infers that _ is of type not-T (#878)", util.check([[
local record R end
local function convert(_: string): R end
local u: string | R
local _r: R = u is R
and u
or convert(u)
]]))
end)
12 changes: 12 additions & 0 deletions tl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12532,6 +12532,18 @@ self:expand_type(node, values, elements) })
self:apply_facts(node, node.e1.known)
elseif node.op.op == "or" then
self:apply_facts(node, facts_not(node, node.e1.known))


if node.e1.kind == "op" and node.e1.op.op == "and" and
node.e1.e1.kind == "op" and node.e1.e1.op.op == "is" and
node.e1.e2.kind == "variable" and
node.e1.e2.tk == node.e1.e1.e1.tk and
node.e1.e1.e2.casttype.typename ~= "boolean" and
node.e1.e1.e2.casttype.typename ~= "nil" then

self:apply_facts(node, facts_not(node, IsFact({ var = node.e1.e1.e1.tk, typ = node.e1.e1.e2.casttype, w = node })))
end

elseif node.op.op == "@funcall" then
if e1type.typename == "generic" then
e1type = self:apply_generic(node, e1type)
Expand Down
12 changes: 12 additions & 0 deletions tl.tl
Original file line number Diff line number Diff line change
Expand Up @@ -12532,6 +12532,18 @@ do
self:apply_facts(node, node.e1.known)
elseif node.op.op == "or" then
self:apply_facts(node, facts_not(node, node.e1.known))

-- special-case `v is T and v or _` when T is a truthy type
if node.e1.kind == "op" and node.e1.op.op == "and"
and node.e1.e1.kind == "op" and node.e1.e1.op.op == "is"
and node.e1.e2.kind == "variable"
and node.e1.e2.tk == node.e1.e1.e1.tk
and node.e1.e1.e2.casttype.typename ~= "boolean"
and node.e1.e1.e2.casttype.typename ~= "nil"
then
self:apply_facts(node, facts_not(node, IsFact { var = node.e1.e1.e1.tk, typ = node.e1.e1.e2.casttype, w = node }))
end

elseif node.op.op == "@funcall" then
if e1type is GenericType then
e1type = self:apply_generic(node, e1type)
Expand Down

0 comments on commit 5631a99

Please sign in to comment.