mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/561 (#820)
* Fix a potential debugger crash by adding checks for invalid stack index values --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
4a2e8013c7
commit
f763f4c948
@ -89,6 +89,7 @@ struct NameConstraint
|
||||
{
|
||||
TypeId namedType;
|
||||
std::string name;
|
||||
bool synthetic = false;
|
||||
};
|
||||
|
||||
// target ~ inst target
|
||||
|
@ -80,6 +80,8 @@ struct ConstraintGraphBuilder
|
||||
// A mapping of AST node to TypePackId.
|
||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
||||
|
||||
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
|
||||
|
||||
// If the node was applied as a function, this is the unspecialized type of
|
||||
// that expression.
|
||||
DenseHashMap<const void*, TypeId> astOriginalCallTypes{nullptr};
|
||||
@ -88,6 +90,8 @@ struct ConstraintGraphBuilder
|
||||
// overload that was selected.
|
||||
DenseHashMap<const void*, TypeId> astOverloadResolvedTypes{nullptr};
|
||||
|
||||
|
||||
|
||||
// Types resolved from type annotations. Analogous to astTypes.
|
||||
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
|
||||
|
||||
@ -207,6 +211,7 @@ struct ConstraintGraphBuilder
|
||||
Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
|
||||
Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType);
|
||||
Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert);
|
||||
Inference check(const ScopePtr& scope, AstExprInterpString* interpString);
|
||||
Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);
|
||||
std::tuple<TypeId, TypeId, ConnectiveId> checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
|
||||
|
||||
|
@ -253,7 +253,8 @@ struct NormalizedType
|
||||
TypeId threads;
|
||||
|
||||
// The (meta)table part of the type.
|
||||
// Each element of this set is a (meta)table type.
|
||||
// Each element of this set is a (meta)table type, or the top `table` type.
|
||||
// An empty set denotes never.
|
||||
TypeIds tables;
|
||||
|
||||
// The function part of the type.
|
||||
|
@ -117,6 +117,7 @@ struct PrimitiveType
|
||||
String,
|
||||
Thread,
|
||||
Function,
|
||||
Table,
|
||||
};
|
||||
|
||||
Type type;
|
||||
@ -651,6 +652,7 @@ public:
|
||||
const TypeId threadType;
|
||||
const TypeId functionType;
|
||||
const TypeId classType;
|
||||
const TypeId tableType;
|
||||
const TypeId trueType;
|
||||
const TypeId falseType;
|
||||
const TypeId anyType;
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "Luau/Type.h"
|
||||
|
||||
LUAU_FASTINT(LuauVisitRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauCompleteVisitor);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -322,8 +321,6 @@ struct GenericTypeVisitor
|
||||
if (visit(ty, *ntv))
|
||||
traverse(ntv->ty);
|
||||
}
|
||||
else if (!FFlag::LuauCompleteVisitor)
|
||||
return visit_detail::unsee(seen, ty);
|
||||
else
|
||||
LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypeId) is not exhaustive!");
|
||||
|
||||
|
@ -15,9 +15,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBuiltInMetatableNoBadSynthetic, false)
|
||||
LUAU_FASTFLAG(LuauReportShadowedTypeAlias)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
*
|
||||
@ -252,12 +250,9 @@ void registerBuiltinTypes(Frontend& frontend)
|
||||
frontend.getGlobalScope()->addBuiltinTypeBinding("string", TypeFun{{}, frontend.builtinTypes->stringType});
|
||||
frontend.getGlobalScope()->addBuiltinTypeBinding("boolean", TypeFun{{}, frontend.builtinTypes->booleanType});
|
||||
frontend.getGlobalScope()->addBuiltinTypeBinding("thread", TypeFun{{}, frontend.builtinTypes->threadType});
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
frontend.getGlobalScope()->addBuiltinTypeBinding("unknown", TypeFun{{}, frontend.builtinTypes->unknownType});
|
||||
frontend.getGlobalScope()->addBuiltinTypeBinding("never", TypeFun{{}, frontend.builtinTypes->neverType});
|
||||
}
|
||||
}
|
||||
|
||||
void registerBuiltinGlobals(TypeChecker& typeChecker)
|
||||
{
|
||||
@ -315,7 +310,7 @@ void registerBuiltinGlobals(TypeChecker& typeChecker)
|
||||
FunctionType{
|
||||
{genericMT},
|
||||
{},
|
||||
arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tabTy, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tableMetaMT}})
|
||||
}
|
||||
), "@luau"
|
||||
@ -357,7 +352,6 @@ void registerBuiltinGlobals(Frontend& frontend)
|
||||
LUAU_ASSERT(!frontend.globalTypes.types.isFrozen());
|
||||
LUAU_ASSERT(!frontend.globalTypes.typePacks.isFrozen());
|
||||
|
||||
if (FFlag::LuauReportShadowedTypeAlias)
|
||||
registerBuiltinTypes(frontend);
|
||||
|
||||
TypeArena& arena = frontend.globalTypes;
|
||||
@ -409,7 +403,7 @@ void registerBuiltinGlobals(Frontend& frontend)
|
||||
FunctionType{
|
||||
{genericMT},
|
||||
{},
|
||||
arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tabTy, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tableMetaMT}})
|
||||
}
|
||||
), "@luau"
|
||||
@ -537,11 +531,8 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
|
||||
{
|
||||
auto [paramPack, _predicates] = withPredicate;
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
if (size(paramPack) < 2 && finite(paramPack))
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TypeArena& arena = typechecker.currentModule->internalTypes;
|
||||
|
||||
@ -550,11 +541,8 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
|
||||
TypeId target = follow(expectedArgs[0]);
|
||||
TypeId mt = follow(expectedArgs[1]);
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
typechecker.tablify(target);
|
||||
typechecker.tablify(mt);
|
||||
}
|
||||
|
||||
if (const auto& tab = get<TableType>(target))
|
||||
{
|
||||
@ -564,9 +552,6 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FFlag::LuauUnknownAndNeverType)
|
||||
typechecker.tablify(mt);
|
||||
|
||||
const TableType* mtTtv = get<TableType>(mt);
|
||||
MetatableType mtv{target, mt};
|
||||
if ((tab->name || tab->syntheticName) && (mtTtv && (mtTtv->name || mtTtv->syntheticName)))
|
||||
@ -583,12 +568,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
|
||||
TypeId mtTy = arena.addType(mtv);
|
||||
|
||||
if (expr.args.size < 1)
|
||||
{
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
return std::nullopt;
|
||||
else
|
||||
return WithPredicate<TypePackId>{};
|
||||
}
|
||||
|
||||
if (!expr.self)
|
||||
{
|
||||
@ -635,21 +615,11 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionAssert(
|
||||
if (head.size() > 0)
|
||||
{
|
||||
auto [ty, ok] = typechecker.pickTypesFromSense(head[0], true, typechecker.builtinTypes->nilType);
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
if (get<NeverType>(*ty))
|
||||
head = {*ty};
|
||||
else
|
||||
head[0] = *ty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ty)
|
||||
head = {typechecker.nilType};
|
||||
else
|
||||
head[0] = *ty;
|
||||
}
|
||||
}
|
||||
|
||||
return WithPredicate<TypePackId>{arena.addTypePack(TypePack{std::move(head), tail})};
|
||||
}
|
||||
|
@ -414,6 +414,10 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
||||
std::vector<TypeId> varTypes;
|
||||
varTypes.reserve(local->vars.size);
|
||||
|
||||
// Used to name the first value type, even if it's not placed in varTypes,
|
||||
// for the purpose of synthetic name attribution.
|
||||
std::optional<TypeId> firstValueType;
|
||||
|
||||
for (AstLocal* local : local->vars)
|
||||
{
|
||||
TypeId ty = nullptr;
|
||||
@ -456,6 +460,9 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
||||
else
|
||||
varTypes[i] = exprType;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
firstValueType = exprType;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -488,6 +495,22 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
||||
}
|
||||
}
|
||||
|
||||
if (local->vars.size == 1 && local->values.size == 1 && firstValueType)
|
||||
{
|
||||
AstLocal* var = local->vars.data[0];
|
||||
AstExpr* value = local->values.data[0];
|
||||
|
||||
if (value->is<AstExprTable>())
|
||||
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
|
||||
else if (const AstExprCall* call = value->as<AstExprCall>())
|
||||
{
|
||||
if (const AstExprGlobal* global = call->func->as<AstExprGlobal>(); global && global->name == "setmetatable")
|
||||
{
|
||||
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < local->vars.size; ++i)
|
||||
{
|
||||
AstLocal* l = local->vars.data[i];
|
||||
@ -1138,7 +1161,13 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
|
||||
TypeId resultTy = arena->addType(mtv);
|
||||
|
||||
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
|
||||
{
|
||||
scope->bindings[targetLocal->local].typeId = resultTy;
|
||||
auto def = dfg->getDef(targetLocal->local);
|
||||
if (def)
|
||||
scope->dcrRefinements[*def] = resultTy; // TODO: typestates: track this as an assignment
|
||||
}
|
||||
|
||||
|
||||
return InferencePack{arena->addTypePack({resultTy}), std::move(returnConnectives)};
|
||||
}
|
||||
@ -1248,6 +1277,8 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr, st
|
||||
result = check(scope, ifElse, expectedType);
|
||||
else if (auto typeAssert = expr->as<AstExprTypeAssertion>())
|
||||
result = check(scope, typeAssert);
|
||||
else if (auto interpString = expr->as<AstExprInterpString>())
|
||||
result = check(scope, interpString);
|
||||
else if (auto err = expr->as<AstExprError>())
|
||||
{
|
||||
// Open question: Should we traverse into this?
|
||||
@ -1264,6 +1295,8 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr, st
|
||||
|
||||
LUAU_ASSERT(result.ty);
|
||||
astTypes[expr] = result.ty;
|
||||
if (expectedType)
|
||||
astExpectedTypes[expr] = *expectedType;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1509,6 +1542,14 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTypeAssert
|
||||
return Inference{resolveType(scope, typeAssert->annotation, /* inTypeArguments */ false)};
|
||||
}
|
||||
|
||||
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprInterpString* interpString)
|
||||
{
|
||||
for (AstExpr* expr : interpString->expressions)
|
||||
check(scope, expr);
|
||||
|
||||
return Inference{builtinTypes->stringType};
|
||||
}
|
||||
|
||||
std::tuple<TypeId, TypeId, ConnectiveId> ConstraintGraphBuilder::checkBinary(
|
||||
const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
|
||||
{
|
||||
@ -1551,7 +1592,7 @@ std::tuple<TypeId, TypeId, ConnectiveId> ConstraintGraphBuilder::checkBinary(
|
||||
else if (typeguard->type == "boolean")
|
||||
discriminantTy = builtinTypes->threadType;
|
||||
else if (typeguard->type == "table")
|
||||
discriminantTy = builtinTypes->neverType; // TODO: replace with top table type
|
||||
discriminantTy = builtinTypes->tableType;
|
||||
else if (typeguard->type == "function")
|
||||
discriminantTy = builtinTypes->functionType;
|
||||
else if (typeguard->type == "userdata")
|
||||
|
@ -883,7 +883,12 @@ bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constr
|
||||
return true;
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(target))
|
||||
{
|
||||
if (c.synthetic && !ttv->name)
|
||||
ttv->syntheticName = c.name;
|
||||
else
|
||||
ttv->name = c.name;
|
||||
}
|
||||
else if (MetatableType* mtv = getMutable<MetatableType>(target))
|
||||
mtv->syntheticName = c.name;
|
||||
else if (get<IntersectionType>(target) || get<UnionType>(target))
|
||||
@ -1594,7 +1599,7 @@ bool ConstraintSolver::tryDispatchIterableFunction(
|
||||
const TypeId firstIndex = isNil(firstIndexTy) ? arena->freshType(constraint->scope) // FIXME: Surely this should be a union (free | nil)
|
||||
: firstIndexTy;
|
||||
|
||||
// nextTy : (tableTy, indexTy?) -> (indexTy, valueTailTy...)
|
||||
// nextTy : (tableTy, indexTy?) -> (indexTy?, valueTailTy...)
|
||||
const TypePackId nextArgPack = arena->addTypePack({tableTy, arena->addType(UnionType{{firstIndex, builtinTypes->nilType}})});
|
||||
const TypePackId valueTailTy = arena->addTypePack(FreeTypePack{constraint->scope});
|
||||
const TypePackId nextRetPack = arena->addTypePack(TypePack{{firstIndex}, valueTailTy});
|
||||
@ -1602,7 +1607,25 @@ bool ConstraintSolver::tryDispatchIterableFunction(
|
||||
const TypeId expectedNextTy = arena->addType(FunctionType{TypeLevel{}, constraint->scope, nextArgPack, nextRetPack});
|
||||
unify(nextTy, expectedNextTy, constraint->scope);
|
||||
|
||||
pushConstraint(constraint->scope, constraint->location, PackSubtypeConstraint{c.variables, nextRetPack});
|
||||
auto it = begin(nextRetPack);
|
||||
std::vector<TypeId> modifiedNextRetHead;
|
||||
|
||||
// The first value is never nil in the context of the loop, even if it's nil
|
||||
// in the next function's return type, because the loop will not advance if
|
||||
// it's nil.
|
||||
if (it != end(nextRetPack))
|
||||
{
|
||||
TypeId firstRet = *it;
|
||||
TypeId modifiedFirstRet = stripNil(builtinTypes, *arena, firstRet);
|
||||
modifiedNextRetHead.push_back(modifiedFirstRet);
|
||||
++it;
|
||||
}
|
||||
|
||||
for (; it != end(nextRetPack); ++it)
|
||||
modifiedNextRetHead.push_back(*it);
|
||||
|
||||
TypePackId modifiedNextRetPack = arena->addTypePack(std::move(modifiedNextRetHead), it.tail());
|
||||
pushConstraint(constraint->scope, constraint->location, PackSubtypeConstraint{c.variables, modifiedNextRetPack});
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1649,8 +1672,8 @@ std::optional<TypeId> ConstraintSolver::lookupTableProp(TypeId subjectType, cons
|
||||
resultType = parts[0];
|
||||
else if (parts.size() > 1)
|
||||
resultType = arena->addType(UnionType{std::move(parts)});
|
||||
else
|
||||
LUAU_ASSERT(false); // parts.size() == 0
|
||||
|
||||
// otherwise, nothing: no matching property
|
||||
}
|
||||
else if (auto itv = get<IntersectionType>(subjectType))
|
||||
{
|
||||
@ -1662,8 +1685,8 @@ std::optional<TypeId> ConstraintSolver::lookupTableProp(TypeId subjectType, cons
|
||||
resultType = parts[0];
|
||||
else if (parts.size() > 1)
|
||||
resultType = arena->addType(IntersectionType{std::move(parts)});
|
||||
else
|
||||
LUAU_ASSERT(false); // parts.size() == 0
|
||||
|
||||
// otherwise, nothing: no matching property
|
||||
}
|
||||
|
||||
return resultType;
|
||||
|
@ -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/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -115,6 +113,7 @@ declare function typeof<T>(value: T): string
|
||||
|
||||
-- `assert` has a magic function attached that will give more detailed type information
|
||||
declare function assert<T>(value: T, errorMessage: string?): T
|
||||
declare function error<T>(message: T, level: number?): never
|
||||
|
||||
declare function tostring<T>(value: T): string
|
||||
declare function tonumber<T>(value: T, radix: number?): number?
|
||||
@ -199,14 +198,7 @@ declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
|
||||
std::string result = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
result += "declare function error<T>(message: T, level: number?): never\n";
|
||||
else
|
||||
result += "declare function error<T>(message: T, level: number?)\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -922,10 +922,10 @@ ModulePtr Frontend::check(
|
||||
|
||||
for (TypeError& e : cs.errors)
|
||||
result->errors.emplace_back(std::move(e));
|
||||
|
||||
result->scopes = std::move(cgb.scopes);
|
||||
result->astTypes = std::move(cgb.astTypes);
|
||||
result->astTypePacks = std::move(cgb.astTypePacks);
|
||||
result->astExpectedTypes = std::move(cgb.astExpectedTypes);
|
||||
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
||||
result->astOverloadResolvedTypes = std::move(cgb.astOverloadResolvedTypes);
|
||||
result->astResolvedTypes = std::move(cgb.astResolvedTypes);
|
||||
|
@ -19,7 +19,7 @@ LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
|
||||
LUAU_FASTFLAGVARIABLE(LuauNegatedClassTypes, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauNegatedFunctionTypes, false);
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNegatedTableTypes, false);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauUninhabitedSubAnything2)
|
||||
|
||||
@ -448,8 +448,20 @@ static bool areNormalizedFunctions(const NormalizedFunctionType& tys)
|
||||
static bool areNormalizedTables(const TypeIds& tys)
|
||||
{
|
||||
for (TypeId ty : tys)
|
||||
if (!get<TableType>(ty) && !get<MetatableType>(ty))
|
||||
{
|
||||
if (get<TableType>(ty) || get<MetatableType>(ty))
|
||||
continue;
|
||||
|
||||
const PrimitiveType* pt = get<PrimitiveType>(ty);
|
||||
if (!pt)
|
||||
return false;
|
||||
|
||||
if (pt->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1216,8 +1228,26 @@ void Normalizer::unionTablesWithTable(TypeIds& heres, TypeId there)
|
||||
void Normalizer::unionTables(TypeIds& heres, const TypeIds& theres)
|
||||
{
|
||||
for (TypeId there : theres)
|
||||
{
|
||||
if (FFlag::LuauNegatedTableTypes)
|
||||
{
|
||||
if (there == builtinTypes->tableType)
|
||||
{
|
||||
heres.clear();
|
||||
heres.insert(there);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
unionTablesWithTable(heres, there);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unionTablesWithTable(heres, there);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// So why `ignoreSmallerTyvars`?
|
||||
//
|
||||
@ -1375,6 +1405,11 @@ bool Normalizer::unionNormalWithTy(NormalizedType& here, TypeId there, int ignor
|
||||
LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes);
|
||||
here.functions.resetToTop();
|
||||
}
|
||||
else if (ptv->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes)
|
||||
{
|
||||
here.tables.clear();
|
||||
here.tables.insert(there);
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unreachable");
|
||||
}
|
||||
@ -1504,6 +1539,21 @@ std::optional<NormalizedType> Normalizer::negateNormal(const NormalizedType& her
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not possible to negate an arbitrary table type, because function
|
||||
* types are not runtime-testable. Thus, we prohibit negation of anything
|
||||
* other than `table` and `never`.
|
||||
*/
|
||||
if (FFlag::LuauNegatedTableTypes)
|
||||
{
|
||||
if (here.tables.empty())
|
||||
result.tables.insert(builtinTypes->tableType);
|
||||
else if (here.tables.size() == 1 && here.tables.front() == builtinTypes->tableType)
|
||||
result.tables.clear();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: negating tables
|
||||
// TODO: negating tyvars?
|
||||
|
||||
@ -1571,6 +1621,10 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty)
|
||||
case PrimitiveType::Function:
|
||||
here.functions.resetToNever();
|
||||
break;
|
||||
case PrimitiveType::Table:
|
||||
LUAU_ASSERT(FFlag::LuauNegatedTableTypes);
|
||||
here.tables.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1995,6 +2049,11 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||
if (sharedState->counters.recursionLimit > 0 && sharedState->counters.recursionLimit < sharedState->counters.recursionCount)
|
||||
return std::nullopt;
|
||||
|
||||
if (isPrim(here, PrimitiveType::Table))
|
||||
return there;
|
||||
else if (isPrim(there, PrimitiveType::Table))
|
||||
return here;
|
||||
|
||||
TypeId htable = here;
|
||||
TypeId hmtable = nullptr;
|
||||
if (const MetatableType* hmtv = get<MetatableType>(here))
|
||||
@ -2522,6 +2581,7 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there)
|
||||
NormalizedStringType strings = std::move(here.strings);
|
||||
NormalizedFunctionType functions = std::move(here.functions);
|
||||
TypeId threads = here.threads;
|
||||
TypeIds tables = std::move(here.tables);
|
||||
|
||||
clearNormal(here);
|
||||
|
||||
@ -2540,6 +2600,11 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there)
|
||||
LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes);
|
||||
here.functions = std::move(functions);
|
||||
}
|
||||
else if (ptv->type == PrimitiveType::Table)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNegatedTableTypes);
|
||||
here.tables = std::move(tables);
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unreachable");
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ LUAU_FASTFLAGVARIABLE(LuauSubstitutionFixMissingFields, false)
|
||||
LUAU_FASTFLAG(LuauClonePublicInterfaceLess)
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClassTypeVarsInSubstitution, false)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionReentrant, false)
|
||||
|
||||
namespace Luau
|
||||
@ -184,7 +183,7 @@ TarjanResult Tarjan::loop()
|
||||
if (currEdge == -1)
|
||||
{
|
||||
++childCount;
|
||||
if (childLimit > 0 && (FFlag::LuauUnknownAndNeverType ? childLimit <= childCount : childLimit < childCount))
|
||||
if (childLimit > 0 && childLimit <= childCount)
|
||||
return TarjanResult::TooManyChildren;
|
||||
|
||||
stack.push_back(index);
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFunctionReturnStringificationFixup, false)
|
||||
|
||||
/*
|
||||
@ -444,6 +443,9 @@ struct TypeStringifier
|
||||
case PrimitiveType::Function:
|
||||
state.emit("function");
|
||||
return;
|
||||
case PrimitiveType::Table:
|
||||
state.emit("table");
|
||||
return;
|
||||
default:
|
||||
LUAU_ASSERT(!"Unknown primitive type");
|
||||
throw InternalCompilerError("Unknown primitive type " + std::to_string(ptv.type));
|
||||
@ -823,7 +825,7 @@ struct TypeStringifier
|
||||
void operator()(TypeId, const ErrorType& tv)
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||
state.emit("*error-type*");
|
||||
}
|
||||
|
||||
void operator()(TypeId, const LazyType& ltv)
|
||||
@ -962,7 +964,7 @@ struct TypePackStringifier
|
||||
void operator()(TypePackId, const Unifiable::Error& error)
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||
state.emit("*error-type*");
|
||||
}
|
||||
|
||||
void operator()(TypePackId, const VariadicTypePack& pack)
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -24,7 +24,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMatchReturnsOptionalString, false);
|
||||
@ -213,7 +212,7 @@ bool isOptional(TypeId ty)
|
||||
|
||||
ty = follow(ty);
|
||||
|
||||
if (get<AnyType>(ty) || (FFlag::LuauUnknownAndNeverType && get<UnknownType>(ty)))
|
||||
if (get<AnyType>(ty) || get<UnknownType>(ty))
|
||||
return true;
|
||||
|
||||
auto utv = get<UnionType>(ty);
|
||||
@ -761,6 +760,7 @@ BuiltinTypes::BuiltinTypes()
|
||||
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
||||
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
||||
, classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}}, /*persistent*/ true}))
|
||||
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
||||
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
||||
, falseType(arena->addType(Type{SingletonType{BooleanSingleton{false}}, /*persistent*/ true}))
|
||||
, anyType(arena->addType(Type{AnyType{}, /*persistent*/ true}))
|
||||
|
@ -372,7 +372,7 @@ struct TypeChecker2
|
||||
break;
|
||||
}
|
||||
|
||||
AstLocal* var = local->vars.data[i];
|
||||
AstLocal* var = local->vars.data[j];
|
||||
if (var->annotation)
|
||||
{
|
||||
TypeId varType = lookupAnnotation(var->annotation);
|
||||
@ -755,6 +755,8 @@ struct TypeChecker2
|
||||
return visit(e);
|
||||
else if (auto e = expr->as<AstExprIfElse>())
|
||||
return visit(e);
|
||||
else if (auto e = expr->as<AstExprInterpString>())
|
||||
return visit(e);
|
||||
else if (auto e = expr->as<AstExprError>())
|
||||
return visit(e);
|
||||
else
|
||||
@ -1358,6 +1360,12 @@ struct TypeChecker2
|
||||
visit(expr->falseExpr, RValue);
|
||||
}
|
||||
|
||||
void visit(AstExprInterpString* interpString)
|
||||
{
|
||||
for (AstExpr* expr : interpString->expressions)
|
||||
visit(expr, RValue);
|
||||
}
|
||||
|
||||
void visit(AstExprError* expr)
|
||||
{
|
||||
// TODO!
|
||||
|
@ -35,18 +35,10 @@ LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauScopelessModule, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTryhardAnd, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportShadowedTypeAlias, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterMessagingOnCountMismatch, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIntersectionTestForEquality, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauImplicitElseRefinement, false)
|
||||
LUAU_FASTFLAG(LuauNegatedClassTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowIndexClassParameters, false)
|
||||
LUAU_FASTFLAG(LuauUninhabitedSubAnything2)
|
||||
@ -246,12 +238,9 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, NotNull<BuiltinTypes> builtin
|
||||
globalScope->addBuiltinTypeBinding("string", TypeFun{{}, stringType});
|
||||
globalScope->addBuiltinTypeBinding("boolean", TypeFun{{}, booleanType});
|
||||
globalScope->addBuiltinTypeBinding("thread", TypeFun{{}, threadType});
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
globalScope->addBuiltinTypeBinding("unknown", TypeFun{{}, unknownType});
|
||||
globalScope->addBuiltinTypeBinding("never", TypeFun{{}, neverType});
|
||||
}
|
||||
}
|
||||
|
||||
ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope)
|
||||
{
|
||||
@ -661,7 +650,7 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std
|
||||
|
||||
Name name = typealias->name.value;
|
||||
|
||||
if (FFlag::LuauReportShadowedTypeAlias && duplicateTypeAliases.contains({typealias->exported, name}))
|
||||
if (duplicateTypeAliases.contains({typealias->exported, name}))
|
||||
continue;
|
||||
|
||||
TypeId type = bindings[name].type;
|
||||
@ -1066,9 +1055,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
||||
|
||||
// If the expression list only contains one expression and it's a function call or is otherwise within parentheses, use FunctionResult.
|
||||
// Otherwise, we'll want to use ExprListResult to make the error messaging more general.
|
||||
CountMismatch::Context ctx = FFlag::LuauBetterMessagingOnCountMismatch ? CountMismatch::ExprListResult : CountMismatch::FunctionResult;
|
||||
if (FFlag::LuauBetterMessagingOnCountMismatch)
|
||||
{
|
||||
CountMismatch::Context ctx = CountMismatch::ExprListResult;
|
||||
if (local.values.size == 1)
|
||||
{
|
||||
AstExpr* e = local.values.data[0];
|
||||
@ -1077,7 +1064,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
||||
if (e->is<AstExprCall>())
|
||||
ctx = CountMismatch::FunctionResult;
|
||||
}
|
||||
}
|
||||
|
||||
Unifier state = mkUnifier(scope, local.location);
|
||||
state.ctx = ctx;
|
||||
@ -1438,11 +1424,8 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
||||
|
||||
checkFunctionBody(funScope, ty, *function.func);
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
InplaceDemoter demoter{funScope->level, ¤tModule->internalTypes};
|
||||
demoter.traverse(ty);
|
||||
}
|
||||
|
||||
if (ttv && ttv->state != TableState::Sealed)
|
||||
ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation};
|
||||
@ -1591,12 +1574,9 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||
Location location = scope->typeAliasLocations[name];
|
||||
reportError(TypeError{typealias.location, DuplicateTypeDefinition{name, location}});
|
||||
|
||||
if (!FFlag::LuauReportShadowedTypeAlias)
|
||||
bindingsMap[name] = TypeFun{binding->typeParams, binding->typePackParams, errorRecoveryType(anyType)};
|
||||
|
||||
duplicateTypeAliases.insert({typealias.exported, name});
|
||||
}
|
||||
else if (FFlag::LuauReportShadowedTypeAlias)
|
||||
else
|
||||
{
|
||||
if (globalScope->builtinTypeNames.contains(name))
|
||||
{
|
||||
@ -1623,25 +1603,6 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||
scope->typeAliasNameLocations[name] = typealias.nameLocation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScopePtr aliasScope = childScope(scope, typealias.location);
|
||||
aliasScope->level = scope->level.incr();
|
||||
aliasScope->level.subLevel = subLevel;
|
||||
|
||||
auto [generics, genericPacks] =
|
||||
createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks, /* useCache = */ true);
|
||||
|
||||
TypeId ty = freshType(aliasScope);
|
||||
FreeType* ftv = getMutable<FreeType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
|
||||
scope->typeAliasLocations[name] = typealias.location;
|
||||
if (FFlag::SupportTypeAliasGoToDeclaration)
|
||||
scope->typeAliasNameLocations[name] = typealias.nameLocation;
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
||||
@ -1840,7 +1801,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||
else if (auto a = expr.as<AstExprUnary>())
|
||||
result = checkExpr(scope, *a);
|
||||
else if (auto a = expr.as<AstExprBinary>())
|
||||
result = checkExpr(scope, *a, FFlag::LuauBinaryNeedsExpectedTypesToo ? expectedType : std::nullopt);
|
||||
result = checkExpr(scope, *a, expectedType);
|
||||
else if (auto a = expr.as<AstExprTypeAssertion>())
|
||||
result = checkExpr(scope, *a);
|
||||
else if (auto a = expr.as<AstExprError>())
|
||||
@ -2084,7 +2045,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
||||
}
|
||||
|
||||
std::vector<TypeId> result = reduceUnion(goodOptions);
|
||||
if (FFlag::LuauUnknownAndNeverType && result.empty())
|
||||
if (result.empty())
|
||||
return neverType;
|
||||
|
||||
if (result.size() == 1)
|
||||
@ -2432,13 +2393,8 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||
operandType = stripFromNilAndReport(operandType, expr.location);
|
||||
|
||||
// # operator is guaranteed to return number
|
||||
if ((FFlag::LuauNeverTypesAndOperatorsInference && get<AnyType>(operandType)) || get<ErrorType>(operandType) || get<NeverType>(operandType))
|
||||
{
|
||||
if (FFlag::LuauNeverTypesAndOperatorsInference)
|
||||
if (get<AnyType>(operandType) || get<ErrorType>(operandType) || get<NeverType>(operandType))
|
||||
return {numberType};
|
||||
else
|
||||
return {!FFlag::LuauUnknownAndNeverType ? errorRecoveryType(scope) : operandType};
|
||||
}
|
||||
|
||||
DenseHashSet<TypeId> seen{nullptr};
|
||||
|
||||
@ -2518,7 +2474,7 @@ TypeId TypeChecker::unionOfTypes(TypeId a, TypeId b, const ScopePtr& scope, cons
|
||||
return a;
|
||||
|
||||
std::vector<TypeId> types = reduceUnion({a, b});
|
||||
if (FFlag::LuauUnknownAndNeverType && types.empty())
|
||||
if (types.empty())
|
||||
return neverType;
|
||||
|
||||
if (types.size() == 1)
|
||||
@ -2648,13 +2604,10 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||
case AstExprBinary::CompareGt:
|
||||
case AstExprBinary::CompareGe:
|
||||
case AstExprBinary::CompareLe:
|
||||
{
|
||||
if (FFlag::LuauNeverTypesAndOperatorsInference)
|
||||
{
|
||||
// If one of the operand is never, it doesn't make sense to unify these.
|
||||
if (get<NeverType>(lhsType) || get<NeverType>(rhsType))
|
||||
return booleanType;
|
||||
}
|
||||
|
||||
if (FFlag::LuauIntersectionTestForEquality && isEquality)
|
||||
{
|
||||
@ -2897,10 +2850,8 @@ TypeId TypeChecker::checkBinaryOperation(
|
||||
|
||||
// If we know nothing at all about the lhs type, we can usually say nothing about the result.
|
||||
// The notable exception to this is the equality and inequality operators, which always produce a boolean.
|
||||
const bool lhsIsAny = get<AnyType>(lhsType) || get<ErrorType>(lhsType) ||
|
||||
(FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get<NeverType>(lhsType));
|
||||
const bool rhsIsAny = get<AnyType>(rhsType) || get<ErrorType>(rhsType) ||
|
||||
(FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get<NeverType>(rhsType));
|
||||
const bool lhsIsAny = get<AnyType>(lhsType) || get<ErrorType>(lhsType) || get<NeverType>(lhsType);
|
||||
const bool rhsIsAny = get<AnyType>(rhsType) || get<ErrorType>(rhsType) || get<NeverType>(rhsType);
|
||||
|
||||
if (lhsIsAny)
|
||||
return lhsType;
|
||||
@ -3102,7 +3053,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||
return {trueType.type};
|
||||
|
||||
std::vector<TypeId> types = reduceUnion({trueType.type, falseType.type});
|
||||
if (FFlag::LuauUnknownAndNeverType && types.empty())
|
||||
if (types.empty())
|
||||
return {neverType};
|
||||
return {types.size() == 1 ? types[0] : addType(UnionType{std::move(types)})};
|
||||
}
|
||||
@ -3708,17 +3659,12 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
||||
}
|
||||
|
||||
WithPredicate<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const AstExpr& expr)
|
||||
{
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
WithPredicate<TypePackId> result = checkExprPackHelper(scope, expr);
|
||||
if (containsNever(result.type))
|
||||
return {uninhabitableTypePack};
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return checkExprPackHelper(scope, expr);
|
||||
}
|
||||
|
||||
WithPredicate<TypePackId> TypeChecker::checkExprPackHelper(const ScopePtr& scope, const AstExpr& expr)
|
||||
{
|
||||
@ -3843,10 +3789,7 @@ void TypeChecker::checkArgumentList(const ScopePtr& scope, const AstExpr& funNam
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||
if (FFlag::LuauReturnsFromCallsitesAreNotWidened)
|
||||
state.tryUnify(tail, varPack);
|
||||
else
|
||||
state.tryUnify(varPack, tail);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -4031,8 +3974,6 @@ WithPredicate<TypePackId> TypeChecker::checkExprPackHelper(const ScopePtr& scope
|
||||
return {errorRecoveryTypePack(scope)};
|
||||
|
||||
TypePack* args = nullptr;
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
if (expr.self)
|
||||
{
|
||||
argPack = addTypePack(TypePack{{selfType}, argPack});
|
||||
@ -4040,15 +3981,6 @@ WithPredicate<TypePackId> TypeChecker::checkExprPackHelper(const ScopePtr& scope
|
||||
}
|
||||
args = getMutable<TypePack>(argPack);
|
||||
LUAU_ASSERT(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
args = getMutable<TypePack>(argPack);
|
||||
LUAU_ASSERT(args != nullptr);
|
||||
|
||||
if (expr.self)
|
||||
args->head.insert(args->head.begin(), selfType);
|
||||
}
|
||||
|
||||
std::vector<Location> argLocations;
|
||||
argLocations.reserve(expr.args.size + 1);
|
||||
@ -4107,7 +4039,7 @@ std::vector<std::optional<TypeId>> TypeChecker::getExpectedTypesForCall(const st
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> result = reduceUnion({*el, ty});
|
||||
if (FFlag::LuauUnknownAndNeverType && result.empty())
|
||||
if (result.empty())
|
||||
el = neverType;
|
||||
else
|
||||
el = result.size() == 1 ? result[0] : addType(UnionType{std::move(result)});
|
||||
@ -4451,7 +4383,7 @@ WithPredicate<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, cons
|
||||
auto [typePack, exprPredicates] = checkExprPack(scope, *expr);
|
||||
insert(exprPredicates);
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType && containsNever(typePack))
|
||||
if (containsNever(typePack))
|
||||
{
|
||||
// f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never,
|
||||
// ...never)
|
||||
@ -4474,7 +4406,7 @@ WithPredicate<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, cons
|
||||
auto [type, exprPredicates] = checkExpr(scope, *expr, expectedType);
|
||||
insert(exprPredicates);
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType && get<NeverType>(type))
|
||||
if (get<NeverType>(type))
|
||||
{
|
||||
// f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never,
|
||||
// ...never)
|
||||
@ -4509,7 +4441,7 @@ WithPredicate<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, cons
|
||||
for (TxnLog& log : inverseLogs)
|
||||
log.commit();
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType && uninhabitable)
|
||||
if (uninhabitable)
|
||||
return {uninhabitableTypePack};
|
||||
return {pack, predicates};
|
||||
}
|
||||
@ -4996,18 +4928,10 @@ std::optional<TypeId> TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate pr
|
||||
}
|
||||
|
||||
std::pair<std::optional<TypeId>, bool> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate)
|
||||
{
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
TypeId ty = filterMapImpl(type, predicate).value_or(neverType);
|
||||
return {ty, !bool(get<NeverType>(ty))};
|
||||
}
|
||||
else
|
||||
{
|
||||
std::optional<TypeId> ty = filterMapImpl(type, predicate);
|
||||
return {ty, bool(ty)};
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::optional<TypeId>, bool> TypeChecker::pickTypesFromSense(TypeId type, bool sense, TypeId emptySetTy)
|
||||
{
|
||||
@ -5587,18 +5511,7 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const
|
||||
if (!key)
|
||||
{
|
||||
auto [result, ok] = filterMap(*ty, predicate);
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
addRefinement(refis, *target, *result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ok)
|
||||
addRefinement(refis, *target, *result);
|
||||
else
|
||||
addRefinement(refis, *target, errorRecoveryType(scope));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5621,23 +5534,12 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const
|
||||
return; // Do nothing. An error was already reported, as per usual.
|
||||
|
||||
auto [result, ok] = filterMap(*discriminantTy, predicate);
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
if (!get<NeverType>(*result))
|
||||
{
|
||||
viableTargetOptions.insert(option);
|
||||
viableChildOptions.insert(*result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
viableTargetOptions.insert(option);
|
||||
viableChildOptions.insert(*result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto intoType = [this](const std::unordered_set<TypeId>& s) -> std::optional<TypeId> {
|
||||
if (s.empty())
|
||||
@ -5891,7 +5793,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
||||
|
||||
auto refine = [this, &lvalue = typeguardP.lvalue, &refis, &scope, sense](bool(f)(TypeId), std::optional<TypeId> mapsTo = std::nullopt) {
|
||||
TypeIdPredicate predicate = [f, mapsTo, sense](TypeId ty) -> std::optional<TypeId> {
|
||||
if (FFlag::LuauUnknownAndNeverType && sense && get<UnknownType>(ty))
|
||||
if (sense && get<UnknownType>(ty))
|
||||
return mapsTo.value_or(ty);
|
||||
|
||||
if (f(ty) == sense)
|
||||
@ -5984,8 +5886,6 @@ void TypeChecker::resolve(const EqPredicate& eqP, RefinementMap& refis, const Sc
|
||||
return (isUndecidable(option) || !isNil(option)) ? std::optional<TypeId>(option) : std::nullopt;
|
||||
|
||||
if (maybeSingleton(eqP.type))
|
||||
{
|
||||
if (FFlag::LuauImplicitElseRefinement)
|
||||
{
|
||||
bool optionIsSubtype = canUnify(option, eqP.type, scope, eqP.location).empty();
|
||||
bool targetIsSubtype = canUnify(eqP.type, option, scope, eqP.location).empty();
|
||||
@ -6027,16 +5927,6 @@ void TypeChecker::resolve(const EqPredicate& eqP, RefinementMap& refis, const Sc
|
||||
return nope;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sense || canUnify(eqP.type, option, scope, eqP.location).empty())
|
||||
return sense ? eqP.type : option;
|
||||
|
||||
// local variable works around an odd gcc 9.3 warning: <anonymous> may be used uninitialized
|
||||
std::optional<TypeId> res = std::nullopt;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return option;
|
||||
};
|
||||
@ -6063,7 +5953,6 @@ std::vector<TypeId> TypeChecker::unTypePack(const ScopePtr& scope, TypePackId tp
|
||||
|
||||
// HACK: tryUnify would undo the changes to the expectedTypePack if the length mismatches, but
|
||||
// we want to tie up free types to be error types, so we do this instead.
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
currentModule->errors.resize(oldErrorsSize);
|
||||
|
||||
for (TypeId& tp : expectedPack->head)
|
||||
|
@ -56,12 +56,6 @@ struct TypeReducer
|
||||
TypeId memoize(TypeId ty, TypeId reducedTy);
|
||||
TypePackId memoize(TypePackId tp, TypePackId reducedTp);
|
||||
|
||||
// It's either cyclic with no memoized result, so we should terminate, or
|
||||
// there is a memoized result but one that's being reduced top-down, so
|
||||
// we need to return the root of that memoized result to tighten up things.
|
||||
TypeId memoizedOr(TypeId ty) const;
|
||||
TypePackId memoizedOr(TypePackId tp) const;
|
||||
|
||||
using BinaryFold = std::optional<TypeId> (TypeReducer::*)(TypeId, TypeId);
|
||||
using UnaryFold = TypeId (TypeReducer::*)(TypeId);
|
||||
|
||||
@ -319,6 +313,24 @@ std::optional<TypeId> TypeReducer::intersectionType(TypeId left, TypeId right)
|
||||
}
|
||||
else if (auto [f, p] = get2<FunctionType, PrimitiveType>(left, right); f && p)
|
||||
return intersectionType(right, left); // () -> () & P ~ P & () -> ()
|
||||
else if (auto [p, t] = get2<PrimitiveType, TableType>(left, right); p && t)
|
||||
{
|
||||
if (p->type == PrimitiveType::Table)
|
||||
return right; // table & {} ~ {}
|
||||
else
|
||||
return builtinTypes->neverType; // string & {} ~ never
|
||||
}
|
||||
else if (auto [p, t] = get2<PrimitiveType, MetatableType>(left, right); p && t)
|
||||
{
|
||||
if (p->type == PrimitiveType::Table)
|
||||
return right; // table & {} ~ {}
|
||||
else
|
||||
return builtinTypes->neverType; // string & {} ~ never
|
||||
}
|
||||
else if (auto [t, p] = get2<TableType, PrimitiveType>(left, right); t && p)
|
||||
return intersectionType(right, left); // {} & P ~ P & {}
|
||||
else if (auto [t, p] = get2<MetatableType, PrimitiveType>(left, right); t && p)
|
||||
return intersectionType(right, left); // M & P ~ P & M
|
||||
else if (auto [s1, s2] = get2<SingletonType, SingletonType>(left, right); s1 && s2)
|
||||
{
|
||||
if (*s1 == *s2)
|
||||
@ -472,6 +484,20 @@ std::optional<TypeId> TypeReducer::intersectionType(TypeId left, TypeId right)
|
||||
else
|
||||
return right; // ~Base & Unrelated ~ Unrelated
|
||||
}
|
||||
else if (auto [np, t] = get2<PrimitiveType, TableType>(nlTy, right); np && t)
|
||||
{
|
||||
if (np->type == PrimitiveType::Table)
|
||||
return builtinTypes->neverType; // ~table & {} ~ never
|
||||
else
|
||||
return right; // ~string & {} ~ {}
|
||||
}
|
||||
else if (auto [np, t] = get2<PrimitiveType, MetatableType>(nlTy, right); np && t)
|
||||
{
|
||||
if (np->type == PrimitiveType::Table)
|
||||
return builtinTypes->neverType; // ~table & {} ~ never
|
||||
else
|
||||
return right; // ~string & {} ~ {}
|
||||
}
|
||||
else
|
||||
return std::nullopt; // TODO
|
||||
}
|
||||
@ -529,6 +555,24 @@ std::optional<TypeId> TypeReducer::unionType(TypeId left, TypeId right)
|
||||
}
|
||||
else if (auto [f, p] = get2<FunctionType, PrimitiveType>(left, right); f && p)
|
||||
return unionType(right, left); // () -> () | P ~ P | () -> ()
|
||||
else if (auto [p, t] = get2<PrimitiveType, TableType>(left, right); p && t)
|
||||
{
|
||||
if (p->type == PrimitiveType::Table)
|
||||
return left; // table | {} ~ table
|
||||
else
|
||||
return std::nullopt; // P | {} ~ P | {}
|
||||
}
|
||||
else if (auto [p, t] = get2<PrimitiveType, MetatableType>(left, right); p && t)
|
||||
{
|
||||
if (p->type == PrimitiveType::Table)
|
||||
return left; // table | {} ~ table
|
||||
else
|
||||
return std::nullopt; // P | {} ~ P | {}
|
||||
}
|
||||
else if (auto [t, p] = get2<TableType, PrimitiveType>(left, right); t && p)
|
||||
return unionType(right, left); // {} | P ~ P | {}
|
||||
else if (auto [t, p] = get2<MetatableType, PrimitiveType>(left, right); t && p)
|
||||
return unionType(right, left); // M | P ~ P | M
|
||||
else if (auto [s1, s2] = get2<SingletonType, SingletonType>(left, right); s1 && s2)
|
||||
{
|
||||
if (*s1 == *s2)
|
||||
@ -642,6 +686,20 @@ std::optional<TypeId> TypeReducer::unionType(TypeId left, TypeId right)
|
||||
else
|
||||
return left; // ~Base | Unrelated ~ ~Base
|
||||
}
|
||||
else if (auto [np, t] = get2<PrimitiveType, TableType>(nlTy, right); np && t)
|
||||
{
|
||||
if (np->type == PrimitiveType::Table)
|
||||
return std::nullopt; // ~table | {} ~ ~table | {}
|
||||
else
|
||||
return right; // ~P | {} ~ ~P | {}
|
||||
}
|
||||
else if (auto [np, t] = get2<PrimitiveType, MetatableType>(nlTy, right); np && t)
|
||||
{
|
||||
if (np->type == PrimitiveType::Table)
|
||||
return std::nullopt; // ~table | {} ~ ~table | {}
|
||||
else
|
||||
return right; // ~P | M ~ ~P | M
|
||||
}
|
||||
else
|
||||
return std::nullopt; // TODO
|
||||
}
|
||||
@ -850,26 +908,6 @@ TypePackId TypeReducer::memoize(TypePackId tp, TypePackId reducedTp)
|
||||
return reducedTp;
|
||||
}
|
||||
|
||||
TypeId TypeReducer::memoizedOr(TypeId ty) const
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
if (auto ctx = memoizedTypes->find(ty))
|
||||
return ctx->type;
|
||||
else
|
||||
return ty;
|
||||
};
|
||||
|
||||
TypePackId TypeReducer::memoizedOr(TypePackId tp) const
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (auto ctx = memoizedTypePacks->find(tp))
|
||||
return ctx->type;
|
||||
else
|
||||
return tp;
|
||||
};
|
||||
|
||||
struct MarkCycles : TypeVisitor
|
||||
{
|
||||
DenseHashSet<TypeId> cyclicTypes{nullptr};
|
||||
|
@ -15,10 +15,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false)
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnifyAnyTxnLog, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauScalarShapeUnifyToMtOwner2, false)
|
||||
@ -28,6 +26,7 @@ LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauNegatedFunctionTypes)
|
||||
LUAU_FASTFLAG(LuauNegatedClassTypes)
|
||||
LUAU_FASTFLAG(LuauNegatedTableTypes)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -451,14 +450,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||
return;
|
||||
}
|
||||
else if (subFree)
|
||||
{
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
{
|
||||
// Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
|
||||
// But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors.
|
||||
if (log.get<UnknownType>(superTy))
|
||||
return;
|
||||
}
|
||||
|
||||
// Unification can't change the level of a generic.
|
||||
auto superGeneric = log.getMutable<GenericType>(superTy);
|
||||
@ -569,6 +565,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||
// Ok. Do nothing. forall functions F, F <: function
|
||||
}
|
||||
|
||||
else if (FFlag::LuauNegatedTableTypes && isPrim(superTy, PrimitiveType::Table) && (get<TableType>(subTy) || get<MetatableType>(subTy)))
|
||||
{
|
||||
// Ok, do nothing: forall tables T, T <: table
|
||||
}
|
||||
|
||||
else if (log.getMutable<FunctionType>(superTy) && log.getMutable<FunctionType>(subTy))
|
||||
tryUnifyFunctions(subTy, superTy, isFunctionCall);
|
||||
|
||||
@ -576,11 +577,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||
{
|
||||
tryUnifyTables(subTy, superTy, isIntersection);
|
||||
}
|
||||
else if (FFlag::LuauScalarShapeSubtyping && log.get<TableType>(superTy) && (log.get<PrimitiveType>(subTy) || log.get<SingletonType>(subTy)))
|
||||
else if (log.get<TableType>(superTy) && (log.get<PrimitiveType>(subTy) || log.get<SingletonType>(subTy)))
|
||||
{
|
||||
tryUnifyScalarShape(subTy, superTy, /*reversed*/ false);
|
||||
}
|
||||
else if (FFlag::LuauScalarShapeSubtyping && log.get<TableType>(subTy) && (log.get<PrimitiveType>(superTy) || log.get<SingletonType>(superTy)))
|
||||
else if (log.get<TableType>(subTy) && (log.get<PrimitiveType>(superTy) || log.get<SingletonType>(superTy)))
|
||||
{
|
||||
tryUnifyScalarShape(subTy, superTy, /*reversed*/ true);
|
||||
}
|
||||
@ -1032,6 +1033,12 @@ void Unifier::tryUnifyNormalizedTypes(
|
||||
bool found = false;
|
||||
for (TypeId superTable : superNorm.tables)
|
||||
{
|
||||
if (FFlag::LuauNegatedTableTypes && isPrim(superTable, PrimitiveType::Table))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Unifier innerState = makeChildUnifier();
|
||||
if (get<MetatableType>(superTable))
|
||||
innerState.tryUnifyWithMetatable(subTable, superTable, /* reversed */ false);
|
||||
@ -2031,8 +2038,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||
|
||||
void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauScalarShapeSubtyping);
|
||||
|
||||
TypeId osubTy = subTy;
|
||||
TypeId osuperTy = superTy;
|
||||
|
||||
@ -2490,22 +2495,14 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
||||
return;
|
||||
}
|
||||
|
||||
TypePackId anyTp;
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
||||
else
|
||||
{
|
||||
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{builtinTypes->anyType}});
|
||||
anyTp = get<AnyType>(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}});
|
||||
}
|
||||
TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
||||
|
||||
std::vector<TypeId> queue = {subTy};
|
||||
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
Luau::tryUnifyWithAny(
|
||||
queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, FFlag::LuauUnknownAndNeverType ? anyTy : builtinTypes->anyType, anyTp);
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
|
||||
|
@ -52,246 +52,12 @@ struct AstName
|
||||
}
|
||||
};
|
||||
|
||||
class AstVisitor
|
||||
{
|
||||
public:
|
||||
virtual ~AstVisitor() {}
|
||||
|
||||
virtual bool visit(class AstNode*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool visit(class AstExpr* node)
|
||||
{
|
||||
return visit((class AstNode*)node);
|
||||
}
|
||||
|
||||
virtual bool visit(class AstExprGroup* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprConstantNil* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprConstantBool* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprConstantNumber* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprConstantString* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprLocal* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprGlobal* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprVarargs* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprCall* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprIndexName* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprIndexExpr* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprFunction* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprTable* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprUnary* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprBinary* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprTypeAssertion* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprIfElse* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprInterpString* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
virtual bool visit(class AstExprError* node)
|
||||
{
|
||||
return visit((class AstExpr*)node);
|
||||
}
|
||||
|
||||
virtual bool visit(class AstStat* node)
|
||||
{
|
||||
return visit((class AstNode*)node);
|
||||
}
|
||||
|
||||
virtual bool visit(class AstStatBlock* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatIf* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatWhile* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatRepeat* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatBreak* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatContinue* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatReturn* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatExpr* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatLocal* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatFor* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatForIn* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatAssign* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatCompoundAssign* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatFunction* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatLocalFunction* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatTypeAlias* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareFunction* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareGlobal* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareClass* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
virtual bool visit(class AstStatError* node)
|
||||
{
|
||||
return visit((class AstStat*)node);
|
||||
}
|
||||
|
||||
// By default visiting type annotations is disabled; override this in your visitor if you need to!
|
||||
virtual bool visit(class AstType* node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(class AstTypeReference* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeTable* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeFunction* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeTypeof* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeUnion* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeIntersection* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeSingletonBool* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeSingletonString* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypeError* node)
|
||||
{
|
||||
return visit((class AstType*)node);
|
||||
}
|
||||
|
||||
virtual bool visit(class AstTypePack* node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool visit(class AstTypePackExplicit* node)
|
||||
{
|
||||
return visit((class AstTypePack*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypePackVariadic* node)
|
||||
{
|
||||
return visit((class AstTypePack*)node);
|
||||
}
|
||||
virtual bool visit(class AstTypePackGeneric* node)
|
||||
{
|
||||
return visit((class AstTypePack*)node);
|
||||
}
|
||||
};
|
||||
|
||||
class AstType;
|
||||
class AstVisitor;
|
||||
class AstStat;
|
||||
class AstStatBlock;
|
||||
class AstExpr;
|
||||
class AstTypePack;
|
||||
|
||||
struct AstLocal
|
||||
{
|
||||
@ -1277,6 +1043,245 @@ public:
|
||||
AstName genericName;
|
||||
};
|
||||
|
||||
class AstVisitor
|
||||
{
|
||||
public:
|
||||
virtual ~AstVisitor() {}
|
||||
|
||||
virtual bool visit(class AstNode*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool visit(class AstExpr* node)
|
||||
{
|
||||
return visit(static_cast<AstNode*>(node));
|
||||
}
|
||||
|
||||
virtual bool visit(class AstExprGroup* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprConstantNil* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprConstantBool* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprConstantNumber* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprConstantString* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprLocal* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprGlobal* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprVarargs* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprCall* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprIndexName* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprIndexExpr* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprFunction* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprTable* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprUnary* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprBinary* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprTypeAssertion* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprIfElse* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprInterpString* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
virtual bool visit(class AstExprError* node)
|
||||
{
|
||||
return visit(static_cast<AstExpr*>(node));
|
||||
}
|
||||
|
||||
virtual bool visit(class AstStat* node)
|
||||
{
|
||||
return visit(static_cast<AstNode*>(node));
|
||||
}
|
||||
|
||||
virtual bool visit(class AstStatBlock* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatIf* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatWhile* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatRepeat* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatBreak* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatContinue* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatReturn* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatExpr* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatLocal* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatFor* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatForIn* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatAssign* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatCompoundAssign* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatFunction* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatLocalFunction* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatTypeAlias* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareFunction* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareGlobal* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareClass* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatError* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
|
||||
// By default visiting type annotations is disabled; override this in your visitor if you need to!
|
||||
virtual bool visit(class AstType* node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(class AstTypeReference* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeTable* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeFunction* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeTypeof* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeUnion* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeIntersection* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeSingletonBool* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeSingletonString* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypeError* node)
|
||||
{
|
||||
return visit(static_cast<AstType*>(node));
|
||||
}
|
||||
|
||||
virtual bool visit(class AstTypePack* node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool visit(class AstTypePackExplicit* node)
|
||||
{
|
||||
return visit(static_cast<AstTypePack*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypePackVariadic* node)
|
||||
{
|
||||
return visit(static_cast<AstTypePack*>(node));
|
||||
}
|
||||
virtual bool visit(class AstTypePackGeneric* node)
|
||||
{
|
||||
return visit(static_cast<AstTypePack*>(node));
|
||||
}
|
||||
};
|
||||
|
||||
AstName getIdentifier(AstExpr*);
|
||||
Location getLocation(const AstTypeList& typeList);
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauInterpolatedStringBaseSupport)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -835,13 +833,7 @@ Lexeme Lexer::readNext()
|
||||
return readQuotedString();
|
||||
|
||||
case '`':
|
||||
if (FFlag::LuauInterpolatedStringBaseSupport)
|
||||
return readInterpolatedStringBegin();
|
||||
else
|
||||
{
|
||||
consume();
|
||||
return Lexeme(Location(start, 1), '`');
|
||||
}
|
||||
|
||||
case '.':
|
||||
consume();
|
||||
|
@ -17,8 +17,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauParserErrorsOnMissingDefaultTypePackArgument, false)
|
||||
|
||||
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
||||
@ -2174,11 +2172,11 @@ AstExpr* Parser::parseSimpleExpr()
|
||||
return parseNumber();
|
||||
}
|
||||
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString ||
|
||||
(FFlag::LuauInterpolatedStringBaseSupport && lexer.current().type == Lexeme::InterpStringSimple))
|
||||
lexer.current().type == Lexeme::InterpStringSimple)
|
||||
{
|
||||
return parseString();
|
||||
}
|
||||
else if (FFlag::LuauInterpolatedStringBaseSupport && lexer.current().type == Lexeme::InterpStringBegin)
|
||||
else if (lexer.current().type == Lexeme::InterpStringBegin)
|
||||
{
|
||||
return parseInterpString();
|
||||
}
|
||||
|
33
CLI/Repl.cpp
33
CLI/Repl.cpp
@ -48,8 +48,10 @@ enum class CompileFormat
|
||||
Text,
|
||||
Binary,
|
||||
Remarks,
|
||||
Codegen,
|
||||
CodegenVerbose,
|
||||
Codegen, // Prints annotated native code including IR and assembly
|
||||
CodegenAsm, // Prints annotated native code assembly
|
||||
CodegenIr, // Prints annotated native code IR
|
||||
CodegenVerbose, // Prints annotated native code including IR, assembly and outlined code
|
||||
CodegenNull,
|
||||
Null
|
||||
};
|
||||
@ -716,7 +718,19 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st
|
||||
try
|
||||
{
|
||||
Luau::BytecodeBuilder bcb;
|
||||
Luau::CodeGen::AssemblyOptions options = {format == CompileFormat::CodegenNull, format == CompileFormat::Codegen, annotateInstruction, &bcb};
|
||||
|
||||
Luau::CodeGen::AssemblyOptions options;
|
||||
options.outputBinary = format == CompileFormat::CodegenNull;
|
||||
|
||||
if (!options.outputBinary)
|
||||
{
|
||||
options.includeAssembly = format != CompileFormat::CodegenIr;
|
||||
options.includeIr = format != CompileFormat::CodegenAsm;
|
||||
options.includeOutlinedCode = format == CompileFormat::CodegenVerbose;
|
||||
}
|
||||
|
||||
options.annotator = annotateInstruction;
|
||||
options.annotatorContext = &bcb;
|
||||
|
||||
if (format == CompileFormat::Text)
|
||||
{
|
||||
@ -729,7 +743,8 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
||||
bcb.setDumpSource(*source);
|
||||
}
|
||||
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenVerbose)
|
||||
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr ||
|
||||
format == CompileFormat::CodegenVerbose)
|
||||
{
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
|
||||
Luau::BytecodeBuilder::Dump_Remarks);
|
||||
@ -760,6 +775,8 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st
|
||||
fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout);
|
||||
break;
|
||||
case CompileFormat::Codegen:
|
||||
case CompileFormat::CodegenAsm:
|
||||
case CompileFormat::CodegenIr:
|
||||
case CompileFormat::CodegenVerbose:
|
||||
printf("%s", getCodegenAssembly(name, bcb.getBytecode(), options).c_str());
|
||||
break;
|
||||
@ -850,6 +867,14 @@ int replMain(int argc, char** argv)
|
||||
{
|
||||
compileFormat = CompileFormat::Codegen;
|
||||
}
|
||||
else if (strcmp(argv[1], "--compile=codegenasm") == 0)
|
||||
{
|
||||
compileFormat = CompileFormat::CodegenAsm;
|
||||
}
|
||||
else if (strcmp(argv[1], "--compile=codegenir") == 0)
|
||||
{
|
||||
compileFormat = CompileFormat::CodegenIr;
|
||||
}
|
||||
else if (strcmp(argv[1], "--compile=codegenverbose") == 0)
|
||||
{
|
||||
compileFormat = CompileFormat::CodegenVerbose;
|
||||
|
@ -22,7 +22,10 @@ using annotatorFn = void (*)(void* context, std::string& result, int fid, int in
|
||||
struct AssemblyOptions
|
||||
{
|
||||
bool outputBinary = false;
|
||||
bool skipOutlinedCode = false;
|
||||
|
||||
bool includeAssembly = false;
|
||||
bool includeIr = false;
|
||||
bool includeOutlinedCode = false;
|
||||
|
||||
// Optional annotator function can be provided to describe each instruction, it takes function id and sequential instruction id
|
||||
annotatorFn annotator = nullptr;
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include "CodeGenX64.h"
|
||||
#include "EmitCommonX64.h"
|
||||
#include "EmitInstructionX64.h"
|
||||
#include "IrAnalysis.h"
|
||||
#include "IrBuilder.h"
|
||||
#include "IrLoweringX64.h"
|
||||
#include "NativeState.h"
|
||||
|
||||
#include "lapi.h"
|
||||
@ -27,6 +30,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugUseOldCodegen, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -241,7 +246,7 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers&
|
||||
skip = emitInstFastCall2K(build, pc, i, next);
|
||||
break;
|
||||
case LOP_FORNPREP:
|
||||
emitInstForNPrep(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
|
||||
emitInstForNPrep(build, pc, i, next, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
|
||||
break;
|
||||
case LOP_FORNLOOP:
|
||||
emitInstForNLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)], next);
|
||||
@ -404,7 +409,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
|
||||
result->proto = proto;
|
||||
|
||||
if (build.logText)
|
||||
if (options.includeAssembly || options.includeIr)
|
||||
{
|
||||
if (proto->debugname)
|
||||
build.logAppend("; function %s()", getstr(proto->debugname));
|
||||
@ -417,6 +422,38 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
build.logAppend("\n");
|
||||
}
|
||||
|
||||
if (!FFlag::DebugUseOldCodegen)
|
||||
{
|
||||
build.align(kFunctionAlignment, AlignmentDataX64::Ud2);
|
||||
|
||||
Label start = build.setLabel();
|
||||
|
||||
IrBuilder builder;
|
||||
builder.buildFunctionIr(proto);
|
||||
|
||||
updateUseInfo(builder.function);
|
||||
|
||||
IrLoweringX64 lowering(build, helpers, data, proto, builder.function);
|
||||
|
||||
lowering.lower(options);
|
||||
|
||||
result->instTargets = new uintptr_t[proto->sizecode];
|
||||
|
||||
for (int i = 0; i < proto->sizecode; i++)
|
||||
{
|
||||
auto [irLocation, asmLocation] = builder.function.bcMapping[i];
|
||||
|
||||
result->instTargets[i] = irLocation == ~0u ? 0 : asmLocation - start.location;
|
||||
}
|
||||
|
||||
result->location = start.location;
|
||||
|
||||
if (build.logText)
|
||||
build.logAppend("\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Label> instLabels;
|
||||
instLabels.resize(proto->sizecode);
|
||||
|
||||
@ -457,7 +494,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
size_t textSize = build.text.size();
|
||||
uint32_t codeSize = build.getCodeSize();
|
||||
|
||||
if (options.annotator && !options.skipOutlinedCode)
|
||||
if (options.annotator && options.includeOutlinedCode)
|
||||
build.logAppend("; outlined instructions\n");
|
||||
|
||||
for (auto [pcpos, length] : instOutlines)
|
||||
@ -474,7 +511,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
|
||||
build.setLabel(instLabels[i]);
|
||||
|
||||
if (options.annotator && !options.skipOutlinedCode)
|
||||
if (options.annotator && options.includeOutlinedCode)
|
||||
options.annotator(options.annotatorContext, build.text, proto->bytecodeid, i);
|
||||
|
||||
Label& next = nexti < proto->sizecode ? instLabels[nexti] : start; // Last instruction can't use 'next' label
|
||||
@ -489,7 +526,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
build.jmp(instLabels[i]);
|
||||
}
|
||||
|
||||
if (options.annotator && !options.skipOutlinedCode)
|
||||
if (options.annotator && options.includeOutlinedCode)
|
||||
build.logAppend("; outlined code\n");
|
||||
|
||||
for (int i = 0, instid = 0; i < proto->sizecode; ++instid)
|
||||
@ -506,7 +543,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.annotator && !options.skipOutlinedCode)
|
||||
if (options.annotator && options.includeOutlinedCode)
|
||||
options.annotator(options.annotatorContext, build.text, proto->bytecodeid, instid);
|
||||
|
||||
build.setLabel(instFallbacks[i]);
|
||||
@ -521,7 +558,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
|
||||
}
|
||||
|
||||
// Truncate assembly output if we don't care for outlined code part
|
||||
if (options.skipOutlinedCode)
|
||||
if (!options.includeOutlinedCode)
|
||||
{
|
||||
build.text.resize(textSize);
|
||||
|
||||
@ -730,7 +767,7 @@ std::string getAssembly(lua_State* L, int idx, AssemblyOptions options)
|
||||
LUAU_ASSERT(lua_isLfunction(L, idx));
|
||||
const TValue* func = luaA_toobject(L, idx);
|
||||
|
||||
AssemblyBuilderX64 build(/* logText= */ !options.outputBinary);
|
||||
AssemblyBuilderX64 build(/* logText= */ options.includeAssembly);
|
||||
|
||||
NativeState data;
|
||||
initFallbackTable(data);
|
||||
|
@ -191,9 +191,19 @@ static void callBarrierImpl(AssemblyBuilderX64& build, RegisterX64 tmp, Register
|
||||
build.test(byte[tmp + offsetof(GCheader, marked)], bit2mask(WHITE0BIT, WHITE1BIT));
|
||||
build.jcc(ConditionX64::Zero, skip);
|
||||
|
||||
LUAU_ASSERT(object != rArg3);
|
||||
// TODO: even with re-ordering we have a chance of failure, we have a task to fix this in the future
|
||||
if (object == rArg3)
|
||||
{
|
||||
LUAU_ASSERT(tmp != rArg2);
|
||||
build.mov(rArg2, object);
|
||||
build.mov(rArg3, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
build.mov(rArg3, tmp);
|
||||
build.mov(rArg2, object);
|
||||
}
|
||||
|
||||
build.mov(rArg1, rState);
|
||||
build.call(qword[rNativeContext + contextOffset]);
|
||||
}
|
||||
|
@ -1077,11 +1077,11 @@ int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos
|
||||
return emitInstFastCallN(build, pc, /* customParams */ false, /* customParamCount */ 0, /* customArgs */ 0, pcpos, fallback);
|
||||
}
|
||||
|
||||
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopExit)
|
||||
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopStart, Label& loopExit)
|
||||
{
|
||||
int ra = LUAU_INSN_A(*pc);
|
||||
|
||||
Label tryConvert, exit;
|
||||
Label tryConvert;
|
||||
|
||||
jumpIfTagIsNot(build, ra + 0, LUA_TNUMBER, tryConvert);
|
||||
jumpIfTagIsNot(build, ra + 1, LUA_TNUMBER, tryConvert);
|
||||
@ -1107,12 +1107,12 @@ void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
|
||||
|
||||
// TODO: target branches can probably be arranged better, but we need tests for NaN behavior preservation
|
||||
// false: idx <= limit
|
||||
jumpOnNumberCmp(build, noreg, idx, limit, ConditionX64::LessEqual, exit);
|
||||
jumpOnNumberCmp(build, noreg, idx, limit, ConditionX64::LessEqual, loopStart);
|
||||
build.jmp(loopExit);
|
||||
|
||||
// true: limit <= idx
|
||||
build.setLabel(reverse);
|
||||
jumpOnNumberCmp(build, noreg, limit, idx, ConditionX64::LessEqual, exit);
|
||||
jumpOnNumberCmp(build, noreg, limit, idx, ConditionX64::LessEqual, loopStart);
|
||||
build.jmp(loopExit);
|
||||
|
||||
// TOOD: place at the end of the function
|
||||
@ -1120,8 +1120,6 @@ void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
|
||||
emitSetSavedPc(build, pcpos + 1);
|
||||
callPrepareForN(build, ra + 0, ra + 1, ra + 2);
|
||||
build.jmp(retry);
|
||||
|
||||
build.setLabel(exit);
|
||||
}
|
||||
|
||||
void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit)
|
||||
|
@ -60,7 +60,7 @@ int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
|
||||
int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
|
||||
int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
|
||||
int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
|
||||
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopExit);
|
||||
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopStart, Label& loopExit);
|
||||
void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit);
|
||||
void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit, Label& fallback);
|
||||
void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat);
|
||||
|
50
CodeGen/src/IrAnalysis.cpp
Normal file
50
CodeGen/src/IrAnalysis.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "IrAnalysis.h"
|
||||
|
||||
#include "IrData.h"
|
||||
#include "IrUtils.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
{
|
||||
|
||||
static void recordUse(IrInst& inst, size_t index)
|
||||
{
|
||||
LUAU_ASSERT(inst.useCount < 0xffff);
|
||||
|
||||
inst.useCount++;
|
||||
inst.lastUse = uint32_t(index);
|
||||
}
|
||||
|
||||
void updateUseInfo(IrFunction& function)
|
||||
{
|
||||
std::vector<IrInst>& instructions = function.instructions;
|
||||
|
||||
for (IrInst& inst : instructions)
|
||||
{
|
||||
inst.useCount = 0;
|
||||
inst.lastUse = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < instructions.size(); ++i)
|
||||
{
|
||||
IrInst& inst = instructions[i];
|
||||
|
||||
auto checkOp = [&instructions, i](IrOp op) {
|
||||
if (op.kind == IrOpKind::Inst)
|
||||
recordUse(instructions[op.index], i);
|
||||
};
|
||||
|
||||
checkOp(inst.a);
|
||||
checkOp(inst.b);
|
||||
checkOp(inst.c);
|
||||
checkOp(inst.d);
|
||||
checkOp(inst.e);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
14
CodeGen/src/IrAnalysis.h
Normal file
14
CodeGen/src/IrAnalysis.h
Normal file
@ -0,0 +1,14 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
{
|
||||
|
||||
struct IrFunction;
|
||||
|
||||
void updateUseInfo(IrFunction& function);
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
@ -318,9 +318,12 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
|
||||
}
|
||||
case LOP_FORNPREP:
|
||||
{
|
||||
IrOp loopStart = blockAtInst(i + getOpLength(LOP_FORNPREP));
|
||||
IrOp loopExit = blockAtInst(i + 1 + LUAU_INSN_D(*pc));
|
||||
|
||||
inst(IrCmd::LOP_FORNPREP, constUint(i), loopExit);
|
||||
inst(IrCmd::LOP_FORNPREP, constUint(i), loopStart, loopExit);
|
||||
|
||||
beginBlock(loopStart);
|
||||
break;
|
||||
}
|
||||
case LOP_FORNLOOP:
|
||||
@ -437,7 +440,11 @@ bool IrBuilder::isInternalBlock(IrOp block)
|
||||
|
||||
void IrBuilder::beginBlock(IrOp block)
|
||||
{
|
||||
function.blocks[block.index].start = uint32_t(function.instructions.size());
|
||||
IrBlock& target = function.blocks[block.index];
|
||||
|
||||
LUAU_ASSERT(target.start == ~0u || target.start == uint32_t(function.instructions.size()));
|
||||
|
||||
target.start = uint32_t(function.instructions.size());
|
||||
}
|
||||
|
||||
IrOp IrBuilder::constBool(bool value)
|
||||
|
1242
CodeGen/src/IrLoweringX64.cpp
Normal file
1242
CodeGen/src/IrLoweringX64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
CodeGen/src/IrLoweringX64.h
Normal file
89
CodeGen/src/IrLoweringX64.h
Normal file
@ -0,0 +1,89 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/AssemblyBuilderX64.h"
|
||||
|
||||
#include "IrData.h"
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
struct Proto;
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
{
|
||||
|
||||
struct ModuleHelpers;
|
||||
struct NativeState;
|
||||
struct AssemblyOptions;
|
||||
|
||||
struct IrLoweringX64
|
||||
{
|
||||
// Some of these arguments are only required while we re-use old direct bytecode to x64 lowering
|
||||
IrLoweringX64(AssemblyBuilderX64& build, ModuleHelpers& helpers, NativeState& data, Proto* proto, IrFunction& function);
|
||||
|
||||
void lower(AssemblyOptions options);
|
||||
|
||||
void lowerInst(IrInst& inst, uint32_t index, IrBlock& next);
|
||||
|
||||
bool isFallthroughBlock(IrBlock target, IrBlock next);
|
||||
void jumpOrFallthrough(IrBlock& target, IrBlock& next);
|
||||
|
||||
// Operand data lookup helpers
|
||||
OperandX64 memRegDoubleOp(IrOp op) const;
|
||||
OperandX64 memRegTagOp(IrOp op) const;
|
||||
RegisterX64 regOp(IrOp op) const;
|
||||
|
||||
IrConst constOp(IrOp op) const;
|
||||
uint8_t tagOp(IrOp op) const;
|
||||
bool boolOp(IrOp op) const;
|
||||
int intOp(IrOp op) const;
|
||||
unsigned uintOp(IrOp op) const;
|
||||
double doubleOp(IrOp op) const;
|
||||
|
||||
IrBlock& blockOp(IrOp op) const;
|
||||
Label& labelOp(IrOp op) const;
|
||||
|
||||
// Unscoped register allocation
|
||||
RegisterX64 allocGprReg(SizeX64 preferredSize);
|
||||
RegisterX64 allocXmmReg();
|
||||
|
||||
RegisterX64 allocGprRegOrReuse(SizeX64 preferredSize, uint32_t index, std::initializer_list<IrOp> oprefs);
|
||||
RegisterX64 allocXmmRegOrReuse(uint32_t index, std::initializer_list<IrOp> oprefs);
|
||||
|
||||
void freeReg(RegisterX64 reg);
|
||||
void freeLastUseReg(IrInst& target, uint32_t index);
|
||||
void freeLastUseRegs(const IrInst& inst, uint32_t index);
|
||||
|
||||
ConditionX64 getX64Condition(IrCondition cond) const;
|
||||
|
||||
struct ScopedReg
|
||||
{
|
||||
ScopedReg(IrLoweringX64& owner, SizeX64 size);
|
||||
~ScopedReg();
|
||||
|
||||
ScopedReg(const ScopedReg&) = delete;
|
||||
ScopedReg& operator=(const ScopedReg&) = delete;
|
||||
|
||||
void free();
|
||||
|
||||
IrLoweringX64& owner;
|
||||
RegisterX64 reg;
|
||||
};
|
||||
|
||||
AssemblyBuilderX64& build;
|
||||
ModuleHelpers& helpers;
|
||||
NativeState& data;
|
||||
Proto* proto = nullptr; // Temporarily required to provide 'Instruction* pc' to old emitInst* methods
|
||||
|
||||
IrFunction& function;
|
||||
|
||||
std::array<bool, 16> freeGprMap;
|
||||
std::array<bool, 16> freeXmmMap;
|
||||
};
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
@ -236,9 +236,9 @@ public:
|
||||
std::swap(capacity, newtable.capacity);
|
||||
}
|
||||
|
||||
void rehash_if_full()
|
||||
void rehash_if_full(const Key& key)
|
||||
{
|
||||
if (count >= capacity * 3 / 4)
|
||||
if (count >= capacity * 3 / 4 && !find(key))
|
||||
{
|
||||
rehash();
|
||||
}
|
||||
@ -489,7 +489,7 @@ public:
|
||||
|
||||
const Key& insert(const Key& key)
|
||||
{
|
||||
impl.rehash_if_full();
|
||||
impl.rehash_if_full(key);
|
||||
return *impl.insert_unsafe(key);
|
||||
}
|
||||
|
||||
@ -559,7 +559,7 @@ public:
|
||||
// Note: this reference is invalidated by any insert operation (i.e. operator[])
|
||||
Value& operator[](const Key& key)
|
||||
{
|
||||
impl.rehash_if_full();
|
||||
impl.rehash_if_full(key);
|
||||
return impl.insert_unsafe(key)->second;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ inline bool isFlagExperimental(const char* flag)
|
||||
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
|
||||
// or critical bugs that are found after the code has been submitted.
|
||||
static const char* kList[] = {
|
||||
"LuauInterpolatedStringBaseSupport",
|
||||
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
|
||||
"LuauTryhardAnd", // waiting for a fix in graphql-lua -> apollo-client-lia -> lua-apps
|
||||
// makes sure we always have at least one entry
|
||||
|
@ -25,7 +25,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||
|
||||
LUAU_FASTFLAG(LuauInterpolatedStringBaseSupport)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMultiAssignmentConflictFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfAssignmentSkip, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileInterpStringLimit, false)
|
||||
@ -2090,7 +2089,7 @@ struct Compiler
|
||||
{
|
||||
compileExprIfElse(expr, target, targetTemp);
|
||||
}
|
||||
else if (AstExprInterpString* interpString = node->as<AstExprInterpString>(); FFlag::LuauInterpolatedStringBaseSupport && interpString)
|
||||
else if (AstExprInterpString* interpString = node->as<AstExprInterpString>())
|
||||
{
|
||||
compileExprInterpString(interpString, target, targetTemp);
|
||||
}
|
||||
|
@ -82,8 +82,10 @@ target_sources(Luau.CodeGen PRIVATE
|
||||
CodeGen/src/EmitCommonX64.cpp
|
||||
CodeGen/src/EmitInstructionX64.cpp
|
||||
CodeGen/src/Fallbacks.cpp
|
||||
CodeGen/src/IrAnalysis.cpp
|
||||
CodeGen/src/IrBuilder.cpp
|
||||
CodeGen/src/IrDump.cpp
|
||||
CodeGen/src/IrLoweringX64.cpp
|
||||
CodeGen/src/IrTranslation.cpp
|
||||
CodeGen/src/NativeState.cpp
|
||||
CodeGen/src/UnwindBuilderDwarf2.cpp
|
||||
@ -98,9 +100,11 @@ target_sources(Luau.CodeGen PRIVATE
|
||||
CodeGen/src/EmitInstructionX64.h
|
||||
CodeGen/src/Fallbacks.h
|
||||
CodeGen/src/FallbacksProlog.h
|
||||
CodeGen/src/IrAnalysis.h
|
||||
CodeGen/src/IrBuilder.h
|
||||
CodeGen/src/IrDump.h
|
||||
CodeGen/src/IrData.h
|
||||
CodeGen/src/IrLoweringX64.h
|
||||
CodeGen/src/IrTranslation.h
|
||||
CodeGen/src/IrUtils.h
|
||||
CodeGen/src/NativeState.h
|
||||
@ -330,6 +334,7 @@ if(TARGET Luau.UnitTest)
|
||||
tests/ConstraintSolver.test.cpp
|
||||
tests/CostModel.test.cpp
|
||||
tests/DataFlowGraph.test.cpp
|
||||
tests/DenseHash.test.cpp
|
||||
tests/Error.test.cpp
|
||||
tests/Frontend.test.cpp
|
||||
tests/JsonEmitter.test.cpp
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCheckGetInfoIndex, false)
|
||||
|
||||
static const char* getfuncname(Closure* f);
|
||||
|
||||
static int currentpc(lua_State* L, CallInfo* ci)
|
||||
@ -173,11 +175,20 @@ int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
|
||||
Closure* f = NULL;
|
||||
CallInfo* ci = NULL;
|
||||
if (level < 0)
|
||||
{
|
||||
if (FFlag::LuauCheckGetInfoIndex)
|
||||
{
|
||||
const TValue* func = luaA_toobject(L, level);
|
||||
api_check(L, ttisfunction(func));
|
||||
f = clvalue(func);
|
||||
}
|
||||
else
|
||||
{
|
||||
StkId func = L->top + level;
|
||||
api_check(L, ttisfunction(func));
|
||||
f = clvalue(func);
|
||||
}
|
||||
}
|
||||
else if (unsigned(level) < unsigned(L->ci - L->base_ci))
|
||||
{
|
||||
ci = L->ci - level;
|
||||
|
@ -185,8 +185,6 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprIfThen")
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprInterpString")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
AstStat* statement = expectParseStatement("local a = `var = {x}`");
|
||||
|
||||
std::string_view expected =
|
||||
|
@ -2814,8 +2814,6 @@ a = if temp then even else abc@3
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_constant")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
check(R"(f(`@1`))");
|
||||
auto ac = autocomplete('1');
|
||||
CHECK(ac.entryMap.empty());
|
||||
@ -2839,8 +2837,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_constant")
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
check(R"(f(`expression = {@1}`))");
|
||||
auto ac = autocomplete('1');
|
||||
CHECK(ac.entryMap.count("table"));
|
||||
@ -2849,8 +2845,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression")
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression_with_comments")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
check(R"(f(`expression = {--[[ bla bla bla ]]@1`))");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
@ -2866,8 +2860,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression_with_c
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_as_singleton")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
check(R"(
|
||||
--!strict
|
||||
local function f(a: "cat" | "dog") end
|
||||
|
@ -1277,15 +1277,11 @@ RETURN R1 1
|
||||
|
||||
TEST_CASE("InterpStringWithNoExpressions")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CHECK_EQ(compileFunction0(R"(return "hello")"), compileFunction0("return `hello`"));
|
||||
}
|
||||
|
||||
TEST_CASE("InterpStringZeroCost")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction0(R"(local _ = `hello, {"world"}!`)"),
|
||||
R"(
|
||||
LOADK R1 K0 ['hello, %*!']
|
||||
@ -1299,8 +1295,6 @@ RETURN R0 0
|
||||
|
||||
TEST_CASE("InterpStringRegisterCleanup")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction0(R"(
|
||||
local a, b, c = nil, "um", "uh oh"
|
||||
a = `foo{"bar"}`
|
||||
@ -1325,7 +1319,6 @@ RETURN R0 0
|
||||
|
||||
TEST_CASE("InterpStringRegisterLimit")
|
||||
{
|
||||
ScopedFastFlag luauInterpolatedStringBaseSupport{"LuauInterpolatedStringBaseSupport", true};
|
||||
ScopedFastFlag luauCompileInterpStringLimit{"LuauCompileInterpStringLimit", true};
|
||||
|
||||
CHECK_THROWS_AS(compileFunction0(("local a = `" + rep("{1}", 254) + "`").c_str()), std::exception);
|
||||
|
@ -304,8 +304,6 @@ TEST_CASE("Strings")
|
||||
|
||||
TEST_CASE("StringInterp")
|
||||
{
|
||||
ScopedFastFlag sffInterpStrings{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
runConformance("stringinterp.lua");
|
||||
}
|
||||
|
||||
|
@ -227,8 +227,6 @@ end
|
||||
|
||||
TEST_CASE("InterpString")
|
||||
{
|
||||
ScopedFastFlag sff("LuauInterpolatedStringBaseSupport", true);
|
||||
|
||||
uint64_t model = modelFunction(R"(
|
||||
function test(a)
|
||||
return `hello, {a}!`
|
||||
|
79
tests/DenseHash.test.cpp
Normal file
79
tests/DenseHash.test.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/DenseHash.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
/** So... why are we picking a very specific number to fill the DenseHash(Map|Set)?
|
||||
*
|
||||
* That's because that's the count that happens to trigger a specific bug.
|
||||
*
|
||||
* DHT determines if it is full by checking whether the count is greater than or equal to 75% of
|
||||
* its capacity. Once this condition triggers, it rehashes everything by doubling its buffer.
|
||||
*
|
||||
* If DHT gets rehashed, the iterator gets invalidated, but it's very important that we support
|
||||
* the use case of overwriting/merging DHTs while iterating.
|
||||
*/
|
||||
|
||||
TEST_SUITE_BEGIN("DenseHashTests");
|
||||
|
||||
TEST_CASE("overwriting_an_existing_field_when_full_shouldnt_rehash")
|
||||
{
|
||||
// See the note at the top on why these numbers were chosen.
|
||||
|
||||
Luau::DenseHashMap<int, int> m{-1};
|
||||
for (int i = 0; i < 12; ++i)
|
||||
m[i] = i;
|
||||
|
||||
REQUIRE(m.size() == 12);
|
||||
|
||||
for (auto [k, a] : m)
|
||||
m[k] = a + 1;
|
||||
|
||||
for (size_t i = 0; i < m.size(); ++i)
|
||||
{
|
||||
int* a = m.find(int(i));
|
||||
REQUIRE(a);
|
||||
CHECK(i + 1 == *a);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("merging_another_map_and_resolve_conflicts_that_also_just_so_happens_to_rehash_while_iterating")
|
||||
{
|
||||
// See the note at the top on why these numbers were chosen.
|
||||
|
||||
Luau::DenseHashMap<int, int> m1{-1};
|
||||
for (int i = 0; i < 12; ++i)
|
||||
m1[i] = i;
|
||||
|
||||
Luau::DenseHashMap<int, int> m2{-1};
|
||||
for (int i = 8; i < 24; ++i)
|
||||
m2[i] = i;
|
||||
|
||||
REQUIRE(m1.size() == 12);
|
||||
REQUIRE(m2.size() == 16);
|
||||
|
||||
for (auto [k, a] : m1)
|
||||
{
|
||||
if (int* b = m2.find(k))
|
||||
m1[k] = a + *b;
|
||||
}
|
||||
|
||||
for (auto [k, a] : m2)
|
||||
{
|
||||
if (!m1.find(k))
|
||||
m1[k] = a;
|
||||
}
|
||||
|
||||
REQUIRE(m1.size() == 24);
|
||||
for (size_t i = 0; i < m1.size(); ++i)
|
||||
{
|
||||
int* a = m1.find(int(i));
|
||||
REQUIRE(a);
|
||||
if (i < 8 || i >= 12)
|
||||
CHECK(i == *a);
|
||||
else
|
||||
CHECK(i + i == *a);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
@ -21,8 +21,6 @@
|
||||
static const char* mainModuleName = "MainModule";
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAG(LuauReportShadowedTypeAlias)
|
||||
|
||||
extern std::optional<unsigned> randomSeed; // tests/main.cpp
|
||||
|
||||
@ -585,6 +583,7 @@ void registerHiddenTypes(Frontend* frontend)
|
||||
globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend->builtinTypes->functionType};
|
||||
globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend->builtinTypes->classType};
|
||||
globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend->builtinTypes->errorType};
|
||||
globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend->builtinTypes->tableType};
|
||||
}
|
||||
|
||||
void createSomeClasses(Frontend* frontend)
|
||||
|
@ -94,7 +94,6 @@ struct Fixture
|
||||
TypeId requireTypeAlias(const std::string& name);
|
||||
|
||||
ScopedFastFlag sff_DebugLuauFreezeArena;
|
||||
ScopedFastFlag sff_UnknownNever{"LuauUnknownAndNeverType", true};
|
||||
|
||||
TestFileResolver fileResolver;
|
||||
TestConfigResolver configResolver;
|
||||
|
@ -140,8 +140,6 @@ TEST_CASE("lookahead")
|
||||
|
||||
TEST_CASE("string_interpolation_basic")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
const std::string testInput = R"(`foo {"bar"}`)";
|
||||
Luau::Allocator alloc;
|
||||
AstNameTable table(alloc);
|
||||
@ -159,8 +157,6 @@ TEST_CASE("string_interpolation_basic")
|
||||
|
||||
TEST_CASE("string_interpolation_double_brace")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
const std::string testInput = R"(`foo{{bad}}bar`)";
|
||||
Luau::Allocator alloc;
|
||||
AstNameTable table(alloc);
|
||||
@ -179,8 +175,6 @@ TEST_CASE("string_interpolation_double_brace")
|
||||
|
||||
TEST_CASE("string_interpolation_double_but_unmatched_brace")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
const std::string testInput = R"(`{{oops}`, 1)";
|
||||
Luau::Allocator alloc;
|
||||
AstNameTable table(alloc);
|
||||
@ -195,8 +189,6 @@ TEST_CASE("string_interpolation_double_but_unmatched_brace")
|
||||
|
||||
TEST_CASE("string_interpolation_unmatched_brace")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
const std::string testInput = R"({
|
||||
`hello {"world"}
|
||||
} -- this might be incorrectly parsed as a string)";
|
||||
@ -213,8 +205,6 @@ TEST_CASE("string_interpolation_unmatched_brace")
|
||||
|
||||
TEST_CASE("string_interpolation_with_unicode_escape")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
const std::string testInput = R"(`\u{1F41B}`)";
|
||||
Luau::Allocator alloc;
|
||||
AstNameTable table(alloc);
|
||||
|
@ -1686,8 +1686,6 @@ TEST_CASE_FIXTURE(Fixture, "WrongCommentOptimize")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "TestStringInterpolation")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
LintResult result = lint(R"(
|
||||
--!nocheck
|
||||
local _ = `unknown {foo}`
|
||||
|
@ -492,9 +492,12 @@ TEST_CASE_FIXTURE(NormalizeFixture, "union_function_and_top_function")
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "negated_function_is_anything_except_a_function")
|
||||
{
|
||||
ScopedFastFlag{"LuauNegatedClassTypes", true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauNegatedTableTypes", true},
|
||||
{"LuauNegatedClassTypes", true},
|
||||
};
|
||||
|
||||
CHECK("(boolean | class | number | string | thread)?" == toString(normal(R"(
|
||||
CHECK("(boolean | class | number | string | table | thread)?" == toString(normal(R"(
|
||||
Not<fun>
|
||||
)")));
|
||||
}
|
||||
@ -506,9 +509,13 @@ TEST_CASE_FIXTURE(NormalizeFixture, "specific_functions_cannot_be_negated")
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "bare_negated_boolean")
|
||||
{
|
||||
ScopedFastFlag{"LuauNegatedClassTypes", true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauNegatedTableTypes", true},
|
||||
{"LuauNegatedClassTypes", true},
|
||||
};
|
||||
|
||||
// TODO: We don't yet have a way to say number | string | thread | nil | Class | Table | Function
|
||||
CHECK("(class | function | number | string | thread)?" == toString(normal(R"(
|
||||
CHECK("(class | function | number | string | table | thread)?" == toString(normal(R"(
|
||||
Not<boolean>
|
||||
)")));
|
||||
}
|
||||
@ -603,15 +610,18 @@ TEST_CASE_FIXTURE(NormalizeFixture, "narrow_union_of_classes_with_intersection")
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_classes")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauNegatedClassTypes", true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauNegatedTableTypes", true},
|
||||
{"LuauNegatedClassTypes", true},
|
||||
};
|
||||
|
||||
createSomeClasses(&frontend);
|
||||
CHECK("(Parent & ~Child) | Unrelated" == toString(normal("(Parent & Not<Child>) | Unrelated")));
|
||||
CHECK("((class & ~Child) | boolean | function | number | string | thread)?" == toString(normal("Not<Child>")));
|
||||
CHECK("((class & ~Child) | boolean | function | number | string | table | thread)?" == toString(normal("Not<Child>")));
|
||||
CHECK("Child" == toString(normal("Not<Parent> & Child")));
|
||||
CHECK("((class & ~Parent) | Child | boolean | function | number | string | thread)?" == toString(normal("Not<Parent> | Child")));
|
||||
CHECK("(boolean | function | number | string | thread)?" == toString(normal("Not<cls>")));
|
||||
CHECK("(Parent | Unrelated | boolean | function | number | string | thread)?" ==
|
||||
CHECK("((class & ~Parent) | Child | boolean | function | number | string | table | thread)?" == toString(normal("Not<Parent> | Child")));
|
||||
CHECK("(boolean | function | number | string | table | thread)?" == toString(normal("Not<cls>")));
|
||||
CHECK("(Parent | Unrelated | boolean | function | number | string | table | thread)?" ==
|
||||
toString(normal("Not<cls & Not<Parent> & Not<Child> & Not<Unrelated>>")));
|
||||
}
|
||||
|
||||
@ -631,4 +641,22 @@ TEST_CASE_FIXTURE(NormalizeFixture, "classes_and_never")
|
||||
CHECK("never" == toString(normal("Parent & never")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "top_table_type")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauNegatedTableTypes", true};
|
||||
|
||||
CHECK("table" == toString(normal("{} | tbl")));
|
||||
CHECK("{| |}" == toString(normal("{} & tbl")));
|
||||
CHECK("never" == toString(normal("number & tbl")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_tables")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauNegatedTableTypes", true};
|
||||
|
||||
CHECK(nullptr == toNormalizedType("Not<{}>"));
|
||||
CHECK("(boolean | class | function | number | string | thread)?" == toString(normal("Not<tbl>")));
|
||||
CHECK("table" == toString(normal("Not<Not<tbl>>")));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -906,8 +906,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment_error_multiple")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_begin")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -923,8 +921,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_begin")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -940,8 +936,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
auto columnOfEndBraceError = [this](const char* code) {
|
||||
try
|
||||
{
|
||||
@ -966,8 +960,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -986,8 +978,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_mid_without_end_brace_in_table")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -1006,8 +996,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_mid_without_end_brace_in_t
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_as_type_fail")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
@ -1028,8 +1016,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_as_type_fail")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_call_without_parens")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
try
|
||||
{
|
||||
parse(R"(
|
||||
|
@ -417,7 +417,7 @@ n1 [label="BoundType 1"];
|
||||
n1 -> n2;
|
||||
n2 [label="TableType 2"];
|
||||
n2 -> n3 [label="boundTo"];
|
||||
n3 [label="TableType 3"];
|
||||
n3 [label="TableType a"];
|
||||
n3 -> n4 [label="x"];
|
||||
n4 [label="number"];
|
||||
})",
|
||||
|
@ -681,8 +681,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
std::string code = R"( local _ = `hello {name}` )";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
@ -690,8 +688,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -201,11 +201,11 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases")
|
||||
|
||||
const char* expectedError;
|
||||
if (FFlag::LuauTypeMismatchInvarianceInError)
|
||||
expectedError = "Type '{ v: string }' could not be converted into 'T<number>'\n"
|
||||
expectedError = "Type 'bad' could not be converted into 'T<number>'\n"
|
||||
"caused by:\n"
|
||||
" Property 'v' is not compatible. Type 'string' could not be converted into 'number' in an invariant context";
|
||||
else
|
||||
expectedError = "Type '{ v: string }' could not be converted into 'T<number>'\n"
|
||||
expectedError = "Type 'bad' could not be converted into 'T<number>'\n"
|
||||
"caused by:\n"
|
||||
" Property 'v' is not compatible. Type 'string' could not be converted into 'number'";
|
||||
|
||||
@ -228,13 +228,13 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
|
||||
|
||||
const char* expectedError;
|
||||
if (FFlag::LuauTypeMismatchInvarianceInError)
|
||||
expectedError = "Type '{ t: { v: string } }' could not be converted into 'U<number>'\n"
|
||||
expectedError = "Type 'bad' could not be converted into 'U<number>'\n"
|
||||
"caused by:\n"
|
||||
" Property 't' is not compatible. Type '{ v: string }' could not be converted into 'T<number>'\n"
|
||||
"caused by:\n"
|
||||
" Property 'v' is not compatible. Type 'string' could not be converted into 'number' in an invariant context";
|
||||
else
|
||||
expectedError = "Type '{ t: { v: string } }' could not be converted into 'U<number>'\n"
|
||||
expectedError = "Type 'bad' could not be converted into 'U<number>'\n"
|
||||
"caused by:\n"
|
||||
" Property 't' is not compatible. Type '{ v: string }' could not be converted into 'T<number>'\n"
|
||||
"caused by:\n"
|
||||
@ -890,8 +890,6 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "report_shadowed_aliases")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauReportShadowedTypeAlias", true};
|
||||
|
||||
// We allow a previous type alias to depend on a future type alias. That exact feature enables a confusing example, like the following snippet,
|
||||
// which has the type alias FakeString point to the type alias `string` that which points to `number`.
|
||||
CheckResult result = check(R"(
|
||||
|
@ -865,8 +865,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBetterMessagingOnCountMismatch", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -888,8 +886,6 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBetterMessagingOnCountMismatch", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -911,8 +907,6 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_no_function")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBetterMessagingOnCountMismatch", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
|
@ -519,7 +519,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus")
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK(toString(result.errors[0]) == "Type '{ value: number }' could not be converted into 'number'");
|
||||
CHECK(toString(result.errors[0]) == "Type 'bar' could not be converted into 'number'");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -978,8 +978,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "equality_operations_succeed_if_any_union_bra
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "expected_types_through_binary_and")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBinaryNeedsExpectedTypesToo", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local x: "a" | "b" | boolean = math.random() > 0.5 and "a"
|
||||
)");
|
||||
@ -989,8 +987,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "expected_types_through_binary_and")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "expected_types_through_binary_or")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBinaryNeedsExpectedTypesToo", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local x: "a" | "b" | boolean = math.random() > 0.5 or "b"
|
||||
)");
|
||||
|
@ -342,8 +342,6 @@ TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_variadic_pack")
|
||||
// Belongs in TypeInfer.builtins.test.cpp.
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "pcall_returns_at_least_two_value_but_function_returns_nothing")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauBetterMessagingOnCountMismatch", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(): () end
|
||||
local ok, res = pcall(f)
|
||||
|
@ -1014,7 +1014,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "merge_should_be_fully_agnostic_of_hashmap_or
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK_EQ("(never | string) & (string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28})));
|
||||
CHECK_EQ("(string | table) & (string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28})));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1241,8 +1241,6 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_tag")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "discriminate_tag_with_implicit_else")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauImplicitElseRefinement", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Cat = {tag: "Cat", name: string, catfood: string}
|
||||
type Dog = {tag: "Dog", name: string, dogfood: string}
|
||||
@ -1561,7 +1559,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "refine_param_of_type_instance_without
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK_EQ("Folder & Instance", toString(requireTypeAtPosition({3, 28})));
|
||||
CHECK_EQ("Instance & ~Folder & never", toString(requireTypeAtPosition({5, 28})));
|
||||
CHECK_EQ("Instance & ~Folder & table", toString(requireTypeAtPosition({5, 28})));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1728,8 +1726,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "what_nonsensical_condition")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "else_with_no_explicit_expression_should_also_refine_the_tagged_union")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauImplicitElseRefinement", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Ok<T> = { tag: "ok", value: T }
|
||||
type Err<E> = { tag: "err", err: E }
|
||||
|
@ -341,9 +341,9 @@ TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias")
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expectedError = "Type '{ result: string, success: false }' could not be converted into 'Err<number> | Ok<string>'\n"
|
||||
const std::string expectedError = "Type 'a' could not be converted into 'Err<number> | Ok<string>'\n"
|
||||
"caused by:\n"
|
||||
" None of the union options are compatible. For example: Table type '{ result: string, success: false }'"
|
||||
" None of the union options are compatible. For example: Table type 'a'"
|
||||
" not compatible with type 'Err<number>' because the former is missing field 'error'";
|
||||
|
||||
CHECK(toString(result.errors[0]) == expectedError);
|
||||
@ -500,8 +500,6 @@ TEST_CASE_FIXTURE(Fixture, "taking_the_length_of_union_of_string_singleton")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "no_widening_from_callsites")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauReturnsFromCallsitesAreNotWidened", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Direction = "North" | "East" | "West" | "South"
|
||||
|
||||
|
@ -3214,8 +3214,6 @@ local b = a.x
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauScalarShapeSubtyping", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(s)
|
||||
return s:lower()
|
||||
@ -3231,8 +3229,6 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauScalarShapeSubtyping", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(s)
|
||||
return s:absolutely_no_scalar_has_this_method()
|
||||
@ -3263,7 +3259,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compatible")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauScalarShapeSubtyping", true};
|
||||
ScopedFastFlag luauScalarShapeUnifyToMtOwner{"LuauScalarShapeUnifyToMtOwner2", true}; // Changes argument from table type to primitive
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -3279,8 +3274,6 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compati
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauScalarShapeSubtyping", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(s): string
|
||||
local foo = s:absolutely_no_scalar_has_this_method()
|
||||
@ -3298,7 +3291,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly")
|
||||
{
|
||||
ScopedFastFlag luauScalarShapeSubtyping{"LuauScalarShapeSubtyping", true};
|
||||
ScopedFastFlag luauScalarShapeUnifyToMtOwner{"LuauScalarShapeUnifyToMtOwner2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -3385,7 +3377,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_has_a_side_effect")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK(toString(requireType("foo")) == "{ @metatable { __add: <a, b>(a, b) -> number }, { } }");
|
||||
CHECK(toString(requireType("foo")) == "{ @metatable mt, foo }");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tables_should_be_fully_populated")
|
||||
|
@ -816,8 +816,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "tc_interpolated_string_basic")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local foo: string = `hello {"world"}`
|
||||
)");
|
||||
@ -827,8 +825,6 @@ TEST_CASE_FIXTURE(Fixture, "tc_interpolated_string_basic")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "tc_interpolated_string_with_invalid_expression")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: number) end
|
||||
|
||||
@ -840,8 +836,6 @@ TEST_CASE_FIXTURE(Fixture, "tc_interpolated_string_with_invalid_expression")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "tc_interpolated_string_constant_type")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local foo: "hello" = `hello`
|
||||
)");
|
||||
@ -1153,6 +1147,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "it_is_ok_to_have_inconsistent_number_of_retu
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "fuzz_free_table_type_change_during_index_check")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauScalarShapeUnifyToMtOwner2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local _ = nil
|
||||
while _["" >= _] do
|
||||
|
@ -268,8 +268,6 @@ TEST_CASE_FIXTURE(Fixture, "unary_minus_of_never")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "length_of_never")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauNeverTypesAndOperatorsInference", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local x = #({} :: never)
|
||||
)");
|
||||
@ -282,8 +280,6 @@ TEST_CASE_FIXTURE(Fixture, "length_of_never")
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_in_any_ordering_operators")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauUnknownAndNeverType", true},
|
||||
{"LuauNeverTypesAndOperatorsInference", true},
|
||||
{"LuauTryhardAnd", true},
|
||||
};
|
||||
|
||||
@ -300,11 +296,6 @@ TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_i
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "math_operators_and_never")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauUnknownAndNeverType", true},
|
||||
{"LuauNeverTypesAndOperatorsInference", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function mul(x: nil, y)
|
||||
return x ~= nil and x * y -- infers boolean | never, which is normalized into boolean
|
||||
|
@ -521,6 +521,30 @@ TEST_CASE_FIXTURE(ReductionFixture, "intersections_without_negations")
|
||||
TypeId ty = reductionof("(Parent & Child) | (Parent & AnotherChild) | (Parent & Unrelated)");
|
||||
CHECK("AnotherChild | Child" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_and_table")
|
||||
{
|
||||
TypeId ty = reductionof("tbl & {}");
|
||||
CHECK("{| |}" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_and_non_table")
|
||||
{
|
||||
TypeId ty = reductionof("tbl & \"foo\"");
|
||||
CHECK("never" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_and_metatable")
|
||||
{
|
||||
BuiltinsFixture fixture;
|
||||
registerHiddenTypes(&fixture.frontend);
|
||||
fixture.check(R"(
|
||||
type Ty = tbl & typeof(setmetatable({}, {}))
|
||||
)");
|
||||
|
||||
TypeId ty = reductionof(fixture.requireTypeAlias("Ty"));
|
||||
CHECK("{ @metatable { }, { } }" == toString(ty));
|
||||
}
|
||||
} // intersections_without_negations
|
||||
|
||||
TEST_CASE_FIXTURE(ReductionFixture, "intersections_with_negations")
|
||||
@ -686,6 +710,24 @@ TEST_CASE_FIXTURE(ReductionFixture, "intersections_with_negations")
|
||||
TypeId ty = reductionof("{ x: { p: string } } & { x: { p: Not<number> } }");
|
||||
CHECK("{| x: {| p: string |} |}" == toStringFull(ty));
|
||||
}
|
||||
|
||||
SUBCASE("not_top_table_and_table")
|
||||
{
|
||||
TypeId ty = reductionof("Not<tbl> & {}");
|
||||
CHECK("never" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("not_top_table_and_metatable")
|
||||
{
|
||||
BuiltinsFixture fixture;
|
||||
registerHiddenTypes(&fixture.frontend);
|
||||
fixture.check(R"(
|
||||
type Ty = Not<tbl> & typeof(setmetatable({}, {}))
|
||||
)");
|
||||
|
||||
TypeId ty = reductionof(fixture.requireTypeAlias("Ty"));
|
||||
CHECK("never" == toString(ty));
|
||||
}
|
||||
} // intersections_with_negations
|
||||
|
||||
TEST_CASE_FIXTURE(ReductionFixture, "unions_without_negations")
|
||||
@ -911,6 +953,30 @@ TEST_CASE_FIXTURE(ReductionFixture, "unions_without_negations")
|
||||
TypeId ty = reductionof("string | err");
|
||||
CHECK("*error-type* | string" == toStringFull(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_or_table")
|
||||
{
|
||||
TypeId ty = reductionof("tbl | {}");
|
||||
CHECK("table" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_or_metatable")
|
||||
{
|
||||
BuiltinsFixture fixture;
|
||||
registerHiddenTypes(&fixture.frontend);
|
||||
fixture.check(R"(
|
||||
type Ty = tbl | typeof(setmetatable({}, {}))
|
||||
)");
|
||||
|
||||
TypeId ty = reductionof(fixture.requireTypeAlias("Ty"));
|
||||
CHECK("table" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("top_table_or_non_table")
|
||||
{
|
||||
TypeId ty = reductionof("tbl | number");
|
||||
CHECK("number | table" == toString(ty));
|
||||
}
|
||||
} // unions_without_negations
|
||||
|
||||
TEST_CASE_FIXTURE(ReductionFixture, "unions_with_negations")
|
||||
@ -1124,6 +1190,24 @@ TEST_CASE_FIXTURE(ReductionFixture, "unions_with_negations")
|
||||
TypeId ty = reductionof("string | Not<err>");
|
||||
CHECK("string | ~*error-type*" == toStringFull(ty));
|
||||
}
|
||||
|
||||
SUBCASE("not_top_table_or_table")
|
||||
{
|
||||
TypeId ty = reductionof("Not<tbl> | {}");
|
||||
CHECK("{| |} | ~table" == toString(ty));
|
||||
}
|
||||
|
||||
SUBCASE("not_top_table_or_metatable")
|
||||
{
|
||||
BuiltinsFixture fixture;
|
||||
registerHiddenTypes(&fixture.frontend);
|
||||
fixture.check(R"(
|
||||
type Ty = Not<tbl> | typeof(setmetatable({}, {}))
|
||||
)");
|
||||
|
||||
TypeId ty = reductionof(fixture.requireTypeAlias("Ty"));
|
||||
CHECK("{ @metatable { }, { } } | ~table" == toString(ty));
|
||||
}
|
||||
} // unions_with_negations
|
||||
|
||||
TEST_CASE_FIXTURE(ReductionFixture, "tables")
|
||||
|
@ -11,14 +11,7 @@ AstQuery::getDocumentationSymbolAtPosition.overloaded_class_method
|
||||
AstQuery::getDocumentationSymbolAtPosition.overloaded_fn
|
||||
AstQuery::getDocumentationSymbolAtPosition.table_overloaded_function_prop
|
||||
AutocompleteTest.autocomplete_first_function_arg_expected_type
|
||||
AutocompleteTest.autocomplete_interpolated_string_as_singleton
|
||||
AutocompleteTest.autocomplete_interpolated_string_constant
|
||||
AutocompleteTest.autocomplete_interpolated_string_expression
|
||||
AutocompleteTest.autocomplete_interpolated_string_expression_with_comments
|
||||
AutocompleteTest.autocomplete_oop_implicit_self
|
||||
AutocompleteTest.autocomplete_string_singleton_escape
|
||||
AutocompleteTest.autocomplete_string_singletons
|
||||
AutocompleteTest.autocompleteProp_index_function_metamethod_is_variadic
|
||||
AutocompleteTest.do_compatible_self_calls
|
||||
AutocompleteTest.do_wrong_compatible_self_calls
|
||||
AutocompleteTest.keyword_methods
|
||||
@ -34,12 +27,7 @@ AutocompleteTest.type_correct_expected_argument_type_suggestion_optional
|
||||
AutocompleteTest.type_correct_expected_argument_type_suggestion_self
|
||||
AutocompleteTest.type_correct_expected_return_type_pack_suggestion
|
||||
AutocompleteTest.type_correct_expected_return_type_suggestion
|
||||
AutocompleteTest.type_correct_function_no_parenthesis
|
||||
AutocompleteTest.type_correct_function_return_types
|
||||
AutocompleteTest.type_correct_keywords
|
||||
AutocompleteTest.type_correct_suggestion_for_overloads
|
||||
AutocompleteTest.type_correct_suggestion_in_argument
|
||||
AutocompleteTest.type_correct_suggestion_in_table
|
||||
BuiltinTests.aliased_string_format
|
||||
BuiltinTests.assert_removes_falsy_types
|
||||
BuiltinTests.assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type
|
||||
@ -49,20 +37,13 @@ BuiltinTests.coroutine_wrap_anything_goes
|
||||
BuiltinTests.debug_info_is_crazy
|
||||
BuiltinTests.debug_traceback_is_crazy
|
||||
BuiltinTests.dont_add_definitions_to_persistent_types
|
||||
BuiltinTests.find_capture_types
|
||||
BuiltinTests.find_capture_types2
|
||||
BuiltinTests.find_capture_types3
|
||||
BuiltinTests.gmatch_definition
|
||||
BuiltinTests.ipairs_iterator_should_infer_types_and_type_check
|
||||
BuiltinTests.match_capture_types
|
||||
BuiltinTests.match_capture_types2
|
||||
BuiltinTests.math_max_checks_for_numbers
|
||||
BuiltinTests.next_iterator_should_infer_types_and_type_check
|
||||
BuiltinTests.pairs_iterator_should_infer_types_and_type_check
|
||||
BuiltinTests.see_thru_select
|
||||
BuiltinTests.select_slightly_out_of_range
|
||||
BuiltinTests.select_way_out_of_range
|
||||
BuiltinTests.select_with_decimal_argument_is_rounded_down
|
||||
BuiltinTests.set_metatable_needs_arguments
|
||||
BuiltinTests.setmetatable_should_not_mutate_persisted_types
|
||||
BuiltinTests.sort_with_bad_predicate
|
||||
@ -78,19 +59,16 @@ BuiltinTests.table_pack_reduce
|
||||
BuiltinTests.table_pack_variadic
|
||||
DefinitionTests.class_definition_overload_metamethods
|
||||
DefinitionTests.class_definition_string_props
|
||||
DefinitionTests.declaring_generic_functions
|
||||
DefinitionTests.definition_file_classes
|
||||
DefinitionTests.definitions_symbols_are_generated_for_recursively_referenced_types
|
||||
DefinitionTests.single_class_type_identity_in_global_types
|
||||
FrontendTest.environments
|
||||
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
|
||||
FrontendTest.nocheck_cycle_used_by_checked
|
||||
FrontendTest.reexport_cyclic_type
|
||||
FrontendTest.trace_requires_in_nonstrict_mode
|
||||
GenericsTests.apply_type_function_nested_generics1
|
||||
GenericsTests.apply_type_function_nested_generics2
|
||||
GenericsTests.better_mismatch_error_messages
|
||||
GenericsTests.check_generic_typepack_function
|
||||
GenericsTests.check_mutual_generic_functions
|
||||
GenericsTests.correctly_instantiate_polymorphic_member_functions
|
||||
GenericsTests.do_not_infer_generic_functions
|
||||
@ -176,7 +154,6 @@ RefinementTest.x_is_not_instance_or_else_not_part
|
||||
RuntimeLimits.typescript_port_of_Result_type
|
||||
TableTests.a_free_shape_can_turn_into_a_scalar_directly
|
||||
TableTests.a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible
|
||||
TableTests.access_index_metamethod_that_returns_variadic
|
||||
TableTests.accidentally_checked_prop_in_opposite_branch
|
||||
TableTests.builtin_table_names
|
||||
TableTests.call_method
|
||||
@ -196,14 +173,12 @@ TableTests.expected_indexer_from_table_union
|
||||
TableTests.expected_indexer_value_type_extra
|
||||
TableTests.expected_indexer_value_type_extra_2
|
||||
TableTests.explicitly_typed_table
|
||||
TableTests.explicitly_typed_table_error
|
||||
TableTests.explicitly_typed_table_with_indexer
|
||||
TableTests.found_like_key_in_table_function_call
|
||||
TableTests.found_like_key_in_table_property_access
|
||||
TableTests.found_multiple_like_keys
|
||||
TableTests.function_calls_produces_sealed_table_given_unsealed_table
|
||||
TableTests.generic_table_instantiation_potential_regression
|
||||
TableTests.getmetatable_returns_pointer_to_metatable
|
||||
TableTests.give_up_after_one_metatable_index_look_up
|
||||
TableTests.indexer_on_sealed_table_must_unify_with_free_table
|
||||
TableTests.indexing_from_a_table_should_prefer_properties_when_possible
|
||||
@ -217,8 +192,6 @@ TableTests.invariant_table_properties_means_instantiating_tables_in_assignment_i
|
||||
TableTests.invariant_table_properties_means_instantiating_tables_in_call_is_unsound
|
||||
TableTests.leaking_bad_metatable_errors
|
||||
TableTests.less_exponential_blowup_please
|
||||
TableTests.meta_add_inferred
|
||||
TableTests.metatable_mismatch_should_fail
|
||||
TableTests.missing_metatable_for_sealed_tables_do_not_get_inferred
|
||||
TableTests.mixed_tables_with_implicit_numbered_keys
|
||||
TableTests.nil_assign_doesnt_hit_indexer
|
||||
@ -228,7 +201,6 @@ TableTests.only_ascribe_synthetic_names_at_module_scope
|
||||
TableTests.oop_indexer_works
|
||||
TableTests.oop_polymorphic
|
||||
TableTests.open_table_unification_2
|
||||
TableTests.property_lookup_through_tabletypevar_metatable
|
||||
TableTests.quantify_even_that_table_was_never_exported_at_all
|
||||
TableTests.quantify_metatables_of_metatables_of_table
|
||||
TableTests.quantifying_a_bound_var_works
|
||||
@ -248,7 +220,6 @@ TableTests.table_param_row_polymorphism_3
|
||||
TableTests.table_simple_call
|
||||
TableTests.table_subtyping_with_extra_props_dont_report_multiple_errors
|
||||
TableTests.table_subtyping_with_missing_props_dont_report_multiple_errors
|
||||
TableTests.tables_get_names_from_their_locals
|
||||
TableTests.tc_member_function
|
||||
TableTests.tc_member_function_2
|
||||
TableTests.unification_of_unions_in_a_self_referential_type
|
||||
@ -288,7 +259,6 @@ TypeAliases.mutually_recursive_types_restriction_not_ok_2
|
||||
TypeAliases.mutually_recursive_types_swapsies_not_ok
|
||||
TypeAliases.recursive_types_restriction_not_ok
|
||||
TypeAliases.report_shadowed_aliases
|
||||
TypeAliases.stringify_optional_parameterized_alias
|
||||
TypeAliases.stringify_type_alias_of_recursive_template_table_type
|
||||
TypeAliases.type_alias_local_mutation
|
||||
TypeAliases.type_alias_local_rename
|
||||
@ -308,12 +278,9 @@ TypeInfer.no_stack_overflow_from_isoptional
|
||||
TypeInfer.no_stack_overflow_from_isoptional2
|
||||
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
|
||||
TypeInfer.tc_if_else_expressions_expected_type_3
|
||||
TypeInfer.tc_interpolated_string_basic
|
||||
TypeInfer.tc_interpolated_string_with_invalid_expression
|
||||
TypeInfer.type_infer_recursion_limit_no_ice
|
||||
TypeInfer.type_infer_recursion_limit_normalizer
|
||||
TypeInferAnyError.for_in_loop_iterator_is_any2
|
||||
TypeInferAnyError.metatable_of_any_can_be_a_table
|
||||
TypeInferClasses.can_read_prop_of_base_class_using_string
|
||||
TypeInferClasses.class_type_mismatch_with_name_conflict
|
||||
TypeInferClasses.classes_without_overloaded_operators_cannot_be_added
|
||||
@ -355,7 +322,6 @@ TypeInferFunctions.too_many_arguments_error_location
|
||||
TypeInferFunctions.too_many_return_values_in_parentheses
|
||||
TypeInferFunctions.too_many_return_values_no_function
|
||||
TypeInferFunctions.vararg_function_is_quantified
|
||||
TypeInferLoops.for_in_loop
|
||||
TypeInferLoops.for_in_loop_error_on_factory_not_returning_the_right_amount_of_values
|
||||
TypeInferLoops.for_in_loop_with_next
|
||||
TypeInferLoops.for_in_with_generic_next
|
||||
@ -393,6 +359,7 @@ TypeInferOperators.infer_any_in_all_modes_when_lhs_is_unknown
|
||||
TypeInferOperators.operator_eq_completely_incompatible
|
||||
TypeInferOperators.or_joins_types_with_no_superfluous_union
|
||||
TypeInferOperators.produce_the_correct_error_message_when_comparing_a_table_with_a_metatable_with_one_that_does_not
|
||||
TypeInferOperators.refine_and_or
|
||||
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection
|
||||
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection_on_rhs
|
||||
TypeInferOperators.UnknownGlobalCompoundAssign
|
||||
@ -435,8 +402,6 @@ TypePackTests.unify_variadic_tails_in_arguments
|
||||
TypePackTests.unify_variadic_tails_in_arguments_free
|
||||
TypePackTests.variadic_packs
|
||||
TypeReductionTests.negations
|
||||
TypeSingletons.error_detailed_tagged_union_mismatch_bool
|
||||
TypeSingletons.error_detailed_tagged_union_mismatch_string
|
||||
TypeSingletons.function_call_with_singletons
|
||||
TypeSingletons.function_call_with_singletons_mismatch
|
||||
TypeSingletons.indexing_on_string_singletons
|
||||
@ -451,8 +416,6 @@ TypeSingletons.taking_the_length_of_union_of_string_singleton
|
||||
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
|
||||
TypeSingletons.widening_happens_almost_everywhere
|
||||
TypeSingletons.widening_happens_almost_everywhere_except_for_tables
|
||||
UnionTypes.error_detailed_optional
|
||||
UnionTypes.error_detailed_union_all
|
||||
UnionTypes.index_on_a_union_type_with_missing_property
|
||||
UnionTypes.index_on_a_union_type_with_one_optional_property
|
||||
UnionTypes.index_on_a_union_type_with_one_property_of_type_any
|
||||
|
Loading…
Reference in New Issue
Block a user