mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 17:28:06 +00:00
Merge branch 'upstream' into merge
This commit is contained in:
commit
241fcf8eba
33 changed files with 771 additions and 332 deletions
|
@ -102,4 +102,12 @@ bool subsumesStrict(Scope* left, Scope* right);
|
||||||
// outermost-possible scope.
|
// outermost-possible scope.
|
||||||
bool subsumes(Scope* left, Scope* right);
|
bool subsumes(Scope* left, Scope* right);
|
||||||
|
|
||||||
|
inline Scope* max(Scope* left, Scope* right)
|
||||||
|
{
|
||||||
|
if (subsumes(left, right))
|
||||||
|
return right;
|
||||||
|
else
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMakeStringMethodsChecked, false);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -773,153 +772,87 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
|
||||||
const TypePackId numberVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
const TypePackId numberVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
||||||
|
|
||||||
|
|
||||||
if (FFlag::LuauMakeStringMethodsChecked)
|
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
||||||
{
|
formatFTV.magicFunction = &magicFunctionFormat;
|
||||||
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
formatFTV.isCheckedFunction = true;
|
||||||
formatFTV.magicFunction = &magicFunctionFormat;
|
const TypeId formatFn = arena->addType(formatFTV);
|
||||||
formatFTV.isCheckedFunction = true;
|
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||||
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(
|
const TypeId replArgType =
|
||||||
UnionType{{stringType, arena->addType(TableType({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
arena->addType(UnionType{{stringType, arena->addType(TableType({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
||||||
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ false)}});
|
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ false)}});
|
||||||
const TypeId gsubFunc =
|
const TypeId gsubFunc =
|
||||||
makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType}, /* checked */ false);
|
makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType}, /* checked */ false);
|
||||||
const TypeId gmatchFunc = makeFunction(
|
const TypeId gmatchFunc =
|
||||||
*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionType{emptyPack, stringVariadicList})}, /* checked */ true);
|
makeFunction(*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionType{emptyPack, stringVariadicList})}, /* checked */ true);
|
||||||
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
||||||
attachDcrMagicFunction(gmatchFunc, dcrMagicFunctionGmatch);
|
attachDcrMagicFunction(gmatchFunc, dcrMagicFunctionGmatch);
|
||||||
|
|
||||||
FunctionType matchFuncTy{
|
FunctionType matchFuncTy{
|
||||||
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})};
|
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})};
|
||||||
matchFuncTy.isCheckedFunction = true;
|
matchFuncTy.isCheckedFunction = true;
|
||||||
const TypeId matchFunc = arena->addType(matchFuncTy);
|
const TypeId matchFunc = arena->addType(matchFuncTy);
|
||||||
attachMagicFunction(matchFunc, magicFunctionMatch);
|
attachMagicFunction(matchFunc, magicFunctionMatch);
|
||||||
attachDcrMagicFunction(matchFunc, dcrMagicFunctionMatch);
|
attachDcrMagicFunction(matchFunc, dcrMagicFunctionMatch);
|
||||||
|
|
||||||
FunctionType findFuncTy{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
FunctionType findFuncTy{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
||||||
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})};
|
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})};
|
||||||
findFuncTy.isCheckedFunction = true;
|
findFuncTy.isCheckedFunction = true;
|
||||||
const TypeId findFunc = arena->addType(findFuncTy);
|
const TypeId findFunc = arena->addType(findFuncTy);
|
||||||
attachMagicFunction(findFunc, magicFunctionFind);
|
attachMagicFunction(findFunc, magicFunctionFind);
|
||||||
attachDcrMagicFunction(findFunc, dcrMagicFunctionFind);
|
attachDcrMagicFunction(findFunc, dcrMagicFunctionFind);
|
||||||
|
|
||||||
// string.byte : string -> number? -> number? -> ...number
|
// string.byte : string -> number? -> number? -> ...number
|
||||||
FunctionType stringDotByte{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList};
|
FunctionType stringDotByte{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList};
|
||||||
stringDotByte.isCheckedFunction = true;
|
stringDotByte.isCheckedFunction = true;
|
||||||
|
|
||||||
// string.char : .... number -> string
|
// string.char : .... number -> string
|
||||||
FunctionType stringDotChar{numberVariadicList, arena->addTypePack({stringType})};
|
FunctionType stringDotChar{numberVariadicList, arena->addTypePack({stringType})};
|
||||||
stringDotChar.isCheckedFunction = true;
|
stringDotChar.isCheckedFunction = true;
|
||||||
|
|
||||||
// string.unpack : string -> string -> number? -> ...any
|
// string.unpack : string -> string -> number? -> ...any
|
||||||
FunctionType stringDotUnpack{
|
FunctionType stringDotUnpack{
|
||||||
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
||||||
variadicTailPack,
|
variadicTailPack,
|
||||||
};
|
};
|
||||||
stringDotUnpack.isCheckedFunction = true;
|
stringDotUnpack.isCheckedFunction = true;
|
||||||
|
|
||||||
TableType::Props stringLib = {
|
TableType::Props stringLib = {
|
||||||
{"byte", {arena->addType(stringDotByte)}},
|
{"byte", {arena->addType(stringDotByte)}},
|
||||||
{"char", {arena->addType(stringDotChar)}},
|
{"char", {arena->addType(stringDotChar)}},
|
||||||
{"find", {findFunc}},
|
{"find", {findFunc}},
|
||||||
{"format", {formatFn}}, // FIXME
|
{"format", {formatFn}}, // FIXME
|
||||||
{"gmatch", {gmatchFunc}},
|
{"gmatch", {gmatchFunc}},
|
||||||
{"gsub", {gsubFunc}},
|
{"gsub", {gsubFunc}},
|
||||||
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||||
{"lower", {stringToStringType}},
|
{"lower", {stringToStringType}},
|
||||||
{"match", {matchFunc}},
|
{"match", {matchFunc}},
|
||||||
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType}, /* checked */ true)}},
|
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType}, /* checked */ true)}},
|
||||||
{"reverse", {stringToStringType}},
|
{"reverse", {stringToStringType}},
|
||||||
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType}, /* checked */ true)}},
|
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType}, /* checked */ true)}},
|
||||||
{"upper", {stringToStringType}},
|
{"upper", {stringToStringType}},
|
||||||
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
||||||
{arena->addType(TableType{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})},
|
{arena->addType(TableType{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})},
|
||||||
/* checked */ true)}},
|
/* checked */ true)}},
|
||||||
{"pack", {arena->addType(FunctionType{
|
{"pack", {arena->addType(FunctionType{
|
||||||
arena->addTypePack(TypePack{{stringType}, variadicTailPack}),
|
arena->addTypePack(TypePack{{stringType}, variadicTailPack}),
|
||||||
oneStringPack,
|
oneStringPack,
|
||||||
})}},
|
})}},
|
||||||
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
|
||||||
{"unpack", {arena->addType(stringDotUnpack)}},
|
{"unpack", {arena->addType(stringDotUnpack)}},
|
||||||
};
|
};
|
||||||
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
|
||||||
|
|
||||||
TypeId tableType = arena->addType(TableType{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
||||||
|
|
||||||
if (TableType* ttv = getMutable<TableType>(tableType))
|
TypeId tableType = arena->addType(TableType{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||||
ttv->name = "typeof(string)";
|
|
||||||
|
|
||||||
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
if (TableType* ttv = getMutable<TableType>(tableType))
|
||||||
}
|
ttv->name = "typeof(string)";
|
||||||
else
|
|
||||||
{
|
|
||||||
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
|
|
||||||
formatFTV.magicFunction = &magicFunctionFormat;
|
|
||||||
const TypeId formatFn = arena->addType(formatFTV);
|
|
||||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
|
||||||
|
|
||||||
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType});
|
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||||
|
|
||||||
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});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
|
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
|
||||||
|
|
|
@ -7,11 +7,13 @@
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/StringUtils.h"
|
#include "Luau/StringUtils.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
|
#include "Luau/TypeFamily.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||||
|
|
||||||
|
@ -61,6 +63,23 @@ static std::string wrongNumberOfArgsString(
|
||||||
namespace Luau
|
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
|
struct ErrorConverter
|
||||||
{
|
{
|
||||||
FileResolver* fileResolver = nullptr;
|
FileResolver* fileResolver = nullptr;
|
||||||
|
@ -565,6 +584,96 @@ struct ErrorConverter
|
||||||
|
|
||||||
std::string operator()(const UninhabitedTypeFamily& e) const
|
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";
|
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;
|
state = tttv->state;
|
||||||
|
|
||||||
TypeLevel level = max(httv->level, tttv->level);
|
TypeLevel level = max(httv->level, tttv->level);
|
||||||
|
Scope* scope = max(httv->scope, tttv->scope);
|
||||||
|
|
||||||
std::unique_ptr<TableType> result = nullptr;
|
std::unique_ptr<TableType> result = nullptr;
|
||||||
bool hereSubThere = true;
|
bool hereSubThere = true;
|
||||||
|
@ -2644,7 +2645,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||||
if (prop.readTy || prop.writeTy)
|
if (prop.readTy || prop.writeTy)
|
||||||
{
|
{
|
||||||
if (!result.get())
|
if (!result.get())
|
||||||
result = std::make_unique<TableType>(TableType{state, level});
|
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||||
result->props[name] = prop;
|
result->props[name] = prop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2654,7 +2655,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||||
if (httv->props.count(name) == 0)
|
if (httv->props.count(name) == 0)
|
||||||
{
|
{
|
||||||
if (!result.get())
|
if (!result.get())
|
||||||
result = std::make_unique<TableType>(TableType{state, level});
|
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||||
|
|
||||||
result->props[name] = tprop;
|
result->props[name] = tprop;
|
||||||
hereSubThere = false;
|
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 index = unionType(httv->indexer->indexType, tttv->indexer->indexType);
|
||||||
TypeId indexResult = intersectionType(httv->indexer->indexResultType, tttv->indexer->indexResultType);
|
TypeId indexResult = intersectionType(httv->indexer->indexResultType, tttv->indexer->indexResultType);
|
||||||
if (!result.get())
|
if (!result.get())
|
||||||
result = std::make_unique<TableType>(TableType{state, level});
|
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||||
result->indexer = {index, indexResult};
|
result->indexer = {index, indexResult};
|
||||||
hereSubThere &= (httv->indexer->indexType == index) && (httv->indexer->indexResultType == indexResult);
|
hereSubThere &= (httv->indexer->indexType == index) && (httv->indexer->indexResultType == indexResult);
|
||||||
thereSubHere &= (tttv->indexer->indexType == index) && (tttv->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)
|
else if (httv->indexer)
|
||||||
{
|
{
|
||||||
if (!result.get())
|
if (!result.get())
|
||||||
result = std::make_unique<TableType>(TableType{state, level});
|
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||||
result->indexer = httv->indexer;
|
result->indexer = httv->indexer;
|
||||||
thereSubHere = false;
|
thereSubHere = false;
|
||||||
}
|
}
|
||||||
else if (tttv->indexer)
|
else if (tttv->indexer)
|
||||||
{
|
{
|
||||||
if (!result.get())
|
if (!result.get())
|
||||||
result = std::make_unique<TableType>(TableType{state, level});
|
result = std::make_unique<TableType>(TableType{state, level, scope});
|
||||||
result->indexer = tttv->indexer;
|
result->indexer = tttv->indexer;
|
||||||
hereSubThere = false;
|
hereSubThere = false;
|
||||||
}
|
}
|
||||||
|
@ -2697,7 +2698,7 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||||
if (result.get())
|
if (result.get())
|
||||||
table = arena->addType(std::move(*result));
|
table = arena->addType(std::move(*result));
|
||||||
else
|
else
|
||||||
table = arena->addType(TableType{state, level});
|
table = arena->addType(TableType{state, level, scope});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmtable && hmtable)
|
if (tmtable && hmtable)
|
||||||
|
|
|
@ -1255,6 +1255,10 @@ TypeId TypeSimplifier::union_(TypeId left, TypeId right)
|
||||||
case Relation::Coincident:
|
case Relation::Coincident:
|
||||||
case Relation::Superset:
|
case Relation::Superset:
|
||||||
return left;
|
return left;
|
||||||
|
case Relation::Subset:
|
||||||
|
newParts.insert(right);
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
newParts.insert(part);
|
newParts.insert(part);
|
||||||
newParts.insert(right);
|
newParts.insert(right);
|
||||||
|
|
|
@ -1242,13 +1242,14 @@ struct TypeChecker2
|
||||||
|
|
||||||
void visit(AstExprConstantBool* expr)
|
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 bestType = expr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
||||||
const TypeId inferredType = lookupType(expr);
|
const TypeId inferredType = lookupType(expr);
|
||||||
|
|
||||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType));
|
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||||
#endif
|
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstExprConstantNumber* expr)
|
void visit(AstExprConstantNumber* expr)
|
||||||
|
@ -1264,13 +1265,14 @@ struct TypeChecker2
|
||||||
|
|
||||||
void visit(AstExprConstantString* expr)
|
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 bestType = module->internalTypes.addType(SingletonType{StringSingleton{std::string{expr->value.data, expr->value.size}}});
|
||||||
const TypeId inferredType = lookupType(expr);
|
const TypeId inferredType = lookupType(expr);
|
||||||
|
|
||||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType));
|
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||||
#endif
|
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstExprLocal* expr)
|
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.
|
// when this value is set to a negative value, guessing will be totally disabled.
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolver);
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false);
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -184,7 +184,7 @@ struct FamilyReducer
|
||||||
if (subject->owningArena != ctx.arena.get())
|
if (subject->owningArena != ctx.arena.get())
|
||||||
ctx.ice->ice("Attempting to modify a type family instance from another arena", location);
|
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());
|
printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str());
|
||||||
|
|
||||||
asMutable(subject)->ty.template emplace<Unifiable::Bound<T>>(replacement);
|
asMutable(subject)->ty.template emplace<Unifiable::Bound<T>>(replacement);
|
||||||
|
@ -206,7 +206,7 @@ struct FamilyReducer
|
||||||
|
|
||||||
if (reduction.uninhabited || force)
|
if (reduction.uninhabited || force)
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauLogSolver)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("%s is uninhabited\n", toString(subject, {true}).c_str());
|
printf("%s is uninhabited\n", toString(subject, {true}).c_str());
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, TypeId>)
|
if constexpr (std::is_same_v<T, TypeId>)
|
||||||
|
@ -216,7 +216,7 @@ struct FamilyReducer
|
||||||
}
|
}
|
||||||
else if (!reduction.uninhabited && !force)
|
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(),
|
printf("%s is irreducible; blocked on %zu types, %zu packs\n", toString(subject, {true}).c_str(), reduction.blockedTypes.size(),
|
||||||
reduction.blockedPacks.size());
|
reduction.blockedPacks.size());
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ struct FamilyReducer
|
||||||
|
|
||||||
if (skip == SkipTestResult::Irreducible)
|
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());
|
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||||
|
|
||||||
irreducible.insert(subject);
|
irreducible.insert(subject);
|
||||||
|
@ -251,7 +251,7 @@ struct FamilyReducer
|
||||||
}
|
}
|
||||||
else if (skip == SkipTestResult::Defer)
|
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());
|
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>)
|
if constexpr (std::is_same_v<T, TypeId>)
|
||||||
|
@ -269,7 +269,7 @@ struct FamilyReducer
|
||||||
|
|
||||||
if (skip == SkipTestResult::Irreducible)
|
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());
|
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||||
|
|
||||||
irreducible.insert(subject);
|
irreducible.insert(subject);
|
||||||
|
@ -277,7 +277,7 @@ struct FamilyReducer
|
||||||
}
|
}
|
||||||
else if (skip == SkipTestResult::Defer)
|
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());
|
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>)
|
if constexpr (std::is_same_v<T, TypeId>)
|
||||||
|
@ -297,7 +297,7 @@ struct FamilyReducer
|
||||||
{
|
{
|
||||||
if (shouldGuess.contains(subject))
|
if (shouldGuess.contains(subject))
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauLogSolver)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("Flagged %s for reduction with guesser.\n", toString(subject, {true}).c_str());
|
printf("Flagged %s for reduction with guesser.\n", toString(subject, {true}).c_str());
|
||||||
|
|
||||||
TypeFamilyReductionGuesser guesser{ctx.arena, ctx.builtins, ctx.normalizer};
|
TypeFamilyReductionGuesser guesser{ctx.arena, ctx.builtins, ctx.normalizer};
|
||||||
|
@ -305,14 +305,14 @@ struct FamilyReducer
|
||||||
|
|
||||||
if (guessed)
|
if (guessed)
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauLogSolver)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("Selected %s as the guessed result type.\n", toString(*guessed, {true}).c_str());
|
printf("Selected %s as the guessed result type.\n", toString(*guessed, {true}).c_str());
|
||||||
|
|
||||||
replace(subject, *guessed);
|
replace(subject, *guessed);
|
||||||
return true;
|
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());
|
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))
|
if (irreducible.contains(subject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogSolver)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
||||||
|
|
||||||
if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(subject))
|
if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(subject))
|
||||||
|
@ -337,7 +337,7 @@ struct FamilyReducer
|
||||||
|
|
||||||
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFamily)
|
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");
|
printf("Irreducible due to irreducible/pending and a non-cyclic family\n");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -361,7 +361,7 @@ struct FamilyReducer
|
||||||
if (irreducible.contains(subject))
|
if (irreducible.contains(subject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogSolver)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
||||||
|
|
||||||
if (const TypeFamilyInstanceTypePack* tfit = get<TypeFamilyInstanceTypePack>(subject))
|
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.
|
// SharedCodeGenContext must be destroyed before this function is called.
|
||||||
void destroySharedCodeGenContext(const SharedCodeGenContext* codeGenContext) noexcept;
|
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);
|
||||||
|
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);
|
void create(lua_State* L, SharedCodeGenContext* codeGenContext);
|
||||||
|
|
||||||
// Check if native execution is enabled
|
// 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
|
// 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
|
class UnwindBuilder
|
||||||
{
|
{
|
||||||
|
@ -52,11 +52,10 @@ public:
|
||||||
virtual void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
virtual void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||||
const std::vector<X64::RegisterX64>& simd) = 0;
|
const std::vector<X64::RegisterX64>& simd) = 0;
|
||||||
|
|
||||||
virtual size_t getSize() const = 0;
|
virtual size_t getUnwindInfoSize(size_t blockSize) const = 0;
|
||||||
virtual size_t getFunctionCount() const = 0;
|
|
||||||
|
|
||||||
// This will place the unwinding data at the target address and might update values of some fields
|
// 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
|
} // namespace CodeGen
|
||||||
|
|
|
@ -33,10 +33,9 @@ public:
|
||||||
void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||||
const std::vector<X64::RegisterX64>& simd) override;
|
const std::vector<X64::RegisterX64>& simd) override;
|
||||||
|
|
||||||
size_t getSize() const override;
|
size_t getUnwindInfoSize(size_t blockSize = 0) const override;
|
||||||
size_t getFunctionCount() 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:
|
private:
|
||||||
size_t beginOffset = 0;
|
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,
|
void prologueX64(uint32_t prologueSize, uint32_t stackSize, bool setupFrame, std::initializer_list<X64::RegisterX64> gpr,
|
||||||
const std::vector<X64::RegisterX64>& simd) override;
|
const std::vector<X64::RegisterX64>& simd) override;
|
||||||
|
|
||||||
size_t getSize() const override;
|
size_t getUnwindInfoSize(size_t blockSize = 0) const override;
|
||||||
size_t getFunctionCount() 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:
|
private:
|
||||||
size_t beginOffset = 0;
|
size_t beginOffset = 0;
|
||||||
|
|
|
@ -102,17 +102,17 @@ void* createBlockUnwindInfo(void* context, uint8_t* block, size_t blockSize, siz
|
||||||
UnwindBuilder* unwind = (UnwindBuilder*)context;
|
UnwindBuilder* unwind = (UnwindBuilder*)context;
|
||||||
|
|
||||||
// All unwinding related data is placed together at the start of the block
|
// 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
|
unwindSize = (unwindSize + (kCodeAlignment - 1)) & ~(kCodeAlignment - 1); // Match code allocator alignment
|
||||||
CODEGEN_ASSERT(blockSize >= unwindSize);
|
CODEGEN_ASSERT(blockSize >= unwindSize);
|
||||||
|
|
||||||
char* unwindData = (char*)block;
|
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 defined(_WIN32) && defined(CODEGEN_TARGET_X64)
|
||||||
|
|
||||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
|
#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");
|
CODEGEN_ASSERT(!"Failed to allocate function table");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -95,10 +95,6 @@ std::string toString(const CodeGenCompilationResult& result)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void* gPerfLogContext = nullptr;
|
|
||||||
PerfLogFn gPerfLogFn = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
void onDisable(lua_State* L, Proto* proto)
|
void onDisable(lua_State* L, Proto* proto)
|
||||||
{
|
{
|
||||||
// do nothing if proto already uses bytecode
|
// do nothing if proto already uses bytecode
|
||||||
|
@ -196,59 +192,5 @@ bool isSupported()
|
||||||
#endif
|
#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 CodeGen
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -253,7 +253,7 @@ static EntryLocations buildEntryFunction(AssemblyBuilderA64& build, UnwindBuilde
|
||||||
// Our entry function is special, it spans the whole remaining code area
|
// Our entry function is special, it spans the whole remaining code area
|
||||||
unwind.startFunction();
|
unwind.startFunction();
|
||||||
unwind.prologueA64(prologueSize, kStackSize, {x29, x30, x19, x20, x21, x22, x23, x24, x25});
|
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;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,17 @@ namespace CodeGen
|
||||||
static const Instruction kCodeEntryInsn = LOP_NATIVECALL;
|
static const Instruction kCodeEntryInsn = LOP_NATIVECALL;
|
||||||
|
|
||||||
// From CodeGen.cpp
|
// From CodeGen.cpp
|
||||||
extern void* gPerfLogContext;
|
static void* gPerfLogContext = nullptr;
|
||||||
extern PerfLogFn gPerfLogFn;
|
static PerfLogFn gPerfLogFn = nullptr;
|
||||||
|
|
||||||
unsigned int getCpuFeaturesA64();
|
unsigned int getCpuFeaturesA64();
|
||||||
|
|
||||||
|
void setPerfLog(void* context, PerfLogFn logFn)
|
||||||
|
{
|
||||||
|
gPerfLogContext = context;
|
||||||
|
gPerfLogFn = logFn;
|
||||||
|
}
|
||||||
|
|
||||||
static void logPerfFunction(Proto* p, uintptr_t addr, unsigned size)
|
static void logPerfFunction(Proto* p, uintptr_t addr, unsigned size)
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(p->source);
|
CODEGEN_ASSERT(p->source);
|
||||||
|
@ -365,17 +371,17 @@ static void initializeExecutionCallbacks(lua_State* L, BaseCodeGenContext* codeG
|
||||||
ecb->getmemorysize = getMemorySize;
|
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::unique_ptr<StandaloneCodeGenContext> codeGenContext =
|
||||||
std::make_unique<StandaloneCodeGenContext>(blockSize, maxTotalSize, allocationCallback, allocationCallbackContext);
|
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());
|
initializeExecutionCallbacks(L, codeGenContext.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_NEW(lua_State* L, SharedCodeGenContext* codeGenContext)
|
void create(lua_State* L, SharedCodeGenContext* codeGenContext)
|
||||||
{
|
{
|
||||||
initializeExecutionCallbacks(L, codeGenContext);
|
initializeExecutionCallbacks(L, codeGenContext);
|
||||||
}
|
}
|
||||||
|
@ -575,22 +581,32 @@ template<typename AssemblyBuilder>
|
||||||
return compilationResult;
|
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);
|
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);
|
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;
|
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)
|
if (getCodeGenContext(L) != nullptr)
|
||||||
L->global->ecb.enter = enabled ? onEnter : onEnterDisabled;
|
L->global->ecb.enter = enabled ? onEnter : onEnterDisabled;
|
||||||
|
|
|
@ -88,33 +88,5 @@ private:
|
||||||
SharedCodeAllocator sharedAllocator;
|
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 CodeGen
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -181,7 +181,7 @@ static EntryLocations buildEntryFunction(AssemblyBuilderX64& build, UnwindBuilde
|
||||||
build.ret();
|
build.ret();
|
||||||
|
|
||||||
// Our entry function is special, it spans the whole remaining code area
|
// 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;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCodegenSplitDoarith, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
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, luauRegAddress(ra));
|
||||||
callWrap.addArgument(SizeX64::qword, b);
|
callWrap.addArgument(SizeX64::qword, b);
|
||||||
callWrap.addArgument(SizeX64::qword, c);
|
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);
|
emitUpdateBase(build);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5)
|
LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5)
|
||||||
|
LUAU_FASTFLAG(LuauCodegenSplitDoarith)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1242,9 +1243,47 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
else
|
else
|
||||||
build.add(x3, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
build.add(x3, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
||||||
|
|
||||||
build.mov(w4, TMS(intOp(inst.d)));
|
if (FFlag::LuauCodegenSplitDoarith)
|
||||||
build.ldr(x5, mem(rNativeContext, offsetof(NativeContext, luaV_doarith)));
|
{
|
||||||
build.blr(x5);
|
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);
|
emitUpdateBase(build);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -43,6 +43,16 @@ void initFunctions(NativeState& data)
|
||||||
data.context.luaV_lessequal = luaV_lessequal;
|
data.context.luaV_lessequal = luaV_lessequal;
|
||||||
data.context.luaV_equalval = luaV_equalval;
|
data.context.luaV_equalval = luaV_equalval;
|
||||||
data.context.luaV_doarith = luaV_doarith;
|
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_dolen = luaV_dolen;
|
||||||
data.context.luaV_gettable = luaV_gettable;
|
data.context.luaV_gettable = luaV_gettable;
|
||||||
data.context.luaV_settable = luaV_settable;
|
data.context.luaV_settable = luaV_settable;
|
||||||
|
@ -121,6 +131,16 @@ void initFunctions(NativeContext& context)
|
||||||
context.luaV_lessequal = luaV_lessequal;
|
context.luaV_lessequal = luaV_lessequal;
|
||||||
context.luaV_equalval = luaV_equalval;
|
context.luaV_equalval = luaV_equalval;
|
||||||
context.luaV_doarith = luaV_doarith;
|
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_dolen = luaV_dolen;
|
||||||
context.luaV_gettable = luaV_gettable;
|
context.luaV_gettable = luaV_gettable;
|
||||||
context.luaV_settable = luaV_settable;
|
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_lessequal)(lua_State* L, const TValue* l, const TValue* r) = nullptr;
|
||||||
int (*luaV_equalval)(lua_State* L, const TValue* t1, const TValue* t2) = 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_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_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_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;
|
void (*luaV_settable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
||||||
|
|
|
@ -202,7 +202,7 @@ void UnwindBuilderDwarf2::finishInfo()
|
||||||
// Terminate section
|
// Terminate section
|
||||||
pos = writeu32(pos, 0);
|
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)
|
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);
|
CODEGEN_ASSERT(prologueOffset == prologueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t UnwindBuilderDwarf2::getSize() const
|
size_t UnwindBuilderDwarf2::getUnwindInfoSize(size_t blockSize) const
|
||||||
{
|
{
|
||||||
return size_t(pos - rawData);
|
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();
|
memcpy(target, rawData, getUnwindInfoSize());
|
||||||
}
|
|
||||||
|
|
||||||
void UnwindBuilderDwarf2::finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const
|
|
||||||
{
|
|
||||||
memcpy(target, rawData, getSize());
|
|
||||||
|
|
||||||
for (const UnwindFunctionDwarf2& func : unwindFunctions)
|
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);
|
writeu64(fdeEntry + kFdeInitialLocationOffset, uintptr_t(funcAddress) + offset + func.beginOffset);
|
||||||
|
|
||||||
if (func.endOffset == kFullBlockFuncton)
|
if (func.endOffset == kFullBlockFunction)
|
||||||
writeu64(fdeEntry + kFdeAddressRangeOffset, funcSize - offset);
|
writeu64(fdeEntry + kFdeAddressRangeOffset, blockSize - offset);
|
||||||
else
|
else
|
||||||
writeu64(fdeEntry + kFdeAddressRangeOffset, func.endOffset - func.beginOffset);
|
writeu64(fdeEntry + kFdeAddressRangeOffset, func.endOffset - func.beginOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return unwindFunctions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CodeGen
|
} // namespace CodeGen
|
||||||
|
|
|
@ -194,17 +194,12 @@ void UnwindBuilderWin::prologueX64(uint32_t prologueSize, uint32_t stackSize, bo
|
||||||
this->prologSize = prologueSize;
|
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);
|
return sizeof(UnwindFunctionWin) * unwindFunctions.size() + size_t(rawDataPos - rawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t UnwindBuilderWin::getFunctionCount() const
|
size_t UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress, size_t blockSize) const
|
||||||
{
|
|
||||||
return unwindFunctions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress, size_t funcSize) const
|
|
||||||
{
|
{
|
||||||
// Copy adjusted function information
|
// Copy adjusted function information
|
||||||
for (UnwindFunctionWin func : unwindFunctions)
|
for (UnwindFunctionWin func : unwindFunctions)
|
||||||
|
@ -213,8 +208,8 @@ void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress,
|
||||||
func.beginOffset += uint32_t(offset);
|
func.beginOffset += uint32_t(offset);
|
||||||
|
|
||||||
// Whole block is a part of a 'single function'
|
// Whole block is a part of a 'single function'
|
||||||
if (func.endOffset == kFullBlockFuncton)
|
if (func.endOffset == kFullBlockFunction)
|
||||||
func.endOffset = uint32_t(funcSize);
|
func.endOffset = uint32_t(blockSize);
|
||||||
else
|
else
|
||||||
func.endOffset += uint32_t(offset);
|
func.endOffset += uint32_t(offset);
|
||||||
|
|
||||||
|
@ -226,6 +221,8 @@ void UnwindBuilderWin::finalize(char* target, size_t offset, void* funcAddress,
|
||||||
|
|
||||||
// Copy unwind codes
|
// Copy unwind codes
|
||||||
memcpy(target, rawData, size_t(rawDataPos - rawData));
|
memcpy(target, rawData, size_t(rawDataPos - rawData));
|
||||||
|
|
||||||
|
return unwindFunctions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CodeGen
|
} // 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_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 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);
|
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 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 TValue* luaV_tonumber(const TValue* obj, TValue* n);
|
||||||
LUAI_FUNC const float* luaV_tovector(const TValue* obj);
|
LUAI_FUNC const float* luaV_tovector(const TValue* obj);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauVmSplitDoarith, false)
|
||||||
|
|
||||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#if __has_warning("-Wc99-designator")
|
#if __has_warning("-Wc99-designator")
|
||||||
|
@ -1487,7 +1489,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1533,7 +1542,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1594,7 +1610,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1655,7 +1678,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1703,7 +1733,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1727,7 +1764,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1748,7 +1792,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1769,7 +1820,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1790,7 +1848,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1835,7 +1900,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1881,7 +1953,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1928,7 +2007,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1952,7 +2038,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1979,7 +2072,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2092,7 +2192,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2711,7 +2818,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2739,7 +2853,14 @@ reentry:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slow-path, may invoke C/Lua via metamethods
|
// 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();
|
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
|
} 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)
|
void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op)
|
||||||
{
|
{
|
||||||
TValue tempb, tempc;
|
TValue tempb, tempc;
|
||||||
|
|
|
@ -191,7 +191,7 @@ TEST_CASE("WindowsUnwindCodesX64")
|
||||||
unwind.finishInfo();
|
unwind.finishInfo();
|
||||||
|
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
data.resize(unwind.getSize());
|
data.resize(unwind.getUnwindInfoSize());
|
||||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
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,
|
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();
|
unwind.finishInfo();
|
||||||
|
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
data.resize(unwind.getSize());
|
data.resize(unwind.getUnwindInfoSize());
|
||||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
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,
|
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();
|
unwind.finishInfo();
|
||||||
|
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
data.resize(unwind.getSize());
|
data.resize(unwind.getUnwindInfoSize());
|
||||||
unwind.finalize(data.data(), 0, nullptr, 0);
|
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,
|
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;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ErrorTests");
|
TEST_SUITE_BEGIN("ErrorTests");
|
||||||
|
|
||||||
TEST_CASE("TypeError_code_should_return_nonzero_code")
|
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]));
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -214,6 +214,14 @@ TEST_CASE_FIXTURE(SimplifyFixture, "any_and_indeterminate_types")
|
||||||
CHECK(errorTy == anyLhsPending->options[1]);
|
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")
|
TEST_CASE_FIXTURE(SimplifyFixture, "unknown_and_indeterminate_types")
|
||||||
{
|
{
|
||||||
CHECK(freeTy == intersect(unknownTy, freeTy));
|
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?
|
// 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);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
CHECK(toString(result.errors[0]) == "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 family instance keyof<MyObject | boolean> is uninhabited");
|
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")
|
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?
|
// 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);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
CHECK(toString(result.errors[0]) == "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 family instance rawkeyof<MyObject | boolean> is uninhabited");
|
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")
|
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?
|
// 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);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
CHECK(toString(result.errors[0]) == "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 family instance keyof<BaseClass | boolean> is uninhabited");
|
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")
|
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_common_subset_if_union_of_differing_classes")
|
||||||
|
|
|
@ -2298,10 +2298,10 @@ end
|
||||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
CHECK(toString(result.errors[0]) == "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]) == "Type family instance sub<unknown, number> is uninhabited");
|
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]) == "Type family instance sub<unknown, number> is uninhabited");
|
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]) == "Type family instance sub<unknown, number> is uninhabited");
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -4479,4 +4479,31 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_results_compare_to_nil")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -219,7 +219,6 @@ TableTests.setmetatable_has_a_side_effect
|
||||||
TableTests.shared_selfs
|
TableTests.shared_selfs
|
||||||
TableTests.shared_selfs_from_free_param
|
TableTests.shared_selfs_from_free_param
|
||||||
TableTests.shared_selfs_through_metatables
|
TableTests.shared_selfs_through_metatables
|
||||||
TableTests.should_not_unblock_table_type_twice
|
|
||||||
TableTests.table_call_metamethod_basic
|
TableTests.table_call_metamethod_basic
|
||||||
TableTests.table_call_metamethod_must_be_callable
|
TableTests.table_call_metamethod_must_be_callable
|
||||||
TableTests.table_param_width_subtyping_2
|
TableTests.table_param_width_subtyping_2
|
||||||
|
@ -288,7 +287,6 @@ TypeInfer.unify_nearly_identical_recursive_types
|
||||||
TypeInferAnyError.can_subscript_any
|
TypeInferAnyError.can_subscript_any
|
||||||
TypeInferAnyError.for_in_loop_iterator_is_error
|
TypeInferAnyError.for_in_loop_iterator_is_error
|
||||||
TypeInferAnyError.for_in_loop_iterator_is_error2
|
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
|
TypeInferAnyError.replace_every_free_type_when_unifying_a_complex_function_with_any
|
||||||
TypeInferClasses.callable_classes
|
TypeInferClasses.callable_classes
|
||||||
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
||||||
|
|
Loading…
Reference in a new issue