mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Merge branch 'Roblox:master' into master
This commit is contained in:
commit
1bf0c75cfd
72 changed files with 1491 additions and 1903 deletions
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
|
@ -27,13 +27,13 @@ jobs:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: make test
|
- name: make test
|
||||||
run: |
|
run: |
|
||||||
make -j2 config=sanitize test
|
make -j2 config=sanitize werror=1 test
|
||||||
- name: make test w/flags
|
- name: make test w/flags
|
||||||
run: |
|
run: |
|
||||||
make -j2 config=sanitize flags=true test
|
make -j2 config=sanitize werror=1 flags=true test
|
||||||
- name: make cli
|
- name: make cli
|
||||||
run: |
|
run: |
|
||||||
make -j2 config=sanitize luau luau-analyze # match config with tests to improve build time
|
make -j2 config=sanitize werror=1 luau luau-analyze # match config with tests to improve build time
|
||||||
./luau tests/conformance/assert.lua
|
./luau tests/conformance/assert.lua
|
||||||
./luau-analyze tests/conformance/assert.lua
|
./luau-analyze tests/conformance/assert.lua
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: cmake configure
|
- name: cmake configure
|
||||||
run: cmake . -A ${{matrix.arch}}
|
run: cmake . -A ${{matrix.arch}} -DLUAU_WERROR=ON
|
||||||
- name: cmake test
|
- name: cmake test
|
||||||
shell: bash # necessary for fail-fast
|
shell: bash # necessary for fail-fast
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -368,22 +367,12 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
auto endIter = end(u);
|
auto endIter = end(u);
|
||||||
|
|
||||||
while (iter != endIter)
|
while (iter != endIter)
|
||||||
{
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
|
||||||
{
|
{
|
||||||
if (isNil(*iter))
|
if (isNil(*iter))
|
||||||
++iter;
|
++iter;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (auto primTy = Luau::get<PrimitiveTypeVar>(*iter); primTy && primTy->type == PrimitiveTypeVar::NilType)
|
|
||||||
++iter;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iter == endIter)
|
if (iter == endIter)
|
||||||
return;
|
return;
|
||||||
|
@ -397,22 +386,11 @@ 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;
|
++iter;
|
||||||
continue;
|
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);
|
||||||
|
|
||||||
|
@ -496,7 +474,7 @@ static bool canSuggestInferredType(ScopePtr scope, TypeId ty)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// No syntax for unnamed tables with a metatable
|
// No syntax for unnamed tables with a metatable
|
||||||
if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
if (get<MetatableTypeVar>(ty))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
||||||
|
@ -688,7 +666,7 @@ static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* n
|
||||||
|
|
||||||
TypeId expectedType = follow(*it);
|
TypeId expectedType = follow(*it);
|
||||||
|
|
||||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(expectedType))
|
if (get<FunctionTypeVar>(expectedType))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(expectedType))
|
if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(expectedType))
|
||||||
|
@ -1519,9 +1497,9 @@ 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)
|
||||||
|
@ -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,17 +732,16 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -709,6 +750,9 @@ void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
|
||||||
{
|
{
|
||||||
e.wantedType = clone(e.wantedType);
|
e.wantedType = clone(e.wantedType);
|
||||||
e.givenType = clone(e.givenType);
|
e.givenType = clone(e.givenType);
|
||||||
|
|
||||||
|
if (e.error)
|
||||||
|
visit(visitErrorData, e.error->data);
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, UnknownSymbol>)
|
else if constexpr (std::is_same_v<T, UnknownSymbol>)
|
||||||
{
|
{
|
||||||
|
@ -814,6 +858,15 @@ void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
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)
|
||||||
{
|
|
||||||
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
|
||||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
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,7 +397,6 @@ 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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
|
||||||
state.emit(state.getName(ty));
|
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,17 +797,9 @@ struct TypePackStringifier
|
||||||
void operator()(TypePackId tp, const FreeTypePack& pack)
|
void operator()(TypePackId tp, const FreeTypePack& pack)
|
||||||
{
|
{
|
||||||
state.result.invalid = true;
|
state.result.invalid = true;
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
|
||||||
{
|
|
||||||
state.emit(state.getName(tp));
|
state.emit(state.getName(tp));
|
||||||
state.emit("...");
|
state.emit("...");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
state.emit("<FREETP>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(TypePackId, const BoundTypePack& btv)
|
void operator()(TypePackId, const BoundTypePack& btv)
|
||||||
{
|
{
|
||||||
|
@ -864,8 +832,6 @@ 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 we have a cycle type in type parameters, assign a cycle name for this named table
|
||||||
|
@ -876,12 +842,6 @@ static void assignCycleNames(const std::unordered_set<TypeId>& cycles, const std
|
||||||
|
|
||||||
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);
|
||||||
++nextIndex;
|
++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>())
|
||||||
{
|
{
|
||||||
|
if (!forVarArg)
|
||||||
writer.symbol("...");
|
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.size = ftv.generics.size();
|
||||||
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
|
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
|
||||||
size_t i = 0;
|
size_t numGenerics = 0;
|
||||||
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
|
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
|
||||||
{
|
{
|
||||||
if (auto gtv = get<GenericTypeVar>(*it))
|
if (auto gtv = get<GenericTypeVar>(*it))
|
||||||
generics.data[i++] = AstName(gtv->name.c_str());
|
generics.data[numGenerics++] = 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.size = ftv.genericPacks.size();
|
||||||
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
|
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
|
||||||
size_t i = 0;
|
size_t numGenericPacks = 0;
|
||||||
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
|
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
|
||||||
{
|
{
|
||||||
if (auto gtv = get<GenericTypeVar>(*it))
|
if (auto gtv = get<GenericTypeVar>(*it))
|
||||||
genericPacks.data[i++] = AstName(gtv->name.c_str());
|
genericPacks.data[numGenericPacks++] = AstName(gtv->name.c_str());
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
generics.size = 0;
|
|
||||||
generics.data = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstArray<AstType*> argTypes;
|
AstArray<AstType*> argTypes;
|
||||||
|
|
|
@ -22,29 +22,19 @@ LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 500)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
|
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
|
||||||
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctions, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGenericVariadicsUnification, false)
|
|
||||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauClassPropertyAccessAsString, false)
|
LUAU_FASTFLAGVARIABLE(LuauClassPropertyAccessAsString, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
||||||
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneCorrectlyBeforeMutatingTableType, false)
|
LUAU_FASTFLAGVARIABLE(LuauCloneCorrectlyBeforeMutatingTableType, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreMatchingOverloadFnType, false)
|
LUAU_FASTFLAGVARIABLE(LuauStoreMatchingOverloadFnType, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRankNTypes, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauOrPredicate, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInferReturnAssertAssign, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAddMissingFollow, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeGuardPeelsAwaySubclasses, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSlightlyMoreFlexibleBinaryPredicates, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowInTypeFunApply, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionAnalysisSupport, false)
|
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionAnalysisSupport, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStrictRequire, false)
|
LUAU_FASTFLAGVARIABLE(LuauStrictRequire, false)
|
||||||
LUAU_FASTFLAG(LuauSubstitutionDontReplaceIgnoredTypes)
|
LUAU_FASTFLAG(LuauSubstitutionDontReplaceIgnoredTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
||||||
LUAU_FASTFLAG(LuauNewRequireTrace)
|
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -222,9 +212,9 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan
|
||||||
, threadType(singletonTypes.threadType)
|
, threadType(singletonTypes.threadType)
|
||||||
, anyType(singletonTypes.anyType)
|
, anyType(singletonTypes.anyType)
|
||||||
, errorType(singletonTypes.errorType)
|
, errorType(singletonTypes.errorType)
|
||||||
, optionalNumberType(globalTypes.addType(UnionTypeVar{{numberType, nilType}}))
|
, optionalNumberType(singletonTypes.optionalNumberType)
|
||||||
, anyTypePack(globalTypes.addTypePack(TypePackVar{VariadicTypePack{singletonTypes.anyType}, true}))
|
, anyTypePack(singletonTypes.anyTypePack)
|
||||||
, errorTypePack(globalTypes.addTypePack(TypePackVar{Unifiable::Error{}}))
|
, errorTypePack(singletonTypes.errorTypePack)
|
||||||
{
|
{
|
||||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||||
|
|
||||||
|
@ -251,10 +241,8 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
|
||||||
|
|
||||||
if (module.cyclic)
|
if (module.cyclic)
|
||||||
moduleScope->returnType = addTypePack(TypePack{{anyType}, std::nullopt});
|
moduleScope->returnType = addTypePack(TypePack{{anyType}, std::nullopt});
|
||||||
else if (FFlag::LuauRankNTypes)
|
|
||||||
moduleScope->returnType = freshTypePack(moduleScope);
|
|
||||||
else
|
else
|
||||||
moduleScope->returnType = DEPRECATED_freshTypePack(moduleScope, true);
|
moduleScope->returnType = freshTypePack(moduleScope);
|
||||||
|
|
||||||
moduleScope->varargPack = anyTypePack;
|
moduleScope->varargPack = anyTypePack;
|
||||||
|
|
||||||
|
@ -268,7 +256,7 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
|
||||||
|
|
||||||
checkBlock(moduleScope, *module.root);
|
checkBlock(moduleScope, *module.root);
|
||||||
|
|
||||||
if (get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(moduleScope->returnType) : moduleScope->returnType))
|
if (get<FreeTypePack>(follow(moduleScope->returnType)))
|
||||||
moduleScope->returnType = addTypePack(TypePack{{}, std::nullopt});
|
moduleScope->returnType = addTypePack(TypePack{{}, std::nullopt});
|
||||||
else
|
else
|
||||||
moduleScope->returnType = anyify(moduleScope, moduleScope->returnType, Location{});
|
moduleScope->returnType = anyify(moduleScope, moduleScope->returnType, Location{});
|
||||||
|
@ -326,7 +314,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStat& program)
|
||||||
check(scope, *typealias);
|
check(scope, *typealias);
|
||||||
else if (auto global = program.as<AstStatDeclareGlobal>())
|
else if (auto global = program.as<AstStatDeclareGlobal>())
|
||||||
{
|
{
|
||||||
TypeId globalType = (FFlag::LuauRankNTypes ? resolveType(scope, *global->type) : resolveType(scope, *global->type, true));
|
TypeId globalType = resolveType(scope, *global->type);
|
||||||
Name globalName(global->name.value);
|
Name globalName(global->name.value);
|
||||||
|
|
||||||
currentModule->declaredGlobals[globalName] = globalType;
|
currentModule->declaredGlobals[globalName] = globalType;
|
||||||
|
@ -494,7 +482,7 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std
|
||||||
|
|
||||||
Name name = typealias->name.value;
|
Name name = typealias->name.value;
|
||||||
TypeId type = bindings[name].type;
|
TypeId type = bindings[name].type;
|
||||||
if (get<FreeTypeVar>(FFlag::LuauAddMissingFollow ? follow(type) : type))
|
if (get<FreeTypeVar>(follow(type)))
|
||||||
{
|
{
|
||||||
*asMutable(type) = ErrorTypeVar{};
|
*asMutable(type) = ErrorTypeVar{};
|
||||||
reportError(TypeError{typealias->location, OccursCheckFailed{}});
|
reportError(TypeError{typealias->location, OccursCheckFailed{}});
|
||||||
|
@ -607,9 +595,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatRepeat& statement)
|
||||||
void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
|
void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
|
||||||
{
|
{
|
||||||
std::vector<std::optional<TypeId>> expectedTypes;
|
std::vector<std::optional<TypeId>> expectedTypes;
|
||||||
|
|
||||||
if (FFlag::LuauInferReturnAssertAssign)
|
|
||||||
{
|
|
||||||
expectedTypes.reserve(return_.list.size);
|
expectedTypes.reserve(return_.list.size);
|
||||||
|
|
||||||
TypePackIterator expectedRetCurr = begin(scope->returnType);
|
TypePackIterator expectedRetCurr = begin(scope->returnType);
|
||||||
|
@ -628,7 +613,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
|
||||||
expectedTypes.push_back(vtp->ty);
|
expectedTypes.push_back(vtp->ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type;
|
TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type;
|
||||||
|
|
||||||
|
@ -672,9 +656,6 @@ ErrorVec TypeChecker::tryUnify_(Id left, Id right, const Location& location)
|
||||||
void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
||||||
{
|
{
|
||||||
std::vector<std::optional<TypeId>> expectedTypes;
|
std::vector<std::optional<TypeId>> expectedTypes;
|
||||||
|
|
||||||
if (FFlag::LuauInferReturnAssertAssign)
|
|
||||||
{
|
|
||||||
expectedTypes.reserve(assign.vars.size);
|
expectedTypes.reserve(assign.vars.size);
|
||||||
|
|
||||||
ScopePtr moduleScope = currentModule->getModuleScope();
|
ScopePtr moduleScope = currentModule->getModuleScope();
|
||||||
|
@ -701,7 +682,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
||||||
expectedTypes.push_back(checkLValue(scope, *dest));
|
expectedTypes.push_back(checkLValue(scope, *dest));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId valuePack = checkExprList(scope, assign.location, assign.values, false, {}, expectedTypes).type;
|
TypePackId valuePack = checkExprList(scope, assign.location, assign.values, false, {}, expectedTypes).type;
|
||||||
|
|
||||||
|
@ -715,7 +695,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
||||||
AstExpr* dest = assign.vars.data[i];
|
AstExpr* dest = assign.vars.data[i];
|
||||||
TypeId left = nullptr;
|
TypeId left = nullptr;
|
||||||
|
|
||||||
if (!FFlag::LuauInferReturnAssertAssign || dest->is<AstExprLocal>() || dest->is<AstExprGlobal>())
|
if (dest->is<AstExprLocal>() || dest->is<AstExprGlobal>())
|
||||||
left = checkLValue(scope, *dest);
|
left = checkLValue(scope, *dest);
|
||||||
else
|
else
|
||||||
left = *expectedTypes[i];
|
left = *expectedTypes[i];
|
||||||
|
@ -751,11 +731,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
||||||
|
|
||||||
if (right)
|
if (right)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauGenericFunctions && !maybeGeneric(left) && isGeneric(right))
|
if (!maybeGeneric(left) && isGeneric(right))
|
||||||
right = instantiate(scope, right, loc);
|
|
||||||
|
|
||||||
if (!FFlag::LuauGenericFunctions && get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(left) : left) &&
|
|
||||||
get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(right) : right))
|
|
||||||
right = instantiate(scope, right, loc);
|
right = instantiate(scope, right, loc);
|
||||||
|
|
||||||
// Setting a table entry to nil doesn't mean nil is the type of the indexer, it is just deleting the entry
|
// Setting a table entry to nil doesn't mean nil is the type of the indexer, it is just deleting the entry
|
||||||
|
@ -766,7 +742,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
||||||
if (!destTableTypeReceivingNil || !destTableTypeReceivingNil->indexer)
|
if (!destTableTypeReceivingNil || !destTableTypeReceivingNil->indexer)
|
||||||
{
|
{
|
||||||
// In nonstrict mode, any assignments where the lhs is free and rhs isn't a function, we give it any typevar.
|
// In nonstrict mode, any assignments where the lhs is free and rhs isn't a function, we give it any typevar.
|
||||||
if (isNonstrictMode() && get<FreeTypeVar>(FFlag::LuauAddMissingFollow ? follow(left) : left) && !get<FunctionTypeVar>(follow(right)))
|
if (isNonstrictMode() && get<FreeTypeVar>(follow(left)) && !get<FunctionTypeVar>(follow(right)))
|
||||||
unify(left, anyType, loc);
|
unify(left, anyType, loc);
|
||||||
else
|
else
|
||||||
unify(left, right, loc);
|
unify(left, right, loc);
|
||||||
|
@ -815,7 +791,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
||||||
|
|
||||||
if (annotation)
|
if (annotation)
|
||||||
{
|
{
|
||||||
ty = (FFlag::LuauRankNTypes ? resolveType(scope, *annotation) : resolveType(scope, *annotation, true));
|
ty = resolveType(scope, *annotation);
|
||||||
|
|
||||||
// If the annotation type has an error, treat it as if there was no annotation
|
// If the annotation type has an error, treat it as if there was no annotation
|
||||||
if (get<ErrorTypeVar>(follow(ty)))
|
if (get<ErrorTypeVar>(follow(ty)))
|
||||||
|
@ -823,23 +799,19 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ty)
|
if (!ty)
|
||||||
ty = rhsIsTable ? (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true))
|
ty = rhsIsTable ? freshType(scope) : isNonstrictMode() ? anyType : freshType(scope);
|
||||||
: isNonstrictMode() ? anyType : (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
|
||||||
|
|
||||||
varBindings.emplace_back(vars[i], Binding{ty, vars[i]->location});
|
varBindings.emplace_back(vars[i], Binding{ty, vars[i]->location});
|
||||||
|
|
||||||
variableTypes.push_back(ty);
|
variableTypes.push_back(ty);
|
||||||
expectedTypes.push_back(ty);
|
expectedTypes.push_back(ty);
|
||||||
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
instantiateGenerics.push_back(annotation != nullptr && !maybeGeneric(ty));
|
instantiateGenerics.push_back(annotation != nullptr && !maybeGeneric(ty));
|
||||||
else
|
|
||||||
instantiateGenerics.push_back(annotation != nullptr && get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local.values.size > 0)
|
if (local.values.size > 0)
|
||||||
{
|
{
|
||||||
TypePackId variablePack = addTypePack(variableTypes, FFlag::LuauRankNTypes ? freshTypePack(scope) : DEPRECATED_freshTypePack(scope, true));
|
TypePackId variablePack = addTypePack(variableTypes, freshTypePack(scope));
|
||||||
TypePackId valuePack =
|
TypePackId valuePack =
|
||||||
checkExprList(scope, local.location, local.values, /* substituteFreeForNil= */ true, instantiateGenerics, expectedTypes).type;
|
checkExprList(scope, local.location, local.values, /* substituteFreeForNil= */ true, instantiateGenerics, expectedTypes).type;
|
||||||
|
|
||||||
|
@ -979,8 +951,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
||||||
{
|
{
|
||||||
AstExprCall* exprCall = firstValue->as<AstExprCall>();
|
AstExprCall* exprCall = firstValue->as<AstExprCall>();
|
||||||
callRetPack = checkExprPack(scope, *exprCall).type;
|
callRetPack = checkExprPack(scope, *exprCall).type;
|
||||||
if (!FFlag::LuauRankNTypes)
|
|
||||||
callRetPack = DEPRECATED_instantiate(scope, callRetPack, exprCall->location);
|
|
||||||
callRetPack = follow(callRetPack);
|
callRetPack = follow(callRetPack);
|
||||||
|
|
||||||
if (get<Unifiable::Free>(callRetPack))
|
if (get<Unifiable::Free>(callRetPack))
|
||||||
|
@ -998,7 +968,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iterTy = *first(callRetPack);
|
iterTy = *first(callRetPack);
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
iterTy = instantiate(scope, iterTy, exprCall->location);
|
iterTy = instantiate(scope, iterTy, exprCall->location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1158,10 +1127,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
||||||
|
|
||||||
checkFunctionBody(funScope, ty, *function.func);
|
checkFunctionBody(funScope, ty, *function.func);
|
||||||
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
scope->bindings[function.name] = {quantify(funScope, ty, function.name->location), function.name->location};
|
scope->bindings[function.name] = {quantify(funScope, ty, function.name->location), function.name->location};
|
||||||
else
|
|
||||||
scope->bindings[function.name] = {quantify(scope, ty, function.name->location), function.name->location};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel, bool forwardDeclare)
|
void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel, bool forwardDeclare)
|
||||||
|
@ -1199,7 +1165,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||||
{
|
{
|
||||||
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks);
|
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks);
|
||||||
|
|
||||||
TypeId ty = (FFlag::LuauRankNTypes ? freshType(aliasScope) : DEPRECATED_freshType(scope, true));
|
TypeId ty = freshType(aliasScope);
|
||||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
ftv->forwardedTypeAlias = true;
|
ftv->forwardedTypeAlias = true;
|
||||||
|
@ -1234,7 +1200,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||||
aliasScope->privateTypeBindings[n] = TypeFun{{}, g};
|
aliasScope->privateTypeBindings[n] = TypeFun{{}, g};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ty = (FFlag::LuauRankNTypes ? freshType(aliasScope) : DEPRECATED_freshType(scope, true));
|
TypeId ty = freshType(aliasScope);
|
||||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
ftv->forwardedTypeAlias = true;
|
ftv->forwardedTypeAlias = true;
|
||||||
|
@ -1266,7 +1232,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ty = (FFlag::LuauRankNTypes ? resolveType(aliasScope, *typealias.type) : resolveType(aliasScope, *typealias.type, true));
|
TypeId ty = resolveType(aliasScope, *typealias.type);
|
||||||
if (auto ttv = getMutable<TableTypeVar>(follow(ty)))
|
if (auto ttv = getMutable<TableTypeVar>(follow(ty)))
|
||||||
{
|
{
|
||||||
// If the table is already named and we want to rename the type function, we have to bind new alias to a copy
|
// If the table is already named and we want to rename the type function, we have to bind new alias to a copy
|
||||||
|
@ -1325,27 +1291,14 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
||||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && (!FFlag::LuauTypeAliasPacks || lookupType->typePackParams.size() == 0));
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && (!FFlag::LuauTypeAliasPacks || lookupType->typePackParams.size() == 0));
|
||||||
superTy = lookupType->type;
|
superTy = lookupType->type;
|
||||||
|
|
||||||
if (FFlag::LuauAddMissingFollow)
|
|
||||||
{
|
|
||||||
if (!get<ClassTypeVar>(follow(*superTy)))
|
if (!get<ClassTypeVar>(follow(*superTy)))
|
||||||
{
|
{
|
||||||
reportError(declaredClass.location, GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'",
|
reportError(declaredClass.location,
|
||||||
superName.c_str(), declaredClass.name.value)});
|
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass.name.value)});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (const ClassTypeVar* superCtv = get<ClassTypeVar>(*superTy); !superCtv)
|
|
||||||
{
|
|
||||||
reportError(declaredClass.location, GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'",
|
|
||||||
superName.c_str(), declaredClass.name.value)});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Name className(declaredClass.name.value);
|
Name className(declaredClass.name.value);
|
||||||
|
|
||||||
|
@ -1556,10 +1509,10 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprVa
|
||||||
std::vector<TypeId> types = flatten(varargPack).first;
|
std::vector<TypeId> types = flatten(varargPack).first;
|
||||||
return {!types.empty() ? types[0] : nilType};
|
return {!types.empty() ? types[0] : nilType};
|
||||||
}
|
}
|
||||||
else if (auto ftp = get<FreeTypePack>(varargPack))
|
else if (get<FreeTypePack>(varargPack))
|
||||||
{
|
{
|
||||||
TypeId head = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, ftp->DEPRECATED_canBeGeneric));
|
TypeId head = freshType(scope);
|
||||||
TypePackId tail = (FFlag::LuauRankNTypes ? freshTypePack(scope) : DEPRECATED_freshTypePack(scope, ftp->DEPRECATED_canBeGeneric));
|
TypePackId tail = freshTypePack(scope);
|
||||||
*asMutable(varargPack) = TypePack{{head}, tail};
|
*asMutable(varargPack) = TypePack{{head}, tail};
|
||||||
return {head};
|
return {head};
|
||||||
}
|
}
|
||||||
|
@ -1567,7 +1520,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprVa
|
||||||
return {errorType};
|
return {errorType};
|
||||||
else if (auto vtp = get<VariadicTypePack>(varargPack))
|
else if (auto vtp = get<VariadicTypePack>(varargPack))
|
||||||
return {vtp->ty};
|
return {vtp->ty};
|
||||||
else if (FFlag::LuauGenericVariadicsUnification && get<Unifiable::Generic>(varargPack))
|
else if (get<Unifiable::Generic>(varargPack))
|
||||||
{
|
{
|
||||||
// TODO: Better error?
|
// TODO: Better error?
|
||||||
reportError(expr.location, GenericError{"Trying to get a type from a variadic type parameter"});
|
reportError(expr.location, GenericError{"Trying to get a type from a variadic type parameter"});
|
||||||
|
@ -1586,9 +1539,9 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprCa
|
||||||
{
|
{
|
||||||
return {pack->head.empty() ? nilType : pack->head[0], std::move(result.predicates)};
|
return {pack->head.empty() ? nilType : pack->head[0], std::move(result.predicates)};
|
||||||
}
|
}
|
||||||
else if (auto ftp = get<Unifiable::Free>(retPack))
|
else if (get<Unifiable::Free>(retPack))
|
||||||
{
|
{
|
||||||
TypeId head = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, ftp->DEPRECATED_canBeGeneric));
|
TypeId head = freshType(scope);
|
||||||
TypePackId pack = addTypePack(TypePackVar{TypePack{{head}, freshTypePack(scope)}});
|
TypePackId pack = addTypePack(TypePackVar{TypePack{{head}, freshTypePack(scope)}});
|
||||||
unify(retPack, pack, expr.location);
|
unify(retPack, pack, expr.location);
|
||||||
return {head, std::move(result.predicates)};
|
return {head, std::move(result.predicates)};
|
||||||
|
@ -1667,7 +1620,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
||||||
}
|
}
|
||||||
else if (tableType->state == TableState::Free)
|
else if (tableType->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId result = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
TypeId result = freshType(scope);
|
||||||
tableType->props[name] = {result};
|
tableType->props[name] = {result};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -2129,7 +2082,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||||
if (!isNonstrictMode() && !isOrOp)
|
if (!isNonstrictMode() && !isOrOp)
|
||||||
return ty;
|
return ty;
|
||||||
|
|
||||||
if (auto i = get<UnionTypeVar>(ty))
|
if (get<UnionTypeVar>(ty))
|
||||||
{
|
{
|
||||||
std::optional<TypeId> cleaned = tryStripUnionFromNil(ty);
|
std::optional<TypeId> cleaned = tryStripUnionFromNil(ty);
|
||||||
|
|
||||||
|
@ -2157,18 +2110,11 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||||
if (AstExprBinary* subexp = expr.left->as<AstExprBinary>())
|
if (AstExprBinary* subexp = expr.left->as<AstExprBinary>())
|
||||||
{
|
{
|
||||||
if (expr.op == AstExprBinary::Or && subexp->op == AstExprBinary::And)
|
if (expr.op == AstExprBinary::Or && subexp->op == AstExprBinary::And)
|
||||||
{
|
|
||||||
if (FFlag::LuauSlightlyMoreFlexibleBinaryPredicates)
|
|
||||||
{
|
{
|
||||||
ScopePtr subScope = childScope(scope, subexp->location);
|
ScopePtr subScope = childScope(scope, subexp->location);
|
||||||
reportErrors(resolve(predicates, subScope, true));
|
reportErrors(resolve(predicates, subScope, true));
|
||||||
return unionOfTypes(rhsType, stripNil(checkExpr(subScope, *subexp->right).type, true), expr.location);
|
return unionOfTypes(rhsType, stripNil(checkExpr(subScope, *subexp->right).type, true), expr.location);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return unionOfTypes(rhsType, checkExpr(scope, *subexp->right).type, expr.location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua casts the results of these to boolean
|
// Lua casts the results of these to boolean
|
||||||
|
@ -2217,10 +2163,8 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||||
|
|
||||||
std::string metamethodName = opToMetaTableEntry(expr.op);
|
std::string metamethodName = opToMetaTableEntry(expr.op);
|
||||||
|
|
||||||
std::optional<TypeId> leftMetatable =
|
std::optional<TypeId> leftMetatable = isString(lhsType) ? std::nullopt : getMetatable(follow(lhsType));
|
||||||
isString(lhsType) ? std::nullopt : getMetatable(FFlag::LuauAddMissingFollow ? follow(lhsType) : lhsType);
|
std::optional<TypeId> rightMetatable = isString(rhsType) ? std::nullopt : getMetatable(follow(rhsType));
|
||||||
std::optional<TypeId> rightMetatable =
|
|
||||||
isString(rhsType) ? std::nullopt : getMetatable(FFlag::LuauAddMissingFollow ? follow(rhsType) : rhsType);
|
|
||||||
|
|
||||||
if (bool(leftMetatable) != bool(rightMetatable) && leftMetatable != rightMetatable)
|
if (bool(leftMetatable) != bool(rightMetatable) && leftMetatable != rightMetatable)
|
||||||
{
|
{
|
||||||
|
@ -2266,7 +2210,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<FreeTypeVar>(FFlag::LuauAddMissingFollow ? follow(lhsType) : lhsType) && !isEquality)
|
if (get<FreeTypeVar>(follow(lhsType)) && !isEquality)
|
||||||
{
|
{
|
||||||
auto name = getIdentifierOfBaseVar(expr.left);
|
auto name = getIdentifierOfBaseVar(expr.left);
|
||||||
reportError(expr.location, CannotInferBinaryOperation{expr.op, name, CannotInferBinaryOperation::Comparison});
|
reportError(expr.location, CannotInferBinaryOperation{expr.op, name, CannotInferBinaryOperation::Comparison});
|
||||||
|
@ -2417,12 +2361,10 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
|
||||||
resolve(lhs.predicates, innerScope, true);
|
resolve(lhs.predicates, innerScope, true);
|
||||||
|
|
||||||
ExprResult<TypeId> rhs = checkExpr(innerScope, *expr.right);
|
ExprResult<TypeId> rhs = checkExpr(innerScope, *expr.right);
|
||||||
if (!FFlag::LuauSlightlyMoreFlexibleBinaryPredicates)
|
|
||||||
resolve(rhs.predicates, innerScope, true);
|
|
||||||
|
|
||||||
return {checkBinaryOperation(innerScope, expr, lhs.type, rhs.type), {AndPredicate{std::move(lhs.predicates), std::move(rhs.predicates)}}};
|
return {checkBinaryOperation(innerScope, expr, lhs.type, rhs.type), {AndPredicate{std::move(lhs.predicates), std::move(rhs.predicates)}}};
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauOrPredicate && expr.op == AstExprBinary::Or)
|
else if (expr.op == AstExprBinary::Or)
|
||||||
{
|
{
|
||||||
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left);
|
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left);
|
||||||
|
|
||||||
|
@ -2468,19 +2410,8 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
|
||||||
|
|
||||||
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr)
|
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr)
|
||||||
{
|
{
|
||||||
ExprResult<TypeId> result;
|
TypeId annotationType = resolveType(scope, *expr.annotation);
|
||||||
TypeId annotationType;
|
ExprResult<TypeId> result = checkExpr(scope, *expr.expr, annotationType);
|
||||||
|
|
||||||
if (FFlag::LuauInferReturnAssertAssign)
|
|
||||||
{
|
|
||||||
annotationType = (FFlag::LuauRankNTypes ? resolveType(scope, *expr.annotation) : resolveType(scope, *expr.annotation, true));
|
|
||||||
result = checkExpr(scope, *expr.expr, annotationType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = checkExpr(scope, *expr.expr);
|
|
||||||
annotationType = (FFlag::LuauRankNTypes ? resolveType(scope, *expr.annotation) : resolveType(scope, *expr.annotation, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorVec errorVec = canUnify(result.type, annotationType, expr.location);
|
ErrorVec errorVec = canUnify(result.type, annotationType, expr.location);
|
||||||
if (!errorVec.empty())
|
if (!errorVec.empty())
|
||||||
|
@ -2570,10 +2501,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
if (it != moduleScope->bindings.end())
|
if (it != moduleScope->bindings.end())
|
||||||
return std::pair(it->second.typeId, &it->second.typeId);
|
return std::pair(it->second.typeId, &it->second.typeId);
|
||||||
|
|
||||||
if (isNonstrictMode() || FFlag::LuauSecondTypecheckKnowsTheDataModel)
|
TypeId result = freshType(scope);
|
||||||
{
|
|
||||||
TypeId result = (FFlag::LuauGenericFunctions && FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(moduleScope, true));
|
|
||||||
|
|
||||||
Binding& binding = moduleScope->bindings[expr.name];
|
Binding& binding = moduleScope->bindings[expr.name];
|
||||||
binding = {result, expr.location};
|
binding = {result, expr.location};
|
||||||
|
|
||||||
|
@ -2583,10 +2511,6 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
reportError(TypeError{expr.location, UnknownSymbol{name, UnknownSymbol::Binding}});
|
reportError(TypeError{expr.location, UnknownSymbol{name, UnknownSymbol::Binding}});
|
||||||
|
|
||||||
return std::pair(result, &binding.typeId);
|
return std::pair(result, &binding.typeId);
|
||||||
}
|
|
||||||
|
|
||||||
reportError(TypeError{expr.location, UnknownSymbol{name, UnknownSymbol::Binding}});
|
|
||||||
return std::pair(errorType, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr)
|
std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr)
|
||||||
|
@ -2611,7 +2535,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
}
|
}
|
||||||
else if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free)
|
else if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId theType = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
TypeId theType = freshType(scope);
|
||||||
Property& property = lhsTable->props[name];
|
Property& property = lhsTable->props[name];
|
||||||
property.type = theType;
|
property.type = theType;
|
||||||
property.location = expr.indexLocation;
|
property.location = expr.indexLocation;
|
||||||
|
@ -2683,7 +2607,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
|
|
||||||
AstExprConstantString* value = expr.index->as<AstExprConstantString>();
|
AstExprConstantString* value = expr.index->as<AstExprConstantString>();
|
||||||
|
|
||||||
if (value && FFlag::LuauClassPropertyAccessAsString)
|
if (value)
|
||||||
{
|
{
|
||||||
if (const ClassTypeVar* exprClass = get<ClassTypeVar>(exprType))
|
if (const ClassTypeVar* exprClass = get<ClassTypeVar>(exprType))
|
||||||
{
|
{
|
||||||
|
@ -2714,7 +2638,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
}
|
}
|
||||||
else if (exprTable->state == TableState::Unsealed || exprTable->state == TableState::Free)
|
else if (exprTable->state == TableState::Unsealed || exprTable->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId resultType = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
TypeId resultType = freshType(scope);
|
||||||
Property& property = exprTable->props[value->value.data];
|
Property& property = exprTable->props[value->value.data];
|
||||||
property.type = resultType;
|
property.type = resultType;
|
||||||
property.location = expr.index->location;
|
property.location = expr.index->location;
|
||||||
|
@ -2730,7 +2654,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
||||||
}
|
}
|
||||||
else if (exprTable->state == TableState::Unsealed || exprTable->state == TableState::Free)
|
else if (exprTable->state == TableState::Unsealed || exprTable->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId resultType = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
TypeId resultType = freshType(scope);
|
||||||
exprTable->indexer = TableIndexer{anyIfNonstrict(indexType), anyIfNonstrict(resultType)};
|
exprTable->indexer = TableIndexer{anyIfNonstrict(indexType), anyIfNonstrict(resultType)};
|
||||||
return std::pair(resultType, nullptr);
|
return std::pair(resultType, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -2758,7 +2682,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId ty = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
TypeId ty = freshType(scope);
|
||||||
globalScope->bindings[name] = {ty, funName.location};
|
globalScope->bindings[name] = {ty, funName.location};
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
@ -2768,7 +2692,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
||||||
Symbol name = localName->local;
|
Symbol name = localName->local;
|
||||||
Binding& binding = scope->bindings[name];
|
Binding& binding = scope->bindings[name];
|
||||||
if (binding.typeId == nullptr)
|
if (binding.typeId == nullptr)
|
||||||
binding = {(FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true)), funName.location};
|
binding = {freshType(scope), funName.location};
|
||||||
|
|
||||||
return binding.typeId;
|
return binding.typeId;
|
||||||
}
|
}
|
||||||
|
@ -2798,7 +2722,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
||||||
|
|
||||||
Property& property = ttv->props[name];
|
Property& property = ttv->props[name];
|
||||||
|
|
||||||
property.type = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
property.type = freshType(scope);
|
||||||
property.location = indexName->indexLocation;
|
property.location = indexName->indexLocation;
|
||||||
ttv->methodDefinitionLocations[name] = funName.location;
|
ttv->methodDefinitionLocations[name] = funName.location;
|
||||||
return property.type;
|
return property.type;
|
||||||
|
@ -2865,22 +2789,11 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
||||||
expectedFunctionType = nullptr;
|
expectedFunctionType = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeId> generics;
|
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
||||||
std::vector<TypePackId> genericPacks;
|
|
||||||
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
{
|
|
||||||
std::tie(generics, genericPacks) = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId retPack;
|
TypePackId retPack;
|
||||||
if (expr.hasReturnAnnotation)
|
if (expr.hasReturnAnnotation)
|
||||||
{
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
retPack = resolveTypePack(funScope, expr.returnAnnotation);
|
retPack = resolveTypePack(funScope, expr.returnAnnotation);
|
||||||
else
|
|
||||||
retPack = resolveTypePack(scope, expr.returnAnnotation);
|
|
||||||
}
|
|
||||||
else if (isNonstrictMode())
|
else if (isNonstrictMode())
|
||||||
retPack = anyTypePack;
|
retPack = anyTypePack;
|
||||||
else if (expectedFunctionType)
|
else if (expectedFunctionType)
|
||||||
|
@ -2889,24 +2802,17 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
||||||
|
|
||||||
// Do not infer 'nil' as function return type
|
// Do not infer 'nil' as function return type
|
||||||
if (!tail && head.size() == 1 && isNil(head[0]))
|
if (!tail && head.size() == 1 && isNil(head[0]))
|
||||||
retPack = FFlag::LuauGenericFunctions ? freshTypePack(funScope) : freshTypePack(scope);
|
retPack = freshTypePack(funScope);
|
||||||
else
|
else
|
||||||
retPack = addTypePack(head, tail);
|
retPack = addTypePack(head, tail);
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauGenericFunctions)
|
|
||||||
retPack = freshTypePack(funScope);
|
|
||||||
else
|
else
|
||||||
retPack = freshTypePack(scope);
|
retPack = freshTypePack(funScope);
|
||||||
|
|
||||||
if (expr.vararg)
|
if (expr.vararg)
|
||||||
{
|
{
|
||||||
if (expr.varargAnnotation)
|
if (expr.varargAnnotation)
|
||||||
{
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
funScope->varargPack = resolveTypePack(funScope, *expr.varargAnnotation);
|
funScope->varargPack = resolveTypePack(funScope, *expr.varargAnnotation);
|
||||||
else
|
|
||||||
funScope->varargPack = resolveTypePack(scope, *expr.varargAnnotation);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (expectedFunctionType && !isNonstrictMode())
|
if (expectedFunctionType && !isNonstrictMode())
|
||||||
|
@ -2963,7 +2869,7 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
||||||
|
|
||||||
if (local->annotation)
|
if (local->annotation)
|
||||||
{
|
{
|
||||||
argType = resolveType((FFlag::LuauGenericFunctions ? funScope : scope), *local->annotation);
|
argType = resolveType(funScope, *local->annotation);
|
||||||
|
|
||||||
// If the annotation type has an error, treat it as if there was no annotation
|
// If the annotation type has an error, treat it as if there was no annotation
|
||||||
if (get<ErrorTypeVar>(follow(argType)))
|
if (get<ErrorTypeVar>(follow(argType)))
|
||||||
|
@ -3022,7 +2928,7 @@ static bool allowsNoReturnValues(const TypePackId tp)
|
||||||
{
|
{
|
||||||
for (TypeId ty : tp)
|
for (TypeId ty : tp)
|
||||||
{
|
{
|
||||||
if (!get<ErrorTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty))
|
if (!get<ErrorTypeVar>(follow(ty)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3058,7 +2964,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
||||||
check(scope, *function.body);
|
check(scope, *function.body);
|
||||||
|
|
||||||
// We explicitly don't follow here to check if we have a 'true' free type instead of bound one
|
// We explicitly don't follow here to check if we have a 'true' free type instead of bound one
|
||||||
if (FFlag::LuauAddMissingFollow ? get_if<FreeTypePack>(&funTy->retType->ty) : get<FreeTypePack>(funTy->retType))
|
if (get_if<FreeTypePack>(&funTy->retType->ty))
|
||||||
*asMutable(funTy->retType) = TypePack{{}, std::nullopt};
|
*asMutable(funTy->retType) = TypePack{{}, std::nullopt};
|
||||||
|
|
||||||
bool reachesImplicitReturn = getFallthrough(function.body) != nullptr;
|
bool reachesImplicitReturn = getFallthrough(function.body) != nullptr;
|
||||||
|
@ -3287,7 +3193,7 @@ void TypeChecker::checkArgumentList(
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauGenericVariadicsUnification && get<FreeTypePack>(tail))
|
else if (get<FreeTypePack>(tail))
|
||||||
{
|
{
|
||||||
// Create a type pack out of the remaining argument types
|
// Create a type pack out of the remaining argument types
|
||||||
// and unify it with the tail.
|
// and unify it with the tail.
|
||||||
|
@ -3310,7 +3216,7 @@ void TypeChecker::checkArgumentList(
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauRankNTypes && get<GenericTypePack>(tail))
|
else if (get<GenericTypePack>(tail))
|
||||||
{
|
{
|
||||||
// For this case, we want the error span to cover every errant extra parameter
|
// For this case, we want the error span to cover every errant extra parameter
|
||||||
Location location = state.location;
|
Location location = state.location;
|
||||||
|
@ -3323,10 +3229,7 @@ void TypeChecker::checkArgumentList(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
unifyWithInstantiationIfNeeded(scope, *paramIter, *argIter, state);
|
unifyWithInstantiationIfNeeded(scope, *paramIter, *argIter, state);
|
||||||
else
|
|
||||||
state.tryUnify(*paramIter, *argIter, /*isFunctionCall*/ false);
|
|
||||||
++argIter;
|
++argIter;
|
||||||
++paramIter;
|
++paramIter;
|
||||||
}
|
}
|
||||||
|
@ -3356,9 +3259,6 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
|
||||||
ice("method call expression has no 'self'");
|
ice("method call expression has no 'self'");
|
||||||
|
|
||||||
selfType = checkExpr(scope, *indexExpr->expr).type;
|
selfType = checkExpr(scope, *indexExpr->expr).type;
|
||||||
if (!FFlag::LuauRankNTypes)
|
|
||||||
instantiate(scope, selfType, expr.func->location);
|
|
||||||
|
|
||||||
selfType = stripFromNilAndReport(selfType, expr.func->location);
|
selfType = stripFromNilAndReport(selfType, expr.func->location);
|
||||||
|
|
||||||
if (std::optional<TypeId> propTy = getIndexTypeFromType(scope, selfType, indexExpr->index.value, expr.location, true))
|
if (std::optional<TypeId> propTy = getIndexTypeFromType(scope, selfType, indexExpr->index.value, expr.location, true))
|
||||||
|
@ -3393,8 +3293,7 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
|
||||||
std::vector<std::optional<TypeId>> expectedTypes = getExpectedTypesForCall(overloads, expr.args.size, expr.self);
|
std::vector<std::optional<TypeId>> expectedTypes = getExpectedTypesForCall(overloads, expr.args.size, expr.self);
|
||||||
|
|
||||||
ExprResult<TypePackId> argListResult = checkExprList(scope, expr.location, expr.args, false, {}, expectedTypes);
|
ExprResult<TypePackId> argListResult = checkExprList(scope, expr.location, expr.args, false, {}, expectedTypes);
|
||||||
TypePackId argList = argListResult.type;
|
TypePackId argPack = argListResult.type;
|
||||||
TypePackId argPack = (FFlag::LuauRankNTypes ? argList : DEPRECATED_instantiate(scope, argList, expr.location));
|
|
||||||
|
|
||||||
if (get<Unifiable::Error>(argPack))
|
if (get<Unifiable::Error>(argPack))
|
||||||
return ExprResult<TypePackId>{errorTypePack};
|
return ExprResult<TypePackId>{errorTypePack};
|
||||||
|
@ -3526,7 +3425,6 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
|
||||||
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
||||||
|
|
||||||
TypeId fn = *ty;
|
TypeId fn = *ty;
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
fn = instantiate(scope, fn, expr.func->location);
|
fn = instantiate(scope, fn, expr.func->location);
|
||||||
|
|
||||||
return checkCallOverload(
|
return checkCallOverload(
|
||||||
|
@ -3800,7 +3698,7 @@ ExprResult<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, const L
|
||||||
|
|
||||||
TypeId actualType = substituteFreeForNil && expr->is<AstExprConstantNil>() ? freshType(scope) : type;
|
TypeId actualType = substituteFreeForNil && expr->is<AstExprConstantNil>() ? freshType(scope) : type;
|
||||||
|
|
||||||
if (instantiateGenerics.size() > i && instantiateGenerics[i] && (FFlag::LuauGenericFunctions || get<FunctionTypeVar>(actualType)))
|
if (instantiateGenerics.size() > i && instantiateGenerics[i])
|
||||||
actualType = instantiate(scope, actualType, expr->location);
|
actualType = instantiate(scope, actualType, expr->location);
|
||||||
|
|
||||||
if (expectedType)
|
if (expectedType)
|
||||||
|
@ -3837,7 +3735,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
||||||
LUAU_TIMETRACE_SCOPE("TypeChecker::checkRequire", "TypeChecker");
|
LUAU_TIMETRACE_SCOPE("TypeChecker::checkRequire", "TypeChecker");
|
||||||
LUAU_TIMETRACE_ARGUMENT("moduleInfo", moduleInfo.name.c_str());
|
LUAU_TIMETRACE_ARGUMENT("moduleInfo", moduleInfo.name.c_str());
|
||||||
|
|
||||||
if (FFlag::LuauNewRequireTrace && moduleInfo.name.empty())
|
if (FFlag::LuauNewRequireTrace2 && moduleInfo.name.empty())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStrictRequire && currentModule->mode == Mode::Strict)
|
if (FFlag::LuauStrictRequire && currentModule->mode == Mode::Strict)
|
||||||
{
|
{
|
||||||
|
@ -3922,7 +3820,6 @@ bool TypeChecker::unify(TypePackId left, TypePackId right, const Location& locat
|
||||||
|
|
||||||
bool TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, const Location& location)
|
bool TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, const Location& location)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
Unifier state = mkUnifier(location);
|
Unifier state = mkUnifier(location);
|
||||||
unifyWithInstantiationIfNeeded(scope, left, right, state);
|
unifyWithInstantiationIfNeeded(scope, left, right, state);
|
||||||
|
|
||||||
|
@ -3933,7 +3830,6 @@ bool TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId l
|
||||||
|
|
||||||
void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, Unifier& state)
|
void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, Unifier& state)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
if (!maybeGeneric(right))
|
if (!maybeGeneric(right))
|
||||||
// Quick check to see if we definitely can't instantiate
|
// Quick check to see if we definitely can't instantiate
|
||||||
state.tryUnify(left, right, /*isFunctionCall*/ false);
|
state.tryUnify(left, right, /*isFunctionCall*/ false);
|
||||||
|
@ -3973,38 +3869,19 @@ void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId l
|
||||||
|
|
||||||
bool Instantiation::isDirty(TypeId ty)
|
bool Instantiation::isDirty(TypeId ty)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
{
|
|
||||||
if (get<FunctionTypeVar>(ty))
|
if (get<FunctionTypeVar>(ty))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
|
||||||
return !ftv->generics.empty() || !ftv->genericPacks.empty();
|
|
||||||
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
|
||||||
return ttv->state == TableState::Generic;
|
|
||||||
else if (get<GenericTypeVar>(ty))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instantiation::isDirty(TypePackId tp)
|
bool Instantiation::isDirty(TypePackId tp)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (get<GenericTypePack>(tp))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instantiation::ignoreChildren(TypeId ty)
|
bool Instantiation::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
if (get<FunctionTypeVar>(ty))
|
if (get<FunctionTypeVar>(ty))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
|
@ -4013,18 +3890,15 @@ bool Instantiation::ignoreChildren(TypeId ty)
|
||||||
|
|
||||||
TypeId Instantiation::clean(TypeId ty)
|
TypeId Instantiation::clean(TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(isDirty(ty));
|
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
|
||||||
|
LUAU_ASSERT(ftv);
|
||||||
|
|
||||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
|
||||||
{
|
|
||||||
FunctionTypeVar clone = FunctionTypeVar{level, ftv->argTypes, ftv->retType, ftv->definition, ftv->hasSelf};
|
FunctionTypeVar clone = FunctionTypeVar{level, ftv->argTypes, ftv->retType, ftv->definition, ftv->hasSelf};
|
||||||
clone.magicFunction = ftv->magicFunction;
|
clone.magicFunction = ftv->magicFunction;
|
||||||
clone.tags = ftv->tags;
|
clone.tags = ftv->tags;
|
||||||
clone.argNames = ftv->argNames;
|
clone.argNames = ftv->argNames;
|
||||||
TypeId result = addType(std::move(clone));
|
TypeId result = addType(std::move(clone));
|
||||||
|
|
||||||
if (FFlag::LuauRankNTypes)
|
|
||||||
{
|
|
||||||
// Annoyingly, we have to do this even if there are no generics,
|
// Annoyingly, we have to do this even if there are no generics,
|
||||||
// to replace any generic tables.
|
// to replace any generic tables.
|
||||||
replaceGenerics.level = level;
|
replaceGenerics.level = level;
|
||||||
|
@ -4035,41 +3909,19 @@ TypeId Instantiation::clean(TypeId ty)
|
||||||
// TODO: What to do if this returns nullopt?
|
// TODO: What to do if this returns nullopt?
|
||||||
// We don't have access to the error-reporting machinery
|
// We don't have access to the error-reporting machinery
|
||||||
result = replaceGenerics.substitute(result).value_or(result);
|
result = replaceGenerics.substitute(result).value_or(result);
|
||||||
}
|
|
||||||
|
|
||||||
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
|
||||||
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, level, TableState::Free};
|
|
||||||
clone.methodDefinitionLocations = ttv->methodDefinitionLocations;
|
|
||||||
clone.definitionModuleName = ttv->definitionModuleName;
|
|
||||||
TypeId result = addType(std::move(clone));
|
|
||||||
|
|
||||||
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
|
||||||
TypeId result = addType(FreeTypeVar{level});
|
|
||||||
|
|
||||||
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId Instantiation::clean(TypePackId tp)
|
TypePackId Instantiation::clean(TypePackId tp)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
LUAU_ASSERT(false);
|
||||||
return addTypePack(TypePackVar(FreeTypePack{level}));
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||||
// We aren't recursing in the case of a generic function which
|
// We aren't recursing in the case of a generic function which
|
||||||
// binds the same generics. This can happen if, for example, there's recursive types.
|
// binds the same generics. This can happen if, for example, there's recursive types.
|
||||||
|
@ -4083,7 +3935,6 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
||||||
|
|
||||||
bool ReplaceGenerics::isDirty(TypeId ty)
|
bool ReplaceGenerics::isDirty(TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
||||||
return ttv->state == TableState::Generic;
|
return ttv->state == TableState::Generic;
|
||||||
else if (get<GenericTypeVar>(ty))
|
else if (get<GenericTypeVar>(ty))
|
||||||
|
@ -4094,7 +3945,6 @@ bool ReplaceGenerics::isDirty(TypeId ty)
|
||||||
|
|
||||||
bool ReplaceGenerics::isDirty(TypePackId tp)
|
bool ReplaceGenerics::isDirty(TypePackId tp)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauRankNTypes);
|
|
||||||
if (get<GenericTypePack>(tp))
|
if (get<GenericTypePack>(tp))
|
||||||
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
|
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
|
||||||
else
|
else
|
||||||
|
@ -4255,21 +4105,6 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId TypeChecker::DEPRECATED_instantiate(const ScopePtr& scope, TypePackId ty, Location location)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
|
||||||
instantiation.level = scope->level;
|
|
||||||
instantiation.currentModule = currentModule;
|
|
||||||
std::optional<TypePackId> instantiated = instantiation.substitute(ty);
|
|
||||||
if (instantiated.has_value())
|
|
||||||
return *instantiated;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reportError(location, UnificationTooComplex{});
|
|
||||||
return errorTypePack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
||||||
{
|
{
|
||||||
anyification.anyType = anyType;
|
anyification.anyType = anyType;
|
||||||
|
@ -4444,16 +4279,6 @@ TypeId TypeChecker::freshType(TypeLevel level)
|
||||||
return currentModule->internalTypes.addType(TypeVar(FreeTypeVar(level)));
|
return currentModule->internalTypes.addType(TypeVar(FreeTypeVar(level)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeChecker::DEPRECATED_freshType(const ScopePtr& scope, bool canBeGeneric)
|
|
||||||
{
|
|
||||||
return DEPRECATED_freshType(scope->level, canBeGeneric);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId TypeChecker::DEPRECATED_freshType(TypeLevel level, bool canBeGeneric)
|
|
||||||
{
|
|
||||||
return currentModule->internalTypes.addType(TypeVar(FreeTypeVar(level, canBeGeneric)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TypeId> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate)
|
std::optional<TypeId> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate)
|
||||||
{
|
{
|
||||||
std::vector<TypeId> types = Luau::filterMap(type, predicate);
|
std::vector<TypeId> types = Luau::filterMap(type, predicate);
|
||||||
|
@ -4509,21 +4334,8 @@ TypePackId TypeChecker::freshTypePack(TypeLevel level)
|
||||||
return addTypePack(TypePackVar(FreeTypePack(level)));
|
return addTypePack(TypePackVar(FreeTypePack(level)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId TypeChecker::DEPRECATED_freshTypePack(const ScopePtr& scope, bool canBeGeneric)
|
TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation)
|
||||||
{
|
{
|
||||||
return DEPRECATED_freshTypePack(scope->level, canBeGeneric);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId TypeChecker::DEPRECATED_freshTypePack(TypeLevel level, bool canBeGeneric)
|
|
||||||
{
|
|
||||||
return addTypePack(TypePackVar(FreeTypePack(level, canBeGeneric)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation, bool DEPRECATED_canBeGeneric)
|
|
||||||
{
|
|
||||||
if (DEPRECATED_canBeGeneric)
|
|
||||||
LUAU_ASSERT(!FFlag::LuauRankNTypes);
|
|
||||||
|
|
||||||
if (const auto& lit = annotation.as<AstTypeReference>())
|
if (const auto& lit = annotation.as<AstTypeReference>())
|
||||||
{
|
{
|
||||||
std::optional<TypeFun> tf;
|
std::optional<TypeFun> tf;
|
||||||
|
@ -4668,11 +4480,11 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
std::optional<TableIndexer> tableIndexer;
|
std::optional<TableIndexer> tableIndexer;
|
||||||
|
|
||||||
for (const auto& prop : table->props)
|
for (const auto& prop : table->props)
|
||||||
props[prop.name.value] = {resolveType(scope, *prop.type, DEPRECATED_canBeGeneric)};
|
props[prop.name.value] = {resolveType(scope, *prop.type)};
|
||||||
|
|
||||||
if (const auto& indexer = table->indexer)
|
if (const auto& indexer = table->indexer)
|
||||||
tableIndexer = TableIndexer(
|
tableIndexer = TableIndexer(
|
||||||
resolveType(scope, *indexer->indexType, DEPRECATED_canBeGeneric), resolveType(scope, *indexer->resultType, DEPRECATED_canBeGeneric));
|
resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType));
|
||||||
|
|
||||||
return addType(TableTypeVar{
|
return addType(TableTypeVar{
|
||||||
props, tableIndexer, scope->level,
|
props, tableIndexer, scope->level,
|
||||||
|
@ -4683,17 +4495,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
{
|
{
|
||||||
ScopePtr funcScope = childScope(scope, func->location);
|
ScopePtr funcScope = childScope(scope, func->location);
|
||||||
|
|
||||||
std::vector<TypeId> generics;
|
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
||||||
std::vector<TypePackId> genericPacks;
|
|
||||||
|
|
||||||
if (FFlag::LuauGenericFunctions)
|
|
||||||
{
|
|
||||||
std::tie(generics, genericPacks) = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: better error message CLI-39912
|
|
||||||
if (FFlag::LuauGenericFunctions && !FFlag::LuauRankNTypes && !DEPRECATED_canBeGeneric && (generics.size() > 0 || genericPacks.size() > 0))
|
|
||||||
reportError(TypeError{annotation.location, GenericError{"generic function where only monotypes are allowed"}});
|
|
||||||
|
|
||||||
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
||||||
TypePackId retTypes = resolveTypePack(funcScope, func->returnTypes);
|
TypePackId retTypes = resolveTypePack(funcScope, func->returnTypes);
|
||||||
|
@ -4716,16 +4518,13 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
else if (auto typeOf = annotation.as<AstTypeTypeof>())
|
else if (auto typeOf = annotation.as<AstTypeTypeof>())
|
||||||
{
|
{
|
||||||
TypeId ty = checkExpr(scope, *typeOf->expr).type;
|
TypeId ty = checkExpr(scope, *typeOf->expr).type;
|
||||||
// TODO: better error message CLI-39912
|
|
||||||
if (FFlag::LuauGenericFunctions && !FFlag::LuauRankNTypes && !DEPRECATED_canBeGeneric && isGeneric(ty))
|
|
||||||
reportError(TypeError{annotation.location, GenericError{"typeof produced a polytype where only monotypes are allowed"}});
|
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
else if (const auto& un = annotation.as<AstTypeUnion>())
|
else if (const auto& un = annotation.as<AstTypeUnion>())
|
||||||
{
|
{
|
||||||
std::vector<TypeId> types;
|
std::vector<TypeId> types;
|
||||||
for (AstType* ann : un->types)
|
for (AstType* ann : un->types)
|
||||||
types.push_back(resolveType(scope, *ann, DEPRECATED_canBeGeneric));
|
types.push_back(resolveType(scope, *ann));
|
||||||
|
|
||||||
return addType(UnionTypeVar{types});
|
return addType(UnionTypeVar{types});
|
||||||
}
|
}
|
||||||
|
@ -4733,7 +4532,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
{
|
{
|
||||||
std::vector<TypeId> types;
|
std::vector<TypeId> types;
|
||||||
for (AstType* ann : un->types)
|
for (AstType* ann : un->types)
|
||||||
types.push_back(resolveType(scope, *ann, DEPRECATED_canBeGeneric));
|
types.push_back(resolveType(scope, *ann));
|
||||||
|
|
||||||
return addType(IntersectionTypeVar{types});
|
return addType(IntersectionTypeVar{types});
|
||||||
}
|
}
|
||||||
|
@ -4919,9 +4718,8 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
|
||||||
|
|
||||||
if (FFlag::LuauCloneCorrectlyBeforeMutatingTableType)
|
if (FFlag::LuauCloneCorrectlyBeforeMutatingTableType)
|
||||||
{
|
{
|
||||||
// TODO: CLI-46926 it's a bad idea to rename the type whether we follow through the BoundTypeVar or not
|
// TODO: CLI-46926 it's not a good idea to rename the type here
|
||||||
TypeId target = FFlag::LuauFollowInTypeFunApply ? follow(instantiated) : instantiated;
|
TypeId target = follow(instantiated);
|
||||||
|
|
||||||
bool needsClone = follow(tf.type) == target;
|
bool needsClone = follow(tf.type) == target;
|
||||||
TableTypeVar* ttv = getMutableTableType(target);
|
TableTypeVar* ttv = getMutableTableType(target);
|
||||||
|
|
||||||
|
@ -5152,8 +4950,6 @@ void TypeChecker::resolve(const TruthyPredicate& truthyP, ErrorVec& errVec, Refi
|
||||||
|
|
||||||
void TypeChecker::resolve(const AndPredicate& andP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
void TypeChecker::resolve(const AndPredicate& andP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauOrPredicate)
|
|
||||||
{
|
|
||||||
if (!sense)
|
if (!sense)
|
||||||
{
|
{
|
||||||
OrPredicate orP{
|
OrPredicate orP{
|
||||||
|
@ -5166,17 +4962,6 @@ void TypeChecker::resolve(const AndPredicate& andP, ErrorVec& errVec, Refinement
|
||||||
|
|
||||||
resolve(andP.lhs, errVec, refis, scope, sense);
|
resolve(andP.lhs, errVec, refis, scope, sense);
|
||||||
resolve(andP.rhs, errVec, refis, scope, sense);
|
resolve(andP.rhs, errVec, refis, scope, sense);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// And predicate is currently not resolvable when sense is false. 'not (a and b)' is synonymous with '(not a) or (not b)'.
|
|
||||||
// TODO: implement environment merging to permit this case.
|
|
||||||
if (!sense)
|
|
||||||
return;
|
|
||||||
|
|
||||||
resolve(andP.lhs, errVec, refis, scope, sense);
|
|
||||||
resolve(andP.rhs, errVec, refis, scope, sense);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::resolve(const OrPredicate& orP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
void TypeChecker::resolve(const OrPredicate& orP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
||||||
|
@ -5207,8 +4992,6 @@ void TypeChecker::resolve(const OrPredicate& orP, ErrorVec& errVec, RefinementMa
|
||||||
void TypeChecker::resolve(const IsAPredicate& isaP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
void TypeChecker::resolve(const IsAPredicate& isaP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense)
|
||||||
{
|
{
|
||||||
auto predicate = [&](TypeId option) -> std::optional<TypeId> {
|
auto predicate = [&](TypeId option) -> std::optional<TypeId> {
|
||||||
if (FFlag::LuauTypeGuardPeelsAwaySubclasses)
|
|
||||||
{
|
|
||||||
// This by itself is not truly enough to determine that A is stronger than B or vice versa.
|
// This by itself is not truly enough to determine that A is stronger than B or vice versa.
|
||||||
// The best unambiguous way about this would be to have a function that returns the relationship ordering of a pair.
|
// The best unambiguous way about this would be to have a function that returns the relationship ordering of a pair.
|
||||||
// i.e. TypeRelationship relationshipOf(TypeId superTy, TypeId subTy)
|
// i.e. TypeRelationship relationshipOf(TypeId superTy, TypeId subTy)
|
||||||
|
@ -5246,23 +5029,10 @@ void TypeChecker::resolve(const IsAPredicate& isaP, ErrorVec& errVec, Refinement
|
||||||
if (sense)
|
if (sense)
|
||||||
return isaP.ty;
|
return isaP.ty;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto lctv = get<ClassTypeVar>(option);
|
|
||||||
auto rctv = get<ClassTypeVar>(isaP.ty);
|
|
||||||
|
|
||||||
if (isSubclass(lctv, rctv) == sense)
|
// local variable works around an odd gcc 9.3 warning: <anonymous> may be used uninitialized
|
||||||
return option;
|
std::optional<TypeId> res = std::nullopt;
|
||||||
|
return res;
|
||||||
if (isSubclass(rctv, lctv) == sense)
|
|
||||||
return isaP.ty;
|
|
||||||
|
|
||||||
if (canUnify(option, isaP.ty, isaP.location).empty() == sense)
|
|
||||||
return isaP.ty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<TypeId> ty = resolveLValue(refis, scope, isaP.lvalue);
|
std::optional<TypeId> ty = resolveLValue(refis, scope, isaP.lvalue);
|
||||||
|
@ -5457,7 +5227,7 @@ std::vector<TypeId> TypeChecker::unTypePack(const ScopePtr& scope, TypePackId tp
|
||||||
TypePack* expectedPack = getMutable<TypePack>(expectedTypePack);
|
TypePack* expectedPack = getMutable<TypePack>(expectedTypePack);
|
||||||
LUAU_ASSERT(expectedPack);
|
LUAU_ASSERT(expectedPack);
|
||||||
for (size_t i = 0; i < expectedLength; ++i)
|
for (size_t i = 0; i < expectedLength; ++i)
|
||||||
expectedPack->head.push_back(FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
|
expectedPack->head.push_back(freshType(scope));
|
||||||
|
|
||||||
unify(expectedTypePack, tp, location);
|
unify(expectedTypePack, tp, location);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -216,7 +216,7 @@ bool finite(TypePackId tp)
|
||||||
if (auto pack = get<TypePack>(tp))
|
if (auto pack = get<TypePack>(tp))
|
||||||
return pack->tail ? finite(*pack->tail) : true;
|
return pack->tail ? finite(*pack->tail) : true;
|
||||||
|
|
||||||
if (auto pack = get<VariadicTypePack>(tp))
|
if (get<VariadicTypePack>(tp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -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))
|
||||||
|
@ -295,8 +293,8 @@ bool isGeneric(TypeId ty)
|
||||||
bool maybeGeneric(TypeId ty)
|
bool maybeGeneric(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
if (auto ftv = get<FreeTypeVar>(ty))
|
if (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()};
|
||||||
|
@ -749,9 +762,9 @@ void StateDot::visitChild(TypeId ty, int parentIndex, const char* linkName)
|
||||||
|
|
||||||
if (opts.duplicatePrimitives && canDuplicatePrimitive(ty))
|
if (opts.duplicatePrimitives && canDuplicatePrimitive(ty))
|
||||||
{
|
{
|
||||||
if (const PrimitiveTypeVar* ptv = get<PrimitiveTypeVar>(ty))
|
if (get<PrimitiveTypeVar>(ty))
|
||||||
formatAppend(result, "n%d [label=\"%s\"];\n", index, toStringDetailed(ty, {}).name.c_str());
|
formatAppend(result, "n%d [label=\"%s\"];\n", index, toStringDetailed(ty, {}).name.c_str());
|
||||||
else if (const AnyTypeVar* atv = get<AnyTypeVar>(ty))
|
else if (get<AnyTypeVar>(ty))
|
||||||
formatAppend(result, "n%d [label=\"any\"];\n", index);
|
formatAppend(result, "n%d [label=\"any\"];\n", index);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -902,19 +915,19 @@ void StateDot::visitChildren(TypeId ty, int index)
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
}
|
}
|
||||||
else if (const AnyTypeVar* atv = get<AnyTypeVar>(ty))
|
else if (get<AnyTypeVar>(ty))
|
||||||
{
|
{
|
||||||
formatAppend(result, "AnyTypeVar %d", index);
|
formatAppend(result, "AnyTypeVar %d", index);
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
}
|
}
|
||||||
else if (const PrimitiveTypeVar* ptv = get<PrimitiveTypeVar>(ty))
|
else if (get<PrimitiveTypeVar>(ty))
|
||||||
{
|
{
|
||||||
formatAppend(result, "PrimitiveTypeVar %s", toStringDetailed(ty, {}).name.c_str());
|
formatAppend(result, "PrimitiveTypeVar %s", toStringDetailed(ty, {}).name.c_str());
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
}
|
}
|
||||||
else if (const ErrorTypeVar* etv = get<ErrorTypeVar>(ty))
|
else if (get<ErrorTypeVar>(ty))
|
||||||
{
|
{
|
||||||
formatAppend(result, "ErrorTypeVar %d", index);
|
formatAppend(result, "ErrorTypeVar %d", index);
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
|
@ -994,7 +1007,7 @@ void StateDot::visitChildren(TypePackId tp, int index)
|
||||||
finishNodeLabel(tp);
|
finishNodeLabel(tp);
|
||||||
finishNode();
|
finishNode();
|
||||||
}
|
}
|
||||||
else if (const Unifiable::Error* etp = get<Unifiable::Error>(tp))
|
else if (get<Unifiable::Error>(tp))
|
||||||
{
|
{
|
||||||
formatAppend(result, "ErrorTypePack %d", index);
|
formatAppend(result, "ErrorTypePack %d", index);
|
||||||
finishNodeLabel(tp);
|
finishNodeLabel(tp);
|
||||||
|
@ -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,8 +340,13 @@ 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)
|
||||||
|
{
|
||||||
|
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}});
|
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
|
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
|
||||||
{
|
{
|
||||||
// T <: A | B if T <: A or T <: B
|
// T <: A | B if T <: A or T <: B
|
||||||
|
@ -425,9 +412,43 @@ 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)
|
||||||
|
{
|
||||||
|
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}});
|
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(superTy))
|
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(superTy))
|
||||||
|
{
|
||||||
|
if (FFlag::LuauExtendedTypeMismatchError)
|
||||||
|
{
|
||||||
|
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
|
// T <: A & B if A <: T and B <: T
|
||||||
for (TypeId type : uv->parts)
|
for (TypeId type : uv->parts)
|
||||||
|
@ -435,6 +456,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||||
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
|
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
|
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
|
||||||
{
|
{
|
||||||
// A & B <: T if T <: A or T <: B
|
// A & B <: T if T <: A or T <: B
|
||||||
|
@ -480,8 +502,13 @@ 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)
|
||||||
|
{
|
||||||
|
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}});
|
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++)
|
for (size_t i = 0; i < numGenerics; i++)
|
||||||
log.pushSeen(lf->generics[i], rf->generics[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--)
|
for (int i = int(numGenerics) - 1; 0 <= i; i--)
|
||||||
log.popSeen(lf->generics[i], rf->generics[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);
|
||||||
|
|
||||||
|
if (FFlag::LuauExtendedTypeMismatchError)
|
||||||
|
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||||
|
else
|
||||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
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);
|
||||||
|
|
||||||
|
if (FFlag::LuauExtendedTypeMismatchError)
|
||||||
|
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||||
|
else
|
||||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
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);
|
||||||
|
|
||||||
|
if (FFlag::LuauExtendedTypeMismatchError)
|
||||||
|
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
|
||||||
|
else
|
||||||
checkChildUnifierTypeMismatch(innerState.errors, left, right);
|
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
|
||||||
|
@ -1383,22 +1419,9 @@ 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))
|
if (isOptional(it.second.type))
|
||||||
continue;
|
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);
|
||||||
|
|
||||||
|
@ -1481,22 +1504,9 @@ 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))
|
if (isOptional(it.second.type))
|
||||||
continue;
|
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);
|
||||||
|
|
||||||
|
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);
|
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
|
||||||
|
}
|
||||||
|
|
||||||
log.concat(std::move(innerState.log));
|
log.concat(std::move(innerState.log));
|
||||||
}
|
}
|
||||||
|
@ -1613,11 +1634,35 @@ 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}});
|
||||||
|
|
||||||
|
if (!FFlag::LuauExtendedClassMismatchError)
|
||||||
tryUnify_(prop.type, singletonTypes.errorType);
|
tryUnify_(prop.type, singletonTypes.errorType);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
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);
|
tryUnify_(prop.type, classProp->type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (table->indexer)
|
if (table->indexer)
|
||||||
{
|
{
|
||||||
|
@ -1649,15 +1694,12 @@ 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);
|
state.log(a);
|
||||||
|
@ -1672,24 +1714,6 @@ static void queueTypePack_DEPRECATED(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (get<Unifiable::Free>(a))
|
|
||||||
{
|
|
||||||
state.log(a);
|
|
||||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto tp = get<TypePack>(a))
|
|
||||||
{
|
|
||||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
|
||||||
if (tp->tail)
|
|
||||||
a = *tp->tail;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
||||||
|
@ -1698,15 +1722,12 @@ 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);
|
state.log(a);
|
||||||
|
@ -1721,24 +1742,6 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (get<Unifiable::Free>(a))
|
|
||||||
{
|
|
||||||
state.log(a);
|
|
||||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto tp = get<TypePack>(a))
|
|
||||||
{
|
|
||||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
|
||||||
if (tp->tail)
|
|
||||||
a = *tp->tail;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unifier::tryUnifyVariadics(TypePackId superTp, TypePackId subTp, bool reversed, int subOffset)
|
void Unifier::tryUnifyVariadics(TypePackId superTp, TypePackId subTp, bool reversed, int subOffset)
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Lexeme parameterStart = lexer.current();
|
||||||
|
|
||||||
|
if (!FFlag::LuauParseGenericFunctionTypeBegin)
|
||||||
|
begin = parameterStart;
|
||||||
|
|
||||||
expectAndConsume('(', "function parameters");
|
expectAndConsume('(', "function parameters");
|
||||||
else
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(begin.type == '(');
|
|
||||||
nextLexeme(); // (
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -142,6 +142,7 @@ static bool traverseDirectoryRec(const std::string& path, const std::function<vo
|
||||||
joinPaths(buf, path.c_str(), data.d_name);
|
joinPaths(buf, path.c_str(), data.d_name);
|
||||||
|
|
||||||
int type = data.d_type;
|
int type = data.d_type;
|
||||||
|
int mode = -1;
|
||||||
|
|
||||||
// we need to stat DT_UNKNOWN to be able to tell the type
|
// we need to stat DT_UNKNOWN to be able to tell the type
|
||||||
if (type == DT_UNKNOWN)
|
if (type == DT_UNKNOWN)
|
||||||
|
@ -153,18 +154,18 @@ static bool traverseDirectoryRec(const std::string& path, const std::function<vo
|
||||||
lstat(buf.c_str(), &st);
|
lstat(buf.c_str(), &st);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
type = IFTODT(st.st_mode);
|
mode = st.st_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == DT_DIR)
|
if (type == DT_DIR || mode == S_IFDIR)
|
||||||
{
|
{
|
||||||
traverseDirectoryRec(buf, callback);
|
traverseDirectoryRec(buf, callback);
|
||||||
}
|
}
|
||||||
else if (type == DT_REG)
|
else if (type == DT_REG || mode == S_IFREG)
|
||||||
{
|
{
|
||||||
callback(buf);
|
callback(buf);
|
||||||
}
|
}
|
||||||
else if (type == DT_LNK)
|
else if (type == DT_LNK || mode == S_IFLNK)
|
||||||
{
|
{
|
||||||
// Skip symbolic links to avoid handling cycles
|
// Skip symbolic links to avoid handling cycles
|
||||||
}
|
}
|
||||||
|
|
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);
|
||||||
|
|
||||||
// run code + collect error
|
// static string for caching result (prevents dangling ptr on function exit)
|
||||||
std::string error = runCode(L, source);
|
static std::string result;
|
||||||
result = error;
|
|
||||||
|
|
||||||
if (error.length())
|
// run code + collect error
|
||||||
{
|
result = runCode(L, source);
|
||||||
return result.c_str();
|
|
||||||
}
|
return result.empty() ? NULL : result.c_str();
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -591,3 +586,4 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ project(Luau LANGUAGES CXX)
|
||||||
|
|
||||||
option(LUAU_BUILD_CLI "Build CLI" ON)
|
option(LUAU_BUILD_CLI "Build CLI" ON)
|
||||||
option(LUAU_BUILD_TESTS "Build tests" ON)
|
option(LUAU_BUILD_TESTS "Build tests" ON)
|
||||||
|
option(LUAU_WERROR "Warnings as errors" OFF)
|
||||||
|
|
||||||
add_library(Luau.Ast STATIC)
|
add_library(Luau.Ast STATIC)
|
||||||
add_library(Luau.Compiler STATIC)
|
add_library(Luau.Compiler STATIC)
|
||||||
|
@ -57,11 +58,18 @@ set(LUAU_OPTIONS)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
list(APPEND LUAU_OPTIONS /D_CRT_SECURE_NO_WARNINGS) # We need to use the portable CRT functions.
|
list(APPEND LUAU_OPTIONS /D_CRT_SECURE_NO_WARNINGS) # We need to use the portable CRT functions.
|
||||||
list(APPEND LUAU_OPTIONS /WX) # Warnings are errors
|
|
||||||
list(APPEND LUAU_OPTIONS /MP) # Distribute single project compilation across multiple cores
|
list(APPEND LUAU_OPTIONS /MP) # Distribute single project compilation across multiple cores
|
||||||
else()
|
else()
|
||||||
list(APPEND LUAU_OPTIONS -Wall) # All warnings
|
list(APPEND LUAU_OPTIONS -Wall) # All warnings
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Enabled in CI; we should be warning free on our main compiler versions but don't guarantee being warning free everywhere
|
||||||
|
if(LUAU_WERROR)
|
||||||
|
if(MSVC)
|
||||||
|
list(APPEND LUAU_OPTIONS /WX) # Warnings are errors
|
||||||
|
else()
|
||||||
list(APPEND LUAU_OPTIONS -Werror) # Warnings are errors
|
list(APPEND LUAU_OPTIONS -Werror) # Warnings are errors
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
||||||
|
@ -79,8 +87,11 @@ if(LUAU_BUILD_CLI)
|
||||||
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.VM)
|
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.VM)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
find_library(LIBPTHREAD pthread)
|
||||||
|
if (LIBPTHREAD)
|
||||||
target_link_libraries(Luau.Repl.CLI PRIVATE pthread)
|
target_link_libraries(Luau.Repl.CLI PRIVATE pthread)
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
||||||
|
|
|
@ -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,12 +3717,10 @@ 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
|
||||||
{
|
{
|
||||||
for (const char* global : kSpecialGlobals)
|
for (const char* global : kSpecialGlobals)
|
||||||
|
@ -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);
|
||||||
|
|
15
Makefile
15
Makefile
|
@ -46,14 +46,20 @@ endif
|
||||||
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(ANALYSIS_OBJECTS) $(VM_OBJECTS) $(TESTS_OBJECTS) $(CLI_OBJECTS) $(FUZZ_OBJECTS)
|
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(ANALYSIS_OBJECTS) $(VM_OBJECTS) $(TESTS_OBJECTS) $(CLI_OBJECTS) $(FUZZ_OBJECTS)
|
||||||
|
|
||||||
# common flags
|
# common flags
|
||||||
CXXFLAGS=-g -Wall -Werror
|
CXXFLAGS=-g -Wall
|
||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
|
|
||||||
# temporary, for older gcc versions as they treat var in `if (type var = val)` as unused
|
# some gcc versions treat var in `if (type var = val)` as unused
|
||||||
|
# some gcc versions treat variables used in constexpr if blocks as unused
|
||||||
ifeq ($(findstring g++,$(shell $(CXX) --version)),g++)
|
ifeq ($(findstring g++,$(shell $(CXX) --version)),g++)
|
||||||
CXXFLAGS+=-Wno-unused
|
CXXFLAGS+=-Wno-unused
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# enabled in CI; we should be warning free on our main compiler versions but don't guarantee being warning free everywhere
|
||||||
|
ifneq ($(werror),)
|
||||||
|
CXXFLAGS+=-Werror
|
||||||
|
endif
|
||||||
|
|
||||||
# configuration-specific flags
|
# configuration-specific flags
|
||||||
ifeq ($(config),release)
|
ifeq ($(config),release)
|
||||||
CXXFLAGS+=-O2 -DNDEBUG
|
CXXFLAGS+=-O2 -DNDEBUG
|
||||||
|
@ -137,12 +143,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)
|
||||||
|
|
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Security Guarantees
|
||||||
|
|
||||||
|
Luau provides a safe sandbox that scripts can not escape from, short of vulnerabilities in custom C functions exposed by the host. This includes the virtual machine and builtin libraries.
|
||||||
|
|
||||||
|
Any source code can not result in memory safety errors or crashes during its compilation or execution. Violations of memory safety are considered vulnerabilities.
|
||||||
|
|
||||||
|
Note that Luau does not provide termination guarantees - some code may exhaust CPU or RAM resources on the system during compilation or execution.
|
||||||
|
|
||||||
|
The runtime expects valid bytecode as an input. Feeding bytecode that was not produced by Luau compiler into the VM is not supported and
|
||||||
|
doesn't come with any security guarantees; make sure to sign the bytecode when it crosses a network or file system boundary to avoid tampering.
|
||||||
|
|
||||||
|
# Reporting a Vulnerability
|
||||||
|
|
||||||
|
You can report security bugs via [Hackerone](https://hackerone.com/roblox). Please refer to the linked page for rules of the bounty program.
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
150
VM/src/lgc.cpp
150
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
|
else
|
||||||
g->gcstats.currcycle.explicitwork += work;
|
g->gcstats.currcycle.explicitwork += work;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// always perform at least one single step
|
|
||||||
do
|
|
||||||
{
|
|
||||||
lim -= singlestep(L);
|
|
||||||
|
|
||||||
// if we have switched to a different state, capture the duration of last stage
|
|
||||||
// this way we reduce the number of timer calls we make
|
|
||||||
if (lastgcstate != g->gcstate)
|
|
||||||
{
|
|
||||||
GC_INTERRUPT(lastgcstate);
|
|
||||||
|
|
||||||
double now = lua_clock();
|
|
||||||
|
|
||||||
recordGcStateTime(g, lastgcstate, now - lasttimestamp, assist);
|
|
||||||
|
|
||||||
lasttimestamp = now;
|
|
||||||
lastgcstate = g->gcstate;
|
|
||||||
}
|
|
||||||
} while (lim > 0 && g->gcstate != GCSpause);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 */
|
||||||
|
if (FFlag::LuauStrPackUBCastFix)
|
||||||
|
{
|
||||||
|
long long n = (long long)luaL_checknumber(L, arg);
|
||||||
|
if (size < SZINT) /* need overflow check? */
|
||||||
|
luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
||||||
|
packint(&b, (unsigned long long)n, h.islittle, size, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
|
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
|
||||||
if (size < SZINT) /* need overflow check? */
|
if (size < SZINT) /* need overflow check? */
|
||||||
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
|
||||||
packint(&b, n, h.islittle, size, 0);
|
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,28 +205,24 @@ 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)
|
||||||
|
if v == 0 then return 32 end
|
||||||
local offset = 0
|
local offset = 0
|
||||||
local result = 0
|
while bit32.extract(v, offset) == 0 do
|
||||||
|
|
||||||
if target == 0 then
|
|
||||||
target = self.h
|
|
||||||
result = 32
|
|
||||||
end
|
|
||||||
|
|
||||||
if target == 0 then
|
|
||||||
return 64
|
|
||||||
end
|
|
||||||
|
|
||||||
while bit32.extract(target, offset) == 0 do
|
|
||||||
offset = offset + 1
|
offset = offset + 1
|
||||||
end
|
end
|
||||||
|
return offset
|
||||||
return result + offset
|
end
|
||||||
end
|
function Bitboard:ctz()
|
||||||
|
local result = ctz(self.l)
|
||||||
function Bitboard:ctzafter(start)
|
if result == 32 then
|
||||||
|
return ctz(self.h) + 32
|
||||||
|
else
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Bitboard:ctzafter(start)
|
||||||
start = start + 1
|
start = start + 1
|
||||||
if start < 32 then
|
if start < 32 then
|
||||||
for i=start,31 do
|
for i=start,31 do
|
||||||
|
@ -237,6 +233,20 @@ function Bitboard:ctzafter(start)
|
||||||
if bit32.extract(self.h, i-32) == 1 then return i end
|
if bit32.extract(self.h, i-32) == 1 then return i end
|
||||||
end
|
end
|
||||||
return 64
|
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
|
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())
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
# Allow method call on string literals
|
|
||||||
|
|
||||||
> Note: this RFC was adapted from an internal proposal that predates RFC process
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Allow string literals to be indexed on without parentheses or from an identifier. That is, the following snippet will become legal under this proposal:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
print("Hello, %s!":format("world"))
|
|
||||||
print("0123456789ABCDEF":sub(i, i))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
Experienced Lua developers occasionally run into this paper-cut even after years of working with the language. Programmers in Lua frequently wants to format a user-facing message using a constant string, but the parser will not accept it as legible syntax.
|
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
Formally, the proposal is to move the `String` parser from `exp` to `prefixexp`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
var ::= Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name
|
|
||||||
- exp ::= nil | false | true | Number | String | `...´ | function |
|
|
||||||
+ exp ::= nil | false | true | Number | `...´ | function
|
|
||||||
| prefixexp | tableconstructor | exp binop exp | unop exp
|
|
||||||
- prefixexp ::= var | functioncall | `(´ exp `)´
|
|
||||||
+ prefixexp ::= String | var | functioncall | `(´ exp `)´
|
|
||||||
functioncall ::= prefixexp args | prefixexp `:´ Name args
|
|
||||||
```
|
|
||||||
|
|
||||||
By itself, this change introduces an additional ambiguity because of the combination of non-significant whitespace and function calls with string literal as the first argument without the use of parentheses.
|
|
||||||
|
|
||||||
Consider code like this:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local foo = bar
|
|
||||||
("fmt"):format(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
The grammar for this sequence suggests that the above is a function call to bar with a single string literal argument, "fmt", and format method is called on the result. This is a consequence of line endings not being significant, but humans don't read the code like this, and are likely to think that here, format is called on the string literal "fmt".
|
|
||||||
|
|
||||||
Because of this, Lua 5.1 produces a syntax error whenever function call arguments start on the next line. Luau has the same error production rule; future versions of Lua remove this restriction but it's not clear that we want to remove this as this does help prevent errors.
|
|
||||||
|
|
||||||
The grammar today also allows calling functions with string literals as their first (and only) argument without the use of parentheses; bar "fmt" is a function call. This is helpful when defining embedded domain-specific languages.
|
|
||||||
|
|
||||||
By itself, this proposal thus would create a similar ambiguity in code like this:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local foo = bar
|
|
||||||
"fmt":format(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
While we could extend the line-based error check to include function literal arguments, this is not syntactically backwards compatible and as such may break existing code. A simpler and more conservative solution is to disallow string literal as the leading token of a new statement - there are no cases when this is valid today, so it's safe to limit this.
|
|
||||||
|
|
||||||
Doing so would prohibit code like this:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
"fmt":format(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
However, there are no methods on the string object where code like this would be meaningful. As such, in addition to changing the grammar wrt string literals, we will add an extra ambiguity error whenever a statement starts with a string literal.
|
|
||||||
|
|
||||||
## Drawbacks
|
|
||||||
|
|
||||||
None known.
|
|
||||||
|
|
||||||
## Alternatives
|
|
||||||
|
|
||||||
The infallible parser could be mended in this exact scenario to report a more friendly error message. We decided not to do this because there is more value to gain by simply supporting the main proposal.
|
|
|
@ -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>");
|
||||||
|
|
||||||
{
|
|
||||||
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
|
|
||||||
ScopedFastFlag luauGenericFunctionsParserFix{"LuauGenericFunctionsParserFix", true};
|
|
||||||
|
|
||||||
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
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({6, 26})));
|
||||||
CHECK_EQ("nil", toString(requireTypeAtPosition({7, 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…
Add table
Reference in a new issue