mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/627 (#1266)
### What's new? * Removed new `table.move` optimization because of correctness problems. ### New Type Solver * Improved error messages for type families to describe what's wrong in more detail, and ideally without using the term `type family` at all. * Change `boolean` and `string` singletons in type checking to report errors to the user when they've gotten an impossible type (indicating a type error from their context). * Split debugging flags for type family reduction (`DebugLuauLogTypeFamilies`) from general solver logging (`DebugLuauLogSolver`). * Improve type simplification to support patterns like `(number | string) | (string | number)` becoming `number | string`. ### Native Code Generation * Use templated `luaV_doarith` to speedup vector operation fallbacks. * Various small changes to better support arm64 on Windows. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@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>
This commit is contained in:
parent
0dbe1a5022
commit
c8fe77c268
@ -102,4 +102,12 @@ bool subsumesStrict(Scope* left, Scope* right);
|
||||
// outermost-possible scope.
|
||||
bool subsumes(Scope* left, Scope* right);
|
||||
|
||||
inline Scope* max(Scope* left, Scope* right)
|
||||
{
|
||||
if (subsumes(left, right))
|
||||
return right;
|
||||
else
|
||||
return left;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -24,7 +24,6 @@
|
||||
*/
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauMakeStringMethodsChecked, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -773,153 +772,87 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
|
||||
const TypePackId numberVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
||||
|
||||
|
||||
if (FFlag::LuauMakeStringMethodsChecked)
|
||||
{
|
||||
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
||||
formatFTV.magicFunction = &magicFunctionFormat;
|
||||
formatFTV.isCheckedFunction = true;
|
||||
const TypeId formatFn = arena->addType(formatFTV);
|
||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
||||
formatFTV.magicFunction = &magicFunctionFormat;
|
||||
formatFTV.isCheckedFunction = true;
|
||||
const TypeId formatFn = arena->addType(formatFTV);
|
||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||
|
||||
|
||||
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ true);
|
||||
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ true);
|
||||
|
||||
const TypeId replArgType = arena->addType(
|
||||
UnionType{{stringType, arena->addType(TableType({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
||||
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ false)}});
|
||||
const TypeId gsubFunc =
|
||||
makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType}, /* checked */ false);
|
||||
const TypeId gmatchFunc = makeFunction(
|
||||
*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionType{emptyPack, stringVariadicList})}, /* checked */ true);
|
||||
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
||||
attachDcrMagicFunction(gmatchFunc, dcrMagicFunctionGmatch);
|
||||
const TypeId replArgType =
|
||||
arena->addType(UnionType{{stringType, arena->addType(TableType({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
||||
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ false)}});
|
||||
const TypeId gsubFunc =
|
||||
makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType}, /* checked */ false);
|
||||
const TypeId gmatchFunc =
|
||||
makeFunction(*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionType{emptyPack, stringVariadicList})}, /* checked */ true);
|
||||
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
||||
attachDcrMagicFunction(gmatchFunc, dcrMagicFunctionGmatch);
|
||||
|
||||
FunctionType matchFuncTy{
|
||||
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})};
|
||||
matchFuncTy.isCheckedFunction = true;
|
||||
const TypeId matchFunc = arena->addType(matchFuncTy);
|
||||
attachMagicFunction(matchFunc, magicFunctionMatch);
|
||||
attachDcrMagicFunction(matchFunc, dcrMagicFunctionMatch);
|
||||
FunctionType matchFuncTy{
|
||||
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})};
|
||||
matchFuncTy.isCheckedFunction = true;
|
||||
const TypeId matchFunc = arena->addType(matchFuncTy);
|
||||
attachMagicFunction(matchFunc, magicFunctionMatch);
|
||||
attachDcrMagicFunction(matchFunc, dcrMagicFunctionMatch);
|
||||
|
||||
FunctionType findFuncTy{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
||||
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})};
|
||||
findFuncTy.isCheckedFunction = true;
|
||||
const TypeId findFunc = arena->addType(findFuncTy);
|
||||
attachMagicFunction(findFunc, magicFunctionFind);
|
||||
attachDcrMagicFunction(findFunc, dcrMagicFunctionFind);
|
||||
FunctionType findFuncTy{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
||||
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})};
|
||||
findFuncTy.isCheckedFunction = true;
|
||||
const TypeId findFunc = arena->addType(findFuncTy);
|
||||
attachMagicFunction(findFunc, magicFunctionFind);
|
||||
attachDcrMagicFunction(findFunc, dcrMagicFunctionFind);
|
||||
|
||||
// string.byte : string -> number? -> number? -> ...number
|
||||
FunctionType stringDotByte{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList};
|
||||
stringDotByte.isCheckedFunction = true;
|
||||
// string.byte : string -> number? -> number? -> ...number
|
||||
FunctionType stringDotByte{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList};
|
||||
stringDotByte.isCheckedFunction = true;
|
||||
|
||||
// string.char : .... number -> string
|
||||
FunctionType stringDotChar{numberVariadicList, arena->addTypePack({stringType})};
|
||||
stringDotChar.isCheckedFunction = true;
|
||||
// string.char : .... number -> string
|
||||
FunctionType stringDotChar{numberVariadicList, arena->addTypePack({stringType})};
|
||||
stringDotChar.isCheckedFunction = true;
|
||||
|
||||
// string.unpack : string -> string -> number? -> ...any
|
||||
FunctionType stringDotUnpack{
|
||||
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
||||
variadicTailPack,
|
||||
};
|
||||
stringDotUnpack.isCheckedFunction = true;
|
||||
// string.unpack : string -> string -> number? -> ...any
|
||||
FunctionType stringDotUnpack{
|
||||
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
||||
variadicTailPack,
|
||||
};
|
||||
stringDotUnpack.isCheckedFunction = true;
|
||||
|
||||
TableType::Props stringLib = {
|
||||
{"byte", {arena->addType(stringDotByte)}},
|
||||
{"char", {arena->addType(stringDotChar)}},
|
||||
{"find", {findFunc}},
|
||||
{"format", {formatFn}}, // FIXME
|
||||
{"gmatch", {gmatchFunc}},
|
||||
{"gsub", {gsubFunc}},
|
||||
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||
{"lower", {stringToStringType}},
|
||||
{"match", {matchFunc}},
|
||||
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType}, /* checked */ true)}},
|
||||
{"reverse", {stringToStringType}},
|
||||
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType}, /* checked */ true)}},
|
||||
{"upper", {stringToStringType}},
|
||||
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
||||
{arena->addType(TableType{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})},
|
||||
/* checked */ true)}},
|
||||
{"pack", {arena->addType(FunctionType{
|
||||
arena->addTypePack(TypePack{{stringType}, variadicTailPack}),
|
||||
oneStringPack,
|
||||
})}},
|
||||
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||
{"unpack", {arena->addType(stringDotUnpack)}},
|
||||
};
|
||||
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
||||
TableType::Props stringLib = {
|
||||
{"byte", {arena->addType(stringDotByte)}},
|
||||
{"char", {arena->addType(stringDotChar)}},
|
||||
{"find", {findFunc}},
|
||||
{"format", {formatFn}}, // FIXME
|
||||
{"gmatch", {gmatchFunc}},
|
||||
{"gsub", {gsubFunc}},
|
||||
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||
{"lower", {stringToStringType}},
|
||||
{"match", {matchFunc}},
|
||||
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType}, /* checked */ true)}},
|
||||
{"reverse", {stringToStringType}},
|
||||
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType}, /* checked */ true)}},
|
||||
{"upper", {stringToStringType}},
|
||||
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
||||
{arena->addType(TableType{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})},
|
||||
/* checked */ true)}},
|
||||
{"pack", {arena->addType(FunctionType{
|
||||
arena->addTypePack(TypePack{{stringType}, variadicTailPack}),
|
||||
oneStringPack,
|
||||
})}},
|
||||
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||
{"unpack", {arena->addType(stringDotUnpack)}},
|
||||
};
|
||||
|
||||
TypeId tableType = arena->addType(TableType{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(tableType))
|
||||
ttv->name = "typeof(string)";
|
||||
TypeId tableType = arena->addType(TableType{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
|
||||
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
}
|
||||
else
|
||||
{
|
||||
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
||||
formatFTV.magicFunction = &magicFunctionFormat;
|
||||
const TypeId formatFn = arena->addType(formatFTV);
|
||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||
if (TableType* ttv = getMutable<TableType>(tableType))
|
||||
ttv->name = "typeof(string)";
|
||||
|
||||
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType});
|
||||
|
||||
const TypeId replArgType = arena->addType(
|
||||
UnionType{{stringType, arena->addType(TableType({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
||||
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType})}});
|
||||
const TypeId gsubFunc = makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType});
|
||||
const TypeId gmatchFunc =
|
||||
makeFunction(*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionType{emptyPack, stringVariadicList})});
|
||||
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
||||
attachDcrMagicFunction(gmatchFunc, dcrMagicFunctionGmatch);
|
||||
|
||||
const TypeId matchFunc = arena->addType(FunctionType{
|
||||
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})});
|
||||
attachMagicFunction(matchFunc, magicFunctionMatch);
|
||||
attachDcrMagicFunction(matchFunc, dcrMagicFunctionMatch);
|
||||
|
||||
const TypeId findFunc = arena->addType(FunctionType{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
||||
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})});
|
||||
attachMagicFunction(findFunc, magicFunctionFind);
|
||||
attachDcrMagicFunction(findFunc, dcrMagicFunctionFind);
|
||||
|
||||
TableType::Props stringLib = {
|
||||
{"byte", {arena->addType(FunctionType{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList})}},
|
||||
{"char", {arena->addType(FunctionType{numberVariadicList, arena->addTypePack({stringType})})}},
|
||||
{"find", {findFunc}},
|
||||
{"format", {formatFn}}, // FIXME
|
||||
{"gmatch", {gmatchFunc}},
|
||||
{"gsub", {gsubFunc}},
|
||||
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType})}},
|
||||
{"lower", {stringToStringType}},
|
||||
{"match", {matchFunc}},
|
||||
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType})}},
|
||||
{"reverse", {stringToStringType}},
|
||||
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType})}},
|
||||
{"upper", {stringToStringType}},
|
||||
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
||||
{arena->addType(TableType{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})})}},
|
||||
{"pack", {arena->addType(FunctionType{
|
||||
arena->addTypePack(TypePack{{stringType}, variadicTailPack}),
|
||||
oneStringPack,
|
||||
})}},
|
||||
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType})}},
|
||||
{"unpack", {arena->addType(FunctionType{
|
||||
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
||||
variadicTailPack,
|
||||
})}},
|
||||
};
|
||||
|
||||
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
||||
|
||||
TypeId tableType = arena->addType(TableType{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(tableType))
|
||||
ttv->name = "typeof(string)";
|
||||
|
||||
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
}
|
||||
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
}
|
||||
|
||||
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
|
||||
|
@ -7,11 +7,13 @@
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypeFamily.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||
|
||||
@ -61,6 +63,23 @@ static std::string wrongNumberOfArgsString(
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
// this list of binary operator type families is used for better stringification of type families errors
|
||||
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||
{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"}, {"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"},
|
||||
{"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}
|
||||
};
|
||||
|
||||
// this list of unary operator type families is used for better stringification of type families errors
|
||||
static const std::unordered_map<std::string, const char*> kUnaryOps{
|
||||
{"unm", "-"}, {"len", "#"}, {"not", "not"}
|
||||
};
|
||||
|
||||
// this list of type families will receive a special error indicating that the user should file a bug on the GitHub repository
|
||||
// putting a type family in this list indicates that it is expected to _always_ reduce
|
||||
static const std::unordered_set<std::string> kUnreachableTypeFamilies{
|
||||
"refine", "singleton", "union", "intersect"
|
||||
};
|
||||
|
||||
struct ErrorConverter
|
||||
{
|
||||
FileResolver* fileResolver = nullptr;
|
||||
@ -565,6 +584,96 @@ struct ErrorConverter
|
||||
|
||||
std::string operator()(const UninhabitedTypeFamily& e) const
|
||||
{
|
||||
auto tfit = get<TypeFamilyInstanceType>(e.ty);
|
||||
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type family.
|
||||
if (!tfit)
|
||||
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type family.";
|
||||
|
||||
// unary operators
|
||||
if (auto unaryString = kUnaryOps.find(tfit->family->name); unaryString != kUnaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(unaryString->second) + "' could not be applied to ";
|
||||
|
||||
if (tfit->typeArguments.size() == 1 && tfit->packArguments.empty())
|
||||
{
|
||||
result += "operand of type " + Luau::toString(tfit->typeArguments[0]);
|
||||
|
||||
if (tfit->family->name != "not")
|
||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it's not the expected case, we ought to add a specialization later, but this is a sane default.
|
||||
result += "operands of types ";
|
||||
|
||||
bool isFirst = true;
|
||||
for (auto arg : tfit->typeArguments)
|
||||
{
|
||||
if (!isFirst)
|
||||
result += ", ";
|
||||
|
||||
result += Luau::toString(arg);
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
for (auto packArg : tfit->packArguments)
|
||||
result += ", " + Luau::toString(packArg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// binary operators
|
||||
if (auto binaryString = kBinaryOps.find(tfit->family->name); binaryString != kBinaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||
|
||||
if (tfit->typeArguments.size() == 2 && tfit->packArguments.empty())
|
||||
{
|
||||
// this is the expected case.
|
||||
result += Luau::toString(tfit->typeArguments[0]) + " and " + Luau::toString(tfit->typeArguments[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it's not the expected case, we ought to add a specialization later, but this is a sane default.
|
||||
|
||||
bool isFirst = true;
|
||||
for (auto arg : tfit->typeArguments)
|
||||
{
|
||||
if (!isFirst)
|
||||
result += ", ";
|
||||
|
||||
result += Luau::toString(arg);
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
for (auto packArg : tfit->packArguments)
|
||||
result += ", " + Luau::toString(packArg);
|
||||
}
|
||||
|
||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// miscellaneous
|
||||
|
||||
if ("keyof" == tfit->family->name || "rawkeyof" == tfit->family->name)
|
||||
{
|
||||
if (tfit->typeArguments.size() == 1 && tfit->packArguments.empty())
|
||||
return "Type '" + toString(tfit->typeArguments[0]) + "' does not have keys, so '" + Luau::toString(e.ty) + "' is invalid";
|
||||
else
|
||||
return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid";
|
||||
}
|
||||
|
||||
if (kUnreachableTypeFamilies.count(tfit->family->name))
|
||||
{
|
||||
return "Type family instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
}
|
||||
|
||||
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type families" explicitly.
|
||||
// If we produce this message, it's an indication that we've missed a specialization and it should be fixed!
|
||||
return "Type family instance " + Luau::toString(e.ty) + " is uninhabited";
|
||||
}
|
||||
|
||||
|
@ -2534,6 +2534,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
state = tttv->state;
|
||||
|
||||
TypeLevel level = max(httv->level, tttv->level);
|
||||
Scope* scope = max(httv->scope, tttv->scope);
|
||||
|
||||
std::unique_ptr<TableType> result = nullptr;
|
||||
bool hereSubThere = true;
|
||||
@ -2644,7 +2645,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
if (prop.readTy || prop.writeTy)
|
||||
{
|
||||
if (!result.get())
|
||||
result = std::make_unique<TableType>(TableType{state, level});
|
||||
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||
result->props[name] = prop;
|
||||
}
|
||||
}
|
||||
@ -2654,7 +2655,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
if (httv->props.count(name) == 0)
|
||||
{
|
||||
if (!result.get())
|
||||
result = std::make_unique<TableType>(TableType{state, level});
|
||||
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||
|
||||
result->props[name] = tprop;
|
||||
hereSubThere = false;
|
||||
@ -2667,7 +2668,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
TypeId index = unionType(httv->indexer->indexType, tttv->indexer->indexType);
|
||||
TypeId indexResult = intersectionType(httv->indexer->indexResultType, tttv->indexer->indexResultType);
|
||||
if (!result.get())
|
||||
result = std::make_unique<TableType>(TableType{state, level});
|
||||
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||
result->indexer = {index, indexResult};
|
||||
hereSubThere &= (httv->indexer->indexType == index) && (httv->indexer->indexResultType == indexResult);
|
||||
thereSubHere &= (tttv->indexer->indexType == index) && (tttv->indexer->indexResultType == indexResult);
|
||||
@ -2675,14 +2676,14 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
else if (httv->indexer)
|
||||
{
|
||||
if (!result.get())
|
||||
result = std::make_unique<TableType>(TableType{state, level});
|
||||
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||
result->indexer = httv->indexer;
|
||||
thereSubHere = false;
|
||||
}
|
||||
else if (tttv->indexer)
|
||||
{
|
||||
if (!result.get())
|
||||
result = std::make_unique<TableType>(TableType{state, level});
|
||||
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||
result->indexer = tttv->indexer;
|
||||
hereSubThere = false;
|
||||
}
|
||||
@ -2697,7 +2698,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
if (result.get())
|
||||
table = arena->addType(std::move(*result));
|
||||
else
|
||||
table = arena->addType(TableType{state, level});
|
||||
table = arena->addType(TableType{state, level, scope});
|
||||
}
|
||||
|
||||
if (tmtable && hmtable)
|
||||
|
@ -1255,6 +1255,10 @@ TypeId TypeSimplifier::union_(TypeId left, TypeId right)
|
||||
case Relation::Coincident:
|
||||
case Relation::Superset:
|
||||
return left;
|
||||
case Relation::Subset:
|
||||
newParts.insert(right);
|
||||
changed = true;
|
||||
break;
|
||||
default:
|
||||
newParts.insert(part);
|
||||
newParts.insert(right);
|
||||
|
@ -1242,13 +1242,14 @@ struct TypeChecker2
|
||||
|
||||
void visit(AstExprConstantBool* expr)
|
||||
{
|
||||
#if defined(LUAU_ENABLE_ASSERT)
|
||||
// booleans use specialized inference logic for singleton types, which can lead to real type errors here.
|
||||
|
||||
const TypeId bestType = expr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
||||
const TypeId inferredType = lookupType(expr);
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType));
|
||||
#endif
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
|
||||
void visit(AstExprConstantNumber* expr)
|
||||
@ -1264,13 +1265,14 @@ struct TypeChecker2
|
||||
|
||||
void visit(AstExprConstantString* expr)
|
||||
{
|
||||
#if defined(LUAU_ENABLE_ASSERT)
|
||||
// strings use specialized inference logic for singleton types, which can lead to real type errors here.
|
||||
|
||||
const TypeId bestType = module->internalTypes.addType(SingletonType{StringSingleton{std::string{expr->value.data, expr->value.size}}});
|
||||
const TypeId inferredType = lookupType(expr);
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType));
|
||||
#endif
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
|
||||
void visit(AstExprLocal* expr)
|
||||
|
@ -37,7 +37,7 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
||||
// when this value is set to a negative value, guessing will be totally disabled.
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauLogSolver);
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -184,7 +184,7 @@ struct FamilyReducer
|
||||
if (subject->owningArena != ctx.arena.get())
|
||||
ctx.ice->ice("Attempting to modify a type family instance from another arena", location);
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str());
|
||||
|
||||
asMutable(subject)->ty.template emplace<Unifiable::Bound<T>>(replacement);
|
||||
@ -206,7 +206,7 @@ struct FamilyReducer
|
||||
|
||||
if (reduction.uninhabited || force)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s is uninhabited\n", toString(subject, {true}).c_str());
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeId>)
|
||||
@ -216,7 +216,7 @@ struct FamilyReducer
|
||||
}
|
||||
else if (!reduction.uninhabited && !force)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s is irreducible; blocked on %zu types, %zu packs\n", toString(subject, {true}).c_str(), reduction.blockedTypes.size(),
|
||||
reduction.blockedPacks.size());
|
||||
|
||||
@ -243,7 +243,7 @@ struct FamilyReducer
|
||||
|
||||
if (skip == SkipTestResult::Irreducible)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||
|
||||
irreducible.insert(subject);
|
||||
@ -251,7 +251,7 @@ struct FamilyReducer
|
||||
}
|
||||
else if (skip == SkipTestResult::Defer)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Deferring %s until %s is solved\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeId>)
|
||||
@ -269,7 +269,7 @@ struct FamilyReducer
|
||||
|
||||
if (skip == SkipTestResult::Irreducible)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||
|
||||
irreducible.insert(subject);
|
||||
@ -277,7 +277,7 @@ struct FamilyReducer
|
||||
}
|
||||
else if (skip == SkipTestResult::Defer)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Deferring %s until %s is solved\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||
|
||||
if constexpr (std::is_same_v<T, TypeId>)
|
||||
@ -297,7 +297,7 @@ struct FamilyReducer
|
||||
{
|
||||
if (shouldGuess.contains(subject))
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Flagged %s for reduction with guesser.\n", toString(subject, {true}).c_str());
|
||||
|
||||
TypeFamilyReductionGuesser guesser{ctx.arena, ctx.builtins, ctx.normalizer};
|
||||
@ -305,14 +305,14 @@ struct FamilyReducer
|
||||
|
||||
if (guessed)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Selected %s as the guessed result type.\n", toString(*guessed, {true}).c_str());
|
||||
|
||||
replace(subject, *guessed);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Failed to produce a guess for the result of %s.\n", toString(subject, {true}).c_str());
|
||||
}
|
||||
|
||||
@ -328,7 +328,7 @@ struct FamilyReducer
|
||||
if (irreducible.contains(subject))
|
||||
return;
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
||||
|
||||
if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(subject))
|
||||
@ -337,7 +337,7 @@ struct FamilyReducer
|
||||
|
||||
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFamily)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Irreducible due to irreducible/pending and a non-cyclic family\n");
|
||||
|
||||
return;
|
||||
@ -361,7 +361,7 @@ struct FamilyReducer
|
||||
if (irreducible.contains(subject))
|
||||
return;
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
||||
|
||||
if (const TypeFamilyInstanceTypePack* tfit = get<TypeFamilyInstanceTypePack>(subject))
|
||||
|
@ -144,8 +144,17 @@ using UniqueSharedCodeGenContext = std::unique_ptr<SharedCodeGenContext, SharedC
|
||||
// SharedCodeGenContext must be destroyed before this function is called.
|
||||
void destroySharedCodeGenContext(const SharedCodeGenContext* codeGenContext) noexcept;
|
||||
|
||||
void create(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext);
|
||||
// Initializes native code-gen on the provided Luau VM, using a VM-specific
|
||||
// code-gen context and either the default allocator parameters or custom
|
||||
// allocator parameters.
|
||||
void create(lua_State* L);
|
||||
void create(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext);
|
||||
void create(lua_State* L, size_t blockSize, size_t maxTotalSize, AllocationCallback* allocationCallback, void* allocationCallbackContext);
|
||||
|
||||
// Initializes native code-gen on the provided Luau VM, using the provided
|
||||
// SharedCodeGenContext. Note that after this function is called, the
|
||||
// SharedCodeGenContext must not be destroyed until after the Luau VM L is
|
||||
// destroyed via lua_close.
|
||||
void create(lua_State* L, SharedCodeGenContext* codeGenContext);
|
||||
|
||||
// Check if native execution is enabled
|
||||
|
@ -16,7 +16,7 @@ namespace CodeGen
|
||||
{
|
||||
|
||||
// This value is used in 'finishFunction' to mark the function that spans to the end of the whole code block
|
||||
static uint32_t kFullBlockFuncton = ~0u;
|
||||
static uint32_t kFullBlockFunction = ~0u;
|
||||
|
||||
class UnwindBuilder
|
||||
{
|
||||
@ -52,11 +52,10 @@ public:
|
||||
virtual void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||
const std::vector<X64::RegisterX64>& simd) = 0;
|
||||
|
||||
virtual size_t getSize() const = 0;
|
||||
virtual size_t getFunctionCount() const = 0;
|
||||
virtual size_t getUnwindInfoSize(size_t blockSize) const = 0;
|
||||
|
||||
// This will place the unwinding data at the target address and might update values of some fields
|
||||
virtual void finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const = 0;
|
||||
virtual size_t finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const = 0;
|
||||
};
|
||||
|
||||
} // namespace CodeGen
|
||||
|
@ -33,10 +33,9 @@ public:
|
||||
void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||
const std::vector<X64::RegisterX64>& simd) override;
|
||||
|
||||
size_t getSize() const override;
|
||||
size_t getFunctionCount() const override;
|
||||
size_t getUnwindInfoSize(size_t blockSize = 0) const override;
|
||||
|
||||
void finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const override;
|
||||
size_t finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const override;
|
||||
|
||||
private:
|
||||
size_t beginOffset = 0;
|
||||
|
@ -53,10 +53,9 @@ public:
|
||||
void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||
const std::vector<X64::RegisterX64>& simd) override;
|
||||
|
||||
size_t getSize() const override;
|
||||
size_t getFunctionCount() const override;
|
||||
size_t getUnwindInfoSize(size_t blockSize = 0) const override;
|
||||
|
||||
void finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const override;
|
||||
size_t finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const override;
|
||||
|
||||
private:
|
||||
size_t beginOffset = 0;
|
||||
|
@ -102,17 +102,17 @@ void* createBlockUnwindInfo(void* context, uint8_t* block, size_t blockSize, siz
|
||||
UnwindBuilder* unwind = (UnwindBuilder*)context;
|
||||
|
||||
// All unwinding related data is placed together at the start of the block
|
||||
size_t unwindSize = unwind->getSize();
|
||||
size_t unwindSize = unwind->getUnwindInfoSize(blockSize);
|
||||
unwindSize = (unwindSize + (kCodeAlignment - 1)) & ~(kCodeAlignment - 1); // Match code allocator alignment
|
||||
CODEGEN_ASSERT(blockSize >= unwindSize);
|
||||
|
||||
char* unwindData = (char*)block;
|
||||
unwind->finalize(unwindData, unwindSize, block, blockSize);
|
||||
[[maybe_unused]] size_t functionCount = unwind->finalize(unwindData, unwindSize, block, blockSize);
|
||||
|
||||
#if defined(_WIN32) && defined(CODEGEN_TARGET_X64)
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
|
||||
if (!RtlAddFunctionTable((RUNTIME_FUNCTION*)block, uint32_t(unwind->getFunctionCount()), uintptr_t(block)))
|
||||
if (!RtlAddFunctionTable((RUNTIME_FUNCTION*)block, uint32_t(functionCount), uintptr_t(block)))
|
||||
{
|
||||
CODEGEN_ASSERT(!"Failed to allocate function table");
|
||||
return nullptr;
|
||||
|
@ -95,10 +95,6 @@ std::string toString(const CodeGenCompilationResult& result)
|
||||
return "";
|
||||
}
|
||||
|
||||
void* gPerfLogContext = nullptr;
|
||||
PerfLogFn gPerfLogFn = nullptr;
|
||||
|
||||
|
||||
void onDisable(lua_State* L, Proto* proto)
|
||||
{
|
||||
// do nothing if proto already uses bytecode
|
||||
@ -196,59 +192,5 @@ bool isSupported()
|
||||
#endif
|
||||
}
|
||||
|
||||
void create(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
||||
{
|
||||
create_NEW(L, allocationCallback, allocationCallbackContext);
|
||||
}
|
||||
|
||||
void create(lua_State* L)
|
||||
{
|
||||
create_NEW(L);
|
||||
}
|
||||
|
||||
void create(lua_State* L, SharedCodeGenContext* codeGenContext)
|
||||
{
|
||||
create_NEW(L, codeGenContext);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNativeExecutionEnabled(lua_State* L)
|
||||
{
|
||||
return isNativeExecutionEnabled_NEW(L);
|
||||
}
|
||||
|
||||
void setNativeExecutionEnabled(lua_State* L, bool enabled)
|
||||
{
|
||||
setNativeExecutionEnabled_NEW(L, enabled);
|
||||
}
|
||||
|
||||
CompilationResult compile(lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
||||
{
|
||||
Luau::CodeGen::CompilationOptions options{flags};
|
||||
|
||||
return compile_NEW(L, idx, options, stats);
|
||||
}
|
||||
|
||||
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
||||
{
|
||||
Luau::CodeGen::CompilationOptions options{flags};
|
||||
return compile_NEW(moduleId, L, idx, options, stats);
|
||||
}
|
||||
|
||||
CompilationResult compile(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
{
|
||||
return compile_NEW(L, idx, options, stats);
|
||||
}
|
||||
|
||||
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
{
|
||||
return compile_NEW(moduleId, L, idx, options, stats);
|
||||
}
|
||||
|
||||
void setPerfLog(void* context, PerfLogFn logFn)
|
||||
{
|
||||
gPerfLogContext = context;
|
||||
gPerfLogFn = logFn;
|
||||
}
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
||||
|
@ -253,7 +253,7 @@ static EntryLocations buildEntryFunction(AssemblyBuilderA64& build, UnwindBuilde
|
||||
// Our entry function is special, it spans the whole remaining code area
|
||||
unwind.startFunction();
|
||||
unwind.prologueA64(prologueSize, kStackSize, {x29, x30, x19, x20, x21, x22, x23, x24, x25});
|
||||
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFuncton);
|
||||
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFunction);
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
@ -25,11 +25,17 @@ namespace CodeGen
|
||||
static const Instruction kCodeEntryInsn = LOP_NATIVECALL;
|
||||
|
||||
// From CodeGen.cpp
|
||||
extern void* gPerfLogContext;
|
||||
extern PerfLogFn gPerfLogFn;
|
||||
static void* gPerfLogContext = nullptr;
|
||||
static PerfLogFn gPerfLogFn = nullptr;
|
||||
|
||||
unsigned int getCpuFeaturesA64();
|
||||
|
||||
void setPerfLog(void* context, PerfLogFn logFn)
|
||||
{
|
||||
gPerfLogContext = context;
|
||||
gPerfLogFn = logFn;
|
||||
}
|
||||
|
||||
static void logPerfFunction(Proto* p, uintptr_t addr, unsigned size)
|
||||
{
|
||||
CODEGEN_ASSERT(p->source);
|
||||
@ -365,17 +371,17 @@ static void initializeExecutionCallbacks(lua_State* L, BaseCodeGenContext* codeG
|
||||
ecb->getmemorysize = getMemorySize;
|
||||
}
|
||||
|
||||
void create_NEW(lua_State* L)
|
||||
void create(lua_State* L)
|
||||
{
|
||||
return create_NEW(L, size_t(FInt::LuauCodeGenBlockSize), size_t(FInt::LuauCodeGenMaxTotalSize), nullptr, nullptr);
|
||||
return create(L, size_t(FInt::LuauCodeGenBlockSize), size_t(FInt::LuauCodeGenMaxTotalSize), nullptr, nullptr);
|
||||
}
|
||||
|
||||
void create_NEW(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
||||
void create(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
||||
{
|
||||
return create_NEW(L, size_t(FInt::LuauCodeGenBlockSize), size_t(FInt::LuauCodeGenMaxTotalSize), allocationCallback, allocationCallbackContext);
|
||||
return create(L, size_t(FInt::LuauCodeGenBlockSize), size_t(FInt::LuauCodeGenMaxTotalSize), allocationCallback, allocationCallbackContext);
|
||||
}
|
||||
|
||||
void create_NEW(lua_State* L, size_t blockSize, size_t maxTotalSize, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
||||
void create(lua_State* L, size_t blockSize, size_t maxTotalSize, AllocationCallback* allocationCallback, void* allocationCallbackContext)
|
||||
{
|
||||
std::unique_ptr<StandaloneCodeGenContext> codeGenContext =
|
||||
std::make_unique<StandaloneCodeGenContext>(blockSize, maxTotalSize, allocationCallback, allocationCallbackContext);
|
||||
@ -386,7 +392,7 @@ void create_NEW(lua_State* L, size_t blockSize, size_t maxTotalSize, AllocationC
|
||||
initializeExecutionCallbacks(L, codeGenContext.release());
|
||||
}
|
||||
|
||||
void create_NEW(lua_State* L, SharedCodeGenContext* codeGenContext)
|
||||
void create(lua_State* L, SharedCodeGenContext* codeGenContext)
|
||||
{
|
||||
initializeExecutionCallbacks(L, codeGenContext);
|
||||
}
|
||||
@ -575,22 +581,32 @@ template<typename AssemblyBuilder>
|
||||
return compilationResult;
|
||||
}
|
||||
|
||||
CompilationResult compile_NEW(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
{
|
||||
return compileInternal(moduleId, L, idx, options, stats);
|
||||
}
|
||||
|
||||
CompilationResult compile_NEW(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
CompilationResult compile(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats)
|
||||
{
|
||||
return compileInternal({}, L, idx, options, stats);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNativeExecutionEnabled_NEW(lua_State* L)
|
||||
CompilationResult compile(lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
||||
{
|
||||
return compileInternal({}, L, idx, CompilationOptions{flags}, stats);
|
||||
}
|
||||
|
||||
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, unsigned int flags, CompilationStats* stats)
|
||||
{
|
||||
return compileInternal(moduleId, L, idx, CompilationOptions{flags}, stats);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNativeExecutionEnabled(lua_State* L)
|
||||
{
|
||||
return getCodeGenContext(L) != nullptr && L->global->ecb.enter == onEnter;
|
||||
}
|
||||
|
||||
void setNativeExecutionEnabled_NEW(lua_State* L, bool enabled)
|
||||
void setNativeExecutionEnabled(lua_State* L, bool enabled)
|
||||
{
|
||||
if (getCodeGenContext(L) != nullptr)
|
||||
L->global->ecb.enter = enabled ? onEnter : onEnterDisabled;
|
||||
|
@ -88,33 +88,5 @@ private:
|
||||
SharedCodeAllocator sharedAllocator;
|
||||
};
|
||||
|
||||
|
||||
// The following will become the public interface, and can be moved into
|
||||
// CodeGen.h after the shared allocator work is complete. When the old
|
||||
// implementation is removed, the _NEW suffix can be dropped from these
|
||||
// functions.
|
||||
|
||||
// Initializes native code-gen on the provided Luau VM, using a VM-specific
|
||||
// code-gen context and either the default allocator parameters or custom
|
||||
// allocator parameters.
|
||||
void create_NEW(lua_State* L);
|
||||
void create_NEW(lua_State* L, AllocationCallback* allocationCallback, void* allocationCallbackContext);
|
||||
void create_NEW(lua_State* L, size_t blockSize, size_t maxTotalSize, AllocationCallback* allocationCallback, void* allocationCallbackContext);
|
||||
|
||||
// Initializes native code-gen on the provided Luau VM, using the provided
|
||||
// SharedCodeGenContext. Note that after this function is called, the
|
||||
// SharedCodeGenContext must not be destroyed until after the Luau VM L is
|
||||
// destroyed via lua_close.
|
||||
void create_NEW(lua_State* L, SharedCodeGenContext* codeGenContext);
|
||||
|
||||
CompilationResult compile_NEW(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats);
|
||||
CompilationResult compile_NEW(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats);
|
||||
|
||||
// Returns true if native execution is currently enabled for this VM
|
||||
[[nodiscard]] bool isNativeExecutionEnabled_NEW(lua_State* L);
|
||||
|
||||
// Enables or disables native excution for this VM
|
||||
void setNativeExecutionEnabled_NEW(lua_State* L, bool enabled);
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
||||
|
@ -181,7 +181,7 @@ static EntryLocations buildEntryFunction(AssemblyBuilderX64& build, UnwindBuilde
|
||||
build.ret();
|
||||
|
||||
// Our entry function is special, it spans the whole remaining code area
|
||||
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFuncton);
|
||||
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFunction);
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodegenSplitDoarith, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -155,8 +157,45 @@ void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, Ope
|
||||
callWrap.addArgument(SizeX64::qword, luauRegAddress(ra));
|
||||
callWrap.addArgument(SizeX64::qword, b);
|
||||
callWrap.addArgument(SizeX64::qword, c);
|
||||
callWrap.addArgument(SizeX64::dword, tm);
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarith)]);
|
||||
|
||||
if (FFlag::LuauCodegenSplitDoarith)
|
||||
{
|
||||
switch (tm)
|
||||
{
|
||||
case TM_ADD:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithadd)]);
|
||||
break;
|
||||
case TM_SUB:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithsub)]);
|
||||
break;
|
||||
case TM_MUL:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmul)]);
|
||||
break;
|
||||
case TM_DIV:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithdiv)]);
|
||||
break;
|
||||
case TM_IDIV:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithidiv)]);
|
||||
break;
|
||||
case TM_MOD:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmod)]);
|
||||
break;
|
||||
case TM_POW:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithpow)]);
|
||||
break;
|
||||
case TM_UNM:
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithunm)]);
|
||||
break;
|
||||
default:
|
||||
CODEGEN_ASSERT(!"Invalid doarith helper operation tag");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
callWrap.addArgument(SizeX64::dword, tm);
|
||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarith)]);
|
||||
}
|
||||
|
||||
emitUpdateBase(build);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "lgc.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5)
|
||||
LUAU_FASTFLAG(LuauCodegenSplitDoarith)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -1242,9 +1243,47 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
else
|
||||
build.add(x3, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
||||
|
||||
build.mov(w4, TMS(intOp(inst.d)));
|
||||
build.ldr(x5, mem(rNativeContext, offsetof(NativeContext, luaV_doarith)));
|
||||
build.blr(x5);
|
||||
if (FFlag::LuauCodegenSplitDoarith)
|
||||
{
|
||||
switch (TMS(intOp(inst.d)))
|
||||
{
|
||||
case TM_ADD:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithadd)));
|
||||
break;
|
||||
case TM_SUB:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithsub)));
|
||||
break;
|
||||
case TM_MUL:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmul)));
|
||||
break;
|
||||
case TM_DIV:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithdiv)));
|
||||
break;
|
||||
case TM_IDIV:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithidiv)));
|
||||
break;
|
||||
case TM_MOD:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmod)));
|
||||
break;
|
||||
case TM_POW:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithpow)));
|
||||
break;
|
||||
case TM_UNM:
|
||||
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithunm)));
|
||||
break;
|
||||
default:
|
||||
CODEGEN_ASSERT(!"Invalid doarith helper operation tag");
|
||||
break;
|
||||
}
|
||||
|
||||
build.blr(x4);
|
||||
}
|
||||
else
|
||||
{
|
||||
build.mov(w4, TMS(intOp(inst.d)));
|
||||
build.ldr(x5, mem(rNativeContext, offsetof(NativeContext, luaV_doarith)));
|
||||
build.blr(x5);
|
||||
}
|
||||
|
||||
emitUpdateBase(build);
|
||||
break;
|
||||
|
@ -43,6 +43,16 @@ void initFunctions(NativeState& data)
|
||||
data.context.luaV_lessequal = luaV_lessequal;
|
||||
data.context.luaV_equalval = luaV_equalval;
|
||||
data.context.luaV_doarith = luaV_doarith;
|
||||
|
||||
data.context.luaV_doarithadd = luaV_doarithimpl<TM_ADD>;
|
||||
data.context.luaV_doarithsub = luaV_doarithimpl<TM_SUB>;
|
||||
data.context.luaV_doarithmul = luaV_doarithimpl<TM_MUL>;
|
||||
data.context.luaV_doarithdiv = luaV_doarithimpl<TM_DIV>;
|
||||
data.context.luaV_doarithidiv = luaV_doarithimpl<TM_IDIV>;
|
||||
data.context.luaV_doarithmod = luaV_doarithimpl<TM_MOD>;
|
||||
data.context.luaV_doarithpow = luaV_doarithimpl<TM_POW>;
|
||||
data.context.luaV_doarithunm = luaV_doarithimpl<TM_UNM>;
|
||||
|
||||
data.context.luaV_dolen = luaV_dolen;
|
||||
data.context.luaV_gettable = luaV_gettable;
|
||||
data.context.luaV_settable = luaV_settable;
|
||||
@ -121,6 +131,16 @@ void initFunctions(NativeContext& context)
|
||||
context.luaV_lessequal = luaV_lessequal;
|
||||
context.luaV_equalval = luaV_equalval;
|
||||
context.luaV_doarith = luaV_doarith;
|
||||
|
||||
context.luaV_doarithadd = luaV_doarithimpl<TM_ADD>;
|
||||
context.luaV_doarithsub = luaV_doarithimpl<TM_SUB>;
|
||||
context.luaV_doarithmul = luaV_doarithimpl<TM_MUL>;
|
||||
context.luaV_doarithdiv = luaV_doarithimpl<TM_DIV>;
|
||||
context.luaV_doarithidiv = luaV_doarithimpl<TM_IDIV>;
|
||||
context.luaV_doarithmod = luaV_doarithimpl<TM_MOD>;
|
||||
context.luaV_doarithpow = luaV_doarithimpl<TM_POW>;
|
||||
context.luaV_doarithunm = luaV_doarithimpl<TM_UNM>;
|
||||
|
||||
context.luaV_dolen = luaV_dolen;
|
||||
context.luaV_gettable = luaV_gettable;
|
||||
context.luaV_settable = luaV_settable;
|
||||
|
@ -34,6 +34,14 @@ struct NativeContext
|
||||
int (*luaV_lessequal)(lua_State* L, const TValue* l, const TValue* r) = nullptr;
|
||||
int (*luaV_equalval)(lua_State* L, const TValue* t1, const TValue* t2) = nullptr;
|
||||
void (*luaV_doarith)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op) = nullptr;
|
||||
void (*luaV_doarithadd)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithsub)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithmul)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithdiv)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithidiv)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithmod)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithpow)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_doarithunm)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr;
|
||||
void (*luaV_dolen)(lua_State* L, StkId ra, const TValue* rb) = nullptr;
|
||||
void (*luaV_gettable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
||||
void (*luaV_settable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
||||
|
@ -202,7 +202,7 @@ void UnwindBuilderDwarf2::finishInfo()
|
||||
// Terminate section
|
||||
pos = writeu32(pos, 0);
|
||||
|
||||
CODEGEN_ASSERT(getSize() <= kRawDataLimit);
|
||||
CODEGEN_ASSERT(getUnwindInfoSize() <= kRawDataLimit);
|
||||
}
|
||||
|
||||
void UnwindBuilderDwarf2::prologueA64(uint32_t prologueSize, uint32_t stackSize, std::initializer_list<A64::RegisterA64> regs)
|
||||
@ -271,19 +271,14 @@ void UnwindBuilderDwarf2::prologueX64(uint32_t prologueSize, uint32_t stackSize,
|
||||
CODEGEN_ASSERT(prologueOffset == prologueSize);
|
||||
}
|
||||
|
||||
size_t UnwindBuilderDwarf2::getSize() const
|
||||
size_t UnwindBuilderDwarf2::getUnwindInfoSize(size_t blockSize) const
|
||||
{
|
||||
return size_t(pos - rawData);
|
||||
}
|
||||
|
||||
size_t UnwindBuilderDwarf2::getFunctionCount() const
|
||||
size_t UnwindBuilderDwarf2::finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const
|
||||
{
|
||||
return unwindFunctions.size();
|
||||
}
|
||||
|
||||
void UnwindBuilderDwarf2::finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const
|
||||
{
|
||||
memcpy(target, rawData, getSize());
|
||||
memcpy(target, rawData, getUnwindInfoSize());
|
||||
|
||||
for (const UnwindFunctionDwarf2& func : unwindFunctions)
|
||||
{
|
||||
@ -291,11 +286,13 @@ void UnwindBuilderDwarf2::finalize(char* target, size_t offset, void* funcAddres
|
||||
|
||||
writeu64(fdeEntry + kFdeInitialLocationOffset, uintptr_t(funcAddress) + offset + func.beginOffset);
|
||||
|
||||
if (func.endOffset == kFullBlockFuncton)
|
||||
writeu64(fdeEntry + kFdeAddressRangeOffset, funcSize - offset);
|
||||
if (func.endOffset == kFullBlockFunction)
|
||||
writeu64(fdeEntry + kFdeAddressRangeOffset, blockSize - offset);
|
||||
else
|
||||
writeu64(fdeEntry + kFdeAddressRangeOffset, func.endOffset - func.beginOffset);
|
||||
}
|
||||
|
||||
return unwindFunctions.size();
|
||||
}
|
||||
|
||||
} // namespace CodeGen
|
||||
|
@ -194,17 +194,12 @@ void UnwindBuilderWin::prologueX64(uint32_t prologueSize, uint32_t stackSize, bo
|
||||
this->prologSize = prologueSize;
|
||||
}
|
||||
|
||||
size_t UnwindBuilderWin::getSize() const
|
||||
size_t UnwindBuilderWin::getUnwindInfoSize(size_t blockSize) const
|
||||
{
|
||||
return sizeof(UnwindFunctionWin) * unwindFunctions.size() + size_t(rawDataPos - rawData);
|
||||
}
|
||||
|
||||
size_t UnwindBuilderWin::getFunctionCount() const
|
||||
{
|
||||
return unwindFunctions.size();
|
||||
}
|
||||
|
||||
void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const
|
||||
size_t UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const
|
||||
{
|
||||
// Copy adjusted function information
|
||||
for (UnwindFunctionWin func : unwindFunctions)
|
||||
@ -213,8 +208,8 @@ void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress,
|
||||
func.beginOffset += uint32_t(offset);
|
||||
|
||||
// Whole block is a part of a 'single function'
|
||||
if (func.endOffset == kFullBlockFuncton)
|
||||
func.endOffset = uint32_t(funcSize);
|
||||
if (func.endOffset == kFullBlockFunction)
|
||||
func.endOffset = uint32_t(blockSize);
|
||||
else
|
||||
func.endOffset += uint32_t(offset);
|
||||
|
||||
@ -226,6 +221,8 @@ void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress,
|
||||
|
||||
// Copy unwind codes
|
||||
memcpy(target, rawData, size_t(rawDataPos - rawData));
|
||||
|
||||
return unwindFunctions.size();
|
||||
}
|
||||
|
||||
} // namespace CodeGen
|
||||
|
@ -16,6 +16,10 @@ LUAI_FUNC int luaV_lessthan(lua_State* L, const TValue* l, const TValue* r);
|
||||
LUAI_FUNC int luaV_lessequal(lua_State* L, const TValue* l, const TValue* r);
|
||||
LUAI_FUNC int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2);
|
||||
LUAI_FUNC void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op);
|
||||
|
||||
template<TMS op>
|
||||
void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
|
||||
LUAI_FUNC void luaV_dolen(lua_State* L, StkId ra, const TValue* rb);
|
||||
LUAI_FUNC const TValue* luaV_tonumber(const TValue* obj, TValue* n);
|
||||
LUAI_FUNC const float* luaV_tovector(const TValue* obj);
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauVmSplitDoarith, false)
|
||||
|
||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||
#ifdef __clang__
|
||||
#if __has_warning("-Wc99-designator")
|
||||
@ -1487,7 +1489,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_ADD));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_ADD));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1533,7 +1542,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_SUB));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_SUB));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1594,7 +1610,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MUL));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MUL));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1655,7 +1678,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_DIV));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_DIV));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1703,7 +1733,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_IDIV));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_IDIV));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1727,7 +1764,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MOD));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MOD));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1748,7 +1792,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_POW));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_POW));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1769,7 +1820,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_ADD));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_ADD));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1790,7 +1848,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_SUB));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_SUB));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1835,7 +1900,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MUL));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MUL));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1881,7 +1953,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_DIV));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_DIV));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1928,7 +2007,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_IDIV));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_IDIV));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1952,7 +2038,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MOD));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MOD));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -1979,7 +2072,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_POW));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, kv));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_POW));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -2092,7 +2192,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rb, TM_UNM));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_UNM>(L, ra, rb, rb));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, rb, rb, TM_UNM));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -2711,7 +2818,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, kv, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
@ -2739,7 +2853,14 @@ reentry:
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV));
|
||||
if (FFlag::LuauVmSplitDoarith)
|
||||
{
|
||||
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, kv, rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV));
|
||||
}
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
|
@ -373,6 +373,152 @@ void luaV_concat(lua_State* L, int total, int last)
|
||||
} while (total > 1); // repeat until only 1 result left
|
||||
}
|
||||
|
||||
template<TMS op>
|
||||
void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc)
|
||||
{
|
||||
TValue tempb, tempc;
|
||||
const TValue *b, *c;
|
||||
|
||||
// vector operations that we support:
|
||||
// v+v v-v -v (add/sub/neg)
|
||||
// v*v s*v v*s (mul)
|
||||
// v/v s/v v/s (div)
|
||||
// v//v s//v v//s (floor div)
|
||||
const float* vb = ttisvector(rb) ? vvalue(rb) : nullptr;
|
||||
const float* vc = ttisvector(rc) ? vvalue(rc) : nullptr;
|
||||
|
||||
if (vb && vc)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case TM_ADD:
|
||||
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]);
|
||||
return;
|
||||
case TM_SUB:
|
||||
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]);
|
||||
return;
|
||||
case TM_MUL:
|
||||
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]);
|
||||
return;
|
||||
case TM_IDIV:
|
||||
setvvalue(ra, float(luai_numidiv(vb[0], vc[0])), float(luai_numidiv(vb[1], vc[1])), float(luai_numidiv(vb[2], vc[2])),
|
||||
float(luai_numidiv(vb[3], vc[3])));
|
||||
return;
|
||||
case TM_UNM:
|
||||
setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (vb)
|
||||
{
|
||||
c = ttisnumber(rc) ? rc : luaV_tonumber(rc, &tempc);
|
||||
|
||||
if (c)
|
||||
{
|
||||
float nc = cast_to(float, nvalue(c));
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case TM_MUL:
|
||||
setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc, vb[3] * nc);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc);
|
||||
return;
|
||||
case TM_IDIV:
|
||||
setvvalue(ra, float(luai_numidiv(vb[0], nc)), float(luai_numidiv(vb[1], nc)), float(luai_numidiv(vb[2], nc)),
|
||||
float(luai_numidiv(vb[3], nc)));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vc)
|
||||
{
|
||||
b = ttisnumber(rb) ? rb : luaV_tonumber(rb, &tempb);
|
||||
|
||||
if (b)
|
||||
{
|
||||
float nb = cast_to(float, nvalue(b));
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case TM_MUL:
|
||||
setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2], nb * vc[3]);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]);
|
||||
return;
|
||||
case TM_IDIV:
|
||||
setvvalue(ra, float(luai_numidiv(nb, vc[0])), float(luai_numidiv(nb, vc[1])), float(luai_numidiv(nb, vc[2])),
|
||||
float(luai_numidiv(nb, vc[3])));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL)
|
||||
{
|
||||
double nb = nvalue(b), nc = nvalue(c);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case TM_ADD:
|
||||
setnvalue(ra, luai_numadd(nb, nc));
|
||||
break;
|
||||
case TM_SUB:
|
||||
setnvalue(ra, luai_numsub(nb, nc));
|
||||
break;
|
||||
case TM_MUL:
|
||||
setnvalue(ra, luai_nummul(nb, nc));
|
||||
break;
|
||||
case TM_DIV:
|
||||
setnvalue(ra, luai_numdiv(nb, nc));
|
||||
break;
|
||||
case TM_IDIV:
|
||||
setnvalue(ra, luai_numidiv(nb, nc));
|
||||
break;
|
||||
case TM_MOD:
|
||||
setnvalue(ra, luai_nummod(nb, nc));
|
||||
break;
|
||||
case TM_POW:
|
||||
setnvalue(ra, luai_numpow(nb, nc));
|
||||
break;
|
||||
case TM_UNM:
|
||||
setnvalue(ra, luai_numunm(nb));
|
||||
break;
|
||||
default:
|
||||
LUAU_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!call_binTM(L, rb, rc, ra, op))
|
||||
{
|
||||
luaG_aritherror(L, rb, rc, op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate private template implementation for external callers
|
||||
template void luaV_doarithimpl<TM_ADD>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_SUB>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_MUL>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_DIV>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_IDIV>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_MOD>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_POW>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
template void luaV_doarithimpl<TM_UNM>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
|
||||
|
||||
void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op)
|
||||
{
|
||||
TValue tempb, tempc;
|
||||
|
@ -191,7 +191,7 @@ TEST_CASE("WindowsUnwindCodesX64")
|
||||
unwind.finishInfo();
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(unwind.getSize());
|
||||
data.resize(unwind.getUnwindInfoSize());
|
||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
||||
|
||||
std::vector<uint8_t> expected{0x44, 0x33, 0x22, 0x11, 0x22, 0x33, 0x44, 0x55, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x17, 0x0a, 0x05, 0x17, 0x82, 0x13,
|
||||
@ -215,7 +215,7 @@ TEST_CASE("Dwarf2UnwindCodesX64")
|
||||
unwind.finishInfo();
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(unwind.getSize());
|
||||
data.resize(unwind.getUnwindInfoSize());
|
||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
||||
|
||||
std::vector<uint8_t> expected{0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x78, 0x10, 0x0c, 0x07, 0x08, 0x90, 0x01, 0x00,
|
||||
@ -241,7 +241,7 @@ TEST_CASE("Dwarf2UnwindCodesA64")
|
||||
unwind.finishInfo();
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(unwind.getSize());
|
||||
data.resize(unwind.getUnwindInfoSize());
|
||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
||||
|
||||
std::vector<uint8_t> expected{0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x78, 0x1e, 0x0c, 0x1f, 0x00, 0x2c, 0x00, 0x00,
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
||||
TEST_SUITE_BEGIN("ErrorTests");
|
||||
|
||||
TEST_CASE("TypeError_code_should_return_nonzero_code")
|
||||
@ -34,4 +36,44 @@ local x: Account = 5
|
||||
CHECK_EQ("Type 'number' could not be converted into 'Account'", toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_family_errors")
|
||||
{
|
||||
frontend.options.retainFullTypeGraphs = false;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local x = 1 + "foo"
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("Operator '+' could not be applied to operands of types number and string; there is no corresponding overload for __add", toString(result.errors[0]));
|
||||
else
|
||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_family_errors")
|
||||
{
|
||||
frontend.options.retainFullTypeGraphs = false;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local x = -"foo"
|
||||
)");
|
||||
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ("Operator '-' could not be applied to operand of type string; there is no corresponding overload for __unm", toString(result.errors[0]));
|
||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -214,6 +214,14 @@ TEST_CASE_FIXTURE(SimplifyFixture, "any_and_indeterminate_types")
|
||||
CHECK(errorTy == anyLhsPending->options[1]);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SimplifyFixture, "union_where_lhs_elements_are_a_subset_of_the_rhs")
|
||||
{
|
||||
TypeId lhs = union_(numberTy, stringTy);
|
||||
TypeId rhs = union_(stringTy, numberTy);
|
||||
|
||||
CHECK("number | string" == toString(union_(lhs, rhs)));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SimplifyFixture, "unknown_and_indeterminate_types")
|
||||
{
|
||||
CHECK(freeTy == intersect(unknownTy, freeTy));
|
||||
|
@ -391,8 +391,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_errors_if_it_has_nontable_
|
||||
|
||||
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think?
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK(toString(result.errors[0]) == "Type family instance keyof<MyObject | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[1]) == "Type family instance keyof<MyObject | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid");
|
||||
CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_string_indexer")
|
||||
@ -517,8 +517,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_errors_if_it_has_nontab
|
||||
|
||||
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think?
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK(toString(result.errors[0]) == "Type family instance rawkeyof<MyObject | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[1]) == "Type family instance rawkeyof<MyObject | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid");
|
||||
CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_common_subset_if_union_of_differing_tables")
|
||||
@ -590,8 +590,8 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_errors_if_it_has_nonclass_par
|
||||
|
||||
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think?
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK(toString(result.errors[0]) == "Type family instance keyof<BaseClass | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[1]) == "Type family instance keyof<BaseClass | boolean> is uninhabited");
|
||||
CHECK(toString(result.errors[0]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid");
|
||||
CHECK(toString(result.errors[1]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_common_subset_if_union_of_differing_classes")
|
||||
|
@ -2298,10 +2298,10 @@ end
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||
CHECK(toString(result.errors[1]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||
CHECK(toString(result.errors[2]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||
CHECK(toString(result.errors[3]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||
CHECK(toString(result.errors[0]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub");
|
||||
CHECK(toString(result.errors[1]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub");
|
||||
CHECK(toString(result.errors[2]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub");
|
||||
CHECK(toString(result.errors[3]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4479,4 +4479,31 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_results_compare_to_nil")
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_normalization_preserves_tbl_scopes")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
Module 'l0':
|
||||
do end
|
||||
|
||||
Module 'l1':
|
||||
local _ = {n0=nil,}
|
||||
if if nil then _ then
|
||||
if nil and (_)._ ~= (_)._ then
|
||||
do end
|
||||
while _ do
|
||||
_ = _
|
||||
do end
|
||||
end
|
||||
end
|
||||
do end
|
||||
end
|
||||
local l0
|
||||
while _ do
|
||||
_ = nil
|
||||
(_[_])._ %= `{# _}{bit32.extract(# _,1)}`
|
||||
end
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -219,7 +219,6 @@ TableTests.setmetatable_has_a_side_effect
|
||||
TableTests.shared_selfs
|
||||
TableTests.shared_selfs_from_free_param
|
||||
TableTests.shared_selfs_through_metatables
|
||||
TableTests.should_not_unblock_table_type_twice
|
||||
TableTests.table_call_metamethod_basic
|
||||
TableTests.table_call_metamethod_must_be_callable
|
||||
TableTests.table_param_width_subtyping_2
|
||||
@ -288,7 +287,6 @@ TypeInfer.unify_nearly_identical_recursive_types
|
||||
TypeInferAnyError.can_subscript_any
|
||||
TypeInferAnyError.for_in_loop_iterator_is_error
|
||||
TypeInferAnyError.for_in_loop_iterator_is_error2
|
||||
TypeInferAnyError.metatable_of_any_can_be_a_table
|
||||
TypeInferAnyError.replace_every_free_type_when_unifying_a_complex_function_with_any
|
||||
TypeInferClasses.callable_classes
|
||||
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
||||
|
Loading…
Reference in New Issue
Block a user