Sync to upstream/release/504 (#200)

- Type mismatch errors now show detailed information for compound types, highlighting the mismatching component
- Fix string.pack bug on ARM when packing negative numbers using unsigned formats
- Implement bit32.countlz/countrz (RFC RFC: bit32.countlz/countrz #89)
- Minor compiler throughput optimization (~2% faster compilation)
- Improve transpiler behavior for edge cases and better test coverage (not exposed through CLI at the moment)
- Improve error recovery when parsing invalid assignments
- Build fixes for fuzzing targets
This commit is contained in:
Arseny Kapoulkine 2021-11-12 06:27:34 -08:00 committed by GitHub
parent a6a2b86c9b
commit d47b2f1dfe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 1433 additions and 1807 deletions

View file

@ -8,11 +8,20 @@
namespace Luau
{
struct TypeError;
struct TypeMismatch
{
TypeId wantedType;
TypeId givenType;
TypeMismatch() = default;
TypeMismatch(TypeId wantedType, TypeId givenType);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error);
TypeId wantedType = nullptr;
TypeId givenType = nullptr;
std::string reason;
std::shared_ptr<TypeError> error;
bool operator==(const TypeMismatch& rhs) const;
};

View file

@ -53,7 +53,7 @@ struct FileResolver
}
// DEPRECATED APIS
// These are going to be removed with LuauNewRequireTracer
// These are going to be removed with LuauNewRequireTrace2
virtual bool moduleExists(const ModuleName& name) const = 0;
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
virtual ModuleName concat(const ModuleName& lhs, std::string_view rhs) const = 0;

View file

@ -18,6 +18,7 @@ struct TranspileResult
std::string parseError; // Nonempty if the transpile failed
};
std::string toString(AstNode* node);
void dump(AstNode* node);
// Never fails on a well-formed AST
@ -25,6 +26,6 @@ std::string transpile(AstStatBlock& ast);
std::string transpileWithTypes(AstStatBlock& block);
// Only fails when parsing fails
TranspileResult transpile(std::string_view source, ParseOptions options = ParseOptions{});
TranspileResult transpile(std::string_view source, ParseOptions options = ParseOptions{}, bool withTypes = false);
} // namespace Luau

View file

@ -263,8 +263,6 @@ public:
*
*/
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location);
// Removed by FFlag::LuauRankNTypes
TypePackId DEPRECATED_instantiate(const ScopePtr& scope, TypePackId ty, Location location);
// Replace any free types or type packs by `any`.
// This is used when exporting types from modules, to make sure free types don't leak.
@ -298,8 +296,6 @@ private:
// Produce a new free type var.
TypeId freshType(const ScopePtr& scope);
TypeId freshType(TypeLevel level);
TypeId DEPRECATED_freshType(const ScopePtr& scope, bool canBeGeneric = false);
TypeId DEPRECATED_freshType(TypeLevel level, bool canBeGeneric = false);
// Returns nullopt if the predicate filters down the TypeId to 0 options.
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
@ -326,10 +322,8 @@ private:
TypePackId addTypePack(std::initializer_list<TypeId>&& ty);
TypePackId freshTypePack(const ScopePtr& scope);
TypePackId freshTypePack(TypeLevel level);
TypePackId DEPRECATED_freshTypePack(const ScopePtr& scope, bool canBeGeneric = false);
TypePackId DEPRECATED_freshTypePack(TypeLevel level, bool canBeGeneric = false);
TypeId resolveType(const ScopePtr& scope, const AstType& annotation, bool canBeGeneric = false);
TypeId resolveType(const ScopePtr& scope, const AstType& annotation);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& types);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,

View file

@ -8,8 +8,6 @@
#include <optional>
#include <set>
LUAU_FASTFLAG(LuauAddMissingFollow)
namespace Luau
{
@ -127,28 +125,22 @@ TypePack* asMutable(const TypePack* tp);
template<typename T>
const T* get(TypePackId tp)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
}
return get_if<T>(&(tp->ty));
}
template<typename T>
T* getMutable(TypePackId tp)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
}
return get_if<T>(&(asMutable(tp)->ty));
}

View file

@ -18,7 +18,6 @@
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
LUAU_FASTFLAG(LuauAddMissingFollow)
namespace Luau
{
@ -413,13 +412,17 @@ bool maybeGeneric(const TypeId ty);
struct SingletonTypes
{
const TypeId nilType = &nilType_;
const TypeId numberType = &numberType_;
const TypeId stringType = &stringType_;
const TypeId booleanType = &booleanType_;
const TypeId threadType = &threadType_;
const TypeId anyType = &anyType_;
const TypeId errorType = &errorType_;
const TypeId nilType;
const TypeId numberType;
const TypeId stringType;
const TypeId booleanType;
const TypeId threadType;
const TypeId anyType;
const TypeId errorType;
const TypeId optionalNumberType;
const TypePackId anyTypePack;
const TypePackId errorTypePack;
SingletonTypes();
SingletonTypes(const SingletonTypes&) = delete;
@ -427,14 +430,6 @@ struct SingletonTypes
private:
std::unique_ptr<struct TypeArena> arena;
TypeVar nilType_;
TypeVar numberType_;
TypeVar stringType_;
TypeVar booleanType_;
TypeVar threadType_;
TypeVar anyType_;
TypeVar errorType_;
TypeId makeStringMetatable();
};
@ -471,28 +466,22 @@ TypeVar* asMutable(TypeId ty);
template<typename T>
const T* get(TypeId tv)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
}
return get_if<T>(&tv->ty);
}
template<typename T>
T* getMutable(TypeId tv)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
}
return get_if<T>(&asMutable(tv)->ty);
}

View file

@ -63,12 +63,9 @@ using Name = std::string;
struct Free
{
explicit Free(TypeLevel level);
Free(TypeLevel level, bool DEPRECATED_canBeGeneric);
int index;
TypeLevel level;
// Removed by FFlag::LuauRankNTypes
bool DEPRECATED_canBeGeneric = false;
// True if this free type variable is part of a mutually
// recursive type alias whose definitions haven't been
// resolved yet.

View file

@ -87,7 +87,6 @@ private:
void tryUnifyWithAny(TypePackId any, TypePackId ty);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry);
public:
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
@ -102,6 +101,7 @@ private:
bool isNonstrictMode() const;
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType);
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);

View file

@ -12,7 +12,6 @@
#include <unordered_set>
#include <utility>
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
@ -368,22 +367,12 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
auto endIter = end(u);
while (iter != endIter)
{
if (FFlag::LuauAddMissingFollow)
{
if (isNil(*iter))
++iter;
else
break;
}
else
{
if (auto primTy = Luau::get<PrimitiveTypeVar>(*iter); primTy && primTy->type == PrimitiveTypeVar::NilType)
++iter;
else
break;
}
}
if (iter == endIter)
return;
@ -397,22 +386,11 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryMap inner;
std::unordered_set<TypeId> innerSeen = seen;
if (FFlag::LuauAddMissingFollow)
{
if (isNil(*iter))
{
++iter;
continue;
}
}
else
{
if (auto innerPrimTy = Luau::get<PrimitiveTypeVar>(*iter); innerPrimTy && innerPrimTy->type == PrimitiveTypeVar::NilType)
{
++iter;
continue;
}
}
autocompleteProps(module, typeArena, *iter, indexType, nodes, inner, innerSeen);
@ -1519,9 +1497,9 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
return {};
TypeChecker& typeChecker =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
(frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
: frontend.moduleResolver.getModule(moduleName));
if (!module)
@ -1550,7 +1528,7 @@ OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view
sourceModule->commentLocations = std::move(result.commentLocations);
TypeChecker& typeChecker =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);

View file

