Skip to content

Commit 67e9d85

Browse files
Add 2-component vector constructor (#1569)
Implement RFC: 2-component vector constructor. This includes 2-component overload for `vector.create` and associated fastcall function, and its type definition. These features are controlled by a new feature flag `LuauVector2Constructor`. Additionally constant folding now supports two components when `LuauVector2Constants` feature flag is set. Note: this work does not include changes to CodeGen. Thus calls to `vector.create` with only two arguments are not natively compiled currently. This is left for future work.
1 parent 24cacc9 commit 67e9d85

10 files changed

+182
-30
lines changed

Analysis/src/EmbeddedBuiltinDefinitions.cpp

+71-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
55
LUAU_FASTFLAG(LuauBufferBitMethods)
6+
LUAU_FASTFLAG(LuauVector2Constructor)
67

78
namespace Luau
89
{
@@ -265,7 +266,7 @@ declare buffer: {
265266
266267
)BUILTIN_SRC";
267268

268-
static const std::string kBuiltinDefinitionVectorSrc_DEPRECATED = R"BUILTIN_SRC(
269+
static const std::string kBuiltinDefinitionVectorSrc_NoExtra_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC(
269270
270271
-- TODO: this will be replaced with a built-in primitive type
271272
declare class vector end
@@ -291,7 +292,33 @@ declare vector: {
291292
292293
)BUILTIN_SRC";
293294

294-
static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
295+
static const std::string kBuiltinDefinitionVectorSrc_NoExtra_DEPRECATED = R"BUILTIN_SRC(
296+
297+
-- TODO: this will be replaced with a built-in primitive type
298+
declare class vector end
299+
300+
declare vector: {
301+
create: @checked (x: number, y: number, z: number?) -> vector,
302+
magnitude: @checked (vec: vector) -> number,
303+
normalize: @checked (vec: vector) -> vector,
304+
cross: @checked (vec1: vector, vec2: vector) -> vector,
305+
dot: @checked (vec1: vector, vec2: vector) -> number,
306+
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
307+
floor: @checked (vec: vector) -> vector,
308+
ceil: @checked (vec: vector) -> vector,
309+
abs: @checked (vec: vector) -> vector,
310+
sign: @checked (vec: vector) -> vector,
311+
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
312+
max: @checked (vector, ...vector) -> vector,
313+
min: @checked (vector, ...vector) -> vector,
314+
315+
zero: vector,
316+
one: vector,
317+
}
318+
319+
)BUILTIN_SRC";
320+
321+
static const std::string kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC(
295322
296323
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
297324
declare class vector
@@ -321,16 +348,56 @@ declare vector: {
321348
322349
)BUILTIN_SRC";
323350

351+
static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
352+
353+
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
354+
declare class vector
355+
x: number
356+
y: number
357+
z: number
358+
end
359+
360+
declare vector: {
361+
create: @checked (x: number, y: number, z: number?) -> vector,
362+
magnitude: @checked (vec: vector) -> number,
363+
normalize: @checked (vec: vector) -> vector,
364+
cross: @checked (vec1: vector, vec2: vector) -> vector,
365+
dot: @checked (vec1: vector, vec2: vector) -> number,
366+
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
367+
floor: @checked (vec: vector) -> vector,
368+
ceil: @checked (vec: vector) -> vector,
369+
abs: @checked (vec: vector) -> vector,
370+
sign: @checked (vec: vector) -> vector,
371+
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
372+
max: @checked (vector, ...vector) -> vector,
373+
min: @checked (vector, ...vector) -> vector,
374+
375+
zero: vector,
376+
one: vector,
377+
}
378+
379+
)BUILTIN_SRC";
380+
324381
std::string getBuiltinDefinitionSource()
325382
{
326383
std::string result = kBuiltinDefinitionLuaSrcChecked;
327384

328385
result += FFlag::LuauBufferBitMethods ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
329386

330387
if (FFlag::LuauVectorDefinitionsExtra)
331-
result += kBuiltinDefinitionVectorSrc;
388+
{
389+
if (FFlag::LuauVector2Constructor)
390+
result += kBuiltinDefinitionVectorSrc;
391+
else
392+
result += kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED;
393+
}
332394
else
333-
result += kBuiltinDefinitionVectorSrc_DEPRECATED;
395+
{
396+
if (FFlag::LuauVector2Constructor)
397+
result += kBuiltinDefinitionVectorSrc_NoExtra_DEPRECATED;
398+
else
399+
result += kBuiltinDefinitionVectorSrc_NoExtra_NoVector2Ctor_DEPRECATED;
400+
}
334401

335402
return result;
336403
}

Compiler/src/BuiltinFolding.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <math.h>
77

8+
LUAU_FASTFLAGVARIABLE(LuauVector2Constants)
89
LUAU_FASTFLAG(LuauCompileMathLerp)
910

1011
namespace Luau
@@ -473,11 +474,13 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count)
473474
break;
474475

475476
case LBF_VECTOR:
476-
if (count >= 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number)
477+
if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
477478
{
478-
if (count == 3)
479+
if (count == 2 && FFlag::LuauVector2Constants)
480+
return cvector(args[0].valueNumber, args[1].valueNumber, 0.0, 0.0);
481+
else if (count == 3 && args[2].type == Constant::Type_Number)
479482
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, 0.0);
480-
else if (count == 4 && args[3].type == Constant::Type_Number)
483+
else if (count == 4 && args[2].type == Constant::Type_Number && args[3].type == Constant::Type_Number)
481484
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, args[3].valueNumber);
482485
}
483486
break;

