mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/647
This commit is contained in:
parent
8531df04c9
commit
aa2e5c096d
@ -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);
|
||||
|
@ -881,7 +881,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||
PROP(name);
|
||||
PROP(generics);
|
||||
PROP(genericPacks);
|
||||
PROP(type);
|
||||
write("value", node->type);
|
||||
PROP(exported);
|
||||
}
|
||||
);
|
||||
|
@ -26,7 +26,6 @@
|
||||
*/
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAGVARIABLE(LuauDCRMagicFunctionTypeChecker, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -931,8 +930,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
|
||||
formatFTV.isCheckedFunction = true;
|
||||
const TypeId formatFn = arena->addType(formatFTV);
|
||||
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
|
||||
if (FFlag::LuauDCRMagicFunctionTypeChecker)
|
||||
attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat);
|
||||
attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat);
|
||||
|
||||
|
||||
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ true);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), mode == Mode::NoCheck);
|
||||
{
|
||||
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,8 +926,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||
else
|
||||
mode = sourceModule.mode.value_or(config.mode);
|
||||
|
||||
if (FFlag::LuauSourceModuleUpdatedWithSelectedMode)
|
||||
item.sourceModule->mode = {mode};
|
||||
item.sourceModule->mode = {mode};
|
||||
ScopePtr environmentScope = item.environmentScope;
|
||||
double timestamp = getTimestamp();
|
||||
const std::vector<RequireCycle>& requireCycles = item.requireCycles;
|
||||
|
@ -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,43 +2571,29 @@ 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 (seenSet.contains(*hprop.readTy) && seenSet.contains(*tprop.readTy))
|
||||
{
|
||||
// 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);
|
||||
seenSet.erase(*tprop.readTy);
|
||||
return {builtinTypes->neverType};
|
||||
}
|
||||
else
|
||||
{
|
||||
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};
|
||||
seenSet.erase(*hprop.readTy);
|
||||
seenSet.erase(*tprop.readTy);
|
||||
return {builtinTypes->neverType};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NormalizationResult::False == isIntersectionInhabited(*hprop.readTy, *tprop.readTy))
|
||||
return {builtinTypes->neverType};
|
||||
seenSet.insert(*hprop.readTy);
|
||||
seenSet.insert(*tprop.readTy);
|
||||
}
|
||||
|
||||
NormalizationResult res = isIntersectionInhabited(*hprop.readTy, *tprop.readTy);
|
||||
|
||||
// Cleanup
|
||||
seenSet.erase(*hprop.readTy);
|
||||
seenSet.erase(*tprop.readTy);
|
||||
|
||||
if (NormalizationResult::True != res)
|
||||
return {builtinTypes->neverType};
|
||||
|
||||
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
|
||||
prop.readTy = ty;
|
||||
hereSubThere &= (ty == hprop.readTy);
|
||||
|
@ -1625,8 +1625,11 @@ void TypeChecker2::indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const M
|
||||
indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType);
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(tt || get<PrimitiveType>(follow(metaTable->table)));
|
||||
|
||||
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,11 +1382,11 @@ static int isEqualToType(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Register the type userdata
|
||||
void registerTypeUserData(lua_State* L)
|
||||
void registerTypesLibrary(lua_State* L)
|
||||
{
|
||||
// List of fields for type userdata
|
||||
luaL_Reg typeUserdataFields[] = {
|
||||
LUAU_ASSERT(FFlag::LuauUserTypeFunFixRegister);
|
||||
|
||||
luaL_Reg fields[] = {
|
||||
{"unknown", createUnknown},
|
||||
{"never", createNever},
|
||||
{"any", createAny},
|
||||
@ -1395,8 +1396,7 @@ void registerTypeUserData(lua_State* L)
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// List of methods for type userdata
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
luaL_Reg methods[] = {
|
||||
{"singleton", createSingleton},
|
||||
{"negationof", createNegation},
|
||||
{"unionof", createUnion},
|
||||
@ -1405,72 +1405,191 @@ void registerTypeUserData(lua_State* L)
|
||||
{"newfunction", createFunction},
|
||||
{"copy", deepCopy},
|
||||
|
||||
// Common methods
|
||||
{"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},
|
||||
{"indexer", getIndexer},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
// Protect metatable from being fetched.
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
// Set type userdata metatable's __eq to type_equals()
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
|
||||
// Set type userdata metatable's __index to itself
|
||||
lua_pushvalue(L, -1); // Push a copy of type userdata metatable
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
luaL_register(L, "types", methods);
|
||||
|
||||
// Set fields for type userdata
|
||||
for (luaL_Reg* l = typeUserdataFields; l->name; l++)
|
||||
for (luaL_Reg* l = fields; l->name; l++)
|
||||
{
|
||||
l->func(L);
|
||||
lua_setfield(L, -2, l->name);
|
||||
}
|
||||
|
||||
// Set types library as a global name "types"
|
||||
lua_setglobal(L, "types");
|
||||
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[] = {
|
||||
{"unknown", createUnknown},
|
||||
{"never", createNever},
|
||||
{"any", createAny},
|
||||
{"boolean", createBoolean},
|
||||
{"number", createNumber},
|
||||
{"string", createString},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// List of methods for type userdata
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
{"singleton", createSingleton},
|
||||
{"negationof", createNegation},
|
||||
{"unionof", createUnion},
|
||||
{"intersectionof", createIntersection},
|
||||
{"newtable", createTable},
|
||||
{"newfunction", createFunction},
|
||||
{"copy", deepCopy},
|
||||
|
||||
// Common methods
|
||||
{"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},
|
||||
{"indexer", getIndexer},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
// Protect metatable from being fetched.
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
// Set type userdata metatable's __eq to type_equals()
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
|
||||
// Set type userdata metatable's __index to itself
|
||||
lua_pushvalue(L, -1); // Push a copy of type userdata metatable
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
|
||||
// Set fields for type userdata
|
||||
for (luaL_Reg* l = typeUserdataFields; l->name; l++)
|
||||
{
|
||||
l->func(L);
|
||||
lua_setfield(L, -2, l->name);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
@ -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;
|
||||
|
@ -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, int osize, int 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)
|
||||
|
||||
|
@ -138,7 +138,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_table_array")
|
||||
|
||||
CHECK(
|
||||
json ==
|
||||
R"({"type":"AstStatBlock","location":"0,0 - 0,17","hasEnd":true,"body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","nameLocation":"0,10 - 0,16","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","nameLocation":"0,10 - 0,16","parameters":[]}}},"exported":false}]})"
|
||||
R"({"type":"AstStatBlock","location":"0,0 - 0,17","hasEnd":true,"body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"value":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","nameLocation":"0,10 - 0,16","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","nameLocation":"0,10 - 0,16","parameters":[]}}},"exported":false}]})"
|
||||
);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_table_indexer")
|
||||
|
||||
CHECK(
|
||||
json ==
|
||||
R"({"type":"AstStatBlock","location":"0,0 - 0,17","hasEnd":true,"body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","nameLocation":"0,10 - 0,16","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","nameLocation":"0,10 - 0,16","parameters":[]}}},"exported":false}]})"
|
||||
R"({"type":"AstStatBlock","location":"0,0 - 0,17","hasEnd":true,"body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"value":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","nameLocation":"0,10 - 0,16","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","nameLocation":"0,10 - 0,16","parameters":[]}}},"exported":false}]})"
|
||||
);
|
||||
}
|
||||
|
||||
@ -408,7 +408,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatTypeAlias")
|
||||
AstStat* statement = expectParseStatement("type A = B");
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,10","name":"A","generics":[],"genericPacks":[],"type":{"type":"AstTypeReference","location":"0,9 - 0,10","name":"B","nameLocation":"0,9 - 0,10","parameters":[]},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,10","name":"A","generics":[],"genericPacks":[],"value":{"type":"AstTypeReference","location":"0,9 - 0,10","name":"B","nameLocation":"0,9 - 0,10","parameters":[]},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
@ -462,7 +462,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}]}},{"type":"AstTypeFunction","location":"0,41 - 0,55","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}]},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}]}},{"type":"AstTypeFunction","location":"0,41 - 0,55","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}]},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
@ -474,7 +474,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_type_literal")
|
||||
auto json = toJson(statement);
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,73","name":"Action","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,14 - 0,73","props":[{"name":"strings","type":"AstTableProp","location":"0,16 - 0,23","propType":{"type":"AstTypeUnion","location":"0,25 - 0,40","types":[{"type":"AstTypeSingletonString","location":"0,25 - 0,28","value":"A"},{"type":"AstTypeSingletonString","location":"0,31 - 0,34","value":"B"},{"type":"AstTypeSingletonString","location":"0,37 - 0,40","value":"C"}]}},{"name":"mixed","type":"AstTableProp","location":"0,42 - 0,47","propType":{"type":"AstTypeUnion","location":"0,49 - 0,71","types":[{"type":"AstTypeSingletonString","location":"0,49 - 0,55","value":"This"},{"type":"AstTypeSingletonString","location":"0,58 - 0,64","value":"That"},{"type":"AstTypeSingletonBool","location":"0,67 - 0,71","value":true}]}}],"indexer":null},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,73","name":"Action","generics":[],"genericPacks":[],"value":{"type":"AstTypeTable","location":"0,14 - 0,73","props":[{"name":"strings","type":"AstTableProp","location":"0,16 - 0,23","propType":{"type":"AstTypeUnion","location":"0,25 - 0,40","types":[{"type":"AstTypeSingletonString","location":"0,25 - 0,28","value":"A"},{"type":"AstTypeSingletonString","location":"0,31 - 0,34","value":"B"},{"type":"AstTypeSingletonString","location":"0,37 - 0,40","value":"C"}]}},{"name":"mixed","type":"AstTableProp","location":"0,42 - 0,47","propType":{"type":"AstTypeUnion","location":"0,49 - 0,71","types":[{"type":"AstTypeSingletonString","location":"0,49 - 0,55","value":"This"},{"type":"AstTypeSingletonString","location":"0,58 - 0,64","value":"That"},{"type":"AstTypeSingletonBool","location":"0,67 - 0,71","value":true}]}}],"indexer":null},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
@ -484,7 +484,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_indexed_type_literal")
|
||||
AstStat* statement = expectParseStatement(R"(type StringSet = { [string]: true })");
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,35","name":"StringSet","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,17 - 0,35","props":[],"indexer":{"location":"0,19 - 0,33","indexType":{"type":"AstTypeReference","location":"0,20 - 0,26","name":"string","nameLocation":"0,20 - 0,26","parameters":[]},"resultType":{"type":"AstTypeSingletonBool","location":"0,29 - 0,33","value":true}}},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,35","name":"StringSet","generics":[],"genericPacks":[],"value":{"type":"AstTypeTable","location":"0,17 - 0,35","props":[],"indexer":{"location":"0,19 - 0,33","indexType":{"type":"AstTypeReference","location":"0,20 - 0,26","name":"string","nameLocation":"0,20 - 0,26","parameters":[]},"resultType":{"type":"AstTypeSingletonBool","location":"0,29 - 0,33","value":true}}},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
@ -494,7 +494,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction")
|
||||
AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())");
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"type":{"type":"AstTypeFunction","location":"0,11 - 0,46","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
@ -507,7 +507,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError")
|
||||
AstStat* statement = parseResult.root->body.data[0];
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,9","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeError","location":"0,8 - 0,9","types":[],"messageIndex":0},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,9","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeError","location":"0,8 - 0,9","types":[],"messageIndex":0},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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