@ -8,10 +8,7 @@
#include <algorithm>
LUAU_FASTFLAG(LuauParseGenericFunctions)
LUAU_FASTFLAG(LuauGenericFunctions)
LUAU_FASTFLAG(LuauRankNTypes)
LUAU_FASTFLAG(LuauNewRequireTrace)
LUAU_FASTFLAG(LuauNewRequireTrace2)
/** FIXME: Many of these type definitions are not quite completely accurate.
*
@ -185,25 +182,11 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
TypeId numberType = typeChecker.numberType;
TypeId booleanType = typeChecker.booleanType;
TypeId nilType = typeChecker.nilType;
TypeId stringType = typeChecker.stringType;
TypeId threadType = typeChecker.threadType;
TypeId anyType = typeChecker.anyType;
TypeArena& arena = typeChecker.globalTypes;
TypeId optionalNumber = makeOption(typeChecker, arena, numberType);
TypeId optionalString = makeOption(typeChecker, arena, stringType);
TypeId optionalBoolean = makeOption(typeChecker, arena, booleanType);
TypeId stringOrNumber = makeUnion(arena, {stringType, numberType});
TypePackId emptyPack = arena.addTypePack({});
TypePackId oneNumberPack = arena.addTypePack({numberType});
TypePackId oneStringPack = arena.addTypePack({stringType});
TypePackId oneBooleanPack = arena.addTypePack({booleanType});
TypePackId oneAnyPack = arena.addTypePack({anyType});
TypePackId anyTypePack = typeChecker.anyTypePack;
TypePackId numberVariadicList = arena.addTypePack(TypePackVar{VariadicTypePack{numberType}});
TypePackId listOfAtLeastOneNumber = arena.addTypePack(TypePack{{numberType}, numberVariadicList});
@ -215,8 +198,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
TypeId listOfAtLeastZeroNumbersToNumberType = arena.addType(FunctionTypeVar{numberVariadicList, oneNumberPack});
TypeId stringToAnyMap = arena.addType(TableTypeVar{{}, TableIndexer(stringType, anyType), typeChecker.globalScope->level});
LoadDefinitionFileResult loadResult = Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), "@luau");
LUAU_ASSERT(loadResult.success);
@ -236,8 +217,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
ttv->props["btest"] = makeProperty(arena.addType(FunctionTypeVar{listOfAtLeastOneNumber, oneBooleanPack}), "@luau/global/bit32.btest");
}
TypeId anyFunction = arena.addType(FunctionTypeVar{anyTypePack, anyTypePack});
TypeId genericK = arena.addType(GenericTypeVar{"K"});
TypeId genericV = arena.addType(GenericTypeVar{"V"});
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level});
@ -252,222 +231,6 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
addGlobalBinding(typeChecker, "string", it->second.type, "@luau");
if (!FFlag::LuauParseGenericFunctions || !FFlag::LuauGenericFunctions)
{
TableTypeVar::Props debugLib{
{"info", {makeIntersection(arena,
{
arena.addType(FunctionTypeVar{arena.addTypePack({typeChecker.threadType, numberType, stringType}), anyTypePack}),
arena.addType(FunctionTypeVar{arena.addTypePack({numberType, stringType}), anyTypePack}),
arena.addType(FunctionTypeVar{arena.addTypePack({anyFunction, stringType}), anyTypePack}),
})}},
{"traceback", {makeIntersection(arena,
{
makeFunction(arena, std::nullopt, {optionalString, optionalNumber}, {stringType}),
makeFunction(arena, std::nullopt, {typeChecker.threadType, optionalString, optionalNumber}, {stringType}),
})}},
};
assignPropDocumentationSymbols(debugLib, "@luau/global/debug");
addGlobalBinding(typeChecker, "debug",
arena.addType(TableTypeVar{debugLib, std::nullopt, typeChecker.globalScope->level, Luau::TableState::Sealed}), "@luau");
TableTypeVar::Props utf8Lib = {
{"char", {arena.addType(FunctionTypeVar{listOfAtLeastOneNumber, oneStringPack})}}, // FIXME
{"charpattern", {stringType}},
{"codes", {makeFunction(arena, std::nullopt, {stringType},
{makeFunction(arena, std::nullopt, {stringType, numberType}, {numberType, numberType}), stringType, numberType})}},
{"codepoint",
{arena.addType(FunctionTypeVar{arena.addTypePack({stringType, optionalNumber, optionalNumber}), listOfAtLeastOneNumber})}}, // FIXME
{"len", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber}, {optionalNumber, numberType})}},
{"offset", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber}, {numberType})}},
{"nfdnormalize", {makeFunction(arena, std::nullopt, {stringType}, {stringType})}},
{"graphemes", {makeFunction(arena, std::nullopt, {stringType, optionalNumber, optionalNumber},
{makeFunction(arena, std::nullopt, {}, {numberType, numberType})})}},
{"nfcnormalize", {makeFunction(arena, std::nullopt, {stringType}, {stringType})}},
};
assignPropDocumentationSymbols(utf8Lib, "@luau/global/utf8");
addGlobalBinding(
typeChecker, "utf8", arena.addType(TableTypeVar{utf8Lib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
TypeId optionalV = makeOption(typeChecker, arena, genericV);
TypeId arrayOfV = arena.addType(TableTypeVar{{}, TableIndexer(numberType, genericV), typeChecker.globalScope->level});
TypePackId unpackArgsPack = arena.addTypePack(TypePack{{arrayOfV, optionalNumber, optionalNumber}});
TypePackId unpackReturnPack = arena.addTypePack(TypePack{{}, anyTypePack});
TypeId unpackFunc = arena.addType(FunctionTypeVar{{genericV}, {}, unpackArgsPack, unpackReturnPack});
TypeId packResult = arena.addType(TableTypeVar{
TableTypeVar::Props{{"n", {numberType}}}, TableIndexer{numberType, numberType}, typeChecker.globalScope->level, TableState::Sealed});
TypePackId packArgsPack = arena.addTypePack(TypePack{{}, anyTypePack});
TypePackId packReturnPack = arena.addTypePack(TypePack{{packResult}});
TypeId comparator = makeFunction(arena, std::nullopt, {genericV, genericV}, {booleanType});
TypeId optionalComparator = makeOption(typeChecker, arena, comparator);
TypeId packFn = arena.addType(FunctionTypeVar(packArgsPack, packReturnPack));
TableTypeVar::Props tableLib = {
{"concat", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalString, optionalNumber, optionalNumber}, {stringType})}},
{"insert", {makeIntersection(arena, {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, genericV}, {}),
makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, numberType, genericV}, {})})}},
{"maxn", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV}, {numberType})}},
{"remove", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalNumber}, {optionalV})}},
{"sort", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, optionalComparator}, {})}},
{"create", {makeFunction(arena, std::nullopt, {genericV}, {}, {numberType, optionalV}, {arrayOfV})}},
{"find", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, genericV, optionalNumber}, {optionalNumber})}},
{"unpack", {unpackFunc}}, // FIXME
{"pack", {packFn}},
// Lua 5.0 compat
{"getn", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV}, {numberType})}},
{"foreach", {makeFunction(arena, std::nullopt, {genericK, genericV}, {},
{mapOfKtoV, makeFunction(arena, std::nullopt, {genericK, genericV}, {})}, {})}},
{"foreachi", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, makeFunction(arena, std::nullopt, {genericV}, {})}, {})}},
// backported from Lua 5.3
{"move", {makeFunction(arena, std::nullopt, {genericV}, {}, {arrayOfV, numberType, numberType, numberType, arrayOfV}, {})}},
// added in Luau (borrowed from LuaJIT)
{"clear", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {})}},
{"freeze", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {mapOfKtoV})}},
{"isfrozen", {makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV}, {booleanType})}},
};
assignPropDocumentationSymbols(tableLib, "@luau/global/table");
addGlobalBinding(
typeChecker, "table", arena.addType(TableTypeVar{tableLib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
TableTypeVar::Props coroutineLib = {
{"create", {makeFunction(arena, std::nullopt, {anyFunction}, {threadType})}},
{"resume", {arena.addType(FunctionTypeVar{arena.addTypePack(TypePack{{threadType}, anyTypePack}), anyTypePack})}},
{"running", {makeFunction(arena, std::nullopt, {}, {threadType})}},
{"status", {makeFunction(arena, std::nullopt, {threadType}, {stringType})}},
{"wrap", {makeFunction(
arena, std::nullopt, {anyFunction}, {anyType})}}, // FIXME this technically returns a function, but we can't represent this
// atm since it can be called with different arg types at different times
{"yield", {arena.addType(FunctionTypeVar{anyTypePack, anyTypePack})}},
{"isyieldable", {makeFunction(arena, std::nullopt, {}, {booleanType})}},
};
assignPropDocumentationSymbols(coroutineLib, "@luau/global/coroutine");
addGlobalBinding(typeChecker, "coroutine",
arena.addType(TableTypeVar{coroutineLib, std::nullopt, typeChecker.globalScope->level, TableState::Sealed}), "@luau");
TypeId genericT = arena.addType(GenericTypeVar{"T"});
TypeId genericR = arena.addType(GenericTypeVar{"R"});
// assert returns all arguments
TypePackId assertArgs = arena.addTypePack({genericT, optionalString});
TypePackId assertRets = arena.addTypePack({genericT});
addGlobalBinding(typeChecker, "assert", arena.addType(FunctionTypeVar{assertArgs, assertRets}), "@luau");
addGlobalBinding(typeChecker, "print", arena.addType(FunctionTypeVar{anyTypePack, emptyPack}), "@luau");
addGlobalBinding(typeChecker, "type", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
addGlobalBinding(typeChecker, "typeof", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
addGlobalBinding(typeChecker, "error", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT, optionalNumber}, {}), "@luau");
addGlobalBinding(typeChecker, "tostring", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT}, {stringType}), "@luau");
addGlobalBinding(
typeChecker, "tonumber", makeFunction(arena, std::nullopt, {genericT}, {}, {genericT, optionalNumber}, {numberType}), "@luau");
addGlobalBinding(
typeChecker, "rawequal", makeFunction(arena, std::nullopt, {genericT, genericR}, {}, {genericT, genericR}, {booleanType}), "@luau");
addGlobalBinding(
typeChecker, "rawget", makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV, genericK}, {genericV}), "@luau");
addGlobalBinding(typeChecker, "rawset",
makeFunction(arena, std::nullopt, {genericK, genericV}, {}, {mapOfKtoV, genericK, genericV}, {mapOfKtoV}), "@luau");
TypePackId genericTPack = arena.addTypePack({genericT});
TypePackId genericRPack = arena.addTypePack({genericR});
TypeId genericArgsToReturnFunction = arena.addType(
FunctionTypeVar{{genericT, genericR}, {}, arena.addTypePack(TypePack{{}, genericTPack}), arena.addTypePack(TypePack{{}, genericRPack})});
TypeId setfenvArgType = makeUnion(arena, {numberType, genericArgsToReturnFunction});
TypeId setfenvReturnType = makeOption(typeChecker, arena, genericArgsToReturnFunction);
addGlobalBinding(typeChecker, "setfenv", makeFunction(arena, std::nullopt, {setfenvArgType, stringToAnyMap}, {setfenvReturnType}), "@luau");
TypePackId ipairsArgsTypePack = arena.addTypePack({arrayOfV});
TypeId ipairsNextFunctionType = arena.addType(
FunctionTypeVar{{genericK, genericV}, {}, arena.addTypePack({arrayOfV, numberType}), arena.addTypePack({numberType, genericV})});
// ipairs returns 'next, Array<V>, 0' so we would need type-level primitives and change to
// again, we have a direct reference to 'next' because ipairs returns it
// ipairs<V>(t: Array<V>) -> ((Array<V>) -> (number, V), Array<V>, 0)
TypePackId ipairsReturnTypePack = arena.addTypePack(TypePack{{ipairsNextFunctionType, arrayOfV, numberType}});
// ipairs<V>(t: Array<V>) -> ((Array<V>) -> (number, V), Array<V>, number)
addGlobalBinding(typeChecker, "ipairs", arena.addType(FunctionTypeVar{{genericV}, {}, ipairsArgsTypePack, ipairsReturnTypePack}), "@luau");
TypePackId pcallArg0FnArgs = arena.addTypePack(TypePackVar{GenericTypeVar{"A"}});
TypePackId pcallArg0FnRet = arena.addTypePack(TypePackVar{GenericTypeVar{"R"}});
TypeId pcallArg0 = arena.addType(FunctionTypeVar{pcallArg0FnArgs, pcallArg0FnRet});
TypePackId pcallArgsTypePack = arena.addTypePack(TypePack{{pcallArg0}, pcallArg0FnArgs});
TypePackId pcallReturnTypePack = arena.addTypePack(TypePack{{booleanType}, pcallArg0FnRet});
// pcall<A..., R...>(f: (A...) -> R..., args: A...) -> boolean, R...
addGlobalBinding(typeChecker, "pcall",
arena.addType(FunctionTypeVar{{}, {pcallArg0FnArgs, pcallArg0FnRet}, pcallArgsTypePack, pcallReturnTypePack}), "@luau");
// errors thrown by the function 'f' are propagated onto the function 'err' that accepts it.
// and either 'f' or 'err' are valid results of this xpcall
// if 'err' did throw an error, then it returns: false, "error in error handling"
// TODO: the above is not represented (nor representable) in the type annotation below.
//
// The real type of xpcall is as such: <E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., A...) -> (true, R1...) | (false,
// R2...)
TypePackId genericAPack = arena.addTypePack(TypePackVar{GenericTypeVar{"A"}});
TypePackId genericR1Pack = arena.addTypePack(TypePackVar{GenericTypeVar{"R1"}});
TypePackId genericR2Pack = arena.addTypePack(TypePackVar{GenericTypeVar{"R2"}});
TypeId genericE = arena.addType(GenericTypeVar{"E"});
TypeId xpcallFArg = arena.addType(FunctionTypeVar{genericAPack, genericR1Pack});
TypeId xpcallErrArg = arena.addType(FunctionTypeVar{arena.addTypePack({genericE}), genericR2Pack});
TypePackId xpcallArgsPack = arena.addTypePack({{xpcallFArg, xpcallErrArg}, genericAPack});
TypePackId xpcallRetPack = arena.addTypePack({{booleanType}, genericR1Pack}); // FIXME
addGlobalBinding(typeChecker, "xpcall",
arena.addType(FunctionTypeVar{{genericE}, {genericAPack, genericR1Pack, genericR2Pack}, xpcallArgsPack, xpcallRetPack}), "@luau");
addGlobalBinding(typeChecker, "unpack", unpackFunc, "@luau");
TypePackId selectArgsTypePack = arena.addTypePack(TypePack{
{stringOrNumber},
anyTypePack // FIXME? select() is tricky.
});
addGlobalBinding(typeChecker, "select", arena.addType(FunctionTypeVar{selectArgsTypePack, anyTypePack}), "@luau");
// TODO: not completely correct. loadstring's return type should be a function or (nil, string)
TypeId loadstringFunc = arena.addType(FunctionTypeVar{anyTypePack, oneAnyPack});
addGlobalBinding(typeChecker, "loadstring",
makeFunction(arena, std::nullopt, {stringType, optionalString},
{
makeOption(typeChecker, arena, loadstringFunc),
makeOption(typeChecker, arena, stringType),
}),
"@luau");
// a userdata object is "roughly" the same as a sealed empty table
// except `type(newproxy(false))` evaluates to "userdata" so we may need another special type here too.
// another important thing to note: the value passed in conditionally creates an empty metatable, and you have to use getmetatable, NOT
// setmetatable.
// TODO: change this to something Luau can understand how to reject `setmetatable(newproxy(false or true), {})`.
TypeId sealedTable = arena.addType(TableTypeVar(TableState::Sealed, typeChecker.globalScope->level));
addGlobalBinding(typeChecker, "newproxy", makeFunction(arena, std::nullopt, {optionalBoolean}, {sealedTable}), "@luau");
}
// next<K, V>(t: Table<K, V>, i: K | nil) -> (K, V)
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(typeChecker, arena, genericK)}});
addGlobalBinding(typeChecker, "next",
@ -475,8 +238,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
TypeId pairsNext = (FFlag::LuauRankNTypes ? arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})})
: getGlobalBinding(typeChecker, "next"));
TypeId pairsNext = arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})});
TypePackId pairsReturnTypePack = arena.addTypePack(TypePack{{pairsNext, mapOfKtoV, nilType}});
// NOTE we are missing 'i: K | nil' argument in the first return types' argument.
@ -711,7 +473,7 @@ static std::optional<ExprResult<TypePackId>> magicFunctionRequire(
if (!checkRequirePath(typechecker, expr.args.data[0]))
return std::nullopt;
const AstExpr* require = FFlag::LuauNewRequireTrace ? &expr : expr.args.data[0];
const AstExpr* require = FFlag::LuauNewRequireTrace2 ? &expr : expr.args.data[0];
if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, *require))
return ExprResult<TypePackId>{arena.addTypePack({typechecker.checkRequire(scope, *moduleInfo, expr.location)})};

View file

@ -1,9 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h"
LUAU_FASTFLAG(LuauParseGenericFunctions)
LUAU_FASTFLAG(LuauGenericFunctions)
namespace Luau
{
@ -19,6 +16,8 @@ declare bit32: {
bnot: (number) -> number,
extract: (number, number, number?) -> number,
replace: (number, number, number, number?) -> number,
countlz: (number) -> number,
countrz: (number) -> number,
}
declare math: {
@ -103,15 +102,6 @@ declare _VERSION: string
declare function gcinfo(): number
)BUILTIN_SRC";
std::string getBuiltinDefinitionSource()
{
std::string src = kBuiltinDefinitionLuaSrc;
if (FFlag::LuauParseGenericFunctions && FFlag::LuauGenericFunctions)
{
src += R"(
declare function print<T...>(...: T...)
declare function type<T>(value: T): string
@ -208,10 +198,12 @@ std::string getBuiltinDefinitionSource()
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
)";
}
return src;
)BUILTIN_SRC";
std::string getBuiltinDefinitionSource()
{
return kBuiltinDefinitionLuaSrc;
}
} // namespace Luau

View file

@ -94,8 +94,23 @@ struct ErrorConverter
{
std::string operator()(const Luau::TypeMismatch& tm) const
{
ToStringOptions opts;
return "Type '" + Luau::toString(tm.givenType, opts) + "' could not be converted into '" + Luau::toString(tm.wantedType, opts) + "'";
std::string result = "Type '" + Luau::toString(tm.givenType) + "' could not be converted into '" + Luau::toString(tm.wantedType) + "'";
if (tm.error)
{
result += "\ncaused by:\n ";
if (!tm.reason.empty())
result += tm.reason + ". ";
result += Luau::toString(*tm.error);
}
else if (!tm.reason.empty())
{
result += "; " + tm.reason;
}
return result;
}
std::string operator()(const Luau::UnknownSymbol& e) const
@ -478,9 +493,36 @@ struct InvalidNameChecker
}
};
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType)
: wantedType(wantedType)
, givenType(givenType)
{
}
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason)
: wantedType(wantedType)
, givenType(givenType)
, reason(reason)
{
}
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error)
: wantedType(wantedType)
, givenType(givenType)
, reason(reason)
, error(std::make_shared<TypeError>(std::move(error)))
{
}
bool TypeMismatch::operator==(const TypeMismatch& rhs) const
{
return *wantedType == *rhs.wantedType && *givenType == *rhs.givenType;
if (!!error != !!rhs.error)
return false;
if (error && !(*error == *rhs.error))
return false;
return *wantedType == *rhs.wantedType && *givenType == *rhs.givenType && reason == rhs.reason;
}
bool UnknownSymbol::operator==(const UnknownSymbol& rhs) const
@ -690,17 +732,16 @@ bool containsParseErrorName(const TypeError& error)
return Luau::visit(InvalidNameChecker{}, error.data);
}
void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
template<typename T>
void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
auto clone = [&](auto&& ty) {
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
};
auto visitErrorData = [&](auto&& e) {
using T = std::decay_t<decltype(e)>;
copyError(e, destArena, seenTypes, seenTypePacks);
};
if constexpr (false)
{
@ -709,6 +750,9 @@ void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
{
e.wantedType = clone(e.wantedType);
e.givenType = clone(e.givenType);
if (e.error)
visit(visitErrorData, e.error->data);
}
else if constexpr (std::is_same_v<T, UnknownSymbol>)
{
@ -814,6 +858,15 @@ void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
}
else
static_assert(always_false_v<T>, "Non-exhaustive type switch");
}
void copyErrors(ErrorVec& errors, TypeArena& destArena)
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
auto visitErrorData = [&](auto&& e) {
copyError(e, destArena, seenTypes, seenTypePacks);
};
LUAU_ASSERT(!destArena.typeVars.isFrozen());

View file

@ -18,11 +18,10 @@
LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTFLAGVARIABLE(LuauSecondTypecheckKnowsTheDataModel, false)
LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
LUAU_FASTFLAG(LuauNewRequireTrace)
LUAU_FASTFLAG(LuauNewRequireTrace2)
LUAU_FASTFLAGVARIABLE(LuauClearScopes, false)
namespace Luau
@ -415,7 +414,7 @@ CheckResult Frontend::check(const ModuleName& name)
// If we're typechecking twice, we do so.
// The second typecheck is always in strict mode with DM awareness
// to provide better typen information for IDE features.
if (options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel)
if (options.typecheckTwice)
{
ModulePtr moduleForAutocomplete = typeCheckerForAutocomplete.check(sourceModule, Mode::Strict);
moduleResolverForAutocomplete.modules[moduleName] = moduleForAutocomplete;
@ -897,7 +896,7 @@ std::optional<ModuleInfo> FrontendModuleResolver::resolveModuleInfo(const Module
const auto& exprs = it->second.exprs;
const ModuleInfo* info = exprs.find(&pathExpr);
if (!info || (!FFlag::LuauNewRequireTrace && info->name.empty()))
if (!info || (!FFlag::LuauNewRequireTrace2 && info->name.empty()))
return std::nullopt;
return *info;
@ -914,7 +913,7 @@ const ModulePtr FrontendModuleResolver::getModule(const ModuleName& moduleName)
bool FrontendModuleResolver::moduleExists(const ModuleName& moduleName) const
{
if (FFlag::LuauNewRequireTrace)
if (FFlag::LuauNewRequireTrace2)
return frontend->sourceNodes.count(moduleName) != 0;
else
return frontend->fileResolver->moduleExists(moduleName);

View file

@ -12,9 +12,6 @@
#include <math.h>
#include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauLinterUnknownTypeVectorAware, false)
LUAU_FASTFLAGVARIABLE(LuauLinterTableMoveZero, false)
namespace Luau
{
@ -1110,10 +1107,7 @@ private:
if (g && g->name == "type")
{
if (FFlag::LuauLinterUnknownTypeVectorAware)
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
else
validateType(arg, {Kind_Primitive}, "primitive type");
}
else if (g && g->name == "typeof")
{
@ -2146,7 +2140,7 @@ private:
"wrap it in parentheses to silence");
}
if (FFlag::LuauLinterTableMoveZero && func->index == "move" && node->args.size >= 4)
if (func->index == "move" && node->args.size >= 4)
{
// table.move(t, 0, _, _)
if (isConstant(args[1], 0.0))

View file

@ -12,7 +12,6 @@
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
LUAU_FASTFLAG(LuauTypeAliasPacks)
LUAU_FASTFLAGVARIABLE(LuauCloneBoundTables, false)
@ -290,9 +289,7 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
for (TypePackId genericPack : t.genericPacks)
ftv->genericPacks.push_back(clone(genericPack, dest, seenTypes, seenTypePacks, encounteredFreeType));
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
ftv->tags = t.tags;
ftv->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, encounteredFreeType);
ftv->argNames = t.argNames;
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
@ -319,12 +316,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
ttv->level = TypeLevel{0, 0};
for (const auto& [name, prop] : t.props)
{
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
else
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
}
if (t.indexer)
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
@ -379,10 +371,7 @@ void TypeCloner::operator()(const ClassTypeVar& t)
seenTypes[typeId] = result;
for (const auto& [name, prop] : t.props)
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
else
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
if (t.parent)
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);

View file

@ -3,8 +3,6 @@
#include "Luau/Ast.h"
LUAU_FASTFLAG(LuauOrPredicate)
namespace Luau
{
@ -60,8 +58,6 @@ std::string toString(const LValue& lvalue)
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
{
LUAU_ASSERT(FFlag::LuauOrPredicate);
auto itL = l.begin();
auto itR = r.begin();
while (itL != l.end() && itR != r.end())

View file

@ -5,7 +5,7 @@
#include "Luau/Module.h"
LUAU_FASTFLAGVARIABLE(LuauTraceRequireLookupChild, false)
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace, false)
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace2, false)
namespace Luau
{
@ -19,7 +19,7 @@ struct RequireTracerOld : AstVisitor
: fileResolver(fileResolver)
, currentModuleName(currentModuleName)
{
LUAU_ASSERT(!FFlag::LuauNewRequireTrace);
LUAU_ASSERT(!FFlag::LuauNewRequireTrace2);
}
FileResolver* const fileResolver;
@ -188,7 +188,7 @@ struct RequireTracer : AstVisitor
, currentModuleName(currentModuleName)
, locals(nullptr)
{
LUAU_ASSERT(FFlag::LuauNewRequireTrace);
LUAU_ASSERT(FFlag::LuauNewRequireTrace2);
}
bool visit(AstExprTypeAssertion* expr) override
@ -332,7 +332,7 @@ struct RequireTracer : AstVisitor
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName)
{
if (FFlag::LuauNewRequireTrace)
if (FFlag::LuauNewRequireTrace2)
{
RequireTraceResult result;
RequireTracer tracer{result, fileResolver, currentModuleName};

View file

@ -8,8 +8,6 @@
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000)
LUAU_FASTFLAGVARIABLE(LuauSubstitutionDontReplaceIgnoredTypes, false)
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
LUAU_FASTFLAG(LuauRankNTypes)
LUAU_FASTFLAG(LuauTypeAliasPacks)
namespace Luau
@ -19,7 +17,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
{
ty = follow(ty);
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
if (ignoreChildren(ty))
return;
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
@ -68,7 +66,7 @@ void Tarjan::visitChildren(TypePackId tp, int index)
{
tp = follow(tp);
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
if (ignoreChildren(tp))
return;
if (const TypePack* tpp = get<TypePack>(tp))
@ -399,7 +397,6 @@ TypeId Substitution::clone(TypeId ty)
if (FFlag::LuauTypeAliasPacks)
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
clone.tags = ttv->tags;
result = addType(std::move(clone));
}
@ -486,7 +483,7 @@ void Substitution::replaceChildren(TypeId ty)
{
ty = follow(ty);
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
if (ignoreChildren(ty))
return;
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
@ -535,7 +532,7 @@ void Substitution::replaceChildren(TypePackId tp)
{
tp = follow(tp);
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
if (ignoreChildren(tp))
return;
if (TypePack* tpp = getMutable<TypePack>(tp))

View file

@ -11,7 +11,6 @@
#include <stdexcept>
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
LUAU_FASTFLAGVARIABLE(LuauInstantiatedTypeParamRecursion, false)
LUAU_FASTFLAG(LuauTypeAliasPacks)
namespace Luau
@ -237,15 +236,6 @@ struct TypeVarStringifier
return;
}
if (!FFlag::LuauAddMissingFollow)
{
if (get<FreeTypeVar>(tv))
{
state.emit(state.getName(tv));
return;
}
}
Luau::visit(
[this, tv](auto&& t) {
return (*this)(tv, t);
@ -316,11 +306,7 @@ struct TypeVarStringifier
void operator()(TypeId ty, const Unifiable::Free& ftv)
{
state.result.invalid = true;
if (FFlag::LuauAddMissingFollow)
state.emit(state.getName(ty));
else
state.emit("<FREE>");
}
void operator()(TypeId, const BoundTypeVar& btv)
@ -724,16 +710,6 @@ struct TypePackStringifier
return;
}
if (!FFlag::LuauAddMissingFollow)
{
if (get<FreeTypePack>(tp))
{
state.emit(state.getName(tp));
state.emit("...");
return;
}
}
auto it = state.cycleTpNames.find(tp);
if (it != state.cycleTpNames.end())
{
@ -821,17 +797,9 @@ struct TypePackStringifier
void operator()(TypePackId tp, const FreeTypePack& pack)
{
state.result.invalid = true;
if (FFlag::LuauAddMissingFollow)
{
state.emit(state.getName(tp));
state.emit("...");
}
else
{
state.emit("<FREETP>");
}
}
void operator()(TypePackId, const BoundTypePack& btv)
{
@ -864,8 +832,6 @@ static void assignCycleNames(const std::unordered_set<TypeId>& cycles, const std
std::string name;
// TODO: use the stringified type list if there are no cycles
if (FFlag::LuauInstantiatedTypeParamRecursion)
{
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
{
// If 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;
}
}
else
{
if (auto ttv = get<TableTypeVar>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
continue;
}
name = "t" + std::to_string(nextIndex);
++nextIndex;
@ -912,58 +872,6 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
ToStringResult result;
if (!FFlag::LuauInstantiatedTypeParamRecursion && !opts.exhaustive)
{
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
{
if (ttv->syntheticName)
result.invalid = true;
// If scope if provided, add module name and check visibility
if (ttv->name && opts.scope)
{
auto [success, moduleName] = canUseTypeNameInScope(opts.scope, *ttv->name);
if (!success)
result.invalid = true;
if (moduleName)
result.name = format("%s.", moduleName->c_str());
}
result.name += ttv->name ? *ttv->name : *ttv->syntheticName;
if (ttv->instantiatedTypeParams.empty() && (!FFlag::LuauTypeAliasPacks || ttv->instantiatedTypePackParams.empty()))
return result;
std::vector<std::string> params;
for (TypeId tp : ttv->instantiatedTypeParams)
params.push_back(toString(tp));
if (FFlag::LuauTypeAliasPacks)
{
// Doesn't preserve grouping of multiple type packs
// But this is under a parent block of code that is being removed later
for (TypePackId tp : ttv->instantiatedTypePackParams)
{
std::string content = toString(tp);
if (!content.empty())
params.push_back(std::move(content));
}
}
result.name += "<" + join(params, ", ") + ">";
return result;
}
else if (auto mtv = get<MetatableTypeVar>(ty); mtv && mtv->syntheticName)
{
result.invalid = true;
result.name = *mtv->syntheticName;
return result;
}
}
StringifierState state{opts, result, opts.nameMap};
std::unordered_set<TypeId> cycles;
@ -975,7 +883,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
TypeVarStringifier tvs{state};
if (FFlag::LuauInstantiatedTypeParamRecursion && !opts.exhaustive)
if (!opts.exhaustive)
{
if (auto ttv = get<TableTypeVar>(ty); ttv && (ttv->name || ttv->syntheticName))
{

View file

@ -10,7 +10,6 @@
#include <limits>
#include <math.h>
LUAU_FASTFLAG(LuauGenericFunctions)
LUAU_FASTFLAG(LuauTypeAliasPacks)
namespace
@ -97,9 +96,6 @@ struct Writer
{
virtual ~Writer() {}
virtual void begin() {}
virtual void end() {}
virtual void advance(const Position&) = 0;
virtual void newline() = 0;
virtual void space() = 0;
@ -131,6 +127,7 @@ struct StringWriter : Writer
if (pos.column < newPos.column)
write(std::string(newPos.column - pos.column, ' '));
}
void maybeSpace(const Position& newPos, int reserve) override
{
if (pos.column + reserve < newPos.column)
@ -279,11 +276,14 @@ struct Printer
writer.identifier(func->index.value);
}
void visualizeTypePackAnnotation(const AstTypePack& annotation)
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg)
{
advance(annotation.location.begin);
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
{
if (!forVarArg)
writer.symbol("...");
visualizeTypeAnnotation(*variadicTp->variadicType);
}
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
@ -293,6 +293,7 @@ struct Printer
}
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
{
LUAU_ASSERT(!forVarArg);
visualizeTypeList(explicitTp->typeList, true);
}
else
@ -317,7 +318,7 @@ struct Printer
// Only variadic tail
if (list.types.size == 0)
{
visualizeTypePackAnnotation(*list.tailType);
visualizeTypePackAnnotation(*list.tailType, false);
}
else
{
@ -345,7 +346,7 @@ struct Printer
if (list.tailType)
{
writer.symbol(",");
visualizeTypePackAnnotation(*list.tailType);
visualizeTypePackAnnotation(*list.tailType, false);
}
writer.symbol(")");
@ -542,6 +543,7 @@ struct Printer
case AstExprBinary::CompareLt:
case AstExprBinary::CompareGt:
writer.maybeSpace(a->right->location.begin, 2);
writer.symbol(toString(a->op));
break;
case AstExprBinary::Concat:
case AstExprBinary::CompareNe:
@ -550,19 +552,35 @@ struct Printer
case AstExprBinary::CompareGe:
case AstExprBinary::Or:
writer.maybeSpace(a->right->location.begin, 3);
writer.keyword(toString(a->op));
break;
case AstExprBinary::And:
writer.maybeSpace(a->right->location.begin, 4);
writer.keyword(toString(a->op));
break;
}
writer.symbol(toString(a->op));
visualize(*a->right);
}
else if (const auto& a = expr.as<AstExprTypeAssertion>())
{
visualize(*a->expr);
if (writeTypes)
{
writer.maybeSpace(a->annotation->location.begin, 2);
writer.symbol("::");
visualizeTypeAnnotation(*a->annotation);
}
}
else if (const auto& a = expr.as<AstExprIfElse>())
{
writer.keyword("if");
visualize(*a->condition);
writer.keyword("then");
visualize(*a->trueExpr);
writer.keyword("else");
visualize(*a->falseExpr);
}
else if (const auto& a = expr.as<AstExprError>())
{
@ -769,24 +787,31 @@ struct Printer
switch (a->op)
{
case AstExprBinary::Add:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("+=");
break;
case AstExprBinary::Sub:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("-=");
break;
case AstExprBinary::Mul:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("*=");
break;
case AstExprBinary::Div:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("/=");
break;
case AstExprBinary::Mod:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("%=");
break;
case AstExprBinary::Pow:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("^=");
break;
case AstExprBinary::Concat:
writer.maybeSpace(a->value->location.begin, 3);
writer.symbol("..=");
break;
default:
@ -874,7 +899,7 @@ struct Printer
void visualizeFunctionBody(AstExprFunction& func)
{
if (FFlag::LuauGenericFunctions && (func.generics.size > 0 || func.genericPacks.size > 0))
if (func.generics.size > 0 || func.genericPacks.size > 0)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
@ -913,12 +938,13 @@ struct Printer
if (func.vararg)
{
comma();
advance(func.varargLocation.begin);
writer.symbol("...");
if (func.varargAnnotation)
{
writer.symbol(":");
visualizeTypePackAnnotation(*func.varargAnnotation);
visualizeTypePackAnnotation(*func.varargAnnotation, true);
}
}
@ -980,8 +1006,14 @@ struct Printer
advance(typeAnnotation.location.begin);
if (const auto& a = typeAnnotation.as<AstTypeReference>())
{
if (a->hasPrefix)
{
writer.write(a->prefix.value);
writer.symbol(".");
}
writer.write(a->name.value);
if (a->parameters.size > 0)
if (a->parameters.size > 0 || a->hasParameterList)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
@ -992,7 +1024,7 @@ struct Printer
if (o.type)
visualizeTypeAnnotation(*o.type);
else
visualizeTypePackAnnotation(*o.typePack);
visualizeTypePackAnnotation(*o.typePack, false);
}
writer.symbol(">");
@ -1000,7 +1032,7 @@ struct Printer
}
else if (const auto& a = typeAnnotation.as<AstTypeFunction>())
{
if (FFlag::LuauGenericFunctions && (a->generics.size > 0 || a->genericPacks.size > 0))
if (a->generics.size > 0 || a->genericPacks.size > 0)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
@ -1075,7 +1107,16 @@ struct Printer
auto rta = r->as<AstTypeReference>();
if (rta && rta->name == "nil")
{
bool wrap = l->as<AstTypeIntersection>() || l->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
visualizeTypeAnnotation(*l);
if (wrap)
writer.symbol(")");
writer.symbol("?");
return;
}
@ -1089,7 +1130,15 @@ struct Printer
writer.symbol("|");
}
bool wrap = a->types.data[i]->as<AstTypeIntersection>() || a->types.data[i]->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
visualizeTypeAnnotation(*a->types.data[i]);
if (wrap)
writer.symbol(")");
}
}
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
@ -1102,7 +1151,15 @@ struct Printer
writer.symbol("&");
}
bool wrap = a->types.data[i]->as<AstTypeUnion>() || a->types.data[i]->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
visualizeTypeAnnotation(*a->types.data[i]);
if (wrap)
writer.symbol(")");
}
}
else if (typeAnnotation.is<AstTypeError>())
@ -1116,31 +1173,27 @@ struct Printer
}
};
void dump(AstNode* node)
std::string toString(AstNode* node)
{
StringWriter writer;
writer.pos = node->location.begin;
Printer printer(writer);
printer.writeTypes = true;
if (auto statNode = dynamic_cast<AstStat*>(node))
{
printer.visualize(*statNode);
printf("%s\n", writer.str().c_str());
}
else if (auto exprNode = dynamic_cast<AstExpr*>(node))
{
printer.visualize(*exprNode);
printf("%s\n", writer.str().c_str());
}
else if (auto typeNode = dynamic_cast<AstType*>(node))
{
printer.visualizeTypeAnnotation(*typeNode);
printf("%s\n", writer.str().c_str());
return writer.str();
}
else
void dump(AstNode* node)
{
printf("Can't dump this node\n");
}
printf("%s\n", toString(node).c_str());
}
std::string transpile(AstStatBlock& block)
@ -1149,6 +1202,7 @@ std::string transpile(AstStatBlock& block)
Printer(writer).visualizeBlock(block);
return writer.str();
}
std::string transpileWithTypes(AstStatBlock& block)
{
StringWriter writer;
@ -1158,7 +1212,7 @@ std::string transpileWithTypes(AstStatBlock& block)
return writer.str();
}
TranspileResult transpile(std::string_view source, ParseOptions options)
TranspileResult transpile(std::string_view source, ParseOptions options, bool withTypes)
{
auto allocator = Allocator{};
auto names = AstNameTable{allocator};
@ -1176,6 +1230,9 @@ TranspileResult transpile(std::string_view source, ParseOptions options)
if (!parseResult.root)
return TranspileResult{"", {}, "Internal error: Parser yielded empty parse tree"};
if (withTypes)
return TranspileResult{transpileWithTypes(*parseResult.root)};
return TranspileResult{transpile(*parseResult.root)};
}

View file

@ -13,7 +13,6 @@
#include <string>
LUAU_FASTFLAG(LuauGenericFunctions)
LUAU_FASTFLAG(LuauTypeAliasPacks)
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
@ -203,39 +202,23 @@ public:
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("<Cycle>"));
AstArray<AstName> generics;
if (FFlag::LuauGenericFunctions)
{
generics.size = ftv.generics.size();
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
size_t i = 0;
size_t numGenerics = 0;
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
{
if (auto gtv = get<GenericTypeVar>(*it))
generics.data[i++] = AstName(gtv->name.c_str());
}
}
else
{
generics.size = 0;
generics.data = nullptr;
generics.data[numGenerics++] = AstName(gtv->name.c_str());
}
AstArray<AstName> genericPacks;
if (FFlag::LuauGenericFunctions)
{
genericPacks.size = ftv.genericPacks.size();
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
size_t i = 0;
size_t numGenericPacks = 0;
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
{
if (auto gtv = get<GenericTypeVar>(*it))
genericPacks.data[i++] = AstName(gtv->name.c_str());
}
}
else
{
generics.size = 0;
generics.data = nullptr;
genericPacks.data[numGenericPacks++] = AstName(gtv->name.c_str());
}
AstArray<AstType*> argTypes;

View file

@ -22,29 +22,19 @@ LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false)
LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 500)
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauGenericFunctions, false)
LUAU_FASTFLAGVARIABLE(LuauGenericVariadicsUnification, false)
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauSecondTypecheckKnowsTheDataModel)
LUAU_FASTFLAGVARIABLE(LuauClassPropertyAccessAsString, false)
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
LUAU_FASTFLAGVARIABLE(LuauCloneCorrectlyBeforeMutatingTableType, false)
LUAU_FASTFLAGVARIABLE(LuauStoreMatchingOverloadFnType, false)
LUAU_FASTFLAGVARIABLE(LuauRankNTypes, false)
LUAU_FASTFLAGVARIABLE(LuauOrPredicate, false)
LUAU_FASTFLAGVARIABLE(LuauInferReturnAssertAssign, 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(LuauStrictRequire, false)
LUAU_FASTFLAG(LuauSubstitutionDontReplaceIgnoredTypes)
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
LUAU_FASTFLAG(LuauNewRequireTrace)
LUAU_FASTFLAG(LuauNewRequireTrace2)
LUAU_FASTFLAG(LuauTypeAliasPacks)
namespace Luau
@ -222,9 +212,9 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan
, threadType(singletonTypes.threadType)
, anyType(singletonTypes.anyType)
, errorType(singletonTypes.errorType)
, optionalNumberType(globalTypes.addType(UnionTypeVar{{numberType, nilType}}))
, anyTypePack(globalTypes.addTypePack(TypePackVar{VariadicTypePack{singletonTypes.anyType}, true}))
, errorTypePack(globalTypes.addTypePack(TypePackVar{Unifiable::Error{}}))
, optionalNumberType(singletonTypes.optionalNumberType)
, anyTypePack(singletonTypes.anyTypePack)
, errorTypePack(singletonTypes.errorTypePack)
{
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)
moduleScope->returnType = addTypePack(TypePack{{anyType}, std::nullopt});
else if (FFlag::LuauRankNTypes)
moduleScope->returnType = freshTypePack(moduleScope);
else
moduleScope->returnType = DEPRECATED_freshTypePack(moduleScope, true);
moduleScope->returnType = freshTypePack(moduleScope);
moduleScope->varargPack = anyTypePack;
@ -268,7 +256,7 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
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});
else
moduleScope->returnType = anyify(moduleScope, moduleScope->returnType, Location{});
@ -326,7 +314,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStat& program)
check(scope, *typealias);
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);
currentModule->declaredGlobals[globalName] = globalType;
@ -494,7 +482,7 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std
Name name = typealias->name.value;
TypeId type = bindings[name].type;
if (get<FreeTypeVar>(FFlag::LuauAddMissingFollow ? follow(type) : type))
if (get<FreeTypeVar>(follow(type)))
{
*asMutable(type) = ErrorTypeVar{};
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_)
{
std::vector<std::optional<TypeId>> expectedTypes;
if (FFlag::LuauInferReturnAssertAssign)
{
expectedTypes.reserve(return_.list.size);
TypePackIterator expectedRetCurr = begin(scope->returnType);
@ -628,7 +613,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
expectedTypes.push_back(vtp->ty);
}
}
}
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)
{
std::vector<std::optional<TypeId>> expectedTypes;
if (FFlag::LuauInferReturnAssertAssign)
{
expectedTypes.reserve(assign.vars.size);
ScopePtr moduleScope = currentModule->getModuleScope();
@ -701,7 +682,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
expectedTypes.push_back(checkLValue(scope, *dest));
}
}
}
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];
TypeId left = nullptr;
if (!FFlag::LuauInferReturnAssertAssign || dest->is<AstExprLocal>() || dest->is<AstExprGlobal>())
if (dest->is<AstExprLocal>() || dest->is<AstExprGlobal>())
left = checkLValue(scope, *dest);
else
left = *expectedTypes[i];
@ -751,11 +731,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
if (right)
{
if (FFlag::LuauGenericFunctions && !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))
if (!maybeGeneric(left) && isGeneric(right))
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
@ -766,7 +742,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
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.
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);
else
unify(left, right, loc);
@ -815,7 +791,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
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 (get<ErrorTypeVar>(follow(ty)))
@ -823,23 +799,19 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
}
if (!ty)
ty = rhsIsTable ? (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true))
: isNonstrictMode() ? anyType : (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
ty = rhsIsTable ? freshType(scope) : isNonstrictMode() ? anyType : freshType(scope);
varBindings.emplace_back(vars[i], Binding{ty, vars[i]->location});
variableTypes.push_back(ty);
expectedTypes.push_back(ty);
if (FFlag::LuauGenericFunctions)
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)
{
TypePackId variablePack = addTypePack(variableTypes, FFlag::LuauRankNTypes ? freshTypePack(scope) : DEPRECATED_freshTypePack(scope, true));
TypePackId variablePack = addTypePack(variableTypes, freshTypePack(scope));
TypePackId valuePack =
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>();
callRetPack = checkExprPack(scope, *exprCall).type;
if (!FFlag::LuauRankNTypes)
callRetPack = DEPRECATED_instantiate(scope, callRetPack, exprCall->location);
callRetPack = follow(callRetPack);
if (get<Unifiable::Free>(callRetPack))
@ -998,7 +968,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
else
{
iterTy = *first(callRetPack);
if (FFlag::LuauRankNTypes)
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);
if (FFlag::LuauGenericFunctions)
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)
@ -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);
TypeId ty = (FFlag::LuauRankNTypes ? freshType(aliasScope) : DEPRECATED_freshType(scope, true));
TypeId ty = freshType(aliasScope);
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
LUAU_ASSERT(ftv);
ftv->forwardedTypeAlias = true;
@ -1234,7 +1200,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
aliasScope->privateTypeBindings[n] = TypeFun{{}, g};
}
TypeId ty = (FFlag::LuauRankNTypes ? freshType(aliasScope) : DEPRECATED_freshType(scope, true));
TypeId ty = freshType(aliasScope);
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
LUAU_ASSERT(ftv);
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 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));
superTy = lookupType->type;
if (FFlag::LuauAddMissingFollow)
{
if (!get<ClassTypeVar>(follow(*superTy)))
{
reportError(declaredClass.location, GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'",
superName.c_str(), declaredClass.name.value)});
reportError(declaredClass.location,
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass.name.value)});
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);
@ -1558,8 +1511,8 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprVa
}
else if (auto ftp = get<FreeTypePack>(varargPack))
{
TypeId head = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, ftp->DEPRECATED_canBeGeneric));
TypePackId tail = (FFlag::LuauRankNTypes ? freshTypePack(scope) : DEPRECATED_freshTypePack(scope, ftp->DEPRECATED_canBeGeneric));
TypeId head = freshType(scope);
TypePackId tail = freshTypePack(scope);
*asMutable(varargPack) = TypePack{{head}, tail};
return {head};
}
@ -1567,7 +1520,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprVa
return {errorType};
else if (auto vtp = get<VariadicTypePack>(varargPack))
return {vtp->ty};
else if (FFlag::LuauGenericVariadicsUnification && get<Unifiable::Generic>(varargPack))
else if (get<Unifiable::Generic>(varargPack))
{
// TODO: Better error?
reportError(expr.location, GenericError{"Trying to get a type from a variadic type parameter"});
@ -1588,7 +1541,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprCa
}
else if (auto ftp = 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)}});
unify(retPack, pack, expr.location);
return {head, std::move(result.predicates)};
@ -1667,7 +1620,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
}
else if (tableType->state == TableState::Free)
{
TypeId result = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
TypeId result = freshType(scope);
tableType->props[name] = {result};
return result;
}
@ -2157,18 +2110,11 @@ TypeId TypeChecker::checkRelationalOperation(
if (AstExprBinary* subexp = expr.left->as<AstExprBinary>())
{
if (expr.op == AstExprBinary::Or && subexp->op == AstExprBinary::And)
{
if (FFlag::LuauSlightlyMoreFlexibleBinaryPredicates)
{
ScopePtr subScope = childScope(scope, subexp->location);
reportErrors(resolve(predicates, subScope, true));
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
@ -2217,10 +2163,8 @@ TypeId TypeChecker::checkRelationalOperation(
std::string metamethodName = opToMetaTableEntry(expr.op);
std::optional<TypeId> leftMetatable =
isString(lhsType) ? std::nullopt : getMetatable(FFlag::LuauAddMissingFollow ? follow(lhsType) : lhsType);
std::optional<TypeId> rightMetatable =
isString(rhsType) ? std::nullopt : getMetatable(FFlag::LuauAddMissingFollow ? follow(rhsType) : rhsType);
std::optional<TypeId> leftMetatable = isString(lhsType) ? std::nullopt : getMetatable(follow(lhsType));
std::optional<TypeId> rightMetatable = isString(rhsType) ? std::nullopt : getMetatable(follow(rhsType));
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);
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);
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)}}};
}
else if (FFlag::LuauOrPredicate && expr.op == AstExprBinary::Or)
else if (expr.op == AstExprBinary::Or)
{
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> result;
TypeId 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));
}
TypeId annotationType = resolveType(scope, *expr.annotation);
ExprResult<TypeId> result = checkExpr(scope, *expr.expr, annotationType);
ErrorVec errorVec = canUnify(result.type, annotationType, expr.location);
if (!errorVec.empty())
@ -2570,10 +2501,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
if (it != moduleScope->bindings.end())
return std::pair(it->second.typeId, &it->second.typeId);
if (isNonstrictMode() || FFlag::LuauSecondTypecheckKnowsTheDataModel)
{
TypeId result = (FFlag::LuauGenericFunctions && FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(moduleScope, true));
TypeId result = freshType(scope);
Binding& binding = moduleScope->bindings[expr.name];
binding = {result, expr.location};
@ -2585,10 +2513,6 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
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)
{
TypeId lhs = checkExpr(scope, *expr.expr).type;
@ -2611,7 +2535,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
}
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.type = theType;
property.location = expr.indexLocation;
@ -2683,7 +2607,7 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
AstExprConstantString* value = expr.index->as<AstExprConstantString>();
if (value && FFlag::LuauClassPropertyAccessAsString)
if (value)
{
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)
{
TypeId resultType = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
TypeId resultType = freshType(scope);
Property& property = exprTable->props[value->value.data];
property.type = resultType;
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)
{
TypeId resultType = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
TypeId resultType = freshType(scope);
exprTable->indexer = TableIndexer{anyIfNonstrict(indexType), anyIfNonstrict(resultType)};
return std::pair(resultType, nullptr);
}
@ -2758,7 +2682,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
}
else
{
TypeId ty = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
TypeId ty = freshType(scope);
globalScope->bindings[name] = {ty, funName.location};
return ty;
}
@ -2768,7 +2692,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
Symbol name = localName->local;
Binding& binding = scope->bindings[name];
if (binding.typeId == nullptr)
binding = {(FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true)), funName.location};
binding = {freshType(scope), funName.location};
return binding.typeId;
}
@ -2798,7 +2722,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
Property& property = ttv->props[name];
property.type = (FFlag::LuauRankNTypes ? freshType(scope) : DEPRECATED_freshType(scope, true));
property.type = freshType(scope);
property.location = indexName->indexLocation;
ttv->methodDefinitionLocations[name] = funName.location;
return property.type;
@ -2865,22 +2789,11 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
expectedFunctionType = nullptr;
}
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
if (FFlag::LuauGenericFunctions)
{
std::tie(generics, genericPacks) = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
}
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
TypePackId retPack;
if (expr.hasReturnAnnotation)
{
if (FFlag::LuauGenericFunctions)
retPack = resolveTypePack(funScope, expr.returnAnnotation);
else
retPack = resolveTypePack(scope, expr.returnAnnotation);
}
else if (isNonstrictMode())
retPack = anyTypePack;
else if (expectedFunctionType)
@ -2889,24 +2802,17 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
// Do not infer 'nil' as function return type
if (!tail && head.size() == 1 && isNil(head[0]))
retPack = FFlag::LuauGenericFunctions ? freshTypePack(funScope) : freshTypePack(scope);
retPack = freshTypePack(funScope);
else
retPack = addTypePack(head, tail);
}
else if (FFlag::LuauGenericFunctions)
retPack = freshTypePack(funScope);
else
retPack = freshTypePack(scope);
retPack = freshTypePack(funScope);
if (expr.vararg)
{
if (expr.varargAnnotation)
{
if (FFlag::LuauGenericFunctions)
funScope->varargPack = resolveTypePack(funScope, *expr.varargAnnotation);
else
funScope->varargPack = resolveTypePack(scope, *expr.varargAnnotation);
}
else
{
if (expectedFunctionType && !isNonstrictMode())
@ -2963,7 +2869,7 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
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 (get<ErrorTypeVar>(follow(argType)))
@ -3022,7 +2928,7 @@ static bool allowsNoReturnValues(const TypePackId tp)
{
for (TypeId ty : tp)
{
if (!get<ErrorTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty))
if (!get<ErrorTypeVar>(follow(ty)))
{
return false;
}
@ -3058,7 +2964,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
check(scope, *function.body);
// 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};
bool reachesImplicitReturn = getFallthrough(function.body) != nullptr;
@ -3287,7 +3193,7 @@ void TypeChecker::checkArgumentList(
return;
}
else if (FFlag::LuauGenericVariadicsUnification && get<FreeTypePack>(tail))
else if (get<FreeTypePack>(tail))
{
// Create a type pack out of the remaining argument types
// and unify it with the tail.
@ -3310,7 +3216,7 @@ void TypeChecker::checkArgumentList(
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
Location location = state.location;
@ -3323,10 +3229,7 @@ void TypeChecker::checkArgumentList(
}
else
{
if (FFlag::LuauRankNTypes)
unifyWithInstantiationIfNeeded(scope, *paramIter, *argIter, state);
else
state.tryUnify(*paramIter, *argIter, /*isFunctionCall*/ false);
++argIter;
++paramIter;
}
@ -3356,9 +3259,6 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
ice("method call expression has no 'self'");
selfType = checkExpr(scope, *indexExpr->expr).type;
if (!FFlag::LuauRankNTypes)
instantiate(scope, selfType, expr.func->location);
selfType = stripFromNilAndReport(selfType, expr.func->location);
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);
ExprResult<TypePackId> argListResult = checkExprList(scope, expr.location, expr.args, false, {}, expectedTypes);
TypePackId argList = argListResult.type;
TypePackId argPack = (FFlag::LuauRankNTypes ? argList : DEPRECATED_instantiate(scope, argList, expr.location));
TypePackId argPack = argListResult.type;
if (get<Unifiable::Error>(argPack))
return ExprResult<TypePackId>{errorTypePack};
@ -3526,7 +3425,6 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
TypeId fn = *ty;
if (FFlag::LuauRankNTypes)
fn = instantiate(scope, fn, expr.func->location);
return checkCallOverload(
@ -3800,7 +3698,7 @@ ExprResult<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, const L
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);
if (expectedType)
@ -3837,7 +3735,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
LUAU_TIMETRACE_SCOPE("TypeChecker::checkRequire", "TypeChecker");
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)
{
@ -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)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
Unifier state = mkUnifier(location);
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)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
if (!maybeGeneric(right))
// Quick check to see if we definitely can't instantiate
state.tryUnify(left, right, /*isFunctionCall*/ false);
@ -3972,8 +3868,6 @@ void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId l
}
bool Instantiation::isDirty(TypeId ty)
{
if (FFlag::LuauRankNTypes)
{
if (get<FunctionTypeVar>(ty))
return true;
@ -3981,30 +3875,13 @@ bool Instantiation::isDirty(TypeId ty)
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)
{
if (FFlag::LuauRankNTypes)
return false;
if (get<GenericTypePack>(tp))
return true;
else
return false;
}
bool Instantiation::ignoreChildren(TypeId ty)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
if (get<FunctionTypeVar>(ty))
return true;
else
@ -4013,18 +3890,15 @@ bool Instantiation::ignoreChildren(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};
clone.magicFunction = ftv->magicFunction;
clone.tags = ftv->tags;
clone.argNames = ftv->argNames;
TypeId result = addType(std::move(clone));
if (FFlag::LuauRankNTypes)
{
// Annoyingly, we have to do this even if there are no generics,
// to replace any generic tables.
replaceGenerics.level = level;
@ -4035,41 +3909,19 @@ TypeId Instantiation::clean(TypeId ty)
// TODO: What to do if this returns nullopt?
// We don't have access to the error-reporting machinery
result = replaceGenerics.substitute(result).value_or(result);
}
asMutable(result)->documentationSymbol = ty->documentationSymbol;
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)
{
LUAU_ASSERT(!FFlag::LuauRankNTypes);
return addTypePack(TypePackVar(FreeTypePack{level}));
LUAU_ASSERT(false);
return tp;
}
bool ReplaceGenerics::ignoreChildren(TypeId ty)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
// 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.
@ -4083,7 +3935,6 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
bool ReplaceGenerics::isDirty(TypeId ty)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
return ttv->state == TableState::Generic;
else if (get<GenericTypeVar>(ty))
@ -4094,7 +3945,6 @@ bool ReplaceGenerics::isDirty(TypeId ty)
bool ReplaceGenerics::isDirty(TypePackId tp)
{
LUAU_ASSERT(FFlag::LuauRankNTypes);
if (get<GenericTypePack>(tp))
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
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)
{
anyification.anyType = anyType;
@ -4444,16 +4279,6 @@ TypeId TypeChecker::freshType(TypeLevel 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::vector<TypeId> types = Luau::filterMap(type, predicate);
@ -4509,21 +4334,8 @@ TypePackId TypeChecker::freshTypePack(TypeLevel 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>())
{
std::optional<TypeFun> tf;
@ -4668,11 +4480,11 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
std::optional<TableIndexer> tableIndexer;
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)
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{
props, tableIndexer, scope->level,
@ -4683,17 +4495,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
{
ScopePtr funcScope = childScope(scope, func->location);
std::vector<TypeId> generics;
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"}});
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
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>())
{
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;
}
else if (const auto& un = annotation.as<AstTypeUnion>())
{
std::vector<TypeId> types;
for (AstType* ann : un->types)
types.push_back(resolveType(scope, *ann, DEPRECATED_canBeGeneric));
types.push_back(resolveType(scope, *ann));
return addType(UnionTypeVar{types});
}
@ -4733,7 +4532,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
{
std::vector<TypeId> types;
for (AstType* ann : un->types)
types.push_back(resolveType(scope, *ann, DEPRECATED_canBeGeneric));
types.push_back(resolveType(scope, *ann));
return addType(IntersectionTypeVar{types});
}
@ -4919,9 +4718,8 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
if (FFlag::LuauCloneCorrectlyBeforeMutatingTableType)
{
// TODO: CLI-46926 it's a bad idea to rename the type whether we follow through the BoundTypeVar or not
TypeId target = FFlag::LuauFollowInTypeFunApply ? follow(instantiated) : instantiated;
// TODO: CLI-46926 it's not a good idea to rename the type here
TypeId target = follow(instantiated);
bool needsClone = follow(tf.type) == target;
TableTypeVar* ttv = getMutableTableType(target);
@ -5151,8 +4949,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)
{
if (FFlag::LuauOrPredicate)
{
if (!sense)
{
@ -5167,17 +4963,6 @@ void TypeChecker::resolve(const AndPredicate& andP, ErrorVec& errVec, Refinement
resolve(andP.lhs, 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)
{
@ -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)
{
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.
// 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)
@ -5246,23 +5029,10 @@ void TypeChecker::resolve(const IsAPredicate& isaP, ErrorVec& errVec, Refinement
if (sense)
return isaP.ty;
}
}
else
{
auto lctv = get<ClassTypeVar>(option);
auto rctv = get<ClassTypeVar>(isaP.ty);
if (isSubclass(lctv, rctv) == sense)
return option;
if (isSubclass(rctv, lctv) == sense)
return isaP.ty;
if (canUnify(option, isaP.ty, isaP.location).empty() == sense)
return isaP.ty;
}
return std::nullopt;
// local variable works around an odd gcc 9.3 warning: <anonymous> may be used uninitialized
std::optional<TypeId> res = std::nullopt;
return res;
};
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);
LUAU_ASSERT(expectedPack);
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);

View file

@ -97,7 +97,7 @@ TypePackIterator begin(TypePackId tp)
TypePackIterator end(TypePackId tp)
{
return FFlag::LuauAddMissingFollow ? TypePackIterator{} : TypePackIterator{nullptr};
return TypePackIterator{};
}
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
@ -203,7 +203,7 @@ TypePackId follow(TypePackId tp)
size_t size(TypePackId tp)
{
if (auto pack = get<TypePack>(FFlag::LuauAddMissingFollow ? follow(tp) : tp))
if (auto pack = get<TypePack>(follow(tp)))
return size(*pack);
else
return 0;
@ -227,7 +227,7 @@ size_t size(const TypePack& tp)
size_t result = tp.head.size();
if (tp.tail)
{
const TypePack* tail = get<TypePack>(FFlag::LuauAddMissingFollow ? follow(*tp.tail) : *tp.tail);
const TypePack* tail = get<TypePack>(follow(*tp.tail));
if (tail)
result += size(*tail);
}

View file

@ -19,8 +19,6 @@
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTFLAG(LuauRankNTypes)
LUAU_FASTFLAG(LuauTypeGuardPeelsAwaySubclasses)
LUAU_FASTFLAG(LuauTypeAliasPacks)
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
@ -42,7 +40,7 @@ TypeId follow(TypeId t)
};
auto force = [](TypeId ty) {
if (auto ltv = FFlag::LuauAddMissingFollow ? get_if<LazyTypeVar>(&ty->ty) : get<LazyTypeVar>(ty))
if (auto ltv = get_if<LazyTypeVar>(&ty->ty))
{
TypeId res = ltv->thunk();
if (get<LazyTypeVar>(res))
@ -296,7 +294,7 @@ bool maybeGeneric(TypeId ty)
{
ty = follow(ty);
if (auto ftv = get<FreeTypeVar>(ty))
return FFlag::LuauRankNTypes || ftv->DEPRECATED_canBeGeneric;
return true;
else if (auto ttv = get<TableTypeVar>(ty))
{
// TODO: recurse on table types CLI-39914
@ -545,15 +543,30 @@ TypeId makeFunction(TypeArena& arena, std::optional<TypeId> selfType, std::initi
std::initializer_list<TypePackId> genericPacks, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes);
static TypeVar nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true};
static TypeVar numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true};
static TypeVar stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true};
static TypeVar booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true};
static TypeVar threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true};
static TypeVar anyType_{AnyTypeVar{}};
static TypeVar errorType_{ErrorTypeVar{}};
static TypeVar optionalNumberType_{UnionTypeVar{{&numberType_, &nilType_}}};
static TypePackVar anyTypePack_{VariadicTypePack{&anyType_}, true};
static TypePackVar errorTypePack_{Unifiable::Error{}};
SingletonTypes::SingletonTypes()
: arena(new TypeArena)
, nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true}
, numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true}
, stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true}
, booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true}
, threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true}
, anyType_{AnyTypeVar{}}
, errorType_{ErrorTypeVar{}}
: nilType(&nilType_)
, numberType(&numberType_)
, stringType(&stringType_)
, booleanType(&booleanType_)
, threadType(&threadType_)
, anyType(&anyType_)
, errorType(&errorType_)
, optionalNumberType(&optionalNumberType_)
, anyTypePack(&anyTypePack_)
, errorTypePack(&errorTypePack_)
, arena(new TypeArena)
{
TypeId stringMetatable = makeStringMetatable();
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, makeStringMetatable()};
@ -1372,24 +1385,6 @@ UnionTypeVarIterator end(const UnionTypeVar* utv)
return UnionTypeVarIterator{};
}
static std::vector<TypeId> DEPRECATED_filterMap(TypeId type, TypeIdPredicate predicate)
{
std::vector<TypeId> result;
if (auto utv = get<UnionTypeVar>(follow(type)))
{
for (TypeId option : utv)
{
if (auto out = predicate(follow(option)))
result.push_back(*out);
}
}
else if (auto out = predicate(follow(type)))
return {*out};
return result;
}
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
{
const char* options = "cdiouxXeEfgGqs";
@ -1470,9 +1465,6 @@ std::optional<ExprResult<TypePackId>> magicFunctionFormat(
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
{
if (!FFlag::LuauTypeGuardPeelsAwaySubclasses)
return DEPRECATED_filterMap(type, predicate);
type = follow(type);
if (auto utv = get<UnionTypeVar>(type))

View file

@ -1,8 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Unifiable.h"
LUAU_FASTFLAG(LuauRankNTypes)
namespace Luau
{
namespace Unifiable
@ -14,14 +12,6 @@ Free::Free(TypeLevel level)
{
}
Free::Free(TypeLevel level, bool DEPRECATED_canBeGeneric)
: index(++nextIndex)
, level(level)
, DEPRECATED_canBeGeneric(DEPRECATED_canBeGeneric)
{
LUAU_ASSERT(!FFlag::LuauRankNTypes);
}
int Free::nextIndex = 0;
Generic::Generic()

View file

@ -14,17 +14,15 @@
LUAU_FASTINT(LuauTypeInferRecursionLimit);
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
LUAU_FASTFLAG(LuauGenericFunctions)
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance, false);
LUAU_FASTFLAGVARIABLE(LuauDontMutatePersistentFunctions, false)
LUAU_FASTFLAG(LuauRankNTypes)
LUAU_FASTFLAGVARIABLE(LuauUnionHeuristic, false)
LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
LUAU_FASTFLAGVARIABLE(LuauSealedTableUnifyOptionalFix, false)
LUAU_FASTFLAGVARIABLE(LuauOccursCheckOkWithRecursiveFunctions, false)
LUAU_FASTFLAGVARIABLE(LuauTypecheckOpts, false)
LUAU_FASTFLAG(LuauShareTxnSeen);
LUAU_FASTFLAGVARIABLE(LuauCacheUnifyTableResults, false)
LUAU_FASTFLAGVARIABLE(LuauExtendedTypeMismatchError, false)
LUAU_FASTFLAGVARIABLE(LuauExtendedClassMismatchError, false)
namespace Luau
{
@ -219,17 +217,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
*asMutable(subTy) = BoundTypeVar(superTy);
}
if (!FFlag::LuauRankNTypes)
l->DEPRECATED_canBeGeneric &= r->DEPRECATED_canBeGeneric;
return;
}
else if (l && r && FFlag::LuauGenericFunctions)
else if (l && r)
{
log(superTy);
occursCheck(superTy, subTy);
if (!FFlag::LuauRankNTypes)
r->DEPRECATED_canBeGeneric &= l->DEPRECATED_canBeGeneric;
r->level = min(r->level, l->level);
*asMutable(superTy) = BoundTypeVar(subTy);
return;
@ -240,7 +233,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
// Unification can't change the level of a generic.
auto rightGeneric = get<GenericTypeVar>(subTy);
if (FFlag::LuauRankNTypes && rightGeneric && !rightGeneric->level.subsumes(l->level))
if (rightGeneric && !rightGeneric->level.subsumes(l->level))
{
// TODO: a more informative error message? CLI-39912
errors.push_back(TypeError{location, GenericError{"Generic subtype escaping scope"}});
@ -266,31 +259,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
// Unification can't change the level of a generic.
auto leftGeneric = get<GenericTypeVar>(superTy);
if (FFlag::LuauRankNTypes && leftGeneric && !leftGeneric->level.subsumes(r->level))
if (leftGeneric && !leftGeneric->level.subsumes(r->level))
{
// TODO: a more informative error message? CLI-39912
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
return;
}
// This is the old code which is just wrong
auto wrongGeneric = get<GenericTypeVar>(subTy); // Guaranteed to be null
if (!FFlag::LuauRankNTypes && FFlag::LuauGenericFunctions && wrongGeneric && r->level.subsumes(wrongGeneric->level))
{
// This code is unreachable! Should we just remove it?
// TODO: a more informative error message? CLI-39912
errors.push_back(TypeError{location, GenericError{"Generic supertype escaping scope"}});
return;
}
// Check if we're unifying a monotype with a polytype
if (FFlag::LuauGenericFunctions && !FFlag::LuauRankNTypes && !r->DEPRECATED_canBeGeneric && isGeneric(superTy))
{
// TODO: a more informative error message? CLI-39912
errors.push_back(TypeError{location, GenericError{"Failed to unify a polytype with a monotype"}});
return;
}
if (!get<ErrorTypeVar>(subTy))
{
if (auto leftLevel = getMutableLevel(superTy))
@ -333,6 +308,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
// A | B <: T if A <: T and B <: T
bool failed = false;
std::optional<TypeError> unificationTooComplex;
std::optional<TypeError> firstFailedOption;
size_t count = uv->options.size();
size_t i = 0;
@ -345,7 +321,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
if (auto e = hasUnificationTooComplex(innerState.errors))
unificationTooComplex = e;
else if (!innerState.errors.empty())
{
// 'nil' option is skipped from extended report because we present the type in a special way - 'T?'
if (FFlag::LuauExtendedTypeMismatchError && !firstFailedOption && !isNil(type))
firstFailedOption = {innerState.errors.front()};
failed = true;
}
if (i != count - 1)
innerState.log.rollback();
@ -358,8 +340,13 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
if (unificationTooComplex)
errors.push_back(*unificationTooComplex);
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}});
}
}
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
{
// 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)
errors.push_back(*unificationTooComplex);
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}});
}
}
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
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);
}
}
}
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
{
// 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)
errors.push_back(*unificationTooComplex);
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}});
}
}
else if (get<PrimitiveTypeVar>(superTy) && get<PrimitiveTypeVar>(subTy))
tryUnifyPrimitives(superTy, subTy);
@ -773,8 +800,8 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
// If both are at the end, we're done
if (!superIter.good() && !subIter.good())
{
const bool lFreeTail = l->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*l->tail) : *l->tail) != nullptr;
const bool rFreeTail = r->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*r->tail) : *r->tail) != nullptr;
const bool lFreeTail = l->tail && get<FreeTypePack>(follow(*l->tail)) != nullptr;
const bool rFreeTail = r->tail && get<FreeTypePack>(follow(*r->tail)) != nullptr;
if (lFreeTail && rFreeTail)
tryUnify_(*l->tail, *r->tail);
else if (lFreeTail)
@ -812,7 +839,7 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
}
// In nonstrict mode, any also marks an optional argument.
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(FFlag::LuauAddMissingFollow ? follow(*superIter) : *superIter))
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(follow(*superIter)))
{
superIter.advance();
continue;
@ -887,24 +914,21 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
ice("passed non-function types to unifyFunction");
size_t numGenerics = lf->generics.size();
if (FFlag::LuauGenericFunctions && numGenerics != rf->generics.size())
if (numGenerics != rf->generics.size())
{
numGenerics = std::min(lf->generics.size(), rf->generics.size());
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
size_t numGenericPacks = lf->genericPacks.size();
if (FFlag::LuauGenericFunctions && numGenericPacks != rf->genericPacks.size())
if (numGenericPacks != rf->genericPacks.size())
{
numGenericPacks = std::min(lf->genericPacks.size(), rf->genericPacks.size());
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
if (FFlag::LuauGenericFunctions)
{
for (size_t i = 0; i < numGenerics; i++)
log.pushSeen(lf->generics[i], rf->generics[i]);
}
CountMismatch::Context context = ctx;
@ -931,23 +955,20 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
tryUnify_(lf->retType, rf->retType);
}
if (lf->definition && !rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !subTy->persistent))
if (lf->definition && !rf->definition && !subTy->persistent)
{
rf->definition = lf->definition;
}
else if (!lf->definition && rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !superTy->persistent))
else if (!lf->definition && rf->definition && !superTy->persistent)
{
lf->definition = rf->definition;
}
ctx = context;
if (FFlag::LuauGenericFunctions)
{
for (int i = int(numGenerics) - 1; 0 <= i; i--)
log.popSeen(lf->generics[i], rf->generics[i]);
}
}
namespace
{
@ -1032,7 +1053,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, r->second.type);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1047,7 +1073,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, rt->indexer->indexResultType);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1083,7 +1114,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, lt->indexer->indexResultType);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1383,22 +1419,9 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
{
const auto& r = rt->props.find(it.first);
if (r == rt->props.end())
{
if (FFlag::LuauSealedTableUnifyOptionalFix)
{
if (isOptional(it.second.type))
continue;
}
else
{
if (get<UnionTypeVar>(it.second.type))
{
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
const std::vector<TypeId>& options = possiblyOptional->options;
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
continue;
}
}
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);
if (r == lt->props.end())
{
if (FFlag::LuauSealedTableUnifyOptionalFix)
{
if (isOptional(it.second.type))
continue;
}
else
{
if (get<UnionTypeVar>(it.second.type))
{
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
const std::vector<TypeId>& options = possiblyOptional->options;
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
continue;
}
}
extraPropertiesInSub.push_back(it.first);
}
@ -1526,7 +1536,18 @@ void Unifier::tryUnifyWithMetatable(TypeId metatable, TypeId other, bool reverse
innerState.tryUnify_(lhs->table, rhs->table);
innerState.tryUnify_(lhs->metatable, rhs->metatable);
if (FFlag::LuauExtendedTypeMismatchError)
{
if (auto e = hasUnificationTooComplex(innerState.errors))
errors.push_back(*e);
else if (!innerState.errors.empty())
errors.push_back(
TypeError{location, TypeMismatch{reversed ? other : metatable, reversed ? metatable : other, "", innerState.errors.front()}});
}
else
{
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
}
log.concat(std::move(innerState.log));
}
@ -1613,11 +1634,35 @@ void Unifier::tryUnifyWithClass(TypeId superTy, TypeId subTy, bool reversed)
{
ok = false;
errors.push_back(TypeError{location, UnknownProperty{superTy, propName}});
if (!FFlag::LuauExtendedClassMismatchError)
tryUnify_(prop.type, singletonTypes.errorType);
}
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);
}
}
}
if (table->indexer)
{
@ -1649,15 +1694,12 @@ static void queueTypePack_DEPRECATED(
while (true)
{
if (FFlag::LuauAddMissingFollow)
a = follow(a);
if (seenTypePacks.count(a))
break;
seenTypePacks.insert(a);
if (FFlag::LuauAddMissingFollow)
{
if (get<Unifiable::Free>(a))
{
state.log(a);
@ -1672,24 +1714,6 @@ static void queueTypePack_DEPRECATED(
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)
@ -1698,15 +1722,12 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
while (true)
{
if (FFlag::LuauAddMissingFollow)
a = follow(a);
if (seenTypePacks.find(a))
break;
seenTypePacks.insert(a);
if (FFlag::LuauAddMissingFollow)
{
if (get<Unifiable::Free>(a))
{
state.log(a);
@ -1721,24 +1742,6 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
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)
@ -1990,33 +1993,6 @@ std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N
return Luau::findTablePropertyRespectingMeta(errors, globalScope, lhsType, name, location);
}
std::optional<TypeId> Unifier::findMetatableEntry(TypeId type, std::string entry)
{
type = follow(type);
std::optional<TypeId> metatable = getMetatable(type);
if (!metatable)
return std::nullopt;
TypeId unwrapped = follow(*metatable);
if (get<AnyTypeVar>(unwrapped))
return singletonTypes.anyType;
const TableTypeVar* mtt = getTableType(unwrapped);
if (!mtt)
{
errors.push_back(TypeError{location, GenericError{"Metatable was not a table."}});
return std::nullopt;
}
auto it = mtt->props.find(entry);
if (it != mtt->props.end())
return it->second.type;
else
return std::nullopt;
}
void Unifier::occursCheck(TypeId needle, TypeId haystack)
{
std::unordered_set<TypeId> seen_DEPRECATED;
@ -2168,7 +2144,7 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
{
for (const auto& ty : a->head)
{
if (auto f = get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty))
if (auto f = get<FunctionTypeVar>(follow(ty)))
{
occursCheck(seen_DEPRECATED, seen, needle, f->argTypes);
occursCheck(seen_DEPRECATED, seen, needle, f->retType);
@ -2207,6 +2183,17 @@ void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId
errors.push_back(TypeError{location, TypeMismatch{wantedType, givenType}});
}
void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType)
{
LUAU_ASSERT(FFlag::LuauExtendedTypeMismatchError || FFlag::LuauExtendedClassMismatchError);
if (auto e = hasUnificationTooComplex(innerErrors))
errors.push_back(*e);
else if (!innerErrors.empty())
errors.push_back(
TypeError{location, TypeMismatch{wantedType, givenType, format("Property '%s' is not compatible", prop.c_str()), innerErrors.front()}});
}
void Unifier::ice(const std::string& message, const Location& location)
{
sharedState.iceHandler->ice(message, location);

View file

@ -282,7 +282,6 @@ private:
// `<' namelist `>'
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeList();
std::pair<AstArray<AstName>, AstArray<AstName>> parseGenericTypeListIfFFlagParseGenericFunctions();
// `<' typeAnnotation[, ...] `>'
AstArray<AstTypeOrPack> parseTypeParams();

View file

@ -10,13 +10,13 @@
// See docs/SyntaxChanges.md for an explanation.
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsParserFix, false)
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctions, false)
LUAU_FASTFLAGVARIABLE(LuauCaptureBrokenCommentSpans, false)
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionBaseSupport, false)
LUAU_FASTFLAGVARIABLE(LuauIfStatementRecursionGuard, false)
LUAU_FASTFLAGVARIABLE(LuauTypeAliasPacks, false)
LUAU_FASTFLAGVARIABLE(LuauParseTypePackTypeParameters, false)
LUAU_FASTFLAGVARIABLE(LuauFixAmbiguousErrorRecoveryInAssign, false)
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctionTypeBegin, false)
namespace Luau
{
@ -957,7 +957,7 @@ AstStat* Parser::parseAssignment(AstExpr* initial)
{
nextLexeme();
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ false);
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ FFlag::LuauFixAmbiguousErrorRecoveryInAssign);
if (!isExprLValue(expr))
expr = reportExprError(expr->location, copy({expr}), "Assigned expression must be a variable or a field");
@ -995,7 +995,7 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
{
Location start = matchFunction.location;
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
auto [generics, genericPacks] = parseGenericTypeList();
Lexeme matchParen = lexer.current();
expectAndConsume('(', "function");
@ -1343,19 +1343,18 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
{
incrementRecursionCounter("type annotation");
bool monomorphic = !(FFlag::LuauParseGenericFunctions && lexer.current().type == '<');
auto [generics, genericPacks] = parseGenericTypeListIfFFlagParseGenericFunctions();
bool monomorphic = lexer.current().type != '<';
Lexeme begin = lexer.current();
if (FFlag::LuauGenericFunctionsParserFix)
auto [generics, genericPacks] = parseGenericTypeList();
Lexeme parameterStart = lexer.current();
if (!FFlag::LuauParseGenericFunctionTypeBegin)
begin = parameterStart;
expectAndConsume('(', "function parameters");
else
{
LUAU_ASSERT(begin.type == '(');
nextLexeme(); // (
}
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
@ -1366,7 +1365,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
if (lexer.current().type != ')')
varargAnnotation = parseTypeList(params, names);
expectMatchAndConsume(')', begin, true);
expectMatchAndConsume(')', parameterStart, true);
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
@ -1585,7 +1584,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
{
return {parseTableTypeAnnotation(), {}};
}
else if (lexer.current().type == '(' || (FFlag::LuauParseGenericFunctions && lexer.current().type == '<'))
else if (lexer.current().type == '(' || lexer.current().type == '<')
{
return parseFunctionTypeAnnotation(allowPack);
}
@ -2315,19 +2314,6 @@ Parser::Name Parser::parseIndexName(const char* context, const Position& previou
return Name(nameError, location);
}
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeListIfFFlagParseGenericFunctions()
{
if (FFlag::LuauParseGenericFunctions)
return Parser::parseGenericTypeList();
AstArray<AstName> generics;
AstArray<AstName> genericPacks;
generics.size = 0;
generics.data = nullptr;
genericPacks.size = 0;
genericPacks.data = nullptr;
return std::pair(generics, genericPacks);
}
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
{
TempVector<AstName> names{scratchName};
@ -2342,7 +2328,7 @@ std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
while (true)
{
AstName name = parseName().name;
if (FFlag::LuauParseGenericFunctions && lexer.current().type == Lexeme::Dot3)
if (lexer.current().type == Lexeme::Dot3)
{
seenPack = true;
nextLexeme();

View file

@ -215,9 +215,6 @@ extern "C"
{
const char* executeScript(const char* source)
{
// static string for caching result (prevents dangling ptr on function exit)
static std::string result;
// setup flags
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
@ -233,15 +230,13 @@ extern "C"
// sandbox thread
luaL_sandboxthread(L);
// run code + collect error
std::string error = runCode(L, source);
result = error;
// static string for caching result (prevents dangling ptr on function exit)
static std::string result;
if (error.length())
{
return result.c_str();
}
return NULL;
// run code + collect error
result = runCode(L, source);
return result.empty() ? NULL : result.c_str();
}
}
#endif
@ -591,3 +586,4 @@ int main(int argc, char** argv)
}
}
#endif

View file

@ -467,6 +467,10 @@ enum LuauBuiltinFunction
// vector ctor
LBF_VECTOR,
// bit32.count
LBF_BIT32_COUNTLZ,
LBF_BIT32_COUNTRZ,
};
// Capture type, used in LOP_CAPTURE

View file

@ -37,8 +37,7 @@ struct CompileOptions
const char* vectorLib = nullptr;
const char* vectorCtor = nullptr;
// array of globals that are mutable; disables the import optimization for fields accessed through them
// use NULL to end the array
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these
const char** mutableGlobals = nullptr;
};

View file

@ -15,6 +15,7 @@ LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresFenv, false)
LUAU_FASTFLAGVARIABLE(LuauPreloadClosuresUpval, false)
LUAU_FASTFLAGVARIABLE(LuauGenericSpecialGlobals, false)
LUAU_FASTFLAG(LuauIfElseExpressionBaseSupport)
LUAU_FASTFLAGVARIABLE(LuauBit32CountBuiltin, false)
namespace Luau
{
@ -23,6 +24,7 @@ static const uint32_t kMaxRegisterCount = 255;
static const uint32_t kMaxUpvalueCount = 200;
static const uint32_t kMaxLocalCount = 200;
// TODO: Remove with LuauGenericSpecialGlobals
static const char* kSpecialGlobals[] = {"Game", "Workspace", "_G", "game", "plugin", "script", "shared", "workspace"};
CompileError::CompileError(const Location& location, const std::string& message)
@ -3637,6 +3639,10 @@ struct Compiler
return LBF_BIT32_RROTATE;
if (builtin.method == "rshift")
return LBF_BIT32_RSHIFT;
if (builtin.method == "countlz" && FFlag::LuauBit32CountBuiltin)
return LBF_BIT32_COUNTLZ;
if (builtin.method == "countrz" && FFlag::LuauBit32CountBuiltin)
return LBF_BIT32_COUNTRZ;
}
if (builtin.object == "string")
@ -3711,12 +3717,10 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
compiler.globals[name].writable = true;
if (options.mutableGlobals)
for (const char** ptr = options.mutableGlobals; *ptr != NULL; ++ptr)
{
for (const char** ptr = options.mutableGlobals; *ptr; ++ptr)
if (AstName name = names.get(*ptr); name.value)
compiler.globals[name].writable = true;
}
}
else
{
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
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1)
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1 && (names.get("getfenv").value || names.get("setfenv").value))
{
Compiler::FenvVisitor fenvVisitor(compiler.getfenvUsed, compiler.setfenvUsed);
root->visit(&fenvVisitor);

View file

@ -137,12 +137,11 @@ $(TESTS_TARGET) $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET):
# executable targets for fuzzing
fuzz-%: $(BUILD)/fuzz/%.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET)
$(CXX) $^ $(LDFLAGS) -o $@
fuzz-proto: $(BUILD)/fuzz/proto.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
fuzz-prototest: $(BUILD)/fuzz/prototest.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
fuzz-%:
$(CXX) $^ $(LDFLAGS) -o $@
# static library targets
$(AST_TARGET): $(AST_OBJECTS)
$(COMPILER_TARGET): $(COMPILER_OBJECTS)

View file

@ -213,6 +213,8 @@ LUA_API int lua_resume(lua_State* L, lua_State* from, int narg);
LUA_API int lua_resumeerror(lua_State* L, lua_State* from);
LUA_API int lua_status(lua_State* L);
LUA_API int lua_isyieldable(lua_State* L);
LUA_API void* lua_getthreaddata(lua_State* L);
LUA_API void lua_setthreaddata(lua_State* L, void* data);
/*
** garbage-collection function and options

View file

@ -988,6 +988,16 @@ int lua_status(lua_State* L)
return L->status;
}
void* lua_getthreaddata(lua_State* L)
{
return L->userdata;
}
void lua_setthreaddata(lua_State* L, void* data)
{
L->userdata = data;
}
/*
** Garbage-collection function
*/

View file

@ -2,8 +2,11 @@
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#include "lualib.h"
#include "lcommon.h"
#include "lnumutils.h"
LUAU_FASTFLAGVARIABLE(LuauBit32Count, false)
#define ALLONES ~0u
#define NBITS int(8 * sizeof(unsigned))
@ -177,6 +180,44 @@ static int b_replace(lua_State* L)
return 1;
}
static int b_countlz(lua_State* L)
{
if (!FFlag::LuauBit32Count)
luaL_error(L, "bit32.countlz isn't enabled");
b_uint v = luaL_checkunsigned(L, 1);
b_uint r = NBITS;
for (int i = 0; i < NBITS; ++i)
if (v & (1u << (NBITS - 1 - i)))
{
r = i;
break;
}
lua_pushunsigned(L, r);
return 1;
}
static int b_countrz(lua_State* L)
{
if (!FFlag::LuauBit32Count)
luaL_error(L, "bit32.countrz isn't enabled");
b_uint v = luaL_checkunsigned(L, 1);
b_uint r = NBITS;
for (int i = 0; i < NBITS; ++i)
if (v & (1u << i))
{
r = i;
break;
}
lua_pushunsigned(L, r);
return 1;
}
static const luaL_Reg bitlib[] = {
{"arshift", b_arshift},
{"band", b_and},
@ -190,6 +231,8 @@ static const luaL_Reg bitlib[] = {
{"replace", b_replace},
{"rrotate", b_rrot},
{"rshift", b_rshift},
{"countlz", b_countlz},
{"countrz", b_countrz},
{NULL, NULL},
};

View file

@ -1031,6 +1031,52 @@ static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
return -1;
}
static int luauF_countlz(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
{
double a1 = nvalue(arg0);
unsigned n;
luai_num2unsigned(n, a1);
#ifdef _MSC_VER
unsigned long rl;
int r = _BitScanReverse(&rl, n) ? 31 - int(rl) : 32;
#else
int r = n == 0 ? 32 : __builtin_clz(n);
#endif
setnvalue(res, double(r));
return 1;
}
return -1;
}
static int luauF_countrz(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
{
double a1 = nvalue(arg0);
unsigned n;
luai_num2unsigned(n, a1);
#ifdef _MSC_VER
unsigned long rl;
int r = _BitScanForward(&rl, n) ? int(rl) : 32;
#else
int r = n == 0 ? 32 : __builtin_ctz(n);
#endif
setnvalue(res, double(r));
return 1;
}
return -1;
}
luau_FastFunction luauF_table[256] = {
NULL,
luauF_assert,
@ -1097,4 +1143,7 @@ luau_FastFunction luauF_table[256] = {
luauF_tunpack,
luauF_vector,
luauF_countlz,
luauF_countrz,
};

View file

@ -13,7 +13,6 @@
#include <stdio.h>
LUAU_FASTFLAGVARIABLE(LuauRescanGrayAgainForwardBarrier, false)
LUAU_FASTFLAGVARIABLE(LuauConsolidatedStep, false)
LUAU_FASTFLAGVARIABLE(LuauSeparateAtomic, false)
LUAU_FASTFLAG(LuauArrayBoundary)
@ -677,117 +676,6 @@ static size_t atomic(lua_State* L)
return work;
}
static size_t singlestep(lua_State* L)
{
size_t cost = 0;
global_State* g = L->global;
switch (g->gcstate)
{
case GCSpause:
{
markroot(L); /* start a new collection */
LUAU_ASSERT(g->gcstate == GCSpropagate);
break;
}
case GCSpropagate:
{
if (g->gray)
{
g->gcstats.currcycle.markitems++;
cost = propagatemark(g);
}
else
{
// perform one iteration over 'gray again' list
g->gray = g->grayagain;
g->grayagain = NULL;
g->gcstate = GCSpropagateagain;
}
break;
}
case GCSpropagateagain:
{
if (g->gray)
{
g->gcstats.currcycle.markitems++;
cost = propagatemark(g);
}
else /* no more `gray' objects */
{
if (FFlag::LuauSeparateAtomic)
{
g->gcstate = GCSatomic;
}
else
{
double starttimestamp = lua_clock();
g->gcstats.currcycle.atomicstarttimestamp = starttimestamp;
g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes;
atomic(L); /* finish mark phase */
LUAU_ASSERT(g->gcstate == GCSsweepstring);
g->gcstats.currcycle.atomictime += lua_clock() - starttimestamp;
}
}
break;
}
case GCSatomic:
{
g->gcstats.currcycle.atomicstarttimestamp = lua_clock();
g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes;
cost = atomic(L); /* finish mark phase */
LUAU_ASSERT(g->gcstate == GCSsweepstring);
break;
}
case GCSsweepstring:
{
size_t traversedcount = 0;
sweepwholelist(L, &g->strt.hash[g->sweepstrgc++], &traversedcount);
// nothing more to sweep?
if (g->sweepstrgc >= g->strt.size)
{
// sweep string buffer list and preserve used string count
uint32_t nuse = L->global->strt.nuse;
sweepwholelist(L, &g->strbufgc, &traversedcount);
L->global->strt.nuse = nuse;
g->gcstate = GCSsweep; // end sweep-string phase
}
g->gcstats.currcycle.sweepitems += traversedcount;
cost = GC_SWEEPCOST;
break;
}
case GCSsweep:
{
size_t traversedcount = 0;
g->sweepgc = sweeplist(L, g->sweepgc, GC_SWEEPMAX, &traversedcount);
g->gcstats.currcycle.sweepitems += traversedcount;
if (*g->sweepgc == NULL)
{ /* nothing more to sweep? */
shrinkbuffers(L);
g->gcstate = GCSpause; /* end collection */
}
cost = GC_SWEEPMAX * GC_SWEEPCOST;
break;
}
default:
LUAU_ASSERT(!"Unexpected GC state");
}
return cost;
}
static size_t gcstep(lua_State* L, size_t limit)
{
size_t cost = 0;
@ -980,37 +868,12 @@ void luaC_step(lua_State* L, bool assist)
int lastgcstate = g->gcstate;
double lasttimestamp = lua_clock();
if (FFlag::LuauConsolidatedStep)
{
size_t work = gcstep(L, lim);
if (assist)
g->gcstats.currcycle.assistwork += work;
else
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);
@ -1037,15 +900,8 @@ void luaC_step(lua_State* L, bool assist)
g->GCthreshold -= debt;
}
if (FFlag::LuauConsolidatedStep)
{
GC_INTERRUPT(lastgcstate);
}
else
{
GC_INTERRUPT(g->gcstate);
}
}
void luaC_fullgc(lua_State* L)
{
@ -1070,10 +926,7 @@ void luaC_fullgc(lua_State* L)
while (g->gcstate != GCSpause)
{
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
if (FFlag::LuauConsolidatedStep)
gcstep(L, SIZE_MAX);
else
singlestep(L);
}
finishGcCycleStats(g);
@ -1084,10 +937,7 @@ void luaC_fullgc(lua_State* L)
markroot(L);
while (g->gcstate != GCSpause)
{
if (FFlag::LuauConsolidatedStep)
gcstep(L, SIZE_MAX);
else
singlestep(L);
}
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */
shrinkbuffersfull(L);

View file

@ -8,6 +8,8 @@
#include <string.h>
#include <stdio.h>
LUAU_FASTFLAGVARIABLE(LuauStrPackUBCastFix, false)
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
@ -1404,10 +1406,20 @@ static int str_pack(lua_State* L)
}
case Kuint:
{ /* unsigned integers */
if (FFlag::LuauStrPackUBCastFix)
{
long long n = (long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */
luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, (unsigned long long)n, h.islittle, size, 0);
}
else
{
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, n, h.islittle, size, 0);
}
break;
}
case Kfloat:

