mirror of
https://github.com/luau-lang/luau.git
synced 2025-04-07 20:30:53 +01:00
Hey folks, another week means another Luau release! This one features a number of bug fixes in the New Type Solver including improvements to user-defined type functions and a bunch of work to untangle some of the outstanding issues we've been seeing with constraint solving not completing in real world use. We're also continuing to make progress on crashes and other problems that affect the stability of fragment autocomplete, as we work towards delivering consistent, low-latency autocomplete for any editor environment. ## New Type Solver - Fix a bug in user-defined type functions where `print` would incorrectly insert `\1` a number of times. - Fix a bug where attempting to refine an optional generic with a type test will cause a false positive type error (fixes #1666) - Fix a bug where the `refine` type family would not skip over `*no-refine*` discriminants (partial resolution for #1424) - Fix a constraint solving bug where recursive function calls would consistently produce cyclic constraints leading to incomplete or inaccurate type inference. - Implement `readparent` and `writeparent` for class types in user-defined type functions, replacing the incorrectly included `parent` method. - Add initial groundwork (under a debug flag) for eager free type generalization, moving us towards further improvements to constraint solving incomplete errors. ## Fragment Autocomplete - Ease up some assertions to improve stability of mixed-mode use of the two type solvers (i.e. using Fragment Autocomplete on a type graph originally produced by the old type solver) - Resolve a bug with type compatibility checks causing internal compiler errors in autocomplete. ## Lexer and Parser - Improve the accuracy of the roundtrippable AST parsing mode by correctly placing closing parentheses on type groupings. - Add a getter for `offset` in the Lexer by @aduermael in #1688 - Add a second entry point to the parser to parse an expression, `parseExpr` ## Internal Contributors Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Menarul Alam <malam@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
564 lines
16 KiB
C++
564 lines
16 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "Builtins.h"
|
|
|
|
#include "Luau/Bytecode.h"
|
|
#include "Luau/Compiler.h"
|
|
#include "Luau/Lexer.h"
|
|
|
|
#include <array>
|
|
|
|
namespace Luau
|
|
{
|
|
namespace Compile
|
|
{
|
|
|
|
Builtin getBuiltin(AstExpr* node, const DenseHashMap<AstName, Global>& globals, const DenseHashMap<AstLocal*, Variable>& variables)
|
|
{
|
|
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
|
{
|
|
const Variable* v = variables.find(expr->local);
|
|
|
|
return v && !v->written && v->init ? getBuiltin(v->init, globals, variables) : Builtin();
|
|
}
|
|
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
|
|
{
|
|
if (AstExprGlobal* object = expr->expr->as<AstExprGlobal>())
|
|
{
|
|
return getGlobalState(globals, object->name) == Global::Default ? Builtin{object->name, expr->index} : Builtin();
|
|
}
|
|
else
|
|
{
|
|
return Builtin();
|
|
}
|
|
}
|
|
else if (AstExprGlobal* expr = node->as<AstExprGlobal>())
|
|
{
|
|
return getGlobalState(globals, expr->name) == Global::Default ? Builtin{AstName(), expr->name} : Builtin();
|
|
}
|
|
else
|
|
{
|
|
return Builtin();
|
|
}
|
|
}
|
|
|
|
static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
|
|
{
|
|
if (builtin.isGlobal("assert"))
|
|
return LBF_ASSERT;
|
|
|
|
if (builtin.isGlobal("type"))
|
|
return LBF_TYPE;
|
|
|
|
if (builtin.isGlobal("typeof"))
|
|
return LBF_TYPEOF;
|
|
|
|
if (builtin.isGlobal("rawset"))
|
|
return LBF_RAWSET;
|
|
if (builtin.isGlobal("rawget"))
|
|
return LBF_RAWGET;
|
|
if (builtin.isGlobal("rawequal"))
|
|
return LBF_RAWEQUAL;
|
|
if (builtin.isGlobal("rawlen"))
|
|
return LBF_RAWLEN;
|
|
|
|
if (builtin.isGlobal("unpack"))
|
|
return LBF_TABLE_UNPACK;
|
|
|
|
if (builtin.isGlobal("select"))
|
|
return LBF_SELECT_VARARG;
|
|
|
|
if (builtin.isGlobal("getmetatable"))
|
|
return LBF_GETMETATABLE;
|
|
if (builtin.isGlobal("setmetatable"))
|
|
return LBF_SETMETATABLE;
|
|
|
|
if (builtin.isGlobal("tonumber"))
|
|
return LBF_TONUMBER;
|
|
if (builtin.isGlobal("tostring"))
|
|
return LBF_TOSTRING;
|
|
|
|
if (builtin.object == "math")
|
|
{
|
|
if (builtin.method == "abs")
|
|
return LBF_MATH_ABS;
|
|
if (builtin.method == "acos")
|
|
return LBF_MATH_ACOS;
|
|
if (builtin.method == "asin")
|
|
return LBF_MATH_ASIN;
|
|
if (builtin.method == "atan2")
|
|
return LBF_MATH_ATAN2;
|
|
if (builtin.method == "atan")
|
|
return LBF_MATH_ATAN;
|
|
if (builtin.method == "ceil")
|
|
return LBF_MATH_CEIL;
|
|
if (builtin.method == "cosh")
|
|
return LBF_MATH_COSH;
|
|
if (builtin.method == "cos")
|
|
return LBF_MATH_COS;
|
|
if (builtin.method == "deg")
|
|
return LBF_MATH_DEG;
|
|
if (builtin.method == "exp")
|
|
return LBF_MATH_EXP;
|
|
if (builtin.method == "floor")
|
|
return LBF_MATH_FLOOR;
|
|
if (builtin.method == "fmod")
|
|
return LBF_MATH_FMOD;
|
|
if (builtin.method == "frexp")
|
|
return LBF_MATH_FREXP;
|
|
if (builtin.method == "ldexp")
|
|
return LBF_MATH_LDEXP;
|
|
if (builtin.method == "log10")
|
|
return LBF_MATH_LOG10;
|
|
if (builtin.method == "log")
|
|
return LBF_MATH_LOG;
|
|
if (builtin.method == "max")
|
|
return LBF_MATH_MAX;
|
|
if (builtin.method == "min")
|
|
return LBF_MATH_MIN;
|
|
if (builtin.method == "modf")
|
|
return LBF_MATH_MODF;
|
|
if (builtin.method == "pow")
|
|
return LBF_MATH_POW;
|
|
if (builtin.method == "rad")
|
|
return LBF_MATH_RAD;
|
|
if (builtin.method == "sinh")
|
|
return LBF_MATH_SINH;
|
|
if (builtin.method == "sin")
|
|
return LBF_MATH_SIN;
|
|
if (builtin.method == "sqrt")
|
|
return LBF_MATH_SQRT;
|
|
if (builtin.method == "tanh")
|
|
return LBF_MATH_TANH;
|
|
if (builtin.method == "tan")
|
|
return LBF_MATH_TAN;
|
|
if (builtin.method == "clamp")
|
|
return LBF_MATH_CLAMP;
|
|
if (builtin.method == "sign")
|
|
return LBF_MATH_SIGN;
|
|
if (builtin.method == "round")
|
|
return LBF_MATH_ROUND;
|
|
if (builtin.method == "lerp")
|
|
return LBF_MATH_LERP;
|
|
}
|
|
|
|
if (builtin.object == "bit32")
|
|
{
|
|
if (builtin.method == "arshift")
|
|
return LBF_BIT32_ARSHIFT;
|
|
if (builtin.method == "band")
|
|
return LBF_BIT32_BAND;
|
|
if (builtin.method == "bnot")
|
|
return LBF_BIT32_BNOT;
|
|
if (builtin.method == "bor")
|
|
return LBF_BIT32_BOR;
|
|
if (builtin.method == "bxor")
|
|
return LBF_BIT32_BXOR;
|
|
if (builtin.method == "btest")
|
|
return LBF_BIT32_BTEST;
|
|
if (builtin.method == "extract")
|
|
return LBF_BIT32_EXTRACT;
|
|
if (builtin.method == "lrotate")
|
|
return LBF_BIT32_LROTATE;
|
|
if (builtin.method == "lshift")
|
|
return LBF_BIT32_LSHIFT;
|
|
if (builtin.method == "replace")
|
|
return LBF_BIT32_REPLACE;
|
|
if (builtin.method == "rrotate")
|
|
return LBF_BIT32_RROTATE;
|
|
if (builtin.method == "rshift")
|
|
return LBF_BIT32_RSHIFT;
|
|
if (builtin.method == "countlz")
|
|
return LBF_BIT32_COUNTLZ;
|
|
if (builtin.method == "countrz")
|
|
return LBF_BIT32_COUNTRZ;
|
|
if (builtin.method == "byteswap")
|
|
return LBF_BIT32_BYTESWAP;
|
|
}
|
|
|
|
if (builtin.object == "string")
|
|
{
|
|
if (builtin.method == "byte")
|
|
return LBF_STRING_BYTE;
|
|
if (builtin.method == "char")
|
|
return LBF_STRING_CHAR;
|
|
if (builtin.method == "len")
|
|
return LBF_STRING_LEN;
|
|
if (builtin.method == "sub")
|
|
return LBF_STRING_SUB;
|
|
}
|
|
|
|
if (builtin.object == "table")
|
|
{
|
|
if (builtin.method == "insert")
|
|
return LBF_TABLE_INSERT;
|
|
if (builtin.method == "unpack")
|
|
return LBF_TABLE_UNPACK;
|
|
}
|
|
|
|
if (builtin.object == "buffer")
|
|
{
|
|
if (builtin.method == "readi8")
|
|
return LBF_BUFFER_READI8;
|
|
if (builtin.method == "readu8")
|
|
return LBF_BUFFER_READU8;
|
|
if (builtin.method == "writei8" || builtin.method == "writeu8")
|
|
return LBF_BUFFER_WRITEU8;
|
|
if (builtin.method == "readi16")
|
|
return LBF_BUFFER_READI16;
|
|
if (builtin.method == "readu16")
|
|
return LBF_BUFFER_READU16;
|
|
if (builtin.method == "writei16" || builtin.method == "writeu16")
|
|
return LBF_BUFFER_WRITEU16;
|
|
if (builtin.method == "readi32")
|
|
return LBF_BUFFER_READI32;
|
|
if (builtin.method == "readu32")
|
|
return LBF_BUFFER_READU32;
|
|
if (builtin.method == "writei32" || builtin.method == "writeu32")
|
|
return LBF_BUFFER_WRITEU32;
|
|
if (builtin.method == "readf32")
|
|
return LBF_BUFFER_READF32;
|
|
if (builtin.method == "writef32")
|
|
return LBF_BUFFER_WRITEF32;
|
|
if (builtin.method == "readf64")
|
|
return LBF_BUFFER_READF64;
|
|
if (builtin.method == "writef64")
|
|
return LBF_BUFFER_WRITEF64;
|
|
}
|
|
|
|
if (builtin.object == "vector")
|
|
{
|
|
if (builtin.method == "create")
|
|
return LBF_VECTOR;
|
|
if (builtin.method == "magnitude")
|
|
return LBF_VECTOR_MAGNITUDE;
|
|
if (builtin.method == "normalize")
|
|
return LBF_VECTOR_NORMALIZE;
|
|
if (builtin.method == "cross")
|
|
return LBF_VECTOR_CROSS;
|
|
if (builtin.method == "dot")
|
|
return LBF_VECTOR_DOT;
|
|
if (builtin.method == "floor")
|
|
return LBF_VECTOR_FLOOR;
|
|
if (builtin.method == "ceil")
|
|
return LBF_VECTOR_CEIL;
|
|
if (builtin.method == "abs")
|
|
return LBF_VECTOR_ABS;
|
|
if (builtin.method == "sign")
|
|
return LBF_VECTOR_SIGN;
|
|
if (builtin.method == "clamp")
|
|
return LBF_VECTOR_CLAMP;
|
|
if (builtin.method == "min")
|
|
return LBF_VECTOR_MIN;
|
|
if (builtin.method == "max")
|
|
return LBF_VECTOR_MAX;
|
|
}
|
|
|
|
if (options.vectorCtor)
|
|
{
|
|
if (options.vectorLib)
|
|
{
|
|
if (builtin.isMethod(options.vectorLib, options.vectorCtor))
|
|
return LBF_VECTOR;
|
|
}
|
|
else
|
|
{
|
|
if (builtin.isGlobal(options.vectorCtor))
|
|
return LBF_VECTOR;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct BuiltinVisitor : AstVisitor
|
|
{
|
|
DenseHashMap<AstExprCall*, int>& result;
|
|
std::array<bool, 256> builtinIsDisabled;
|
|
|
|
const DenseHashMap<AstName, Global>& globals;
|
|
const DenseHashMap<AstLocal*, Variable>& variables;
|
|
|
|
const CompileOptions& options;
|
|
const AstNameTable& names;
|
|
|
|
BuiltinVisitor(
|
|
DenseHashMap<AstExprCall*, int>& result,
|
|
const DenseHashMap<AstName, Global>& globals,
|
|
const DenseHashMap<AstLocal*, Variable>& variables,
|
|
const CompileOptions& options,
|
|
const AstNameTable& names
|
|
)
|
|
: result(result)
|
|
, globals(globals)
|
|
, variables(variables)
|
|
, options(options)
|
|
, names(names)
|
|
{
|
|
builtinIsDisabled.fill(false);
|
|
|
|
if (const char* const* ptr = options.disabledBuiltins)
|
|
{
|
|
for (; *ptr; ++ptr)
|
|
{
|
|
if (const char* dot = strchr(*ptr, '.'))
|
|
{
|
|
AstName library = names.getWithType(*ptr, dot - *ptr).first;
|
|
AstName name = names.get(dot + 1);
|
|
|
|
if (library.value && name.value && getGlobalState(globals, name) == Global::Default)
|
|
{
|
|
Builtin builtin = Builtin{library, name};
|
|
|
|
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
|
builtinIsDisabled[bfid] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (AstName name = names.get(*ptr); name.value && getGlobalState(globals, name) == Global::Default)
|
|
{
|
|
Builtin builtin = Builtin{AstName(), name};
|
|
|
|
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
|
builtinIsDisabled[bfid] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool visit(AstExprCall* node) override
|
|
{
|
|
Builtin builtin = node->self ? Builtin() : getBuiltin(node->func, globals, variables);
|
|
if (builtin.empty())
|
|
return true;
|
|
|
|
int bfid = getBuiltinFunctionId(builtin, options);
|
|
|
|
if (bfid >= 0 && builtinIsDisabled[bfid])
|
|
bfid = -1;
|
|
|
|
// getBuiltinFunctionId optimistically assumes all select() calls are builtin but actually the second argument must be a vararg
|
|
if (bfid == LBF_SELECT_VARARG && !(node->args.size == 2 && node->args.data[1]->is<AstExprVarargs>()))
|
|
bfid = -1;
|
|
|
|
if (bfid >= 0)
|
|
result[node] = bfid;
|
|
|
|
return true; // propagate to nested calls
|
|
}
|
|
};
|
|
|
|
void analyzeBuiltins(
|
|
DenseHashMap<AstExprCall*, int>& result,
|
|
const DenseHashMap<AstName, Global>& globals,
|
|
const DenseHashMap<AstLocal*, Variable>& variables,
|
|
const CompileOptions& options,
|
|
AstNode* root,
|
|
const AstNameTable& names
|
|
)
|
|
{
|
|
BuiltinVisitor visitor{result, globals, variables, options, names};
|
|
root->visit(&visitor);
|
|
}
|
|
|
|
BuiltinInfo getBuiltinInfo(int bfid)
|
|
{
|
|
switch (LuauBuiltinFunction(bfid))
|
|
{
|
|
case LBF_NONE:
|
|
return {-1, -1};
|
|
|
|
case LBF_ASSERT:
|
|
return {-1, -1}; // assert() returns all values when first value is truthy
|
|
|
|
case LBF_MATH_ABS:
|
|
case LBF_MATH_ACOS:
|
|
case LBF_MATH_ASIN:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_ATAN2:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_ATAN:
|
|
case LBF_MATH_CEIL:
|
|
case LBF_MATH_COSH:
|
|
case LBF_MATH_COS:
|
|
case LBF_MATH_DEG:
|
|
case LBF_MATH_EXP:
|
|
case LBF_MATH_FLOOR:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_FMOD:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_FREXP:
|
|
return {1, 2, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_LDEXP:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_LOG10:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_LOG:
|
|
return {-1, 1}; // 1 or 2 parameters
|
|
|
|
case LBF_MATH_MAX:
|
|
case LBF_MATH_MIN:
|
|
return {-1, 1}; // variadic
|
|
|
|
case LBF_MATH_MODF:
|
|
return {1, 2, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_POW:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_RAD:
|
|
case LBF_MATH_SINH:
|
|
case LBF_MATH_SIN:
|
|
case LBF_MATH_SQRT:
|
|
case LBF_MATH_TANH:
|
|
case LBF_MATH_TAN:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BIT32_ARSHIFT:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BIT32_BAND:
|
|
return {-1, 1}; // variadic
|
|
|
|
case LBF_BIT32_BNOT:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BIT32_BOR:
|
|
case LBF_BIT32_BXOR:
|
|
case LBF_BIT32_BTEST:
|
|
return {-1, 1}; // variadic
|
|
|
|
case LBF_BIT32_EXTRACT:
|
|
return {-1, 1}; // 2 or 3 parameters
|
|
|
|
case LBF_BIT32_LROTATE:
|
|
case LBF_BIT32_LSHIFT:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BIT32_REPLACE:
|
|
return {-1, 1}; // 3 or 4 parameters
|
|
|
|
case LBF_BIT32_RROTATE:
|
|
case LBF_BIT32_RSHIFT:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_TYPE:
|
|
return {1, 1};
|
|
|
|
case LBF_STRING_BYTE:
|
|
return {-1, -1}; // 1, 2 or 3 parameters
|
|
|
|
case LBF_STRING_CHAR:
|
|
return {-1, 1}; // variadic
|
|
|
|
case LBF_STRING_LEN:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_TYPEOF:
|
|
return {1, 1};
|
|
|
|
case LBF_STRING_SUB:
|
|
return {-1, 1}; // 2 or 3 parameters
|
|
|
|
case LBF_MATH_CLAMP:
|
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_MATH_SIGN:
|
|
case LBF_MATH_ROUND:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_RAWSET:
|
|
return {3, 1};
|
|
|
|
case LBF_RAWGET:
|
|
case LBF_RAWEQUAL:
|
|
return {2, 1};
|
|
|
|
case LBF_TABLE_INSERT:
|
|
return {-1, 0}; // 2 or 3 parameters
|
|
|
|
case LBF_TABLE_UNPACK:
|
|
return {-1, -1}; // 1, 2 or 3 parameters
|
|
|
|
case LBF_VECTOR:
|
|
return {-1, 1}; // 3 or 4 parameters in some configurations
|
|
|
|
case LBF_BIT32_COUNTLZ:
|
|
case LBF_BIT32_COUNTRZ:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_SELECT_VARARG:
|
|
return {-1, -1}; // variadic
|
|
|
|
case LBF_RAWLEN:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BIT32_EXTRACTK:
|
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_GETMETATABLE:
|
|
return {1, 1};
|
|
|
|
case LBF_SETMETATABLE:
|
|
return {2, 1};
|
|
|
|
case LBF_TONUMBER:
|
|
return {-1, 1}; // 1 or 2 parameters
|
|
|
|
case LBF_TOSTRING:
|
|
return {1, 1};
|
|
|
|
case LBF_BIT32_BYTESWAP:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BUFFER_READI8:
|
|
case LBF_BUFFER_READU8:
|
|
case LBF_BUFFER_READI16:
|
|
case LBF_BUFFER_READU16:
|
|
case LBF_BUFFER_READI32:
|
|
case LBF_BUFFER_READU32:
|
|
case LBF_BUFFER_READF32:
|
|
case LBF_BUFFER_READF64:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_BUFFER_WRITEU8:
|
|
case LBF_BUFFER_WRITEU16:
|
|
case LBF_BUFFER_WRITEU32:
|
|
case LBF_BUFFER_WRITEF32:
|
|
case LBF_BUFFER_WRITEF64:
|
|
return {3, 0, BuiltinInfo::Flag_NoneSafe};
|
|
|
|
case LBF_VECTOR_MAGNITUDE:
|
|
case LBF_VECTOR_NORMALIZE:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
case LBF_VECTOR_CROSS:
|
|
case LBF_VECTOR_DOT:
|
|
return {2, 1, BuiltinInfo::Flag_NoneSafe};
|
|
case LBF_VECTOR_FLOOR:
|
|
case LBF_VECTOR_CEIL:
|
|
case LBF_VECTOR_ABS:
|
|
case LBF_VECTOR_SIGN:
|
|
return {1, 1, BuiltinInfo::Flag_NoneSafe};
|
|
case LBF_VECTOR_CLAMP:
|
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
|
case LBF_VECTOR_MIN:
|
|
case LBF_VECTOR_MAX:
|
|
return {-1, 1}; // variadic
|
|
|
|
case LBF_MATH_LERP:
|
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
|
}
|
|
|
|
LUAU_UNREACHABLE();
|
|
}
|
|
|
|
} // namespace Compile
|
|
} // namespace Luau
|