VM/src/lbuiltins.cpp

+49-13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#endif
2626
#endif
2727

28+
LUAU_FASTFLAG(LuauVector2Constructor)
29+
2830
// luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM
2931
// The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack.
3032
// If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path
@@ -1055,26 +1057,60 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St
10551057

10561058
static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
10571059
{
1058-
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
1060+
if (FFlag::LuauVector2Constructor)
10591061
{
1060-
double x = nvalue(arg0);
1061-
double y = nvalue(args);
1062-
double z = nvalue(args + 1);
1062+
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
1063+
{
1064+
float x = (float)nvalue(arg0);
1065+
float y = (float)nvalue(args);
1066+
float z = 0.0f;
1067+
1068+
if (nparams >= 3)
1069+
{
1070+
if (!ttisnumber(args + 1))
1071+
return -1;
1072+
z = (float)nvalue(args + 1);
1073+
}
10631074

10641075
#if LUA_VECTOR_SIZE == 4
1065-
double w = 0.0;
1066-
if (nparams >= 4)
1067-
{
1068-
if (!ttisnumber(args + 2))
1069-
return -1;
1070-
w = nvalue(args + 2);
1076+
float w = 0.0f;
1077+
if (nparams >= 4)
1078+
{
1079+
if (!ttisnumber(args + 2))
1080+
return -1;
1081+
w = (float)nvalue(args + 2);
1082+
}
1083+
setvvalue(res, x, y, z, w);
1084+
#else
1085+
setvvalue(res, x, y, z, 0.0f);
1086+
#endif
1087+
1088+
return 1;
10711089
}
1072-
setvvalue(res, float(x), float(y), float(z), float(w));
1090+
}
1091+
else
1092+
{
1093+
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
1094+
{
1095+
double x = nvalue(arg0);
1096+
double y = nvalue(args);
1097+
double z = nvalue(args + 1);
1098+
1099+
#if LUA_VECTOR_SIZE == 4
1100+
double w = 0.0;
1101+
if (nparams >= 4)
1102+
{
1103+
if (!ttisnumber(args + 2))
1104+
return -1;
1105+
w = nvalue(args + 2);
1106+
}
1107+
setvvalue(res, float(x), float(y), float(z), float(w));
10731108
#else
1074-
setvvalue(res, float(x), float(y), float(z), 0.0f);
1109+
setvvalue(res, float(x), float(y), float(z), 0.0f);
10751110
#endif
10761111

1077-
return 1;
1112+
return 1;
1113+
}
10781114
}
10791115

10801116
return -1;

VM/src/lveclib.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@
77
#include <math.h>
88

99
LUAU_FASTFLAGVARIABLE(LuauVectorMetatable)
10+
LUAU_FASTFLAGVARIABLE(LuauVector2Constructor)
1011

1112
static int vector_create(lua_State* L)
1213
{
14+
// checking argument count to avoid accepting 'nil' as a valid value
15+
int count = lua_gettop(L);
16+
1317
double x = luaL_checknumber(L, 1);
1418
double y = luaL_checknumber(L, 2);
15-
double z = luaL_checknumber(L, 3);
19+
double z = FFlag::LuauVector2Constructor ? (count >= 3 ? luaL_checknumber(L, 3) : 0.0) : luaL_checknumber(L, 3);
1620

1721
#if LUA_VECTOR_SIZE == 4
18-
// checking argument count to avoid accepting 'nil' as a valid value
19-
double w = lua_gettop(L) >= 4 ? luaL_checknumber(L, 4) : 0.0;
22+
double w = count >= 4 ? luaL_checknumber(L, 4) : 0.0;
2023

2124
lua_pushvector(L, float(x), float(y), float(z), float(w));
2225
#else

bench/micro_tests/test_vector_lib.lua

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local function prequire(name) local success, result = pcall(require, name); return success and result end
2+
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
3+
4+
bench.runCode(function()
5+
for i=1,1000000 do
6+
vector.create(i, 2, 3)
7+
vector.create(i, 2, 3)
8+
vector.create(i, 2, 3)
9+
vector.create(i, 2, 3)
10+
vector.create(i, 2, 3)
11+
end
12+
end, "vector: create")
13+
14+
-- TODO: add more tests

tests/Compiler.test.cpp

+20-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ LUAU_FASTINT(LuauRecursionLimit)
2626
LUAU_FASTFLAG(LuauCompileOptimizeRevArith)
2727
LUAU_FASTFLAG(LuauCompileLibraryConstants)
2828
LUAU_FASTFLAG(LuauVectorFolding)
29+
LUAU_FASTFLAG(LuauVector2Constants)
2930
LUAU_FASTFLAG(LuauCompileDisabledBuiltins)
3031

3132
using namespace Luau;
@@ -5098,36 +5099,49 @@ L0: RETURN R3 -1
50985099
)");
50995100
}
51005101

5101-
TEST_CASE("VectorLiterals")
5102+
TEST_CASE("VectorConstants")
51025103
{
5103-
CHECK_EQ("\n" + compileFunction("return Vector3.new(1, 2, 3)", 0, 2, 0, /*enableVectors*/ true), R"(
5104+
ScopedFastFlag luauVector2Constants{FFlag::LuauVector2Constants, true};
5105+
5106+
CHECK_EQ("\n" + compileFunction("return vector.create(1, 2)", 0, 2, 0), R"(
5107+
LOADK R0 K0 [1, 2, 0]
5108+
RETURN R0 1
5109+
)");
5110+
5111+
CHECK_EQ("\n" + compileFunction("return vector.create(1, 2, 3)", 0, 2, 0), R"(
51045112
LOADK R0 K0 [1, 2, 3]
51055113
RETURN R0 1
51065114
)");
51075115

5108-
CHECK_EQ("\n" + compileFunction("print(Vector3.new(1, 2, 3))", 0, 2, 0, /*enableVectors*/ true), R"(
5116+
CHECK_EQ("\n" + compileFunction("print(vector.create(1, 2, 3))", 0, 2, 0), R"(
51095117
GETIMPORT R0 1 [print]
51105118
LOADK R1 K2 [1, 2, 3]
51115119
CALL R0 1 0
51125120
RETURN R0 0
51135121
)");
51145122

5115-
CHECK_EQ("\n" + compileFunction("print(Vector3.new(1, 2, 3, 4))", 0, 2, 0, /*enableVectors*/ true), R"(
5123+
CHECK_EQ("\n" + compileFunction("print(vector.create(1, 2, 3, 4))", 0, 2, 0), R"(
51165124
GETIMPORT R0 1 [print]
51175125
LOADK R1 K2 [1, 2, 3, 4]
51185126
CALL R0 1 0
51195127
RETURN R0 0
51205128
)");
51215129

5122-
CHECK_EQ("\n" + compileFunction("return Vector3.new(0, 0, 0), Vector3.new(-0, 0, 0)", 0, 2, 0, /*enableVectors*/ true), R"(
5130+
CHECK_EQ("\n" + compileFunction("return vector.create(0, 0, 0), vector.create(-0, 0, 0)", 0, 2, 0), R"(
51235131
LOADK R0 K0 [0, 0, 0]
51245132
LOADK R1 K1 [-0, 0, 0]
51255133
RETURN R0 2
51265134
)");
51275135

5128-
CHECK_EQ("\n" + compileFunction("return type(Vector3.new(0, 0, 0))", 0, 2, 0, /*enableVectors*/ true), R"(
5136+
CHECK_EQ("\n" + compileFunction("return type(vector.create(0, 0, 0))", 0, 2, 0), R"(
51295137
LOADK R0 K0 ['vector']
51305138
RETURN R0 1
5139+
)");
5140+
5141+
// test legacy constructor
5142+
CHECK_EQ("\n" + compileFunction("return Vector3.new(1, 2, 3)", 0, 2, 0, /*enableVectors*/ true), R"(
5143+
LOADK R0 K0 [1, 2, 3]
5144+
RETURN R0 1
51315145
)");
51325146
}
51335147

tests/Conformance.test.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
3939
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
4040
LUAU_FASTFLAG(LuauVectorLibNativeDot)
4141
LUAU_FASTFLAG(LuauVectorMetatable)
42+
LUAU_FASTFLAG(LuauVector2Constructor)
4243
LUAU_FASTFLAG(LuauBufferBitMethods)
4344
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
4445

@@ -896,6 +897,7 @@ TEST_CASE("VectorLibrary")
896897
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
897898
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
898899
ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true};
900+
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
899901

900902
lua_CompileOptions copts = defaultOptions();
901903

@@ -986,6 +988,7 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
986988

987989
TEST_CASE("Types")
988990
{
991+
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
989992
ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, false}; // waiting for math.lerp to be added to embedded type definitions
990993

991994
runConformance(

tests/Fixture.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
static const char* mainModuleName = "MainModule";
2626

2727
LUAU_FASTFLAG(LuauSolverV2);
28+
LUAU_FASTFLAG(LuauVector2Constructor)
2829
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
2930

3031
LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests);
@@ -580,6 +581,8 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool
580581
BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete)
581582
: Fixture(prepareAutocomplete)
582583
{
584+
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
585+
583586
Luau::unfreeze(frontend.globals.globalTypes);
584587
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
585588

tests/NonStrictTypeChecker.test.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <iostream>
1616

1717
LUAU_FASTFLAG(LuauCountSelfCallsNonstrict)
18+
LUAU_FASTFLAG(LuauVector2Constructor)
1819

1920
using namespace Luau;
2021

@@ -581,7 +582,8 @@ buffer.readi8(b, 0)
581582

582583
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
583584
{
584-
ScopedFastFlag sff{FFlag::LuauCountSelfCallsNonstrict, true};
585+
ScopedFastFlag luauCountSelfCallsNonstrict{FFlag::LuauCountSelfCallsNonstrict, true};
586+
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
585587

586588
Luau::unfreeze(frontend.globals.globalTypes);
587589
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);

tests/conformance/vector_library.lua

+7
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ function ecall(fn, ...)
1111
end
1212

1313
-- make sure we cover both builtin and C impl
14+
assert(vector.create(1, 2) == vector.create("1", "2"))
1415
assert(vector.create(1, 2, 4) == vector.create("1", "2", "4"))
1516

17+
-- 'create'
18+
local v12 = vector.create(1, 2)
19+
local v123 = vector.create(1, 2, 3)
20+
assert(v12.x == 1 and v12.y == 2 and v12.z == 0)
21+
assert(v123.x == 1 and v123.y == 2 and v123.z == 3)
22+
1623
-- testing 'dot' with error handling and different call kinds to mostly check details in the codegen
1724
assert(vector.dot(vector.create(1, 2, 4), vector.create(5, 6, 7)) == 45)
1825
assert(ecall(function() vector.dot(vector.create(1, 2, 4)) end) == "missing argument #2 to 'dot' (vector expected)")

0 commit comments

Comments
 (0)