mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/647 (#1469)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos-arm version:macos-14]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-latest]) (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-20.04]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:windows version:windows-latest]) (push) Has been cancelled
release / web (push) Has been cancelled
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos-arm version:macos-14]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-latest]) (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-20.04]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:windows version:windows-latest]) (push) Has been cancelled
release / web (push) Has been cancelled
# General Updates Fix an old solver crash that occurs in the presence of cyclic `requires()` ## New Solver - Improvements to Luau user-defined type function library - Avoid asserting on unexpected metatable types - Properties in user defined type functions should have a consistent iteration order - in this case it is insertion ordering # Runtime - Track VM allocations for telemetry --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
This commit is contained in:
parent
4559ef26b2
commit
77295c3610
@ -6,7 +6,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using lua_State = struct lua_State;
|
||||
@ -182,7 +182,7 @@ struct TypeFunctionProperty
|
||||
struct TypeFunctionTableType
|
||||
{
|
||||
using Name = std::string;
|
||||
using Props = std::unordered_map<Name, TypeFunctionProperty>;
|
||||
using Props = std::map<Name, TypeFunctionProperty>;
|
||||
|
||||
Props props;
|
||||
|
||||
@ -195,7 +195,7 @@ struct TypeFunctionTableType
|
||||
struct TypeFunctionClassType
|
||||
{
|
||||
using Name = std::string;
|
||||
using Props = std::unordered_map<Name, TypeFunctionProperty>;
|
||||
using Props = std::map<Name, TypeFunctionProperty>;
|
||||
|
||||
Props props;
|
||||
|
||||
@ -260,6 +260,7 @@ bool isTypeUserData(lua_State* L, int idx);
|
||||
TypeFunctionTypeId getTypeUserData(lua_State* L, int idx);
|
||||
std::optional<TypeFunctionTypeId> optionalTypeUserData(lua_State* L, int idx);
|
||||
|
||||
void registerTypesLibrary(lua_State* L);
|
||||
void registerTypeUserData(lua_State* L);
|
||||
|
||||
void setTypeFunctionEnvironment(lua_State* L);
|
||||
|
@ -26,7 +26,6 @@
|
||||
*/
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAGVARIABLE(LuauDCRMagicFunctionTypeChecker, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -931,7 +930,6 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
|
||||
formatFTV.isCheckedFunction = true;
|
||||
const TypeId formatFn = arena->addType(formatFTV);
|
||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||
if (FFlag::LuauDCRMagicFunctionTypeChecker)
|
||||
attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat);
|
||||
|
||||
|
||||
|
@ -31,10 +31,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings, false)
|
||||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||
|
||||
// The default value here is 643 because the first release in which this was implemented is 644,
|
||||
// and actively we want new changes to be off by default until they're enabled consciously.
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeSolverRelease, 643)
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
std::string DiffPathNode::toString() const
|
||||
{
|
||||
switch (kind)
|
||||
@ -944,12 +945,14 @@ std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator DifferEnvironment
|
||||
return visitingStack.crend();
|
||||
}
|
||||
|
||||
|
||||
DifferResult diff(TypeId ty1, TypeId ty2)
|
||||
{
|
||||
DifferEnvironment differEnv{ty1, ty2, std::nullopt, std::nullopt};
|
||||
return diffUsingEnv(differEnv, ty1, ty2);
|
||||
}
|
||||
|
||||
|
||||
DifferResult diffWithSymbols(TypeId ty1, TypeId ty2, std::optional<std::string> symbol1, std::optional<std::string> symbol2)
|
||||
{
|
||||
DifferEnvironment differEnv{ty1, ty2, symbol1, symbol2};
|
||||
|
@ -44,9 +44,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSourceModuleUpdatedWithSelectedMode, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMoreThoroughCycleDetection, false)
|
||||
|
||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||
|
||||
@ -883,14 +883,18 @@ void Frontend::addBuildQueueItems(
|
||||
data.environmentScope = getModuleEnvironment(*sourceModule, data.config, frontendOptions.forAutocomplete);
|
||||
data.recordJsonLog = FFlag::DebugLuauLogSolverToJson;
|
||||
|
||||
Mode mode = sourceModule->mode.value_or(data.config.mode);
|
||||
const Mode mode = sourceModule->mode.value_or(data.config.mode);
|
||||
|
||||
// in NoCheck mode we only need to compute the value of .cyclic for typeck
|
||||
// in the future we could replace toposort with an algorithm that can flag cyclic nodes by itself
|
||||
// however, for now getRequireCycles isn't expensive in practice on the cases we care about, and long term
|
||||
// all correct programs must be acyclic so this code triggers rarely
|
||||
if (cycleDetected)
|
||||
{
|
||||
if (FFlag::LuauMoreThoroughCycleDetection)
|
||||
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), false);
|
||||
else
|
||||
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), mode == Mode::NoCheck);
|
||||
}
|
||||
|
||||
data.options = frontendOptions;
|
||||
|
||||
@ -922,7 +926,6 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||
else
|
||||
mode = sourceModule.mode.value_or(config.mode);
|
||||
|
||||
if (FFlag::LuauSourceModuleUpdatedWithSelectedMode)
|
||||
item.sourceModule->mode = {mode};
|
||||
ScopePtr environmentScope = item.environmentScope;
|
||||
double timestamp = getTimestamp();
|
||||
|
@ -16,8 +16,6 @@
|
||||
#include "Luau/Unifier.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixReduceStackPressure, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicTablesBlowingStack, false);
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
@ -25,16 +23,6 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAGVARIABLE(LuauUseNormalizeIntersectionLimit, false)
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
||||
|
||||
static bool fixReduceStackPressure()
|
||||
{
|
||||
return FFlag::LuauFixReduceStackPressure || FFlag::LuauSolverV2;
|
||||
}
|
||||
|
||||
static bool fixCyclicTablesBlowingStack()
|
||||
{
|
||||
return FFlag::LuauFixCyclicTablesBlowingStack || FFlag::LuauSolverV2;
|
||||
}
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -2583,12 +2571,8 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
if (tprop.readTy.has_value())
|
||||
{
|
||||
// if the intersection of the read types of a property is uninhabited, the whole table is `never`.
|
||||
if (fixReduceStackPressure())
|
||||
{
|
||||
// We've seen these table prop elements before and we're about to ask if their intersection
|
||||
// is inhabited
|
||||
if (fixCyclicTablesBlowingStack())
|
||||
{
|
||||
if (seenSet.contains(*hprop.readTy) && seenSet.contains(*tprop.readTy))
|
||||
{
|
||||
seenSet.erase(*hprop.readTy);
|
||||
@ -2600,25 +2584,15 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
seenSet.insert(*hprop.readTy);
|
||||
seenSet.insert(*tprop.readTy);
|
||||
}
|
||||
}
|
||||
|
||||
NormalizationResult res = isIntersectionInhabited(*hprop.readTy, *tprop.readTy);
|
||||
|
||||
// Cleanup
|
||||
if (fixCyclicTablesBlowingStack())
|
||||
{
|
||||
seenSet.erase(*hprop.readTy);
|
||||
seenSet.erase(*tprop.readTy);
|
||||
}
|
||||
|
||||
if (NormalizationResult::True != res)
|
||||
return {builtinTypes->neverType};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NormalizationResult::False == isIntersectionInhabited(*hprop.readTy, *tprop.readTy))
|
||||
return {builtinTypes->neverType};
|
||||
}
|
||||
|
||||
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
|
||||
prop.readTy = ty;
|
||||
|
@ -1624,9 +1624,12 @@ void TypeChecker2::indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const M
|
||||
else if (auto mtmt = get<MetatableType>(follow(metaTable->metatable)))
|
||||
indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType);
|
||||
else
|
||||
{
|
||||
if (!(DFInt::LuauTypeSolverRelease >= 647))
|
||||
{
|
||||
LUAU_ASSERT(tt || get<PrimitiveType>(follow(metaTable->table)));
|
||||
|
||||
}
|
||||
// CLI-122161: We're not handling unions correctly (probably).
|
||||
reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location);
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2, false)
|
||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
|
||||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
|
||||
@ -1018,9 +1019,11 @@ void TypeFunctionRuntime::prepareState()
|
||||
|
||||
setTypeFunctionEnvironment(L);
|
||||
|
||||
// Register type userdata
|
||||
registerTypeUserData(L);
|
||||
|
||||
if (FFlag::LuauUserTypeFunFixRegister)
|
||||
registerTypesLibrary(L);
|
||||
|
||||
luaL_sandbox(L);
|
||||
luaL_sandboxthread(L);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixRegister, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -1381,8 +1382,125 @@ static int isEqualToType(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Register the type userdata
|
||||
void registerTypesLibrary(lua_State* L)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUserTypeFunFixRegister);
|
||||
|
||||
luaL_Reg fields[] = {
|
||||
{"unknown", createUnknown},
|
||||
{"never", createNever},
|
||||
{"any", createAny},
|
||||
{"boolean", createBoolean},
|
||||
{"number", createNumber},
|
||||
{"string", createString},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
luaL_Reg methods[] = {
|
||||
{"singleton", createSingleton},
|
||||
{"negationof", createNegation},
|
||||
{"unionof", createUnion},
|
||||
{"intersectionof", createIntersection},
|
||||
{"newtable", createTable},
|
||||
{"newfunction", createFunction},
|
||||
{"copy", deepCopy},
|
||||
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
luaL_register(L, "types", methods);
|
||||
|
||||
// Set fields for type userdata
|
||||
for (luaL_Reg* l = fields; l->name; l++)
|
||||
{
|
||||
l->func(L);
|
||||
lua_setfield(L, -2, l->name);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static int typeUserdataIndex(lua_State* L)
|
||||
{
|
||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||
const char* field = luaL_checkstring(L, 2);
|
||||
|
||||
if (strcmp(field, "tag") == 0)
|
||||
{
|
||||
lua_pushstring(L, getTag(L, self).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_getfield(L, -1, field);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void registerTypeUserData(lua_State* L)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunFixRegister)
|
||||
{
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
{"is", checkTag},
|
||||
|
||||
// Negation type methods
|
||||
{"inner", getNegatedValue},
|
||||
|
||||
// Singleton type methods
|
||||
{"value", getSingletonValue},
|
||||
|
||||
// Table type methods
|
||||
{"setproperty", setTableProp},
|
||||
{"setreadproperty", setReadTableProp},
|
||||
{"setwriteproperty", setWriteTableProp},
|
||||
{"readproperty", readTableProp},
|
||||
{"writeproperty", writeTableProp},
|
||||
{"properties", getProps},
|
||||
{"setindexer", setTableIndexer},
|
||||
{"setreadindexer", setTableReadIndexer},
|
||||
{"setwriteindexer", setTableWriteIndexer},
|
||||
{"indexer", getIndexer},
|
||||
{"readindexer", getReadIndexer},
|
||||
{"writeindexer", getWriteIndexer},
|
||||
{"setmetatable", setTableMetatable},
|
||||
{"metatable", getMetatable},
|
||||
|
||||
// Function type methods
|
||||
{"setparameters", setFunctionParameters},
|
||||
{"parameters", getFunctionParameters},
|
||||
{"setreturns", setFunctionReturns},
|
||||
{"returns", getFunctionReturns},
|
||||
|
||||
// Union and Intersection type methods
|
||||
{"components", getComponents},
|
||||
|
||||
// Class type methods
|
||||
{"parent", getClassParent},
|
||||
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
// Protect metatable from being changed
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
|
||||
// Indexing will be a dynamic function because some type fields are dynamic
|
||||
lua_newtable(L);
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pushcclosure(L, typeUserdataIndex, "__index", 1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// List of fields for type userdata
|
||||
luaL_Reg typeUserdataFields[] = {
|
||||
@ -1471,6 +1589,7 @@ void registerTypeUserData(lua_State* L)
|
||||
|
||||
// Set types library as a global name "types"
|
||||
lua_setglobal(L, "types");
|
||||
}
|
||||
|
||||
// Sets up a destructor for the type userdata.
|
||||
lua_setuserdatadtor(L, kTypeUserdataTag, deallocTypeUserData);
|
||||
|
@ -5,6 +5,11 @@
|
||||
|
||||
LUAU_FASTFLAG(LuauNativeAttribute);
|
||||
|
||||
// The default value here is 643 because the first release in which this was implemented is 644,
|
||||
// and actively we want new changes to be off by default until they're enabled consciously.
|
||||
// The flag is placed in AST project here to be common in all Luau libraries
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeSolverRelease, 643)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -2,11 +2,14 @@
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/ExperimentalFlags.h"
|
||||
|
||||
#include <limits> // TODO: remove with LuauTypeSolverRelease
|
||||
#include <string_view>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
|
||||
static void setLuauFlag(std::string_view name, bool state)
|
||||
{
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
@ -23,6 +26,13 @@ static void setLuauFlag(std::string_view name, bool state)
|
||||
|
||||
static void setLuauFlags(bool state)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
// Setting flags to 'true' means enabling all Luau flags including new type solver
|
||||
// In that case, it is provided with all fixes enabled (as if each fix had its own boolean flag)
|
||||
DFInt::LuauTypeSolverRelease.value = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||
flag->value = state;
|
||||
|
@ -124,6 +124,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# Some gcc versions treat var in `if (type var = val)` as unused
|
||||
# Some gcc versions treat variables used in constexpr if blocks as unused
|
||||
list(APPEND LUAU_OPTIONS -Wno-unused)
|
||||
# some gcc versions warn maybe uninitialized on optional<string> members on structs
|
||||
list(APPEND LUAU_OPTIONS -Wno-maybe-uninitialized)
|
||||
endif()
|
||||
|
||||
# Enabled in CI; we should be warning free on our main compiler versions but don't guarantee being warning free everywhere
|
||||
|
@ -346,13 +346,13 @@ void SharedCodeGenContextDeleter::operator()(const SharedCodeGenContext* codeGen
|
||||
return static_cast<BaseCodeGenContext*>(L->global->ecb.context);
|
||||
}
|
||||
|
||||
static void onCloseState(lua_State* L) noexcept
|
||||
static void onCloseState(lua_State* L)
|
||||
{
|
||||
getCodeGenContext(L)->onCloseState();
|
||||
L->global->ecb = lua_ExecutionCallbacks{};
|
||||
}
|
||||
|
||||
static void onDestroyFunction(lua_State* L, Proto* proto) noexcept
|
||||
static void onDestroyFunction(lua_State* L, Proto* proto)
|
||||
{
|
||||
getCodeGenContext(L)->onDestroyFunction(proto->execdata);
|
||||
proto->execdata = nullptr;
|
||||
|
2
Makefile
2
Makefile
@ -82,8 +82,10 @@ LDFLAGS=
|
||||
|
||||
# some gcc versions treat var in `if (type var = val)` as unused
|
||||
# some gcc versions treat variables used in constexpr if blocks as unused
|
||||
# some gcc versions warn maybe uninitalized on optional<std::string> members on structs
|
||||
ifeq ($(findstring g++,$(shell $(CXX) --version)),g++)
|
||||
CXXFLAGS+=-Wno-unused
|
||||
CXXFLAGS+=-Wno-maybe-uninitialized
|
||||
endif
|
||||
|
||||
# enabled in CI; we should be warning free on our main compiler versions but don't guarantee being warning free everywhere
|
||||
|
@ -453,6 +453,8 @@ struct lua_Callbacks
|
||||
void (*debugstep)(lua_State* L, lua_Debug* ar); // gets called after each instruction in single step mode
|
||||
void (*debuginterrupt)(lua_State* L, lua_Debug* ar); // gets called when thread execution is interrupted by break in another thread
|
||||
void (*debugprotectederror)(lua_State* L); // gets called when protected call results in an error
|
||||
|
||||
void (*onallocate)(lua_State* L, size_t osize, size_t nsize); // gets called when memory is allocated
|
||||
};
|
||||
typedef struct lua_Callbacks lua_Callbacks;
|
||||
|
||||
|
@ -504,6 +504,11 @@ void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat)
|
||||
g->totalbytes += nsize;
|
||||
g->memcatbytes[memcat] += nsize;
|
||||
|
||||
if (LUAU_UNLIKELY(!!g->cb.onallocate))
|
||||
{
|
||||
g->cb.onallocate(L, 0, nsize);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@ -539,6 +544,11 @@ GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat)
|
||||
g->totalbytes += nsize;
|
||||
g->memcatbytes[memcat] += nsize;
|
||||
|
||||
if (LUAU_UNLIKELY(!!g->cb.onallocate))
|
||||
{
|
||||
g->cb.onallocate(L, 0, nsize);
|
||||
}
|
||||
|
||||
return (GCObject*)block;
|
||||
}
|
||||
|
||||
@ -618,6 +628,12 @@ void* luaM_realloc_(lua_State* L, void* block, size_t osize, size_t nsize, uint8
|
||||
LUAU_ASSERT((nsize == 0) == (result == NULL));
|
||||
g->totalbytes = (g->totalbytes - osize) + nsize;
|
||||
g->memcatbytes[memcat] += nsize - osize;
|
||||
|
||||
if (LUAU_UNLIKELY(!!g->cb.onallocate))
|
||||
{
|
||||
g->cb.onallocate(L, osize, nsize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -508,6 +508,9 @@ def runTest(subdir, filename, filepath):
|
||||
filepath = os.path.abspath(filepath)
|
||||
|
||||
mainVm = os.path.abspath(arguments.vm)
|
||||
if not os.path.isfile(mainVm):
|
||||
print(f"{colored(Color.RED, 'ERROR')}: VM executable '{mainVm}' does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
# Process output will contain the test name and execution times
|
||||
mainOutput = getVmOutput(substituteArguments(mainVm, getExtraArguments(filepath)) + " " + filepath)
|
||||
@ -887,9 +890,11 @@ def run(args, argsubcb):
|
||||
analyzeResult('', mainResult, compareResults)
|
||||
else:
|
||||
all_files = [subdir + os.sep + filename for subdir, dirs, files in os.walk(arguments.folder) for filename in files]
|
||||
if len(all_files) == 0:
|
||||
print(f"{colored(Color.YELLOW, 'WARNING')}: No test files found in '{arguments.folder}'.")
|
||||
for filepath in sorted(all_files):
|
||||
subdir, filename = os.path.split(filepath)
|
||||
if filename.endswith(".lua"):
|
||||
if filename.endswith(".lua") or filename.endswith(".luau"):
|
||||
if arguments.run_test == None or re.match(arguments.run_test, filename[:-4]):
|
||||
runTest(subdir, filename, filepath)
|
||||
|
||||
|
@ -30,7 +30,7 @@ template<class BaseType>
|
||||
struct ACFixtureImpl : BaseType
|
||||
{
|
||||
ACFixtureImpl()
|
||||
: BaseType(true, true)
|
||||
: BaseType(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,7 @@
|
||||
static const char* mainModuleName = "MainModule";
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
|
||||
LUAU_FASTFLAG(LuauDCRMagicFunctionTypeChecker);
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
|
||||
extern std::optional<unsigned> randomSeed; // tests/main.cpp
|
||||
@ -152,15 +150,8 @@ const Config& TestConfigResolver::getConfig(const ModuleName& name) const
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
Fixture::Fixture(bool freeze, bool prepareAutocomplete)
|
||||
: sff_DebugLuauFreezeArena(FFlag::DebugLuauFreezeArena, freeze)
|
||||
, sff_LuauDCRMagicFunctionTypeChecker(FFlag::LuauDCRMagicFunctionTypeChecker, true)
|
||||
// The first value of LuauTypeSolverRelease was 643, so as long as this is
|
||||
// some number greater than 900 (5 years worth of releases), all tests that
|
||||
// run under the new solver will run against all of the changes guarded by
|
||||
// this flag.
|
||||
, sff_LuauTypeSolverRelease(DFInt::LuauTypeSolverRelease, std::numeric_limits<int>::max())
|
||||
, frontend(
|
||||
Fixture::Fixture(bool prepareAutocomplete)
|
||||
: frontend(
|
||||
&fileResolver,
|
||||
&configResolver,
|
||||
{/* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed}
|
||||
@ -583,8 +574,8 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source)
|
||||
return result;
|
||||
}
|
||||
|
||||
BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
|
||||
: Fixture(freeze, prepareAutocomplete)
|
||||
BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete)
|
||||
: Fixture(prepareAutocomplete)
|
||||
{
|
||||
Luau::unfreeze(frontend.globals.globalTypes);
|
||||
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -63,7 +65,7 @@ struct TestConfigResolver : ConfigResolver
|
||||
|
||||
struct Fixture
|
||||
{
|
||||
explicit Fixture(bool freeze = true, bool prepareAutocomplete = false);
|
||||
explicit Fixture(bool prepareAutocomplete = false);
|
||||
~Fixture();
|
||||
|
||||
// Throws Luau::ParseErrors if the parse fails.
|
||||
@ -100,36 +102,14 @@ struct Fixture
|
||||
TypeId requireTypeAlias(const std::string& name);
|
||||
TypeId requireExportedType(const ModuleName& moduleName, const std::string& name);
|
||||
|
||||
// TODO: Should this be in a container of some kind? Seems a little silly
|
||||
// to have a bunch of flags sitting on the text fixture.
|
||||
// While most flags can be flipped inside the unit test, some code changes affect the state that is part of Fixture initialization
|
||||
// Most often those are changes related to builtin type definitions.
|
||||
// In that case, flag can be forced to 'true' using the example below:
|
||||
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
||||
|
||||
// We have a couple flags that are OK to set for all tests and, in some
|
||||
// cases, cannot easily be flipped on or off on a per-test basis. For these
|
||||
// we set them as part of constructing the test fixture.
|
||||
|
||||
/* From the original commit:
|
||||
*
|
||||
* > This enables arena freezing for all but two unit tests. Arena
|
||||
* > freezing marks the `TypeArena`'s underlying memory as read-only,
|
||||
* > raising an access violation whenever you mutate it. This is useful
|
||||
* > for tracking down violations of Luau's memory model.
|
||||
*/
|
||||
ScopedFastFlag sff_DebugLuauFreezeArena;
|
||||
|
||||
/* Magic typechecker functions for the new solver are initialized when the
|
||||
* typechecker frontend is initialized, which is done at the beginning of
|
||||
* the test: we set this flag as part of the fixture as we always want to
|
||||
* enable the magic functions for, say, `string.format`.
|
||||
*/
|
||||
ScopedFastFlag sff_LuauDCRMagicFunctionTypeChecker;
|
||||
|
||||
/* While the new solver is being rolled out we are using a monotonically
|
||||
* increasing version number to track new changes, we just set it to a
|
||||
* sufficiently high number in tests to ensure that any guards in prod
|
||||
* code pass in tests (so we don't accidentally reintroduce a bug before
|
||||
* it's unflagged).
|
||||
*/
|
||||
ScopedFastInt sff_LuauTypeSolverRelease;
|
||||
// Arena freezing marks the `TypeArena`'s underlying memory as read-only, raising an access violation whenever you mutate it.
|
||||
// This is useful for tracking down violations of Luau's memory model.
|
||||
ScopedFastFlag sff_DebugLuauFreezeArena{FFlag::DebugLuauFreezeArena, true};
|
||||
|
||||
TestFileResolver fileResolver;
|
||||
TestConfigResolver configResolver;
|
||||
@ -158,7 +138,7 @@ struct Fixture
|
||||
|
||||
struct BuiltinsFixture : Fixture
|
||||
{
|
||||
BuiltinsFixture(bool freeze = true, bool prepareAutocomplete = false);
|
||||
BuiltinsFixture(bool prepareAutocomplete = false);
|
||||
};
|
||||
|
||||
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
|
||||
|
@ -175,6 +175,8 @@ TEST_CASE("NativeModuleRefRefcounting")
|
||||
REQUIRE(modRefA->getRefcount() == 1);
|
||||
REQUIRE(modRefB->getRefcount() == 1);
|
||||
|
||||
#if defined(__linux__) && defined(__GNUC__)
|
||||
#else
|
||||
// NativeModuleRef self move assignment:
|
||||
{
|
||||
NativeModuleRef modRef1{modRefA};
|
||||
@ -183,6 +185,8 @@ TEST_CASE("NativeModuleRefRefcounting")
|
||||
REQUIRE(modRefA->getRefcount() == 2);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
REQUIRE(modRefA->getRefcount() == 1);
|
||||
REQUIRE(modRefB->getRefcount() == 1);
|
||||
|
||||
|
@ -21,7 +21,7 @@ struct TypeFunctionFixture : Fixture
|
||||
TypeFunction swapFunction;
|
||||
|
||||
TypeFunctionFixture()
|
||||
: Fixture(true, false)
|
||||
: Fixture(false)
|
||||
{
|
||||
swapFunction = TypeFunction{
|
||||
/* name */ "Swap",
|
||||
|
@ -11,6 +11,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
|
||||
|
||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||
|
||||
@ -1101,4 +1102,101 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer")
|
||||
CHECK(toString(tpm->givenTp) == "{ foo: string }");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "no_type_methods_on_types")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function test(x)
|
||||
return if types.is(x, "number") then types.string else types.boolean
|
||||
end
|
||||
local function ok(tbl: test<number>): never return tbl end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == R"('test' type function errored at runtime: [string "test"]:3: attempt to call a nil value)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "no_types_functions_on_type")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function test(x)
|
||||
return x.singleton("a")
|
||||
end
|
||||
local function ok(tbl: test<number>): never return tbl end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == R"('test' type function errored at runtime: [string "test"]:3: attempt to call a nil value)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "no_metatable_writes")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function test(x)
|
||||
local a = x.__index
|
||||
a.is = function() return false end
|
||||
return types.singleton(x.is("number"))
|
||||
end
|
||||
local function ok(tbl: test<number>): never return tbl end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == R"('test' type function errored at runtime: [string "test"]:4: attempt to index nil with 'is')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "no_eq_field")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function test(x)
|
||||
return types.singleton(x.__eq(x, types.number))
|
||||
end
|
||||
local function ok(tbl: test<number>): never return tbl end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == R"('test' type function errored at runtime: [string "test"]:3: attempt to call a nil value)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function test(x)
|
||||
return types.singleton(x.tag)
|
||||
end
|
||||
|
||||
local function ok1(tbl: test<number>): never return tbl end
|
||||
local function ok2(tbl: test<string>): never return tbl end
|
||||
local function ok3(tbl: test<{}>): never return tbl end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
CHECK(toString(result.errors[0]) == R"(Type pack '"number"' could not be converted into 'never'; at [0], "number" is not a subtype of never)");
|
||||
CHECK(toString(result.errors[1]) == R"(Type pack '"string"' could not be converted into 'never'; at [0], "string" is not a subtype of never)");
|
||||
CHECK(toString(result.errors[2]) == R"(Type pack '"table"' could not be converted into 'never'; at [0], "table" is not a subtype of never)");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -4866,4 +4866,29 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_union_type")
|
||||
{
|
||||
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// This will have one (legitimate) error but previously would crash.
|
||||
auto result = check(R"(
|
||||
local function set(key, value)
|
||||
local Message = {}
|
||||
function Message.new(message)
|
||||
local self = message or {}
|
||||
setmetatable(self, Message)
|
||||
return self
|
||||
end
|
||||
local self = Message.new(nil)
|
||||
self[key] = value
|
||||
end
|
||||
)");
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
"Cannot add indexer to table '{ @metatable t1, (nil & ~(false?)) | { } } where t1 = { new: <a>(a) -> { @metatable t1, (a & ~(false?)) | { } } }'",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -27,9 +27,13 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#include <limits> // TODO: remove with LuauTypeSolverRelease
|
||||
#include <optional>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||
|
||||
// Indicates if verbose output is enabled; can be overridden via --verbose
|
||||
// Currently, this enables output from 'print', but other verbose output could be enabled eventually.
|
||||
bool verbose = false;
|
||||
@ -411,6 +415,12 @@ int main(int argc, char** argv)
|
||||
printf("Using RNG seed %u\n", *randomSeed);
|
||||
}
|
||||
|
||||
// New Luau type solver uses a temporary scheme where fixes are made under a single version flag
|
||||
// When flags are enabled, new solver is enabled with all new features and fixes
|
||||
// When it's disabled, this value should have no effect (all uses under a new solver)
|
||||
// Flag setup argument can still be used to override this to a specific value if desired
|
||||
DFInt::LuauTypeSolverRelease.value = std::numeric_limits<int>::max();
|
||||
|
||||
if (std::vector<doctest::String> flags; doctest::parseCommaSepArgs(argc, argv, "--fflags=", flags))
|
||||
setFastFlags(flags);
|
||||
|
||||
|
@ -135,7 +135,7 @@ def add_argument_parsers(parser):
|
||||
interestness_parser.add_argument('--auto', dest='mode', action='store_const', const=InterestnessMode.AUTO,
|
||||
default=InterestnessMode.AUTO, help='Automatically figure out which one of --pass or --fail should be used')
|
||||
interestness_parser.add_argument('--fail', dest='mode', action='store_const', const=InterestnessMode.FAIL,
|
||||
help='You want this if omitting --fflags=true causes tests to fail')
|
||||
help='You want this if passing --fflags=true causes tests to fail')
|
||||
interestness_parser.add_argument('--pass', dest='mode', action='store_const', const=InterestnessMode.PASS,
|
||||
help='You want this if passing --fflags=true causes tests to pass')
|
||||
interestness_parser.add_argument('--timeout', dest='timeout', type=int, default=0, metavar='SECONDS',
|
||||
|
Loading…
Reference in New Issue
Block a user