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
{
@ -128,13 +126,10 @@ TypePack* asMutable(const TypePack* tp);
template<typename T>
const T* get(TypePackId tp)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tp);
LUAU_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
}
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
return get_if<T>(&(tp->ty));
}
@ -142,13 +137,10 @@ const T* get(TypePackId tp)
template<typename T>
T* getMutable(TypePackId tp)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tp);
LUAU_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
}
if constexpr (!std::is_same_v<T, BoundTypePack>)
LUAU_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
return get_if<T>(&(asMutable(tp)->ty));
}

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();
};
@ -472,13 +467,10 @@ TypeVar* asMutable(TypeId ty);
template<typename T>
const T* get(TypeId tv)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tv);
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
}
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&tv->ty);
}
@ -486,13 +478,10 @@ const T* get(TypeId tv)
template<typename T>
T* getMutable(TypeId tv)
{
if (FFlag::LuauAddMissingFollow)
{
LUAU_ASSERT(tv);
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
}
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&asMutable(tv)->ty);
}

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)
@ -369,20 +368,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
while (iter != endIter)
{
if (FFlag::LuauAddMissingFollow)
{
if (isNil(*iter))
++iter;
else
break;
}
if (isNil(*iter))
++iter;
else
{
if (auto primTy = Luau::get<PrimitiveTypeVar>(*iter); primTy && primTy->type == PrimitiveTypeVar::NilType)
++iter;
else
break;
}
break;
}
if (iter == endIter)
@ -397,21 +386,10 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryMap inner;
std::unordered_set<TypeId> innerSeen = seen;
if (FFlag::LuauAddMissingFollow)
if (isNil(*iter))
{
if (isNil(*iter))
{
++iter;
continue;
}
}
else
{
if (auto innerPrimTy = Luau::get<PrimitiveTypeVar>(*iter); innerPrimTy && innerPrimTy->type == PrimitiveTypeVar::NilType)
{
++iter;
continue;
}
++iter;
continue;
}
autocompleteProps(module, typeArena, *iter, indexType, nodes, inner, innerSeen);
@ -1519,10 +1497,10 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
return {};
TypeChecker& typeChecker =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
: frontend.moduleResolver.getModule(moduleName));
(frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
: frontend.moduleResolver.getModule(moduleName));
if (!module)
return {};
@ -1550,7 +1528,7 @@ OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view
sourceModule->commentLocations = std::move(result.commentLocations);
TypeChecker& typeChecker =
(frontend.options.typecheckTwice && FFlag::LuauSecondTypecheckKnowsTheDataModel ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);

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,130 +732,141 @@ bool containsParseErrorName(const TypeError& error)
return Luau::visit(InvalidNameChecker{}, error.data);
}
void copyErrors(ErrorVec& errors, struct TypeArena& destArena)
template<typename T>
void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
auto clone = [&](auto&& ty) {
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
};
auto visitErrorData = [&](auto&& e) {
using T = std::decay_t<decltype(e)>;
copyError(e, destArena, seenTypes, seenTypePacks);
};
if constexpr (false)
{
}
else if constexpr (std::is_same_v<T, TypeMismatch>)
{
e.wantedType = clone(e.wantedType);
e.givenType = clone(e.givenType);
}
else if constexpr (std::is_same_v<T, UnknownSymbol>)
{
}
else if constexpr (std::is_same_v<T, UnknownProperty>)
{
e.table = clone(e.table);
}
else if constexpr (std::is_same_v<T, NotATable>)
{
e.ty = clone(e.ty);
}
else if constexpr (std::is_same_v<T, CannotExtendTable>)
{
e.tableType = clone(e.tableType);
}
else if constexpr (std::is_same_v<T, OnlyTablesCanHaveMethods>)
{
e.tableType = clone(e.tableType);
}
else if constexpr (std::is_same_v<T, DuplicateTypeDefinition>)
{
}
else if constexpr (std::is_same_v<T, CountMismatch>)
{
}
else if constexpr (std::is_same_v<T, FunctionDoesNotTakeSelf>)
{
}
else if constexpr (std::is_same_v<T, FunctionRequiresSelf>)
{
}
else if constexpr (std::is_same_v<T, OccursCheckFailed>)
{
}
else if constexpr (std::is_same_v<T, UnknownRequire>)
{
}
else if constexpr (std::is_same_v<T, IncorrectGenericParameterCount>)
{
e.typeFun = clone(e.typeFun);
}
else if constexpr (std::is_same_v<T, SyntaxError>)
{
}
else if constexpr (std::is_same_v<T, CodeTooComplex>)
{
}
else if constexpr (std::is_same_v<T, UnificationTooComplex>)
{
}
else if constexpr (std::is_same_v<T, UnknownPropButFoundLikeProp>)
{
e.table = clone(e.table);
}
else if constexpr (std::is_same_v<T, GenericError>)
{
}
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
{
e.ty = clone(e.ty);
}
else if constexpr (std::is_same_v<T, ExtraInformation>)
{
}
else if constexpr (std::is_same_v<T, DeprecatedApiUsed>)
{
}
else if constexpr (std::is_same_v<T, ModuleHasCyclicDependency>)
{
}
else if constexpr (std::is_same_v<T, IllegalRequire>)
{
}
else if constexpr (std::is_same_v<T, FunctionExitsWithoutReturning>)
{
e.expectedReturnType = clone(e.expectedReturnType);
}
else if constexpr (std::is_same_v<T, DuplicateGenericParameter>)
{
}
else if constexpr (std::is_same_v<T, CannotInferBinaryOperation>)
{
}
else if constexpr (std::is_same_v<T, MissingProperties>)
{
e.superType = clone(e.superType);
e.subType = clone(e.subType);
}
else if constexpr (std::is_same_v<T, SwappedGenericTypeParameter>)
{
}
else if constexpr (std::is_same_v<T, OptionalValueAccess>)
{
e.optional = clone(e.optional);
}
else if constexpr (std::is_same_v<T, MissingUnionProperty>)
{
e.type = clone(e.type);
if constexpr (false)
{
}
else if constexpr (std::is_same_v<T, TypeMismatch>)
{
e.wantedType = clone(e.wantedType);
e.givenType = clone(e.givenType);
for (auto& ty : e.missing)
ty = clone(ty);
}
else
static_assert(always_false_v<T>, "Non-exhaustive type switch");
if (e.error)
visit(visitErrorData, e.error->data);
}
else if constexpr (std::is_same_v<T, UnknownSymbol>)
{
}
else if constexpr (std::is_same_v<T, UnknownProperty>)
{
e.table = clone(e.table);
}
else if constexpr (std::is_same_v<T, NotATable>)
{
e.ty = clone(e.ty);
}
else if constexpr (std::is_same_v<T, CannotExtendTable>)
{
e.tableType = clone(e.tableType);
}
else if constexpr (std::is_same_v<T, OnlyTablesCanHaveMethods>)
{
e.tableType = clone(e.tableType);
}
else if constexpr (std::is_same_v<T, DuplicateTypeDefinition>)
{
}
else if constexpr (std::is_same_v<T, CountMismatch>)
{
}
else if constexpr (std::is_same_v<T, FunctionDoesNotTakeSelf>)
{
}
else if constexpr (std::is_same_v<T, FunctionRequiresSelf>)
{
}
else if constexpr (std::is_same_v<T, OccursCheckFailed>)
{
}
else if constexpr (std::is_same_v<T, UnknownRequire>)
{
}
else if constexpr (std::is_same_v<T, IncorrectGenericParameterCount>)
{
e.typeFun = clone(e.typeFun);
}
else if constexpr (std::is_same_v<T, SyntaxError>)
{
}
else if constexpr (std::is_same_v<T, CodeTooComplex>)
{
}
else if constexpr (std::is_same_v<T, UnificationTooComplex>)
{
}
else if constexpr (std::is_same_v<T, UnknownPropButFoundLikeProp>)
{
e.table = clone(e.table);
}
else if constexpr (std::is_same_v<T, GenericError>)
{
}
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
{
e.ty = clone(e.ty);
}
else if constexpr (std::is_same_v<T, ExtraInformation>)
{
}
else if constexpr (std::is_same_v<T, DeprecatedApiUsed>)
{
}
else if constexpr (std::is_same_v<T, ModuleHasCyclicDependency>)
{
}
else if constexpr (std::is_same_v<T, IllegalRequire>)
{
}
else if constexpr (std::is_same_v<T, FunctionExitsWithoutReturning>)
{
e.expectedReturnType = clone(e.expectedReturnType);
}
else if constexpr (std::is_same_v<T, DuplicateGenericParameter>)
{
}
else if constexpr (std::is_same_v<T, CannotInferBinaryOperation>)
{
}
else if constexpr (std::is_same_v<T, MissingProperties>)
{
e.superType = clone(e.superType);
e.subType = clone(e.subType);
}
else if constexpr (std::is_same_v<T, SwappedGenericTypeParameter>)
{
}
else if constexpr (std::is_same_v<T, OptionalValueAccess>)
{
e.optional = clone(e.optional);
}
else if constexpr (std::is_same_v<T, MissingUnionProperty>)
{
e.type = clone(e.type);
for (auto& ty : e.missing)
ty = clone(ty);
}
else
static_assert(always_false_v<T>, "Non-exhaustive type switch");
}
void copyErrors(ErrorVec& errors, TypeArena& destArena)
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
auto visitErrorData = [&](auto&& e) {
copyError(e, destArena, seenTypes, seenTypePacks);
};
LUAU_ASSERT(!destArena.typeVars.isFrozen());

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");
validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type");
}
else if (g && g->name == "typeof")
{
@ -2146,7 +2140,7 @@ private:
"wrap it in parentheses to silence");
}
if (FFlag::LuauLinterTableMoveZero && func->index == "move" && node->args.size >= 4)
if (func->index == "move" && node->args.size >= 4)
{
// table.move(t, 0, _, _)
if (isConstant(args[1], 0.0))

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->tags = t.tags;
ftv->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, encounteredFreeType);
ftv->argNames = t.argNames;
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
@ -319,12 +316,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
ttv->level = TypeLevel{0, 0};
for (const auto& [name, prop] : t.props)
{
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
else
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
}
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
if (t.indexer)
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
@ -379,10 +371,7 @@ void TypeCloner::operator()(const ClassTypeVar& t)
seenTypes[typeId] = result;
for (const auto& [name, prop] : t.props)
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
else
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location};
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
if (t.parent)
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);

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,8 +397,7 @@ TypeId Substitution::clone(TypeId ty)
if (FFlag::LuauTypeAliasPacks)
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
if (FFlag::LuauSecondTypecheckKnowsTheDataModel)
clone.tags = ttv->tags;
clone.tags = ttv->tags;
result = addType(std::move(clone));
}
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
@ -486,7 +483,7 @@ void Substitution::replaceChildren(TypeId ty)
{
ty = follow(ty);
if (FFlag::LuauRankNTypes && ignoreChildren(ty))
if (ignoreChildren(ty))
return;
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
@ -535,7 +532,7 @@ void Substitution::replaceChildren(TypePackId tp)
{
tp = follow(tp);
if (FFlag::LuauRankNTypes && ignoreChildren(tp))
if (ignoreChildren(tp))
return;
if (TypePack* tpp = getMutable<TypePack>(tp))

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

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

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 numGenerics = 0;
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
{
generics.size = ftv.generics.size();
generics.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * generics.size));
size_t i = 0;
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
{
if (auto gtv = get<GenericTypeVar>(*it))
generics.data[i++] = AstName(gtv->name.c_str());
}
}
else
{
generics.size = 0;
generics.data = nullptr;
if (auto gtv = get<GenericTypeVar>(*it))
generics.data[numGenerics++] = AstName(gtv->name.c_str());
}
AstArray<AstName> genericPacks;
if (FFlag::LuauGenericFunctions)
genericPacks.size = ftv.genericPacks.size();
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
size_t numGenericPacks = 0;
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
{
genericPacks.size = ftv.genericPacks.size();
genericPacks.data = static_cast<AstName*>(allocator->allocate(sizeof(AstName) * genericPacks.size));
size_t i = 0;
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
{
if (auto gtv = get<GenericTypeVar>(*it))
genericPacks.data[i++] = AstName(gtv->name.c_str());
}
}
else
{
generics.size = 0;
generics.data = nullptr;
if (auto gtv = get<GenericTypeVar>(*it))
genericPacks.data[numGenericPacks++] = AstName(gtv->name.c_str());
}
AstArray<AstType*> argTypes;

