mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
9c588be16d
# What's changed? * Check interrupt handler inside the pattern match engine to eliminate potential for programs to hang during string library function execution. * Allow iteration over table properties to pass the old type solver. ### Native Code Generation * Use in-place memory operands for math library operations on x64. * Replace opaque bools with separate enum classes in IrDump to improve code maintainability. * Translate operations on inferred vectors to IR. * Enable support for debugging native-compiled functions in Roblox Studio. ### New Type Solver * Rework type inference for boolean and string literals to introduce bounded free types (bounded below by the singleton type, and above by the primitive type) and reworked primitive type constraint to decide which is the appropriate type for the literal. * Introduce `FunctionCheckConstraint` to handle bidirectional typechecking for function calls, pushing the expected parameter types from the function onto the arguments. * Introduce `union` and `intersect` type families to compute deferred simplified unions and intersections to be employed by the constraint generation logic in the new solver. * Implement support for expanding the domain of local types in `Unifier2`. * Rework type inference for iteration variables bound by for in loops to use local types. * Change constraint blocking logic to use a set to prevent accidental re-blocking. * Add logic to detect missing return statements in functions. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
321 lines
6.4 KiB
Lua
321 lines
6.4 KiB
Lua
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
print("testing native code generation")
|
|
|
|
assert((function(x, y)
|
|
-- trigger a linear sequence
|
|
local t1 = x + 2
|
|
local t2 = x - 7
|
|
|
|
local a = x * 10
|
|
local b = a + 1
|
|
a = y -- update 'a' version
|
|
local t = {} -- call to allocate table forces a spill
|
|
local c = x * 10
|
|
return c, b, t, t1, t2
|
|
end)(5, 10) == 50)
|
|
|
|
assert((function(x)
|
|
local oops -- split to prevent inlining
|
|
function oops()
|
|
end
|
|
|
|
-- x is checked to be a number here; we can not execute a reentry from oops() because optimizer assumes this holds until return
|
|
local y = math.abs(x)
|
|
oops()
|
|
return y * x
|
|
end)("42") == 1764)
|
|
|
|
local function fuzzfail1(...)
|
|
repeat
|
|
_ = nil
|
|
until not {}
|
|
for _ in ... do
|
|
for l0=_,_ do
|
|
end
|
|
return
|
|
end
|
|
end
|
|
|
|
local function fuzzfail2()
|
|
local _
|
|
do
|
|
repeat
|
|
_ = typeof(_),{_=_,}
|
|
_ = _(_._)
|
|
until _
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail2) == false)
|
|
|
|
local function fuzzfail3()
|
|
function _(...)
|
|
_({_,_,true,},{...,},_,not _)
|
|
end
|
|
_()
|
|
end
|
|
|
|
assert(pcall(fuzzfail3) == false)
|
|
|
|
local function fuzzfail4()
|
|
local _ = setmetatable({},setmetatable({_=_,},_))
|
|
return _(_:_())
|
|
end
|
|
|
|
assert(pcall(fuzzfail4) == false)
|
|
|
|
local function fuzzfail5()
|
|
local _ = bit32.band
|
|
_(_(_,0),_)
|
|
_(_,_)
|
|
end
|
|
|
|
assert(pcall(fuzzfail5) == false)
|
|
|
|
local function fuzzfail6(_)
|
|
return bit32.extract(_,671088640,_)
|
|
end
|
|
|
|
assert(pcall(fuzzfail6, 1) == false)
|
|
|
|
local function fuzzfail7(_)
|
|
return bit32.extract(_,_,671088640)
|
|
end
|
|
|
|
assert(pcall(fuzzfail7, 1) == false)
|
|
|
|
local function fuzzfail8(...)
|
|
local _ = _,_
|
|
_.n0,_,_,_,_,_,_,_,_._,_,_,_[...],_,_,_ = nil
|
|
_,n0,_,_,_,_,_,_,_,_,l0,_,_,_,_ = nil
|
|
function _()
|
|
end
|
|
_._,_,_,_,_,_,_,_,_,_,_[...],_,n0[l0],_ = nil
|
|
_[...],_,_,_,_,_,_,_,_()[_],_,_,_,_,_ = _(),...
|
|
end
|
|
|
|
assert(pcall(fuzzfail8) == false)
|
|
|
|
local function fuzzfail9()
|
|
local _ = bit32.bor
|
|
local x = _(_(_,_),_(_,_),_(-16834560,_),_(_(- _,-2130706432)),- _),_(_(_,_),_(-16834560,-2130706432))
|
|
end
|
|
|
|
assert(pcall(fuzzfail9) == false)
|
|
|
|
local function fuzzfail10()
|
|
local _
|
|
_ = false,if _ then _ else _
|
|
_ = not _
|
|
l0,_[l0] = not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail10) == false)
|
|
|
|
local function fuzzfail11(x, ...)
|
|
return bit32.arshift(bit32.bnot(x),(...))
|
|
end
|
|
|
|
assert(fuzzfail11(0xffff0000, 8) == 0xff)
|
|
|
|
local function fuzzfail12()
|
|
_,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail12) == true)
|
|
|
|
local function fuzzfail13()
|
|
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail13) == true)
|
|
|
|
local function fuzzfail14()
|
|
for l0=771751936,_ do
|
|
for l0=771751936,0 do
|
|
while 538970624 do
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail14) == false)
|
|
|
|
local function fuzzfail15()
|
|
local a
|
|
if a then
|
|
repeat until a
|
|
else
|
|
local b = `{a}`
|
|
a = nil
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail15) == true)
|
|
|
|
local function fuzzfail16()
|
|
_ = {[{[2]=77,_=_,[2]=_,}]=not _,}
|
|
_ = {77,[2]=11008,[2]=_,[0]=_,}
|
|
end
|
|
|
|
assert(pcall(fuzzfail16) == true)
|
|
|
|
local function fuzzfail17()
|
|
return bit32.extract(1293942816,1293942816)
|
|
end
|
|
|
|
assert(pcall(fuzzfail17) == false)
|
|
|
|
local function fuzzfail18()
|
|
return bit32.extract(7890276,0)
|
|
end
|
|
|
|
assert(pcall(fuzzfail18) == true)
|
|
assert(fuzzfail18() == 0)
|
|
|
|
local function arraySizeInv1()
|
|
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
|
|
|
table.insert(t, 3)
|
|
|
|
return t[10]
|
|
end
|
|
|
|
assert(arraySizeInv1() == true)
|
|
|
|
local function arraySizeInv2()
|
|
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
|
|
|
local u = {a = t}
|
|
table.insert(u.a, 3) -- aliased modifiction of 't' register through other value
|
|
|
|
return t[10]
|
|
end
|
|
|
|
assert(arraySizeInv2() == true)
|
|
|
|
local function nilInvalidatesSlot()
|
|
local function tabs()
|
|
local t = { x=1, y=2, z=3 }
|
|
setmetatable(t, { __index = function(t, k) return 42 end })
|
|
return t, t
|
|
end
|
|
|
|
local t1, t2 = tabs()
|
|
|
|
for i=1,2 do
|
|
local a = t1.x
|
|
t2.x = nil
|
|
local b = t1.x
|
|
t2.x = 1
|
|
assert(a == 1 and b == 42)
|
|
end
|
|
end
|
|
|
|
nilInvalidatesSlot()
|
|
|
|
local function arraySizeOpt1(a)
|
|
a[1] += 2
|
|
a[1] *= 3
|
|
|
|
table.insert(a, 3)
|
|
table.insert(a, 4)
|
|
table.insert(a, 5)
|
|
table.insert(a, 6)
|
|
|
|
a[1] += 4
|
|
a[1] *= 5
|
|
|
|
return a[1] + a[5]
|
|
end
|
|
|
|
assert(arraySizeOpt1({1}) == 71)
|
|
|
|
local function arraySizeOpt2(a, i)
|
|
a[i] += 2
|
|
a[i] *= 3
|
|
|
|
table.insert(a, 3)
|
|
table.insert(a, 4)
|
|
table.insert(a, 5)
|
|
table.insert(a, 6)
|
|
|
|
a[i] += 4
|
|
a[i] *= 5
|
|
|
|
return a[i] + a[5]
|
|
end
|
|
|
|
assert(arraySizeOpt2({1}, 1) == 71)
|
|
|
|
function deadLoopBody(n)
|
|
local r = 0
|
|
if n and false then
|
|
for i = 1, n do
|
|
r += 1
|
|
end
|
|
end
|
|
return r
|
|
end
|
|
|
|
assert(deadLoopBody(5) == 0)
|
|
|
|
function arrayIndexingSpecialNumbers1(a, b, c)
|
|
local arr = table.create(100000)
|
|
arr[a] = 9
|
|
arr[b-1] = 80
|
|
arr[b] = 700
|
|
arr[b+1] = 6000
|
|
arr[c-1] = 50000
|
|
arr[c] = 400000
|
|
arr[c+1] = 3000000
|
|
|
|
return arr[1] + arr[255] + arr[256] + arr[257] + arr[65535] + arr[65536] + arr[65537]
|
|
end
|
|
|
|
assert(arrayIndexingSpecialNumbers1(1, 256, 65536) == 3456789)
|
|
|
|
function loopIteratorProtocol(a, t)
|
|
local sum = 0
|
|
|
|
do
|
|
local a, b, c, d, e, f, g = {}, {}, {}, {}, {}, {}, {}
|
|
end
|
|
|
|
for k, v in ipairs(t) do
|
|
if k == 10 then sum += math.abs('-8') end
|
|
|
|
sum += k
|
|
end
|
|
|
|
return sum
|
|
end
|
|
|
|
assert(loopIteratorProtocol(0, table.create(100, 5)) == 5058)
|
|
|
|
local function vec3compsum(a: vector)
|
|
return a.X + a.Y + a.Z
|
|
end
|
|
|
|
assert(vec3compsum(vector(1, 2, 4)) == 7.0)
|
|
|
|
local function vec3add(a: vector, b: vector) return a + b end
|
|
local function vec3sub(a: vector, b: vector) return a - b end
|
|
local function vec3mul(a: vector, b: vector) return a * b end
|
|
local function vec3div(a: vector, b: vector) return a / b end
|
|
local function vec3neg(a: vector) return -a end
|
|
|
|
assert(vec3add(vector(10, 20, 40), vector(1, 0, 2)) == vector(11, 20, 42))
|
|
assert(vec3sub(vector(10, 20, 40), vector(1, 0, 2)) == vector(9, 20, 38))
|
|
assert(vec3mul(vector(10, 20, 40), vector(1, 0, 2)) == vector(10, 0, 80))
|
|
assert(vec3div(vector(10, 20, 40), vector(1, 0, 2)) == vector(10, math.huge, 20))
|
|
assert(vec3neg(vector(10, 20, 40)) == vector(-10, -20, -40))
|
|
|
|
local function vec3mulnum(a: vector, b: number) return a * b end
|
|
local function vec3mulconst(a: vector) return a * 4 end
|
|
|
|
assert(vec3mulnum(vector(10, 20, 40), 4) == vector(40, 80, 160))
|
|
assert(vec3mulconst(vector(10, 20, 40), 4) == vector(40, 80, 160))
|
|
|
|
return('OK')
|