View file

@ -9,8 +9,6 @@
#include "ldebug.h"
#include "lvm.h"
LUAU_FASTFLAGVARIABLE(LuauTableFreeze, false)
static int foreachi(lua_State* L)
{
luaL_checktype(L, 1, LUA_TTABLE);
@ -491,9 +489,6 @@ static int tclear(lua_State* L)
static int tfreeze(lua_State* L)
{
if (!FFlag::LuauTableFreeze)
luaG_runerror(L, "table.freeze is disabled");
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, !lua_getreadonly(L, 1), 1, "table is already frozen");
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");
@ -506,9 +501,6 @@ static int tfreeze(lua_State* L)
static int tisfrozen(lua_State* L)
{
if (!FFlag::LuauTableFreeze)
luaG_runerror(L, "table.isfrozen is disabled");
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushboolean(L, lua_getreadonly(L, 1));

View file

@ -205,27 +205,23 @@ function Bitboard:empty()
return self.h == 0 and self.l == 0
end
function Bitboard:ctz()
local target = self.l
if not bit32.countrz then
local function ctz(v)
if v == 0 then return 32 end
local offset = 0
local result = 0
if target == 0 then
target = self.h
result = 32
end
if target == 0 then
return 64
end
while bit32.extract(target, offset) == 0 do
while bit32.extract(v, offset) == 0 do
offset = offset + 1
end
return result + offset
return offset
end
function Bitboard:ctz()
local result = ctz(self.l)
if result == 32 then
return ctz(self.h) + 32
else
return result
end
end
function Bitboard:ctzafter(start)
start = start + 1
if start < 32 then
@ -238,6 +234,20 @@ function Bitboard:ctzafter(start)
end
return 64
end
else
function Bitboard:ctz()
local result = bit32.countrz(self.l)
if result == 32 then
return bit32.countrz(self.h) + 32
else
return result
end
end
function Bitboard:ctzafter(start)
local masked = self:band(Bitboard.full:lshift(start+1))
return masked:ctz()
end
end
function Bitboard:lshift(amt)
@ -245,7 +255,7 @@ function Bitboard:lshift(amt)
if amt == 0 then return self end
if amt > 31 then
return Bitboard.from(0, bit32.lshift(self.l, amt-31))
return Bitboard.from(0, bit32.lshift(self.l, amt-32))
end
local l = bit32.lshift(self.l, amt)
@ -832,12 +842,12 @@ end
local testCases = {}
local function addTest(...) table.insert(testCases, {...}) end
addTest(StartingFen, 3, 8902)
addTest("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0", 2, 2039)
addTest("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0", 3, 2812)
addTest("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 3, 9467)
addTest("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 2, 1486)
addTest("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 2, 2079)
addTest(StartingFen, 2, 400)
addTest("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0", 1, 48)
addTest("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0", 2, 191)
addTest("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 2, 264)
addTest("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 1, 44)
addTest("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 1, 46)
local function chess()

View file

@ -19,6 +19,7 @@ message Expr {
ExprTable table = 13;
ExprUnary unary = 14;
ExprBinary binary = 15;
ExprIfElse ifelse = 16;
}
}
@ -149,6 +150,12 @@ message ExprBinary {
required Expr right = 3;
}
message ExprIfElse {
required Expr cond = 1;
required Expr then = 2;
required Expr else = 3;
}
message LValue {
oneof lvalue_oneof {
ExprLocal local = 1;

View file

@ -11,6 +11,7 @@
#include "Luau/BytecodeBuilder.h"
#include "Luau/Common.h"
#include "Luau/ToString.h"
#include "Luau/Transpiler.h"
#include "lua.h"
#include "lualib.h"
@ -23,6 +24,7 @@ const bool kFuzzLinter = true;
const bool kFuzzTypeck = true;
const bool kFuzzVM = true;
const bool kFuzzTypes = true;
const bool kFuzzTranspile = true;
static_assert(!(kFuzzVM && !kFuzzCompiler), "VM requires the compiler!");
@ -242,6 +244,11 @@ DEFINE_PROTO_FUZZER(const luau::StatBlock& message)
}
}
if (kFuzzTranspile && parseResult.root)
{
transpileWithTypes(*parseResult.root);
}
// run resulting bytecode
if (kFuzzVM && bytecode.size())
{

View file

@ -476,6 +476,16 @@ struct ProtoToLuau
print(expr.right());
}
void print(const luau::ExprIfElse& expr)
{
source += " if ";
print(expr.cond());
source += " then ";
print(expr.then());
source += " else ";
print(expr.else_());
}
void print(const luau::LValue& expr)
{
if (expr.has_local())

View file

@ -45,7 +45,6 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "prop")
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "event_callback_arg")
{
ScopedFastFlag sffs[] = {
{"LuauDontMutatePersistentFunctions", true},
{"LuauPersistDefinitionFileTypes", true},
};

View file

@ -1287,9 +1287,6 @@ local e: (n: n@5
TEST_CASE_FIXTURE(ACFixture, "generic_types")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
check(R"(
function f<Tee, Use>(a: T@1
local b: string = "don't trip"

View file

@ -240,8 +240,6 @@ TEST_CASE("Math")
TEST_CASE("Table")
{
ScopedFastFlag sff("LuauTableFreeze", true);
runConformance("nextvar.lua");
}
@ -322,6 +320,8 @@ TEST_CASE("GC")
TEST_CASE("Bitwise")
{
ScopedFastFlag sff("LuauBit32Count", true);
runConformance("bitwise.lua");
}
@ -359,6 +359,8 @@ TEST_CASE("PCall")
TEST_CASE("Pack")
{
ScopedFastFlag sff{ "LuauStrPackUBCastFix", true };
runConformance("tpack.lua");
}

View file

@ -479,10 +479,6 @@ return foo1
TEST_CASE_FIXTURE(Fixture, "UnknownType")
{
ScopedFastFlag sff{"LuauLinterUnknownTypeVectorAware", true};
SourceModule sm;
unfreeze(typeChecker.globalTypes);
TableTypeVar::Props instanceProps{
{"ClassName", {typeChecker.anyType}},
@ -1400,8 +1396,6 @@ end
TEST_CASE_FIXTURE(Fixture, "TableOperations")
{
ScopedFastFlag sff("LuauLinterTableMoveZero", true);
LintResult result = lintTyped(R"(
local t = {}
local tt = {}

View file

@ -7,6 +7,8 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauFixAmbiguousErrorRecoveryInAssign)
using namespace Luau;
namespace
@ -625,10 +627,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
)"),
"Cannot have more than one table indexer");
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauGenericFunctionsParserFix", true};
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
CHECK_EQ(getParseError(R"(
type T = <a>foo
)"),
@ -1624,6 +1622,20 @@ TEST_CASE_FIXTURE(Fixture, "parse_error_confusing_function_call")
"statements");
CHECK(result3.errors.size() == 1);
auto result4 = matchParseError(R"(
local t = {}
function f() return t end
t.x, (f)
().y = 5, 6
)",
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
"statements");
if (FFlag::LuauFixAmbiguousErrorRecoveryInAssign)
CHECK(result4.errors.size() == 1);
else
CHECK(result4.errors.size() == 5);
}
TEST_CASE_FIXTURE(Fixture, "parse_error_varargs")
@ -1824,9 +1836,6 @@ TEST_CASE_FIXTURE(Fixture, "variadic_definition_parsing")
TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
{
// Doesn't need LuauGenericFunctions
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
ParseResult result = parseEx(R"(
function f<a...>(...: a...)
end
@ -1861,9 +1870,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
TEST_CASE_FIXTURE(Fixture, "generic_function_declaration_parsing")
{
// Doesn't need LuauGenericFunctions
ScopedFastFlag sffs{"LuauParseGenericFunctions", true};
ParseResult result = parseEx(R"(
declare function f<a, b, c...>()
)");
@ -1953,13 +1959,8 @@ TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments")
matchParseError("type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)",
"Expected '->' when parsing function type, got <eof>");
{
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
ScopedFastFlag luauGenericFunctionsParserFix{"LuauGenericFunctionsParserFix", true};
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
}
}
TEST_SUITE_END();
@ -2362,8 +2363,6 @@ type Fn = (
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
}
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
try
{
parse(R"(type Fn = (any, string | number | <a>()) -> any)");
@ -2397,8 +2396,6 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
{
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
try
{
parse(R"(
@ -2521,7 +2518,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
AstStat* stat = parse(R"(
@ -2534,4 +2530,9 @@ type C<X...> = Packed<(number, X...)>
REQUIRE(stat != nullptr);
}
TEST_CASE_FIXTURE(Fixture, "function_type_matching_parenthesis")
{
matchParseError("local a: <T>(number -> string", "Expected ')' (to close '(' at column 13), got '->'");
}
TEST_SUITE_END();

View file

@ -33,8 +33,6 @@ TEST_SUITE_BEGIN("Predicate");
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
RefinementMap m{
{"b", typeChecker.stringType},
{"c", typeChecker.numberType},
@ -61,8 +59,6 @@ TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order")
TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
RefinementMap m{
{"a", typeChecker.stringType},
{"b", typeChecker.stringType},
@ -89,8 +85,6 @@ TEST_CASE_FIXTURE(Fixture, "Luau_merge_hashmap_order2")
TEST_CASE_FIXTURE(Fixture, "one_map_has_overlap_at_end_whereas_other_has_it_in_start")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
RefinementMap m{
{"a", typeChecker.stringType},
{"b", typeChecker.numberType},

View file

@ -259,9 +259,6 @@ TEST_CASE_FIXTURE(Fixture, "function_type_with_argument_names")
TEST_CASE_FIXTURE(Fixture, "function_type_with_argument_names_generic")
{
ScopedFastFlag luauGenericFunctions{"LuauGenericFunctions", true};
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
CheckResult result = check("local function f<a...>(n: number, ...: a...): (a...) return ... end");
LUAU_REQUIRE_NO_ERRORS(result);
@ -340,10 +337,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
TEST_CASE_FIXTURE(Fixture, "toStringDetailed2")
{
ScopedFastFlag sff[] = {
{"LuauGenericFunctions", true},
};
CheckResult result = check(R"(
local base = {}
function base:one() return 1 end
@ -468,8 +461,6 @@ TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_inters
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
{
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
TypeVar tableTy{TableTypeVar{}};
TableTypeVar* ttv = getMutable<TableTypeVar>(&tableTy);
ttv->name = "Table";

View file

@ -223,12 +223,24 @@ TEST_CASE("escaped_strings")
CHECK_EQ(code, transpile(code).code);
}
TEST_CASE("escaped_strings_2")
{
const std::string code = R"( local s="\a\b\f\n\r\t\v\'\"\\" )";
CHECK_EQ(code, transpile(code).code);
}
TEST_CASE("need_a_space_between_number_literals_and_dots")
{
const std::string code = R"( return point and math.ceil(point* 100000* 100)/ 100000 .. '%'or '' )";
CHECK_EQ(code, transpile(code).code);
}
TEST_CASE("binary_keywords")
{
const std::string code = "local c = a0 ._ or b0 ._";
CHECK_EQ(code, transpile(code).code);
}
TEST_CASE("do_blocks")
{
const std::string code = R"(
@ -364,10 +376,10 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly")
)";
std::string expected = R"(
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:...string): (string,...number)
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
end
local b:(...string)->(...number)=function(...:...string): ...number
local b:(...string)->(...number)=function(...:string): ...number
end
local c:()->()=function(): ()
@ -400,4 +412,238 @@ TEST_CASE_FIXTURE(Fixture, "function_type_location")
CHECK_EQ(expected, actual);
}
TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion")
{
std::string code = "local a = 5 :: number";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else")
{
ScopedFastFlag luauIfElseExpressionBaseSupport("LuauIfElseExpressionBaseSupport", true);
std::string code = "local a = if 1 then 2 else 3";
CHECK_EQ(code, transpile(code).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_import")
{
fileResolver.source["game/A"] = R"(
export type Type = { a: number }
return {}
)";
std::string code = R"(
local Import = require(game.A)
local a: Import.Type
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_type_packs")
{
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
std::string code = R"(
type Packed<T...> = (T...)->(T...)
local a: Packed<>
local b: Packed<(number, string)>
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested")
{
std::string code = "local a: ((number)->(string))|((string)->(string))";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2")
{
std::string code = "local a: (number&string)|(string&boolean)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
{
std::string code = "local a: nil | (string & number)";
CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested")
{
std::string code = "local a: ((number)->(string))&((string)->(string))";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2")
{
std::string code = "local a: (number|string)&(string|boolean)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_varargs")
{
std::string code = "local function f(...) return ... end";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_index_expr")
{
std::string code = "local a = {1, 2, 3} local b = a[2]";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_unary")
{
std::string code = R"(
local a = 1
local b = -1
local c = true
local d = not c
local e = 'hello'
local d = #e
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_break_continue")
{
std::string code = R"(
local a, b, c
repeat
if a then break end
if b then continue end
until c
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_compound_assignmenr")
{
std::string code = R"(
local a = 1
a += 2
a -= 3
a *= 4
a /= 5
a %= 6
a ^= 7
a ..= ' - result'
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple")
{
std::string code = "a, b, c = 1, 2, 3";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_generic_function")
{
ScopedFastFlag luauParseGenericFunctionTypeBegin("LuauParseGenericFunctionTypeBegin", true);
std::string code = R"(
local function foo<T,S...>(a: T, ...: S...) return 1 end
local f: <T,S...>(T, S...)->(number) = foo
)";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse")
{
std::string code = "local a: nil | number";
CHECK_EQ("local a: number?", transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple")
{
std::string code = "for k,v in next,{}do print(k,v) end";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE_FIXTURE(Fixture, "transpile_error_expr")
{
std::string code = "local a = f:-";
auto allocator = Allocator{};
auto names = AstNameTable{allocator};
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
CHECK_EQ("local a = (error-expr: f.%error-id%)-(error-expr)", transpileWithTypes(*parseResult.root));
}
TEST_CASE_FIXTURE(Fixture, "transpile_error_stat")
{
std::string code = "-";
auto allocator = Allocator{};
auto names = AstNameTable{allocator};
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
CHECK_EQ("(error-stat: (error-expr))", transpileWithTypes(*parseResult.root));
}
TEST_CASE_FIXTURE(Fixture, "transpile_error_type")
{
std::string code = "local a: ";
auto allocator = Allocator{};
auto names = AstNameTable{allocator};
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
CHECK_EQ("local a:%error-type%", transpileWithTypes(*parseResult.root));
}
TEST_CASE_FIXTURE(Fixture, "transpile_parse_error")
{
std::string code = "local a = -";
auto result = transpile(code);
CHECK_EQ("", result.code);
CHECK_EQ("Expected identifier when parsing expression, got <eof>", result.parseError);
}
TEST_CASE_FIXTURE(Fixture, "transpile_to_string")
{
std::string code = "local a: string = 'hello'";
auto allocator = Allocator{};
auto names = AstNameTable{allocator};
ParseResult parseResult = Parser::parse(code.data(), code.size(), names, allocator, {});
REQUIRE(parseResult.root);
REQUIRE(parseResult.root->body.size == 1);
AstStatLocal* statLocal = parseResult.root->body.data[0]->as<AstStatLocal>();
REQUIRE(statLocal);
CHECK_EQ("local a: string = 'hello'", toString(statLocal));
REQUIRE(statLocal->vars.size == 1);
AstLocal* local = statLocal->vars.data[0];
REQUIRE(local->annotation);
CHECK_EQ("string", toString(local->annotation));
REQUIRE(statLocal->values.size == 1);
AstExpr* expr = statLocal->values.data[0];
CHECK_EQ("'hello'", toString(expr));
}
TEST_SUITE_END();

View file

@ -247,9 +247,6 @@ TEST_CASE_FIXTURE(Fixture, "export_type_and_type_alias_are_duplicates")
TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
{
ScopedFastFlag sffs3{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
type Node<T> = { value: T, child: Node<T>? }

View file

@ -444,8 +444,6 @@ TEST_CASE_FIXTURE(Fixture, "os_time_takes_optional_date_table")
TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
{
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
CheckResult result = check(R"(
local co = coroutine.create(function() end)
)");
@ -456,8 +454,6 @@ TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
{
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
CheckResult result = check(R"(
local function nifty(x, y)
print(x, y)
@ -476,8 +472,6 @@ TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
TEST_CASE_FIXTURE(Fixture, "coroutine_wrap_anything_goes")
{
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
CheckResult result = check(R"(
--!nonstrict
local function nifty(x, y)
@ -822,8 +816,6 @@ TEST_CASE_FIXTURE(Fixture, "string_format_report_all_type_errors_at_correct_posi
TEST_CASE_FIXTURE(Fixture, "dont_add_definitions_to_persistent_types")
{
ScopedFastFlag sff{"LuauDontMutatePersistentFunctions", true};
CheckResult result = check(R"(
local f = math.sin
local function g(x) return math.sin(x) end

View file

@ -232,8 +232,6 @@ TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class")
TEST_CASE_FIXTURE(ClassFixture, "can_read_prop_of_base_class_using_string")
{
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
CheckResult result = check(R"(
local c = ChildClass.New()
local x = 1 + c["BaseField"]
@ -244,8 +242,6 @@ TEST_CASE_FIXTURE(ClassFixture, "can_read_prop_of_base_class_using_string")
TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string")
{
ScopedFastFlag luauClassPropertyAccessAsString("LuauClassPropertyAccessAsString", true);
CheckResult result = check(R"(
local c = ChildClass.New()
c["BaseField"] = 444
@ -451,4 +447,25 @@ b.X = 2 -- real Vector2.X is also read-only
CHECK_EQ("Value of type 'Vector2?' could be nil", toString(result.errors[3]));
}
TEST_CASE_FIXTURE(ClassFixture, "detailed_class_unification_error")
{
ScopedFastFlag luauExtendedClassMismatchError{"LuauExtendedClassMismatchError", true};
CheckResult result = check(R"(
local function foo(v)
return v.X :: number + string.len(v.Y)
end
local a: Vector2
local b = foo
b(a)
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(R"(Type 'Vector2' could not be converted into '{- X: a, Y: string -}'
caused by:
Property 'Y' is not compatible. Type 'number' could not be converted into 'string')",
toString(result.errors[0]));
}
TEST_SUITE_END();

View file

@ -171,9 +171,6 @@ TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_classes")
TEST_CASE_FIXTURE(Fixture, "declaring_generic_functions")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
loadDefinition(R"(
declare function f<a, b>(a: a, b: b): string
declare function g<a..., b...>(...: a...): b...

View file

@ -13,8 +13,6 @@ TEST_SUITE_BEGIN("GenericsTests");
TEST_CASE_FIXTURE(Fixture, "check_generic_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function id<a>(x:a): a
return x
@ -27,8 +25,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_function")
TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local function id<a>(x:a): a
return x
@ -41,10 +37,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function")
TEST_CASE_FIXTURE(Fixture, "check_generic_typepack_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauGenericVariadicsUnification", true};
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function id<a...>(...: a...): (a...) return ... end
local x: string, y: boolean = id("hi", true)
@ -56,8 +48,6 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_typepack_function")
TEST_CASE_FIXTURE(Fixture, "types_before_typepacks")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a,b...>() end
)");
@ -66,8 +56,6 @@ TEST_CASE_FIXTURE(Fixture, "types_before_typepacks")
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local function id<a>(x:a):a return x end
local f: <a>(a)->a = id
@ -79,7 +67,6 @@ TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "inferred_local_vars_can_be_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function id(x) return x end
print("This is bogus") -- TODO: CLI-39916
@ -92,7 +79,6 @@ TEST_CASE_FIXTURE(Fixture, "inferred_local_vars_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_instantiated_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function id(x) return x end
print("This is bogus") -- TODO: CLI-39916
@ -104,8 +90,6 @@ TEST_CASE_FIXTURE(Fixture, "local_vars_can_be_instantiated_polytypes")
TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local t = {}
t.m = function<a>(x: a):a return x end
@ -117,8 +101,6 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
local function id<a>(x:a):a return x end
@ -129,8 +111,6 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
TEST_CASE_FIXTURE(Fixture, "check_nested_generic_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local function f()
local function id<a>(x:a): a
@ -145,8 +125,6 @@ TEST_CASE_FIXTURE(Fixture, "check_nested_generic_function")
TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local function id<a>(x:a):a
local y: string = id("hi")
@ -159,8 +137,6 @@ TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
local id2
local function id1<a>(x:a):a
@ -179,8 +155,6 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
type T = { id: <a>(a) -> a }
local x: T = { id = function<a>(x:a):a return x end }
@ -192,8 +166,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
TEST_CASE_FIXTURE(Fixture, "generic_factories")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
type T<a> = { id: (a) -> a }
type Factory = { build: <a>() -> T<a> }
@ -215,10 +187,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
ScopedFastFlag sffs3{"LuauRankNTypes", true};
CheckResult result = check(R"(
type T = { id: <a>(a) -> a }
type Factory = { build: () -> T }
@ -241,7 +209,6 @@ TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
function id(x)
return x
@ -265,7 +232,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function")
TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function id(x)
return x
@ -289,7 +255,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_local_function")
TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function f()
local function id(x)
@ -304,7 +269,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_nested_generic_function")
TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local x = {}
function x:id(x) return x end
@ -316,7 +280,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_methods")
TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local x = {}
function x:id(x) return x end
@ -331,8 +294,6 @@ TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauRankNTypes", true};
CheckResult result = check(R"(
local t = {}
t.m = function(x) return x end
@ -344,9 +305,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
TEST_CASE_FIXTURE(Fixture, "function_arguments_can_be_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
ScopedFastFlag sffs3{"LuauRankNTypes", true};
CheckResult result = check(R"(
local function f(g: <a>(a)->a)
local x: number = g(37)
@ -358,9 +316,6 @@ TEST_CASE_FIXTURE(Fixture, "function_arguments_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "function_results_can_be_polytypes")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
ScopedFastFlag sffs3{"LuauRankNTypes", true};
CheckResult result = check(R"(
local function f() : <a>(a)->a
local function id<a>(x:a):a return x end
@ -372,9 +327,6 @@ TEST_CASE_FIXTURE(Fixture, "function_results_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "type_parameters_can_be_polytypes")
{
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
ScopedFastFlag sffs3{"LuauRankNTypes", true};
CheckResult result = check(R"(
local function id<a>(x:a):a return x end
local f: <a>(a)->a = id(id)
@ -384,7 +336,6 @@ TEST_CASE_FIXTURE(Fixture, "type_parameters_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function f(y)
-- this will only typecheck if we infer z: any
@ -406,7 +357,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
local function f(y)
local z = y
@ -423,12 +373,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
{
ScopedFastFlag sffs[] = {
{"LuauGenericFunctions", true},
{"LuauParseGenericFunctions", true},
{"LuauRankNTypes", true},
};
CheckResult result = check(R"(
type T = { m: <a>(a) -> T }
function f(t : T)
@ -440,10 +384,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
TEST_CASE_FIXTURE(Fixture, "dont_unify_bound_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
ScopedFastFlag sffs3{"LuauRankNTypes", true};
CheckResult result = check(R"(
type F = <a>() -> <b>(a, b) -> a
type G = <b>(b, b) -> b
@ -470,7 +410,6 @@ TEST_CASE_FIXTURE(Fixture, "mutable_state_polymorphism")
// Replaying the classic problem with polymorphism and mutable state in Luau
// See, e.g. Tofte (1990)
// https://www.sciencedirect.com/science/article/pii/089054019090018D.
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
--!strict
-- Our old friend the polymorphic identity function
@ -508,7 +447,6 @@ TEST_CASE_FIXTURE(Fixture, "mutable_state_polymorphism")
TEST_CASE_FIXTURE(Fixture, "rank_N_types_via_typeof")
{
ScopedFastFlag sffs{"LuauGenericFunctions", false};
CheckResult result = check(R"(
--!strict
local function id(x) return x end
@ -531,8 +469,6 @@ TEST_CASE_FIXTURE(Fixture, "rank_N_types_via_typeof")
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs2{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a,a>(x:a):a return x end
)");
@ -541,7 +477,6 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_generic_types")
TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
function f<a...,a...>() end
)");
@ -550,7 +485,6 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_generic_type_packs")
TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
CheckResult result = check(R"(
function f<a...,b>() end
)");
@ -559,9 +493,6 @@ TEST_CASE_FIXTURE(Fixture, "typepacks_before_types")
TEST_CASE_FIXTURE(Fixture, "variadic_generics")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a>(...: a) end
@ -573,9 +504,6 @@ TEST_CASE_FIXTURE(Fixture, "variadic_generics")
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a...>(...: a...): (a...) return ... end
)");
@ -586,10 +514,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_pack_syntax")
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauGenericVariadicsUnification", true};
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a...>(...: a...): any return (...) end
)");
@ -599,9 +523,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses")
TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<T>(...: T...)
return ...
@ -626,9 +547,6 @@ TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages")
TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
{
ScopedFastFlag sffs{"LuauGenericFunctions", true};
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
function f<a, a...>() end
)");
@ -641,8 +559,6 @@ TEST_CASE_FIXTURE(Fixture, "reject_clashing_generic_and_pack_names")
TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
{
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
CheckResult result = check(R"(
function f(z)
local o = {}
@ -665,8 +581,6 @@ TEST_CASE_FIXTURE(Fixture, "instantiation_sharing_types")
TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
{
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
CheckResult result = check(R"(
function f(x) return {5} end
function g(x, y) return f(x) end
@ -680,8 +594,6 @@ TEST_CASE_FIXTURE(Fixture, "quantification_sharing_types")
TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
{
ScopedFastFlag sffs1{"LuauGenericFunctions", true};
CheckResult result = check(R"(
type T<a> = { x: {a}, y: {number} }
local o1: T<boolean> = { x = {true}, y = {5} }
@ -697,7 +609,6 @@ TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
TEST_CASE_FIXTURE(Fixture, "bound_tables_do_not_clone_original_fields")
{
ScopedFastFlag luauRankNTypes{"LuauRankNTypes", true};
ScopedFastFlag luauCloneBoundTables{"LuauCloneBoundTables", true};
CheckResult result = check(R"(

View file

@ -341,4 +341,43 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_setmetatable")
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_part")
{
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type X = { x: number }
type Y = { y: number }
type Z = { z: number }
type XYZ = X & Y & Z
local a: XYZ = 3
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'number' could not be converted into 'X & Y & Z'
caused by:
Not all intersection parts are compatible. Type 'number' could not be converted into 'X')");
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all")
{
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type X = { x: number }
type Y = { y: number }
type Z = { z: number }
type XYZ = X & Y & Z
local a: XYZ
local b: number = a
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'X & Y & Z' could not be converted into 'number'; none of the intersection parts are compatible)");
}
TEST_SUITE_END();

View file

@ -194,9 +194,6 @@ TEST_CASE_FIXTURE(Fixture, "normal_conditional_expression_has_refinements")
// Luau currently doesn't yet know how to allow assignments when the binding was refined.
TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
{
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
ScopedFastFlag sffs5{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
type Node<T> = { value: T, child: Node<T>? }
@ -596,11 +593,9 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
{
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
ScopedFastFlag luauFollowInTypeFunApply{"LuauFollowInTypeFunApply", true};
ScopedFastFlag luauInstantiatedTypeParamRecursion{"LuauInstantiatedTypeParamRecursion", true};
// Mutability in type function application right now can create strange recursive types
// TODO: instantiation right now is problematic, it this example should either leave the Table type alone
// TODO: instantiation right now is problematic, in this example should either leave the Table type alone
// or it should rename the type to 'Self' so that the result will be 'Self<Table>'
CheckResult result = check(R"(
type Table = { a: number }

View file

@ -7,7 +7,6 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauWeakEqConstraint)
LUAU_FASTFLAG(LuauOrPredicate)
LUAU_FASTFLAG(LuauQuantifyInPlace2)
using namespace Luau;
@ -133,12 +132,9 @@ TEST_CASE_FIXTURE(Fixture, "or_predicate_with_truthy_predicates")
CHECK_EQ("string?", toString(requireTypeAtPosition({3, 26})));
CHECK_EQ("number?", toString(requireTypeAtPosition({4, 26})));
if (FFlag::LuauOrPredicate)
{
CHECK_EQ("nil", toString(requireTypeAtPosition({6, 26})));
CHECK_EQ("nil", toString(requireTypeAtPosition({7, 26})));
}
}
TEST_CASE_FIXTURE(Fixture, "type_assertion_expr_carry_its_constraints")
{
@ -283,6 +279,8 @@ TEST_CASE_FIXTURE(Fixture, "assert_non_binary_expressions_actually_resolve_const
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
{
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true};
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
local t: {x: number?} = {x = nil}
@ -293,7 +291,10 @@ TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_ty
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("Type '{| x: number? |}' could not be converted into '{| x: number |}'", toString(result.errors[0]));
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
caused by:
Property 'x' is not compatible. Type 'number?' could not be converted into 'number')",
toString(result.errors[0]));
}
TEST_CASE_FIXTURE(Fixture, "lvalue_is_equal_to_another_lvalue")
@ -749,8 +750,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "type_narrow_for_all_the_userdata")
TEST_CASE_FIXTURE(RefinementClassFixture, "eliminate_subclasses_of_instance")
{
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
CheckResult result = check(R"(
local function f(x: Part | Folder | string)
if typeof(x) == "Instance" then
@ -769,8 +768,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "eliminate_subclasses_of_instance")
TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_this_large_union")
{
ScopedFastFlag sff{"LuauTypeGuardPeelsAwaySubclasses", true};
CheckResult result = check(R"(
local function f(x: Part | Folder | Instance | string | Vector3 | any)
if typeof(x) == "Instance" then
@ -789,8 +786,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_this_large_union")
TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is_table")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
--!nonstrict
@ -811,11 +806,6 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is
TEST_CASE_FIXTURE(RefinementClassFixture, "x_is_not_instance_or_else_not_part")
{
ScopedFastFlag sffs[] = {
{"LuauOrPredicate", true},
{"LuauTypeGuardPeelsAwaySubclasses", true},
};
CheckResult result = check(R"(
local function f(x: Part | Folder | string)
if typeof(x) ~= "Instance" or not x:IsA("Part") then
@ -890,8 +880,6 @@ TEST_CASE_FIXTURE(Fixture, "type_guard_warns_on_no_overlapping_types_only_when_s
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(a: number?, b: number?)
if (not a) or (not b) then
@ -909,8 +897,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b")
TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b2")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(a: number?, b: number?)
if not (a and b) then
@ -928,8 +914,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_or_not_b2")
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(a: number?, b: number?)
if (not a) and (not b) then
@ -947,8 +931,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b")
TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b2")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(a: number?, b: number?)
if not (a or b) then
@ -966,8 +948,6 @@ TEST_CASE_FIXTURE(Fixture, "not_a_and_not_b2")
TEST_CASE_FIXTURE(Fixture, "either_number_or_string")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(x: any)
if type(x) == "number" or type(x) == "string" then
@ -983,8 +963,6 @@ TEST_CASE_FIXTURE(Fixture, "either_number_or_string")
TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(t: {x: boolean}?)
if not t or t.x then
@ -1000,8 +978,6 @@ TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
TEST_CASE_FIXTURE(Fixture, "assert_a_to_be_truthy_then_assert_a_to_be_number")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local a: (number | string)?
assert(a)
@ -1018,8 +994,6 @@ TEST_CASE_FIXTURE(Fixture, "assert_a_to_be_truthy_then_assert_a_to_be_number")
TEST_CASE_FIXTURE(Fixture, "merge_should_be_fully_agnostic_of_hashmap_ordering")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
// This bug came up because there was a mistake in Luau::merge where zipping on two maps would produce the wrong merged result.
CheckResult result = check(R"(
local function f(b: string | { x: string }, a)
@ -1039,8 +1013,6 @@ TEST_CASE_FIXTURE(Fixture, "merge_should_be_fully_agnostic_of_hashmap_ordering")
TEST_CASE_FIXTURE(Fixture, "refine_the_correct_types_opposite_of_when_a_is_not_number_or_string")
{
ScopedFastFlag sff{"LuauOrPredicate", true};
CheckResult result = check(R"(
local function f(a: string | number | boolean)
if type(a) ~= "number" and type(a) ~= "string" then

View file

@ -1950,4 +1950,76 @@ TEST_CASE_FIXTURE(Fixture, "table_insert_should_cope_with_optional_properties_in
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop")
{
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type A = { x: number, y: number }
type B = { x: number, y: string }
local a: A
local b: B = a
)");
LUAU_REQUIRE_ERRORS(result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'A' could not be converted into 'B'
caused by:
Property 'y' is not compatible. Type 'number' could not be converted into 'string')");
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop_nested")
{
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type AS = { x: number, y: number }
type BS = { x: number, y: string }
type A = { a: boolean, b: AS }
type B = { a: boolean, b: BS }
local a: A
local b: B = a
)");
LUAU_REQUIRE_ERRORS(result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'A' could not be converted into 'B'
caused by:
Property 'b' is not compatible. Type 'AS' could not be converted into 'BS'
caused by:
Property 'y' is not compatible. Type 'number' could not be converted into 'string')");
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_metatable_prop")
{
ScopedFastFlag luauTableSubtypingVariance{"LuauTableSubtypingVariance", true}; // Only for new path
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
local a1 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
local b1 = setmetatable({ x = 2, y = "hello" }, { __call = function(s) end });
local c1: typeof(a1) = b1
local a2 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
local b2 = setmetatable({ x = 2, y = 4 }, { __call = function(s, t) end });
local c2: typeof(a2) = b2
)");
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'b1' could not be converted into 'a1'
caused by:
Type '{| x: number, y: string |}' could not be converted into '{| x: number, y: number |}'
caused by:
Property 'y' is not compatible. Type 'string' could not be converted into 'number')");
CHECK_EQ(toString(result.errors[1]), R"(Type 'b2' could not be converted into 'a2'
caused by:
Type '{| __call: (a, b) -> () |}' could not be converted into '{| __call: <a>(a) -> () |}'
caused by:
Property '__call' is not compatible. Type '(a, b) -> ()' could not be converted into '<a>(a) -> ()')");
}
TEST_SUITE_END();

View file

@ -3926,8 +3926,6 @@ local b: number = 1 or a
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
{
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
CheckResult result = check(R"(
--!strict
local tbl = {}
@ -4493,10 +4491,6 @@ f(function(x) print(x) end)
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument")
{
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
CheckResult result = check(R"(
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
return sum(2, 3, function(a, b) return a + b end)
@ -4525,10 +4519,6 @@ local r = foldl(a, {s=0,c=0}, function(a, b) return {s = a.s + b, c = a.c + 1} e
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded")
{
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
CheckResult result = check(R"(
local function g1<T>(a: T, f: (T) -> T) return f(a) end
local function g2<T>(a: T, b: T, f: (T, T) -> T) return f(a, b) end
@ -4579,10 +4569,6 @@ local a: TableWithFunc = { x = 3, y = 4, f = function(a, b) return a + b end }
TEST_CASE_FIXTURE(Fixture, "do_not_infer_generic_functions")
{
ScopedFastFlag luauGenericFunctions("LuauGenericFunctions", true);
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauRankNTypes("LuauRankNTypes", true);
CheckResult result = check(R"(
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
@ -4600,8 +4586,6 @@ local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not i
TEST_CASE_FIXTURE(Fixture, "infer_return_value_type")
{
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
CheckResult result = check(R"(
local function f(): {string|number}
return {1, "b", 3}
@ -4625,8 +4609,6 @@ end
TEST_CASE_FIXTURE(Fixture, "infer_type_assertion_value_type")
{
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
CheckResult result = check(R"(
local function f()
return {4, "b", 3} :: {string|number}
@ -4638,8 +4620,6 @@ end
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types")
{
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
CheckResult result = check(R"(
local a: (number, number) -> number = function(a, b) return a - b end
@ -4655,8 +4635,6 @@ b, c = {2, "s"}, {"b", 4}
TEST_CASE_FIXTURE(Fixture, "infer_assignment_value_types_mutable_lval")
{
ScopedFastFlag luauInferReturnAssertAssign("LuauInferReturnAssertAssign", true);
CheckResult result = check(R"(
local a = {}
a.x = 2
@ -4668,8 +4646,6 @@ a = setmetatable(a, { __call = function(x) end })
TEST_CASE_FIXTURE(Fixture, "refine_and_or")
{
ScopedFastFlag sff{"LuauSlightlyMoreFlexibleBinaryPredicates", true};
CheckResult result = check(R"(
local t: {x: number?}? = {x = nil}
local u = t and t.x or 5
@ -4682,10 +4658,6 @@ TEST_CASE_FIXTURE(Fixture, "refine_and_or")
TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
{
ScopedFastFlag sffs[] = {
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
};
CheckResult result = check(R"(
local t: {x: number?}? = {x = nil}
local u = t.x and t or 5
@ -4698,10 +4670,6 @@ TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch")
{
ScopedFastFlag sffs[] = {
{"LuauSlightlyMoreFlexibleBinaryPredicates", true},
};
CheckResult result = check(R"(
local t: {x: number?}? = {x = nil}
local u = t and t.x == 5 or t.x == 31337
@ -4714,7 +4682,7 @@ TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch")
TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
{
ScopedFastFlag luauFollowInTypeFunApply("LuauFollowInTypeFunApply", true);
ScopedFastFlag luauCloneCorrectlyBeforeMutatingTableType{"LuauCloneCorrectlyBeforeMutatingTableType", true};
CheckResult result = check(R"(
type A = { x: number }

View file

@ -178,9 +178,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "unifying_variadic_pack_with_error_should_wor
TEST_CASE_FIXTURE(TryUnifyFixture, "variadics_should_use_reversed_properly")
{
ScopedFastFlag sffs2{"LuauGenericFunctions", true};
ScopedFastFlag sffs4{"LuauParseGenericFunctions", true};
CheckResult result = check(R"(
--!strict
local function f<T>(...: T): ...T
@ -199,8 +196,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "variadics_should_use_reversed_properly")
TEST_CASE_FIXTURE(TryUnifyFixture, "cli_41095_concat_log_in_sealed_table_unification")
{
ScopedFastFlag sffs2("LuauGenericFunctions", true);
CheckResult result = check(R"(
--!strict
table.insert()

View file

@ -296,7 +296,6 @@ end
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -361,7 +360,6 @@ local c: Packed<string, number, boolean>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_import")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -395,7 +393,6 @@ local d: { a: typeof(c) }
TEST_CASE_FIXTURE(Fixture, "type_pack_type_parameters")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -434,7 +431,6 @@ type C<X...> = Import.Packed<string, (number, X...)>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_nested")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -456,7 +452,6 @@ type Packed4<T...> = (Packed3<T...>, T...) -> (Packed3<T...>, T...)
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_variadic")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -475,7 +470,6 @@ type E = X<(number, ...string)>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_multi")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -507,7 +501,6 @@ type I<S..., R...> = W<number, (string, S...), R...>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -534,7 +527,6 @@ type F = X<(string, ...number)>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -557,10 +549,8 @@ type D<X...> = Y<X..., (number, string, X...)>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi_tostring")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
ScopedFastFlag luauInstantiatedTypeParamRecursion("LuauInstantiatedTypeParamRecursion", true); // For correct toString block
CheckResult result = check(R"(
type Y<T..., U...> = { f: (T...) -> (U...) }
@ -577,7 +567,6 @@ local b: Y<(), ()>
TEST_CASE_FIXTURE(Fixture, "type_alias_backwards_compatible")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
@ -599,7 +588,6 @@ type C = Y<(number), boolean>
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_errors")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);

View file

@ -400,8 +400,6 @@ local e = a.z
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
{
ScopedFastFlag luauSealedTableUnifyOptionalFix("LuauSealedTableUnifyOptionalFix", true);
CheckResult result = check(R"(
local x: { x: number } = { x = 3 }
type A = number?
@ -426,4 +424,43 @@ y = x
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_part")
{
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type X = { x: number }
type Y = { y: number }
type Z = { z: number }
type XYZ = X | Y | Z
local a: XYZ
local b: { w: number } = a
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'X | Y | Z' could not be converted into '{| w: number |}'
caused by:
Not all union options are compatible. Table type 'X' not compatible with type '{| w: number |}' because the former is missing field 'w')");
}
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all")
{
ScopedFastFlag luauExtendedTypeMismatchError{"LuauExtendedTypeMismatchError", true};
CheckResult result = check(R"(
type X = { x: number }
type Y = { y: number }
type Z = { z: number }
type XYZ = X | Y | Z
local a: XYZ = { w = 4 }
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'a' could not be converted into 'X | Y | Z'; none of the union options are compatible)");
}
TEST_SUITE_END();

View file

@ -11,8 +11,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauGenericFunctions);
TEST_SUITE_BEGIN("TypeVarTests");
TEST_CASE_FIXTURE(Fixture, "primitives_are_equal")

View file

@ -113,6 +113,20 @@ assert(bit32.replace(0, -1, 4) == 2^4)
assert(bit32.replace(-1, 0, 31) == 2^31 - 1)
assert(bit32.replace(-1, 0, 1, 2) == 2^32 - 7)
-- testing countlz/countrc
assert(bit32.countlz(0) == 32)
assert(bit32.countlz(42) == 26)
assert(bit32.countlz(0xffffffff) == 0)
assert(bit32.countlz(0x80000000) == 0)
assert(bit32.countlz(0x7fffffff) == 1)
assert(bit32.countrz(0) == 32)
assert(bit32.countrz(1) == 0)
assert(bit32.countrz(42) == 1)
assert(bit32.countrz(0x80000000) == 31)
assert(bit32.countrz(0x40000000) == 30)
assert(bit32.countrz(0x7fffffff) == 0)
--[[
This test verifies a fix in luauF_replace() where if the 4th
parameter was not a number, but the first three are numbers, it will
@ -136,5 +150,7 @@ assert(bit32.bxor("1", 3) == 2)
assert(bit32.bxor(1, "3") == 2)
assert(bit32.btest(1, "3") == true)
assert(bit32.btest("1", 3) == true)
assert(bit32.countlz("42") == 26)
assert(bit32.countrz("42") == 1)
return('OK')