mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/504 (#200)
- Type mismatch errors now show detailed information for compound types, highlighting the mismatching component - Fix string.pack bug on ARM when packing negative numbers using unsigned formats - Implement bit32.countlz/countrz (RFC RFC: bit32.countlz/countrz #89) - Minor compiler throughput optimization (~2% faster compilation) - Improve transpiler behavior for edge cases and better test coverage (not exposed through CLI at the moment) - Improve error recovery when parsing invalid assignments - Build fixes for fuzzing targets
This commit is contained in:
parent
a6a2b86c9b
commit
d47b2f1dfe
@ -8,11 +8,20 @@
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
struct TypeError;
|
||||
|
||||
struct TypeMismatch
|
||||
{
|
||||
TypeId wantedType;
|
||||
TypeId givenType;
|
||||
TypeMismatch() = default;
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType);
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error);
|
||||
|
||||
TypeId wantedType = nullptr;
|
||||
TypeId givenType = nullptr;
|
||||
|
||||
std::string reason;
|
||||
std::shared_ptr<TypeError> error;
|
||||
|
||||
bool operator==(const TypeMismatch& rhs) const;
|
||||
};
|
||||
|
@ -53,7 +53,7 @@ struct FileResolver
|
||||
}
|
||||
|
||||
// DEPRECATED APIS
|
||||
// These are going to be removed with LuauNewRequireTracer
|
||||
// These are going to be removed with LuauNewRequireTrace2
|
||||
virtual bool moduleExists(const ModuleName& name) const = 0;
|
||||
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
|
||||
virtual ModuleName concat(const ModuleName& lhs, std::string_view rhs) const = 0;
|
||||
|
@ -18,6 +18,7 @@ struct TranspileResult
|
||||
std::string parseError; // Nonempty if the transpile failed
|
||||
};
|
||||
|
||||
std::string toString(AstNode* node);
|
||||
void dump(AstNode* node);
|
||||
|
||||
// Never fails on a well-formed AST
|
||||
@ -25,6 +26,6 @@ std::string transpile(AstStatBlock& ast);
|
||||
std::string transpileWithTypes(AstStatBlock& block);
|
||||
|
||||
// Only fails when parsing fails
|
||||
TranspileResult transpile(std::string_view source, ParseOptions options = ParseOptions{});
|
||||
TranspileResult transpile(std::string_view source, ParseOptions options = ParseOptions{}, bool withTypes = false);
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -263,8 +263,6 @@ public:
|
||||
*
|
||||
*/
|
||||
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location);
|
||||
// Removed by FFlag::LuauRankNTypes
|
||||
TypePackId DEPRECATED_instantiate(const ScopePtr& scope, TypePackId ty, Location location);
|
||||
|
||||
// Replace any free types or type packs by `any`.
|
||||
// This is used when exporting types from modules, to make sure free types don't leak.
|
||||
@ -298,8 +296,6 @@ private:
|
||||
// Produce a new free type var.
|
||||
TypeId freshType(const ScopePtr& scope);
|
||||
TypeId freshType(TypeLevel level);
|
||||
TypeId DEPRECATED_freshType(const ScopePtr& scope, bool canBeGeneric = false);
|
||||
TypeId DEPRECATED_freshType(TypeLevel level, bool canBeGeneric = false);
|
||||
|
||||
// Returns nullopt if the predicate filters down the TypeId to 0 options.
|
||||
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||
@ -326,10 +322,8 @@ private:
|
||||
TypePackId addTypePack(std::initializer_list<TypeId>&& ty);
|
||||
TypePackId freshTypePack(const ScopePtr& scope);
|
||||
TypePackId freshTypePack(TypeLevel level);
|
||||
TypePackId DEPRECATED_freshTypePack(const ScopePtr& scope, bool canBeGeneric = false);
|
||||
TypePackId DEPRECATED_freshTypePack(TypeLevel level, bool canBeGeneric = false);
|
||||
|
||||
TypeId resolveType(const ScopePtr& scope, const AstType& annotation, bool canBeGeneric = false);
|
||||
TypeId resolveType(const ScopePtr& scope, const AstType& annotation);
|
||||
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& types);
|
||||
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
|
||||
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
LUAU_FASTFLAG(LuauAddMissingFollow)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -128,13 +126,10 @@ TypePack* asMutable(const TypePack* tp);
|
||||
template<typename T>
|
||||
const T* get(TypePackId tp)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
LUAU_ASSERT(tp);
|
||||
LUAU_ASSERT(tp);
|
||||
|
||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||
}
|
||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||
|
||||
return get_if<T>(&(tp->ty));
|
||||
}
|
||||
@ -142,13 +137,10 @@ const T* get(TypePackId tp)
|
||||
template<typename T>
|
||||
T* getMutable(TypePackId tp)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
LUAU_ASSERT(tp);
|
||||
LUAU_ASSERT(tp);
|
||||
|
||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||
}
|
||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||
|
||||
return get_if<T>(&(asMutable(tp)->ty));
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
||||
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
|
||||
LUAU_FASTFLAG(LuauAddMissingFollow)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -413,13 +412,17 @@ bool maybeGeneric(const TypeId ty);
|
||||
|
||||
struct SingletonTypes
|
||||
{
|
||||
const TypeId nilType = &nilType_;
|
||||
const TypeId numberType = &numberType_;
|
||||
const TypeId stringType = &stringType_;
|
||||
const TypeId booleanType = &booleanType_;
|
||||
const TypeId threadType = &threadType_;
|
||||
const TypeId anyType = &anyType_;
|
||||
const TypeId errorType = &errorType_;
|
||||
const TypeId nilType;
|
||||
const TypeId numberType;
|
||||
const TypeId stringType;
|
||||
const TypeId booleanType;
|
||||
const TypeId threadType;
|
||||
const TypeId anyType;
|
||||
const TypeId errorType;
|
||||
const TypeId optionalNumberType;
|
||||
|
||||
const TypePackId anyTypePack;
|
||||
const TypePackId errorTypePack;
|
||||
|
||||
SingletonTypes();
|
||||
SingletonTypes(const SingletonTypes&) = delete;
|
||||
@ -427,14 +430,6 @@ struct SingletonTypes
|
||||
|
||||
private:
|
||||
std::unique_ptr<struct TypeArena> arena;
|
||||
TypeVar nilType_;
|
||||
TypeVar numberType_;
|
||||
TypeVar stringType_;
|
||||
TypeVar booleanType_;
|
||||
TypeVar threadType_;
|
||||
TypeVar anyType_;
|
||||
TypeVar errorType_;
|
||||
|
||||
TypeId makeStringMetatable();
|
||||
};
|
||||
|
||||
@ -472,13 +467,10 @@ TypeVar* asMutable(TypeId ty);
|
||||
template<typename T>
|
||||
const T* get(TypeId tv)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
LUAU_ASSERT(tv);
|
||||
LUAU_ASSERT(tv);
|
||||
|
||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||
}
|
||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||
|
||||
return get_if<T>(&tv->ty);
|
||||
}
|
||||
@ -486,13 +478,10 @@ const T* get(TypeId tv)
|
||||
template<typename T>
|
||||
T* getMutable(TypeId tv)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
LUAU_ASSERT(tv);
|
||||
LUAU_ASSERT(tv);
|
||||
|
||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||
}
|
||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||
|
||||
return get_if<T>(&asMutable(tv)->ty);
|
||||
}
|
||||
|
@ -63,12 +63,9 @@ using Name = std::string;
|
||||
struct Free
|
||||
{
|
||||
explicit Free(TypeLevel level);
|
||||
Free(TypeLevel level, bool DEPRECATED_canBeGeneric);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
// Removed by FFlag::LuauRankNTypes
|
||||
bool DEPRECATED_canBeGeneric = false;
|
||||
// True if this free type variable is part of a mutually
|
||||
// recursive type alias whose definitions haven't been
|
||||
// resolved yet.
|
||||
|
@ -87,7 +87,6 @@ private:
|
||||
void tryUnifyWithAny(TypePackId any, TypePackId ty);
|
||||
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
||||
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry);
|
||||
|
||||
public:
|
||||
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
|
||||
@ -102,6 +101,7 @@ private:
|
||||
bool isNonstrictMode() const;
|
||||
|
||||
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
|
||||
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType);
|
||||
|
||||
[[noreturn]] void ice(const std::string& message, const Location& location);
|
||||
[[noreturn]] void ice(const std::string& message);
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
||||
LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
|
||||
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
|
||||
|
||||
@ -369,20 +368,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
|
||||
while (iter != endIter)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
if (isNil(*iter))
|
||||
++iter;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isNil(*iter))
|
||||
++iter;
|
||||
else
|
||||
{
|
||||
if (auto primTy = Luau::get<PrimitiveTypeVar>(*iter); primTy && primTy->type == PrimitiveTypeVar::NilType)
|
||||
++iter;
|
||||
else
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (iter == endIter)
|
||||
@ -397,21 +386,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
AutocompleteEntryMap inner;
|
||||
std::unordered_set<TypeId> innerSeen = seen;
|
||||
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
if (isNil(*iter))
|
||||
{
|
||||
if (isNil(*iter))
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto innerPrimTy = Luau::get<PrimitiveTypeVar>(*iter); innerPrimTy && innerPrimTy->type == PrimitiveTypeVar::NilType)
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
autocompleteProps(module, typeArena, *iter, indexType, nodes, inner, innerSeen);
|
||||
@ -1519,10 +1497,10 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
|
||||
return {};
|
||||
|
||||
TypeChecker& typeChecker =
|
||||
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
||||
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
||||
ModulePtr module =
|
||||
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
|
||||
: frontend.moduleResolver.getModule(moduleName));
|
||||
(frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
|
||||
: frontend.moduleResolver.getModule(moduleName));
|
||||
|
||||
if (!module)
|
||||
return {};
|
||||
@ -1550,7 +1528,7 @@ OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view
|
||||
sourceModule->commentLocations = std::move(result.commentLocations);
|
||||
|
||||
TypeChecker& typeChecker =
|
||||
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
||||
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
||||
|
||||
ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);
|
||||
|
||||
|
@ -8,10 +8,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauParseGenericFunctions)
|
||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
||||
LUAU_FASTFLAG(LuauRankNTypes)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
*
|
||||
@ -185,25 +182,11 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
TypeId numberType = typeChecker.numberType;
|
||||
TypeId booleanType = typeChecker.booleanType;
|
||||
TypeId nilType = typeChecker.nilType;
|
||||
TypeId stringType = typeChecker.stringType;
|
||||
TypeId threadType = typeChecker.threadType;
|
||||
TypeId anyType = typeChecker.anyType;
|
||||
|
||||
TypeArena& arena = typeChecker.globalTypes;
|
||||
|
||||
TypeId optionalNumber = makeOption(typeChecker, arena, numberType);
|
||||
TypeId optionalString = makeOption(typeChecker, arena, stringType);
|
||||
TypeId optionalBoolean = makeOption(typeChecker, arena, booleanType);
|
||||
|
||||
TypeId stringOrNumber = makeUnion(arena, {stringType, numberType});
|
||||
|
||||
TypePackId emptyPack = arena.addTypePack({});
|
||||
TypePackId oneNumberPack = arena.addTypePack({numberType});
|
||||
TypePackId oneStringPack = arena.addTypePack({stringType});
|
||||
TypePackId oneBooleanPack = arena.addTypePack({booleanType});
|
||||
TypePackId oneAnyPack = arena.addTypePack({anyType});
|
||||
|
||||
TypePackId anyTypePack = typeChecker.anyTypePack;
|
||||
|
||||
TypePackId numberVariadicList = arena.addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
||||
TypePackId listOfAtLeastOneNumber = arena.addTypePack(TypePack{{numberType}, numberVariadicList});
|
||||
@ -215,8 +198,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
|
||||
TypeId listOfAtLeastZeroNumbersToNumberType = arena.addType(FunctionTypeVar{numberVariadicList, oneNumberPack});
|
||||
|
||||
TypeId stringToAnyMap = arena.addType(TableTypeVar{{}, TableIndexer(stringType, anyType), typeChecker.globalScope->level});
|
||||
|
||||
LoadDefinitionFileResult loadResult = Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), "@luau");
|
||||
LUAU_ASSERT(loadResult.success);
|
||||
|
||||
@ -236,8 +217,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
ttv->props["btest"] = makeProperty(arena.addType(FunctionTypeVar{listOfAtLeastOneNumber, oneBooleanPack}), "@luau/global/bit32.btest");
|
||||
}
|
||||
|
||||
TypeId anyFunction = arena.addType(FunctionTypeVar{anyTypePack, anyTypePack});
|
||||
|
||||
TypeId genericK = arena.addType(GenericTypeVar{"K"});
|
||||
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level});
|
||||
@ -252,222 +231,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
|
||||
addGlobalBinding(typeChecker, "string", it->second.type, "@luau");
|
||||
|
||||
if (!FFlag::LuauParseGenericFunctions || !FFlag::LuauGenericFunctions)
|
||||
{
|
||||
TableTypeVar::Props debugLib{
|
||||
{"info", {makeIntersection(arena,
|
||||
{
|
||||
arena.addType(FunctionTypeVar{arena.addTypePack({typeChecker.threadType, numberType, stringType}), anyTypePack}),
|
||||
arena.addType(FunctionTypeVar{arena.addTypePack({numberType, stringType}), anyTypePack}),
|
||||
arena.addType(FunctionTypeVar{arena.addTypePack({anyFunction, stringType}), anyTypePack}),
|
||||
})}},
|
||||
{"traceback", {makeIntersection(arena,
|
||||
{
|
||||
makeFunction(arena, std::nullopt, {optionalString, optionalNumber}, {stringType}),
|
||||
makeFunction(arena, std::nullopt, {typeChecker.threadType, optionalString, optionalNumber}, {stringType}),
|
||||
})}},
|
||||
};
|
||||
|
||||
assignPropDocumentationSymbols(debugLib, "@luau/global/debug");
|
||||
addGlobalBinding(typeChecker, "debug",
|
||||
arena.addType(TableTypeVar{debugLib, std::nullopt, typeChecker.globalScope->level, Luau::TableState::Sealed}), "@luau");
|
||||
|
||||
TableTypeVar::Props utf8Lib = {
|
||||
{"char", {arena.addType(FunctionTypeVar{listOfAtLeastOneNumber, oneStringPack})}}, // FIXME
|
||||
{"charpattern", {stringType}},
|
||||
{"codes", {makeFunction(arena, std::nullopt, {stringType},
|
||||
{makeFunction(arena, std::nullopt, {stringType, numberType}, {numberType, numberType}), stringType, numberType})}},
|
||||
{"codepoint",
|
||||
{arena.addType(FunctionTypeVar{arena.addTypePack({stringType, optionalNumber, optionalNumber}), listOfAtLeastOneNumber})}}, // FIXME
|
||||
{"len", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber}, {optionalNumber, numberType})}},
|
||||
{"offset", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber}, {numberType})}},
|
||||
{"nfdnormalize", {makeFunction(arena, std::nullopt, {stringType}, {stringType})}},
|
||||
{"graphemes", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber},
|
||||
{makeFunction(arena, std::nullopt, {}, {numberType, numberType})})}},
|
||||
{"nfcnormalize", {makeFunction(arena, std::nullopt, {stringType}, {stringType})}},
|
||||
};
|
||||
|
||||
assignPropDocumentationSymbols(utf8Lib, "@luau/global/utf8");
|
||||
addGlobalBinding(
|
||||
typeChecker, "utf8", arena.addType(TableTypeVar{utf8Lib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
|
||||
|
||||
TypeId optionalV = makeOption(typeChecker, arena, genericV);
|
||||
|
||||
TypeId arrayOfV = arena.addType(TableTypeVar{{}, TableIndexer(numberType, genericV), typeChecker.globalScope->level});
|
||||
|
||||
TypePackId unpackArgsPack = arena.addTypePack(TypePack{{arrayOfV, optionalNumber, optionalNumber}});
|
||||
TypePackId unpackReturnPack = arena.addTypePack(TypePack{{}, anyTypePack});
|
||||
TypeId unpackFunc = arena.addType(FunctionTypeVar{{genericV}, {}, unpackArgsPack, unpackReturnPack});
|
||||
|
||||
TypeId packResult = arena.addType(TableTypeVar{
|
||||
TableTypeVar::Props{{"n", {numberType}}}, TableIndexer{numberType, numberType}, typeChecker.globalScope->level, TableState::Sealed});
|
||||
TypePackId packArgsPack = arena.addTypePack(TypePack{{}, anyTypePack});
|
||||
TypePackId packReturnPack = arena.addTypePack(TypePack{{packResult}});
|
||||
|
||||
TypeId comparator = makeFunction(arena, std::nullopt, {genericV, genericV}, {booleanType});
|
||||
TypeId optionalComparator = makeOption(typeChecker, arena, comparator);
|
||||
|
||||
TypeId packFn = arena.addType(FunctionTypeVar(packArgsPack, packReturnPack));
|
||||
|
||||
TableTypeVar::Props tableLib = {
|
||||
{"concat", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalString, optionalNumber, optionalNumber}, {stringType})}},
|
||||
{"insert", {makeIntersection(arena, {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, genericV}, {}),
|
||||
makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, numberType, genericV}, {})})}},
|
||||
{"maxn", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV}, {numberType})}},
|
||||
{"remove", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalNumber}, {optionalV})}},
|
||||
{"sort", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalComparator}, {})}},
|
||||
{"create", {makeFunction(arena, std::nullopt, {genericV}, {}, {numberType, optionalV}, {arrayOfV})}},
|
||||
{"find", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, genericV, optionalNumber}, {optionalNumber})}},
|
||||
|
||||
{"unpack", {unpackFunc}}, // FIXME
|
||||
{"pack", {packFn}},
|
||||
|
||||
// Lua 5.0 compat
|
||||
{"getn", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV}, {numberType})}},
|
||||
{"foreach", {makeFunction(arena, std::nullopt, {genericK, genericV}, {},
|
||||
{mapOfKtoV, makeFunction(arena, std::nullopt, {genericK, genericV}, {})}, {})}},
|
||||
{"foreachi", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, makeFunction(arena, std::nullopt, {genericV}, {})}, {})}},
|
||||
|
||||
// backported from Lua 5.3
|
||||
{"move", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, numberType, numberType, numberType, arrayOfV}, {})}},
|
||||
|
||||
// added in Luau (borrowed from LuaJIT)
|
||||
{"clear", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {})}},
|
||||
|
||||
{"freeze", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {mapOfKtoV})}},
|
||||
{"isfrozen", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {booleanType})}},
|
||||
};
|
||||
|
||||
assignPropDocumentationSymbols(tableLib, "@luau/global/table");
|
||||
addGlobalBinding(
|
||||
typeChecker, "table", arena.addType(TableTypeVar{tableLib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
|
||||
|
||||
TableTypeVar::Props coroutineLib = {
|
||||
{"create", {makeFunction(arena, std::nullopt, {anyFunction}, {threadType})}},
|
||||
{"resume", {arena.addType(FunctionTypeVar{arena.addTypePack(TypePack{{threadType}, anyTypePack}), anyTypePack})}},
|
||||
{"running", {makeFunction(arena, std::nullopt, {}, {threadType})}},
|
||||
{"status", {makeFunction(arena, std::nullopt, {threadType}, {stringType})}},
|
||||
{"wrap", {makeFunction(
|
||||
arena, std::nullopt, {anyFunction}, {anyType})}}, // FIXME this technically returns a function, but we can't represent this
|
||||
// atm since it can be called with different arg types at different times
|
||||
{"yield", {arena.addType(FunctionTypeVar{anyTypePack, anyTypePack})}},
|
||||
{"isyieldable", {makeFunction(arena, std::nullopt, {}, {booleanType})}},
|
||||
};
|
||||
|
||||
assignPropDocumentationSymbols(coroutineLib, "@luau/global/coroutine");
|
||||
addGlobalBinding(typeChecker, "coroutine",
|
||||
arena.addType(TableTypeVar{coroutineLib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
|
||||
|
||||
TypeId genericT = arena.addType(GenericTypeVar{"T"});
|
||||
TypeId genericR = arena.addType(GenericTypeVar{"R"});
|
||||
|
||||
// assert returns all arguments
|
||||
TypePackId assertArgs = arena.addTypePack({genericT, optionalString});
|
||||
TypePackId assertRets = arena.addTypePack({genericT});
|
||||
addGlobalBinding(typeChecker, "assert", arena.addType(FunctionTypeVar{assertArgs, assertRets}), "@luau");
|
||||
|
||||
addGlobalBinding(typeChecker, "print", arena.addType(FunctionTypeVar{anyTypePack, emptyPack}), "@luau");
|
||||
|
||||
addGlobalBinding(typeChecker, "type", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
|
||||
addGlobalBinding(typeChecker, "typeof", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
|
||||
|
||||
addGlobalBinding(typeChecker, "error", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT, optionalNumber}, {}), "@luau");
|
||||
|
||||
addGlobalBinding(typeChecker, "tostring", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
|
||||
addGlobalBinding(
|
||||
typeChecker, "tonumber", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT, optionalNumber}, {numberType}), "@luau");
|
||||
|
||||
addGlobalBinding(
|
||||
typeChecker, "rawequal", makeFunction(arena, std::nullopt, {genericT, genericR}, {}, {genericT, genericR}, {booleanType}), "@luau");
|
||||
addGlobalBinding(
|
||||
typeChecker, "rawget", makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV, genericK}, {genericV}), "@luau");
|
||||
addGlobalBinding(typeChecker, "rawset",
|
||||
makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV, genericK, genericV}, {mapOfKtoV}), "@luau");
|
||||
|
||||
TypePackId genericTPack = arena.addTypePack({genericT});
|
||||
TypePackId genericRPack = arena.addTypePack({genericR});
|
||||
TypeId genericArgsToReturnFunction = arena.addType(
|
||||
FunctionTypeVar{{genericT, genericR}, {}, arena.addTypePack(TypePack{{}, genericTPack}), arena.addTypePack(TypePack{{}, genericRPack})});
|
||||
|
||||
TypeId setfenvArgType = makeUnion(arena, {numberType, genericArgsToReturnFunction});
|
||||
TypeId setfenvReturnType = makeOption(typeChecker, arena, genericArgsToReturnFunction);
|
||||
addGlobalBinding(typeChecker, "setfenv", makeFunction(arena, std::nullopt, {setfenvArgType, stringToAnyMap}, {setfenvReturnType}), "@luau");
|
||||
|
||||
TypePackId ipairsArgsTypePack = arena.addTypePack({arrayOfV});
|
||||
|
||||
TypeId ipairsNextFunctionType = arena.addType(
|
||||
FunctionTypeVar{{genericK, genericV}, {}, arena.addTypePack({arrayOfV, numberType}), arena.addTypePack({numberType, genericV})});
|
||||
|
||||
// ipairs returns 'next, Array<V>, 0' so we would need type-level primitives and change to
|
||||
// again, we have a direct reference to 'next' because ipairs returns it
|
||||
// ipairs<V>(t: Array<V>) -> ((Array<V>) -> (number, V), Array<V>, 0)
|
||||
TypePackId ipairsReturnTypePack = arena.addTypePack(TypePack{{ipairsNextFunctionType, arrayOfV, numberType}});
|
||||
|
||||
// ipairs<V>(t: Array<V>) -> ((Array<V>) -> (number, V), Array<V>, number)
|
||||
addGlobalBinding(typeChecker, "ipairs", arena.addType(FunctionTypeVar{{genericV}, {}, ipairsArgsTypePack, ipairsReturnTypePack}), "@luau");
|
||||
|
||||
TypePackId pcallArg0FnArgs = arena.addTypePack(TypePackVar{GenericTypeVar{"A"}});
|
||||
TypePackId pcallArg0FnRet = arena.addTypePack(TypePackVar{GenericTypeVar{"R"}});
|
||||
TypeId pcallArg0 = arena.addType(FunctionTypeVar{pcallArg0FnArgs, pcallArg0FnRet});
|
||||
TypePackId pcallArgsTypePack = arena.addTypePack(TypePack{{pcallArg0}, pcallArg0FnArgs});
|
||||
|
||||
TypePackId pcallReturnTypePack = arena.addTypePack(TypePack{{booleanType}, pcallArg0FnRet});
|
||||
|
||||
// pcall<A..., R...>(f: (A...) -> R..., args: A...) -> boolean, R...
|
||||
addGlobalBinding(typeChecker, "pcall",
|
||||
arena.addType(FunctionTypeVar{{}, {pcallArg0FnArgs, pcallArg0FnRet}, pcallArgsTypePack, pcallReturnTypePack}), "@luau");
|
||||
|
||||
// errors thrown by the function 'f' are propagated onto the function 'err' that accepts it.
|
||||
// and either 'f' or 'err' are valid results of this xpcall
|
||||
// if 'err' did throw an error, then it returns: false, "error in error handling"
|
||||
// TODO: the above is not represented (nor representable) in the type annotation below.
|
||||
//
|
||||
// The real type of xpcall is as such: <E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., A...) -> (true, R1...) | (false,
|
||||
// R2...)
|
||||
TypePackId genericAPack = arena.addTypePack(TypePackVar{GenericTypeVar{"A"}});
|
||||
TypePackId genericR1Pack = arena.addTypePack(TypePackVar{GenericTypeVar{"R1"}});
|
||||
TypePackId genericR2Pack = arena.addTypePack(TypePackVar{GenericTypeVar{"R2"}});
|
||||
|
||||
TypeId genericE = arena.addType(GenericTypeVar{"E"});
|
||||
|
||||
TypeId xpcallFArg = arena.addType(FunctionTypeVar{genericAPack, genericR1Pack});
|
||||
TypeId xpcallErrArg = arena.addType(FunctionTypeVar{arena.addTypePack({genericE}), genericR2Pack});
|
||||
|
||||
TypePackId xpcallArgsPack = arena.addTypePack({{xpcallFArg, xpcallErrArg}, genericAPack});
|
||||
TypePackId xpcallRetPack = arena.addTypePack({{booleanType}, genericR1Pack}); // FIXME
|
||||
|
||||
addGlobalBinding(typeChecker, "xpcall",
|
||||
arena.addType(FunctionTypeVar{{genericE}, {genericAPack, genericR1Pack, genericR2Pack}, xpcallArgsPack, xpcallRetPack}), "@luau");
|
||||
|
||||
addGlobalBinding(typeChecker, "unpack", unpackFunc, "@luau");
|
||||
|
||||
TypePackId selectArgsTypePack = arena.addTypePack(TypePack{
|
||||
{stringOrNumber},
|
||||
anyTypePack // FIXME? select() is tricky.
|
||||
});
|
||||
|
||||
addGlobalBinding(typeChecker, "select", arena.addType(FunctionTypeVar{selectArgsTypePack, anyTypePack}), "@luau");
|
||||
|
||||
// TODO: not completely correct. loadstring's return type should be a function or (nil, string)
|
||||
TypeId loadstringFunc = arena.addType(FunctionTypeVar{anyTypePack, oneAnyPack});
|
||||
|
||||
addGlobalBinding(typeChecker, "loadstring",
|
||||
makeFunction(arena, std::nullopt, {stringType, optionalString},
|
||||
{
|
||||
makeOption(typeChecker, arena, loadstringFunc),
|
||||
makeOption(typeChecker, arena, stringType),
|
||||
}),
|
||||
"@luau");
|
||||
|
||||
// a userdata object is "roughly" the same as a sealed empty table
|
||||
// except `type(newproxy(false))` evaluates to "userdata" so we may need another special type here too.
|
||||
// another important thing to note: the value passed in conditionally creates an empty metatable, and you have to use getmetatable, NOT
|
||||
// setmetatable.
|
||||
// TODO: change this to something Luau can understand how to reject `setmetatable(newproxy(false or true), {})`.
|
||||
TypeId sealedTable = arena.addType(TableTypeVar(TableState::Sealed, typeChecker.globalScope->level));
|
||||
addGlobalBinding(typeChecker, "newproxy", makeFunction(arena, std::nullopt, {optionalBoolean}, {sealedTable}), "@luau");
|
||||
}
|
||||
|
||||
// next<K, V>(t: Table<K, V>, i: K | nil) -> (K, V)
|
||||
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(typeChecker, arena, genericK)}});
|
||||
addGlobalBinding(typeChecker, "next",
|
||||
@ -475,8 +238,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
|
||||
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
|
||||
|
||||
TypeId pairsNext = (FFlag::LuauRankNTypes ? arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})})
|
||||
: getGlobalBinding(typeChecker, "next"));
|
||||
TypeId pairsNext = arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})});
|
||||
TypePackId pairsReturnTypePack = arena.addTypePack(TypePack{{pairsNext, mapOfKtoV, nilType}});
|
||||
|
||||
// NOTE we are missing 'i: K | nil' argument in the first return types' argument.
|
||||
@ -711,7 +473,7 @@ static std::optional<ExprResult<TypePackId>> magicFunctionRequire(
|
||||
if (!checkRequirePath(typechecker, expr.args.data[0]))
|
||||
return std::nullopt;
|
||||
|
||||
const AstExpr* require = FFlag::LuauNewRequireTrace ? &expr : expr.args.data[0];
|
||||
const AstExpr* require = FFlag::LuauNewRequireTrace2 ? &expr : expr.args.data[0];
|
||||
|
||||
if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, *require))
|
||||
return ExprResult<TypePackId>{arena.addTypePack({typechecker.checkRequire(scope, *moduleInfo, expr.location)})};
|
||||
|
@ -1,9 +1,6 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauParseGenericFunctions)
|
||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -19,6 +16,8 @@ declare bit32: {
|
||||
bnot: (number) -> number,
|
||||
extract: (number, number, number?) -> number,
|
||||
replace: (number, number, number, number?) -> number,
|
||||
countlz: (number) -> number,
|
||||
countrz: (number) -> number,
|
||||
}
|
||||
|
||||
declare math: {
|
||||
@ -103,15 +102,6 @@ declare _VERSION: string
|
||||
|
||||
declare function gcinfo(): number
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
std::string src = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
if (FFlag::LuauParseGenericFunctions && FFlag::LuauGenericFunctions)
|
||||
{
|
||||
src += R"(
|
||||
declare function print<T...>(...: T...)
|
||||
|
||||
declare function type<T>(value: T): string
|
||||
@ -208,10 +198,12 @@ std::string getBuiltinDefinitionSource()
|
||||
|
||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||
)";
|
||||
}
|
||||
|
||||
return src;
|
||||
)BUILTIN_SRC";
|
||||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
return kBuiltinDefinitionLuaSrc;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -94,8 +94,23 @@ struct ErrorConverter
|
||||
{
|
||||
std::string operator()(const Luau::TypeMismatch& tm) const
|
||||
{
|
||||
ToStringOptions opts;
|
||||
return "Type '" + Luau::toString(tm.givenType, opts) + "' could not be converted into '" + Luau::toString(tm.wantedType, opts) + "'";
|
||||
std::string result = "Type '" + Luau::toString(tm.givenType) + "' could not be converted into '" + Luau::toString(tm.wantedType) + "'";
|
||||
|
||||
if (tm.error)
|
||||
{
|
||||
result += "\ncaused by:\n ";
|
||||
|
||||
if (!tm.reason.empty())
|
||||
result += tm.reason + ". ";
|
||||
|
||||
result += Luau::toString(*tm.error);
|
||||
}
|
||||
else if (!tm.reason.empty())
|
||||
{
|
||||
result += "; " + tm.reason;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string operator()(const Luau::UnknownSymbol& e) const
|
||||
@ -478,9 +493,36 @@ struct InvalidNameChecker
|
||||
}
|
||||
};
|
||||
|
||||
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType)
|
||||
: wantedType(wantedType)
|
||||
, givenType(givenType)
|
||||
{
|
||||
}
|
||||
|
||||
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason)
|
||||
: wantedType(wantedType)
|
||||
, givenType(givenType)
|
||||
, reason(reason)
|
||||
{
|
||||
}
|
||||
|
||||
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error)
|
||||
: wantedType(wantedType)
|
||||
, givenType(givenType)
|
||||
, reason(reason)
|
||||
, error(std::make_shared<TypeError>(std::move(error)))
|
||||
{
|
||||
}
|
||||
|
||||
bool TypeMismatch::operator==(const TypeMismatch& rhs) const
|
||||
{
|
||||
return *wantedType == *rhs.wantedType && *givenType == *rhs.givenType;
|
||||
if (!!error != !!rhs.error)
|
||||
return false;
|
||||
|
||||
if (error && !(*error == *rhs.error))
|
||||
return false;
|
||||
|
||||
return *wantedType == *rhs.wantedType && *givenType == *rhs.givenType && reason == rhs.reason;
|
||||
}
|
||||
|
||||
bool UnknownSymbol::operator==(const UnknownSymbol& rhs) const
|
||||
@ -690,130 +732,141 @@ bool containsParseErrorName(const TypeError& error)
|
||||
return Luau::visit(InvalidNameChecker{}, error.data);
|
||||
}
|
||||
|
||||
void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
|
||||
template<typename T>
|
||||
void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
|
||||
{
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
|
||||
auto clone = [&](auto&& ty) {
|
||||
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
|
||||
};
|
||||
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
using T = std::decay_t<decltype(e)>;
|
||||
copyError(e, destArena, seenTypes, seenTypePacks);
|
||||
};
|
||||
|
||||
if constexpr (false)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypeMismatch>)
|
||||
{
|
||||
e.wantedType = clone(e.wantedType);
|
||||
e.givenType = clone(e.givenType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownSymbol>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownProperty>)
|
||||
{
|
||||
e.table = clone(e.table);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, NotATable>)
|
||||
{
|
||||
e.ty = clone(e.ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotExtendTable>)
|
||||
{
|
||||
e.tableType = clone(e.tableType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OnlyTablesCanHaveMethods>)
|
||||
{
|
||||
e.tableType = clone(e.tableType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DuplicateTypeDefinition>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CountMismatch>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionDoesNotTakeSelf>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionRequiresSelf>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OccursCheckFailed>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownRequire>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IncorrectGenericParameterCount>)
|
||||
{
|
||||
e.typeFun = clone(e.typeFun);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SyntaxError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CodeTooComplex>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnificationTooComplex>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownPropButFoundLikeProp>)
|
||||
{
|
||||
e.table = clone(e.table);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, GenericError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||
{
|
||||
e.ty = clone(e.ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DeprecatedApiUsed>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ModuleHasCyclicDependency>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IllegalRequire>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionExitsWithoutReturning>)
|
||||
{
|
||||
e.expectedReturnType = clone(e.expectedReturnType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DuplicateGenericParameter>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotInferBinaryOperation>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MissingProperties>)
|
||||
{
|
||||
e.superType = clone(e.superType);
|
||||
e.subType = clone(e.subType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SwappedGenericTypeParameter>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OptionalValueAccess>)
|
||||
{
|
||||
e.optional = clone(e.optional);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MissingUnionProperty>)
|
||||
{
|
||||
e.type = clone(e.type);
|
||||
if constexpr (false)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypeMismatch>)
|
||||
{
|
||||
e.wantedType = clone(e.wantedType);
|
||||
e.givenType = clone(e.givenType);
|
||||
|
||||
for (auto& ty : e.missing)
|
||||
ty = clone(ty);
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||
if (e.error)
|
||||
visit(visitErrorData, e.error->data);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownSymbol>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownProperty>)
|
||||
{
|
||||
e.table = clone(e.table);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, NotATable>)
|
||||
{
|
||||
e.ty = clone(e.ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotExtendTable>)
|
||||
{
|
||||
e.tableType = clone(e.tableType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OnlyTablesCanHaveMethods>)
|
||||
{
|
||||
e.tableType = clone(e.tableType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DuplicateTypeDefinition>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CountMismatch>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionDoesNotTakeSelf>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionRequiresSelf>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OccursCheckFailed>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownRequire>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IncorrectGenericParameterCount>)
|
||||
{
|
||||
e.typeFun = clone(e.typeFun);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SyntaxError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CodeTooComplex>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnificationTooComplex>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, UnknownPropButFoundLikeProp>)
|
||||
{
|
||||
e.table = clone(e.table);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, GenericError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||
{
|
||||
e.ty = clone(e.ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DeprecatedApiUsed>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ModuleHasCyclicDependency>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, IllegalRequire>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FunctionExitsWithoutReturning>)
|
||||
{
|
||||
e.expectedReturnType = clone(e.expectedReturnType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DuplicateGenericParameter>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotInferBinaryOperation>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MissingProperties>)
|
||||
{
|
||||
e.superType = clone(e.superType);
|
||||
e.subType = clone(e.subType);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SwappedGenericTypeParameter>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, OptionalValueAccess>)
|
||||
{
|
||||
e.optional = clone(e.optional);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, MissingUnionProperty>)
|
||||
{
|
||||
e.type = clone(e.type);
|
||||
|
||||
for (auto& ty : e.missing)
|
||||
ty = clone(ty);
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||
}
|
||||
|
||||
void copyErrors(ErrorVec& errors, TypeArena& destArena)
|
||||
{
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
copyError(e, destArena, seenTypes, seenTypePacks);
|
||||
};
|
||||
|
||||
LUAU_ASSERT(!destArena.typeVars.isFrozen());
|
||||
|
@ -18,11 +18,10 @@
|
||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSecondTypecheckKnowsTheDataModel, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
|
||||
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClearScopes, false)
|
||||
|
||||
namespace Luau
|
||||
@ -415,7 +414,7 @@ CheckResult Frontend::check(const ModuleName& name)
|
||||
// If we're typechecking twice, we do so.
|
||||
// The second typecheck is always in strict mode with DM awareness
|
||||
// to provide better typen information for IDE features.
|
||||
if (options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
||||
if (options.typecheckTwice)
|
||||
{
|
||||
ModulePtr moduleForAutocomplete = typeCheckerForAutocomplete.check(sourceModule, Mode::Strict);
|
||||
moduleResolverForAutocomplete.modules[moduleName] = moduleForAutocomplete;
|
||||
@ -897,7 +896,7 @@ std::optional<ModuleInfo> FrontendModuleResolver::resolveModuleInfo(const Module
|
||||
const auto& exprs = it->second.exprs;
|
||||
|
||||
const ModuleInfo* info = exprs.find(&pathExpr);
|
||||
if (!info || (!FFlag::LuauNewRequireTrace && info->name.empty()))
|
||||
if (!info || (!FFlag::LuauNewRequireTrace2 && info->name.empty()))
|
||||
return std::nullopt;
|
||||
|
||||
return *info;
|
||||
@ -914,7 +913,7 @@ const ModulePtr FrontendModuleResolver::getModule(const ModuleName& moduleName)
|
||||
|
||||
bool FrontendModuleResolver::moduleExists(const ModuleName& moduleName) const
|
||||
{
|
||||
if (FFlag::LuauNewRequireTrace)
|
||||
if (FFlag::LuauNewRequireTrace2)
|
||||
return frontend->sourceNodes.count(moduleName) != 0;
|
||||
else
|
||||
return frontend->fileResolver->moduleExists(moduleName);
|
||||
|
@ -12,9 +12,6 @@
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLinterUnknownTypeVectorAware, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLinterTableMoveZero, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -1110,10 +1107,7 @@ private:
|
||||
|
||||
if (g && g->name == "type")
|
||||
{
|
||||
if (FFlag::LuauLinterUnknownTypeVectorAware)
|
||||
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
|
||||
else
|
||||
validateType(arg, {Kind_Primitive}, "primitive type");
|
||||
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
|
||||
}
|
||||
else if (g && g->name == "typeof")
|
||||
{
|
||||
@ -2146,7 +2140,7 @@ private:
|
||||
"wrap it in parentheses to silence");
|
||||
}
|
||||
|
||||
if (FFlag::LuauLinterTableMoveZero && func->index == "move" && node->args.size >= 4)
|
||||
if (func->index == "move" && node->args.size >= 4)
|
||||
{
|
||||
// table.move(t, 0, _, _)
|
||||
if (isConstant(args[1], 0.0))
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
||||
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCloneBoundTables, false)
|
||||
@ -290,9 +289,7 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
|
||||
for (TypePackId genericPack : t.genericPacks)
|
||||
ftv->genericPacks.push_back(clone(genericPack, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
|
||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
||||
ftv->tags = t.tags;
|
||||
|
||||
ftv->tags = t.tags;
|
||||
ftv->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
ftv->argNames = t.argNames;
|
||||
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
@ -319,12 +316,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
|
||||
ttv->level = TypeLevel{0, 0};
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
{
|
||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
else
|
||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
|
||||
}
|
||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
|
||||
if (t.indexer)
|
||||
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
|
||||
@ -379,10 +371,7 @@ void TypeCloner::operator()(const ClassTypeVar& t)
|
||||
seenTypes[typeId] = result;
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
||||
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
else
|
||||
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
|
||||
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
|
||||
if (t.parent)
|
||||
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
|
@ -3,8 +3,6 @@
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauOrPredicate)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -60,8 +58,6 @@ std::string toString(const LValue& lvalue)
|
||||
|
||||
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauOrPredicate);
|
||||
|
||||
auto itL = l.begin();
|
||||
auto itR = r.begin();
|
||||
while (itL != l.end() && itR != r.end())
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "Luau/Module.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTraceRequireLookupChild, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace2, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -19,7 +19,7 @@ struct RequireTracerOld : AstVisitor
|
||||
: fileResolver(fileResolver)
|
||||
, currentModuleName(currentModuleName)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauNewRequireTrace);
|
||||
LUAU_ASSERT(!FFlag::LuauNewRequireTrace2);
|
||||
}
|
||||
|
||||
FileResolver* const fileResolver;
|
||||
@ -188,7 +188,7 @@ struct RequireTracer : AstVisitor
|
||||
, currentModuleName(currentModuleName)
|
||||
, locals(nullptr)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewRequireTrace);
|
||||
LUAU_ASSERT(FFlag::LuauNewRequireTrace2);
|
||||
}
|
||||
|
||||
bool visit(AstExprTypeAssertion* expr) override
|
||||
@ -332,7 +332,7 @@ struct RequireTracer : AstVisitor
|
||||
|
||||
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName)
|
||||
{
|
||||
if (FFlag::LuauNewRequireTrace)
|
||||
if (FFlag::LuauNewRequireTrace2)
|
||||
{
|
||||
RequireTraceResult result;
|
||||
RequireTracer tracer{result, fileResolver, currentModuleName};
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionDontReplaceIgnoredTypes, false)
|
||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
||||
LUAU_FASTFLAG(LuauRankNTypes)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace Luau
|
||||
@ -19,7 +17,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
|
||||
if (ignoreChildren(ty))
|
||||
return;
|
||||
|
||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||
@ -68,7 +66,7 @@ void Tarjan::visitChildren(TypePackId tp, int index)
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
|
||||
if (ignoreChildren(tp))
|
||||
return;
|
||||
|
||||
if (const TypePack* tpp = get<TypePack>(tp))
|
||||
@ -399,8 +397,7 @@ TypeId Substitution::clone(TypeId ty)
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
|
||||
|
||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
||||
clone.tags = ttv->tags;
|
||||
clone.tags = ttv->tags;
|
||||
result = addType(std::move(clone));
|
||||
}
|
||||
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
||||
@ -486,7 +483,7 @@ void Substitution::replaceChildren(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
|
||||
if (ignoreChildren(ty))
|
||||
return;
|
||||
|
||||
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
|
||||
@ -535,7 +532,7 @@ void Substitution::replaceChildren(TypePackId tp)
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
|
||||
if (ignoreChildren(tp))
|
||||
return;
|
||||
|
||||
if (TypePack* tpp = getMutable<TypePack>(tp))
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInstantiatedTypeParamRecursion, false)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace Luau
|
||||
@ -237,15 +236,6 @@ struct TypeVarStringifier
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
if (get<FreeTypeVar>(tv))
|
||||
{
|
||||
state.emit(state.getName(tv));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Luau::visit(
|
||||
[this, tv](auto&& t) {
|
||||
return (*this)(tv, t);
|
||||
@ -316,11 +306,7 @@ struct TypeVarStringifier
|
||||
void operator()(TypeId ty, const Unifiable::Free& ftv)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
state.emit(state.getName(ty));
|
||||
else
|
||||
state.emit("<FREE>");
|
||||
state.emit(state.getName(ty));
|
||||
}
|
||||
|
||||
void operator()(TypeId, const BoundTypeVar& btv)
|
||||
@ -724,16 +710,6 @@ struct TypePackStringifier
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
if (get<FreeTypePack>(tp))
|
||||
{
|
||||
state.emit(state.getName(tp));
|
||||
state.emit("...");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto it = state.cycleTpNames.find(tp);
|
||||
if (it != state.cycleTpNames.end())
|
||||
{
|
||||
@ -821,16 +797,8 @@ struct TypePackStringifier
|
||||
void operator()(TypePackId tp, const FreeTypePack& pack)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
{
|
||||
state.emit(state.getName(tp));
|
||||
state.emit("...");
|
||||
}
|
||||
else
|
||||
{
|
||||
state.emit("<FREETP>");
|
||||
}
|
||||
state.emit(state.getName(tp));
|
||||
state.emit("...");
|
||||
}
|
||||
|
||||
void operator()(TypePackId, const BoundTypePack& btv)
|
||||
@ -864,23 +832,15 @@ static void assignCycleNames(const std::unordered_set<TypeId>& cycles, const std
|
||||
std::string name;
|
||||
|
||||
// TODO: use the stringified type list if there are no cycles
|
||||
if (FFlag::LuauInstantiatedTypeParamRecursion)
|
||||
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
|
||||
{
|
||||
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
|
||||
{
|
||||
// If we have a cycle type in type parameters, assign a cycle name for this named table
|
||||
if (std::find_if(ttv->instantiatedTypeParams.begin(), ttv->instantiatedTypeParams.end(), [&](auto&& el) {
|
||||
return cycles.count(follow(el));
|
||||
}) != ttv->instantiatedTypeParams.end())
|
||||
cycleNames[cycleTy] = ttv->name ? *ttv->name : *ttv->syntheticName;
|
||||
// If we have a cycle type in type parameters, assign a cycle name for this named table
|
||||
if (std::find_if(ttv->instantiatedTypeParams.begin(), ttv->instantiatedTypeParams.end(), [&](auto&& el) {
|
||||
return cycles.count(follow(el));
|
||||
}) != ttv->instantiatedTypeParams.end())
|
||||
cycleNames[cycleTy] = ttv->name ? *ttv->name : *ttv->syntheticName;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
name = "t" + std::to_string(nextIndex);
|
||||
@ -912,58 +872,6 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||
|
||||
ToStringResult result;
|
||||
|
||||
if (!FFlag::LuauInstantiatedTypeParamRecursion && !opts.exhaustive)
|
||||
{
|
||||
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
|
||||
{
|
||||
if (ttv->syntheticName)
|
||||
result.invalid = true;
|
||||
|
||||
// If scope if provided, add module name and check visibility
|
||||
if (ttv->name && opts.scope)
|
||||
{
|
||||
auto [success, moduleName] = canUseTypeNameInScope(opts.scope, *ttv->name);
|
||||
|
||||
if (!success)
|
||||
result.invalid = true;
|
||||
|
||||
if (moduleName)
|
||||
result.name = format("%s.", moduleName->c_str());
|
||||
}
|
||||
|
||||
result.name += ttv->name ? *ttv->name : *ttv->syntheticName;
|
||||
|
||||
if (ttv->instantiatedTypeParams.empty() && (!FFlag::LuauTypeAliasPacks || ttv->instantiatedTypePackParams.empty()))
|
||||
return result;
|
||||
|
||||
std::vector<std::string> params;
|
||||
for (TypeId tp : ttv->instantiatedTypeParams)
|
||||
params.push_back(toString(tp));
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
// Doesn't preserve grouping of multiple type packs
|
||||
// But this is under a parent block of code that is being removed later
|
||||
for (TypePackId tp : ttv->instantiatedTypePackParams)
|
||||
{
|
||||
std::string content = toString(tp);
|
||||
|
||||
if (!content.empty())
|
||||
params.push_back(std::move(content));
|
||||
}
|
||||
}
|
||||
|
||||
result.name += "<" + join(params, ", ") + ">";
|
||||
return result;
|
||||
}
|
||||
else if (auto mtv = get<MetatableTypeVar>(ty); mtv && mtv->syntheticName)
|
||||
{
|
||||
result.invalid = true;
|
||||
result.name = *mtv->syntheticName;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
StringifierState state{opts, result, opts.nameMap};
|
||||
|
||||
std::unordered_set<TypeId> cycles;
|
||||
@ -975,7 +883,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||
|
||||
TypeVarStringifier tvs{state};
|
||||
|
||||
if (FFlag::LuauInstantiatedTypeParamRecursion && !opts.exhaustive)
|
||||
if (!opts.exhaustive)
|
||||
{
|
||||
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
|
||||
{
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace
|
||||
@ -97,9 +96,6 @@ struct Writer
|
||||
{
|
||||
virtual ~Writer() {}
|
||||
|
||||
virtual void begin() {}
|
||||
virtual void end() {}
|
||||
|
||||
virtual void advance(const Position&) = 0;
|
||||
virtual void newline() = 0;
|
||||
virtual void space() = 0;
|
||||
@ -131,6 +127,7 @@ struct StringWriter : Writer
|
||||
if (pos.column < newPos.column)
|
||||
write(std::string(newPos.column - pos.column, ' '));
|
||||
}
|
||||
|
||||
void maybeSpace(const Position& newPos, int reserve) override
|
||||
{
|
||||
if (pos.column + reserve < newPos.column)
|
||||
@ -279,11 +276,14 @@ struct Printer
|
||||
writer.identifier(func->index.value);
|
||||
}
|
||||
|
||||
void visualizeTypePackAnnotation(const AstTypePack& annotation)
|
||||
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg)
|
||||
{
|
||||
advance(annotation.location.begin);
|
||||
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||
{
|
||||
writer.symbol("...");
|
||||
if (!forVarArg)
|
||||
writer.symbol("...");
|
||||
|
||||
visualizeTypeAnnotation(*variadicTp->variadicType);
|
||||
}
|
||||
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
|
||||
@ -293,6 +293,7 @@ struct Printer
|
||||
}
|
||||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||
{
|
||||
LUAU_ASSERT(!forVarArg);
|
||||
visualizeTypeList(explicitTp->typeList, true);
|
||||
}
|
||||
else
|
||||
@ -317,7 +318,7 @@ struct Printer
|
||||
// Only variadic tail
|
||||
if (list.types.size == 0)
|
||||
{
|
||||
visualizeTypePackAnnotation(*list.tailType);
|
||||
visualizeTypePackAnnotation(*list.tailType, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -345,7 +346,7 @@ struct Printer
|
||||
if (list.tailType)
|
||||
{
|
||||
writer.symbol(",");
|
||||
visualizeTypePackAnnotation(*list.tailType);
|
||||
visualizeTypePackAnnotation(*list.tailType, false);
|
||||
}
|
||||
|
||||
writer.symbol(")");
|
||||
@ -542,6 +543,7 @@ struct Printer
|
||||
case AstExprBinary::CompareLt:
|
||||
case AstExprBinary::CompareGt:
|
||||
writer.maybeSpace(a->right->location.begin, 2);
|
||||
writer.symbol(toString(a->op));
|
||||
break;
|
||||
case AstExprBinary::Concat:
|
||||
case AstExprBinary::CompareNe:
|
||||
@ -550,19 +552,35 @@ struct Printer
|
||||
case AstExprBinary::CompareGe:
|
||||
case AstExprBinary::Or:
|
||||
writer.maybeSpace(a->right->location.begin, 3);
|
||||
writer.keyword(toString(a->op));
|
||||
break;
|
||||
case AstExprBinary::And:
|
||||
writer.maybeSpace(a->right->location.begin, 4);
|
||||
writer.keyword(toString(a->op));
|
||||
break;
|
||||
}
|
||||
|
||||
writer.symbol(toString(a->op));
|
||||
|
||||
visualize(*a->right);
|
||||
}
|
||||
else if (const auto& a = expr.as<AstExprTypeAssertion>())
|
||||
{
|
||||
visualize(*a->expr);
|
||||
|
||||
if (writeTypes)
|
||||
{
|
||||
writer.maybeSpace(a->annotation->location.begin, 2);
|
||||
writer.symbol("::");
|
||||
visualizeTypeAnnotation(*a->annotation);
|
||||
}
|
||||
}
|
||||
else if (const auto& a = expr.as<AstExprIfElse>())
|
||||
{
|
||||
writer.keyword("if");
|
||||
visualize(*a->condition);
|
||||
writer.keyword("then");
|
||||
visualize(*a->trueExpr);
|
||||
writer.keyword("else");
|
||||
visualize(*a->falseExpr);
|
||||
}
|
||||
else if (const auto& a = expr.as<AstExprError>())
|
||||
{
|
||||
@ -769,24 +787,31 @@ struct Printer
|
||||
switch (a->op)
|
||||
{
|
||||
case AstExprBinary::Add:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("+=");
|
||||
break;
|
||||
case AstExprBinary::Sub:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("-=");
|
||||
break;
|
||||
case AstExprBinary::Mul:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("*=");
|
||||
break;
|
||||
case AstExprBinary::Div:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("/=");
|
||||
break;
|
||||
case AstExprBinary::Mod:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("%=");
|
||||
break;
|
||||
case AstExprBinary::Pow:
|
||||
writer.maybeSpace(a->value->location.begin, 2);
|
||||
writer.symbol("^=");
|
||||
break;
|
||||
case AstExprBinary::Concat:
|
||||
writer.maybeSpace(a->value->location.begin, 3);
|
||||
writer.symbol("..=");
|
||||
break;
|
||||
default:
|
||||
@ -874,7 +899,7 @@ struct Printer
|
||||
|
||||
void visualizeFunctionBody(AstExprFunction& func)
|
||||
{
|
||||
if (FFlag::LuauGenericFunctions && (func.generics.size > 0 || func.genericPacks.size > 0))
|
||||
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer);
|
||||
writer.symbol("<");
|
||||
@ -913,12 +938,13 @@ struct Printer
|
||||
if (func.vararg)
|
||||
{
|
||||
comma();
|
||||
advance(func.varargLocation.begin);
|
||||
writer.symbol("...");
|
||||
|
||||
if (func.varargAnnotation)
|
||||
{
|
||||
writer.symbol(":");
|
||||
visualizeTypePackAnnotation(*func.varargAnnotation);
|
||||
visualizeTypePackAnnotation(*func.varargAnnotation, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -980,8 +1006,14 @@ struct Printer
|
||||
advance(typeAnnotation.location.begin);
|
||||
if (const auto& a = typeAnnotation.as<AstTypeReference>())
|
||||
{
|
||||
if (a->hasPrefix)
|
||||
{
|
||||
writer.write(a->prefix.value);
|
||||
writer.symbol(".");
|
||||
}
|
||||
|
||||
writer.write(a->name.value);
|
||||
if (a->parameters.size > 0)
|
||||
if (a->parameters.size > 0 || a->hasParameterList)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer);
|
||||
writer.symbol("<");
|
||||
@ -992,7 +1024,7 @@ struct Printer
|
||||
if (o.type)
|
||||
visualizeTypeAnnotation(*o.type);
|
||||
else
|
||||
visualizeTypePackAnnotation(*o.typePack);
|
||||
visualizeTypePackAnnotation(*o.typePack, false);
|
||||
}
|
||||
|
||||
writer.symbol(">");
|
||||
@ -1000,7 +1032,7 @@ struct Printer
|
||||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeFunction>())
|
||||
{
|
||||
if (FFlag::LuauGenericFunctions && (a->generics.size > 0 || a->genericPacks.size > 0))
|
||||
if (a->generics.size > 0 || a->genericPacks.size > 0)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer);
|
||||
writer.symbol("<");
|
||||
@ -1075,7 +1107,16 @@ struct Printer
|
||||
auto rta = r->as<AstTypeReference>();
|
||||
if (rta && rta->name == "nil")
|
||||
{
|
||||
bool wrap = l->as<AstTypeIntersection>() || l->as<AstTypeFunction>();
|
||||
|
||||
if (wrap)
|
||||
writer.symbol("(");
|
||||
|
||||
visualizeTypeAnnotation(*l);
|
||||
|
||||
if (wrap)
|
||||
writer.symbol(")");
|
||||
|
||||
writer.symbol("?");
|
||||
return;
|
||||
}
|
||||
@ -1089,7 +1130,15 @@ struct Printer
|
||||
writer.symbol("|");
|
||||
}
|
||||
|
||||
bool wrap = a->types.data[i]->as<AstTypeIntersection>() || a->types.data[i]->as<AstTypeFunction>();
|
||||
|
||||
if (wrap)
|
||||
writer.symbol("(");
|
||||
|
||||
visualizeTypeAnnotation(*a->types.data[i]);
|
||||
|
||||
if (wrap)
|
||||
writer.symbol(")");
|
||||
}
|
||||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
|
||||
@ -1102,7 +1151,15 @@ struct Printer
|
||||
writer.symbol("&");
|
||||
}
|
||||
|
||||
bool wrap = a->types.data[i]->as<AstTypeUnion>() || a->types.data[i]->as<AstTypeFunction>();
|
||||
|
||||
if (wrap)
|
||||
writer.symbol("(");
|
||||
|
||||
visualizeTypeAnnotation(*a->types.data[i]);
|
||||
|
||||
if (wrap)
|
||||
writer.symbol(")");
|
||||
}
|
||||
}
|
||||
else if (typeAnnotation.is<AstTypeError>())
|
||||
@ -1116,31 +1173,27 @@ struct Printer
|
||||
}
|
||||
};
|
||||
|
||||
void dump(AstNode* node)
|
||||
std::string toString(AstNode* node)
|
||||
{
|
||||
StringWriter writer;
|
||||
writer.pos = node->location.begin;
|
||||
|
||||
Printer printer(writer);
|
||||
printer.writeTypes = true;
|
||||
|
||||
if (auto statNode = dynamic_cast<AstStat*>(node))
|
||||
{
|
||||
printer.visualize(*statNode);
|
||||
printf("%s\n", writer.str().c_str());
|
||||
}
|
||||
else if (auto exprNode = dynamic_cast<AstExpr*>(node))
|
||||
{
|
||||
printer.visualize(*exprNode);
|
||||
printf("%s\n", writer.str().c_str());
|
||||
}
|
||||
else if (auto typeNode = dynamic_cast<AstType*>(node))
|
||||
{
|
||||
printer.visualizeTypeAnnotation(*typeNode);
|
||||
printf("%s\n", writer.str().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Can't dump this node\n");
|
||||
}
|
||||
|
||||
return writer.str();
|
||||
}
|
||||
|
||||
void dump(AstNode* node)
|
||||
{
|
||||
printf("%s\n", toString(node).c_str());
|
||||
}
|
||||
|
||||
std::string transpile(AstStatBlock& block)
|
||||
@ -1149,6 +1202,7 @@ std::string transpile(AstStatBlock& block)
|
||||
Printer(writer).visualizeBlock(block);
|
||||
return writer.str();
|
||||
}
|
||||
|
||||
std::string transpileWithTypes(AstStatBlock& block)
|
||||
{
|
||||
StringWriter writer;
|
||||
@ -1158,7 +1212,7 @@ std::string transpileWithTypes(AstStatBlock& block)
|
||||
return writer.str();
|
||||
}
|
||||
|
||||
TranspileResult transpile(std::string_view source, ParseOptions options)
|
||||
TranspileResult transpile(std::string_view source, ParseOptions options, bool withTypes)
|
||||
{
|
||||
auto allocator = Allocator{};
|
||||
auto names = AstNameTable{allocator};
|
||||
@ -1176,6 +1230,9 @@ TranspileResult transpile(std::string_view source, ParseOptions options)
|
||||
if (!parseResult.root)
|
||||
return TranspileResult{"", {}, "Internal error: Parser yielded empty parse tree"};
|
||||
|
||||
if (withTypes)
|
||||
return TranspileResult{transpileWithTypes(*parseResult.root)};
|
||||
|
||||
return TranspileResult{transpile(*parseResult.root)};
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||
@ -203,39 +202,23 @@ public:
|
||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("<Cycle>"));
|
||||
|
||||
AstArray<AstName> generics;
|
||||
if (FFlag::LuauGenericFunctions)
|
||||
generics.size = ftv.generics.size();
|
||||
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
|
||||
size_t numGenerics = 0;
|
||||
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
|
||||
{
|
||||
generics.size = ftv.generics.size();
|
||||
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
|
||||
size_t i = 0;
|
||||
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
|
||||
{
|
||||
if (auto gtv = get<GenericTypeVar>(*it))
|
||||
generics.data[i++] = AstName(gtv->name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
generics.size = 0;
|
||||
generics.data = nullptr;
|
||||
if (auto gtv = get<GenericTypeVar>(*it))
|
||||
generics.data[numGenerics++] = AstName(gtv->name.c_str());
|
||||
}
|
||||
|
||||
AstArray<AstName> genericPacks;
|
||||
if (FFlag::LuauGenericFunctions)
|
||||
genericPacks.size = ftv.genericPacks.size();
|
||||
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
|
||||
size_t numGenericPacks = 0;
|
||||
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
|
||||
{
|
||||
genericPacks.size = ftv.genericPacks.size();
|
||||
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
|
||||
size_t i = 0;
|
||||
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
|
||||
{
|
||||
if (auto gtv = get<GenericTypeVar>(*it))
|
||||
genericPacks.data[i++] = AstName(gtv->name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
generics.size = 0;
|
||||
generics.data = nullptr;
|
||||
if (auto gtv = get<GenericTypeVar>(*it))
|
||||
genericPacks.data[numGenericPacks++] = AstName(gtv->name.c_str());
|
||||
}
|
||||
|
||||
AstArray<AstType*> argTypes;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -97,7 +97,7 @@ TypePackIterator begin(TypePackId tp)
|
||||
|
||||
TypePackIterator end(TypePackId tp)
|
||||
{
|
||||
return FFlag::LuauAddMissingFollow ? TypePackIterator{} : TypePackIterator{nullptr};
|
||||
return TypePackIterator{};
|
||||
}
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
||||
@ -203,7 +203,7 @@ TypePackId follow(TypePackId tp)
|
||||
|
||||
size_t size(TypePackId tp)
|
||||
{
|
||||
if (auto pack = get<TypePack>(FFlag::LuauAddMissingFollow ? follow(tp) : tp))
|
||||
if (auto pack = get<TypePack>(follow(tp)))
|
||||
return size(*pack);
|
||||
else
|
||||
return 0;
|
||||
@ -227,7 +227,7 @@ size_t size(const TypePack& tp)
|
||||
size_t result = tp.head.size();
|
||||
if (tp.tail)
|
||||
{
|
||||
const TypePack* tail = get<TypePack>(FFlag::LuauAddMissingFollow ? follow(*tp.tail) : *tp.tail);
|
||||
const TypePack* tail = get<TypePack>(follow(*tp.tail));
|
||||
if (tail)
|
||||
result += size(*tail);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||
LUAU_FASTFLAG(LuauRankNTypes)
|
||||
LUAU_FASTFLAG(LuauTypeGuardPeelsAwaySubclasses)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
|
||||
|
||||
@ -42,7 +40,7 @@ TypeId follow(TypeId t)
|
||||
};
|
||||
|
||||
auto force = [](TypeId ty) {
|
||||
if (auto ltv = FFlag::LuauAddMissingFollow ? get_if<LazyTypeVar>(&ty->ty) : get<LazyTypeVar>(ty))
|
||||
if (auto ltv = get_if<LazyTypeVar>(&ty->ty))
|
||||
{
|
||||
TypeId res = ltv->thunk();
|
||||
if (get<LazyTypeVar>(res))
|
||||
@ -296,7 +294,7 @@ bool maybeGeneric(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (auto ftv = get<FreeTypeVar>(ty))
|
||||
return FFlag::LuauRankNTypes || ftv->DEPRECATED_canBeGeneric;
|
||||
return true;
|
||||
else if (auto ttv = get<TableTypeVar>(ty))
|
||||
{
|
||||
// TODO: recurse on table types CLI-39914
|
||||
@ -545,15 +543,30 @@ TypeId makeFunction(TypeArena& arena, std::optional<TypeId> selfType, std::initi
|
||||
std::initializer_list<TypePackId> genericPacks, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
|
||||
std::initializer_list<TypeId> retTypes);
|
||||
|
||||
static TypeVar nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true};
|
||||
static TypeVar numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true};
|
||||
static TypeVar stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true};
|
||||
static TypeVar booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true};
|
||||
static TypeVar threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true};
|
||||
static TypeVar anyType_{AnyTypeVar{}};
|
||||
static TypeVar errorType_{ErrorTypeVar{}};
|
||||
static TypeVar optionalNumberType_{UnionTypeVar{{&numberType_, &nilType_}}};
|
||||
|
||||
static TypePackVar anyTypePack_{VariadicTypePack{&anyType_}, true};
|
||||
static TypePackVar errorTypePack_{Unifiable::Error{}};
|
||||
|
||||
SingletonTypes::SingletonTypes()
|
||||
: arena(new TypeArena)
|
||||
, nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true}
|
||||
, numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true}
|
||||
, stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true}
|
||||
, booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true}
|
||||
, threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true}
|
||||
, anyType_{AnyTypeVar{}}
|
||||
, errorType_{ErrorTypeVar{}}
|
||||
: nilType(&nilType_)
|
||||
, numberType(&numberType_)
|
||||
, stringType(&stringType_)
|
||||
, booleanType(&booleanType_)
|
||||
, threadType(&threadType_)
|
||||
, anyType(&anyType_)
|
||||
, errorType(&errorType_)
|
||||
, optionalNumberType(&optionalNumberType_)
|
||||
, anyTypePack(&anyTypePack_)
|
||||
, errorTypePack(&errorTypePack_)
|
||||
, arena(new TypeArena)
|
||||
{
|
||||
TypeId stringMetatable = makeStringMetatable();
|
||||
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, makeStringMetatable()};
|
||||
@ -1372,24 +1385,6 @@ UnionTypeVarIterator end(const UnionTypeVar* utv)
|
||||
return UnionTypeVarIterator{};
|
||||
}
|
||||
|
||||
static std::vector<TypeId> DEPRECATED_filterMap(TypeId type, TypeIdPredicate predicate)
|
||||
{
|
||||
std::vector<TypeId> result;
|
||||
|
||||
if (auto utv = get<UnionTypeVar>(follow(type)))
|
||||
{
|
||||
for (TypeId option : utv)
|
||||
{
|
||||
if (auto out = predicate(follow(option)))
|
||||
result.push_back(*out);
|
||||
}
|
||||
}
|
||||
else if (auto out = predicate(follow(type)))
|
||||
return {*out};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
|
||||
{
|
||||
const char* options = "cdiouxXeEfgGqs";
|
||||
@ -1470,9 +1465,6 @@ std::optional<ExprResult<TypePackId>> magicFunctionFormat(
|
||||
|
||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
||||
{
|
||||
if (!FFlag::LuauTypeGuardPeelsAwaySubclasses)
|
||||
return DEPRECATED_filterMap(type, predicate);
|
||||
|
||||
type = follow(type);
|
||||
|
||||
if (auto utv = get<UnionTypeVar>(type))
|
||||
|
@ -1,8 +1,6 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/Unifiable.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauRankNTypes)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Unifiable
|
||||
@ -14,14 +12,6 @@ Free::Free(TypeLevel level)
|
||||
{
|
||||
}
|
||||
|
||||
Free::Free(TypeLevel level, bool DEPRECATED_canBeGeneric)
|
||||
: index(++nextIndex)
|
||||
, level(level)
|
||||
, DEPRECATED_canBeGeneric(DEPRECATED_canBeGeneric)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
||||
}
|
||||
|
||||
int Free::nextIndex = 0;
|
||||
|
||||
Generic::Generic()
|
||||
|
@ -14,17 +14,15 @@
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauDontMutatePersistentFunctions, false)
|
||||
LUAU_FASTFLAG(LuauRankNTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnionHeuristic, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSealedTableUnifyOptionalFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOccursCheckOkWithRecursiveFunctions, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypecheckOpts, false)
|
||||
LUAU_FASTFLAG(LuauShareTxnSeen);
|
||||
LUAU_FASTFLAGVARIABLE(LuauCacheUnifyTableResults, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExtendedTypeMismatchError, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExtendedClassMismatchError, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -219,17 +217,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
*asMutable(subTy) = BoundTypeVar(superTy);
|
||||
}
|
||||
|
||||
if (!FFlag::LuauRankNTypes)
|
||||
l->DEPRECATED_canBeGeneric &= r->DEPRECATED_canBeGeneric;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (l && r && FFlag::LuauGenericFunctions)
|
||||
else if (l && r)
|
||||
{
|
||||
log(superTy);
|
||||
occursCheck(superTy, subTy);
|
||||
if (!FFlag::LuauRankNTypes)
|
||||
r->DEPRECATED_canBeGeneric &= l->DEPRECATED_canBeGeneric;
|
||||
r->level = min(r->level, l->level);
|
||||
*asMutable(superTy) = BoundTypeVar(subTy);
|
||||
return;
|
||||
@ -240,7 +233,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
|
||||
// Unification can't change the level of a generic.
|
||||
auto rightGeneric = get<GenericTypeVar>(subTy);
|
||||
if (FFlag::LuauRankNTypes && rightGeneric && !rightGeneric->level.subsumes(l->level))
|
||||
if (rightGeneric && !rightGeneric->level.subsumes(l->level))
|
||||
{
|
||||
// TODO: a more informative error message? CLI-39912
|
||||
errors.push_back(TypeError{location, GenericError{"Generic subtype escaping scope"}});
|
||||
@ -266,31 +259,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
|
||||
// Unification can't change the level of a generic.
|
||||
auto leftGeneric = get<GenericTypeVar>(superTy);
|
||||
if (FFlag::LuauRankNTypes && leftGeneric && !leftGeneric->level.subsumes(r->level))
|
||||
if (leftGeneric && !leftGeneric->level.subsumes(r->level))
|
||||
{
|
||||
// TODO: a more informative error message? CLI-39912
|
||||
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the old code which is just wrong
|
||||
auto wrongGeneric = get<GenericTypeVar>(subTy); // Guaranteed to be null
|
||||
if (!FFlag::LuauRankNTypes && FFlag::LuauGenericFunctions && wrongGeneric && r->level.subsumes(wrongGeneric->level))
|
||||
{
|
||||
// This code is unreachable! Should we just remove it?
|
||||
// TODO: a more informative error message? CLI-39912
|
||||
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're unifying a monotype with a polytype
|
||||
if (FFlag::LuauGenericFunctions && !FFlag::LuauRankNTypes && !r->DEPRECATED_canBeGeneric && isGeneric(superTy))
|
||||
{
|
||||
// TODO: a more informative error message? CLI-39912
|
||||
errors.push_back(TypeError{location, GenericError{"Failed to unify a polytype with a monotype"}});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get<ErrorTypeVar>(subTy))
|
||||
{
|
||||
if (auto leftLevel = getMutableLevel(superTy))
|
||||
@ -333,6 +308,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
// A | B <: T if A <: T and B <: T
|
||||
bool failed = false;
|
||||
std::optional<TypeError> unificationTooComplex;
|
||||
std::optional<TypeError> firstFailedOption;
|
||||
|
||||
size_t count = uv->options.size();
|
||||
size_t i = 0;
|
||||
@ -345,7 +321,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
if (auto e = hasUnificationTooComplex(innerState.errors))
|
||||
unificationTooComplex = e;
|
||||
else if (!innerState.errors.empty())
|
||||
{
|
||||
// 'nil' option is skipped from extended report because we present the type in a special way - 'T?'
|
||||
if (FFlag::LuauExtendedTypeMismatchError && !firstFailedOption && !isNil(type))
|
||||
firstFailedOption = {innerState.errors.front()};
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (i != count - 1)
|
||||
innerState.log.rollback();
|
||||
@ -358,7 +340,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
if (unificationTooComplex)
|
||||
errors.push_back(*unificationTooComplex);
|
||||
else if (failed)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
{
|
||||
if (FFlag::LuauExtendedTypeMismatchError && firstFailedOption)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "Not all union options are compatible", *firstFailedOption}});
|
||||
else
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
}
|
||||
}
|
||||
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
|
||||
{
|
||||
@ -425,14 +412,49 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
if (unificationTooComplex)
|
||||
errors.push_back(*unificationTooComplex);
|
||||
else if (!found)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
{
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "none of the union options are compatible"}});
|
||||
else
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
}
|
||||
}
|
||||
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(superTy))
|
||||
{
|
||||
// T <: A & B if A <: T and B <: T
|
||||
for (TypeId type : uv->parts)
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
{
|
||||
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
|
||||
std::optional<TypeError> unificationTooComplex;
|
||||
std::optional<TypeError> firstFailedOption;
|
||||
|
||||
// T <: A & B if A <: T and B <: T
|
||||
for (TypeId type : uv->parts)
|
||||
{
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
|
||||
|
||||
if (auto e = hasUnificationTooComplex(innerState.errors))
|
||||
unificationTooComplex = e;
|
||||
else if (!innerState.errors.empty())
|
||||
{
|
||||
if (!firstFailedOption)
|
||||
firstFailedOption = {innerState.errors.front()};
|
||||
}
|
||||
|
||||
log.concat(std::move(innerState.log));
|
||||
}
|
||||
|
||||
if (unificationTooComplex)
|
||||
errors.push_back(*unificationTooComplex);
|
||||
else if (firstFailedOption)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "Not all intersection parts are compatible", *firstFailedOption}});
|
||||
}
|
||||
else
|
||||
{
|
||||
// T <: A & B if A <: T and B <: T
|
||||
for (TypeId type : uv->parts)
|
||||
{
|
||||
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
|
||||
@ -480,7 +502,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
if (unificationTooComplex)
|
||||
errors.push_back(*unificationTooComplex);
|
||||
else if (!found)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
{
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "none of the intersection parts are compatible"}});
|
||||
else
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
}
|
||||
}
|
||||
else if (get<PrimitiveTypeVar>(superTy) && get<PrimitiveTypeVar>(subTy))
|
||||
tryUnifyPrimitives(superTy, subTy);
|
||||
@ -773,8 +800,8 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
|
||||
// If both are at the end, we're done
|
||||
if (!superIter.good() && !subIter.good())
|
||||
{
|
||||
const bool lFreeTail = l->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*l->tail) : *l->tail) != nullptr;
|
||||
const bool rFreeTail = r->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*r->tail) : *r->tail) != nullptr;
|
||||
const bool lFreeTail = l->tail && get<FreeTypePack>(follow(*l->tail)) != nullptr;
|
||||
const bool rFreeTail = r->tail && get<FreeTypePack>(follow(*r->tail)) != nullptr;
|
||||
if (lFreeTail && rFreeTail)
|
||||
tryUnify_(*l->tail, *r->tail);
|
||||
else if (lFreeTail)
|
||||
@ -812,7 +839,7 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
|
||||
}
|
||||
|
||||
// In nonstrict mode, any also marks an optional argument.
|
||||
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(FFlag::LuauAddMissingFollow ? follow(*superIter) : *superIter))
|
||||
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(follow(*superIter)))
|
||||
{
|
||||
superIter.advance();
|
||||
continue;
|
||||
@ -887,24 +914,21 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
|
||||
ice("passed non-function types to unifyFunction");
|
||||
|
||||
size_t numGenerics = lf->generics.size();
|
||||
if (FFlag::LuauGenericFunctions && numGenerics != rf->generics.size())
|
||||
if (numGenerics != rf->generics.size())
|
||||
{
|
||||
numGenerics = std::min(lf->generics.size(), rf->generics.size());
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
}
|
||||
|
||||
size_t numGenericPacks = lf->genericPacks.size();
|
||||
if (FFlag::LuauGenericFunctions && numGenericPacks != rf->genericPacks.size())
|
||||
if (numGenericPacks != rf->genericPacks.size())
|
||||
{
|
||||
numGenericPacks = std::min(lf->genericPacks.size(), rf->genericPacks.size());
|
||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||
}
|
||||
|
||||
if (FFlag::LuauGenericFunctions)
|
||||
{
|
||||
for (size_t i = 0; i < numGenerics; i++)
|
||||
log.pushSeen(lf->generics[i], rf->generics[i]);
|
||||
}
|
||||
for (size_t i = 0; i < numGenerics; i++)
|
||||
log.pushSeen(lf->generics[i], rf->generics[i]);
|
||||
|
||||
CountMismatch::Context context = ctx;
|
||||
|
||||
@ -931,22 +955,19 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
|
||||
tryUnify_(lf->retType, rf->retType);
|
||||
}
|
||||
|
||||
if (lf->definition && !rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !subTy->persistent))
|
||||
if (lf->definition && !rf->definition && !subTy->persistent)
|
||||
{
|
||||
rf->definition = lf->definition;
|
||||
}
|
||||
else if (!lf->definition && rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !superTy->persistent))
|
||||
else if (!lf->definition && rf->definition && !superTy->persistent)
|
||||
{
|
||||
lf->definition = rf->definition;
|
||||
}
|
||||
|
||||
ctx = context;
|
||||
|
||||
if (FFlag::LuauGenericFunctions)
|
||||
{
|
||||
for (int i = int(numGenerics) - 1; 0 <= i; i--)
|
||||
log.popSeen(lf->generics[i], rf->generics[i]);
|
||||
}
|
||||
for (int i = int(numGenerics) - 1; 0 <= i; i--)
|
||||
log.popSeen(lf->generics[i], rf->generics[i]);
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -1032,7 +1053,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.tryUnify_(prop.type, r->second.type);
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||
else
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (innerState.errors.empty())
|
||||
log.concat(std::move(innerState.log));
|
||||
else
|
||||
@ -1047,7 +1073,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.tryUnify_(prop.type, rt->indexer->indexResultType);
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||
else
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (innerState.errors.empty())
|
||||
log.concat(std::move(innerState.log));
|
||||
else
|
||||
@ -1083,7 +1114,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.tryUnify_(prop.type, lt->indexer->indexResultType);
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||
else
|
||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
||||
|
||||
if (innerState.errors.empty())
|
||||
log.concat(std::move(innerState.log));
|
||||
else
|
||||
@ -1384,21 +1420,8 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
|
||||
const auto& r = rt->props.find(it.first);
|
||||
if (r == rt->props.end())
|
||||
{
|
||||
if (FFlag::LuauSealedTableUnifyOptionalFix)
|
||||
{
|
||||
if (isOptional(it.second.type))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<UnionTypeVar>(it.second.type))
|
||||
{
|
||||
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
|
||||
const std::vector<TypeId>& options = possiblyOptional->options;
|
||||
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isOptional(it.second.type))
|
||||
continue;
|
||||
|
||||
missingPropertiesInSuper.push_back(it.first);
|
||||
|
||||
@ -1482,21 +1505,8 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
|
||||
const auto& r = lt->props.find(it.first);
|
||||
if (r == lt->props.end())
|
||||
{
|
||||
if (FFlag::LuauSealedTableUnifyOptionalFix)
|
||||
{
|
||||
if (isOptional(it.second.type))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<UnionTypeVar>(it.second.type))
|
||||
{
|
||||
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
|
||||
const std::vector<TypeId>& options = possiblyOptional->options;
|
||||
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isOptional(it.second.type))
|
||||
continue;
|
||||
|
||||
extraPropertiesInSub.push_back(it.first);
|
||||
}
|
||||
@ -1526,7 +1536,18 @@ void Unifier::tryUnifyWithMetatable(TypeId metatable, TypeId other, bool reverse
|
||||
innerState.tryUnify_(lhs->table, rhs->table);
|
||||
innerState.tryUnify_(lhs->metatable, rhs->metatable);
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
|
||||
if (FFlag::LuauExtendedTypeMismatchError)
|
||||
{
|
||||
if (auto e = hasUnificationTooComplex(innerState.errors))
|
||||
errors.push_back(*e);
|
||||
else if (!innerState.errors.empty())
|
||||
errors.push_back(
|
||||
TypeError{location, TypeMismatch{reversed ? other : metatable, reversed ? metatable : other, "", innerState.errors.front()}});
|
||||
}
|
||||
else
|
||||
{
|
||||
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
|
||||
}
|
||||
|
||||
log.concat(std::move(innerState.log));
|
||||
}
|
||||
@ -1613,10 +1634,34 @@ void Unifier::tryUnifyWithClass(TypeId superTy, TypeId subTy, bool reversed)
|
||||
{
|
||||
ok = false;
|
||||
errors.push_back(TypeError{location, UnknownProperty{superTy, propName}});
|
||||
tryUnify_(prop.type, singletonTypes.errorType);
|
||||
|
||||
if (!FFlag::LuauExtendedClassMismatchError)
|
||||
tryUnify_(prop.type, singletonTypes.errorType);
|
||||
}
|
||||
else
|
||||
tryUnify_(prop.type, classProp->type);
|
||||
{
|
||||
if (FFlag::LuauExtendedClassMismatchError)
|
||||
{
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.tryUnify_(prop.type, classProp->type);
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState.errors, propName, reversed ? subTy : superTy, reversed ? superTy : subTy);
|
||||
|
||||
if (innerState.errors.empty())
|
||||
{
|
||||
log.concat(std::move(innerState.log));
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
innerState.log.rollback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tryUnify_(prop.type, classProp->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table->indexer)
|
||||
@ -1649,45 +1694,24 @@ static void queueTypePack_DEPRECATED(
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
a = follow(a);
|
||||
a = follow(a);
|
||||
|
||||
if (seenTypePacks.count(a))
|
||||
break;
|
||||
seenTypePacks.insert(a);
|
||||
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
else if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
else
|
||||
else if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
|
||||
if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1698,45 +1722,24 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
a = follow(a);
|
||||
a = follow(a);
|
||||
|
||||
if (seenTypePacks.find(a))
|
||||
break;
|
||||
seenTypePacks.insert(a);
|
||||
|
||||
if (FFlag::LuauAddMissingFollow)
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
else if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
else
|
||||
else if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
|
||||
if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1990,33 +1993,6 @@ std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N
|
||||
return Luau::findTablePropertyRespectingMeta(errors, globalScope, lhsType, name, location);
|
||||
}
|
||||
|
||||
std::optional<TypeId> Unifier::findMetatableEntry(TypeId type, std::string entry)
|
||||
{
|
||||
type = follow(type);
|
||||
|
||||
std::optional<TypeId> metatable = getMetatable(type);
|
||||
if (!metatable)
|
||||
return std::nullopt;
|
||||
|
||||
TypeId unwrapped = follow(*metatable);
|
||||
|
||||
if (get<AnyTypeVar>(unwrapped))
|
||||
return singletonTypes.anyType;
|
||||
|
||||
const TableTypeVar* mtt = getTableType(unwrapped);
|
||||
if (!mtt)
|
||||
{
|
||||
errors.push_back(TypeError{location, GenericError{"Metatable was not a table."}});
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto it = mtt->props.find(entry);
|
||||
if (it != mtt->props.end())
|
||||
return it->second.type;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Unifier::occursCheck(TypeId needle, TypeId haystack)
|
||||
{
|
||||
std::unordered_set<TypeId> seen_DEPRECATED;
|
||||
@ -2168,7 +2144,7 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
|
||||
{
|
||||
for (const auto& ty : a->head)
|
||||
{
|
||||
if (auto f = get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty))
|
||||
if (auto f = get<FunctionTypeVar>(follow(ty)))
|
||||
{
|
||||
occursCheck(seen_DEPRECATED, seen, needle, f->argTypes);
|
||||
occursCheck(seen_DEPRECATED, seen, needle, f->retType);
|
||||
@ -2207,6 +2183,17 @@ void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId
|
||||
errors.push_back(TypeError{location, TypeMismatch{wantedType, givenType}});
|
||||
}
|
||||
|
||||
void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauExtendedTypeMismatchError || FFlag::LuauExtendedClassMismatchError);
|
||||
|
||||
if (auto e = hasUnificationTooComplex(innerErrors))
|
||||
errors.push_back(*e);
|
||||
else if (!innerErrors.empty())
|
||||
errors.push_back(
|
||||
TypeError{location, TypeMismatch{wantedType, givenType, format("Property '%s' is not compatible", prop.c_str()), innerErrors.front()}});
|
||||
}
|
||||
|
||||
void Unifier::ice(const std::string& message, const Location& location)
|
||||
{
|
||||
sharedState.iceHandler->ice(message, location);
|
||||
|
@ -282,7 +282,6 @@ private:
|
||||
|
||||
// `<' namelist `>'
|
||||
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeList();
|
||||
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeListIfFFlagParseGenericFunctions();
|
||||
|
||||
// `<' typeAnnotation[, ...] `>'
|
||||
AstArray<AstTypeOrPack> parseTypeParams();
|
||||
|
@ -10,13 +10,13 @@
|
||||
// See docs/SyntaxChanges.md for an explanation.
|
||||
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
||||
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsParserFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctions, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCaptureBrokenCommentSpans, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionBaseSupport, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfStatementRecursionGuard, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeAliasPacks, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseTypePackTypeParameters, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixAmbiguousErrorRecoveryInAssign, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctionTypeBegin, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -957,7 +957,7 @@ AstStat* Parser::parseAssignment(AstExpr* initial)
|
||||
{
|
||||
nextLexeme();
|
||||
|
||||
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ false);
|
||||
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ FFlag::LuauFixAmbiguousErrorRecoveryInAssign);
|
||||
|
||||
if (!isExprLValue(expr))
|
||||
expr = reportExprError(expr->location, copy({expr}), "Assigned expression must be a variable or a field");
|
||||
@ -995,7 +995,7 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
||||
{
|
||||
Location start = matchFunction.location;
|
||||
|
||||
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
|
||||
auto [generics, genericPacks] = parseGenericTypeList();
|
||||
|
||||
Lexeme matchParen = lexer.current();
|
||||
expectAndConsume('(', "function");
|
||||
@ -1343,19 +1343,18 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
||||
{
|
||||
incrementRecursionCounter("type annotation");
|
||||
|
||||
bool monomorphic = !(FFlag::LuauParseGenericFunctions && lexer.current().type == '<');
|
||||
|
||||
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
|
||||
bool monomorphic = lexer.current().type != '<';
|
||||
|
||||
Lexeme begin = lexer.current();
|
||||
|
||||
if (FFlag::LuauGenericFunctionsParserFix)
|
||||
expectAndConsume('(', "function parameters");
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(begin.type == '(');
|
||||
nextLexeme(); // (
|
||||
}
|
||||
auto [generics, genericPacks] = parseGenericTypeList();
|
||||
|
||||
Lexeme parameterStart = lexer.current();
|
||||
|
||||
if (!FFlag::LuauParseGenericFunctionTypeBegin)
|
||||
begin = parameterStart;
|
||||
|
||||
expectAndConsume('(', "function parameters");
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
||||
|
||||
@ -1366,7 +1365,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
||||
if (lexer.current().type != ')')
|
||||
varargAnnotation = parseTypeList(params, names);
|
||||
|
||||
expectMatchAndConsume(')', begin, true);
|
||||
expectMatchAndConsume(')', parameterStart, true);
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
||||
|
||||
@ -1585,7 +1584,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||
{
|
||||
return {parseTableTypeAnnotation(), {}};
|
||||
}
|
||||
else if (lexer.current().type == '(' || (FFlag::LuauParseGenericFunctions && lexer.current().type == '<'))
|
||||
else if (lexer.current().type == '(' || lexer.current().type == '<')
|
||||
{
|
||||
return parseFunctionTypeAnnotation(allowPack);
|
||||
}
|
||||
@ -2315,19 +2314,6 @@ Parser::Name Parser::parseIndexName(const char* context, const Position& previou
|
||||
return Name(nameError, location);
|
||||
}
|
||||
|
||||
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeListIfFFlagParseGenericFunctions()
|
||||
{
|
||||
if (FFlag::LuauParseGenericFunctions)
|
||||
return Parser::parseGenericTypeList();
|
||||
AstArray<AstName> generics;
|
||||
AstArray<AstName> genericPacks;
|
||||
generics.size = 0;
|
||||
generics.data = nullptr;
|
||||
genericPacks.size = 0;
|
||||
genericPacks.data = nullptr;
|
||||
return std::pair(generics, genericPacks);
|
||||
}
|
||||
|
||||
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
|
||||
{
|
||||
TempVector<AstName> names{scratchName};
|
||||
@ -2342,7 +2328,7 @@ std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
|
||||
while (true)
|
||||
{
|
||||
AstName name = parseName().name;
|
||||
if (FFlag::LuauParseGenericFunctions && lexer.current().type == Lexeme::Dot3)
|
||||
if (lexer.current().type == Lexeme::Dot3)
|
||||
{
|
||||
seenPack = true;
|
||||
nextLexeme();
|
||||
|
18
CLI/Repl.cpp
18
CLI/Repl.cpp
@ -215,9 +215,6 @@ extern "C"
|
||||
{
|
||||
const char* executeScript(const char* source)
|
||||
{
|
||||
// static string for caching result (prevents dangling ptr on function exit)
|
||||
static std::string result;
|
||||
|
||||
// setup flags
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||
@ -233,15 +230,13 @@ extern "C"
|
||||
// sandbox thread
|
||||
luaL_sandboxthread(L);
|
||||
|
||||
// static string for caching result (prevents dangling ptr on function exit)
|
||||
static std::string result;
|
||||
|
||||
// run code + collect error
|
||||
std::string error = runCode(L, source);
|
||||
result = error;
|
||||
|
||||
if (error.length())
|
||||
{
|
||||
return result.c_str();
|
||||
}
|
||||
return NULL;
|
||||
result = runCode(L, source);
|
||||
|
||||
return result.empty() ? NULL : result.c_str();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -591,3 +586,4 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -467,6 +467,10 @@ enum LuauBuiltinFunction
|
||||
|
||||
// vector ctor
|
||||
LBF_VECTOR,
|
||||
|
||||
// bit32.count
|
||||
LBF_BIT32_COUNTLZ,
|
||||
LBF_BIT32_COUNTRZ,
|
||||
};
|
||||
|
||||
// Capture type, used in LOP_CAPTURE
|
||||
|
@ -37,8 +37,7 @@ struct CompileOptions
|
||||
const char* vectorLib = nullptr;
|
||||
const char* vectorCtor = nullptr;
|
||||
|
||||
// array of globals that are mutable; disables the import optimization for fields accessed through them
|
||||
// use NULL to end the array
|
||||
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these
|
||||
const char** mutableGlobals = nullptr;
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@ LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresFenv, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresUpval, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGenericSpecialGlobals, false)
|
||||
LUAU_FASTFLAG(LuauIfElseExpressionBaseSupport)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBit32CountBuiltin, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -23,6 +24,7 @@ static const uint32_t kMaxRegisterCount = 255;
|
||||
static const uint32_t kMaxUpvalueCount = 200;
|
||||
static const uint32_t kMaxLocalCount = 200;
|
||||
|
||||
// TODO: Remove with LuauGenericSpecialGlobals
|
||||
static const char* kSpecialGlobals[] = {"Game", "Workspace", "_G", "game", "plugin", "script", "shared", "workspace"};
|
||||
|
||||
CompileError::CompileError(const Location& location, const std::string& message)
|
||||
@ -3637,6 +3639,10 @@ struct Compiler
|
||||
return LBF_BIT32_RROTATE;
|
||||
if (builtin.method == "rshift")
|
||||
return LBF_BIT32_RSHIFT;
|
||||
if (builtin.method == "countlz" && FFlag::LuauBit32CountBuiltin)
|
||||
return LBF_BIT32_COUNTLZ;
|
||||
if (builtin.method == "countrz" && FFlag::LuauBit32CountBuiltin)
|
||||
return LBF_BIT32_COUNTRZ;
|
||||
}
|
||||
|
||||
if (builtin.object == "string")
|
||||
@ -3711,11 +3717,9 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
|
||||
compiler.globals[name].writable = true;
|
||||
|
||||
if (options.mutableGlobals)
|
||||
for (const char** ptr = options.mutableGlobals; *ptr != NULL; ++ptr)
|
||||
{
|
||||
for (const char** ptr = options.mutableGlobals; *ptr; ++ptr)
|
||||
if (AstName name = names.get(*ptr); name.value)
|
||||
compiler.globals[name].writable = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3738,7 +3742,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
|
||||
}
|
||||
|
||||
// this visitor tracks calls to getfenv/setfenv and disables some optimizations when they are found
|
||||
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1)
|
||||
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1 && (names.get("getfenv").value || names.get("setfenv").value))
|
||||
{
|
||||
Compiler::FenvVisitor fenvVisitor(compiler.getfenvUsed, compiler.setfenvUsed);
|
||||
root->visit(&fenvVisitor);
|
||||
|
5
Makefile
5
Makefile
@ -137,12 +137,11 @@ $(TESTS_TARGET) $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET):
|
||||
|
||||
# executable targets for fuzzing
|
||||
fuzz-%: $(BUILD)/fuzz/%.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET)
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
|
||||
fuzz-proto: $(BUILD)/fuzz/proto.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
||||
fuzz-prototest: $(BUILD)/fuzz/prototest.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
||||
|
||||
fuzz-%:
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
|
||||
# static library targets
|
||||
$(AST_TARGET): $(AST_OBJECTS)
|
||||
$(COMPILER_TARGET): $(COMPILER_OBJECTS)
|
||||
|
@ -213,6 +213,8 @@ LUA_API int lua_resume(lua_State* L, lua_State* from, int narg);
|
||||
LUA_API int lua_resumeerror(lua_State* L, lua_State* from);
|
||||
LUA_API int lua_status(lua_State* L);
|
||||
LUA_API int lua_isyieldable(lua_State* L);
|
||||
LUA_API void* lua_getthreaddata(lua_State* L);
|
||||
LUA_API void lua_setthreaddata(lua_State* L, void* data);
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
|
@ -988,6 +988,16 @@ int lua_status(lua_State* L)
|
||||
return L->status;
|
||||
}
|
||||
|
||||
void* lua_getthreaddata(lua_State* L)
|
||||
{
|
||||
return L->userdata;
|
||||
}
|
||||
|
||||
void lua_setthreaddata(lua_State* L, void* data)
|
||||
{
|
||||
L->userdata = data;
|
||||
}
|
||||
|
||||
/*
|
||||
** Garbage-collection function
|
||||
*/
|
||||
|
@ -2,8 +2,11 @@
|
||||
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lcommon.h"
|
||||
#include "lnumutils.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBit32Count, false)
|
||||
|
||||
#define ALLONES ~0u
|
||||
#define NBITS int(8 * sizeof(unsigned))
|
||||
|
||||
@ -177,6 +180,44 @@ static int b_replace(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int b_countlz(lua_State* L)
|
||||
{
|
||||
if (!FFlag::LuauBit32Count)
|
||||
luaL_error(L, "bit32.countlz isn't enabled");
|
||||
|
||||
b_uint v = luaL_checkunsigned(L, 1);
|
||||
|
||||
b_uint r = NBITS;
|
||||
for (int i = 0; i < NBITS; ++i)
|
||||
if (v & (1u << (NBITS - 1 - i)))
|
||||
{
|
||||
r = i;
|
||||
break;
|
||||
}
|
||||
|
||||
lua_pushunsigned(L, r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int b_countrz(lua_State* L)
|
||||
{
|
||||
if (!FFlag::LuauBit32Count)
|
||||
luaL_error(L, "bit32.countrz isn't enabled");
|
||||
|
||||
b_uint v = luaL_checkunsigned(L, 1);
|
||||
|
||||
b_uint r = NBITS;
|
||||
for (int i = 0; i < NBITS; ++i)
|
||||
if (v & (1u << i))
|
||||
{
|
||||
r = i;
|
||||
break;
|
||||
}
|
||||
|
||||
lua_pushunsigned(L, r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg bitlib[] = {
|
||||
{"arshift", b_arshift},
|
||||
{"band", b_and},
|
||||
@ -190,6 +231,8 @@ static const luaL_Reg bitlib[] = {
|
||||
{"replace", b_replace},
|
||||
{"rrotate", b_rrot},
|
||||
{"rshift", b_rshift},
|
||||
{"countlz", b_countlz},
|
||||
{"countrz", b_countrz},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -1031,6 +1031,52 @@ static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int luauF_countlz(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||
{
|
||||
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
|
||||
{
|
||||
double a1 = nvalue(arg0);
|
||||
|
||||
unsigned n;
|
||||
luai_num2unsigned(n, a1);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
unsigned long rl;
|
||||
int r = _BitScanReverse(&rl, n) ? 31 - int(rl) : 32;
|
||||
#else
|
||||
int r = n == 0 ? 32 : __builtin_clz(n);
|
||||
#endif
|
||||
|
||||
setnvalue(res, double(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int luauF_countrz(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||
{
|
||||
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
|
||||
{
|
||||
double a1 = nvalue(arg0);
|
||||
|
||||
unsigned n;
|
||||
luai_num2unsigned(n, a1);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
unsigned long rl;
|
||||
int r = _BitScanForward(&rl, n) ? int(rl) : 32;
|
||||
#else
|
||||
int r = n == 0 ? 32 : __builtin_ctz(n);
|
||||
#endif
|
||||
|
||||
setnvalue(res, double(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
luau_FastFunction luauF_table[256] = {
|
||||
NULL,
|
||||
luauF_assert,
|
||||
@ -1097,4 +1143,7 @@ luau_FastFunction luauF_table[256] = {
|
||||
luauF_tunpack,
|
||||
|
||||
luauF_vector,
|
||||
|
||||
luauF_countlz,
|
||||
luauF_countrz,
|
||||
};
|
||||
|
164
VM/src/lgc.cpp
164
VM/src/lgc.cpp
@ -13,7 +13,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauRescanGrayAgainForwardBarrier, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauConsolidatedStep, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSeparateAtomic, false)
|
||||
|
||||
LUAU_FASTFLAG(LuauArrayBoundary)
|
||||
@ -677,117 +676,6 @@ static size_t atomic(lua_State* L)
|
||||
return work;
|
||||
}
|
||||
|
||||
static size_t singlestep(lua_State* L)
|
||||
{
|
||||
size_t cost = 0;
|
||||
global_State* g = L->global;
|
||||
switch (g->gcstate)
|
||||
{
|
||||
case GCSpause:
|
||||
{
|
||||
markroot(L); /* start a new collection */
|
||||
LUAU_ASSERT(g->gcstate == GCSpropagate);
|
||||
break;
|
||||
}
|
||||
case GCSpropagate:
|
||||
{
|
||||
if (g->gray)
|
||||
{
|
||||
g->gcstats.currcycle.markitems++;
|
||||
|
||||
cost = propagatemark(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
// perform one iteration over 'gray again' list
|
||||
g->gray = g->grayagain;
|
||||
g->grayagain = NULL;
|
||||
|
||||
g->gcstate = GCSpropagateagain;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GCSpropagateagain:
|
||||
{
|
||||
if (g->gray)
|
||||
{
|
||||
g->gcstats.currcycle.markitems++;
|
||||
|
||||
cost = propagatemark(g);
|
||||
}
|
||||
else /* no more `gray' objects */
|
||||
{
|
||||
if (FFlag::LuauSeparateAtomic)
|
||||
{
|
||||
g->gcstate = GCSatomic;
|
||||
}
|
||||
else
|
||||
{
|
||||
double starttimestamp = lua_clock();
|
||||
|
||||
g->gcstats.currcycle.atomicstarttimestamp = starttimestamp;
|
||||
g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes;
|
||||
|
||||
atomic(L); /* finish mark phase */
|
||||
LUAU_ASSERT(g->gcstate == GCSsweepstring);
|
||||
|
||||
g->gcstats.currcycle.atomictime += lua_clock() - starttimestamp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GCSatomic:
|
||||
{
|
||||
g->gcstats.currcycle.atomicstarttimestamp = lua_clock();
|
||||
g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes;
|
||||
|
||||
cost = atomic(L); /* finish mark phase */
|
||||
LUAU_ASSERT(g->gcstate == GCSsweepstring);
|
||||
break;
|
||||
}
|
||||
case GCSsweepstring:
|
||||
{
|
||||
size_t traversedcount = 0;
|
||||
sweepwholelist(L, &g->strt.hash[g->sweepstrgc++], &traversedcount);
|
||||
|
||||
// nothing more to sweep?
|
||||
if (g->sweepstrgc >= g->strt.size)
|
||||
{
|
||||
// sweep string buffer list and preserve used string count
|
||||
uint32_t nuse = L->global->strt.nuse;
|
||||
sweepwholelist(L, &g->strbufgc, &traversedcount);
|
||||
L->global->strt.nuse = nuse;
|
||||
|
||||
g->gcstate = GCSsweep; // end sweep-string phase
|
||||
}
|
||||
|
||||
g->gcstats.currcycle.sweepitems += traversedcount;
|
||||
|
||||
cost = GC_SWEEPCOST;
|
||||
break;
|
||||
}
|
||||
case GCSsweep:
|
||||
{
|
||||
size_t traversedcount = 0;
|
||||
g->sweepgc = sweeplist(L, g->sweepgc, GC_SWEEPMAX, &traversedcount);
|
||||
|
||||
g->gcstats.currcycle.sweepitems += traversedcount;
|
||||
|
||||
if (*g->sweepgc == NULL)
|
||||
{ /* nothing more to sweep? */
|
||||
shrinkbuffers(L);
|
||||
g->gcstate = GCSpause; /* end collection */
|
||||
}
|
||||
cost = GC_SWEEPMAX * GC_SWEEPCOST;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LUAU_ASSERT(!"Unexpected GC state");
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
static size_t gcstep(lua_State* L, size_t limit)
|
||||
{
|
||||
size_t cost = 0;
|
||||
@ -980,37 +868,12 @@ void luaC_step(lua_State* L, bool assist)
|
||||
int lastgcstate = g->gcstate;
|
||||
double lasttimestamp = lua_clock();
|
||||
|
||||
if (FFlag::LuauConsolidatedStep)
|
||||
{
|
||||
size_t work = gcstep(L, lim);
|
||||
size_t work = gcstep(L, lim);
|
||||
|
||||
if (assist)
|
||||
g->gcstats.currcycle.assistwork += work;
|
||||
else
|
||||
g->gcstats.currcycle.explicitwork += work;
|
||||
}
|
||||
if (assist)
|
||||
g->gcstats.currcycle.assistwork += work;
|
||||
else
|
||||
{
|
||||
// always perform at least one single step
|
||||
do
|
||||
{
|
||||
lim -= singlestep(L);
|
||||
|
||||
// if we have switched to a different state, capture the duration of last stage
|
||||
// this way we reduce the number of timer calls we make
|
||||
if (lastgcstate != g->gcstate)
|
||||
{
|
||||
GC_INTERRUPT(lastgcstate);
|
||||
|
||||
double now = lua_clock();
|
||||
|
||||
recordGcStateTime(g, lastgcstate, now - lasttimestamp, assist);
|
||||
|
||||
lasttimestamp = now;
|
||||
lastgcstate = g->gcstate;
|
||||
}
|
||||
} while (lim > 0 && g->gcstate != GCSpause);
|
||||
}
|
||||
g->gcstats.currcycle.explicitwork += work;
|
||||
|
||||
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
|
||||
|
||||
@ -1037,14 +900,7 @@ void luaC_step(lua_State* L, bool assist)
|
||||
g->GCthreshold -= debt;
|
||||
}
|
||||
|
||||
if (FFlag::LuauConsolidatedStep)
|
||||
{
|
||||
GC_INTERRUPT(lastgcstate);
|
||||
}
|
||||
else
|
||||
{
|
||||
GC_INTERRUPT(g->gcstate);
|
||||
}
|
||||
GC_INTERRUPT(lastgcstate);
|
||||
}
|
||||
|
||||
void luaC_fullgc(lua_State* L)
|
||||
@ -1070,10 +926,7 @@ void luaC_fullgc(lua_State* L)
|
||||
while (g->gcstate != GCSpause)
|
||||
{
|
||||
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
|
||||
if (FFlag::LuauConsolidatedStep)
|
||||
gcstep(L, SIZE_MAX);
|
||||
else
|
||||
singlestep(L);
|
||||
gcstep(L, SIZE_MAX);
|
||||
}
|
||||
|
||||
finishGcCycleStats(g);
|
||||
@ -1084,10 +937,7 @@ void luaC_fullgc(lua_State* L)
|
||||
markroot(L);
|
||||
while (g->gcstate != GCSpause)
|
||||
{
|
||||
if (FFlag::LuauConsolidatedStep)
|
||||
gcstep(L, SIZE_MAX);
|
||||
else
|
||||
singlestep(L);
|
||||
gcstep(L, SIZE_MAX);
|
||||
}
|
||||
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */
|
||||
shrinkbuffersfull(L);
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauStrPackUBCastFix, false)
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
@ -1404,10 +1406,20 @@ static int str_pack(lua_State* L)
|
||||
}
|
||||
case Kuint:
|
||||
{ /* unsigned integers */
|
||||
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
|
||||
if (size < SZINT) /* need overflow check? */
|
||||
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
||||
packint(&b, n, h.islittle, size, 0);
|
||||
if (FFlag::LuauStrPackUBCastFix)
|
||||
{
|
||||
long long n = (long long)luaL_checknumber(L, arg);
|
||||
if (size < SZINT) /* need overflow check? */
|
||||
luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
||||
packint(&b, (unsigned long long)n, h.islittle, size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
|
||||
if (size < SZINT) /* need overflow check? */
|
||||
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
||||
packint(&b, n, h.islittle, size, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Kfloat:
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include "ldebug.h"
|
||||
#include "lvm.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableFreeze, false)
|
||||
|
||||
static int foreachi(lua_State* L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
@ -491,9 +489,6 @@ static int tclear(lua_State* L)
|
||||
|
||||
static int tfreeze(lua_State* L)
|
||||
{
|
||||
if (!FFlag::LuauTableFreeze)
|
||||
luaG_runerror(L, "table.freeze is disabled");
|
||||
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_argcheck(L, !lua_getreadonly(L, 1), 1, "table is already frozen");
|
||||
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");
|
||||
@ -506,9 +501,6 @@ static int tfreeze(lua_State* L)
|
||||
|
||||
static int tisfrozen(lua_State* L)
|
||||
{
|
||||
if (!FFlag::LuauTableFreeze)
|
||||
luaG_runerror(L, "table.isfrozen is disabled");
|
||||
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
|
||||
lua_pushboolean(L, lua_getreadonly(L, 1));
|
||||
|
@ -205,38 +205,48 @@ function Bitboard:empty()
|
||||
return self.h == 0 and self.l == 0
|
||||
end
|
||||
|
||||
function Bitboard:ctz()
|
||||
local target = self.l
|
||||
local offset = 0
|
||||
local result = 0
|
||||
|
||||
if target == 0 then
|
||||
target = self.h
|
||||
result = 32
|
||||
if not bit32.countrz then
|
||||
local function ctz(v)
|
||||
if v == 0 then return 32 end
|
||||
local offset = 0
|
||||
while bit32.extract(v, offset) == 0 do
|
||||
offset = offset + 1
|
||||
end
|
||||
return offset
|
||||
end
|
||||
|
||||
if target == 0 then
|
||||
return 64
|
||||
end
|
||||
|
||||
while bit32.extract(target, offset) == 0 do
|
||||
offset = offset + 1
|
||||
end
|
||||
|
||||
return result + offset
|
||||
end
|
||||
|
||||
function Bitboard:ctzafter(start)
|
||||
start = start + 1
|
||||
if start < 32 then
|
||||
for i=start,31 do
|
||||
if bit32.extract(self.l, i) == 1 then return i end
|
||||
function Bitboard:ctz()
|
||||
local result = ctz(self.l)
|
||||
if result == 32 then
|
||||
return ctz(self.h) + 32
|
||||
else
|
||||
return result
|
||||
end
|
||||
end
|
||||
for i=math.max(32,start),63 do
|
||||
if bit32.extract(self.h, i-32) == 1 then return i end
|
||||
function Bitboard:ctzafter(start)
|
||||
start = start + 1
|
||||
if start < 32 then
|
||||
for i=start,31 do
|
||||
if bit32.extract(self.l, i) == 1 then return i end
|
||||
end
|
||||
end
|
||||
for i=math.max(32,start),63 do
|
||||
if bit32.extract(self.h, i-32) == 1 then return i end
|
||||
end
|
||||
return 64
|
||||
end
|
||||
else
|
||||
function Bitboard:ctz()
|
||||
local result = bit32.countrz(self.l)
|
||||
if result == 32 then
|
||||
return bit32.countrz(self.h) + 32
|
||||
else
|
||||
return result
|
||||
end
|
||||
end
|
||||
function Bitboard:ctzafter(start)
|
||||
local masked = self:band(Bitboard.full:lshift(start+1))
|
||||
return masked:ctz()
|
||||
end
|
||||
return 64
|
||||
end
|
||||
|
||||
|
||||
@ -245,7 +255,7 @@ function Bitboard:lshift(amt)
|
||||
if amt == 0 then return self end
|
||||
|
||||
if amt > 31 then
|
||||
return Bitboard.from(0, bit32.lshift(self.l, amt-31))
|
||||
return Bitboard.from(0, bit32.lshift(self.l, amt-32))
|
||||
end
|
||||
|
||||
local l = bit32.lshift(self.l, amt)
|
||||
@ -832,12 +842,12 @@ end
|
||||
local testCases = {}
|
||||
local function addTest(...) table.insert(testCases, {...}) end
|
||||
|
||||
addTest(StartingFen, 3, 8902)
|
||||
addTest("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0", 2, 2039)
|
||||
addTest("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0", 3, 2812)
|
||||
addTest("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 3, 9467)
|
||||
addTest("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 2, 1486)
|
||||
addTest("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 2, 2079)
|
||||
addTest(StartingFen, 2, 400)
|
||||
addTest("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0", 1, 48)
|
||||
addTest("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0", 2, 191)
|
||||
addTest("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 2, 264)
|
||||
addTest("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 1, 44)
|
||||
addTest("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 1, 46)
|
||||
|
||||
|
||||
local function chess()
|
||||
|
@ -19,6 +19,7 @@ message Expr {
|
||||
ExprTable table = 13;
|
||||
ExprUnary unary = 14;
|
||||
ExprBinary binary = 15;
|
||||
ExprIfElse ifelse = 16;
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,6 +150,12 @@ message ExprBinary {
|
||||
required Expr right = 3;
|
||||
}
|
||||
|
||||
message ExprIfElse {
|
||||
required Expr cond = 1;
|
||||
required Expr then = 2;
|
||||
required Expr else = 3;
|
||||
}
|
||||
|
||||
message LValue {
|
||||
oneof lvalue_oneof {
|
||||
ExprLocal local = 1;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "Luau/BytecodeBuilder.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Transpiler.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
@ -23,6 +24,7 @@ const bool kFuzzLinter = true;
|
||||
const bool kFuzzTypeck = true;
|
||||
const bool kFuzzVM = true;
|
||||
const bool kFuzzTypes = true;
|
||||
const bool kFuzzTranspile = true;
|
||||
|
||||
static_assert(!(kFuzzVM && !kFuzzCompiler), "VM requires the compiler!");
|
||||
|
||||
@ -242,6 +244,11 @@ DEFINE_PROTO_FUZZER(const luau::StatBlock& message)
|
||||
}
|
||||
}
|
||||
|
||||
if (kFuzzTranspile && parseResult.root)
|
||||
{
|
||||
transpileWithTypes(*parseResult.root);
|
||||
}
|
||||
|
||||
// run resulting bytecode
|
||||
if (kFuzzVM && bytecode.size())
|
||||
{
|
||||
|
@ -476,6 +476,16 @@ struct ProtoToLuau
|
||||
print(expr.right());
|
||||
}
|
||||
|
||||
void print(const luau::ExprIfElse& expr)
|
||||
{
|
||||
source += " if ";
|
||||
print(expr.cond());
|
||||
source += " then ";
|
||||
print(expr.then());
|
||||
source += " else ";
|
||||
print(expr.else_());
|
||||
}
|
||||
|
||||
void print(const luau::LValue& expr)
|
||||
{
|
||||
if (expr.has_local())
|
||||
|
@ -45,7 +45,6 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "prop")
|
||||
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "event_callback_arg")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauDontMutatePersistentFunctions", true},
|
||||
{"LuauPersistDefinitionFileTypes", true},
|
||||
};
|
||||
|
||||
|
@ -1287,9 +1287,6 @@ local e: (n: n@5
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "generic_types")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
|
||||
|
||||
check(R"(
|
||||
function f<Tee, Use>(a: T@1
|
||||
local b: string = "don't trip"
|
||||
|
@ -240,8 +240,6 @@ TEST_CASE("Math")
|
||||
|
||||
TEST_CASE("Table")
|
||||
{
|
||||
ScopedFastFlag sff("LuauTableFreeze", true);
|
||||
|
||||
runConformance("nextvar.lua");
|
||||
}
|
||||
|
||||
@ -322,6 +320,8 @@ TEST_CASE("GC")
|
||||
|
||||
TEST_CASE("Bitwise")
|
||||
{
|
||||
ScopedFastFlag sff("LuauBit32Count", true);
|
||||
|
||||
runConformance("bitwise.lua");
|
||||
}
|
||||
|
||||
@ -359,6 +359,8 @@ TEST_CASE("PCall")
|
||||
|
||||
TEST_CASE("Pack")
|
||||
{
|
||||
ScopedFastFlag sff{ "LuauStrPackUBCastFix", true };
|
||||
|
||||
runConformance("tpack.lua");
|
||||
}
|
||||
|
||||
|
@ -479,10 +479,6 @@ return foo1
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "UnknownType")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLinterUnknownTypeVectorAware", true};
|
||||
|
||||
SourceModule sm;
|
||||
|
||||
unfreeze(typeChecker.globalTypes);
|
||||
TableTypeVar::Props instanceProps{
|
||||
{"ClassName", {typeChecker.anyType}},
|
||||
@ -1400,8 +1396,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "TableOperations")
|
||||
{
|
||||
ScopedFastFlag sff("LuauLinterTableMoveZero", true);
|
||||
|
||||
LintResult result = lintTyped(R"(
|
||||
local t = {}
|
||||
local tt = {}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauFixAmbiguousErrorRecoveryInAssign)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
namespace
|
||||
@ -625,10 +627,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
|
||||
)"),
|
||||
"Cannot have more than one table indexer");
|
||||
|
||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauGenericFunctionsParserFix", true};
|
||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
||||
|
||||
CHECK_EQ(getParseError(R"(
|
||||
type T = <a>foo
|
||||
)"),
|
||||
@ -1624,6 +1622,20 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_confusing_function_call")
|
||||
"statements");
|
||||
|
||||
CHECK(result3.errors.size() == 1);
|
||||
|
||||
auto result4 = matchParseError(R"(
|
||||
local t = {}
|
||||
function f() return t end
|
||||
t.x, (f)
|
||||
().y = 5, 6
|
||||
)",
|
||||
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
|
||||
"statements");
|
||||
|
||||
if (FFlag::LuauFixAmbiguousErrorRecoveryInAssign)
|
||||
CHECK(result4.errors.size() == 1);
|
||||
else
|
||||
CHECK(result4.errors.size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_error_varargs")
|
||||
@ -1824,9 +1836,6 @@ TEST_CASE_FIXTURE(Fixture, "variadic_definition_parsing")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
|
||||
{
|
||||
// Doesn't need LuauGenericFunctions
|
||||
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
|
||||
|
||||
ParseResult result = parseEx(R"(
|
||||
function f<a...>(...: a...)
|
||||
end
|
||||
@ -1861,9 +1870,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_function_declaration_parsing")
|
||||
{
|
||||
// Doesn't need LuauGenericFunctions
|
||||
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
|
||||
|
||||
ParseResult result = parseEx(R"(
|
||||
declare function f<a, b, c...>()
|
||||
)");
|
||||
@ -1953,12 +1959,7 @@ TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments")
|
||||
matchParseError("type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)",
|
||||
"Expected '->' when parsing function type, got <eof>");
|
||||
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag luauGenericFunctionsParserFix{"LuauGenericFunctionsParserFix", true};
|
||||
|
||||
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
||||
}
|
||||
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@ -2362,8 +2363,6 @@ type Fn = (
|
||||
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
|
||||
}
|
||||
|
||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(type Fn = (any, string | number | <a>()) -> any)");
|
||||
@ -2397,8 +2396,6 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -2521,7 +2518,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
AstStat* stat = parse(R"(
|
||||
@ -2534,4 +2530,9 @@ type C<X...> = Packed<(number, X...)>
|
||||
REQUIRE(stat != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_type_matching_parenthesis")
|
||||
{
|
||||
matchParseError("local a: <T>(number -> string", "Expected ')' (to close '(' at column 13), got '->'");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -33,8 +33,6 @@ TEST_SUITE_BEGIN("Predicate");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
RefinementMap m{
|
||||
{"b", typeChecker.stringType},
|
||||
{"c", typeChecker.numberType},
|
||||
@ -61,8 +59,6 @@ TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
RefinementMap m{
|
||||
{"a", typeChecker.stringType},
|
||||
{"b", typeChecker.stringType},
|
||||
@ -89,8 +85,6 @@ TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
RefinementMap m{
|
||||
{"a", typeChecker.stringType},
|
||||
{"b", typeChecker.numberType},
|
||||
|
@ -259,9 +259,6 @@ TEST_CASE_FIXTURE(Fixture, "function_type_with_argument_names")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_type_with_argument_names_generic")
|
||||
{
|
||||
ScopedFastFlag luauGenericFunctions{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check("local function f<a...>(n: number, ...: a...): (a...) return ... end");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
@ -340,10 +337,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "toStringDetailed2")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{"LuauGenericFunctions", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local base = {}
|
||||
function base:one() return 1 end
|
||||
@ -468,8 +461,6 @@ TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_inters
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
||||
{
|
||||
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
|
||||
|
||||
TypeVar tableTy{TableTypeVar{}};
|
||||
TableTypeVar* ttv = getMutable<TableTypeVar>(&tableTy);
|
||||
ttv->name = "Table";
|
||||
|
@ -21,7 +21,7 @@ local function isPortal(element)
|
||||
return false
|
||||
end
|
||||
|
||||
return element.component==Core.Portal
|
||||
return element.component == Core.Portal
|
||||
end
|
||||
)";
|
||||
|
||||
@ -223,12 +223,24 @@ TEST_CASE("escaped_strings")
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("escaped_strings_2")
|
||||
{
|
||||
const std::string code = R"( local s="\a\b\f\n\r\t\v\'\"\\" )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("need_a_space_between_number_literals_and_dots")
|
||||
{
|
||||
const std::string code = R"( return point and math.ceil(point* 100000* 100)/ 100000 .. '%'or '' )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("binary_keywords")
|
||||
{
|
||||
const std::string code = "local c = a0 ._ or b0 ._";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("do_blocks")
|
||||
{
|
||||
const std::string code = R"(
|
||||
@ -364,10 +376,10 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly")
|
||||
)";
|
||||
|
||||
std::string expected = R"(
|
||||
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:...string): (string,...number)
|
||||
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
|
||||
end
|
||||
|
||||
local b:(...string)->(...number)=function(...:...string): ...number
|
||||
local b:(...string)->(...number)=function(...:string): ...number
|
||||
end
|
||||
|
||||
local c:()->()=function(): ()
|
||||
@ -400,4 +412,238 @@ TEST_CASE_FIXTURE(Fixture, "function_type_location")
|
||||
CHECK_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion")
|
||||
{
|
||||
std::string code = "local a = 5 :: number";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else")
|
||||
{
|
||||
ScopedFastFlag luauIfElseExpressionBaseSupport("LuauIfElseExpressionBaseSupport", true);
|
||||
|
||||
std::string code = "local a = if 1 then 2 else 3";
|
||||
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_import")
|
||||
{
|
||||
fileResolver.source["game/A"] = R"(
|
||||
export type Type = { a: number }
|
||||
return {}
|
||||
)";
|
||||
|
||||
std::string code = R"(
|
||||
local Import = require(game.A)
|
||||
local a: Import.Type
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_packs")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
std::string code = R"(
|
||||
type Packed<T...> = (T...)->(T...)
|
||||
local a: Packed<>
|
||||
local b: Packed<(number, string)>
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested")
|
||||
{
|
||||
std::string code = "local a: ((number)->(string))|((string)->(string))";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2")
|
||||
{
|
||||
std::string code = "local a: (number&string)|(string&boolean)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
|
||||
{
|
||||
std::string code = "local a: nil | (string & number)";
|
||||
|
||||
CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested")
|
||||
{
|
||||
std::string code = "local a: ((number)->(string))&((string)->(string))";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2")
|
||||
{
|
||||
std::string code = "local a: (number|string)&(string|boolean)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_varargs")
|
||||
{
|
||||
std::string code = "local function f(...) return ... end";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_index_expr")
|
||||
{
|
||||
std::string code = "local a = {1, 2, 3} local b = a[2]";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_unary")
|
||||
{
|
||||
std::string code = R"(
|
||||
local a = 1
|
||||
local b = -1
|
||||
local c = true
|
||||
local d = not c
|
||||
local e = 'hello'
|
||||
local d = #e
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_break_continue")
|
||||
{
|
||||
std::string code = R"(
|
||||
local a, b, c
|
||||
repeat
|
||||
if a then break end
|
||||
if b then continue end
|
||||
until c
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_compound_assignmenr")
|
||||
{
|
||||
std::string code = R"(
|
||||
local a = 1
|
||||
a += 2
|
||||
a -= 3
|
||||
a *= 4
|
||||
a /= 5
|
||||
a %= 6
|
||||
a ^= 7
|
||||
a ..= ' - result'
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple")
|
||||
{
|
||||
std::string code = "a, b, c = 1, 2, 3";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_generic_function")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctionTypeBegin("LuauParseGenericFunctionTypeBegin", true);
|
||||
|
||||
std::string code = R"(
|
||||
local function foo<T,S...>(a: T, ...: S...) return 1 end
|
||||
local f: <T,S...>(T, S...)->(number) = foo
|
||||
)";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse")
|
||||
{
|
||||
std::string code = "local a: nil | number";
|
||||
|
||||
CHECK_EQ("local a: number?", transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple")
|
||||
{
|
||||
std::string code = "for k,v in next,{}do print(k,v) end";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_error_expr")
|
||||
{
|
||||
std::string code = "local a = f:-";
|
||||
|
||||
auto allocator = Allocator{};
|
||||
auto names = AstNameTable{allocator};
|
||||
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
|
||||
|
||||
CHECK_EQ("local a = (error-expr: f.%error-id%)-(error-expr)", transpileWithTypes(*parseResult.root));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_error_stat")
|
||||
{
|
||||
std::string code = "-";
|
||||
|
||||
auto allocator = Allocator{};
|
||||
auto names = AstNameTable{allocator};
|
||||
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
|
||||
|
||||
CHECK_EQ("(error-stat: (error-expr))", transpileWithTypes(*parseResult.root));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_error_type")
|
||||
{
|
||||
std::string code = "local a: ";
|
||||
|
||||
auto allocator = Allocator{};
|
||||
auto names = AstNameTable{allocator};
|
||||
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
|
||||
|
||||
CHECK_EQ("local a:%error-type%", transpileWithTypes(*parseResult.root));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_parse_error")
|
||||
{
|
||||
std::string code = "local a = -";
|
||||
|
||||
auto result = transpile(code);
|
||||
CHECK_EQ("", result.code);
|
||||
CHECK_EQ("Expected identifier when parsing expression, got <eof>", result.parseError);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_to_string")
|
||||
{
|
||||
std::string code = "local a: string = 'hello'";
|
||||
|
||||
auto allocator = Allocator{};
|
||||
auto names = AstNameTable{allocator};
|
||||
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
|
||||
|
||||
REQUIRE(parseResult.root);
|
||||
REQUIRE(parseResult.root->body.size == 1);
|
||||
AstStatLocal* statLocal = parseResult.root->body.data[0]->as<AstStatLocal>();
|
||||
REQUIRE(statLocal);
|
||||
CHECK_EQ("local a: string = 'hello'", toString(statLocal));
|
||||
REQUIRE(statLocal->vars.size == 1);
|
||||
AstLocal* local = statLocal->vars.data[0];
|
||||
REQUIRE(local->annotation);
|
||||
CHECK_EQ("string", toString(local->annotation));
|
||||
REQUIRE(statLocal->values.size == 1);
|
||||
AstExpr* expr = statLocal->values.data[0];
|
||||
CHECK_EQ("'hello'", toString(expr));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -247,9 +247,6 @@ TEST_CASE_FIXTURE(Fixture, "export_type_and_type_alias_are_duplicates")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
|
||||
{
|
||||
ScopedFastFlag sffs3{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Node<T> = { value: T, child: Node<T>? }
|
||||
|
||||
|
@ -444,8 +444,6 @@ TEST_CASE_FIXTURE(Fixture, "os_time_takes_optional_date_table")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local co = coroutine.create(function() end)
|
||||
)");
|
||||
@ -456,8 +454,6 @@ TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function nifty(x, y)
|
||||
print(x, y)
|
||||
@ -476,8 +472,6 @@ TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "coroutine_wrap_anything_goes")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
local function nifty(x, y)
|
||||
@ -822,8 +816,6 @@ TEST_CASE_FIXTURE(Fixture, "string_format_report_all_type_errors_at_correct_posi
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_add_definitions_to_persistent_types")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local f = math.sin
|
||||
local function g(x) return math.sin(x) end
|
||||
|
@ -232,8 +232,6 @@ TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class")
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "can_read_prop_of_base_class_using_string")
|
||||
{
|
||||
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local c = ChildClass.New()
|
||||
local x = 1 + c["BaseField"]
|
||||
@ -244,8 +242,6 @@ TEST_CASE_FIXTURE(ClassFixture, "can_read_prop_of_base_class_using_string")
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string")
|
||||
{
|
||||
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local c = ChildClass.New()
|
||||
c["BaseField"] = 444
|
||||
@ -451,4 +447,25 @@ b.X = 2 -- real Vector2.X is also read-only
|
||||
CHECK_EQ("Value of type 'Vector2?' could be nil", toString(result.errors[3]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "detailed_class_unification_error")
|
||||
{
|
||||
ScopedFastFlag luauExtendedClassMismatchError{"LuauExtendedClassMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function foo(v)
|
||||
return v.X :: number + string.len(v.Y)
|
||||
end
|
||||
|
||||
local a: Vector2
|
||||
local b = foo
|
||||
b(a)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(R"(Type 'Vector2' could not be converted into '{- X: a, Y: string -}'
|
||||
caused by:
|
||||
Property 'Y' is not compatible. Type 'number' could not be converted into 'string')",
|
||||
toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -171,9 +171,6 @@ TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_classes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "declaring_generic_functions")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
||||
|
||||
loadDefinition(R"(
|
||||
declare function f<a, b>(a: a, b: b): string
|
||||
declare function g<a..., b...>(...: a...): b...
|
||||
|
@ -13,8 +13,6 @@ TEST_SUITE_BEGIN("GenericsTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function id<a>(x:a): a
|
||||
return x
|
||||
@ -27,8 +25,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id<a>(x:a): a
|
||||
return x
|
||||
@ -41,10 +37,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_generic_typepack_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauGenericVariadicsUnification", true};
|
||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function id<a...>(...: a...): (a...) return ... end
|
||||
local x: string, y: boolean = id("hi", true)
|
||||
@ -56,8 +48,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_typepack_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "types_before_typepacks")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function f<a,b...>() end
|
||||
)");
|
||||
@ -66,8 +56,6 @@ TEST_CASE_FIXTURE(Fixture, "types_before_typepacks")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id<a>(x:a):a return x end
|
||||
local f: <a>(a)->a = id
|
||||
@ -79,7 +67,6 @@ TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "inferred_local_vars_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id(x) return x end
|
||||
print("This is bogus") -- TODO: CLI-39916
|
||||
@ -92,7 +79,6 @@ TEST_CASE_FIXTURE(Fixture, "inferred_local_vars_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_instantiated_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id(x) return x end
|
||||
print("This is bogus") -- TODO: CLI-39916
|
||||
@ -104,8 +90,6 @@ TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_instantiated_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local t = {}
|
||||
t.m = function<a>(x: a):a return x end
|
||||
@ -117,8 +101,6 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
||||
local function id<a>(x:a):a return x end
|
||||
@ -129,8 +111,6 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_nested_generic_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f()
|
||||
local function id<a>(x:a): a
|
||||
@ -145,8 +125,6 @@ TEST_CASE_FIXTURE(Fixture, "check_nested_generic_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id<a>(x:a):a
|
||||
local y: string = id("hi")
|
||||
@ -159,8 +137,6 @@ TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local id2
|
||||
local function id1<a>(x:a):a
|
||||
@ -179,8 +155,6 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
type T = { id: <a>(a) -> a }
|
||||
local x: T = { id = function<a>(x:a):a return x end }
|
||||
@ -192,8 +166,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
type T<a> = { id: (a) -> a }
|
||||
type Factory = { build: <a>() -> T<a> }
|
||||
@ -215,10 +187,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = { id: <a>(a) -> a }
|
||||
type Factory = { build: () -> T }
|
||||
@ -241,7 +209,6 @@ TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function id(x)
|
||||
return x
|
||||
@ -265,7 +232,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id(x)
|
||||
return x
|
||||
@ -289,7 +255,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f()
|
||||
local function id(x)
|
||||
@ -304,7 +269,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local x = {}
|
||||
function x:id(x) return x end
|
||||
@ -316,7 +280,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local x = {}
|
||||
function x:id(x) return x end
|
||||
@ -331,8 +294,6 @@ TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauRankNTypes", true};
|
||||
CheckResult result = check(R"(
|
||||
local t = {}
|
||||
t.m = function(x) return x end
|
||||
@ -344,9 +305,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_arguments_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f(g: <a>(a)->a)
|
||||
local x: number = g(37)
|
||||
@ -358,9 +316,6 @@ TEST_CASE_FIXTURE(Fixture, "function_arguments_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_results_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f() : <a>(a)->a
|
||||
local function id<a>(x:a):a return x end
|
||||
@ -372,9 +327,6 @@ TEST_CASE_FIXTURE(Fixture, "function_results_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_parameters_can_be_polytypes")
|
||||
{
|
||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
||||
CheckResult result = check(R"(
|
||||
local function id<a>(x:a):a return x end
|
||||
local f: <a>(a)->a = id(id)
|
||||
@ -384,7 +336,6 @@ TEST_CASE_FIXTURE(Fixture, "type_parameters_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f(y)
|
||||
-- this will only typecheck if we infer z: any
|
||||
@ -406,7 +357,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
local function f(y)
|
||||
local z = y
|
||||
@ -423,12 +373,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauGenericFunctions", true},
|
||||
{"LuauParseGenericFunctions", true},
|
||||
{"LuauRankNTypes", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = { m: <a>(a) -> T }
|
||||
function f(t : T)
|
||||
@ -440,10 +384,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_unify_bound_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type F = <a>() -> <b>(a, b) -> a
|
||||
type G = <b>(b, b) -> b
|
||||
@ -470,7 +410,6 @@ TEST_CASE_FIXTURE(Fixture, "mutable_state_polymorphism")
|
||||
// Replaying the classic problem with polymorphism and mutable state in Luau
|
||||
// See, e.g. Tofte (1990)
|
||||
// https://www.sciencedirect.com/science/article/pii/089054019090018D.
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
-- Our old friend the polymorphic identity function
|
||||
@ -508,7 +447,6 @@ TEST_CASE_FIXTURE(Fixture, "mutable_state_polymorphism")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "rank_N_types_via_typeof")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", false};
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local function id(x) return x end
|
||||
@ -531,8 +469,6 @@ TEST_CASE_FIXTURE(Fixture, "rank_N_types_via_typeof")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function f<a,a>(x:a):a return x end
|
||||
)");
|
||||
@ -541,7 +477,6 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_generic_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function f<a...,a...>() end
|
||||
)");
|
||||
@ -550,7 +485,6 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
CheckResult result = check(R"(
|
||||
function f<a...,b>() end
|
||||
)");
|
||||
@ -559,9 +493,6 @@ TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "variadic_generics")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f<a>(...: a) end
|
||||
|
||||
@ -573,9 +504,6 @@ TEST_CASE_FIXTURE(Fixture, "variadic_generics")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f<a...>(...: a...): (a...) return ... end
|
||||
)");
|
||||
@ -586,10 +514,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauGenericVariadicsUnification", true};
|
||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f<a...>(...: a...): any return (...) end
|
||||
)");
|
||||
@ -599,9 +523,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f<T>(...: T...)
|
||||
return ...
|
||||
@ -626,9 +547,6 @@ TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
|
||||
{
|
||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f<a, a...>() end
|
||||
)");
|
||||
@ -641,8 +559,6 @@ TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
|
||||
{
|
||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(z)
|
||||
local o = {}
|
||||
@ -665,8 +581,6 @@ TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
|
||||
{
|
||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(x) return {5} end
|
||||
function g(x, y) return f(x) end
|
||||
@ -680,8 +594,6 @@ TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
|
||||
{
|
||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T<a> = { x: {a}, y: {number} }
|
||||
local o1: T<boolean> = { x = {true}, y = {5} }
|
||||
@ -697,7 +609,6 @@ TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "bound_tables_do_not_clone_original_fields")
|
||||
{
|
||||
ScopedFastFlag luauRankNTypes{"LuauRankNTypes", true};
|
||||
ScopedFastFlag luauCloneBoundTables{"LuauCloneBoundTables", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -341,4 +341,43 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_setmetatable")
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_part")
|
||||
{
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X = { x: number }
|
||||
type Y = { y: number }
|
||||
type Z = { z: number }
|
||||
|
||||
type XYZ = X & Y & Z
|
||||
|
||||
local a: XYZ = 3
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'number' could not be converted into 'X & Y & Z'
|
||||
caused by:
|
||||
Not all intersection parts are compatible. Type 'number' could not be converted into 'X')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all")
|
||||
{
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X = { x: number }
|
||||
type Y = { y: number }
|
||||
type Z = { z: number }
|
||||
|
||||
type XYZ = X & Y & Z
|
||||
|
||||
local a: XYZ
|
||||
local b: number = a
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'X & Y & Z' could not be converted into 'number'; none of the intersection parts are compatible)");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -194,9 +194,6 @@ TEST_CASE_FIXTURE(Fixture, "normal_conditional_expression_has_refinements")
|
||||
// Luau currently doesn't yet know how to allow assignments when the binding was refined.
|
||||
TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
|
||||
{
|
||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Node<T> = { value: T, child: Node<T>? }
|
||||
|
||||
@ -596,11 +593,9 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
|
||||
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
||||
{
|
||||
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
|
||||
ScopedFastFlag luauFollowInTypeFunApply{"LuauFollowInTypeFunApply", true};
|
||||
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
|
||||
|
||||
// Mutability in type function application right now can create strange recursive types
|
||||
// TODO: instantiation right now is problematic, it this example should either leave the Table type alone
|
||||
// TODO: instantiation right now is problematic, in this example should either leave the Table type alone
|
||||
// or it should rename the type to 'Self' so that the result will be 'Self<Table>'
|
||||
CheckResult result = check(R"(
|
||||
type Table = { a: number }
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauWeakEqConstraint)
|
||||
LUAU_FASTFLAG(LuauOrPredicate)
|
||||
LUAU_FASTFLAG(LuauQuantifyInPlace2)
|
||||
|
||||
using namespace Luau;
|
||||
@ -133,11 +132,8 @@ TEST_CASE_FIXTURE(Fixture, "or_predicate_with_truthy_predicates")
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({3, 26})));
|
||||
CHECK_EQ("number?", toString(requireTypeAtPosition({4, 26})));
|
||||
|
||||
if (FFlag::LuauOrPredicate)
|
||||
{
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({6, 26})));
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({7, 26})));
|
||||
}
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({6, 26})));
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({7, 26})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_assertion_expr_carry_its_constraints")
|
||||
@ -283,6 +279,8 @@ TEST_CASE_FIXTURE(Fixture, "assert_non_binary_expressions_actually_resolve_const
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true};
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?} = {x = nil}
|
||||
@ -293,7 +291,10 @@ TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_ty
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ("Type '{| x: number? |}' could not be converted into '{| x: number |}'", toString(result.errors[0]));
|
||||
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
|
||||
caused by:
|
||||
Property 'x' is not compatible. Type 'number?' could not be converted into 'number')",
|
||||
toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "lvalue_is_equal_to_another_lvalue")
|
||||
@ -749,8 +750,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "type_narrow_for_all_the_userdata")
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "eliminate_subclasses_of_instance")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: Part | Folder | string)
|
||||
if typeof(x) == "Instance" then
|
||||
@ -769,8 +768,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "eliminate_subclasses_of_instance")
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_this_large_union")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: Part | Folder | Instance | string | Vector3 | any)
|
||||
if typeof(x) == "Instance" then
|
||||
@ -789,8 +786,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_this_large_union")
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is_table")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
|
||||
@ -811,11 +806,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "x_is_not_instance_or_else_not_part")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauOrPredicate", true},
|
||||
{"LuauTypeGuardPeelsAwaySubclasses", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: Part | Folder | string)
|
||||
if typeof(x) ~= "Instance" or not x:IsA("Part") then
|
||||
@ -890,8 +880,6 @@ TEST_CASE_FIXTURE(Fixture, "type_guard_warns_on_no_overlapping_types_only_when_s
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a: number?, b: number?)
|
||||
if (not a) or (not b) then
|
||||
@ -909,8 +897,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a: number?, b: number?)
|
||||
if not (a and b) then
|
||||
@ -928,8 +914,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b2")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a: number?, b: number?)
|
||||
if (not a) and (not b) then
|
||||
@ -947,8 +931,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a: number?, b: number?)
|
||||
if not (a or b) then
|
||||
@ -966,8 +948,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b2")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "either_number_or_string")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: any)
|
||||
if type(x) == "number" or type(x) == "string" then
|
||||
@ -983,8 +963,6 @@ TEST_CASE_FIXTURE(Fixture, "either_number_or_string")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(t: {x: boolean}?)
|
||||
if not t or t.x then
|
||||
@ -1000,8 +978,6 @@ TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "assert_a_to_be_truthy_then_assert_a_to_be_number")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a: (number | string)?
|
||||
assert(a)
|
||||
@ -1018,8 +994,6 @@ TEST_CASE_FIXTURE(Fixture, "assert_a_to_be_truthy_then_assert_a_to_be_number")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "merge_should_be_fully_agnostic_of_hashmap_ordering")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
// This bug came up because there was a mistake in Luau::merge where zipping on two maps would produce the wrong merged result.
|
||||
CheckResult result = check(R"(
|
||||
local function f(b: string | { x: string }, a)
|
||||
@ -1039,8 +1013,6 @@ TEST_CASE_FIXTURE(Fixture, "merge_should_be_fully_agnostic_of_hashmap_ordering")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "refine_the_correct_types_opposite_of_when_a_is_not_number_or_string")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a: string | number | boolean)
|
||||
if type(a) ~= "number" and type(a) ~= "string" then
|
||||
|
@ -1950,4 +1950,76 @@ TEST_CASE_FIXTURE(Fixture, "table_insert_should_cope_with_optional_properties_in
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = { x: number, y: number }
|
||||
type B = { x: number, y: string }
|
||||
|
||||
local a: A
|
||||
local b: B = a
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'A' could not be converted into 'B'
|
||||
caused by:
|
||||
Property 'y' is not compatible. Type 'number' could not be converted into 'string')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop_nested")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type AS = { x: number, y: number }
|
||||
type BS = { x: number, y: string }
|
||||
|
||||
type A = { a: boolean, b: AS }
|
||||
type B = { a: boolean, b: BS }
|
||||
|
||||
local a: A
|
||||
local b: B = a
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'A' could not be converted into 'B'
|
||||
caused by:
|
||||
Property 'b' is not compatible. Type 'AS' could not be converted into 'BS'
|
||||
caused by:
|
||||
Property 'y' is not compatible. Type 'number' could not be converted into 'string')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_metatable_prop")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a1 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
|
||||
local b1 = setmetatable({ x = 2, y = "hello" }, { __call = function(s) end });
|
||||
local c1: typeof(a1) = b1
|
||||
|
||||
local a2 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
|
||||
local b2 = setmetatable({ x = 2, y = 4 }, { __call = function(s, t) end });
|
||||
local c2: typeof(a2) = b2
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'b1' could not be converted into 'a1'
|
||||
caused by:
|
||||
Type '{| x: number, y: string |}' could not be converted into '{| x: number, y: number |}'
|
||||
caused by:
|
||||
Property 'y' is not compatible. Type 'string' could not be converted into 'number')");
|
||||
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'b2' could not be converted into 'a2'
|
||||
caused by:
|
||||
Type '{| __call: (a, b) -> () |}' could not be converted into '{| __call: <a>(a) -> () |}'
|
||||
caused by:
|
||||
Property '__call' is not compatible. Type '(a, b) -> ()' could not be converted into '<a>(a) -> ()')");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -3926,8 +3926,6 @@ local b: number = 1 or a
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
||||
{
|
||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local tbl = {}
|
||||
@ -4493,10 +4491,6 @@ f(function(x) print(x) end)
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument")
|
||||
{
|
||||
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
|
||||
return sum(2, 3, function(a, b) return a + b end)
|
||||
@ -4525,10 +4519,6 @@ local r = foldl(a, {s=0,c=0}, function(a, b) return {s = a.s + b, c = a.c + 1} e
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded")
|
||||
{
|
||||
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function g1<T>(a: T, f: (T) -> T) return f(a) end
|
||||
local function g2<T>(a: T, b: T, f: (T, T) -> T) return f(a, b) end
|
||||
@ -4579,10 +4569,6 @@ local a: TableWithFunc = { x = 3, y = 4, f = function(a, b) return a + b end }
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "do_not_infer_generic_functions")
|
||||
{
|
||||
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
|
||||
|
||||
@ -4600,8 +4586,6 @@ local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not i
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_return_value_type")
|
||||
{
|
||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(): {string|number}
|
||||
return {1, "b", 3}
|
||||
@ -4625,8 +4609,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_type_assertion_value_type")
|
||||
{
|
||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f()
|
||||
return {4, "b", 3} :: {string|number}
|
||||
@ -4638,8 +4620,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types")
|
||||
{
|
||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a: (number, number) -> number = function(a, b) return a - b end
|
||||
|
||||
@ -4655,8 +4635,6 @@ b, c = {2, "s"}, {"b", 4}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types_mutable_lval")
|
||||
{
|
||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a = {}
|
||||
a.x = 2
|
||||
@ -4668,8 +4646,6 @@ a = setmetatable(a, { __call = function(x) end })
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "refine_and_or")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauSlightlyMoreFlexibleBinaryPredicates", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?}? = {x = nil}
|
||||
local u = t and t.x or 5
|
||||
@ -4682,10 +4658,6 @@ TEST_CASE_FIXTURE(Fixture, "refine_and_or")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?}? = {x = nil}
|
||||
local u = t.x and t or 5
|
||||
@ -4698,10 +4670,6 @@ TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?}? = {x = nil}
|
||||
local u = t and t.x == 5 or t.x == 31337
|
||||
@ -4714,7 +4682,7 @@ TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
|
||||
{
|
||||
ScopedFastFlag luauFollowInTypeFunApply("LuauFollowInTypeFunApply", true);
|
||||
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = { x: number }
|
||||
|
@ -178,9 +178,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "unifying_variadic_pack_with_error_should_wor
|
||||
|
||||
TEST_CASE_FIXTURE(TryUnifyFixture, "variadics_should_use_reversed_properly")
|
||||
{
|
||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local function f<T>(...: T): ...T
|
||||
@ -199,8 +196,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "variadics_should_use_reversed_properly")
|
||||
|
||||
TEST_CASE_FIXTURE(TryUnifyFixture, "cli_41095_concat_log_in_sealed_table_unification")
|
||||
{
|
||||
ScopedFastFlag sffs2("LuauGenericFunctions", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
table.insert()
|
||||
|
@ -296,7 +296,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -361,7 +360,6 @@ local c: Packed<string, number, boolean>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_import")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -395,7 +393,6 @@ local d: { a: typeof(c) }
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_pack_type_parameters")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -434,7 +431,6 @@ type C<X...> = Import.Packed<string, (number, X...)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_nested")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -456,7 +452,6 @@ type Packed4<T...> = (Packed3<T...>, T...) -> (Packed3<T...>, T...)
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_variadic")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -475,7 +470,6 @@ type E = X<(number, ...string)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_multi")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -507,7 +501,6 @@ type I<S..., R...> = W<number, (string, S...), R...>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -534,7 +527,6 @@ type F = X<(string, ...number)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -557,10 +549,8 @@ type D<X...> = Y<X..., (number, string, X...)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi_tostring")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
ScopedFastFlag luauInstantiatedTypeParamRecursion("LuauInstantiatedTypeParamRecursion", true); // For correct toString block
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T..., U...> = { f: (T...) -> (U...) }
|
||||
@ -577,7 +567,6 @@ local b: Y<(), ()>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_backwards_compatible")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
@ -599,7 +588,6 @@ type C = Y<(number), boolean>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_errors")
|
||||
{
|
||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
|
@ -400,8 +400,6 @@ local e = a.z
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
||||
{
|
||||
ScopedFastFlag luauSealedTableUnifyOptionalFix("LuauSealedTableUnifyOptionalFix", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local x: { x: number } = { x = 3 }
|
||||
type A = number?
|
||||
@ -426,4 +424,43 @@ y = x
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_part")
|
||||
{
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X = { x: number }
|
||||
type Y = { y: number }
|
||||
type Z = { z: number }
|
||||
|
||||
type XYZ = X | Y | Z
|
||||
|
||||
local a: XYZ
|
||||
local b: { w: number } = a
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'X | Y | Z' could not be converted into '{| w: number |}'
|
||||
caused by:
|
||||
Not all union options are compatible. Table type 'X' not compatible with type '{| w: number |}' because the former is missing field 'w')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all")
|
||||
{
|
||||
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X = { x: number }
|
||||
type Y = { y: number }
|
||||
type Z = { z: number }
|
||||
|
||||
type XYZ = X | Y | Z
|
||||
|
||||
local a: XYZ = { w = 4 }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'a' could not be converted into 'X | Y | Z'; none of the union options are compatible)");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauGenericFunctions);
|
||||
|
||||
TEST_SUITE_BEGIN("TypeVarTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "primitives_are_equal")
|
||||
|
@ -113,6 +113,20 @@ assert(bit32.replace(0, -1, 4) == 2^4)
|
||||
assert(bit32.replace(-1, 0, 31) == 2^31 - 1)
|
||||
assert(bit32.replace(-1, 0, 1, 2) == 2^32 - 7)
|
||||
|
||||
-- testing countlz/countrc
|
||||
assert(bit32.countlz(0) == 32)
|
||||
assert(bit32.countlz(42) == 26)
|
||||
assert(bit32.countlz(0xffffffff) == 0)
|
||||
assert(bit32.countlz(0x80000000) == 0)
|
||||
assert(bit32.countlz(0x7fffffff) == 1)
|
||||
|
||||
assert(bit32.countrz(0) == 32)
|
||||
assert(bit32.countrz(1) == 0)
|
||||
assert(bit32.countrz(42) == 1)
|
||||
assert(bit32.countrz(0x80000000) == 31)
|
||||
assert(bit32.countrz(0x40000000) == 30)
|
||||
assert(bit32.countrz(0x7fffffff) == 0)
|
||||
|
||||
--[[
|
||||
This test verifies a fix in luauF_replace() where if the 4th
|
||||
parameter was not a number, but the first three are numbers, it will
|
||||
@ -136,5 +150,7 @@ assert(bit32.bxor("1", 3) == 2)
|
||||
assert(bit32.bxor(1, "3") == 2)
|
||||
assert(bit32.btest(1, "3") == true)
|
||||
assert(bit32.btest("1", 3) == true)
|
||||
assert(bit32.countlz("42") == 26)
|
||||
assert(bit32.countrz("42") == 1)
|
||||
|
||||
return('OK')
|
||||
|
Loading…
Reference in New Issue
Block a user