mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-12 13:00:38 +00: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
67 changed files with 1433 additions and 1807 deletions
|
@ -8,11 +8,20 @@
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
struct TypeError;
|
||||||
|
|
||||||
struct TypeMismatch
|
struct TypeMismatch
|
||||||
{
|
{
|
||||||
TypeId wantedType;
|
TypeMismatch() = default;
|
||||||
TypeId givenType;
|
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;
|
bool operator==(const TypeMismatch& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,7 @@ struct FileResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED APIS
|
// 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 bool moduleExists(const ModuleName& name) const = 0;
|
||||||
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
|
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
|
||||||
virtual ModuleName concat(const ModuleName& lhs, std::string_view rhs) 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 parseError; // Nonempty if the transpile failed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string toString(AstNode* node);
|
||||||
void dump(AstNode* node);
|
void dump(AstNode* node);
|
||||||
|
|
||||||
// Never fails on a well-formed AST
|
// Never fails on a well-formed AST
|
||||||
|
@ -25,6 +26,6 @@ std::string transpile(AstStatBlock& ast);
|
||||||
std::string transpileWithTypes(AstStatBlock& block);
|
std::string transpileWithTypes(AstStatBlock& block);
|
||||||
|
|
||||||
// Only fails when parsing fails
|
// 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
|
} // namespace Luau
|
||||||
|
|
|
@ -263,8 +263,6 @@ public:
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location);
|
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`.
|
// 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.
|
// 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.
|
// Produce a new free type var.
|
||||||
TypeId freshType(const ScopePtr& scope);
|
TypeId freshType(const ScopePtr& scope);
|
||||||
TypeId freshType(TypeLevel level);
|
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.
|
// Returns nullopt if the predicate filters down the TypeId to 0 options.
|
||||||
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||||
|
@ -326,10 +322,8 @@ private:
|
||||||
TypePackId addTypePack(std::initializer_list<TypeId>&& ty);
|
TypePackId addTypePack(std::initializer_list<TypeId>&& ty);
|
||||||
TypePackId freshTypePack(const ScopePtr& scope);
|
TypePackId freshTypePack(const ScopePtr& scope);
|
||||||
TypePackId freshTypePack(TypeLevel level);
|
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 AstTypeList& types);
|
||||||
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
|
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
|
||||||
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAddMissingFollow)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -128,13 +126,10 @@ TypePack* asMutable(const TypePack* tp);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const T* get(TypePackId tp)
|
const T* get(TypePackId tp)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
LUAU_ASSERT(tp);
|
||||||
{
|
|
||||||
LUAU_ASSERT(tp);
|
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
return get_if<T>(&(tp->ty));
|
return get_if<T>(&(tp->ty));
|
||||||
}
|
}
|
||||||
|
@ -142,13 +137,10 @@ const T* get(TypePackId tp)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T* getMutable(TypePackId tp)
|
T* getMutable(TypePackId tp)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
LUAU_ASSERT(tp);
|
||||||
{
|
|
||||||
LUAU_ASSERT(tp);
|
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
if constexpr (!std::is_same_v<T, BoundTypePack>)
|
||||||
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
return get_if<T>(&(asMutable(tp)->ty));
|
return get_if<T>(&(asMutable(tp)->ty));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
||||||
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
|
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
|
||||||
LUAU_FASTFLAG(LuauAddMissingFollow)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -413,13 +412,17 @@ bool maybeGeneric(const TypeId ty);
|
||||||
|
|
||||||
struct SingletonTypes
|
struct SingletonTypes
|
||||||
{
|
{
|
||||||
const TypeId nilType = &nilType_;
|
const TypeId nilType;
|
||||||
const TypeId numberType = &numberType_;
|
const TypeId numberType;
|
||||||
const TypeId stringType = &stringType_;
|
const TypeId stringType;
|
||||||
const TypeId booleanType = &booleanType_;
|
const TypeId booleanType;
|
||||||
const TypeId threadType = &threadType_;
|
const TypeId threadType;
|
||||||
const TypeId anyType = &anyType_;
|
const TypeId anyType;
|
||||||
const TypeId errorType = &errorType_;
|
const TypeId errorType;
|
||||||
|
const TypeId optionalNumberType;
|
||||||
|
|
||||||
|
const TypePackId anyTypePack;
|
||||||
|
const TypePackId errorTypePack;
|
||||||
|
|
||||||
SingletonTypes();
|
SingletonTypes();
|
||||||
SingletonTypes(const SingletonTypes&) = delete;
|
SingletonTypes(const SingletonTypes&) = delete;
|
||||||
|
@ -427,14 +430,6 @@ struct SingletonTypes
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<struct TypeArena> arena;
|
std::unique_ptr<struct TypeArena> arena;
|
||||||
TypeVar nilType_;
|
|
||||||
TypeVar numberType_;
|
|
||||||
TypeVar stringType_;
|
|
||||||
TypeVar booleanType_;
|
|
||||||
TypeVar threadType_;
|
|
||||||
TypeVar anyType_;
|
|
||||||
TypeVar errorType_;
|
|
||||||
|
|
||||||
TypeId makeStringMetatable();
|
TypeId makeStringMetatable();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -472,13 +467,10 @@ TypeVar* asMutable(TypeId ty);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const T* get(TypeId tv)
|
const T* get(TypeId tv)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
LUAU_ASSERT(tv);
|
||||||
{
|
|
||||||
LUAU_ASSERT(tv);
|
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
return get_if<T>(&tv->ty);
|
return get_if<T>(&tv->ty);
|
||||||
}
|
}
|
||||||
|
@ -486,13 +478,10 @@ const T* get(TypeId tv)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T* getMutable(TypeId tv)
|
T* getMutable(TypeId tv)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
LUAU_ASSERT(tv);
|
||||||
{
|
|
||||||
LUAU_ASSERT(tv);
|
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
if constexpr (!std::is_same_v<T, BoundTypeVar>)
|
||||||
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
return get_if<T>(&asMutable(tv)->ty);
|
return get_if<T>(&asMutable(tv)->ty);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,12 +63,9 @@ using Name = std::string;
|
||||||
struct Free
|
struct Free
|
||||||
{
|
{
|
||||||
explicit Free(TypeLevel level);
|
explicit Free(TypeLevel level);
|
||||||
Free(TypeLevel level, bool DEPRECATED_canBeGeneric);
|
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
// Removed by FFlag::LuauRankNTypes
|
|
||||||
bool DEPRECATED_canBeGeneric = false;
|
|
||||||
// True if this free type variable is part of a mutually
|
// True if this free type variable is part of a mutually
|
||||||
// recursive type alias whose definitions haven't been
|
// recursive type alias whose definitions haven't been
|
||||||
// resolved yet.
|
// resolved yet.
|
||||||
|
|
|
@ -87,7 +87,6 @@ private:
|
||||||
void tryUnifyWithAny(TypePackId any, TypePackId ty);
|
void tryUnifyWithAny(TypePackId any, TypePackId ty);
|
||||||
|
|
||||||
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
||||||
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
|
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
|
||||||
|
@ -102,6 +101,7 @@ private:
|
||||||
bool isNonstrictMode() const;
|
bool isNonstrictMode() const;
|
||||||
|
|
||||||
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
|
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, const Location& location);
|
||||||
[[noreturn]] void ice(const std::string& message);
|
[[noreturn]] void ice(const std::string& message);
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
|
||||||
LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
|
LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
|
||||||
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
|
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
|
||||||
|
|
||||||
|
@ -369,20 +368,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
|
|
||||||
while (iter != endIter)
|
while (iter != endIter)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
if (isNil(*iter))
|
||||||
{
|
++iter;
|
||||||
if (isNil(*iter))
|
|
||||||
++iter;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
break;
|
||||||
if (auto primTy = Luau::get<PrimitiveTypeVar>(*iter); primTy && primTy->type == PrimitiveTypeVar::NilType)
|
|
||||||
++iter;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter == endIter)
|
if (iter == endIter)
|
||||||
|
@ -397,21 +386,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
AutocompleteEntryMap inner;
|
AutocompleteEntryMap inner;
|
||||||
std::unordered_set<TypeId> innerSeen = seen;
|
std::unordered_set<TypeId> innerSeen = seen;
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
if (isNil(*iter))
|
||||||
{
|
{
|
||||||
if (isNil(*iter))
|
++iter;
|
||||||
{
|
continue;
|
||||||
++iter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (auto innerPrimTy = Luau::get<PrimitiveTypeVar>(*iter); innerPrimTy && innerPrimTy->type == PrimitiveTypeVar::NilType)
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
autocompleteProps(module, typeArena, *iter, indexType, nodes, inner, innerSeen);
|
autocompleteProps(module, typeArena, *iter, indexType, nodes, inner, innerSeen);
|
||||||
|
@ -1519,10 +1497,10 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
TypeChecker& typeChecker =
|
TypeChecker& typeChecker =
|
||||||
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
|
||||||
ModulePtr module =
|
ModulePtr module =
|
||||||
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
|
(frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
|
||||||
: frontend.moduleResolver.getModule(moduleName));
|
: frontend.moduleResolver.getModule(moduleName));
|
||||||
|
|
||||||
if (!module)
|
if (!module)
|
||||||
return {};
|
return {};
|
||||||
|
@ -1550,7 +1528,7 @@ OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view
|
||||||
sourceModule->commentLocations = std::move(result.commentLocations);
|
sourceModule->commentLocations = std::move(result.commentLocations);
|
||||||
|
|
||||||
TypeChecker& typeChecker =
|
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);
|
ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauParseGenericFunctions)
|
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
|
||||||
LUAU_FASTFLAG(LuauRankNTypes)
|
|
||||||
LUAU_FASTFLAG(LuauNewRequireTrace)
|
|
||||||
|
|
||||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||||
*
|
*
|
||||||
|
@ -185,25 +182,11 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||||
TypeId numberType = typeChecker.numberType;
|
TypeId numberType = typeChecker.numberType;
|
||||||
TypeId booleanType = typeChecker.booleanType;
|
TypeId booleanType = typeChecker.booleanType;
|
||||||
TypeId nilType = typeChecker.nilType;
|
TypeId nilType = typeChecker.nilType;
|
||||||
TypeId stringType = typeChecker.stringType;
|
|
||||||
TypeId threadType = typeChecker.threadType;
|
|
||||||
TypeId anyType = typeChecker.anyType;
|
|
||||||
|
|
||||||
TypeArena& arena = typeChecker.globalTypes;
|
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 oneNumberPack = arena.addTypePack({numberType});
|
||||||
TypePackId oneStringPack = arena.addTypePack({stringType});
|
|
||||||
TypePackId oneBooleanPack = arena.addTypePack({booleanType});
|
TypePackId oneBooleanPack = arena.addTypePack({booleanType});
|
||||||
TypePackId oneAnyPack = arena.addTypePack({anyType});
|
|
||||||
|
|
||||||
TypePackId anyTypePack = typeChecker.anyTypePack;
|
|
||||||
|
|
||||||
TypePackId numberVariadicList = arena.addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
TypePackId numberVariadicList = arena.addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
||||||
TypePackId listOfAtLeastOneNumber = arena.addTypePack(TypePack{{numberType}, numberVariadicList});
|
TypePackId listOfAtLeastOneNumber = arena.addTypePack(TypePack{{numberType}, numberVariadicList});
|
||||||
|
@ -215,8 +198,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||||
|
|
||||||
TypeId listOfAtLeastZeroNumbersToNumberType = arena.addType(FunctionTypeVar{numberVariadicList, oneNumberPack});
|
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");
|
LoadDefinitionFileResult loadResult = Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), "@luau");
|
||||||
LUAU_ASSERT(loadResult.success);
|
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");
|
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 genericK = arena.addType(GenericTypeVar{"K"});
|
||||||
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
||||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level});
|
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");
|
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)
|
// next<K, V>(t: Table<K, V>, i: K | nil) -> (K, V)
|
||||||
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(typeChecker, arena, genericK)}});
|
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(typeChecker, arena, genericK)}});
|
||||||
addGlobalBinding(typeChecker, "next",
|
addGlobalBinding(typeChecker, "next",
|
||||||
|
@ -475,8 +238,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||||
|
|
||||||
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
|
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
|
||||||
|
|
||||||
TypeId pairsNext = (FFlag::LuauRankNTypes ? arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})})
|
TypeId pairsNext = arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})});
|
||||||
: getGlobalBinding(typeChecker, "next"));
|
|
||||||
TypePackId pairsReturnTypePack = arena.addTypePack(TypePack{{pairsNext, mapOfKtoV, nilType}});
|
TypePackId pairsReturnTypePack = arena.addTypePack(TypePack{{pairsNext, mapOfKtoV, nilType}});
|
||||||
|
|
||||||
// NOTE we are missing 'i: K | nil' argument in the first return types' argument.
|
// 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]))
|
if (!checkRequirePath(typechecker, expr.args.data[0]))
|
||||||
return std::nullopt;
|
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))
|
if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, *require))
|
||||||
return ExprResult<TypePackId>{arena.addTypePack({typechecker.checkRequire(scope, *moduleInfo, expr.location)})};
|
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
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauParseGenericFunctions)
|
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -19,6 +16,8 @@ declare bit32: {
|
||||||
bnot: (number) -> number,
|
bnot: (number) -> number,
|
||||||
extract: (number, number, number?) -> number,
|
extract: (number, number, number?) -> number,
|
||||||
replace: (number, number, number, number?) -> number,
|
replace: (number, number, number, number?) -> number,
|
||||||
|
countlz: (number) -> number,
|
||||||
|
countrz: (number) -> number,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare math: {
|
declare math: {
|
||||||
|
@ -103,15 +102,6 @@ declare _VERSION: string
|
||||||
|
|
||||||
declare function gcinfo(): number
|
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 print<T...>(...: T...)
|
||||||
|
|
||||||
declare function type<T>(value: T): string
|
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.
|
-- 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
|
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||||
)";
|
|
||||||
}
|
|
||||||
|
|
||||||
return src;
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
std::string getBuiltinDefinitionSource()
|
||||||
|
{
|
||||||
|
return kBuiltinDefinitionLuaSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -94,8 +94,23 @@ struct ErrorConverter
|
||||||
{
|
{
|
||||||
std::string operator()(const Luau::TypeMismatch& tm) const
|
std::string operator()(const Luau::TypeMismatch& tm) const
|
||||||
{
|
{
|
||||||
ToStringOptions opts;
|
std::string result = "Type '" + Luau::toString(tm.givenType) + "' could not be converted into '" + Luau::toString(tm.wantedType) + "'";
|
||||||
return "Type '" + Luau::toString(tm.givenType, opts) + "' could not be converted into '" + Luau::toString(tm.wantedType, opts) + "'";
|
|
||||||
|
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
|
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
|
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
|
bool UnknownSymbol::operator==(const UnknownSymbol& rhs) const
|
||||||
|
@ -690,130 +732,141 @@ bool containsParseErrorName(const TypeError& error)
|
||||||
return Luau::visit(InvalidNameChecker{}, error.data);
|
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) {
|
auto clone = [&](auto&& ty) {
|
||||||
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
|
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto visitErrorData = [&](auto&& e) {
|
auto visitErrorData = [&](auto&& e) {
|
||||||
using T = std::decay_t<decltype(e)>;
|
copyError(e, destArena, seenTypes, seenTypePacks);
|
||||||
|
};
|
||||||
|
|
||||||
if constexpr (false)
|
if constexpr (false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, TypeMismatch>)
|
else if constexpr (std::is_same_v<T, TypeMismatch>)
|
||||||
{
|
{
|
||||||
e.wantedType = clone(e.wantedType);
|
e.wantedType = clone(e.wantedType);
|
||||||
e.givenType = clone(e.givenType);
|
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);
|
|
||||||
|
|
||||||
for (auto& ty : e.missing)
|
if (e.error)
|
||||||
ty = clone(ty);
|
visit(visitErrorData, e.error->data);
|
||||||
}
|
}
|
||||||
else
|
else if constexpr (std::is_same_v<T, UnknownSymbol>)
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
{
|
||||||
|
}
|
||||||
|
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());
|
LUAU_ASSERT(!destArena.typeVars.isFrozen());
|
||||||
|
|
|
@ -18,11 +18,10 @@
|
||||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSecondTypecheckKnowsTheDataModel, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
|
LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
|
||||||
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
|
||||||
LUAU_FASTFLAG(LuauNewRequireTrace)
|
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauClearScopes, false)
|
LUAU_FASTFLAGVARIABLE(LuauClearScopes, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -415,7 +414,7 @@ CheckResult Frontend::check(const ModuleName& name)
|
||||||
// If we're typechecking twice, we do so.
|
// If we're typechecking twice, we do so.
|
||||||
// The second typecheck is always in strict mode with DM awareness
|
// The second typecheck is always in strict mode with DM awareness
|
||||||
// to provide better typen information for IDE features.
|
// to provide better typen information for IDE features.
|
||||||
if (options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
if (options.typecheckTwice)
|
||||||
{
|
{
|
||||||
ModulePtr moduleForAutocomplete = typeCheckerForAutocomplete.check(sourceModule, Mode::Strict);
|
ModulePtr moduleForAutocomplete = typeCheckerForAutocomplete.check(sourceModule, Mode::Strict);
|
||||||
moduleResolverForAutocomplete.modules[moduleName] = moduleForAutocomplete;
|
moduleResolverForAutocomplete.modules[moduleName] = moduleForAutocomplete;
|
||||||
|
@ -897,7 +896,7 @@ std::optional<ModuleInfo> FrontendModuleResolver::resolveModuleInfo(const Module
|
||||||
const auto& exprs = it->second.exprs;
|
const auto& exprs = it->second.exprs;
|
||||||
|
|
||||||
const ModuleInfo* info = exprs.find(&pathExpr);
|
const ModuleInfo* info = exprs.find(&pathExpr);
|
||||||
if (!info || (!FFlag::LuauNewRequireTrace && info->name.empty()))
|
if (!info || (!FFlag::LuauNewRequireTrace2 && info->name.empty()))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return *info;
|
return *info;
|
||||||
|
@ -914,7 +913,7 @@ const ModulePtr FrontendModuleResolver::getModule(const ModuleName& moduleName)
|
||||||
|
|
||||||
bool FrontendModuleResolver::moduleExists(const ModuleName& moduleName) const
|
bool FrontendModuleResolver::moduleExists(const ModuleName& moduleName) const
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewRequireTrace)
|
if (FFlag::LuauNewRequireTrace2)
|
||||||
return frontend->sourceNodes.count(moduleName) != 0;
|
return frontend->sourceNodes.count(moduleName) != 0;
|
||||||
else
|
else
|
||||||
return frontend->fileResolver->moduleExists(moduleName);
|
return frontend->fileResolver->moduleExists(moduleName);
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLinterUnknownTypeVectorAware, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLinterTableMoveZero, false)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1110,10 +1107,7 @@ private:
|
||||||
|
|
||||||
if (g && g->name == "type")
|
if (g && g->name == "type")
|
||||||
{
|
{
|
||||||
if (FFlag::LuauLinterUnknownTypeVectorAware)
|
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
|
||||||
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
|
|
||||||
else
|
|
||||||
validateType(arg, {Kind_Primitive}, "primitive type");
|
|
||||||
}
|
}
|
||||||
else if (g && g->name == "typeof")
|
else if (g && g->name == "typeof")
|
||||||
{
|
{
|
||||||
|
@ -2146,7 +2140,7 @@ private:
|
||||||
"wrap it in parentheses to silence");
|
"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, _, _)
|
// table.move(t, 0, _, _)
|
||||||
if (isConstant(args[1], 0.0))
|
if (isConstant(args[1], 0.0))
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
||||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
|
||||||
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
|
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneBoundTables, false)
|
LUAU_FASTFLAGVARIABLE(LuauCloneBoundTables, false)
|
||||||
|
@ -290,9 +289,7 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
|
||||||
for (TypePackId genericPack : t.genericPacks)
|
for (TypePackId genericPack : t.genericPacks)
|
||||||
ftv->genericPacks.push_back(clone(genericPack, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
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->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||||
ftv->argNames = t.argNames;
|
ftv->argNames = t.argNames;
|
||||||
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||||
|
@ -319,12 +316,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
|
||||||
ttv->level = TypeLevel{0, 0};
|
ttv->level = TypeLevel{0, 0};
|
||||||
|
|
||||||
for (const auto& [name, prop] : t.props)
|
for (const auto& [name, prop] : t.props)
|
||||||
{
|
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.indexer)
|
if (t.indexer)
|
||||||
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
|
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
|
||||||
|
@ -379,10 +371,7 @@ void TypeCloner::operator()(const ClassTypeVar& t)
|
||||||
seenTypes[typeId] = result;
|
seenTypes[typeId] = result;
|
||||||
|
|
||||||
for (const auto& [name, prop] : t.props)
|
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};
|
||||||
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};
|
|
||||||
|
|
||||||
if (t.parent)
|
if (t.parent)
|
||||||
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauOrPredicate)
|
|
||||||
|
|
||||||
namespace Luau
|
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)
|
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauOrPredicate);
|
|
||||||
|
|
||||||
auto itL = l.begin();
|
auto itL = l.begin();
|
||||||
auto itR = r.begin();
|
auto itR = r.begin();
|
||||||
while (itL != l.end() && itR != r.end())
|
while (itL != l.end() && itR != r.end())
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTraceRequireLookupChild, false)
|
LUAU_FASTFLAGVARIABLE(LuauTraceRequireLookupChild, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace, false)
|
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace2, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ struct RequireTracerOld : AstVisitor
|
||||||
: fileResolver(fileResolver)
|
: fileResolver(fileResolver)
|
||||||
, currentModuleName(currentModuleName)
|
, currentModuleName(currentModuleName)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!FFlag::LuauNewRequireTrace);
|
LUAU_ASSERT(!FFlag::LuauNewRequireTrace2);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileResolver* const fileResolver;
|
FileResolver* const fileResolver;
|
||||||
|
@ -188,7 +188,7 @@ struct RequireTracer : AstVisitor
|
||||||
, currentModuleName(currentModuleName)
|
, currentModuleName(currentModuleName)
|
||||||
, locals(nullptr)
|
, locals(nullptr)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauNewRequireTrace);
|
LUAU_ASSERT(FFlag::LuauNewRequireTrace2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(AstExprTypeAssertion* expr) override
|
bool visit(AstExprTypeAssertion* expr) override
|
||||||
|
@ -332,7 +332,7 @@ struct RequireTracer : AstVisitor
|
||||||
|
|
||||||
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName)
|
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewRequireTrace)
|
if (FFlag::LuauNewRequireTrace2)
|
||||||
{
|
{
|
||||||
RequireTraceResult result;
|
RequireTraceResult result;
|
||||||
RequireTracer tracer{result, fileResolver, currentModuleName};
|
RequireTracer tracer{result, fileResolver, currentModuleName};
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000)
|
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionDontReplaceIgnoredTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauSubstitutionDontReplaceIgnoredTypes, false)
|
||||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
|
||||||
LUAU_FASTFLAG(LuauRankNTypes)
|
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -19,7 +17,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
|
if (ignoreChildren(ty))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||||
|
@ -68,7 +66,7 @@ void Tarjan::visitChildren(TypePackId tp, int index)
|
||||||
{
|
{
|
||||||
tp = follow(tp);
|
tp = follow(tp);
|
||||||
|
|
||||||
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
|
if (ignoreChildren(tp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (const TypePack* tpp = get<TypePack>(tp))
|
if (const TypePack* tpp = get<TypePack>(tp))
|
||||||
|
@ -399,8 +397,7 @@ TypeId Substitution::clone(TypeId ty)
|
||||||
if (FFlag::LuauTypeAliasPacks)
|
if (FFlag::LuauTypeAliasPacks)
|
||||||
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
|
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
|
||||||
|
|
||||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
clone.tags = ttv->tags;
|
||||||
clone.tags = ttv->tags;
|
|
||||||
result = addType(std::move(clone));
|
result = addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
||||||
|
@ -486,7 +483,7 @@ void Substitution::replaceChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
|
if (ignoreChildren(ty))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
|
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
|
||||||
|
@ -535,7 +532,7 @@ void Substitution::replaceChildren(TypePackId tp)
|
||||||
{
|
{
|
||||||
tp = follow(tp);
|
tp = follow(tp);
|
||||||
|
|
||||||
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
|
if (ignoreChildren(tp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TypePack* tpp = getMutable<TypePack>(tp))
|
if (TypePack* tpp = getMutable<TypePack>(tp))
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
|
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInstantiatedTypeParamRecursion, false)
|
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -237,15 +236,6 @@ struct TypeVarStringifier
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FFlag::LuauAddMissingFollow)
|
|
||||||
{
|
|
||||||
if (get<FreeTypeVar>(tv))
|
|
||||||
{
|
|
||||||
state.emit(state.getName(tv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Luau::visit(
|
Luau::visit(
|
||||||
[this, tv](auto&& t) {
|
[this, tv](auto&& t) {
|
||||||
return (*this)(tv, t);
|
return (*this)(tv, t);
|
||||||
|
@ -316,11 +306,7 @@ struct TypeVarStringifier
|
||||||
void operator()(TypeId ty, const Unifiable::Free& ftv)
|
void operator()(TypeId ty, const Unifiable::Free& ftv)
|
||||||
{
|
{
|
||||||
state.result.invalid = true;
|
state.result.invalid = true;
|
||||||
|
state.emit(state.getName(ty));
|
||||||
if (FFlag::LuauAddMissingFollow)
|
|
||||||
state.emit(state.getName(ty));
|
|
||||||
else
|
|
||||||
state.emit("<FREE>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypeId, const BoundTypeVar& btv)
|
void operator()(TypeId, const BoundTypeVar& btv)
|
||||||
|
@ -724,16 +710,6 @@ struct TypePackStringifier
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FFlag::LuauAddMissingFollow)
|
|
||||||
{
|
|
||||||
if (get<FreeTypePack>(tp))
|
|
||||||
{
|
|
||||||
state.emit(state.getName(tp));
|
|
||||||
state.emit("...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = state.cycleTpNames.find(tp);
|
auto it = state.cycleTpNames.find(tp);
|
||||||
if (it != state.cycleTpNames.end())
|
if (it != state.cycleTpNames.end())
|
||||||
{
|
{
|
||||||
|
@ -821,16 +797,8 @@ struct TypePackStringifier
|
||||||
void operator()(TypePackId tp, const FreeTypePack& pack)
|
void operator()(TypePackId tp, const FreeTypePack& pack)
|
||||||
{
|
{
|
||||||
state.result.invalid = true;
|
state.result.invalid = true;
|
||||||
|
state.emit(state.getName(tp));
|
||||||
if (FFlag::LuauAddMissingFollow)
|
state.emit("...");
|
||||||
{
|
|
||||||
state.emit(state.getName(tp));
|
|
||||||
state.emit("...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state.emit("<FREETP>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypePackId, const BoundTypePack& btv)
|
void operator()(TypePackId, const BoundTypePack& btv)
|
||||||
|
@ -864,23 +832,15 @@ static void assignCycleNames(const std::unordered_set<TypeId>& cycles, const std
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
// TODO: use the stringified type list if there are no cycles
|
// 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) {
|
||||||
// If we have a cycle type in type parameters, assign a cycle name for this named table
|
return cycles.count(follow(el));
|
||||||
if (std::find_if(ttv->instantiatedTypeParams.begin(), ttv->instantiatedTypeParams.end(), [&](auto&& el) {
|
}) != ttv->instantiatedTypeParams.end())
|
||||||
return cycles.count(follow(el));
|
cycleNames[cycleTy] = ttv->name ? *ttv->name : *ttv->syntheticName;
|
||||||
}) != ttv->instantiatedTypeParams.end())
|
|
||||||
cycleNames[cycleTy] = ttv->name ? *ttv->name : *ttv->syntheticName;
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name = "t" + std::to_string(nextIndex);
|
name = "t" + std::to_string(nextIndex);
|
||||||
|
@ -912,58 +872,6 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||||
|
|
||||||
ToStringResult result;
|
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};
|
StringifierState state{opts, result, opts.nameMap};
|
||||||
|
|
||||||
std::unordered_set<TypeId> cycles;
|
std::unordered_set<TypeId> cycles;
|
||||||
|
@ -975,7 +883,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||||
|
|
||||||
TypeVarStringifier tvs{state};
|
TypeVarStringifier tvs{state};
|
||||||
|
|
||||||
if (FFlag::LuauInstantiatedTypeParamRecursion && !opts.exhaustive)
|
if (!opts.exhaustive)
|
||||||
{
|
{
|
||||||
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
|
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -97,9 +96,6 @@ struct Writer
|
||||||
{
|
{
|
||||||
virtual ~Writer() {}
|
virtual ~Writer() {}
|
||||||
|
|
||||||
virtual void begin() {}
|
|
||||||
virtual void end() {}
|
|
||||||
|
|
||||||
virtual void advance(const Position&) = 0;
|
virtual void advance(const Position&) = 0;
|
||||||
virtual void newline() = 0;
|
virtual void newline() = 0;
|
||||||
virtual void space() = 0;
|
virtual void space() = 0;
|
||||||
|
@ -131,6 +127,7 @@ struct StringWriter : Writer
|
||||||
if (pos.column < newPos.column)
|
if (pos.column < newPos.column)
|
||||||
write(std::string(newPos.column - pos.column, ' '));
|
write(std::string(newPos.column - pos.column, ' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
void maybeSpace(const Position& newPos, int reserve) override
|
void maybeSpace(const Position& newPos, int reserve) override
|
||||||
{
|
{
|
||||||
if (pos.column + reserve < newPos.column)
|
if (pos.column + reserve < newPos.column)
|
||||||
|
@ -279,11 +276,14 @@ struct Printer
|
||||||
writer.identifier(func->index.value);
|
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>())
|
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||||
{
|
{
|
||||||
writer.symbol("...");
|
if (!forVarArg)
|
||||||
|
writer.symbol("...");
|
||||||
|
|
||||||
visualizeTypeAnnotation(*variadicTp->variadicType);
|
visualizeTypeAnnotation(*variadicTp->variadicType);
|
||||||
}
|
}
|
||||||
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
|
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
|
||||||
|
@ -293,6 +293,7 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!forVarArg);
|
||||||
visualizeTypeList(explicitTp->typeList, true);
|
visualizeTypeList(explicitTp->typeList, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -317,7 +318,7 @@ struct Printer
|
||||||
// Only variadic tail
|
// Only variadic tail
|
||||||
if (list.types.size == 0)
|
if (list.types.size == 0)
|
||||||
{
|
{
|
||||||
visualizeTypePackAnnotation(*list.tailType);
|
visualizeTypePackAnnotation(*list.tailType, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -345,7 +346,7 @@ struct Printer
|
||||||
if (list.tailType)
|
if (list.tailType)
|
||||||
{
|
{
|
||||||
writer.symbol(",");
|
writer.symbol(",");
|
||||||
visualizeTypePackAnnotation(*list.tailType);
|
visualizeTypePackAnnotation(*list.tailType, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
|
@ -542,6 +543,7 @@ struct Printer
|
||||||
case AstExprBinary::CompareLt:
|
case AstExprBinary::CompareLt:
|
||||||
case AstExprBinary::CompareGt:
|
case AstExprBinary::CompareGt:
|
||||||
writer.maybeSpace(a->right->location.begin, 2);
|
writer.maybeSpace(a->right->location.begin, 2);
|
||||||
|
writer.symbol(toString(a->op));
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Concat:
|
case AstExprBinary::Concat:
|
||||||
case AstExprBinary::CompareNe:
|
case AstExprBinary::CompareNe:
|
||||||
|
@ -550,19 +552,35 @@ struct Printer
|
||||||
case AstExprBinary::CompareGe:
|
case AstExprBinary::CompareGe:
|
||||||
case AstExprBinary::Or:
|
case AstExprBinary::Or:
|
||||||
writer.maybeSpace(a->right->location.begin, 3);
|
writer.maybeSpace(a->right->location.begin, 3);
|
||||||
|
writer.keyword(toString(a->op));
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::And:
|
case AstExprBinary::And:
|
||||||
writer.maybeSpace(a->right->location.begin, 4);
|
writer.maybeSpace(a->right->location.begin, 4);
|
||||||
|
writer.keyword(toString(a->op));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.symbol(toString(a->op));
|
|
||||||
|
|
||||||
visualize(*a->right);
|
visualize(*a->right);
|
||||||
}
|
}
|
||||||
else if (const auto& a = expr.as<AstExprTypeAssertion>())
|
else if (const auto& a = expr.as<AstExprTypeAssertion>())
|
||||||
{
|
{
|
||||||
visualize(*a->expr);
|
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>())
|
else if (const auto& a = expr.as<AstExprError>())
|
||||||
{
|
{
|
||||||
|
@ -769,24 +787,31 @@ struct Printer
|
||||||
switch (a->op)
|
switch (a->op)
|
||||||
{
|
{
|
||||||
case AstExprBinary::Add:
|
case AstExprBinary::Add:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("+=");
|
writer.symbol("+=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Sub:
|
case AstExprBinary::Sub:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("-=");
|
writer.symbol("-=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Mul:
|
case AstExprBinary::Mul:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("*=");
|
writer.symbol("*=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Div:
|
case AstExprBinary::Div:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("/=");
|
writer.symbol("/=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Mod:
|
case AstExprBinary::Mod:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("%=");
|
writer.symbol("%=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Pow:
|
case AstExprBinary::Pow:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 2);
|
||||||
writer.symbol("^=");
|
writer.symbol("^=");
|
||||||
break;
|
break;
|
||||||
case AstExprBinary::Concat:
|
case AstExprBinary::Concat:
|
||||||
|
writer.maybeSpace(a->value->location.begin, 3);
|
||||||
writer.symbol("..=");
|
writer.symbol("..=");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -874,7 +899,7 @@ struct Printer
|
||||||
|
|
||||||
void visualizeFunctionBody(AstExprFunction& func)
|
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);
|
CommaSeparatorInserter comma(writer);
|
||||||
writer.symbol("<");
|
writer.symbol("<");
|
||||||
|
@ -913,12 +938,13 @@ struct Printer
|
||||||
if (func.vararg)
|
if (func.vararg)
|
||||||
{
|
{
|
||||||
comma();
|
comma();
|
||||||
|
advance(func.varargLocation.begin);
|
||||||
writer.symbol("...");
|
writer.symbol("...");
|
||||||
|
|
||||||
if (func.varargAnnotation)
|
if (func.varargAnnotation)
|
||||||
{
|
{
|
||||||
writer.symbol(":");
|
writer.symbol(":");
|
||||||
visualizeTypePackAnnotation(*func.varargAnnotation);
|
visualizeTypePackAnnotation(*func.varargAnnotation, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -980,8 +1006,14 @@ struct Printer
|
||||||
advance(typeAnnotation.location.begin);
|
advance(typeAnnotation.location.begin);
|
||||||
if (const auto& a = typeAnnotation.as<AstTypeReference>())
|
if (const auto& a = typeAnnotation.as<AstTypeReference>())
|
||||||
{
|
{
|
||||||
|
if (a->hasPrefix)
|
||||||
|
{
|
||||||
|
writer.write(a->prefix.value);
|
||||||
|
writer.symbol(".");
|
||||||
|
}
|
||||||
|
|
||||||
writer.write(a->name.value);
|
writer.write(a->name.value);
|
||||||
if (a->parameters.size > 0)
|
if (a->parameters.size > 0 || a->hasParameterList)
|
||||||
{
|
{
|
||||||
CommaSeparatorInserter comma(writer);
|
CommaSeparatorInserter comma(writer);
|
||||||
writer.symbol("<");
|
writer.symbol("<");
|
||||||
|
@ -992,7 +1024,7 @@ struct Printer
|
||||||
if (o.type)
|
if (o.type)
|
||||||
visualizeTypeAnnotation(*o.type);
|
visualizeTypeAnnotation(*o.type);
|
||||||
else
|
else
|
||||||
visualizeTypePackAnnotation(*o.typePack);
|
visualizeTypePackAnnotation(*o.typePack, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.symbol(">");
|
writer.symbol(">");
|
||||||
|
@ -1000,7 +1032,7 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeFunction>())
|
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);
|
CommaSeparatorInserter comma(writer);
|
||||||
writer.symbol("<");
|
writer.symbol("<");
|
||||||
|
@ -1075,7 +1107,16 @@ struct Printer
|
||||||
auto rta = r->as<AstTypeReference>();
|
auto rta = r->as<AstTypeReference>();
|
||||||
if (rta && rta->name == "nil")
|
if (rta && rta->name == "nil")
|
||||||
{
|
{
|
||||||
|
bool wrap = l->as<AstTypeIntersection>() || l->as<AstTypeFunction>();
|
||||||
|
|
||||||
|
if (wrap)
|
||||||
|
writer.symbol("(");
|
||||||
|
|
||||||
visualizeTypeAnnotation(*l);
|
visualizeTypeAnnotation(*l);
|
||||||
|
|
||||||
|
if (wrap)
|
||||||
|
writer.symbol(")");
|
||||||
|
|
||||||
writer.symbol("?");
|
writer.symbol("?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1089,7 +1130,15 @@ struct Printer
|
||||||
writer.symbol("|");
|
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]);
|
visualizeTypeAnnotation(*a->types.data[i]);
|
||||||
|
|
||||||
|
if (wrap)
|
||||||
|
writer.symbol(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
|
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
|
||||||
|
@ -1102,7 +1151,15 @@ struct Printer
|
||||||
writer.symbol("&");
|
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]);
|
visualizeTypeAnnotation(*a->types.data[i]);
|
||||||
|
|
||||||
|
if (wrap)
|
||||||
|
writer.symbol(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (typeAnnotation.is<AstTypeError>())
|
else if (typeAnnotation.is<AstTypeError>())
|
||||||
|
@ -1116,31 +1173,27 @@ struct Printer
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump(AstNode* node)
|
std::string toString(AstNode* node)
|
||||||
{
|
{
|
||||||
StringWriter writer;
|
StringWriter writer;
|
||||||
|
writer.pos = node->location.begin;
|
||||||
|
|
||||||
Printer printer(writer);
|
Printer printer(writer);
|
||||||
printer.writeTypes = true;
|
printer.writeTypes = true;
|
||||||
|
|
||||||
if (auto statNode = dynamic_cast<AstStat*>(node))
|
if (auto statNode = dynamic_cast<AstStat*>(node))
|
||||||
{
|
|
||||||
printer.visualize(*statNode);
|
printer.visualize(*statNode);
|
||||||
printf("%s\n", writer.str().c_str());
|
|
||||||
}
|
|
||||||
else if (auto exprNode = dynamic_cast<AstExpr*>(node))
|
else if (auto exprNode = dynamic_cast<AstExpr*>(node))
|
||||||
{
|
|
||||||
printer.visualize(*exprNode);
|
printer.visualize(*exprNode);
|
||||||
printf("%s\n", writer.str().c_str());
|
|
||||||
}
|
|
||||||
else if (auto typeNode = dynamic_cast<AstType*>(node))
|
else if (auto typeNode = dynamic_cast<AstType*>(node))
|
||||||
{
|
|
||||||
printer.visualizeTypeAnnotation(*typeNode);
|
printer.visualizeTypeAnnotation(*typeNode);
|
||||||
printf("%s\n", writer.str().c_str());
|
|
||||||
}
|
return writer.str();
|
||||||
else
|
}
|
||||||
{
|
|
||||||
printf("Can't dump this node\n");
|
void dump(AstNode* node)
|
||||||
}
|
{
|
||||||
|
printf("%s\n", toString(node).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string transpile(AstStatBlock& block)
|
std::string transpile(AstStatBlock& block)
|
||||||
|
@ -1149,6 +1202,7 @@ std::string transpile(AstStatBlock& block)
|
||||||
Printer(writer).visualizeBlock(block);
|
Printer(writer).visualizeBlock(block);
|
||||||
return writer.str();
|
return writer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string transpileWithTypes(AstStatBlock& block)
|
std::string transpileWithTypes(AstStatBlock& block)
|
||||||
{
|
{
|
||||||
StringWriter writer;
|
StringWriter writer;
|
||||||
|
@ -1158,7 +1212,7 @@ std::string transpileWithTypes(AstStatBlock& block)
|
||||||
return writer.str();
|
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 allocator = Allocator{};
|
||||||
auto names = AstNameTable{allocator};
|
auto names = AstNameTable{allocator};
|
||||||
|
@ -1176,6 +1230,9 @@ TranspileResult transpile(std::string_view source, ParseOptions options)
|
||||||
if (!parseResult.root)
|
if (!parseResult.root)
|
||||||
return TranspileResult{"", {}, "Internal error: Parser yielded empty parse tree"};
|
return TranspileResult{"", {}, "Internal error: Parser yielded empty parse tree"};
|
||||||
|
|
||||||
|
if (withTypes)
|
||||||
|
return TranspileResult{transpileWithTypes(*parseResult.root)};
|
||||||
|
|
||||||
return TranspileResult{transpile(*parseResult.root)};
|
return TranspileResult{transpile(*parseResult.root)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
|
|
||||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||||
|
@ -203,39 +202,23 @@ public:
|
||||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("<Cycle>"));
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("<Cycle>"));
|
||||||
|
|
||||||
AstArray<AstName> generics;
|
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();
|
if (auto gtv = get<GenericTypeVar>(*it))
|
||||||
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
|
generics.data[numGenerics++] = AstName(gtv->name.c_str());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstArray<AstName> genericPacks;
|
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();
|
if (auto gtv = get<GenericTypeVar>(*it))
|
||||||
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
|
genericPacks.data[numGenericPacks++] = AstName(gtv->name.c_str());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstArray<AstType*> argTypes;
|
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)
|
TypePackIterator end(TypePackId tp)
|
||||||
{
|
{
|
||||||
return FFlag::LuauAddMissingFollow ? TypePackIterator{} : TypePackIterator{nullptr};
|
return TypePackIterator{};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
||||||
|
@ -203,7 +203,7 @@ TypePackId follow(TypePackId tp)
|
||||||
|
|
||||||
size_t size(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);
|
return size(*pack);
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -227,7 +227,7 @@ size_t size(const TypePack& tp)
|
||||||
size_t result = tp.head.size();
|
size_t result = tp.head.size();
|
||||||
if (tp.tail)
|
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)
|
if (tail)
|
||||||
result += size(*tail);
|
result += size(*tail);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||||
LUAU_FASTFLAG(LuauRankNTypes)
|
|
||||||
LUAU_FASTFLAG(LuauTypeGuardPeelsAwaySubclasses)
|
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
|
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
|
||||||
|
|
||||||
|
@ -42,7 +40,7 @@ TypeId follow(TypeId t)
|
||||||
};
|
};
|
||||||
|
|
||||||
auto force = [](TypeId ty) {
|
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();
|
TypeId res = ltv->thunk();
|
||||||
if (get<LazyTypeVar>(res))
|
if (get<LazyTypeVar>(res))
|
||||||
|
@ -296,7 +294,7 @@ bool maybeGeneric(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
if (auto ftv = get<FreeTypeVar>(ty))
|
if (auto ftv = get<FreeTypeVar>(ty))
|
||||||
return FFlag::LuauRankNTypes || ftv->DEPRECATED_canBeGeneric;
|
return true;
|
||||||
else if (auto ttv = get<TableTypeVar>(ty))
|
else if (auto ttv = get<TableTypeVar>(ty))
|
||||||
{
|
{
|
||||||
// TODO: recurse on table types CLI-39914
|
// 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<TypePackId> genericPacks, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
|
||||||
std::initializer_list<TypeId> retTypes);
|
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()
|
SingletonTypes::SingletonTypes()
|
||||||
: arena(new TypeArena)
|
: nilType(&nilType_)
|
||||||
, nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true}
|
, numberType(&numberType_)
|
||||||
, numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true}
|
, stringType(&stringType_)
|
||||||
, stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true}
|
, booleanType(&booleanType_)
|
||||||
, booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true}
|
, threadType(&threadType_)
|
||||||
, threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true}
|
, anyType(&anyType_)
|
||||||
, anyType_{AnyTypeVar{}}
|
, errorType(&errorType_)
|
||||||
, errorType_{ErrorTypeVar{}}
|
, optionalNumberType(&optionalNumberType_)
|
||||||
|
, anyTypePack(&anyTypePack_)
|
||||||
|
, errorTypePack(&errorTypePack_)
|
||||||
|
, arena(new TypeArena)
|
||||||
{
|
{
|
||||||
TypeId stringMetatable = makeStringMetatable();
|
TypeId stringMetatable = makeStringMetatable();
|
||||||
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, makeStringMetatable()};
|
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, makeStringMetatable()};
|
||||||
|
@ -1372,24 +1385,6 @@ UnionTypeVarIterator end(const UnionTypeVar* utv)
|
||||||
return UnionTypeVarIterator{};
|
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)
|
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
|
||||||
{
|
{
|
||||||
const char* options = "cdiouxXeEfgGqs";
|
const char* options = "cdiouxXeEfgGqs";
|
||||||
|
@ -1470,9 +1465,6 @@ std::optional<ExprResult<TypePackId>> magicFunctionFormat(
|
||||||
|
|
||||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauTypeGuardPeelsAwaySubclasses)
|
|
||||||
return DEPRECATED_filterMap(type, predicate);
|
|
||||||
|
|
||||||
type = follow(type);
|
type = follow(type);
|
||||||
|
|
||||||
if (auto utv = get<UnionTypeVar>(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
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Unifiable.h"
|
#include "Luau/Unifiable.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRankNTypes)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Unifiable
|
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;
|
int Free::nextIndex = 0;
|
||||||
|
|
||||||
Generic::Generic()
|
Generic::Generic()
|
||||||
|
|
|
@ -14,17 +14,15 @@
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance, false);
|
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance, false);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDontMutatePersistentFunctions, false)
|
|
||||||
LUAU_FASTFLAG(LuauRankNTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnionHeuristic, false)
|
LUAU_FASTFLAGVARIABLE(LuauUnionHeuristic, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
|
LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSealedTableUnifyOptionalFix, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauOccursCheckOkWithRecursiveFunctions, false)
|
LUAU_FASTFLAGVARIABLE(LuauOccursCheckOkWithRecursiveFunctions, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypecheckOpts, false)
|
LUAU_FASTFLAGVARIABLE(LuauTypecheckOpts, false)
|
||||||
LUAU_FASTFLAG(LuauShareTxnSeen);
|
LUAU_FASTFLAG(LuauShareTxnSeen);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCacheUnifyTableResults, false)
|
LUAU_FASTFLAGVARIABLE(LuauCacheUnifyTableResults, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauExtendedTypeMismatchError, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauExtendedClassMismatchError, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -219,17 +217,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
*asMutable(subTy) = BoundTypeVar(superTy);
|
*asMutable(subTy) = BoundTypeVar(superTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FFlag::LuauRankNTypes)
|
|
||||||
l->DEPRECATED_canBeGeneric &= r->DEPRECATED_canBeGeneric;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (l && r && FFlag::LuauGenericFunctions)
|
else if (l && r)
|
||||||
{
|
{
|
||||||
log(superTy);
|
log(superTy);
|
||||||
occursCheck(superTy, subTy);
|
occursCheck(superTy, subTy);
|
||||||
if (!FFlag::LuauRankNTypes)
|
|
||||||
r->DEPRECATED_canBeGeneric &= l->DEPRECATED_canBeGeneric;
|
|
||||||
r->level = min(r->level, l->level);
|
r->level = min(r->level, l->level);
|
||||||
*asMutable(superTy) = BoundTypeVar(subTy);
|
*asMutable(superTy) = BoundTypeVar(subTy);
|
||||||
return;
|
return;
|
||||||
|
@ -240,7 +233,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
|
|
||||||
// Unification can't change the level of a generic.
|
// Unification can't change the level of a generic.
|
||||||
auto rightGeneric = get<GenericTypeVar>(subTy);
|
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
|
// TODO: a more informative error message? CLI-39912
|
||||||
errors.push_back(TypeError{location, GenericError{"Generic subtype escaping scope"}});
|
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.
|
// Unification can't change the level of a generic.
|
||||||
auto leftGeneric = get<GenericTypeVar>(superTy);
|
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
|
// TODO: a more informative error message? CLI-39912
|
||||||
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
|
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
|
||||||
return;
|
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 (!get<ErrorTypeVar>(subTy))
|
||||||
{
|
{
|
||||||
if (auto leftLevel = getMutableLevel(superTy))
|
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
|
// A | B <: T if A <: T and B <: T
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
std::optional<TypeError> unificationTooComplex;
|
std::optional<TypeError> unificationTooComplex;
|
||||||
|
std::optional<TypeError> firstFailedOption;
|
||||||
|
|
||||||
size_t count = uv->options.size();
|
size_t count = uv->options.size();
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -345,7 +321,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
if (auto e = hasUnificationTooComplex(innerState.errors))
|
if (auto e = hasUnificationTooComplex(innerState.errors))
|
||||||
unificationTooComplex = e;
|
unificationTooComplex = e;
|
||||||
else if (!innerState.errors.empty())
|
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;
|
failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (i != count - 1)
|
if (i != count - 1)
|
||||||
innerState.log.rollback();
|
innerState.log.rollback();
|
||||||
|
@ -358,7 +340,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
errors.push_back(*unificationTooComplex);
|
errors.push_back(*unificationTooComplex);
|
||||||
else if (failed)
|
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))
|
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
|
||||||
{
|
{
|
||||||
|
@ -425,14 +412,49 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
errors.push_back(*unificationTooComplex);
|
errors.push_back(*unificationTooComplex);
|
||||||
else if (!found)
|
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))
|
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(superTy))
|
||||||
{
|
{
|
||||||
// T <: A & B if A <: T and B <: T
|
if (FFlag::LuauExtendedTypeMismatchError)
|
||||||
for (TypeId type : uv->parts)
|
|
||||||
{
|
{
|
||||||
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))
|
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
|
||||||
|
@ -480,7 +502,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
errors.push_back(*unificationTooComplex);
|
errors.push_back(*unificationTooComplex);
|
||||||
else if (!found)
|
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))
|
else if (get<PrimitiveTypeVar>(superTy) && get<PrimitiveTypeVar>(subTy))
|
||||||
tryUnifyPrimitives(superTy, 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 both are at the end, we're done
|
||||||
if (!superIter.good() && !subIter.good())
|
if (!superIter.good() && !subIter.good())
|
||||||
{
|
{
|
||||||
const bool lFreeTail = l->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*l->tail) : *l->tail) != nullptr;
|
const bool lFreeTail = l->tail && get<FreeTypePack>(follow(*l->tail)) != nullptr;
|
||||||
const bool rFreeTail = r->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*r->tail) : *r->tail) != nullptr;
|
const bool rFreeTail = r->tail && get<FreeTypePack>(follow(*r->tail)) != nullptr;
|
||||||
if (lFreeTail && rFreeTail)
|
if (lFreeTail && rFreeTail)
|
||||||
tryUnify_(*l->tail, *r->tail);
|
tryUnify_(*l->tail, *r->tail);
|
||||||
else if (lFreeTail)
|
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.
|
// 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();
|
superIter.advance();
|
||||||
continue;
|
continue;
|
||||||
|
@ -887,24 +914,21 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
|
||||||
ice("passed non-function types to unifyFunction");
|
ice("passed non-function types to unifyFunction");
|
||||||
|
|
||||||
size_t numGenerics = lf->generics.size();
|
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());
|
numGenerics = std::min(lf->generics.size(), rf->generics.size());
|
||||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numGenericPacks = lf->genericPacks.size();
|
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());
|
numGenericPacks = std::min(lf->genericPacks.size(), rf->genericPacks.size());
|
||||||
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
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;
|
CountMismatch::Context context = ctx;
|
||||||
|
|
||||||
|
@ -931,22 +955,19 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
|
||||||
tryUnify_(lf->retType, rf->retType);
|
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;
|
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;
|
lf->definition = rf->definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = context;
|
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
|
namespace
|
||||||
|
@ -1032,7 +1053,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||||
|
|
||||||
Unifier innerState = makeChildUnifier();
|
Unifier innerState = makeChildUnifier();
|
||||||
innerState.tryUnify_(prop.type, r->second.type);
|
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())
|
if (innerState.errors.empty())
|
||||||
log.concat(std::move(innerState.log));
|
log.concat(std::move(innerState.log));
|
||||||
else
|
else
|
||||||
|
@ -1047,7 +1073,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||||
|
|
||||||
Unifier innerState = makeChildUnifier();
|
Unifier innerState = makeChildUnifier();
|
||||||
innerState.tryUnify_(prop.type, rt->indexer->indexResultType);
|
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())
|
if (innerState.errors.empty())
|
||||||
log.concat(std::move(innerState.log));
|
log.concat(std::move(innerState.log));
|
||||||
else
|
else
|
||||||
|
@ -1083,7 +1114,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
|
||||||
|
|
||||||
Unifier innerState = makeChildUnifier();
|
Unifier innerState = makeChildUnifier();
|
||||||
innerState.tryUnify_(prop.type, lt->indexer->indexResultType);
|
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())
|
if (innerState.errors.empty())
|
||||||
log.concat(std::move(innerState.log));
|
log.concat(std::move(innerState.log));
|
||||||
else
|
else
|
||||||
|
@ -1384,21 +1420,8 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
|
||||||
const auto& r = rt->props.find(it.first);
|
const auto& r = rt->props.find(it.first);
|
||||||
if (r == rt->props.end())
|
if (r == rt->props.end())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSealedTableUnifyOptionalFix)
|
if (isOptional(it.second.type))
|
||||||
{
|
continue;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
missingPropertiesInSuper.push_back(it.first);
|
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);
|
const auto& r = lt->props.find(it.first);
|
||||||
if (r == lt->props.end())
|
if (r == lt->props.end())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSealedTableUnifyOptionalFix)
|
if (isOptional(it.second.type))
|
||||||
{
|
continue;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extraPropertiesInSub.push_back(it.first);
|
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->table, rhs->table);
|
||||||
innerState.tryUnify_(lhs->metatable, rhs->metatable);
|
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));
|
log.concat(std::move(innerState.log));
|
||||||
}
|
}
|
||||||
|
@ -1613,10 +1634,34 @@ void Unifier::tryUnifyWithClass(TypeId superTy, TypeId subTy, bool reversed)
|
||||||
{
|
{
|
||||||
ok = false;
|
ok = false;
|
||||||
errors.push_back(TypeError{location, UnknownProperty{superTy, propName}});
|
errors.push_back(TypeError{location, UnknownProperty{superTy, propName}});
|
||||||
tryUnify_(prop.type, singletonTypes.errorType);
|
|
||||||
|
if (!FFlag::LuauExtendedClassMismatchError)
|
||||||
|
tryUnify_(prop.type, singletonTypes.errorType);
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (table->indexer)
|
||||||
|
@ -1649,45 +1694,24 @@ static void queueTypePack_DEPRECATED(
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
a = follow(a);
|
||||||
a = follow(a);
|
|
||||||
|
|
||||||
if (seenTypePacks.count(a))
|
if (seenTypePacks.count(a))
|
||||||
break;
|
break;
|
||||||
seenTypePacks.insert(a);
|
seenTypePacks.insert(a);
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
if (get<Unifiable::Free>(a))
|
||||||
{
|
{
|
||||||
if (get<Unifiable::Free>(a))
|
state.log(a);
|
||||||
{
|
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (auto tp = get<TypePack>(a))
|
||||||
{
|
{
|
||||||
if (get<Unifiable::Free>(a))
|
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||||
{
|
if (tp->tail)
|
||||||
state.log(a);
|
a = *tp->tail;
|
||||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
else
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (auto tp = get<TypePack>(a))
|
|
||||||
{
|
|
||||||
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)
|
while (true)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAddMissingFollow)
|
a = follow(a);
|
||||||
a = follow(a);
|
|
||||||
|
|
||||||
if (seenTypePacks.find(a))
|
if (seenTypePacks.find(a))
|
||||||
break;
|
break;
|
||||||
seenTypePacks.insert(a);
|
seenTypePacks.insert(a);
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
if (get<Unifiable::Free>(a))
|
||||||
{
|
{
|
||||||
if (get<Unifiable::Free>(a))
|
state.log(a);
|
||||||
{
|
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (auto tp = get<TypePack>(a))
|
||||||
{
|
{
|
||||||
if (get<Unifiable::Free>(a))
|
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||||
{
|
if (tp->tail)
|
||||||
state.log(a);
|
a = *tp->tail;
|
||||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
else
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (auto tp = get<TypePack>(a))
|
|
||||||
{
|
|
||||||
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);
|
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)
|
void Unifier::occursCheck(TypeId needle, TypeId haystack)
|
||||||
{
|
{
|
||||||
std::unordered_set<TypeId> seen_DEPRECATED;
|
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)
|
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->argTypes);
|
||||||
occursCheck(seen_DEPRECATED, seen, needle, f->retType);
|
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}});
|
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)
|
void Unifier::ice(const std::string& message, const Location& location)
|
||||||
{
|
{
|
||||||
sharedState.iceHandler->ice(message, location);
|
sharedState.iceHandler->ice(message, location);
|
||||||
|
|
|
@ -282,7 +282,6 @@ private:
|
||||||
|
|
||||||
// `<' namelist `>'
|
// `<' namelist `>'
|
||||||
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeList();
|
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeList();
|
||||||
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeListIfFFlagParseGenericFunctions();
|
|
||||||
|
|
||||||
// `<' typeAnnotation[, ...] `>'
|
// `<' typeAnnotation[, ...] `>'
|
||||||
AstArray<AstTypeOrPack> parseTypeParams();
|
AstArray<AstTypeOrPack> parseTypeParams();
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
// See docs/SyntaxChanges.md for an explanation.
|
// See docs/SyntaxChanges.md for an explanation.
|
||||||
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
||||||
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsParserFix, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctions, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCaptureBrokenCommentSpans, false)
|
LUAU_FASTFLAGVARIABLE(LuauCaptureBrokenCommentSpans, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionBaseSupport, false)
|
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionBaseSupport, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIfStatementRecursionGuard, false)
|
LUAU_FASTFLAGVARIABLE(LuauIfStatementRecursionGuard, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeAliasPacks, false)
|
LUAU_FASTFLAGVARIABLE(LuauTypeAliasPacks, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseTypePackTypeParameters, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseTypePackTypeParameters, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFixAmbiguousErrorRecoveryInAssign, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctionTypeBegin, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -957,7 +957,7 @@ AstStat* Parser::parseAssignment(AstExpr* initial)
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ false);
|
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ FFlag::LuauFixAmbiguousErrorRecoveryInAssign);
|
||||||
|
|
||||||
if (!isExprLValue(expr))
|
if (!isExprLValue(expr))
|
||||||
expr = reportExprError(expr->location, copy({expr}), "Assigned expression must be a variable or a field");
|
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;
|
Location start = matchFunction.location;
|
||||||
|
|
||||||
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
|
auto [generics, genericPacks] = parseGenericTypeList();
|
||||||
|
|
||||||
Lexeme matchParen = lexer.current();
|
Lexeme matchParen = lexer.current();
|
||||||
expectAndConsume('(', "function");
|
expectAndConsume('(', "function");
|
||||||
|
@ -1343,19 +1343,18 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
||||||
{
|
{
|
||||||
incrementRecursionCounter("type annotation");
|
incrementRecursionCounter("type annotation");
|
||||||
|
|
||||||
bool monomorphic = !(FFlag::LuauParseGenericFunctions && lexer.current().type == '<');
|
bool monomorphic = lexer.current().type != '<';
|
||||||
|
|
||||||
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
|
|
||||||
|
|
||||||
Lexeme begin = lexer.current();
|
Lexeme begin = lexer.current();
|
||||||
|
|
||||||
if (FFlag::LuauGenericFunctionsParserFix)
|
auto [generics, genericPacks] = parseGenericTypeList();
|
||||||
expectAndConsume('(', "function parameters");
|
|
||||||
else
|
Lexeme parameterStart = lexer.current();
|
||||||
{
|
|
||||||
LUAU_ASSERT(begin.type == '(');
|
if (!FFlag::LuauParseGenericFunctionTypeBegin)
|
||||||
nextLexeme(); // (
|
begin = parameterStart;
|
||||||
}
|
|
||||||
|
expectAndConsume('(', "function parameters");
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
||||||
|
|
||||||
|
@ -1366,7 +1365,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
||||||
if (lexer.current().type != ')')
|
if (lexer.current().type != ')')
|
||||||
varargAnnotation = parseTypeList(params, names);
|
varargAnnotation = parseTypeList(params, names);
|
||||||
|
|
||||||
expectMatchAndConsume(')', begin, true);
|
expectMatchAndConsume(')', parameterStart, true);
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
||||||
|
|
||||||
|
@ -1585,7 +1584,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||||
{
|
{
|
||||||
return {parseTableTypeAnnotation(), {}};
|
return {parseTableTypeAnnotation(), {}};
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '(' || (FFlag::LuauParseGenericFunctions && lexer.current().type == '<'))
|
else if (lexer.current().type == '(' || lexer.current().type == '<')
|
||||||
{
|
{
|
||||||
return parseFunctionTypeAnnotation(allowPack);
|
return parseFunctionTypeAnnotation(allowPack);
|
||||||
}
|
}
|
||||||
|
@ -2315,19 +2314,6 @@ Parser::Name Parser::parseIndexName(const char* context, const Position& previou
|
||||||
return Name(nameError, location);
|
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()
|
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
|
||||||
{
|
{
|
||||||
TempVector<AstName> names{scratchName};
|
TempVector<AstName> names{scratchName};
|
||||||
|
@ -2342,7 +2328,7 @@ std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
AstName name = parseName().name;
|
AstName name = parseName().name;
|
||||||
if (FFlag::LuauParseGenericFunctions && lexer.current().type == Lexeme::Dot3)
|
if (lexer.current().type == Lexeme::Dot3)
|
||||||
{
|
{
|
||||||
seenPack = true;
|
seenPack = true;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
18
CLI/Repl.cpp
18
CLI/Repl.cpp
|
@ -215,9 +215,6 @@ extern "C"
|
||||||
{
|
{
|
||||||
const char* executeScript(const char* source)
|
const char* executeScript(const char* source)
|
||||||
{
|
{
|
||||||
// static string for caching result (prevents dangling ptr on function exit)
|
|
||||||
static std::string result;
|
|
||||||
|
|
||||||
// setup flags
|
// setup flags
|
||||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||||
|
@ -233,15 +230,13 @@ extern "C"
|
||||||
// sandbox thread
|
// sandbox thread
|
||||||
luaL_sandboxthread(L);
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
|
// static string for caching result (prevents dangling ptr on function exit)
|
||||||
|
static std::string result;
|
||||||
|
|
||||||
// run code + collect error
|
// run code + collect error
|
||||||
std::string error = runCode(L, source);
|
result = runCode(L, source);
|
||||||
result = error;
|
|
||||||
|
return result.empty() ? NULL : result.c_str();
|
||||||
if (error.length())
|
|
||||||
{
|
|
||||||
return result.c_str();
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -591,3 +586,4 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -467,6 +467,10 @@ enum LuauBuiltinFunction
|
||||||
|
|
||||||
// vector ctor
|
// vector ctor
|
||||||
LBF_VECTOR,
|
LBF_VECTOR,
|
||||||
|
|
||||||
|
// bit32.count
|
||||||
|
LBF_BIT32_COUNTLZ,
|
||||||
|
LBF_BIT32_COUNTRZ,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capture type, used in LOP_CAPTURE
|
// Capture type, used in LOP_CAPTURE
|
||||||
|
|
|
@ -37,8 +37,7 @@ struct CompileOptions
|
||||||
const char* vectorLib = nullptr;
|
const char* vectorLib = nullptr;
|
||||||
const char* vectorCtor = nullptr;
|
const char* vectorCtor = nullptr;
|
||||||
|
|
||||||
// array of globals that are mutable; disables the import optimization for fields accessed through them
|
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these
|
||||||
// use NULL to end the array
|
|
||||||
const char** mutableGlobals = nullptr;
|
const char** mutableGlobals = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresFenv, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresUpval, false)
|
LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresUpval, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGenericSpecialGlobals, false)
|
LUAU_FASTFLAGVARIABLE(LuauGenericSpecialGlobals, false)
|
||||||
LUAU_FASTFLAG(LuauIfElseExpressionBaseSupport)
|
LUAU_FASTFLAG(LuauIfElseExpressionBaseSupport)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauBit32CountBuiltin, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,7 @@ static const uint32_t kMaxRegisterCount = 255;
|
||||||
static const uint32_t kMaxUpvalueCount = 200;
|
static const uint32_t kMaxUpvalueCount = 200;
|
||||||
static const uint32_t kMaxLocalCount = 200;
|
static const uint32_t kMaxLocalCount = 200;
|
||||||
|
|
||||||
|
// TODO: Remove with LuauGenericSpecialGlobals
|
||||||
static const char* kSpecialGlobals[] = {"Game", "Workspace", "_G", "game", "plugin", "script", "shared", "workspace"};
|
static const char* kSpecialGlobals[] = {"Game", "Workspace", "_G", "game", "plugin", "script", "shared", "workspace"};
|
||||||
|
|
||||||
CompileError::CompileError(const Location& location, const std::string& message)
|
CompileError::CompileError(const Location& location, const std::string& message)
|
||||||
|
@ -3637,6 +3639,10 @@ struct Compiler
|
||||||
return LBF_BIT32_RROTATE;
|
return LBF_BIT32_RROTATE;
|
||||||
if (builtin.method == "rshift")
|
if (builtin.method == "rshift")
|
||||||
return LBF_BIT32_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")
|
if (builtin.object == "string")
|
||||||
|
@ -3711,11 +3717,9 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
|
||||||
compiler.globals[name].writable = true;
|
compiler.globals[name].writable = true;
|
||||||
|
|
||||||
if (options.mutableGlobals)
|
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)
|
if (AstName name = names.get(*ptr); name.value)
|
||||||
compiler.globals[name].writable = true;
|
compiler.globals[name].writable = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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
|
// 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);
|
Compiler::FenvVisitor fenvVisitor(compiler.getfenvUsed, compiler.setfenvUsed);
|
||||||
root->visit(&fenvVisitor);
|
root->visit(&fenvVisitor);
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -137,12 +137,11 @@ $(TESTS_TARGET) $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET):
|
||||||
|
|
||||||
# executable targets for fuzzing
|
# executable targets for fuzzing
|
||||||
fuzz-%: $(BUILD)/fuzz/%.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET)
|
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-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-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
|
# static library targets
|
||||||
$(AST_TARGET): $(AST_OBJECTS)
|
$(AST_TARGET): $(AST_OBJECTS)
|
||||||
$(COMPILER_TARGET): $(COMPILER_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_resumeerror(lua_State* L, lua_State* from);
|
||||||
LUA_API int lua_status(lua_State* L);
|
LUA_API int lua_status(lua_State* L);
|
||||||
LUA_API int lua_isyieldable(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
|
** garbage-collection function and options
|
||||||
|
|
|
@ -988,6 +988,16 @@ int lua_status(lua_State* L)
|
||||||
return L->status;
|
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
|
** 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
|
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#include "lcommon.h"
|
||||||
#include "lnumutils.h"
|
#include "lnumutils.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauBit32Count, false)
|
||||||
|
|
||||||
#define ALLONES ~0u
|
#define ALLONES ~0u
|
||||||
#define NBITS int(8 * sizeof(unsigned))
|
#define NBITS int(8 * sizeof(unsigned))
|
||||||
|
|
||||||
|
@ -177,6 +180,44 @@ static int b_replace(lua_State* L)
|
||||||
return 1;
|
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[] = {
|
static const luaL_Reg bitlib[] = {
|
||||||
{"arshift", b_arshift},
|
{"arshift", b_arshift},
|
||||||
{"band", b_and},
|
{"band", b_and},
|
||||||
|
@ -190,6 +231,8 @@ static const luaL_Reg bitlib[] = {
|
||||||
{"replace", b_replace},
|
{"replace", b_replace},
|
||||||
{"rrotate", b_rrot},
|
{"rrotate", b_rrot},
|
||||||
{"rshift", b_rshift},
|
{"rshift", b_rshift},
|
||||||
|
{"countlz", b_countlz},
|
||||||
|
{"countrz", b_countrz},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1031,6 +1031,52 @@ static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
|
||||||
return -1;
|
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] = {
|
luau_FastFunction luauF_table[256] = {
|
||||||
NULL,
|
NULL,
|
||||||
luauF_assert,
|
luauF_assert,
|
||||||
|
@ -1097,4 +1143,7 @@ luau_FastFunction luauF_table[256] = {
|
||||||
luauF_tunpack,
|
luauF_tunpack,
|
||||||
|
|
||||||
luauF_vector,
|
luauF_vector,
|
||||||
|
|
||||||
|
luauF_countlz,
|
||||||
|
luauF_countrz,
|
||||||
};
|
};
|
||||||
|
|
164
VM/src/lgc.cpp
164
VM/src/lgc.cpp
|
@ -13,7 +13,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRescanGrayAgainForwardBarrier, false)
|
LUAU_FASTFLAGVARIABLE(LuauRescanGrayAgainForwardBarrier, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauConsolidatedStep, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSeparateAtomic, false)
|
LUAU_FASTFLAGVARIABLE(LuauSeparateAtomic, false)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauArrayBoundary)
|
LUAU_FASTFLAG(LuauArrayBoundary)
|
||||||
|
@ -677,117 +676,6 @@ static size_t atomic(lua_State* L)
|
||||||
return work;
|
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)
|
static size_t gcstep(lua_State* L, size_t limit)
|
||||||
{
|
{
|
||||||
size_t cost = 0;
|
size_t cost = 0;
|
||||||
|
@ -980,37 +868,12 @@ void luaC_step(lua_State* L, bool assist)
|
||||||
int lastgcstate = g->gcstate;
|
int lastgcstate = g->gcstate;
|
||||||
double lasttimestamp = lua_clock();
|
double lasttimestamp = lua_clock();
|
||||||
|
|
||||||
if (FFlag::LuauConsolidatedStep)
|
size_t work = gcstep(L, lim);
|
||||||
{
|
|
||||||
size_t work = gcstep(L, lim);
|
|
||||||
|
|
||||||
if (assist)
|
if (assist)
|
||||||
g->gcstats.currcycle.assistwork += work;
|
g->gcstats.currcycle.assistwork += work;
|
||||||
else
|
|
||||||
g->gcstats.currcycle.explicitwork += work;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
g->gcstats.currcycle.explicitwork += work;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
|
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
|
||||||
|
|
||||||
|
@ -1037,14 +900,7 @@ void luaC_step(lua_State* L, bool assist)
|
||||||
g->GCthreshold -= debt;
|
g->GCthreshold -= debt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauConsolidatedStep)
|
GC_INTERRUPT(lastgcstate);
|
||||||
{
|
|
||||||
GC_INTERRUPT(lastgcstate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GC_INTERRUPT(g->gcstate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaC_fullgc(lua_State* L)
|
void luaC_fullgc(lua_State* L)
|
||||||
|
@ -1070,10 +926,7 @@ void luaC_fullgc(lua_State* L)
|
||||||
while (g->gcstate != GCSpause)
|
while (g->gcstate != GCSpause)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
|
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
|
||||||
if (FFlag::LuauConsolidatedStep)
|
gcstep(L, SIZE_MAX);
|
||||||
gcstep(L, SIZE_MAX);
|
|
||||||
else
|
|
||||||
singlestep(L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finishGcCycleStats(g);
|
finishGcCycleStats(g);
|
||||||
|
@ -1084,10 +937,7 @@ void luaC_fullgc(lua_State* L)
|
||||||
markroot(L);
|
markroot(L);
|
||||||
while (g->gcstate != GCSpause)
|
while (g->gcstate != GCSpause)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauConsolidatedStep)
|
gcstep(L, SIZE_MAX);
|
||||||
gcstep(L, SIZE_MAX);
|
|
||||||
else
|
|
||||||
singlestep(L);
|
|
||||||
}
|
}
|
||||||
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */
|
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */
|
||||||
shrinkbuffersfull(L);
|
shrinkbuffersfull(L);
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauStrPackUBCastFix, false)
|
||||||
|
|
||||||
/* macro to `unsign' a character */
|
/* macro to `unsign' a character */
|
||||||
#define uchar(c) ((unsigned char)(c))
|
#define uchar(c) ((unsigned char)(c))
|
||||||
|
|
||||||
|
@ -1404,10 +1406,20 @@ static int str_pack(lua_State* L)
|
||||||
}
|
}
|
||||||
case Kuint:
|
case Kuint:
|
||||||
{ /* unsigned integers */
|
{ /* unsigned integers */
|
||||||
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
|
if (FFlag::LuauStrPackUBCastFix)
|
||||||
if (size < SZINT) /* need overflow check? */
|
{
|
||||||
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
long long n = (long long)luaL_checknumber(L, arg);
|
||||||
packint(&b, n, h.islittle, size, 0);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case Kfloat:
|
case Kfloat:
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
#include "ldebug.h"
|
#include "ldebug.h"
|
||||||
#include "lvm.h"
|
#include "lvm.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableFreeze, false)
|
|
||||||
|
|
||||||
static int foreachi(lua_State* L)
|
static int foreachi(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
@ -491,9 +489,6 @@ static int tclear(lua_State* L)
|
||||||
|
|
||||||
static int tfreeze(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_checktype(L, 1, LUA_TTABLE);
|
||||||
luaL_argcheck(L, !lua_getreadonly(L, 1), 1, "table is already frozen");
|
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");
|
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)
|
static int tisfrozen(lua_State* L)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauTableFreeze)
|
|
||||||
luaG_runerror(L, "table.isfrozen is disabled");
|
|
||||||
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
lua_pushboolean(L, lua_getreadonly(L, 1));
|
lua_pushboolean(L, lua_getreadonly(L, 1));
|
||||||
|
|
|
@ -205,38 +205,48 @@ function Bitboard:empty()
|
||||||
return self.h == 0 and self.l == 0
|
return self.h == 0 and self.l == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Bitboard:ctz()
|
if not bit32.countrz then
|
||||||
local target = self.l
|
local function ctz(v)
|
||||||
local offset = 0
|
if v == 0 then return 32 end
|
||||||
local result = 0
|
local offset = 0
|
||||||
|
while bit32.extract(v, offset) == 0 do
|
||||||
if target == 0 then
|
offset = offset + 1
|
||||||
target = self.h
|
end
|
||||||
result = 32
|
return offset
|
||||||
end
|
end
|
||||||
|
function Bitboard:ctz()
|
||||||
if target == 0 then
|
local result = ctz(self.l)
|
||||||
return 64
|
if result == 32 then
|
||||||
end
|
return ctz(self.h) + 32
|
||||||
|
else
|
||||||
while bit32.extract(target, offset) == 0 do
|
return result
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for i=math.max(32,start),63 do
|
function Bitboard:ctzafter(start)
|
||||||
if bit32.extract(self.h, i-32) == 1 then return i end
|
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
|
end
|
||||||
return 64
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -245,7 +255,7 @@ function Bitboard:lshift(amt)
|
||||||
if amt == 0 then return self end
|
if amt == 0 then return self end
|
||||||
|
|
||||||
if amt > 31 then
|
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
|
end
|
||||||
|
|
||||||
local l = bit32.lshift(self.l, amt)
|
local l = bit32.lshift(self.l, amt)
|
||||||
|
@ -832,12 +842,12 @@ end
|
||||||
local testCases = {}
|
local testCases = {}
|
||||||
local function addTest(...) table.insert(testCases, {...}) end
|
local function addTest(...) table.insert(testCases, {...}) end
|
||||||
|
|
||||||
addTest(StartingFen, 3, 8902)
|
addTest(StartingFen, 2, 400)
|
||||||
addTest("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0", 2, 2039)
|
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", 3, 2812)
|
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", 3, 9467)
|
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", 2, 1486)
|
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", 2, 2079)
|
addTest("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 1, 46)
|
||||||
|
|
||||||
|
|
||||||
local function chess()
|
local function chess()
|
||||||
|
|
|
@ -19,6 +19,7 @@ message Expr {
|
||||||
ExprTable table = 13;
|
ExprTable table = 13;
|
||||||
ExprUnary unary = 14;
|
ExprUnary unary = 14;
|
||||||
ExprBinary binary = 15;
|
ExprBinary binary = 15;
|
||||||
|
ExprIfElse ifelse = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +150,12 @@ message ExprBinary {
|
||||||
required Expr right = 3;
|
required Expr right = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ExprIfElse {
|
||||||
|
required Expr cond = 1;
|
||||||
|
required Expr then = 2;
|
||||||
|
required Expr else = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message LValue {
|
message LValue {
|
||||||
oneof lvalue_oneof {
|
oneof lvalue_oneof {
|
||||||
ExprLocal local = 1;
|
ExprLocal local = 1;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Luau/BytecodeBuilder.h"
|
#include "Luau/BytecodeBuilder.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
|
#include "Luau/Transpiler.h"
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
@ -23,6 +24,7 @@ const bool kFuzzLinter = true;
|
||||||
const bool kFuzzTypeck = true;
|
const bool kFuzzTypeck = true;
|
||||||
const bool kFuzzVM = true;
|
const bool kFuzzVM = true;
|
||||||
const bool kFuzzTypes = true;
|
const bool kFuzzTypes = true;
|
||||||
|
const bool kFuzzTranspile = true;
|
||||||
|
|
||||||
static_assert(!(kFuzzVM && !kFuzzCompiler), "VM requires the compiler!");
|
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
|
// run resulting bytecode
|
||||||
if (kFuzzVM && bytecode.size())
|
if (kFuzzVM && bytecode.size())
|
||||||
{
|
{
|
||||||
|
|
|
@ -476,6 +476,16 @@ struct ProtoToLuau
|
||||||
print(expr.right());
|
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)
|
void print(const luau::LValue& expr)
|
||||||
{
|
{
|
||||||
if (expr.has_local())
|
if (expr.has_local())
|
||||||
|
|
|
@ -45,7 +45,6 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "prop")
|
||||||
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "event_callback_arg")
|
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "event_callback_arg")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{"LuauDontMutatePersistentFunctions", true},
|
|
||||||
{"LuauPersistDefinitionFileTypes", true},
|
{"LuauPersistDefinitionFileTypes", true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1287,9 +1287,6 @@ local e: (n: n@5
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "generic_types")
|
TEST_CASE_FIXTURE(ACFixture, "generic_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
|
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
function f<Tee, Use>(a: T@1
|
function f<Tee, Use>(a: T@1
|
||||||
local b: string = "don't trip"
|
local b: string = "don't trip"
|
||||||
|
|
|
@ -240,8 +240,6 @@ TEST_CASE("Math")
|
||||||
|
|
||||||
TEST_CASE("Table")
|
TEST_CASE("Table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauTableFreeze", true);
|
|
||||||
|
|
||||||
runConformance("nextvar.lua");
|
runConformance("nextvar.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +320,8 @@ TEST_CASE("GC")
|
||||||
|
|
||||||
TEST_CASE("Bitwise")
|
TEST_CASE("Bitwise")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag sff("LuauBit32Count", true);
|
||||||
|
|
||||||
runConformance("bitwise.lua");
|
runConformance("bitwise.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,6 +359,8 @@ TEST_CASE("PCall")
|
||||||
|
|
||||||
TEST_CASE("Pack")
|
TEST_CASE("Pack")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag sff{ "LuauStrPackUBCastFix", true };
|
||||||
|
|
||||||
runConformance("tpack.lua");
|
runConformance("tpack.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -479,10 +479,6 @@ return foo1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "UnknownType")
|
TEST_CASE_FIXTURE(Fixture, "UnknownType")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauLinterUnknownTypeVectorAware", true};
|
|
||||||
|
|
||||||
SourceModule sm;
|
|
||||||
|
|
||||||
unfreeze(typeChecker.globalTypes);
|
unfreeze(typeChecker.globalTypes);
|
||||||
TableTypeVar::Props instanceProps{
|
TableTypeVar::Props instanceProps{
|
||||||
{"ClassName", {typeChecker.anyType}},
|
{"ClassName", {typeChecker.anyType}},
|
||||||
|
@ -1400,8 +1396,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "TableOperations")
|
TEST_CASE_FIXTURE(Fixture, "TableOperations")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauLinterTableMoveZero", true);
|
|
||||||
|
|
||||||
LintResult result = lintTyped(R"(
|
LintResult result = lintTyped(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
local tt = {}
|
local tt = {}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauFixAmbiguousErrorRecoveryInAssign)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -625,10 +627,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
|
||||||
)"),
|
)"),
|
||||||
"Cannot have more than one table indexer");
|
"Cannot have more than one table indexer");
|
||||||
|
|
||||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauGenericFunctionsParserFix", true};
|
|
||||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CHECK_EQ(getParseError(R"(
|
CHECK_EQ(getParseError(R"(
|
||||||
type T = <a>foo
|
type T = <a>foo
|
||||||
)"),
|
)"),
|
||||||
|
@ -1624,6 +1622,20 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_confusing_function_call")
|
||||||
"statements");
|
"statements");
|
||||||
|
|
||||||
CHECK(result3.errors.size() == 1);
|
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")
|
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")
|
TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
|
||||||
{
|
{
|
||||||
// Doesn't need LuauGenericFunctions
|
|
||||||
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
ParseResult result = parseEx(R"(
|
ParseResult result = parseEx(R"(
|
||||||
function f<a...>(...: a...)
|
function f<a...>(...: a...)
|
||||||
end
|
end
|
||||||
|
@ -1861,9 +1870,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_function_declaration_parsing")
|
TEST_CASE_FIXTURE(Fixture, "generic_function_declaration_parsing")
|
||||||
{
|
{
|
||||||
// Doesn't need LuauGenericFunctions
|
|
||||||
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
ParseResult result = parseEx(R"(
|
ParseResult result = parseEx(R"(
|
||||||
declare function f<a, b, c...>()
|
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)",
|
matchParseError("type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)",
|
||||||
"Expected '->' when parsing function type, got <eof>");
|
"Expected '->' when parsing function type, got <eof>");
|
||||||
|
|
||||||
{
|
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
||||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
|
||||||
ScopedFastFlag luauGenericFunctionsParserFix{"LuauGenericFunctionsParserFix", true};
|
|
||||||
|
|
||||||
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -2362,8 +2363,6 @@ type Fn = (
|
||||||
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
|
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parse(R"(type Fn = (any, string | number | <a>()) -> any)");
|
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")
|
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parse(R"(
|
parse(R"(
|
||||||
|
@ -2521,7 +2518,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
|
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
AstStat* stat = parse(R"(
|
AstStat* stat = parse(R"(
|
||||||
|
@ -2534,4 +2530,9 @@ type C<X...> = Packed<(number, X...)>
|
||||||
REQUIRE(stat != nullptr);
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -33,8 +33,6 @@ TEST_SUITE_BEGIN("Predicate");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
|
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
RefinementMap m{
|
RefinementMap m{
|
||||||
{"b", typeChecker.stringType},
|
{"b", typeChecker.stringType},
|
||||||
{"c", typeChecker.numberType},
|
{"c", typeChecker.numberType},
|
||||||
|
@ -61,8 +59,6 @@ TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
|
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
RefinementMap m{
|
RefinementMap m{
|
||||||
{"a", typeChecker.stringType},
|
{"a", typeChecker.stringType},
|
||||||
{"b", 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")
|
TEST_CASE_FIXTURE(Fixture, "one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
RefinementMap m{
|
RefinementMap m{
|
||||||
{"a", typeChecker.stringType},
|
{"a", typeChecker.stringType},
|
||||||
{"b", typeChecker.numberType},
|
{"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")
|
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");
|
CheckResult result = check("local function f<a...>(n: number, ...: a...): (a...) return ... end");
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
@ -340,10 +337,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "toStringDetailed2")
|
TEST_CASE_FIXTURE(Fixture, "toStringDetailed2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[] = {
|
|
||||||
{"LuauGenericFunctions", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local base = {}
|
local base = {}
|
||||||
function base:one() return 1 end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
|
|
||||||
|
|
||||||
TypeVar tableTy{TableTypeVar{}};
|
TypeVar tableTy{TableTypeVar{}};
|
||||||
TableTypeVar* ttv = getMutable<TableTypeVar>(&tableTy);
|
TableTypeVar* ttv = getMutable<TableTypeVar>(&tableTy);
|
||||||
ttv->name = "Table";
|
ttv->name = "Table";
|
||||||
|
|
|
@ -21,7 +21,7 @@ local function isPortal(element)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return element.component==Core.Portal
|
return element.component == Core.Portal
|
||||||
end
|
end
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -223,12 +223,24 @@ TEST_CASE("escaped_strings")
|
||||||
CHECK_EQ(code, transpile(code).code);
|
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")
|
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 '' )";
|
const std::string code = R"( return point and math.ceil(point* 100000* 100)/ 100000 .. '%'or '' )";
|
||||||
CHECK_EQ(code, transpile(code).code);
|
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")
|
TEST_CASE("do_blocks")
|
||||||
{
|
{
|
||||||
const std::string code = R"(
|
const std::string code = R"(
|
||||||
|
@ -364,10 +376,10 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly")
|
||||||
)";
|
)";
|
||||||
|
|
||||||
std::string expected = R"(
|
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
|
end
|
||||||
|
|
||||||
local b:(...string)->(...number)=function(...:...string): ...number
|
local b:(...string)->(...number)=function(...:string): ...number
|
||||||
end
|
end
|
||||||
|
|
||||||
local c:()->()=function(): ()
|
local c:()->()=function(): ()
|
||||||
|
@ -400,4 +412,238 @@ TEST_CASE_FIXTURE(Fixture, "function_type_location")
|
||||||
CHECK_EQ(expected, actual);
|
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();
|
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")
|
TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs3{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Node<T> = { value: T, child: Node<T>? }
|
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")
|
TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local co = coroutine.create(function() end)
|
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")
|
TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function nifty(x, y)
|
local function nifty(x, y)
|
||||||
print(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")
|
TEST_CASE_FIXTURE(Fixture, "coroutine_wrap_anything_goes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!nonstrict
|
--!nonstrict
|
||||||
local function nifty(x, y)
|
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")
|
TEST_CASE_FIXTURE(Fixture, "dont_add_definitions_to_persistent_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local f = math.sin
|
local f = math.sin
|
||||||
local function g(x) return math.sin(x) end
|
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")
|
TEST_CASE_FIXTURE(ClassFixture, "can_read_prop_of_base_class_using_string")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local c = ChildClass.New()
|
local c = ChildClass.New()
|
||||||
local x = 1 + c["BaseField"]
|
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")
|
TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local c = ChildClass.New()
|
local c = ChildClass.New()
|
||||||
c["BaseField"] = 444
|
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]));
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -171,9 +171,6 @@ TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_classes")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "declaring_generic_functions")
|
TEST_CASE_FIXTURE(Fixture, "declaring_generic_functions")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
loadDefinition(R"(
|
loadDefinition(R"(
|
||||||
declare function f<a, b>(a: a, b: b): string
|
declare function f<a, b>(a: a, b: b): string
|
||||||
declare function g<a..., b...>(...: a...): b...
|
declare function g<a..., b...>(...: a...): b...
|
||||||
|
|
|
@ -13,8 +13,6 @@ TEST_SUITE_BEGIN("GenericsTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function id<a>(x:a): a
|
function id<a>(x:a): a
|
||||||
return x
|
return x
|
||||||
|
@ -27,8 +25,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
|
TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id<a>(x:a): a
|
local function id<a>(x:a): a
|
||||||
return x
|
return x
|
||||||
|
@ -41,10 +37,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "check_generic_typepack_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"(
|
CheckResult result = check(R"(
|
||||||
function id<a...>(...: a...): (a...) return ... end
|
function id<a...>(...: a...): (a...) return ... end
|
||||||
local x: string, y: boolean = id("hi", true)
|
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")
|
TEST_CASE_FIXTURE(Fixture, "types_before_typepacks")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a,b...>() end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_polytypes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id<a>(x:a):a return x end
|
local function id<a>(x:a):a return x end
|
||||||
local f: <a>(a)->a = id
|
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")
|
TEST_CASE_FIXTURE(Fixture, "inferred_local_vars_can_be_polytypes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id(x) return x end
|
local function id(x) return x end
|
||||||
print("This is bogus") -- TODO: CLI-39916
|
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")
|
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_instantiated_polytypes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id(x) return x end
|
local function id(x) return x end
|
||||||
print("This is bogus") -- TODO: CLI-39916
|
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")
|
TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
t.m = function<a>(x: a):a return x end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
||||||
local function id<a>(x:a):a return x 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")
|
TEST_CASE_FIXTURE(Fixture, "check_nested_generic_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f()
|
local function f()
|
||||||
local function id<a>(x:a): a
|
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")
|
TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id<a>(x:a):a
|
local function id<a>(x:a):a
|
||||||
local y: string = id("hi")
|
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")
|
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local id2
|
local id2
|
||||||
local function id1<a>(x:a):a
|
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")
|
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T = { id: <a>(a) -> a }
|
type T = { id: <a>(a) -> a }
|
||||||
local x: T = { id = function<a>(x:a):a return x end }
|
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")
|
TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T<a> = { id: (a) -> a }
|
type T<a> = { id: (a) -> a }
|
||||||
type Factory = { build: <a>() -> T<a> }
|
type Factory = { build: <a>() -> T<a> }
|
||||||
|
@ -215,10 +187,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T = { id: <a>(a) -> a }
|
type T = { id: <a>(a) -> a }
|
||||||
type Factory = { build: () -> T }
|
type Factory = { build: () -> T }
|
||||||
|
@ -241,7 +209,6 @@ TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
|
TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function id(x)
|
function id(x)
|
||||||
return x
|
return x
|
||||||
|
@ -265,7 +232,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
|
TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function id(x)
|
local function id(x)
|
||||||
return x
|
return x
|
||||||
|
@ -289,7 +255,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
|
TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f()
|
local function f()
|
||||||
local function id(x)
|
local function id(x)
|
||||||
|
@ -304,7 +269,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
|
TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x = {}
|
local x = {}
|
||||||
function x:id(x) return x end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x = {}
|
local x = {}
|
||||||
function x:id(x) return x end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauRankNTypes", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
t.m = function(x) return x end
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function f(g: <a>(a)->a)
|
local function f(g: <a>(a)->a)
|
||||||
local x: number = g(37)
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function f() : <a>(a)->a
|
local function f() : <a>(a)->a
|
||||||
local function id<a>(x:a):a return x end
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function id<a>(x:a):a return x end
|
local function id<a>(x:a):a return x end
|
||||||
local f: <a>(a)->a = id(id)
|
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")
|
TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(y)
|
local function f(y)
|
||||||
-- this will only typecheck if we infer z: any
|
-- 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")
|
TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(y)
|
local function f(y)
|
||||||
local z = 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")
|
TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauGenericFunctions", true},
|
|
||||||
{"LuauParseGenericFunctions", true},
|
|
||||||
{"LuauRankNTypes", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T = { m: <a>(a) -> T }
|
type T = { m: <a>(a) -> T }
|
||||||
function f(t : 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")
|
TEST_CASE_FIXTURE(Fixture, "dont_unify_bound_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs3{"LuauRankNTypes", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type F = <a>() -> <b>(a, b) -> a
|
type F = <a>() -> <b>(a, b) -> a
|
||||||
type G = <b>(b, b) -> b
|
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
|
// Replaying the classic problem with polymorphism and mutable state in Luau
|
||||||
// See, e.g. Tofte (1990)
|
// See, e.g. Tofte (1990)
|
||||||
// https://www.sciencedirect.com/science/article/pii/089054019090018D.
|
// https://www.sciencedirect.com/science/article/pii/089054019090018D.
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
-- Our old friend the polymorphic identity function
|
-- 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")
|
TEST_CASE_FIXTURE(Fixture, "rank_N_types_via_typeof")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", false};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local function id(x) return x end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a,a>(x:a):a return x end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a...,a...>() end
|
function f<a...,a...>() end
|
||||||
)");
|
)");
|
||||||
|
@ -550,7 +485,6 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
|
TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a...,b>() end
|
function f<a...,b>() end
|
||||||
)");
|
)");
|
||||||
|
@ -559,9 +493,6 @@ TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "variadic_generics")
|
TEST_CASE_FIXTURE(Fixture, "variadic_generics")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a>(...: a) end
|
function f<a>(...: a) end
|
||||||
|
|
||||||
|
@ -573,9 +504,6 @@ TEST_CASE_FIXTURE(Fixture, "variadic_generics")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
|
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a...>(...: a...): (a...) return ... end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs4{"LuauGenericVariadicsUnification", true};
|
|
||||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a...>(...: a...): any return (...) end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<T>(...: T...)
|
function f<T>(...: T...)
|
||||||
return ...
|
return ...
|
||||||
|
@ -626,9 +547,6 @@ TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
|
TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a, a...>() end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f(z)
|
function f(z)
|
||||||
local o = {}
|
local o = {}
|
||||||
|
@ -665,8 +581,6 @@ TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
|
TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f(x) return {5} end
|
function f(x) return {5} end
|
||||||
function g(x, y) return f(x) 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")
|
TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T<a> = { x: {a}, y: {number} }
|
type T<a> = { x: {a}, y: {number} }
|
||||||
local o1: T<boolean> = { x = {true}, y = {5} }
|
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")
|
TEST_CASE_FIXTURE(Fixture, "bound_tables_do_not_clone_original_fields")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauRankNTypes{"LuauRankNTypes", true};
|
|
||||||
ScopedFastFlag luauCloneBoundTables{"LuauCloneBoundTables", true};
|
ScopedFastFlag luauCloneBoundTables{"LuauCloneBoundTables", true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
|
|
@ -341,4 +341,43 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_setmetatable")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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();
|
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.
|
// Luau currently doesn't yet know how to allow assignments when the binding was refined.
|
||||||
TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
|
TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Node<T> = { value: T, child: Node<T>? }
|
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")
|
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
|
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
|
||||||
ScopedFastFlag luauFollowInTypeFunApply{"LuauFollowInTypeFunApply", true};
|
|
||||||
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
|
|
||||||
|
|
||||||
// Mutability in type function application right now can create strange recursive types
|
// 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>'
|
// or it should rename the type to 'Self' so that the result will be 'Self<Table>'
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Table = { a: number }
|
type Table = { a: number }
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauWeakEqConstraint)
|
LUAU_FASTFLAG(LuauWeakEqConstraint)
|
||||||
LUAU_FASTFLAG(LuauOrPredicate)
|
|
||||||
LUAU_FASTFLAG(LuauQuantifyInPlace2)
|
LUAU_FASTFLAG(LuauQuantifyInPlace2)
|
||||||
|
|
||||||
using namespace Luau;
|
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("string?", toString(requireTypeAtPosition({3, 26})));
|
||||||
CHECK_EQ("number?", toString(requireTypeAtPosition({4, 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")
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local t: {x: number?} = {x = nil}
|
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);
|
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")
|
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")
|
TEST_CASE_FIXTURE(RefinementClassFixture, "eliminate_subclasses_of_instance")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x: Part | Folder | string)
|
local function f(x: Part | Folder | string)
|
||||||
if typeof(x) == "Instance" then
|
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")
|
TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_this_large_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x: Part | Folder | Instance | string | Vector3 | any)
|
local function f(x: Part | Folder | Instance | string | Vector3 | any)
|
||||||
if typeof(x) == "Instance" then
|
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")
|
TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!nonstrict
|
--!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")
|
TEST_CASE_FIXTURE(RefinementClassFixture, "x_is_not_instance_or_else_not_part")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauOrPredicate", true},
|
|
||||||
{"LuauTypeGuardPeelsAwaySubclasses", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x: Part | Folder | string)
|
local function f(x: Part | Folder | string)
|
||||||
if typeof(x) ~= "Instance" or not x:IsA("Part") then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: number?, b: number?)
|
local function f(a: number?, b: number?)
|
||||||
if (not a) or (not b) then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: number?, b: number?)
|
local function f(a: number?, b: number?)
|
||||||
if not (a and b) then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: number?, b: number?)
|
local function f(a: number?, b: number?)
|
||||||
if (not a) and (not b) then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: number?, b: number?)
|
local function f(a: number?, b: number?)
|
||||||
if not (a or b) then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "either_number_or_string")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x: any)
|
local function f(x: any)
|
||||||
if type(x) == "number" or type(x) == "string" then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(t: {x: boolean}?)
|
local function f(t: {x: boolean}?)
|
||||||
if not t or t.x then
|
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")
|
TEST_CASE_FIXTURE(Fixture, "assert_a_to_be_truthy_then_assert_a_to_be_number")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOrPredicate", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a: (number | string)?
|
local a: (number | string)?
|
||||||
assert(a)
|
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")
|
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.
|
// 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"(
|
CheckResult result = check(R"(
|
||||||
local function f(b: string | { x: string }, a)
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: string | number | boolean)
|
local function f(a: string | number | boolean)
|
||||||
if type(a) ~= "number" and type(a) ~= "string" then
|
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);
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -3926,8 +3926,6 @@ local b: number = 1 or a
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local tbl = {}
|
local tbl = {}
|
||||||
|
@ -4493,10 +4491,6 @@ f(function(x) print(x) end)
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
|
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)
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function g1<T>(a: T, f: (T) -> T) return f(a) end
|
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
|
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")
|
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"(
|
CheckResult result = check(R"(
|
||||||
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "infer_return_value_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(): {string|number}
|
local function f(): {string|number}
|
||||||
return {1, "b", 3}
|
return {1, "b", 3}
|
||||||
|
@ -4625,8 +4609,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_type_assertion_value_type")
|
TEST_CASE_FIXTURE(Fixture, "infer_type_assertion_value_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f()
|
local function f()
|
||||||
return {4, "b", 3} :: {string|number}
|
return {4, "b", 3} :: {string|number}
|
||||||
|
@ -4638,8 +4620,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types")
|
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a: (number, number) -> number = function(a, b) return a - b end
|
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")
|
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types_mutable_lval")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = {}
|
local a = {}
|
||||||
a.x = 2
|
a.x = 2
|
||||||
|
@ -4668,8 +4646,6 @@ a = setmetatable(a, { __call = function(x) end })
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "refine_and_or")
|
TEST_CASE_FIXTURE(Fixture, "refine_and_or")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauSlightlyMoreFlexibleBinaryPredicates", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: {x: number?}? = {x = nil}
|
local t: {x: number?}? = {x = nil}
|
||||||
local u = t and t.x or 5
|
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")
|
TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: {x: number?}? = {x = nil}
|
local t: {x: number?}? = {x = nil}
|
||||||
local u = t.x and t or 5
|
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")
|
TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: {x: number?}? = {x = nil}
|
local t: {x: number?}? = {x = nil}
|
||||||
local u = t and t.x == 5 or t.x == 31337
|
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")
|
TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauFollowInTypeFunApply("LuauFollowInTypeFunApply", true);
|
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number }
|
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")
|
TEST_CASE_FIXTURE(TryUnifyFixture, "variadics_should_use_reversed_properly")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
|
|
||||||
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local function f<T>(...: T): ...T
|
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")
|
TEST_CASE_FIXTURE(TryUnifyFixture, "cli_41095_concat_log_in_sealed_table_unification")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs2("LuauGenericFunctions", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
table.insert()
|
table.insert()
|
||||||
|
|
|
@ -296,7 +296,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
@ -361,7 +360,6 @@ local c: Packed<string, number, boolean>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_import")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_import")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
@ -395,7 +393,6 @@ local d: { a: typeof(c) }
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_pack_type_parameters")
|
TEST_CASE_FIXTURE(Fixture, "type_pack_type_parameters")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", 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")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_nested")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", 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")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_variadic")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
@ -475,7 +470,6 @@ type E = X<(number, ...string)>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_multi")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_multi")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", 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")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
@ -534,7 +527,6 @@ type F = X<(string, ...number)>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", 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")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi_tostring")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
ScopedFastFlag luauInstantiatedTypeParamRecursion("LuauInstantiatedTypeParamRecursion", true); // For correct toString block
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Y<T..., U...> = { f: (T...) -> (U...) }
|
type Y<T..., U...> = { f: (T...) -> (U...) }
|
||||||
|
@ -577,7 +567,6 @@ local b: Y<(), ()>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_backwards_compatible")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_backwards_compatible")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
@ -599,7 +588,6 @@ type C = Y<(number), boolean>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_errors")
|
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_errors")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
|
|
||||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||||
|
|
||||||
|
|
|
@ -400,8 +400,6 @@ local e = a.z
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauSealedTableUnifyOptionalFix("LuauSealedTableUnifyOptionalFix", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: { x: number } = { x = 3 }
|
local x: { x: number } = { x = 3 }
|
||||||
type A = number?
|
type A = number?
|
||||||
|
@ -426,4 +424,43 @@ y = x
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauGenericFunctions);
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeVarTests");
|
TEST_SUITE_BEGIN("TypeVarTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "primitives_are_equal")
|
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, 31) == 2^31 - 1)
|
||||||
assert(bit32.replace(-1, 0, 1, 2) == 2^32 - 7)
|
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
|
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
|
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.bxor(1, "3") == 2)
|
||||||
assert(bit32.btest(1, "3") == true)
|
assert(bit32.btest(1, "3") == true)
|
||||||
assert(bit32.btest("1", 3) == true)
|
assert(bit32.btest("1", 3) == true)
|
||||||
|
assert(bit32.countlz("42") == 26)
|
||||||
|
assert(bit32.countrz("42") == 1)
|
||||||
|
|
||||||
return('OK')
|
return('OK')
|
||||||
|
|
Loading…
Reference in a new issue