File diff suppressed because it is too large Load diff

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,7 +340,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
if (unificationTooComplex)
errors.push_back(*unificationTooComplex);
else if (failed)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
{
if (FFlag::LuauExtendedTypeMismatchError && firstFailedOption)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "Not all union options are compatible", *firstFailedOption}});
else
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
}
else if (const UnionTypeVar* uv = get<UnionTypeVar>(superTy))
{
@ -425,14 +412,49 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
if (unificationTooComplex)
errors.push_back(*unificationTooComplex);
else if (!found)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
{
if (FFlag::LuauExtendedTypeMismatchError)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "none of the union options are compatible"}});
else
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
}
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(superTy))
{
// T <: A & B if A <: T and B <: T
for (TypeId type : uv->parts)
if (FFlag::LuauExtendedTypeMismatchError)
{
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
std::optional<TypeError> unificationTooComplex;
std::optional<TypeError> firstFailedOption;
// T <: A & B if A <: T and B <: T
for (TypeId type : uv->parts)
{
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
if (auto e = hasUnificationTooComplex(innerState.errors))
unificationTooComplex = e;
else if (!innerState.errors.empty())
{
if (!firstFailedOption)
firstFailedOption = {innerState.errors.front()};
}
log.concat(std::move(innerState.log));
}
if (unificationTooComplex)
errors.push_back(*unificationTooComplex);
else if (firstFailedOption)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "Not all intersection parts are compatible", *firstFailedOption}});
}
else
{
// T <: A & B if A <: T and B <: T
for (TypeId type : uv->parts)
{
tryUnify_(type, subTy, /*isFunctionCall*/ false, /*isIntersection*/ true);
}
}
}
else if (const IntersectionTypeVar* uv = get<IntersectionTypeVar>(subTy))
@ -480,7 +502,12 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
if (unificationTooComplex)
errors.push_back(*unificationTooComplex);
else if (!found)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
{
if (FFlag::LuauExtendedTypeMismatchError)
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy, "none of the intersection parts are compatible"}});
else
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
}
else if (get<PrimitiveTypeVar>(superTy) && get<PrimitiveTypeVar>(subTy))
tryUnifyPrimitives(superTy, subTy);
@ -773,8 +800,8 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
// If both are at the end, we're done
if (!superIter.good() && !subIter.good())
{
const bool lFreeTail = l->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*l->tail) : *l->tail) != nullptr;
const bool rFreeTail = r->tail && get<FreeTypePack>(FFlag::LuauAddMissingFollow ? follow(*r->tail) : *r->tail) != nullptr;
const bool lFreeTail = l->tail && get<FreeTypePack>(follow(*l->tail)) != nullptr;
const bool rFreeTail = r->tail && get<FreeTypePack>(follow(*r->tail)) != nullptr;
if (lFreeTail && rFreeTail)
tryUnify_(*l->tail, *r->tail);
else if (lFreeTail)
@ -812,7 +839,7 @@ void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCal
}
// In nonstrict mode, any also marks an optional argument.
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(FFlag::LuauAddMissingFollow ? follow(*superIter) : *superIter))
else if (superIter.good() && isNonstrictMode() && get<AnyTypeVar>(follow(*superIter)))
{
superIter.advance();
continue;
@ -887,24 +914,21 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
ice("passed non-function types to unifyFunction");
size_t numGenerics = lf->generics.size();
if (FFlag::LuauGenericFunctions && numGenerics != rf->generics.size())
if (numGenerics != rf->generics.size())
{
numGenerics = std::min(lf->generics.size(), rf->generics.size());
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
size_t numGenericPacks = lf->genericPacks.size();
if (FFlag::LuauGenericFunctions && numGenericPacks != rf->genericPacks.size())
if (numGenericPacks != rf->genericPacks.size())
{
numGenericPacks = std::min(lf->genericPacks.size(), rf->genericPacks.size());
errors.push_back(TypeError{location, TypeMismatch{superTy, subTy}});
}
if (FFlag::LuauGenericFunctions)
{
for (size_t i = 0; i < numGenerics; i++)
log.pushSeen(lf->generics[i], rf->generics[i]);
}
for (size_t i = 0; i < numGenerics; i++)
log.pushSeen(lf->generics[i], rf->generics[i]);
CountMismatch::Context context = ctx;
@ -931,22 +955,19 @@ void Unifier::tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCal
tryUnify_(lf->retType, rf->retType);
}
if (lf->definition && !rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !subTy->persistent))
if (lf->definition && !rf->definition && !subTy->persistent)
{
rf->definition = lf->definition;
}
else if (!lf->definition && rf->definition && (!FFlag::LuauDontMutatePersistentFunctions || !superTy->persistent))
else if (!lf->definition && rf->definition && !superTy->persistent)
{
lf->definition = rf->definition;
}
ctx = context;
if (FFlag::LuauGenericFunctions)
{
for (int i = int(numGenerics) - 1; 0 <= i; i--)
log.popSeen(lf->generics[i], rf->generics[i]);
}
for (int i = int(numGenerics) - 1; 0 <= i; i--)
log.popSeen(lf->generics[i], rf->generics[i]);
}
namespace
@ -1032,7 +1053,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, r->second.type);
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1047,7 +1073,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, rt->indexer->indexResultType);
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1083,7 +1114,12 @@ void Unifier::tryUnifyTables(TypeId left, TypeId right, bool isIntersection)
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, lt->indexer->indexResultType);
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (FFlag::LuauExtendedTypeMismatchError)
checkChildUnifierTypeMismatch(innerState.errors, name, left, right);
else
checkChildUnifierTypeMismatch(innerState.errors, left, right);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else
@ -1384,21 +1420,8 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
const auto& r = rt->props.find(it.first);
if (r == rt->props.end())
{
if (FFlag::LuauSealedTableUnifyOptionalFix)
{
if (isOptional(it.second.type))
continue;
}
else
{
if (get<UnionTypeVar>(it.second.type))
{
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
const std::vector<TypeId>& options = possiblyOptional->options;
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
continue;
}
}
if (isOptional(it.second.type))
continue;
missingPropertiesInSuper.push_back(it.first);
@ -1482,21 +1505,8 @@ void Unifier::tryUnifySealedTables(TypeId left, TypeId right, bool isIntersectio
const auto& r = lt->props.find(it.first);
if (r == lt->props.end())
{
if (FFlag::LuauSealedTableUnifyOptionalFix)
{
if (isOptional(it.second.type))
continue;
}
else
{
if (get<UnionTypeVar>(it.second.type))
{
const UnionTypeVar* possiblyOptional = get<UnionTypeVar>(it.second.type);
const std::vector<TypeId>& options = possiblyOptional->options;
if (options.end() != std::find_if(options.begin(), options.end(), isNil))
continue;
}
}
if (isOptional(it.second.type))
continue;
extraPropertiesInSub.push_back(it.first);
}
@ -1526,7 +1536,18 @@ void Unifier::tryUnifyWithMetatable(TypeId metatable, TypeId other, bool reverse
innerState.tryUnify_(lhs->table, rhs->table);
innerState.tryUnify_(lhs->metatable, rhs->metatable);
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
if (FFlag::LuauExtendedTypeMismatchError)
{
if (auto e = hasUnificationTooComplex(innerState.errors))
errors.push_back(*e);
else if (!innerState.errors.empty())
errors.push_back(
TypeError{location, TypeMismatch{reversed ? other : metatable, reversed ? metatable : other, "", innerState.errors.front()}});
}
else
{
checkChildUnifierTypeMismatch(innerState.errors, reversed ? other : metatable, reversed ? metatable : other);
}
log.concat(std::move(innerState.log));
}
@ -1613,10 +1634,34 @@ void Unifier::tryUnifyWithClass(TypeId superTy, TypeId subTy, bool reversed)
{
ok = false;
errors.push_back(TypeError{location, UnknownProperty{superTy, propName}});
tryUnify_(prop.type, singletonTypes.errorType);
if (!FFlag::LuauExtendedClassMismatchError)
tryUnify_(prop.type, singletonTypes.errorType);
}
else
tryUnify_(prop.type, classProp->type);
{
if (FFlag::LuauExtendedClassMismatchError)
{
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(prop.type, classProp->type);
checkChildUnifierTypeMismatch(innerState.errors, propName, reversed ? subTy : superTy, reversed ? superTy : subTy);
if (innerState.errors.empty())
{
log.concat(std::move(innerState.log));
}
else
{
ok = false;
innerState.log.rollback();
}
}
else
{
tryUnify_(prop.type, classProp->type);
}
}
}
if (table->indexer)
@ -1649,45 +1694,24 @@ static void queueTypePack_DEPRECATED(
while (true)
{
if (FFlag::LuauAddMissingFollow)
a = follow(a);
a = follow(a);
if (seenTypePacks.count(a))
break;
seenTypePacks.insert(a);
if (FFlag::LuauAddMissingFollow)
if (get<Unifiable::Free>(a))
{
if (get<Unifiable::Free>(a))
{
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
else if (auto tp = get<TypePack>(a))
{
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
else
else if (auto tp = get<TypePack>(a))
{
if (get<Unifiable::Free>(a))
{
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
if (auto tp = get<TypePack>(a))
{
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
}
}
@ -1698,45 +1722,24 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
while (true)
{
if (FFlag::LuauAddMissingFollow)
a = follow(a);
a = follow(a);
if (seenTypePacks.find(a))
break;
seenTypePacks.insert(a);
if (FFlag::LuauAddMissingFollow)
if (get<Unifiable::Free>(a))
{
if (get<Unifiable::Free>(a))
{
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
else if (auto tp = get<TypePack>(a))
{
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
else
else if (auto tp = get<TypePack>(a))
{
if (get<Unifiable::Free>(a))
{
state.log(a);
*asMutable(a) = Unifiable::Bound{anyTypePack};
}
if (auto tp = get<TypePack>(a))
{
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
}
}
@ -1990,33 +1993,6 @@ std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N
return Luau::findTablePropertyRespectingMeta(errors, globalScope, lhsType, name, location);
}
std::optional<TypeId> Unifier::findMetatableEntry(TypeId type, std::string entry)
{
type = follow(type);
std::optional<TypeId> metatable = getMetatable(type);
if (!metatable)
return std::nullopt;
TypeId unwrapped = follow(*metatable);
if (get<AnyTypeVar>(unwrapped))
return singletonTypes.anyType;
const TableTypeVar* mtt = getTableType(unwrapped);
if (!mtt)
{
errors.push_back(TypeError{location, GenericError{"Metatable was not a table."}});
return std::nullopt;
}
auto it = mtt->props.find(entry);
if (it != mtt->props.end())
return it->second.type;
else
return std::nullopt;
}
void Unifier::occursCheck(TypeId needle, TypeId haystack)
{
std::unordered_set<TypeId> seen_DEPRECATED;
@ -2168,7 +2144,7 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
{
for (const auto& ty : a->head)
{
if (auto f = get<FunctionTypeVar>(FFlag::LuauAddMissingFollow ? follow(ty) : ty))
if (auto f = get<FunctionTypeVar>(follow(ty)))
{
occursCheck(seen_DEPRECATED, seen, needle, f->argTypes);
occursCheck(seen_DEPRECATED, seen, needle, f->retType);
@ -2207,6 +2183,17 @@ void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId
errors.push_back(TypeError{location, TypeMismatch{wantedType, givenType}});
}
void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType)
{
LUAU_ASSERT(FFlag::LuauExtendedTypeMismatchError || FFlag::LuauExtendedClassMismatchError);
if (auto e = hasUnificationTooComplex(innerErrors))
errors.push_back(*e);
else if (!innerErrors.empty())
errors.push_back(
TypeError{location, TypeMismatch{wantedType, givenType, format("Property '%s' is not compatible", prop.c_str()), innerErrors.front()}});
}
void Unifier::ice(const std::string& message, const Location& location)
{
sharedState.iceHandler->ice(message, location);

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)
expectAndConsume('(', "function parameters");
else
{
LUAU_ASSERT(begin.type == '(');
nextLexeme(); // (
}
auto [generics, genericPacks] = parseGenericTypeList();
Lexeme parameterStart = lexer.current();
if (!FFlag::LuauParseGenericFunctionTypeBegin)
begin = parameterStart;
expectAndConsume('(', "function parameters");
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
@ -1366,7 +1365,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
if (lexer.current().type != ')')
varargAnnotation = parseTypeList(params, names);
expectMatchAndConsume(')', begin, true);
expectMatchAndConsume(')', parameterStart, true);
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
@ -1585,7 +1584,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
{
return {parseTableTypeAnnotation(), {}};
}
else if (lexer.current().type == '(' || (FFlag::LuauParseGenericFunctions && lexer.current().type == '<'))
else if (lexer.current().type == '(' || lexer.current().type == '<')
{
return parseFunctionTypeAnnotation(allowPack);
}
@ -2315,19 +2314,6 @@ Parser::Name Parser::parseIndexName(const char* context, const Position& previou
return Name(nameError, location);
}
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeListIfFFlagParseGenericFunctions()
{
if (FFlag::LuauParseGenericFunctions)
return Parser::parseGenericTypeList();
AstArray<AstName> generics;
AstArray<AstName> genericPacks;
generics.size = 0;
generics.data = nullptr;
genericPacks.size = 0;
genericPacks.data = nullptr;
return std::pair(generics, genericPacks);
}
std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
{
TempVector<AstName> names{scratchName};
@ -2342,7 +2328,7 @@ std::pair<AstArray<AstName>, AstArray<AstName>> Parser::parseGenericTypeList()
while (true)
{
AstName name = parseName().name;
if (FFlag::LuauParseGenericFunctions && lexer.current().type == Lexeme::Dot3)
if (lexer.current().type == Lexeme::Dot3)
{
seenPack = true;
nextLexeme();

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);
// static string for caching result (prevents dangling ptr on function exit)
static std::string result;
// run code + collect error
std::string error = runCode(L, source);
result = error;
if (error.length())
{
return result.c_str();
}
return NULL;
result = runCode(L, source);
return result.empty() ? NULL : result.c_str();
}
}
#endif
@ -591,3 +586,4 @@ int main(int argc, char** argv)
}
}
#endif

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,11 +3717,9 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
compiler.globals[name].writable = true;
if (options.mutableGlobals)
for (const char** ptr = options.mutableGlobals; *ptr != NULL; ++ptr)
{
for (const char** ptr = options.mutableGlobals; *ptr; ++ptr)
if (AstName name = names.get(*ptr); name.value)
compiler.globals[name].writable = true;
}
}
else
{
@ -3738,7 +3742,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstName
}
// this visitor tracks calls to getfenv/setfenv and disables some optimizations when they are found
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1)
if (FFlag::LuauPreloadClosuresFenv && options.optimizationLevel >= 1 && (names.get("getfenv").value || names.get("setfenv").value))
{
Compiler::FenvVisitor fenvVisitor(compiler.getfenvUsed, compiler.setfenvUsed);
root->visit(&fenvVisitor);

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);
size_t work = gcstep(L, lim);
if (assist)
g->gcstats.currcycle.assistwork += work;
else
g->gcstats.currcycle.explicitwork += work;
}
if (assist)
g->gcstats.currcycle.assistwork += work;
else
{
// always perform at least one single step
do
{
lim -= singlestep(L);
// if we have switched to a different state, capture the duration of last stage
// this way we reduce the number of timer calls we make
if (lastgcstate != g->gcstate)
{
GC_INTERRUPT(lastgcstate);
double now = lua_clock();
recordGcStateTime(g, lastgcstate, now - lasttimestamp, assist);
lasttimestamp = now;
lastgcstate = g->gcstate;
}
} while (lim > 0 && g->gcstate != GCSpause);
}
g->gcstats.currcycle.explicitwork += work;
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
@ -1037,14 +900,7 @@ void luaC_step(lua_State* L, bool assist)
g->GCthreshold -= debt;
}
if (FFlag::LuauConsolidatedStep)
{
GC_INTERRUPT(lastgcstate);
}
else
{
GC_INTERRUPT(g->gcstate);
}
GC_INTERRUPT(lastgcstate);
}
void luaC_fullgc(lua_State* L)
@ -1070,10 +926,7 @@ void luaC_fullgc(lua_State* L)
while (g->gcstate != GCSpause)
{
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
if (FFlag::LuauConsolidatedStep)
gcstep(L, SIZE_MAX);
else
singlestep(L);
gcstep(L, SIZE_MAX);
}
finishGcCycleStats(g);
@ -1084,10 +937,7 @@ void luaC_fullgc(lua_State* L)
markroot(L);
while (g->gcstate != GCSpause)
{
if (FFlag::LuauConsolidatedStep)
gcstep(L, SIZE_MAX);
else
singlestep(L);
gcstep(L, SIZE_MAX);
}
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */
shrinkbuffersfull(L);

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 */
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, n, h.islittle, size, 0);
if (FFlag::LuauStrPackUBCastFix)
{
long long n = (long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */
luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, (unsigned long long)n, h.islittle, size, 0);
}
else
{
unsigned long long n = (unsigned long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */
luaL_argcheck(L, n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, n, h.islittle, size, 0);
}
break;
}
case Kfloat:

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

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,12 +1959,7 @@ TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments")
matchParseError("type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)",
"Expected '->' when parsing function type, got <eof>");
{
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
ScopedFastFlag luauGenericFunctionsParserFix{"LuauGenericFunctionsParserFix", true};
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
}
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
}
TEST_SUITE_END();
@ -2362,8 +2363,6 @@ type Fn = (
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
}
ScopedFastFlag sffs3{"LuauParseGenericFunctions", true};
try
{
parse(R"(type Fn = (any, string | number | <a>()) -> any)");
@ -2397,8 +2396,6 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
{
ScopedFastFlag luauParseGenericFunctions{"LuauParseGenericFunctions", true};
try
{
parse(R"(
@ -2521,7 +2518,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
{
ScopedFastFlag luauParseGenericFunctions("LuauParseGenericFunctions", true);
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
AstStat* stat = parse(R"(
@ -2534,4 +2530,9 @@ type C<X...> = Packed<(number, X...)>
REQUIRE(stat != nullptr);
}
TEST_CASE_FIXTURE(Fixture, "function_type_matching_parenthesis")
{
matchParseError("local a: <T>(number -> string", "Expected ')' (to close '(' at column 13), got '->'");
}
TEST_SUITE_END();

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

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

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

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')