Sync to upstream/release/556

This commit is contained in:
Andy Friesen 2022-12-09 10:07:25 -08:00
parent fc459699da
commit abe6768a1d
53 changed files with 1103 additions and 922 deletions

View file

@ -1,13 +1,18 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Frontend.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypeVar.h"
#include <optional>
namespace Luau
{
struct Frontend;
struct TypeChecker;
struct TypeArena;
void registerBuiltinTypes(Frontend& frontend);
void registerBuiltinGlobals(TypeChecker& typeChecker);

View file

@ -3,6 +3,7 @@
#include "Luau/Ast.h" // Used for some of the enumerations
#include "Luau/Def.h"
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"
@ -67,6 +68,12 @@ struct BinaryConstraint
TypeId leftType;
TypeId rightType;
TypeId resultType;
// When we dispatch this constraint, we update the key at this map to record
// the overload that we selected.
AstExpr* expr;
DenseHashMap<const AstExpr*, TypeId>* astOriginalCallTypes;
DenseHashMap<const AstExpr*, TypeId>* astOverloadResolvedTypes;
};
// iteratee is iterable

View file

@ -76,15 +76,27 @@ struct ConstraintGraphBuilder
// A mapping of AST node to TypeId.
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
// A mapping of AST node to TypePackId.
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
// If the node was applied as a function, this is the unspecialized type of
// that expression.
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
// If overload resolution was performed on this element, this is the
// overload that was selected.
DenseHashMap<const AstExpr*, TypeId> astOverloadResolvedTypes{nullptr};
// Types resolved from type annotations. Analogous to astTypes.
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
// Type packs resolved from type annotations. Analogous to astTypePacks.
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
// Defining scopes for AST nodes.
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
NotNull<const DataFlowGraph> dfg;
ConnectiveArena connectiveArena;

View file

@ -2,12 +2,13 @@
#pragma once
#include "Luau/Error.h"
#include "Luau/Variant.h"
#include "Luau/Constraint.h"
#include "Luau/TypeVar.h"
#include "Luau/ToString.h"
#include "Luau/Error.h"
#include "Luau/Module.h"
#include "Luau/Normalize.h"
#include "Luau/ToString.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"
#include <vector>

View file

@ -1,16 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/FileResolver.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"
#include "Luau/TypeArena.h"
namespace Luau
{
struct TypeError;
struct FileResolver;
struct TypeArena;
struct TypeError;
struct TypeMismatch
{

View file

@ -4,6 +4,7 @@
#include "Luau/Location.h"
#include <memory>
#include <string>
#include <vector>
namespace Luau

View file

@ -42,6 +42,7 @@ public:
void retain(const TypeIds& tys);
void clear();
TypeId front() const;
iterator begin();
iterator end();
const_iterator begin() const;
@ -107,18 +108,7 @@ namespace Luau
/** A normalized string type is either `string` (represented by `nullopt`) or a
* union of string singletons.
*
* When FFlagLuauNegatedStringSingletons is unset, the representation is as
* follows:
*
* * The `string` data type is represented by the option `singletons` having the
* value `std::nullopt`.
* * The type `never` is represented by `singletons` being populated with an
* empty map.
* * A union of string singletons is represented by a map populated by the names
* and TypeIds of the singletons contained therein.
*
* When FFlagLuauNegatedStringSingletons is set, the representation is as
* follows:
* The representation is as follows:
*
* * A union of string singletons is finite and includes the singletons named by
* the `singletons` field.
@ -138,9 +128,7 @@ struct NormalizedStringType
// eg string & ~"a" & ~"b" & ...
bool isCofinite = false;
// TODO: This field cannot be nullopt when FFlagLuauNegatedStringSingletons
// is set. When clipping that flag, we can remove the wrapping optional.
std::optional<std::map<std::string, TypeId>> singletons;
std::map<std::string, TypeId> singletons;
void resetToString();
void resetToNever();
@ -161,8 +149,8 @@ struct NormalizedStringType
static const NormalizedStringType never;
NormalizedStringType() = default;
NormalizedStringType(bool isCofinite, std::optional<std::map<std::string, TypeId>> singletons);
NormalizedStringType();
NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons);
};
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);

View file

@ -1,7 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/Location.h"
#include "Luau/NotNull.h"
#include "Luau/TypeVar.h"

View file

@ -2,13 +2,12 @@
#pragma once
#include "Luau/Common.h"
#include "Luau/TypeVar.h"
#include "Luau/ConstraintGraphBuilder.h"
#include <unordered_map>
#include <optional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
@ -16,6 +15,22 @@ LUAU_FASTINT(LuauTypeMaximumStringifierLength)
namespace Luau
{
class AstExpr;
struct Scope;
struct TypeVar;
using TypeId = const TypeVar*;
struct TypePackVar;
using TypePackId = const TypePackVar*;
struct FunctionTypeVar;
struct Constraint;
struct Position;
struct Location;
struct ToStringNameMap
{
std::unordered_map<TypeId, std::string> typeVars;
@ -125,4 +140,7 @@ std::string dump(const std::shared_ptr<Scope>& scope, const char* name);
std::string generateName(size_t n);
std::string toString(const Position& position);
std::string toString(const Location& location);
} // namespace Luau

View file

@ -1,12 +1,12 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <memory>
#include <unordered_map>
#include "Luau/TypeVar.h"
#include "Luau/TypePack.h"
#include <memory>
#include <unordered_map>
namespace Luau
{

View file

@ -4,6 +4,7 @@
#include "Luau/Error.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/TypePack.h"
#include <memory>
#include <optional>
@ -12,6 +13,7 @@ namespace Luau
{
struct TxnLog;
struct TypeArena;
using ScopePtr = std::shared_ptr<struct Scope>;
@ -19,8 +21,6 @@ std::optional<TypeId> findMetatableEntry(
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId type, const std::string& entry, Location location);
std::optional<TypeId> findTablePropertyRespectingMeta(
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId ty, const std::string& name, Location location);
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle);
// Returns the minimum and maximum number of types the argument list can accept.
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics = false);

View file

@ -264,12 +264,14 @@ using DcrMagicFunction = bool (*)(MagicFunctionCallContext);
struct MagicRefinementContext
{
ScopePtr scope;
NotNull<struct ConstraintGraphBuilder> cgb;
NotNull<const DataFlowGraph> dfg;
NotNull<ConnectiveArena> connectiveArena;
std::vector<ConnectiveId> argumentConnectives;
const class AstExprCall* callSite;
};
using DcrMagicRefinement = std::vector<ConnectiveId> (*)(MagicRefinementContext);
using DcrMagicRefinement = std::vector<ConnectiveId> (*)(const MagicRefinementContext&);
struct FunctionTypeVar
{
@ -666,9 +668,6 @@ public:
const TypePackId errorTypePack;
};
// Clip with FFlagLuauNoMoreGlobalSingletonTypes
SingletonTypes& DEPRECATED_getSingletonTypes();
void persist(TypeId ty);
void persist(TypePackId tp);

View file

@ -6,6 +6,8 @@
#include "Luau/StringUtils.h"
#include "Luau/Common.h"
#include <math.h>
namespace Luau
{
@ -103,9 +105,28 @@ struct AstJsonEncoder : public AstVisitor
void write(double d)
{
char b[32];
snprintf(b, sizeof(b), "%.17g", d);
writeRaw(b);
switch (fpclassify(d))
{
case FP_INFINITE:
if (d < 0)
writeRaw("-Infinity");
else
writeRaw("Infinity");
break;
case FP_NAN:
writeRaw("NaN");
break;
case FP_NORMAL:
case FP_SUBNORMAL:
case FP_ZERO:
default:
char b[32];
snprintf(b, sizeof(b), "%.17g", d);
writeRaw(b);
break;
}
}
void writeString(std::string_view sv)

View file

@ -11,6 +11,8 @@
#include <algorithm>
LUAU_FASTFLAG(LuauCompleteTableKeysBetter);
namespace Luau
{
@ -29,12 +31,24 @@ struct AutocompleteNodeFinder : public AstVisitor
bool visit(AstExpr* expr) override
{
if (expr->location.begin < pos && pos <= expr->location.end)
if (FFlag::LuauCompleteTableKeysBetter)
{
ancestry.push_back(expr);
return true;
if (expr->location.begin <= pos && pos <= expr->location.end)
{
ancestry.push_back(expr);
return true;
}
return false;
}
else
{
if (expr->location.begin < pos && pos <= expr->location.end)
{
ancestry.push_back(expr);
return true;
}
return false;
}
return false;
}
bool visit(AstStat* stat) override

View file

@ -12,6 +12,8 @@
#include <unordered_set>
#include <utility>
LUAU_FASTFLAGVARIABLE(LuauCompleteTableKeysBetter, false);
static const std::unordered_set<std::string> kStatementStartingKeywords = {
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
@ -966,13 +968,28 @@ T* extractStat(const std::vector<AstNode*>& ancestry)
if (!parent)
return nullptr;
if (T* t = parent->as<T>(); t && parent->is<AstStatBlock>())
return t;
AstNode* grandParent = ancestry.size() >= 3 ? ancestry.rbegin()[2] : nullptr;
AstNode* greatGrandParent = ancestry.size() >= 4 ? ancestry.rbegin()[3] : nullptr;
if (!grandParent || !greatGrandParent)
return nullptr;
if (FFlag::LuauCompleteTableKeysBetter)
{
if (!grandParent)
return nullptr;
if (T* t = parent->as<T>(); t && grandParent->is<AstStatBlock>())
return t;
if (!greatGrandParent)
return nullptr;
}
else
{
if (T* t = parent->as<T>(); t && parent->is<AstStatBlock>())
return t;
if (!grandParent || !greatGrandParent)
return nullptr;
}
if (T* t = greatGrandParent->as<T>(); t && grandParent->is<AstStatBlock>() && parent->is<AstStatError>() && isIdentifier(node))
return t;
@ -1469,6 +1486,26 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
{
auto result = autocompleteProps(*module, &typeArena, singletonTypes, *it, PropIndexType::Key, ancestry);
if (FFlag::LuauCompleteTableKeysBetter)
{
if (auto nodeIt = module->astExpectedTypes.find(node->asExpr()))
autocompleteStringSingleton(*nodeIt, !node->is<AstExprConstantString>(), result);
if (!key)
{
// If there is "no key," it may be that the user
// intends for the current token to be the key, but
// has yet to type the `=` sign.
//
// If the key type is a union of singleton strings,
// suggest those too.
if (auto ttv = get<TableTypeVar>(follow(*it)); ttv && ttv->indexer)
{
autocompleteStringSingleton(ttv->indexer->indexType, false, result);
}
}
}
// Remove keys that are already completed
for (const auto& item : exprTable->items)
{

View file

@ -7,6 +7,7 @@
#include "Luau/Common.h"
#include "Luau/ToString.h"
#include "Luau/ConstraintSolver.h"
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
@ -46,6 +47,8 @@ static bool dcrMagicFunctionSelect(MagicFunctionCallContext context);
static bool dcrMagicFunctionRequire(MagicFunctionCallContext context);
static bool dcrMagicFunctionPack(MagicFunctionCallContext context);
static std::vector<ConnectiveId> dcrMagicRefinementAssert(const MagicRefinementContext& context);
TypeId makeUnion(TypeArena& arena, std::vector<TypeId>&& types)
{
return arena.addType(UnionTypeVar{std::move(types)});
@ -478,6 +481,7 @@ void registerBuiltinGlobals(Frontend& frontend)
}
attachMagicFunction(getGlobalBinding(frontend, "assert"), magicFunctionAssert);
attachDcrMagicRefinement(getGlobalBinding(frontend, "assert"), dcrMagicRefinementAssert);
attachMagicFunction(getGlobalBinding(frontend, "setmetatable"), magicFunctionSetMetaTable);
attachMagicFunction(getGlobalBinding(frontend, "select"), magicFunctionSelect);
attachDcrMagicFunction(getGlobalBinding(frontend, "select"), dcrMagicFunctionSelect);
@ -703,6 +707,15 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionAssert(
return WithPredicate<TypePackId>{arena.addTypePack(TypePack{std::move(head), tail})};
}
static std::vector<ConnectiveId> dcrMagicRefinementAssert(const MagicRefinementContext& ctx)
{
if (ctx.argumentConnectives.empty())
return {};
ctx.cgb->applyRefinements(ctx.scope, ctx.callSite->location, ctx.argumentConnectives[0]);
return {};
}
static std::optional<WithPredicate<TypePackId>> magicFunctionPack(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
{

View file

@ -2,16 +2,12 @@
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/Ast.h"
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/Constraint.h"
#include "Luau/DcrLogger.h"
#include "Luau/ModuleResolver.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
#include "Luau/Substitution.h"
#include "Luau/ToString.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeUtils.h"
#include "Luau/TypeVar.h"
@ -1068,16 +1064,9 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
else
expectedArgs = extendTypePack(*arena, singletonTypes, expectedArgPack, exprArgs.size() - 1);
std::vector<ConnectiveId> connectives;
if (auto ftv = get<FunctionTypeVar>(follow(fnType)); ftv && ftv->dcrMagicRefinement)
{
MagicRefinementContext ctx{globalScope, dfg, NotNull{&connectiveArena}, call};
connectives = ftv->dcrMagicRefinement(ctx);
}
std::vector<TypeId> args;
std::optional<TypePackId> argTail;
std::vector<ConnectiveId> argumentConnectives;
Checkpoint argCheckpoint = checkpoint(this);
@ -1101,7 +1090,11 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
args.push_back(arena->freshType(scope.get()));
}
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
args.push_back(check(scope, arg, expectedType).ty);
{
auto [ty, connective] = check(scope, arg, expectedType);
args.push_back(ty);
argumentConnectives.push_back(connective);
}
else
argTail = checkPack(scope, arg, {}).tp; // FIXME? not sure about expectedTypes here
}
@ -1114,6 +1107,13 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
constraint->dependencies.push_back(extractArgsConstraint);
});
std::vector<ConnectiveId> returnConnectives;
if (auto ftv = get<FunctionTypeVar>(follow(fnType)); ftv && ftv->dcrMagicRefinement)
{
MagicRefinementContext ctx{scope, NotNull{this}, dfg, NotNull{&connectiveArena}, std::move(argumentConnectives), call};
returnConnectives = ftv->dcrMagicRefinement(ctx);
}
if (matchSetmetatable(*call))
{
TypePack argTailPack;
@ -1133,7 +1133,7 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
scope->bindings[targetLocal->local].typeId = resultTy;
return InferencePack{arena->addTypePack({resultTy}), std::move(connectives)};
return InferencePack{arena->addTypePack({resultTy}), std::move(returnConnectives)};
}
else
{
@ -1172,7 +1172,7 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
fcc->dependencies.emplace_back(constraint.get());
});
return InferencePack{rets, std::move(connectives)};
return InferencePack{rets, std::move(returnConnectives)};
}
}
@ -1468,16 +1468,22 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* bi
auto [leftType, rightType, connective] = checkBinary(scope, binary, expectedType);
TypeId resultType = arena->addType(BlockedTypeVar{});
addConstraint(scope, binary->location, BinaryConstraint{binary->op, leftType, rightType, resultType});
addConstraint(scope, binary->location, BinaryConstraint{binary->op, leftType, rightType, resultType, binary, &astOriginalCallTypes, &astOverloadResolvedTypes});
return Inference{resultType, std::move(connective)};
}
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType)
{
check(scope, ifElse->condition);
ScopePtr condScope = childScope(ifElse->condition, scope);
auto [_, connective] = check(scope, ifElse->condition);
TypeId thenType = check(scope, ifElse->trueExpr, expectedType).ty;
TypeId elseType = check(scope, ifElse->falseExpr, expectedType).ty;
ScopePtr thenScope = childScope(ifElse->trueExpr, scope);
applyRefinements(thenScope, ifElse->trueExpr->location, connective);
TypeId thenType = check(thenScope, ifElse->trueExpr, expectedType).ty;
ScopePtr elseScope = childScope(ifElse->falseExpr, scope);
applyRefinements(elseScope, ifElse->falseExpr->location, connectiveArena.negation(connective));
TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty;
if (ifElse->hasElse)
{
@ -1807,7 +1813,9 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
signatureScope->privateTypePackBindings[name] = g.tp;
}
expectedType.reset();
// Local variable works around an odd gcc 11.3 warning: <anonymous> may be used uninitialized
std::optional<TypeId> none = std::nullopt;
expectedType = none;
}
std::vector<TypeId> argTypes;

View file

@ -15,7 +15,6 @@
#include "Luau/TypeVar.h"
#include "Luau/Unifier.h"
#include "Luau/VisitTypeVar.h"
#include "Luau/TypeUtils.h"
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
@ -635,9 +634,17 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
if (mm)
{
Instantiation instantiation{TxnLog::empty(), arena, TypeLevel{}, constraint->scope};
std::optional<TypeId> instantiatedMm = instantiation.substitute(*mm);
if (!instantiatedMm)
{
reportError(CodeTooComplex{}, constraint->location);
return true;
}
// TODO: Is a table with __call legal here?
// TODO: Overloads
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(*mm)))
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(*instantiatedMm)))
{
TypePackId inferredArgs;
// For >= and > we invoke __lt and __le respectively with
@ -673,6 +680,9 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
asMutable(resultType)->ty.emplace<BoundTypeVar>(mmResult);
unblock(resultType);
(*c.astOriginalCallTypes)[c.expr] = *mm;
(*c.astOverloadResolvedTypes)[c.expr] = *instantiatedMm;
return true;
}
}
@ -743,19 +753,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
{
TypeId leftFilteredTy = arena->addType(IntersectionTypeVar{{singletonTypes->falsyType, leftType}});
// TODO: normaliztion here should be replaced by a more limited 'simplification'
const NormalizedType* normalized = normalizer->normalize(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}}));
if (!normalized)
{
reportError(CodeTooComplex{}, constraint->location);
asMutable(resultType)->ty.emplace<BoundTypeVar>(errorRecoveryType());
}
else
{
asMutable(resultType)->ty.emplace<BoundTypeVar>(normalizer->typeFromNormal(*normalized));
}
asMutable(resultType)->ty.emplace<BoundTypeVar>(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}}));
unblock(resultType);
return true;
}
@ -763,21 +761,9 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
// LHS is falsey.
case AstExprBinary::Op::Or:
{
TypeId rightFilteredTy = arena->addType(IntersectionTypeVar{{singletonTypes->truthyType, leftType}});
// TODO: normaliztion here should be replaced by a more limited 'simplification'
const NormalizedType* normalized = normalizer->normalize(arena->addType(UnionTypeVar{{rightFilteredTy, rightType}}));
if (!normalized)
{
reportError(CodeTooComplex{}, constraint->location);
asMutable(resultType)->ty.emplace<BoundTypeVar>(errorRecoveryType());
}
else
{
asMutable(resultType)->ty.emplace<BoundTypeVar>(normalizer->typeFromNormal(*normalized));
}
TypeId leftFilteredTy = arena->addType(IntersectionTypeVar{{singletonTypes->truthyType, leftType}});
asMutable(resultType)->ty.emplace<BoundTypeVar>(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}}));
unblock(resultType);
return true;
}

View file

@ -3,6 +3,7 @@
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/FileResolver.h"
#include "Luau/StringUtils.h"
#include "Luau/ToString.h"

View file

@ -21,16 +21,15 @@
#include <algorithm>
#include <chrono>
#include <stdexcept>
#include <string>
LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false)
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
LUAU_FASTFLAGVARIABLE(LuauFixMarkDirtyReverseDeps, false)
namespace Luau
{
@ -409,7 +408,7 @@ double getTimestamp()
} // namespace
Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options)
: singletonTypes(NotNull{FFlag::LuauNoMoreGlobalSingletonTypes ? &singletonTypes_ : &DEPRECATED_getSingletonTypes()})
: singletonTypes(NotNull{&singletonTypes_})
, fileResolver(fileResolver)
, moduleResolver(this)
, moduleResolverForAutocomplete(this)
@ -819,26 +818,13 @@ void Frontend::markDirty(const ModuleName& name, std::vector<ModuleName>* marked
sourceNode.dirtyModule = true;
sourceNode.dirtyModuleForAutocomplete = true;
if (FFlag::LuauFixMarkDirtyReverseDeps)
{
if (0 == reverseDeps.count(next))
continue;
if (0 == reverseDeps.count(next))
continue;
sourceModules.erase(next);
sourceModules.erase(next);
const std::vector<ModuleName>& dependents = reverseDeps[next];
queue.insert(queue.end(), dependents.begin(), dependents.end());
}
else
{
if (0 == reverseDeps.count(name))
continue;
sourceModules.erase(name);
const std::vector<ModuleName>& dependents = reverseDeps[name];
queue.insert(queue.end(), dependents.begin(), dependents.end());
}
const std::vector<ModuleName>& dependents = reverseDeps[next];
queue.insert(queue.end(), dependents.begin(), dependents.end());
}
}
@ -919,6 +905,7 @@ ModulePtr Frontend::check(
result->astTypes = std::move(cgb.astTypes);
result->astTypePacks = std::move(cgb.astTypePacks);
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
result->astOverloadResolvedTypes = std::move(cgb.astOverloadResolvedTypes);
result->astResolvedTypes = std::move(cgb.astResolvedTypes);
result->astResolvedTypePacks = std::move(cgb.astResolvedTypePacks);
result->type = sourceModule.type;

View file

@ -19,12 +19,11 @@ LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false);
LUAU_FASTFLAGVARIABLE(LuauTypeNormalization2, false);
LUAU_FASTFLAGVARIABLE(LuauNegatedStringSingletons, false);
LUAU_FASTFLAGVARIABLE(LuauNegatedFunctionTypes, false);
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAG(LuauOverloadedFunctionSubtypingPerf);
LUAU_FASTFLAG(LuauUninhabitedSubAnything)
LUAU_FASTFLAG(LuauUninhabitedSubAnything2)
namespace Luau
{
@ -46,6 +45,11 @@ void TypeIds::clear()
hash = 0;
}
TypeId TypeIds::front() const
{
return order.at(0);
}
TypeIds::iterator TypeIds::begin()
{
return order.begin();
@ -111,94 +115,68 @@ bool TypeIds::operator==(const TypeIds& there) const
return hash == there.hash && types == there.types;
}
NormalizedStringType::NormalizedStringType(bool isCofinite, std::optional<std::map<std::string, TypeId>> singletons)
NormalizedStringType::NormalizedStringType()
{}
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
: isCofinite(isCofinite)
, singletons(std::move(singletons))
{
if (!FFlag::LuauNegatedStringSingletons)
LUAU_ASSERT(!isCofinite);
}
void NormalizedStringType::resetToString()
{
if (FFlag::LuauNegatedStringSingletons)
{
isCofinite = true;
singletons->clear();
}
else
singletons.reset();
isCofinite = true;
singletons.clear();
}
void NormalizedStringType::resetToNever()
{
if (FFlag::LuauNegatedStringSingletons)
{
isCofinite = false;
singletons.emplace();
}
else
{
if (singletons)
singletons->clear();
else
singletons.emplace();
}
isCofinite = false;
singletons.clear();
}
bool NormalizedStringType::isNever() const
{
if (FFlag::LuauNegatedStringSingletons)
return !isCofinite && singletons->empty();
else
return singletons && singletons->empty();
return !isCofinite && singletons.empty();
}
bool NormalizedStringType::isString() const
{
if (FFlag::LuauNegatedStringSingletons)
return isCofinite && singletons->empty();
else
return !singletons;
return isCofinite && singletons.empty();
}
bool NormalizedStringType::isUnion() const
{
if (FFlag::LuauNegatedStringSingletons)
return !isCofinite;
else
return singletons.has_value();
return !isCofinite;
}
bool NormalizedStringType::isIntersection() const
{
if (FFlag::LuauNegatedStringSingletons)
return isCofinite;
else
return false;
return isCofinite;
}
bool NormalizedStringType::includes(const std::string& str) const
{
if (isString())
return true;
else if (isUnion() && singletons->count(str))
else if (isUnion() && singletons.count(str))
return true;
else if (isIntersection() && !singletons->count(str))
else if (isIntersection() && !singletons.count(str))
return true;
else
return false;
}
const NormalizedStringType NormalizedStringType::never{false, {{}}};
const NormalizedStringType NormalizedStringType::never;
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr)
{
if (subStr.isUnion() && superStr.isUnion())
{
for (auto [name, ty] : *subStr.singletons)
for (auto [name, ty] : subStr.singletons)
{
if (!superStr.singletons->count(name))
if (!superStr.singletons.count(name))
return false;
}
}
@ -251,17 +229,21 @@ static bool isShallowInhabited(const NormalizedType& norm)
bool isInhabited_DEPRECATED(const NormalizedType& norm)
{
LUAU_ASSERT(!FFlag::LuauUninhabitedSubAnything);
LUAU_ASSERT(!FFlag::LuauUninhabitedSubAnything2);
return isShallowInhabited(norm);
}
bool Normalizer::isInhabited(const NormalizedType* norm, std::unordered_set<TypeId> seen)
{
// If normalization failed, the type is complex, and so is more likely than not to be inhabited.
if (!norm)
return true;
if (!get<NeverTypeVar>(norm->tops) || !get<NeverTypeVar>(norm->booleans) || !get<NeverTypeVar>(norm->errors) ||
!get<NeverTypeVar>(norm->nils) || !get<NeverTypeVar>(norm->numbers) || !get<NeverTypeVar>(norm->threads) ||
!norm->classes.empty() || !norm->strings.isNever() || !norm->functions.isNever())
return true;
for (const auto& [_, intersect] : norm->tyvars)
{
if (isInhabited(intersect.get(), seen))
@ -372,7 +354,7 @@ static bool isNormalizedString(const NormalizedStringType& ty)
if (ty.isString())
return true;
for (auto& [str, ty] : *ty.singletons)
for (auto& [str, ty] : ty.singletons)
{
if (const SingletonTypeVar* stv = get<SingletonTypeVar>(ty))
{
@ -682,56 +664,46 @@ void Normalizer::unionClasses(TypeIds& heres, const TypeIds& theres)
void Normalizer::unionStrings(NormalizedStringType& here, const NormalizedStringType& there)
{
if (FFlag::LuauNegatedStringSingletons)
if (there.isString())
here.resetToString();
else if (here.isUnion() && there.isUnion())
here.singletons.insert(there.singletons.begin(), there.singletons.end());
else if (here.isUnion() && there.isIntersection())
{
if (there.isString())
here.resetToString();
else if (here.isUnion() && there.isUnion())
here.singletons->insert(there.singletons->begin(), there.singletons->end());
else if (here.isUnion() && there.isIntersection())
here.isCofinite = true;
for (const auto& pair : there.singletons)
{
here.isCofinite = true;
for (const auto& pair : *there.singletons)
{
auto it = here.singletons->find(pair.first);
if (it != end(*here.singletons))
here.singletons->erase(it);
else
here.singletons->insert(pair);
}
auto it = here.singletons.find(pair.first);
if (it != end(here.singletons))
here.singletons.erase(it);
else
here.singletons.insert(pair);
}
else if (here.isIntersection() && there.isUnion())
{
for (const auto& [name, ty] : *there.singletons)
here.singletons->erase(name);
}
else if (here.isIntersection() && there.isIntersection())
{
auto iter = begin(*here.singletons);
auto endIter = end(*here.singletons);
}
else if (here.isIntersection() && there.isUnion())
{
for (const auto& [name, ty] : there.singletons)
here.singletons.erase(name);
}
else if (here.isIntersection() && there.isIntersection())
{
auto iter = begin(here.singletons);
auto endIter = end(here.singletons);
while (iter != endIter)
while (iter != endIter)
{
if (!there.singletons.count(iter->first))
{
if (!there.singletons->count(iter->first))
{
auto eraseIt = iter;
++iter;
here.singletons->erase(eraseIt);
}
else
++iter;
auto eraseIt = iter;
++iter;
here.singletons.erase(eraseIt);
}
else
++iter;
}
else
LUAU_ASSERT(!"Unreachable");
}
else
{
if (there.isString())
here.resetToString();
else if (here.isUnion())
here.singletons->insert(there.singletons->begin(), there.singletons->end());
}
LUAU_ASSERT(!"Unreachable");
}
std::optional<TypePackId> Normalizer::unionOfTypePacks(TypePackId here, TypePackId there)
@ -1116,22 +1088,14 @@ bool Normalizer::unionNormalWithTy(NormalizedType& here, TypeId there, int ignor
here.booleans = unionOfBools(here.booleans, there);
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
{
if (FFlag::LuauNegatedStringSingletons)
if (here.strings.isCofinite)
{
if (here.strings.isCofinite)
{
auto it = here.strings.singletons->find(sstv->value);
if (it != here.strings.singletons->end())
here.strings.singletons->erase(it);
}
else
here.strings.singletons->insert({sstv->value, there});
auto it = here.strings.singletons.find(sstv->value);
if (it != here.strings.singletons.end())
here.strings.singletons.erase(it);
}
else
{
if (here.strings.isUnion())
here.strings.singletons->insert({sstv->value, there});
}
here.strings.singletons.insert({sstv->value, there});
}
else
LUAU_ASSERT(!"Unreachable");
@ -1278,7 +1242,6 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty)
here.threads = singletonTypes->neverType;
break;
case PrimitiveTypeVar::Function:
LUAU_ASSERT(FFlag::LuauNegatedStringSingletons);
here.functions.resetToNever();
break;
}
@ -1286,20 +1249,18 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty)
void Normalizer::subtractSingleton(NormalizedType& here, TypeId ty)
{
LUAU_ASSERT(FFlag::LuauNegatedStringSingletons);
const SingletonTypeVar* stv = get<SingletonTypeVar>(ty);
LUAU_ASSERT(stv);
if (const StringSingleton* ss = get<StringSingleton>(stv))
{
if (here.strings.isCofinite)
here.strings.singletons->insert({ss->value, ty});
here.strings.singletons.insert({ss->value, ty});
else
{
auto it = here.strings.singletons->find(ss->value);
if (it != here.strings.singletons->end())
here.strings.singletons->erase(it);
auto it = here.strings.singletons.find(ss->value);
if (it != here.strings.singletons.end())
here.strings.singletons.erase(it);
}
}
else if (const BooleanSingleton* bs = get<BooleanSingleton>(stv))
@ -1417,12 +1378,12 @@ void Normalizer::intersectStrings(NormalizedStringType& here, const NormalizedSt
if (here.isString())
here.resetToNever();
for (auto it = here.singletons->begin(); it != here.singletons->end();)
for (auto it = here.singletons.begin(); it != here.singletons.end();)
{
if (there.singletons->count(it->first))
if (there.singletons.count(it->first))
it++;
else
it = here.singletons->erase(it);
it = here.singletons.erase(it);
}
}
@ -2096,12 +2057,12 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there)
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
{
if (strings.includes(sstv->value))
here.strings.singletons->insert({sstv->value, there});
here.strings.singletons.insert({sstv->value, there});
}
else
LUAU_ASSERT(!"Unreachable");
}
else if (const NegationTypeVar* ntv = get<NegationTypeVar>(there); FFlag::LuauNegatedStringSingletons && ntv)
else if (const NegationTypeVar* ntv = get<NegationTypeVar>(there))
{
TypeId t = follow(ntv->ty);
if (const PrimitiveTypeVar* ptv = get<PrimitiveTypeVar>(t))
@ -2171,14 +2132,14 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
result.push_back(singletonTypes->stringType);
else if (norm.strings.isUnion())
{
for (auto& [_, ty] : *norm.strings.singletons)
for (auto& [_, ty] : norm.strings.singletons)
result.push_back(ty);
}
else if (FFlag::LuauNegatedStringSingletons && norm.strings.isIntersection())
else if (norm.strings.isIntersection())
{
std::vector<TypeId> parts;
parts.push_back(singletonTypes->stringType);
for (const auto& [name, ty] : *norm.strings.singletons)
for (const auto& [name, ty] : norm.strings.singletons)
parts.push_back(arena->addType(NegationTypeVar{ty}));
result.push_back(arena->addType(IntersectionTypeVar{std::move(parts)}));

View file

@ -1,6 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ToString.h"
#include "Luau/Constraint.h"
#include "Luau/Location.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
@ -1572,4 +1574,15 @@ std::optional<std::string> getFunctionNameAsString(const AstExpr& expr)
return s;
}
std::string toString(const Position& position)
{
return "{ line = " + std::to_string(position.line) + ", col = " + std::to_string(position.column) + " }";
}
std::string toString(const Location& location)
{
return "Location { " + toString(location.begin) + ", " + toString(location.end) + " }";
}
} // namespace Luau

View file

@ -2,6 +2,7 @@
#include "Luau/TxnLog.h"
#include "Luau/ToString.h"
#include "Luau/TypeArena.h"
#include "Luau/TypePack.h"
#include <algorithm>

View file

@ -90,6 +90,9 @@ struct TypeChecker2
std::vector<NotNull<Scope>> stack;
UnifierSharedState sharedState{&ice};
Normalizer normalizer{&module->internalTypes, singletonTypes, NotNull{&sharedState}};
TypeChecker2(NotNull<SingletonTypes> singletonTypes, DcrLogger* logger, const SourceModule* sourceModule, Module* module)
: singletonTypes(singletonTypes)
, logger(logger)
@ -298,8 +301,6 @@ struct TypeChecker2
TypeArena* arena = &module->internalTypes;
TypePackId actualRetType = reconstructPack(ret->list, *arena);
UnifierSharedState sharedState{&ice};
Normalizer normalizer{arena, singletonTypes, NotNull{&sharedState}};
Unifier u{NotNull{&normalizer}, Mode::Strict, stack.back(), ret->location, Covariant};
u.tryUnify(actualRetType, expectedRetType);
@ -921,7 +922,12 @@ struct TypeChecker2
void visit(AstExprIndexName* indexName)
{
TypeId leftType = lookupType(indexName->expr);
getIndexTypeFromType(module->getModuleScope(), leftType, indexName->index.value, indexName->location, /* addErrors */ true);
const NormalizedType* norm = normalizer.normalize(leftType);
if (!norm)
reportError(NormalizationTooComplex{}, indexName->indexLocation);
checkIndexTypeFromType(leftType, *norm, indexName->index.value, indexName->location);
}
void visit(AstExprIndexExpr* indexExpr)
@ -1109,11 +1115,18 @@ struct TypeChecker2
if (std::optional<TypeId> leftMm = findMetatableEntry(singletonTypes, module->errors, leftType, it->second, expr->left->location))
mm = leftMm;
else if (std::optional<TypeId> rightMm = findMetatableEntry(singletonTypes, module->errors, rightType, it->second, expr->right->location))
{
mm = rightMm;
std::swap(leftType, rightType);
}
if (mm)
{
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(*mm)))
TypeId instantiatedMm = module->astOverloadResolvedTypes[expr];
if (!instantiatedMm)
reportError(CodeTooComplex{}, expr->location);
else if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(instantiatedMm)))
{
TypePackId expectedArgs;
// For >= and > we invoke __lt and __le respectively with
@ -1545,9 +1558,7 @@ struct TypeChecker2
template<typename TID>
bool isSubtype(TID subTy, TID superTy, NotNull<Scope> scope)
{
UnifierSharedState sharedState{&ice};
TypeArena arena;
Normalizer normalizer{&arena, singletonTypes, NotNull{&sharedState}};
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
u.useScopes = true;
@ -1559,8 +1570,6 @@ struct TypeChecker2
template<typename TID>
ErrorVec tryUnify(NotNull<Scope> scope, const Location& location, TID subTy, TID superTy)
{
UnifierSharedState sharedState{&ice};
Normalizer normalizer{&module->internalTypes, singletonTypes, NotNull{&sharedState}};
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, location, Covariant};
u.useScopes = true;
u.tryUnify(subTy, superTy);
@ -1587,9 +1596,90 @@ struct TypeChecker2
reportError(std::move(e));
}
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, TypeId type, const std::string& prop, const Location& location, bool addErrors)
void checkIndexTypeFromType(TypeId denormalizedTy, const NormalizedType& norm, const std::string& prop, const Location& location)
{
return Luau::getIndexTypeFromType(scope, module->errors, &module->internalTypes, singletonTypes, type, prop, location, addErrors, ice);
bool foundOneProp = false;
std::vector<TypeId> typesMissingTheProp;
auto fetch = [&](TypeId ty) {
if (!normalizer.isInhabited(ty))
return;
bool found = hasIndexTypeFromType(ty, prop, location);
foundOneProp |= found;
if (!found)
typesMissingTheProp.push_back(ty);
};
fetch(norm.tops);
fetch(norm.booleans);
for (TypeId ty : norm.classes)
fetch(ty);
fetch(norm.errors);
fetch(norm.nils);
fetch(norm.numbers);
if (!norm.strings.isNever())
fetch(singletonTypes->stringType);
fetch(norm.threads);
for (TypeId ty : norm.tables)
fetch(ty);
if (norm.functions.isTop)
fetch(singletonTypes->functionType);
else if (!norm.functions.isNever())
{
if (norm.functions.parts->size() == 1)
fetch(norm.functions.parts->front());
else
{
std::vector<TypeId> parts;
parts.insert(parts.end(), norm.functions.parts->begin(), norm.functions.parts->end());
fetch(module->internalTypes.addType(IntersectionTypeVar{std::move(parts)}));
}
}
for (const auto& [tyvar, intersect] : norm.tyvars)
{
if (get<NeverTypeVar>(intersect->tops))
{
TypeId ty = normalizer.typeFromNormal(*intersect);
fetch(module->internalTypes.addType(IntersectionTypeVar{{tyvar, ty}}));
}
else
fetch(tyvar);
}
if (!typesMissingTheProp.empty())
{
if (foundOneProp)
reportError(TypeError{location, MissingUnionProperty{denormalizedTy, typesMissingTheProp, prop}});
else
reportError(TypeError{location, UnknownProperty{denormalizedTy, prop}});
}
}
bool hasIndexTypeFromType(TypeId ty, const std::string& prop, const Location& location)
{
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty) || get<NeverTypeVar>(ty))
return true;
if (isString(ty))
{
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(singletonTypes, module->errors, singletonTypes->stringType, "__index", location);
LUAU_ASSERT(mtIndex);
ty = *mtIndex;
}
if (getTableType(ty))
return bool(findTablePropertyRespectingMeta(singletonTypes, module->errors, ty, prop, location));
else if (const ClassTypeVar* cls = get<ClassTypeVar>(ty))
return bool(lookupClassProp(cls, prop));
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
ice.ice("getIndexTypeFromTypeHelper cannot take a UnionTypeVar");
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(ty))
return std::any_of(begin(itv), end(itv), [&](TypeId part) {
return hasIndexTypeFromType(part, prop, location);
});
else
return false;
}
};

View file

@ -52,7 +52,7 @@ LUAU_FASTFLAGVARIABLE(LuauIntersectionTestForEquality, false)
LUAU_FASTFLAGVARIABLE(LuauImplicitElseRefinement, false)
LUAU_FASTFLAGVARIABLE(LuauAllowIndexClassParameters, false)
LUAU_FASTFLAGVARIABLE(LuauDeclareClassPrototype, false)
LUAU_FASTFLAG(LuauUninhabitedSubAnything)
LUAU_FASTFLAG(LuauUninhabitedSubAnything2)
LUAU_FASTFLAGVARIABLE(LuauCallableClasses, false)
namespace Luau
@ -2691,7 +2691,7 @@ static std::optional<bool> areEqComparable(NotNull<TypeArena> arena, NotNull<Nor
if (!n)
return std::nullopt;
if (FFlag::LuauUninhabitedSubAnything)
if (FFlag::LuauUninhabitedSubAnything2)
return normalizer->isInhabited(n);
else
return isInhabited_DEPRECATED(*n);

View file

@ -88,103 +88,6 @@ std::optional<TypeId> findTablePropertyRespectingMeta(
return std::nullopt;
}
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle)
{
type = follow(type);
if (get<ErrorTypeVar>(type) || get<AnyTypeVar>(type) || get<NeverTypeVar>(type))
return type;
if (auto f = get<FreeTypeVar>(type))
*asMutable(type) = TableTypeVar{TableState::Free, f->level};
if (isString(type))
{
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(singletonTypes, errors, singletonTypes->stringType, "__index", location);
LUAU_ASSERT(mtIndex);
type = *mtIndex;
}
if (getTableType(type))
{
return findTablePropertyRespectingMeta(singletonTypes, errors, type, prop, location);
}
else if (const ClassTypeVar* cls = get<ClassTypeVar>(type))
{
if (const Property* p = lookupClassProp(cls, prop))
return p->type;
}
else if (const UnionTypeVar* utv = get<UnionTypeVar>(type))
{
std::vector<TypeId> goodOptions;
std::vector<TypeId> badOptions;
for (TypeId t : utv)
{
if (get<AnyTypeVar>(follow(t)))
return t;
if (std::optional<TypeId> ty =
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
goodOptions.push_back(*ty);
else
badOptions.push_back(t);
}
if (!badOptions.empty())
{
if (addErrors)
{
if (goodOptions.empty())
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
else
errors.push_back(TypeError{location, MissingUnionProperty{type, badOptions, prop}});
}
return std::nullopt;
}
goodOptions = reduceUnion(goodOptions);
if (goodOptions.empty())
return singletonTypes->neverType;
if (goodOptions.size() == 1)
return goodOptions[0];
return arena->addType(UnionTypeVar{std::move(goodOptions)});
}
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(type))
{
std::vector<TypeId> parts;
for (TypeId t : itv->parts)
{
if (std::optional<TypeId> ty =
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
parts.push_back(*ty);
}
// If no parts of the intersection had the property we looked up for, it never existed at all.
if (parts.empty())
{
if (addErrors)
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
return std::nullopt;
}
if (parts.size() == 1)
return parts[0];
return arena->addType(IntersectionTypeVar{std::move(parts)});
}
if (addErrors)
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
return std::nullopt;
}
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics)
{
size_t minCount = 0;

View file

@ -26,7 +26,6 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
LUAU_FASTFLAGVARIABLE(LuauNoMoreGlobalSingletonTypes, false)
LUAU_FASTFLAGVARIABLE(LuauNewLibraryTypeNames, false)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
@ -890,12 +889,6 @@ TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess)
return guess;
}
SingletonTypes& DEPRECATED_getSingletonTypes()
{
static SingletonTypes singletonTypes;
return singletonTypes;
}
void persist(TypeId ty)
{
std::deque<TypeId> queue{ty};

View file

@ -5,12 +5,13 @@
#include "Luau/Instantiation.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
#include "Luau/StringUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/ToString.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/TypeVar.h"
#include "Luau/VisitTypeVar.h"
#include "Luau/ToString.h"
#include <algorithm>
@ -23,7 +24,7 @@ LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false)
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
LUAU_FASTFLAGVARIABLE(LuauOverloadedFunctionSubtypingPerf, false);
LUAU_FASTFLAGVARIABLE(LuauScalarShapeUnifyToMtOwner2, false)
LUAU_FASTFLAGVARIABLE(LuauUninhabitedSubAnything, false)
LUAU_FASTFLAGVARIABLE(LuauUninhabitedSubAnything2, false)
LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
LUAU_FASTFLAG(LuauTxnLogTypePackIterator)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
@ -588,7 +589,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
else if (log.get<NegationTypeVar>(subTy))
tryUnifyNegationWithType(subTy, superTy);
else if (FFlag::LuauUninhabitedSubAnything && !normalizer->isInhabited(subTy))
else if (FFlag::LuauUninhabitedSubAnything2 && !normalizer->isInhabited(subTy))
{}
else
@ -1980,7 +1981,7 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
TypeId osubTy = subTy;
TypeId osuperTy = superTy;
if (FFlag::LuauUninhabitedSubAnything && !normalizer->isInhabited(subTy))
if (FFlag::LuauUninhabitedSubAnything2 && !normalizer->isInhabited(subTy))
return;
if (reversed)

View file

@ -5,6 +5,7 @@
#include <optional>
#include <functional>
#include <string>
#include <string.h>

View file

@ -1,8 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string>
namespace Luau
{
@ -10,130 +8,36 @@ struct Position
{
unsigned int line, column;
Position(unsigned int line, unsigned int column)
: line(line)
, column(column)
{
}
Position(unsigned int line, unsigned int column);
bool operator==(const Position& rhs) const
{
return this->column == rhs.column && this->line == rhs.line;
}
bool operator!=(const Position& rhs) const
{
return !(*this == rhs);
}
bool operator==(const Position& rhs) const;
bool operator!=(const Position& rhs) const;
bool operator<(const Position& rhs) const;
bool operator>(const Position& rhs) const;
bool operator<=(const Position& rhs) const;
bool operator>=(const Position& rhs) const;
bool operator<(const Position& rhs) const
{
if (line == rhs.line)
return column < rhs.column;
else
return line < rhs.line;
}
bool operator>(const Position& rhs) const
{
if (line == rhs.line)
return column > rhs.column;
else
return line > rhs.line;
}
bool operator<=(const Position& rhs) const
{
return *this == rhs || *this < rhs;
}
bool operator>=(const Position& rhs) const
{
return *this == rhs || *this > rhs;
}
void shift(const Position& start, const Position& oldEnd, const Position& newEnd)
{
if (*this >= start)
{
if (this->line > oldEnd.line)
this->line += (newEnd.line - oldEnd.line);
else
{
this->line = newEnd.line;
this->column += (newEnd.column - oldEnd.column);
}
}
}
void shift(const Position& start, const Position& oldEnd, const Position& newEnd);
};
struct Location
{
Position begin, end;
Location()
: begin(0, 0)
, end(0, 0)
{
}
Location();
Location(const Position& begin, const Position& end);
Location(const Position& begin, unsigned int length);
Location(const Location& begin, const Location& end);
Location(const Position& begin, const Position& end)
: begin(begin)
, end(end)
{
}
bool operator==(const Location& rhs) const;
bool operator!=(const Location& rhs) const;
Location(const Position& begin, unsigned int length)
: begin(begin)
, end(begin.line, begin.column + length)
{
}
Location(const Location& begin, const Location& end)
: begin(begin.begin)
, end(end.end)
{
}
bool operator==(const Location& rhs) const
{
return this->begin == rhs.begin && this->end == rhs.end;
}
bool operator!=(const Location& rhs) const
{
return !(*this == rhs);
}
bool encloses(const Location& l) const
{
return begin <= l.begin && end >= l.end;
}
bool overlaps(const Location& l) const
{
return (begin <= l.begin && end >= l.begin) || (begin <= l.end && end >= l.end) || (begin >= l.begin && end <= l.end);
}
bool contains(const Position& p) const
{
return begin <= p && p < end;
}
bool containsClosed(const Position& p) const
{
return begin <= p && p <= end;
}
void extend(const Location& other)
{
if (other.begin < begin)
begin = other.begin;
if (other.end > end)
end = other.end;
}
void shift(const Position& start, const Position& oldEnd, const Position& newEnd)
{
begin.shift(start, oldEnd, newEnd);
end.shift(start, oldEnd, newEnd);
}
bool encloses(const Location& l) const;
bool overlaps(const Location& l) const;
bool contains(const Position& p) const;
bool containsClosed(const Position& p) const;
void extend(const Location& other);
void shift(const Position& start, const Position& oldEnd, const Position& newEnd);
};
std::string toString(const Position& position);
std::string toString(const Location& location);
} // namespace Luau

View file

@ -4,14 +4,128 @@
namespace Luau
{
std::string toString(const Position& position)
Position::Position(unsigned int line, unsigned int column)
: line(line)
, column(column)
{
return "{ line = " + std::to_string(position.line) + ", col = " + std::to_string(position.column) + " }";
}
std::string toString(const Location& location)
bool Position::operator==(const Position& rhs) const
{
return "Location { " + toString(location.begin) + ", " + toString(location.end) + " }";
return this->column == rhs.column && this->line == rhs.line;
}
bool Position::operator!=(const Position& rhs) const
{
return !(*this == rhs);
}
bool Position::operator<(const Position& rhs) const
{
if (line == rhs.line)
return column < rhs.column;
else
return line < rhs.line;
}
bool Position::operator>(const Position& rhs) const
{
if (line == rhs.line)
return column > rhs.column;
else
return line > rhs.line;
}
bool Position::operator<=(const Position& rhs) const
{
return *this == rhs || *this < rhs;
}
bool Position::operator>=(const Position& rhs) const
{
return *this == rhs || *this > rhs;
}
void Position::shift(const Position& start, const Position& oldEnd, const Position& newEnd)
{
if (*this >= start)
{
if (this->line > oldEnd.line)
this->line += (newEnd.line - oldEnd.line);
else
{
this->line = newEnd.line;
this->column += (newEnd.column - oldEnd.column);
}
}
}
Location::Location()
: begin(0, 0)
, end(0, 0)
{
}
Location::Location(const Position& begin, const Position& end)
: begin(begin)
, end(end)
{
}
Location::Location(const Position& begin, unsigned int length)
: begin(begin)
, end(begin.line, begin.column + length)
{
}
Location::Location(const Location& begin, const Location& end)
: begin(begin.begin)
, end(end.end)
{
}
bool Location::operator==(const Location& rhs) const
{
return this->begin == rhs.begin && this->end == rhs.end;
}
bool Location::operator!=(const Location& rhs) const
{
return !(*this == rhs);
}
bool Location::encloses(const Location& l) const
{
return begin <= l.begin && end >= l.end;
}
bool Location::overlaps(const Location& l) const
{
return (begin <= l.begin && end >= l.begin) || (begin <= l.end && end >= l.end) || (begin >= l.begin && end <= l.end);
}
bool Location::contains(const Position& p) const
{
return begin <= p && p < end;
}
bool Location::containsClosed(const Position& p) const
{
return begin <= p && p <= end;
}
void Location::extend(const Location& other)
{
if (other.begin < begin)
begin = other.begin;
if (other.end > end)
end = other.end;
}
void Location::shift(const Position& start, const Position& oldEnd, const Position& newEnd)
{
begin.shift(start, oldEnd, newEnd);
end.shift(start, oldEnd, newEnd);
}
} // namespace Luau

View file

@ -6,6 +6,7 @@
#include "Luau/AstJsonEncoder.h"
#include "Luau/Parser.h"
#include "Luau/ParseOptions.h"
#include "Luau/ToString.h"
#include "FileUtils.h"

View file

@ -59,7 +59,7 @@ static void assembleHelpers(AssemblyBuilderX64& build, ModuleHelpers& helpers)
}
static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& helpers, Proto* proto, LuauOpcode op, const Instruction* pc, int i,
Label* labelarr, Label& fallback)
Label* labelarr, Label& next, Label& fallback)
{
int skip = 0;
@ -89,31 +89,31 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers&
emitInstGetGlobal(build, pc, i, fallback);
break;
case LOP_SETGLOBAL:
emitInstSetGlobal(build, pc, i, labelarr, fallback);
emitInstSetGlobal(build, pc, i, next, fallback);
break;
case LOP_CALL:
emitInstCall(build, helpers, pc, i, labelarr);
emitInstCall(build, helpers, pc, i);
break;
case LOP_RETURN:
emitInstReturn(build, helpers, pc, i, labelarr);
emitInstReturn(build, helpers, pc, i);
break;
case LOP_GETTABLE:
emitInstGetTable(build, pc, i, fallback);
emitInstGetTable(build, pc, fallback);
break;
case LOP_SETTABLE:
emitInstSetTable(build, pc, i, labelarr, fallback);
emitInstSetTable(build, pc, next, fallback);
break;
case LOP_GETTABLEKS:
emitInstGetTableKS(build, pc, i, fallback);
break;
case LOP_SETTABLEKS:
emitInstSetTableKS(build, pc, i, labelarr, fallback);
emitInstSetTableKS(build, pc, i, next, fallback);
break;
case LOP_GETTABLEN:
emitInstGetTableN(build, pc, i, fallback);
emitInstGetTableN(build, pc, fallback);
break;
case LOP_SETTABLEN:
emitInstSetTableN(build, pc, i, labelarr, fallback);
emitInstSetTableN(build, pc, next, fallback);
break;
case LOP_JUMP:
emitInstJump(build, pc, i, labelarr);
@ -161,94 +161,96 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers&
emitInstJumpxEqS(build, pc, i, labelarr);
break;
case LOP_ADD:
emitInstBinary(build, pc, i, TM_ADD, fallback);
emitInstBinary(build, pc, TM_ADD, fallback);
break;
case LOP_SUB:
emitInstBinary(build, pc, i, TM_SUB, fallback);
emitInstBinary(build, pc, TM_SUB, fallback);
break;
case LOP_MUL:
emitInstBinary(build, pc, i, TM_MUL, fallback);
emitInstBinary(build, pc, TM_MUL, fallback);
break;
case LOP_DIV:
emitInstBinary(build, pc, i, TM_DIV, fallback);
emitInstBinary(build, pc, TM_DIV, fallback);
break;
case LOP_MOD:
emitInstBinary(build, pc, i, TM_MOD, fallback);
emitInstBinary(build, pc, TM_MOD, fallback);
break;
case LOP_POW:
emitInstBinary(build, pc, i, TM_POW, fallback);
emitInstBinary(build, pc, TM_POW, fallback);
break;
case LOP_ADDK:
emitInstBinaryK(build, pc, i, TM_ADD, fallback);
emitInstBinaryK(build, pc, TM_ADD, fallback);
break;
case LOP_SUBK:
emitInstBinaryK(build, pc, i, TM_SUB, fallback);
emitInstBinaryK(build, pc, TM_SUB, fallback);
break;
case LOP_MULK:
emitInstBinaryK(build, pc, i, TM_MUL, fallback);
emitInstBinaryK(build, pc, TM_MUL, fallback);
break;
case LOP_DIVK:
emitInstBinaryK(build, pc, i, TM_DIV, fallback);
emitInstBinaryK(build, pc, TM_DIV, fallback);
break;
case LOP_MODK:
emitInstBinaryK(build, pc, i, TM_MOD, fallback);
emitInstBinaryK(build, pc, TM_MOD, fallback);
break;
case LOP_POWK:
emitInstPowK(build, pc, proto->k, i, fallback);
emitInstPowK(build, pc, proto->k, fallback);
break;
case LOP_NOT:
emitInstNot(build, pc);
break;
case LOP_MINUS:
emitInstMinus(build, pc, i, fallback);
emitInstMinus(build, pc, fallback);
break;
case LOP_LENGTH:
emitInstLength(build, pc, i, fallback);
emitInstLength(build, pc, fallback);
break;
case LOP_NEWTABLE:
emitInstNewTable(build, pc, i, labelarr);
emitInstNewTable(build, pc, i, next);
break;
case LOP_DUPTABLE:
emitInstDupTable(build, pc, i, labelarr);
emitInstDupTable(build, pc, i, next);
break;
case LOP_SETLIST:
emitInstSetList(build, pc, i, labelarr);
emitInstSetList(build, pc, next);
break;
case LOP_GETUPVAL:
emitInstGetUpval(build, pc, i);
emitInstGetUpval(build, pc);
break;
case LOP_SETUPVAL:
emitInstSetUpval(build, pc, i, labelarr);
emitInstSetUpval(build, pc, next);
break;
case LOP_CLOSEUPVALS:
emitInstCloseUpvals(build, pc, i, labelarr);
emitInstCloseUpvals(build, pc, next);
break;
case LOP_FASTCALL:
skip = emitInstFastCall(build, pc, i, labelarr);
// We want to lower next instruction at skip+2, but this instruction is only 1 long, so we need to add 1
skip = emitInstFastCall(build, pc, i, next) + 1;
break;
case LOP_FASTCALL1:
skip = emitInstFastCall1(build, pc, i, labelarr);
// We want to lower next instruction at skip+2, but this instruction is only 1 long, so we need to add 1
skip = emitInstFastCall1(build, pc, i, next) + 1;
break;
case LOP_FASTCALL2:
skip = emitInstFastCall2(build, pc, i, labelarr);
skip = emitInstFastCall2(build, pc, i, next);
break;
case LOP_FASTCALL2K:
skip = emitInstFastCall2K(build, pc, i, labelarr);
skip = emitInstFastCall2K(build, pc, i, next);
break;
case LOP_FORNPREP:
emitInstForNPrep(build, pc, i, labelarr);
emitInstForNPrep(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
break;
case LOP_FORNLOOP:
emitInstForNLoop(build, pc, i, labelarr);
emitInstForNLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
break;
case LOP_FORGLOOP:
emitinstForGLoop(build, pc, i, labelarr, fallback);
emitinstForGLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)], next, fallback);
break;
case LOP_FORGPREP_NEXT:
emitInstForGPrepNext(build, pc, i, labelarr, fallback);
emitInstForGPrepNext(build, pc, labelarr[i + 1 + LUAU_INSN_D(*pc)], fallback);
break;
case LOP_FORGPREP_INEXT:
emitInstForGPrepInext(build, pc, i, labelarr, fallback);
emitInstForGPrepInext(build, pc, labelarr[i + 1 + LUAU_INSN_D(*pc)], fallback);
break;
case LOP_AND:
emitInstAnd(build, pc);
@ -266,7 +268,7 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers&
emitInstGetImport(build, pc, fallback);
break;
case LOP_CONCAT:
emitInstConcat(build, pc, i, labelarr);
emitInstConcat(build, pc, i, next);
break;
default:
emitFallback(build, data, op, i);
@ -281,7 +283,8 @@ static void emitInstFallback(AssemblyBuilderX64& build, NativeState& data, LuauO
switch (op)
{
case LOP_GETIMPORT:
emitInstGetImportFallback(build, pc, i);
emitSetSavedPc(build, i + 1);
emitInstGetImportFallback(build, LUAU_INSN_A(*pc), pc[1]);
break;
case LOP_GETTABLE:
emitInstGetTableFallback(build, pc, i);
@ -356,11 +359,11 @@ static void emitInstFallback(AssemblyBuilderX64& build, NativeState& data, LuauO
emitInstLengthFallback(build, pc, i);
break;
case LOP_FORGLOOP:
emitinstForGLoopFallback(build, pc, i, labelarr);
emitinstForGLoopFallback(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
break;
case LOP_FORGPREP_NEXT:
case LOP_FORGPREP_INEXT:
emitInstForGPrepXnextFallback(build, pc, i, labelarr);
emitInstForGPrepXnextFallback(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]);
break;
case LOP_GETGLOBAL:
// TODO: luaV_gettable + cachedslot update instead of full fallback
@ -430,7 +433,9 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
if (options.annotator)
options.annotator(options.annotatorContext, build.text, proto->bytecodeid, i);
int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), instFallbacks[i]);
Label& next = nexti < proto->sizecode ? instLabels[nexti] : start; // Last instruction can't use 'next' label
int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), next, instFallbacks[i]);
if (skip != 0)
instOutlines.push_back({nexti, skip});
@ -454,15 +459,20 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat
const Instruction* pc = &proto->code[i];
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(*pc));
int nexti = i + getOpLength(op);
LUAU_ASSERT(nexti <= proto->sizecode);
build.setLabel(instLabels[i]);
if (options.annotator && !options.skipOutlinedCode)
options.annotator(options.annotatorContext, build.text, proto->bytecodeid, i);
int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), instFallbacks[i]);
Label& next = nexti < proto->sizecode ? instLabels[nexti] : start; // Last instruction can't use 'next' label
int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), next, instFallbacks[i]);
LUAU_ASSERT(skip == 0);
i += getOpLength(op);
i = nexti;
}
if (i < proto->sizecode)

View file

@ -61,10 +61,8 @@ void jumpOnNumberCmp(AssemblyBuilderX64& build, RegisterX64 tmp, OperandX64 lhs,
}
}
void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label, int pcpos)
void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label)
{
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(ra));
build.lea(rArg3, luauRegAddress(rb));
@ -85,10 +83,8 @@ void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX6
label);
}
RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int pcpos)
void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos)
{
RegisterX64 node = rdx;
LUAU_ASSERT(tmp != node);
LUAU_ASSERT(table != node);
@ -102,16 +98,12 @@ RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp,
// LuaNode* n = &h->node[slot];
build.shl(dwordReg(tmp), kLuaNodeSizeLog2);
build.add(node, tmp);
return node;
}
void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, int ri, Label& label)
void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label)
{
LUAU_ASSERT(numi.size == SizeX64::dword);
build.vmovsd(numd, luauRegValue(ri));
// Convert to integer, NaN is converted into 0x80000000
build.vcvttsd2si(numi, numd);
@ -124,10 +116,8 @@ void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, Regi
build.jcc(ConditionX64::NotZero, label);
}
void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, int pcpos, TMS tm)
void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm)
{
emitSetSavedPc(build, pcpos + 1);
if (build.abi == ABIX64::Windows)
build.mov(sArg5, tm);
else
@ -142,10 +132,8 @@ void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, in
emitUpdateBase(build);
}
void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos)
void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb)
{
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(ra));
build.lea(rArg3, luauRegAddress(rb));
@ -154,10 +142,8 @@ void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos)
emitUpdateBase(build);
}
void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, int pcpos)
void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init)
{
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(limit));
build.lea(rArg3, luauRegAddress(step));
@ -165,10 +151,8 @@ void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, i
build.call(qword[rNativeContext + offsetof(NativeContext, luaV_prepareFORN)]);
}
void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos)
void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra)
{
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(rb));
build.lea(rArg3, c);
@ -178,10 +162,8 @@ void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int p
emitUpdateBase(build);
}
void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos)
void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra)
{
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(rb));
build.lea(rArg3, c);

View file

@ -99,7 +99,7 @@ inline OperandX64 luauRegTag(int ri)
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, tt)];
}
inline OperandX64 luauRegValueBoolean(int ri)
inline OperandX64 luauRegValueInt(int ri)
{
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, value)];
}
@ -174,7 +174,7 @@ inline void jumpIfFalsy(AssemblyBuilderX64& build, int ri, Label& target, Label&
jumpIfTagIs(build, ri, LUA_TNIL, target); // false if nil
jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, fallthrough); // true if not nil or boolean
build.cmp(luauRegValueBoolean(ri), 0);
build.cmp(luauRegValueInt(ri), 0);
build.jcc(ConditionX64::Equal, target); // true if boolean value is 'true'
}
@ -184,7 +184,7 @@ inline void jumpIfTruthy(AssemblyBuilderX64& build, int ri, Label& target, Label
jumpIfTagIs(build, ri, LUA_TNIL, fallthrough); // false if nil
jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, target); // true if not nil or boolean
build.cmp(luauRegValueBoolean(ri), 0);
build.cmp(luauRegValueInt(ri), 0);
build.jcc(ConditionX64::NotEqual, target); // true if boolean value is 'true'
}
@ -236,16 +236,16 @@ inline void jumpIfNodeKeyNotInExpectedSlot(AssemblyBuilderX64& build, RegisterX6
}
void jumpOnNumberCmp(AssemblyBuilderX64& build, RegisterX64 tmp, OperandX64 lhs, OperandX64 rhs, ConditionX64 cond, Label& label);
void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label, int pcpos);
void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label);
RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int pcpos);
void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, int ri, Label& label);
void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos);
void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label);
void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, int pcpos, TMS tm);
void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos);
void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, int pcpos);
void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos);
void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos);
void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm);
void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb);
void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init);
void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra);
void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra);
void callBarrierTable(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int ra, Label& skip);
void callBarrierObject(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 object, int ra, Label& skip);
void callBarrierTableFast(AssemblyBuilderX64& build, RegisterX64 table, Label& skip);

View file

@ -69,7 +69,7 @@ void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc)
build.vmovups(luauReg(ra), xmm0);
}
void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos)
{
int ra = LUAU_INSN_A(*pc);
int nparams = LUAU_INSN_B(*pc) - 1;
@ -222,7 +222,7 @@ void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instr
}
}
void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos)
{
emitInterrupt(build, pcpos);
@ -435,7 +435,8 @@ void emitInstJumpIfEqFallback(AssemblyBuilderX64& build, const Instruction* pc,
{
Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target, pcpos);
emitSetSavedPc(build, pcpos + 1);
jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target);
}
void emitInstJumpIfCond(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, ConditionX64 cond, Label& fallback)
@ -456,7 +457,8 @@ void emitInstJumpIfCondFallback(AssemblyBuilderX64& build, const Instruction* pc
{
Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], cond, target, pcpos);
emitSetSavedPc(build, pcpos + 1);
jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], cond, target);
}
void emitInstJumpX(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
@ -488,7 +490,7 @@ void emitInstJumpxEqB(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
jumpIfTagIsNot(build, ra, LUA_TBOOLEAN, not_ ? target : exit);
build.test(luauRegValueBoolean(ra), 1);
build.test(luauRegValueInt(ra), 1);
build.jcc((aux & 0x1) ^ not_ ? ConditionX64::NotZero : ConditionX64::Zero, target);
}
@ -534,7 +536,7 @@ void emitInstJumpxEqS(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
build.jcc(not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target);
}
static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int rc, OperandX64 opc, int pcpos, TMS tm, Label& fallback)
static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int rc, OperandX64 opc, TMS tm, Label& fallback)
{
jumpIfTagIsNot(build, rb, LUA_TNUMBER, fallback);
@ -580,27 +582,29 @@ static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int
build.mov(luauRegTag(ra), LUA_TNUMBER);
}
void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback)
void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback)
{
emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), luauRegValue(LUAU_INSN_C(*pc)), pcpos, tm, fallback);
emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), luauRegValue(LUAU_INSN_C(*pc)), tm, fallback);
}
void emitInstBinaryFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm)
{
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), pcpos, tm);
emitSetSavedPc(build, pcpos + 1);
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), tm);
}
void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback)
void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback)
{
emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, luauConstantValue(LUAU_INSN_C(*pc)), pcpos, tm, fallback);
emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, luauConstantValue(LUAU_INSN_C(*pc)), tm, fallback);
}
void emitInstBinaryKFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm)
{
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstantAddress(LUAU_INSN_C(*pc)), pcpos, tm);
emitSetSavedPc(build, pcpos + 1);
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstantAddress(LUAU_INSN_C(*pc)), tm);
}
void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label& fallback)
void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -647,17 +651,17 @@ void emitInstNot(AssemblyBuilderX64& build, const Instruction* pc)
jumpIfFalsy(build, rb, saveone, savezero);
build.setLabel(savezero);
build.mov(luauRegValueBoolean(ra), 0);
build.mov(luauRegValueInt(ra), 0);
build.jmp(exit);
build.setLabel(saveone);
build.mov(luauRegValueBoolean(ra), 1);
build.mov(luauRegValueInt(ra), 1);
build.setLabel(exit);
build.mov(luauRegTag(ra), LUA_TBOOLEAN);
}
void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -675,10 +679,11 @@ void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos,
void emitInstMinusFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_B(*pc)), pcpos, TM_UNM);
emitSetSavedPc(build, pcpos + 1);
callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_B(*pc)), TM_UNM);
}
void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -699,35 +704,32 @@ void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos,
void emitInstLengthFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
callLengthHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), pcpos);
emitSetSavedPc(build, pcpos + 1);
callLengthHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc));
}
void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next)
{
int ra = LUAU_INSN_A(*pc);
int b = LUAU_INSN_B(*pc);
uint32_t aux = pc[1];
Label& exit = labelarr[pcpos + 2];
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
build.mov(dwordReg(rArg2), aux);
build.mov(dwordReg(rArg3), 1 << (b - 1));
build.mov(dwordReg(rArg3), b == 0 ? 0 : 1 << (b - 1));
build.call(qword[rNativeContext + offsetof(NativeContext, luaH_new)]);
build.mov(luauRegValue(ra), rax);
build.mov(luauRegTag(ra), LUA_TTABLE);
callCheckGc(build, pcpos, /* savepc = */ false, exit);
callCheckGc(build, pcpos, /* savepc = */ false, next);
}
void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next)
{
int ra = LUAU_INSN_A(*pc);
Label& exit = labelarr[pcpos + 1];
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
@ -736,18 +738,16 @@ void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
build.mov(luauRegValue(ra), rax);
build.mov(luauRegTag(ra), LUA_TTABLE);
callCheckGc(build, pcpos, /* savepc= */ false, exit);
callCheckGc(build, pcpos, /* savepc= */ false, next);
}
void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, Label& next)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
int c = LUAU_INSN_C(*pc) - 1;
uint32_t index = pc[1];
Label& exit = labelarr[pcpos + 2];
OperandX64 last = index + c - 1;
// Using non-volatile 'rbx' for dynamic 'c' value (for LUA_MULTRET) to skip later recomputation
@ -842,10 +842,10 @@ void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos
build.setLabel(endLoop);
}
callBarrierTableFast(build, table, exit);
callBarrierTableFast(build, table, next);
}
void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc)
{
int ra = LUAU_INSN_A(*pc);
int up = LUAU_INSN_B(*pc);
@ -869,7 +869,7 @@ void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
build.vmovups(luauReg(ra), xmm0);
}
void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, Label& next)
{
int ra = LUAU_INSN_A(*pc);
int up = LUAU_INSN_B(*pc);
@ -884,32 +884,30 @@ void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
build.vmovups(xmm0, luauReg(ra));
build.vmovups(xmmword[tmp], xmm0);
callBarrierObject(build, tmp, upval, ra, labelarr[pcpos + 1]);
callBarrierObject(build, tmp, upval, ra, next);
}
void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, Label& next)
{
int ra = LUAU_INSN_A(*pc);
Label& skip = labelarr[pcpos + 1];
// L->openupval != 0
build.mov(rax, qword[rState + offsetof(lua_State, openupval)]);
build.test(rax, rax);
build.jcc(ConditionX64::Zero, skip);
build.jcc(ConditionX64::Zero, next);
// ra <= L->openuval->v
build.lea(rcx, addr[rBase + ra * sizeof(TValue)]);
build.cmp(rcx, qword[rax + offsetof(UpVal, v)]);
build.jcc(ConditionX64::Above, skip);
build.jcc(ConditionX64::Above, next);
build.mov(rArg2, rcx);
build.mov(rArg1, rState);
build.call(qword[rNativeContext + offsetof(NativeContext, luaF_close)]);
}
static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, bool customParams, int customParamCount, OperandX64 customArgs,
int pcpos, int instLen, Label* labelarr)
static int emitInstFastCallN(
AssemblyBuilderX64& build, const Instruction* pc, bool customParams, int customParamCount, OperandX64 customArgs, int pcpos, Label& fallback)
{
int bfid = LUAU_INSN_A(*pc);
int skip = LUAU_INSN_C(*pc);
@ -923,11 +921,9 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b
int arg = customParams ? LUAU_INSN_B(*pc) : ra + 1;
OperandX64 args = customParams ? customArgs : luauRegAddress(ra + 2);
Label& exit = labelarr[pcpos + instLen];
jumpIfUnsafeEnv(build, rax, fallback);
jumpIfUnsafeEnv(build, rax, exit);
BuiltinImplResult br = emitBuiltin(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, exit);
BuiltinImplResult br = emitBuiltin(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, fallback);
if (br.type == BuiltinImplType::UsesFallback)
{
@ -945,7 +941,7 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b
build.mov(qword[rState + offsetof(lua_State, top)], rax);
}
return skip + 2 - instLen; // Return fallback instruction sequence length
return skip; // Return fallback instruction sequence length
}
// TODO: we can skip saving pc for some well-behaved builtins which we didn't inline
@ -996,8 +992,8 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b
build.call(rax);
build.test(eax, eax); // test here will set SF=1 for a negative number and it always sets OF to 0
build.jcc(ConditionX64::Less, exit); // jl jumps if SF != OF
build.test(eax, eax); // test here will set SF=1 for a negative number and it always sets OF to 0
build.jcc(ConditionX64::Less, fallback); // jl jumps if SF != OF
if (nresults == LUA_MULTRET)
{
@ -1014,35 +1010,33 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b
build.mov(qword[rState + offsetof(lua_State, top)], rax);
}
return skip + 2 - instLen; // Return fallback instruction sequence length
return skip; // Return fallback instruction sequence length
}
int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
{
return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 1, /* customArgs */ 0, pcpos, /* instLen */ 1, labelarr);
return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 1, /* customArgs */ 0, pcpos, fallback);
}
int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
{
return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauRegAddress(pc[1]), pcpos, fallback);
}
int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
{
return emitInstFastCallN(
build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauRegAddress(pc[1]), pcpos, /* instLen */ 2, labelarr);
build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauConstantAddress(pc[1]), pcpos, fallback);
}
int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
{
return emitInstFastCallN(
build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauConstantAddress(pc[1]), pcpos, /* instLen */ 2, labelarr);
return emitInstFastCallN(build, pc, /* customParams */ false, /* customParamCount */ 0, /* customArgs */ 0, pcpos, fallback);
}
int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
{
return emitInstFastCallN(build, pc, /* customParams */ false, /* customParamCount */ 0, /* customArgs */ 0, pcpos, /* instLen */ 1, labelarr);
}
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopExit)
{
int ra = LUAU_INSN_A(*pc);
Label& loopExit = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
Label tryConvert, exit;
@ -1080,18 +1074,18 @@ void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
// TOOD: place at the end of the function
build.setLabel(tryConvert);
callPrepareForN(build, ra + 0, ra + 1, ra + 2, pcpos);
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* labelarr)
void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat)
{
emitInterrupt(build, pcpos);
int ra = LUAU_INSN_A(*pc);
Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
RegisterX64 limit = xmm0;
RegisterX64 step = xmm1;
@ -1121,14 +1115,11 @@ void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
build.setLabel(exit);
}
void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int aux = pc[1];
Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
Label& exit = labelarr[pcpos + 2];
emitInterrupt(build, pcpos);
// fast-path: builtin table iteration
@ -1160,13 +1151,13 @@ void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
// while (unsigned(index) < unsigned(sizearray))
Label arrayLoop = build.setLabel();
build.cmp(dwordReg(index), dword[table + offsetof(Table, sizearray)]);
build.jcc(ConditionX64::NotBelow, isIpairsIter ? exit : skipArray);
build.jcc(ConditionX64::NotBelow, isIpairsIter ? loopExit : skipArray);
// If element is nil, we increment the index; if it's not, we still need 'index + 1' inside
build.inc(index);
build.cmp(dword[elemPtr + offsetof(TValue, tt)], LUA_TNIL);
build.jcc(ConditionX64::Equal, isIpairsIter ? exit : skipArrayNil);
build.jcc(ConditionX64::Equal, isIpairsIter ? loopExit : skipArrayNil);
// setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
build.mov(luauRegValue(ra + 2), index);
@ -1202,13 +1193,11 @@ void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
}
}
void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat)
{
int ra = LUAU_INSN_A(*pc);
int aux = pc[1];
Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
emitSetSavedPc(build, pcpos + 1);
build.mov(rArg1, rState);
@ -1220,12 +1209,10 @@ void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc,
build.jcc(ConditionX64::NotZero, loopRepeat);
}
void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
// fast-path: pairs/next
jumpIfUnsafeEnv(build, rax, fallback);
jumpIfTagIsNot(build, ra + 1, LUA_TTABLE, fallback);
@ -1240,12 +1227,10 @@ void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int
build.jmp(target);
}
void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
// fast-path: ipairs/inext
jumpIfUnsafeEnv(build, rax, fallback);
jumpIfTagIsNot(build, ra + 1, LUA_TTABLE, fallback);
@ -1264,12 +1249,10 @@ void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int
build.jmp(target);
}
void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& target)
{
int ra = LUAU_INSN_A(*pc);
Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)];
build.mov(rArg1, rState);
build.lea(rArg2, luauRegAddress(ra));
build.mov(dwordReg(rArg3), pcpos + 1);
@ -1353,7 +1336,7 @@ void emitInstOrK(AssemblyBuilderX64& build, const Instruction* pc)
emitInstOrX(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstant(LUAU_INSN_C(*pc)));
}
void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1376,12 +1359,14 @@ void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcp
void emitInstGetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
emitSetSavedPc(build, pcpos + 1);
TValue n;
setnvalue(&n, LUAU_INSN_C(*pc) + 1);
callGetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc), pcpos);
callGetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc));
}
void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1404,17 +1389,19 @@ void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcp
build.vmovups(xmm0, luauReg(ra));
build.vmovups(xmmword[rax + c * sizeof(TValue)], xmm0);
callBarrierTable(build, rax, table, ra, labelarr[pcpos + 1]);
callBarrierTable(build, rax, table, ra, next);
}
void emitInstSetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
emitSetSavedPc(build, pcpos + 1);
TValue n;
setnvalue(&n, LUAU_INSN_C(*pc) + 1);
callSetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc), pcpos);
callSetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc));
}
void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1427,29 +1414,33 @@ void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
RegisterX64 table = rcx;
build.mov(table, luauRegValue(rb));
convertNumberToIndexOrJump(build, xmm1, xmm0, eax, rc, fallback);
RegisterX64 intIndex = eax;
RegisterX64 fpIndex = xmm0;
build.vmovsd(fpIndex, luauRegValue(rc));
convertNumberToIndexOrJump(build, xmm1, fpIndex, intIndex, fallback);
// index - 1
build.dec(eax);
build.dec(intIndex);
// unsigned(index - 1) < unsigned(h->sizearray)
build.cmp(dword[table + offsetof(Table, sizearray)], eax);
build.cmp(dword[table + offsetof(Table, sizearray)], intIndex);
build.jcc(ConditionX64::BelowEqual, fallback);
jumpIfMetatablePresent(build, table, fallback);
// setobj2s(L, ra, &h->array[unsigned(index - 1)]);
build.mov(rdx, qword[table + offsetof(Table, array)]);
build.shl(eax, kTValueSizeLog2);
build.shl(intIndex, kTValueSizeLog2);
setLuauReg(build, xmm0, ra, xmmword[rdx + rax]);
}
void emitInstGetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
callGetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc), pcpos);
emitSetSavedPc(build, pcpos + 1);
callGetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc));
}
void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1462,13 +1453,16 @@ void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
RegisterX64 table = rcx;
build.mov(table, luauRegValue(rb));
convertNumberToIndexOrJump(build, xmm1, xmm0, eax, rc, fallback);
RegisterX64 intIndex = eax;
RegisterX64 fpIndex = xmm0;
build.vmovsd(fpIndex, luauRegValue(rc));
convertNumberToIndexOrJump(build, xmm1, fpIndex, intIndex, fallback);
// index - 1
build.dec(eax);
build.dec(intIndex);
// unsigned(index - 1) < unsigned(h->sizearray)
build.cmp(dword[table + offsetof(Table, sizearray)], eax);
build.cmp(dword[table + offsetof(Table, sizearray)], intIndex);
build.jcc(ConditionX64::BelowEqual, fallback);
jumpIfMetatablePresent(build, table, fallback);
@ -1476,16 +1470,17 @@ void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo
// setobj2t(L, &h->array[unsigned(index - 1)], ra);
build.mov(rdx, qword[table + offsetof(Table, array)]);
build.shl(eax, kTValueSizeLog2);
build.shl(intIndex, kTValueSizeLog2);
build.vmovups(xmm0, luauReg(ra));
build.vmovups(xmmword[rdx + rax], xmm0);
callBarrierTable(build, rdx, table, ra, labelarr[pcpos + 1]);
callBarrierTable(build, rdx, table, ra, next);
}
void emitInstSetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
{
callSetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc), pcpos);
emitSetSavedPc(build, pcpos + 1);
callSetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc));
}
void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback)
@ -1504,13 +1499,8 @@ void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label&
build.vmovups(luauReg(ra), xmm0);
}
void emitInstGetImportFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos)
void emitInstGetImportFallback(AssemblyBuilderX64& build, int ra, uint32_t aux)
{
int ra = LUAU_INSN_A(*pc);
uint32_t aux = pc[1];
emitSetSavedPc(build, pcpos + 1);
build.mov(rax, sClosure);
// luaV_getimport(L, cl->env, k, aux, /* propagatenil= */ false)
@ -1548,14 +1538,15 @@ void emitInstGetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pc
RegisterX64 table = rcx;
build.mov(table, luauRegValue(rb));
RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos);
RegisterX64 node = rdx;
getTableNodeAtCachedSlot(build, rax, node, table, pcpos);
jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback);
setLuauReg(build, xmm0, ra, luauNodeValue(node));
}
void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1567,14 +1558,15 @@ void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pc
build.mov(table, luauRegValue(rb));
// fast-path: set value at the expected slot
RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos);
RegisterX64 node = rdx;
getTableNodeAtCachedSlot(build, rax, node, table, pcpos);
jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback);
jumpIfTableIsReadOnly(build, table, fallback);
setNodeValue(build, xmm0, luauNodeValue(node), ra);
callBarrierTable(build, rax, table, ra, labelarr[pcpos + 2]);
callBarrierTable(build, rax, table, ra, next);
}
void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback)
@ -1585,14 +1577,15 @@ void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcp
RegisterX64 table = rcx;
build.mov(rax, sClosure);
build.mov(table, qword[rax + offsetof(Closure, env)]);
RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos);
RegisterX64 node = rdx;
getTableNodeAtCachedSlot(build, rax, node, table, pcpos);
jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback);
setLuauReg(build, xmm0, ra, luauNodeValue(node));
}
void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback)
void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback)
{
int ra = LUAU_INSN_A(*pc);
uint32_t aux = pc[1];
@ -1600,17 +1593,18 @@ void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcp
RegisterX64 table = rcx;
build.mov(rax, sClosure);
build.mov(table, qword[rax + offsetof(Closure, env)]);
RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos);
RegisterX64 node = rdx;
getTableNodeAtCachedSlot(build, rax, node, table, pcpos);
jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback);
jumpIfTableIsReadOnly(build, table, fallback);
setNodeValue(build, xmm0, luauNodeValue(node), ra);
callBarrierTable(build, rax, table, ra, labelarr[pcpos + 2]);
callBarrierTable(build, rax, table, ra, next);
}
void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr)
void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next)
{
int ra = LUAU_INSN_A(*pc);
int rb = LUAU_INSN_B(*pc);
@ -1630,7 +1624,7 @@ void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos,
build.vmovups(xmm0, luauReg(rb));
build.vmovups(luauReg(ra), xmm0);
callCheckGc(build, pcpos, /* savepc= */ false, labelarr[pcpos + 1]);
callCheckGc(build, pcpos, /* savepc= */ false, next);
}
} // namespace CodeGen

View file

@ -24,8 +24,8 @@ void emitInstLoadN(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstLoadK(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstLoadKX(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos);
void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos);
void emitInstJump(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstJumpBack(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstJumpIf(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, bool not_);
@ -38,52 +38,52 @@ void emitInstJumpxEqNil(AssemblyBuilderX64& build, const Instruction* pc, int pc
void emitInstJumpxEqB(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstJumpxEqN(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label* labelarr);
void emitInstJumpxEqS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback);
void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback);
void emitInstBinaryFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm);
void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback);
void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback);
void emitInstBinaryKFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm);
void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label& fallback);
void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, Label& fallback);
void emitInstNot(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback);
void emitInstMinusFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback);
void emitInstLengthFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next);
void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next);
void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, Label& next);
void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, Label& next);
void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, Label& next);
int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
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 emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat);
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);
void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback);
void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback);
void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& target);
void emitInstAnd(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstAndK(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstOr(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstOrK(AssemblyBuilderX64& build, const Instruction* pc);
void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback);
void emitInstGetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback);
void emitInstSetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback);
void emitInstGetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback);
void emitInstSetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback);
void emitInstGetImportFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos);
void emitInstGetImportFallback(AssemblyBuilderX64& build, int ra, uint32_t aux);
void emitInstGetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback);
void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback);
void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback);
void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr);
void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback);
void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next);
} // namespace CodeGen
} // namespace Luau

View file

@ -6,6 +6,7 @@
#include "doctest.h"
#include <math.h>
#include <ostream>
using namespace Luau;
@ -58,6 +59,9 @@ TEST_CASE("encode_constants")
AstExprConstantBool b{Location(), true};
AstExprConstantNumber n{Location(), 8.2};
AstExprConstantNumber bigNum{Location(), 0.1677721600000003};
AstExprConstantNumber positiveInfinity{Location(), INFINITY};
AstExprConstantNumber negativeInfinity{Location(), -INFINITY};
AstExprConstantNumber nan{Location(), NAN};
AstArray<char> charString;
charString.data = const_cast<char*>("a\x1d\0\\\"b");
@ -69,6 +73,9 @@ TEST_CASE("encode_constants")
CHECK_EQ(R"({"type":"AstExprConstantBool","location":"0,0 - 0,0","value":true})", toJson(&b));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":8.1999999999999993})", toJson(&n));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":0.16777216000000031})", toJson(&bigNum));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":Infinity})", toJson(&positiveInfinity));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":-Infinity})", toJson(&negativeInfinity));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":NaN})", toJson(&nan));
CHECK_EQ("{\"type\":\"AstExprConstantString\",\"location\":\"0,0 - 0,0\",\"value\":\"a\\u001d\\u0000\\\\\\\"b\"}", toJson(&needsEscaping));
}

View file

@ -2948,6 +2948,71 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons")
CHECK_EQ(ac.context, AutocompleteContext::String);
}
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key")
{
ScopedFastFlag sff{"LuauCompleteTableKeysBetter", true};
check(R"(
type Direction = "up" | "down"
local a: {[Direction]: boolean} = {[@1] = true}
local b: {[Direction]: boolean} = {["@2"] = true}
local c: {[Direction]: boolean} = {u@3 = true}
local d: {[Direction]: boolean} = {[u@4] = true}
local e: {[Direction]: boolean} = {[@5]}
local f: {[Direction]: boolean} = {["@6"]}
local g: {[Direction]: boolean} = {u@7}
local h: {[Direction]: boolean} = {[u@8]}
)");
auto ac = autocomplete('1');
CHECK(ac.entryMap.count("\"up\""));
CHECK(ac.entryMap.count("\"down\""));
ac = autocomplete('2');
CHECK(ac.entryMap.count("up"));
CHECK(ac.entryMap.count("down"));
ac = autocomplete('3');
CHECK(ac.entryMap.count("up"));
CHECK(ac.entryMap.count("down"));
ac = autocomplete('4');
CHECK(!ac.entryMap.count("up"));
CHECK(!ac.entryMap.count("down"));
CHECK(ac.entryMap.count("\"up\""));
CHECK(ac.entryMap.count("\"down\""));
ac = autocomplete('5');
CHECK(ac.entryMap.count("\"up\""));
CHECK(ac.entryMap.count("\"down\""));
ac = autocomplete('6');
CHECK(ac.entryMap.count("up"));
CHECK(ac.entryMap.count("down"));
ac = autocomplete('7');
CHECK(ac.entryMap.count("up"));
CHECK(ac.entryMap.count("down"));
ac = autocomplete('8');
CHECK(!ac.entryMap.count("up"));
CHECK(!ac.entryMap.count("down"));
CHECK(ac.entryMap.count("\"up\""));
CHECK(ac.entryMap.count("\"down\""));
}
TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singleton_equality")
{
check(R"(

View file

@ -2,19 +2,21 @@
#include "Fixture.h"
#include "Luau/AstQuery.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Constraint.h"
#include "Luau/ModuleResolver.h"
#include "Luau/NotNull.h"
#include "Luau/Parser.h"
#include "Luau/TypeVar.h"
#include "Luau/TypeAttach.h"
#include "Luau/Transpiler.h"
#include "Luau/BuiltinDefinitions.h"
#include "doctest.h"
#include <algorithm>
#include <sstream>
#include <string_view>
#include <iostream>
static const char* mainModuleName = "MainModule";
@ -27,6 +29,41 @@ extern std::optional<unsigned> randomSeed; // tests/main.cpp
namespace Luau
{
std::optional<ModuleInfo> TestFileResolver::resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr)
{
if (auto name = pathExprToModuleName(currentModuleName, pathExpr))
return {{*name, false}};
return std::nullopt;
}
const ModulePtr TestFileResolver::getModule(const ModuleName& moduleName) const
{
LUAU_ASSERT(false);
return nullptr;
}
bool TestFileResolver::moduleExists(const ModuleName& moduleName) const
{
auto it = source.find(moduleName);
return (it != source.end());
}
std::optional<SourceCode> TestFileResolver::readSource(const ModuleName& name)
{
auto it = source.find(name);
if (it == source.end())
return std::nullopt;
SourceCode::Type sourceType = SourceCode::Module;
auto it2 = sourceTypes.find(name);
if (it2 != sourceTypes.end())
sourceType = it2->second;
return SourceCode{it->second, sourceType};
}
std::optional<ModuleInfo> TestFileResolver::resolveModule(const ModuleInfo* context, AstExpr* expr)
{
if (AstExprGlobal* g = expr->as<AstExprGlobal>())
@ -90,6 +127,15 @@ std::optional<std::string> TestFileResolver::getEnvironmentForModule(const Modul
return std::nullopt;
}
const Config& TestConfigResolver::getConfig(const ModuleName& name) const
{
auto it = configFiles.find(name);
if (it != configFiles.end())
return it->second;
return defaultConfig;
}
Fixture::Fixture(bool freeze, bool prepareAutocomplete)
: sff_DebugLuauFreezeArena("DebugLuauFreezeArena", freeze)
, frontend(&fileResolver, &configResolver,

View file

@ -10,59 +10,31 @@
#include "Luau/ModuleResolver.h"
#include "Luau/Scope.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypeVar.h"
#include "IostreamOptional.h"
#include "ScopedFlags.h"
#include <iostream>
#include <string>
#include <unordered_map>
#include <optional>
namespace Luau
{
struct TypeChecker;
struct TestFileResolver
: FileResolver
, ModuleResolver
{
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override
{
if (auto name = pathExprToModuleName(currentModuleName, pathExpr))
return {{*name, false}};
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override;
return std::nullopt;
}
const ModulePtr getModule(const ModuleName& moduleName) const override;
const ModulePtr getModule(const ModuleName& moduleName) const override
{
LUAU_ASSERT(false);
return nullptr;
}
bool moduleExists(const ModuleName& moduleName) const override;
bool moduleExists(const ModuleName& moduleName) const override
{
auto it = source.find(moduleName);
return (it != source.end());
}
std::optional<SourceCode> readSource(const ModuleName& name) override
{
auto it = source.find(name);
if (it == source.end())
return std::nullopt;
SourceCode::Type sourceType = SourceCode::Module;
auto it2 = sourceTypes.find(name);
if (it2 != sourceTypes.end())
sourceType = it2->second;
return SourceCode{it->second, sourceType};
}
std::optional<SourceCode> readSource(const ModuleName& name) override;
std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* expr) override;
@ -80,14 +52,7 @@ struct TestConfigResolver : ConfigResolver
Config defaultConfig;
std::unordered_map<ModuleName, Config> configFiles;
const Config& getConfig(const ModuleName& name) const override
{
auto it = configFiles.find(name);
if (it != configFiles.end())
return it->second;
return defaultConfig;
}
const Config& getConfig(const ModuleName& name) const override;
};
struct Fixture

View file

@ -519,10 +519,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_is_dirty")
TEST_CASE_FIXTURE(FrontendFixture, "mark_non_immediate_reverse_deps_as_dirty")
{
ScopedFastFlag sff[] = {
{"LuauFixMarkDirtyReverseDeps", true},
};
fileResolver.source["game/Gui/Modules/A"] = "return {hello=5, world=true}";
fileResolver.source["game/Gui/Modules/B"] = R"(
return require(game:GetService('Gui').Modules.A)

View file

@ -393,7 +393,6 @@ TEST_SUITE_END();
struct NormalizeFixture : Fixture
{
ScopedFastFlag sff0{"LuauNegatedStringSingletons", true};
ScopedFastFlag sff1{"LuauNegatedFunctionTypes", true};
TypeArena arena;

View file

@ -269,9 +269,9 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_type_when_length_is_exceeded")
{
o.maxTypeLength = 30;
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
CHECK_EQ(toString(requireType("f1"), o), "<a>(a) -> () -> ()");
CHECK_EQ(toString(requireType("f2"), o), "<b>(b) -> <a>(a) -> () -> ()");
CHECK_EQ(toString(requireType("f3"), o), "<c>(c) -> <b>(b) -> <a>(a) -> (... *TRUNCATED*");
CHECK_EQ(toString(requireType("f1"), o), "<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
CHECK_EQ(toString(requireType("f2"), o), "<b>(b) -> (<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
CHECK_EQ(toString(requireType("f3"), o), "<c>(c) -> (<b>(b) -> (<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
}
else
{
@ -299,9 +299,9 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
{
o.maxTypeLength = 30;
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
CHECK_EQ(toString(requireType("f1"), o), "<a>(a) -> () -> ()");
CHECK_EQ(toString(requireType("f2"), o), "<b>(b) -> <a>(a) -> () -> ()");
CHECK_EQ(toString(requireType("f3"), o), "<c>(c) -> <b>(b) -> <a>(a) -> (... *TRUNCATED*");
CHECK_EQ(toString(requireType("f1"), o), "<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
CHECK_EQ(toString(requireType("f2"), o), "<b>(b) -> (<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
CHECK_EQ(toString(requireType("f3"), o), "<c>(c) -> (<b>(b) -> (<a>(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*");
}
else
{

View file

@ -8,8 +8,8 @@
using namespace Luau;
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes)
LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError)
LUAU_FASTFLAG(LuauNewLibraryTypeNames)
TEST_SUITE_BEGIN("TypeAliases");
@ -525,21 +525,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "general_require_multi_assign")
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation")
{
ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true};
CheckResult result = check("type t10<x> = typeof(table)");
LUAU_REQUIRE_NO_ERRORS(result);
TypeId ty = getGlobalBinding(frontend, "table");
if (FFlag::LuauNoMoreGlobalSingletonTypes)
{
CHECK_EQ(toString(ty), "typeof(table)");
}
if (FFlag::LuauNewLibraryTypeNames)
CHECK(toString(ty) == "typeof(table)");
else
{
CHECK_EQ(toString(ty), "table");
}
CHECK(toString(ty) == "table");
const TableTypeVar* ttv = get<TableTypeVar>(ty);
REQUIRE(ttv);

View file

@ -533,7 +533,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_never_properties")
ScopedFastFlag sffs[]{
{"LuauSubtypeNormalizer", true},
{"LuauTypeNormalization2", true},
{"LuauUninhabitedSubAnything", true},
{"LuauUninhabitedSubAnything2", true},
};
CheckResult result = check(R"(

View file

@ -13,8 +13,7 @@ namespace
struct NegationFixture : Fixture
{
TypeArena arena;
ScopedFastFlag sff[2]{
{"LuauNegatedStringSingletons", true},
ScopedFastFlag sff[1]{
{"LuauSubtypeNormalizer", true},
};

View file

@ -25,8 +25,16 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types")
local x:string|number = s
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(toString(*requireType("s")), "number | string");
CHECK_EQ(toString(*requireType("x")), "number | string");
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ(toString(*requireType("s")), "(string & ~(false?)) | number");
CHECK_EQ(toString(*requireType("x")), "number | string");
}
else
{
CHECK_EQ(toString(*requireType("s")), "number | string");
CHECK_EQ(toString(*requireType("x")), "number | string");
}
}
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
@ -37,8 +45,16 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
local y = x or "s"
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(toString(*requireType("s")), "number | string");
CHECK_EQ(toString(*requireType("y")), "number | string");
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ(toString(*requireType("s")), "(string & ~(false?)) | number");
CHECK_EQ(toString(*requireType("y")), "((number | string) & ~(false?)) | string");
}
else
{
CHECK_EQ(toString(*requireType("s")), "number | string");
CHECK_EQ(toString(*requireType("y")), "number | string");
}
}
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
@ -62,7 +78,14 @@ TEST_CASE_FIXTURE(Fixture, "and_does_not_always_add_boolean")
local x:boolean|number = s
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(toString(*requireType("s")), "number");
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ(toString(*requireType("s")), "((false?) & string) | number");
}
else
{
CHECK_EQ(toString(*requireType("s")), "number");
}
}
TEST_CASE_FIXTURE(Fixture, "and_adds_boolean_no_superfluous_union")
@ -81,7 +104,14 @@ TEST_CASE_FIXTURE(Fixture, "and_or_ternary")
local s = (1/2) > 0.5 and "a" or 10
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(toString(*requireType("s")), "number | string");
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ(toString(*requireType("s")), "((((false?) & boolean) | string) & ~(false?)) | number");
}
else
{
CHECK_EQ(toString(*requireType("s")), "number | string");
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "primitive_arith_no_metatable")
@ -405,11 +435,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_mismatch_metatable")
local v2: V2 = setmetatable({ x = 3, y = 4 }, VMT)
v1 %= v2
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
CHECK_EQ(*tm->wantedType, *requireType("v2"));
CHECK_EQ(*tm->givenType, *typeChecker.numberType);
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("Type 'number' could not be converted into 'V2'" == toString(result.errors[0]));
}
TEST_CASE_FIXTURE(Fixture, "CallOrOfFunctions")
@ -781,7 +809,14 @@ local b: number = 1 or a
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
CHECK_EQ(typeChecker.numberType, tm->wantedType);
CHECK_EQ("number?", toString(tm->givenType));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("((number & ~(false?)) | number)?", toString(tm->givenType));
}
else
{
CHECK_EQ("number?", toString(tm->givenType));
}
}
TEST_CASE_FIXTURE(Fixture, "operator_eq_verifies_types_do_intersect")
@ -842,7 +877,14 @@ TEST_CASE_FIXTURE(Fixture, "refine_and_or")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("number", toString(requireType("u")));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("((((false?) & ({| x: number? |}?)) | a) & ~(false?)) | number", toString(requireType("u")));
}
else
{
CHECK_EQ("number", toString(requireType("u")));
}
}
TEST_CASE_FIXTURE(Fixture, "infer_any_in_all_modes_when_lhs_is_unknown")
@ -1035,10 +1077,10 @@ local w = c and 1
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK("number?" == toString(requireType("x")));
CHECK("number" == toString(requireType("y")));
CHECK("false | number" == toString(requireType("z")));
CHECK("number" == toString(requireType("w"))); // Normalizer considers free & falsy == never
CHECK("((false?) & (number?)) | number" == toString(requireType("x")));
CHECK("((false?) & string) | number" == toString(requireType("y")));
CHECK("((false?) & boolean) | number" == toString(requireType("z")));
CHECK("((false?) & a) | number" == toString(requireType("w")));
}
else
{
@ -1073,12 +1115,12 @@ local f1 = f or 'f'
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK("number | string" == toString(requireType("a1")));
CHECK("number" == toString(requireType("b1")));
CHECK("string | true" == toString(requireType("c1")));
CHECK("string | true" == toString(requireType("d1")));
CHECK("string" == toString(requireType("e1")));
CHECK("string" == toString(requireType("f1")));
CHECK("((false | number) & ~(false?)) | string" == toString(requireType("a1")));
CHECK("((number?) & ~(false?)) | number" == toString(requireType("b1")));
CHECK("(boolean & ~(false?)) | string" == toString(requireType("c1")));
CHECK("(true & ~(false?)) | string" == toString(requireType("d1")));
CHECK("(false & ~(false?)) | string" == toString(requireType("e1")));
CHECK("(nil & ~(false?)) | string" == toString(requireType("f1")));
}
else
{

View file

@ -9,6 +9,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError)
TEST_SUITE_BEGIN("ProvisionalTests");
// These tests check for behavior that differs from the final behavior we'd
@ -776,4 +778,32 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "functions_with_mismatching_arity_but_any_is
// CHECK(!isSubtype(b, c));
}
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
{
CheckResult result = check(R"(
local t: {x: number?} = {x = nil}
if t.x then
local u: {x: number} = t
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauTypeMismatchInvarianceInError)
{
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
caused by:
Property 'x' is not compatible. Type 'number?' could not be converted into 'number' in an invariant context)",
toString(result.errors[0]));
}
else
{
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
caused by:
Property 'x' is not compatible. Type 'number?' could not be converted into 'number')",
toString(result.errors[0]));
}
}
TEST_SUITE_END();

View file

@ -8,7 +8,6 @@
#include "doctest.h"
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError)
using namespace Luau;
@ -36,7 +35,7 @@ std::optional<WithPredicate<TypePackId>> magicFunctionInstanceIsA(
return WithPredicate<TypePackId>{booleanPack, {IsAPredicate{std::move(*lvalue), expr.location, tfun->type}}};
}
std::vector<ConnectiveId> dcrMagicRefinementInstanceIsA(MagicRefinementContext ctx)
std::vector<ConnectiveId> dcrMagicRefinementInstanceIsA(const MagicRefinementContext& ctx)
{
if (ctx.callSite->args.size != 1)
return {};
@ -462,35 +461,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_non_binary_expressions_actually_resol
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
{
CheckResult result = check(R"(
local t: {x: number?} = {x = nil}
if t.x then
local u: {x: number} = t
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauTypeMismatchInvarianceInError)
{
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
caused by:
Property 'x' is not compatible. Type 'number?' could not be converted into 'number' in an invariant context)",
toString(result.errors[0]));
}
else
{
CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}'
caused by:
Property 'x' is not compatible. Type 'number?' could not be converted into 'number')",
toString(result.errors[0]));
}
}
TEST_CASE_FIXTURE(Fixture, "lvalue_is_equal_to_another_lvalue")
{
CheckResult result = check(R"(
@ -1009,8 +979,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_a_to_be_truthy_then_assert_a_to_be_nu
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("number | string", toString(requireTypeAtPosition({3, 18})));
CHECK_EQ("number", toString(requireTypeAtPosition({5, 18})));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("((number | string)?) & ~(false?)", toString(requireTypeAtPosition({3, 18})));
CHECK_EQ("((number | string)?) & ~(false?) & number", toString(requireTypeAtPosition({5, 18})));
}
else
{
CHECK_EQ("number | string", toString(requireTypeAtPosition({3, 18})));
CHECK_EQ("number", toString(requireTypeAtPosition({5, 18})));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "merge_should_be_fully_agnostic_of_hashmap_ordering")
@ -1031,7 +1009,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "merge_should_be_fully_agnostic_of_hashmap_or
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("(string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28})));
CHECK_EQ("(never | string) & (string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28})));
}
else
{
@ -1075,8 +1053,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "is_truthy_constraint_ifelse_expression")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("string", toString(requireTypeAtPosition({2, 29})));
CHECK_EQ("nil", toString(requireTypeAtPosition({2, 45})));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("(string?) & ~(false?)", toString(requireTypeAtPosition({2, 29})));
CHECK_EQ("(string?) & ~~(false?)", toString(requireTypeAtPosition({2, 45})));
}
else
{
CHECK_EQ("string", toString(requireTypeAtPosition({2, 29})));
CHECK_EQ("nil", toString(requireTypeAtPosition({2, 45})));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "invert_is_truthy_constraint_ifelse_expression")
@ -1089,8 +1075,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "invert_is_truthy_constraint_ifelse_expressio
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("nil", toString(requireTypeAtPosition({2, 42})));
CHECK_EQ("string", toString(requireTypeAtPosition({2, 50})));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("(string?) & ~~(false?)", toString(requireTypeAtPosition({2, 42})));
CHECK_EQ("(string?) & ~(false?)", toString(requireTypeAtPosition({2, 50})));
}
else
{
CHECK_EQ("nil", toString(requireTypeAtPosition({2, 42})));
CHECK_EQ("string", toString(requireTypeAtPosition({2, 50})));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression")
@ -1107,8 +1101,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("number", toString(requireTypeAtPosition({6, 49})));
CHECK_EQ("any", toString(requireTypeAtPosition({6, 66})));
if (FFlag::DebugLuauDeferredConstraintResolution)
{
CHECK_EQ("any & number", toString(requireTypeAtPosition({6, 49})));
CHECK_EQ("any & ~number", toString(requireTypeAtPosition({6, 66})));
}
else
{
CHECK_EQ("number", toString(requireTypeAtPosition({6, 49})));
CHECK_EQ("any", toString(requireTypeAtPosition({6, 66})));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "correctly_lookup_a_shadowed_local_that_which_was_previously_refined")

View file

@ -17,8 +17,8 @@ using namespace Luau;
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes)
LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError)
LUAU_FASTFLAG(LuauNewLibraryTypeNames)
TEST_SUITE_BEGIN("TableTests");
@ -1723,8 +1723,6 @@ TEST_CASE_FIXTURE(Fixture, "hide_table_error_properties")
TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names")
{
ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true};
CheckResult result = check(R"(
os.h = 2
string.k = 3
@ -1732,7 +1730,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names")
LUAU_REQUIRE_ERROR_COUNT(2, result);
if (FFlag::LuauNoMoreGlobalSingletonTypes)
if (FFlag::LuauNewLibraryTypeNames)
{
CHECK_EQ("Cannot add property 'h' to table 'typeof(os)'", toString(result.errors[0]));
CHECK_EQ("Cannot add property 'k' to table 'typeof(string)'", toString(result.errors[1]));
@ -1746,22 +1744,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names")
TEST_CASE_FIXTURE(BuiltinsFixture, "persistent_sealed_table_is_immutable")
{
ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true};
CheckResult result = check(R"(
--!nonstrict
function os:bad() end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauNoMoreGlobalSingletonTypes)
{
if (FFlag::LuauNewLibraryTypeNames)
CHECK_EQ("Cannot add property 'bad' to table 'typeof(os)'", toString(result.errors[0]));
}
else
{
CHECK_EQ("Cannot add property 'bad' to table 'os'", toString(result.errors[0]));
}
const TableTypeVar* osType = get<TableTypeVar>(requireType("os"));
REQUIRE(osType != nullptr);
@ -3238,7 +3230,8 @@ 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};
ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true};
if (!FFlag::LuauNewLibraryTypeNames)
return;
CheckResult result = check(R"(
local function f(s)
@ -3252,40 +3245,20 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_
LUAU_REQUIRE_ERROR_COUNT(3, result);
if (FFlag::LuauNoMoreGlobalSingletonTypes)
{
CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[0]));
CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
toString(result.errors[0]));
CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[1]));
CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
toString(result.errors[1]));
CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
Not all union options are compatible. Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[2]));
}
else
{
CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[0]));
CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[1]));
CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
Not all union options are compatible. Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
caused by:
The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[2]));
}
toString(result.errors[2]));
}
TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compatible")
@ -3307,7 +3280,8 @@ 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};
ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true};
if (!FFlag::LuauNewLibraryTypeNames)
return;
CheckResult result = check(R"(
local function f(s): string
@ -3317,22 +3291,11 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauNoMoreGlobalSingletonTypes)
{
CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string'
CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string'
caused by:
The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[0]));
CHECK_EQ("<a, b...>(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f")));
}
else
{
CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string'
caused by:
The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' because the former is missing field 'absolutely_no_scalar_has_this_method')",
toString(result.errors[0]));
CHECK_EQ("<a, b...>(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f")));
}
toString(result.errors[0]));
CHECK_EQ("<a, b...>(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly")

View file

@ -145,7 +145,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "uninhabited_table_sub_never")
ScopedFastFlag sffs[]{
{"LuauSubtypeNormalizer", true},
{"LuauTypeNormalization2", true},
{"LuauUninhabitedSubAnything", true},
{"LuauUninhabitedSubAnything2", true},
};
CheckResult result = check(R"(
@ -161,7 +161,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "uninhabited_table_sub_anything")
ScopedFastFlag sffs[]{
{"LuauSubtypeNormalizer", true},
{"LuauTypeNormalization2", true},
{"LuauUninhabitedSubAnything", true},
{"LuauUninhabitedSubAnything2", true},
};
CheckResult result = check(R"(

View file

@ -27,6 +27,7 @@ AutocompleteTest.do_wrong_compatible_self_calls
AutocompleteTest.keyword_methods
AutocompleteTest.no_incompatible_self_calls
AutocompleteTest.no_wrong_compatible_self_calls_with_generics
AutocompleteTest.string_singleton_as_table_key
AutocompleteTest.suggest_external_module_type
AutocompleteTest.suggest_table_keys
AutocompleteTest.type_correct_argument_type_suggestion
@ -88,7 +89,6 @@ DefinitionTests.class_definition_string_props
DefinitionTests.declaring_generic_functions
DefinitionTests.definition_file_classes
FrontendTest.environments
FrontendTest.imported_table_modification_2
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
FrontendTest.nocheck_cycle_used_by_checked
FrontendTest.reexport_cyclic_type
@ -96,7 +96,6 @@ FrontendTest.trace_requires_in_nonstrict_mode
GenericsTests.apply_type_function_nested_generics1
GenericsTests.apply_type_function_nested_generics2
GenericsTests.better_mismatch_error_messages
GenericsTests.calling_self_generic_methods
GenericsTests.check_generic_typepack_function
GenericsTests.check_mutual_generic_functions
GenericsTests.correctly_instantiate_polymorphic_member_functions
@ -113,7 +112,6 @@ GenericsTests.higher_rank_polymorphism_should_not_accept_instantiated_arguments
GenericsTests.infer_generic_function_function_argument
GenericsTests.infer_generic_function_function_argument_overloaded
GenericsTests.infer_generic_lib_function_function_argument
GenericsTests.infer_generic_methods
GenericsTests.infer_generic_property
GenericsTests.instantiated_function_argument_names
GenericsTests.instantiation_sharing_types
@ -147,6 +145,7 @@ ParseErrorRecovery.generic_type_list_recovery
ParseErrorRecovery.recovery_of_parenthesized_expressions
ParserTests.parse_nesting_based_end_detection_failsafe_earlier
ParserTests.parse_nesting_based_end_detection_local_function
ProvisionalTests.assign_table_with_refined_property_with_a_similar_type_is_illegal
ProvisionalTests.bail_early_if_unification_is_too_complicated
ProvisionalTests.discriminate_from_x_not_equal_to_nil
ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack
@ -163,26 +162,16 @@ ProvisionalTests.typeguard_inference_incomplete
ProvisionalTests.weirditer_should_not_loop_forever
ProvisionalTests.while_body_are_also_refined
RefinementTest.apply_refinements_on_astexprindexexpr_whose_subscript_expr_is_constant_string
RefinementTest.assert_a_to_be_truthy_then_assert_a_to_be_number
RefinementTest.assert_non_binary_expressions_actually_resolve_constraints
RefinementTest.assign_table_with_refined_property_with_a_similar_type_is_illegal
RefinementTest.call_an_incompatible_function_after_using_typeguard
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined2
RefinementTest.discriminate_on_properties_of_disjoint_tables_where_that_property_is_true_or_false
RefinementTest.discriminate_tag
RefinementTest.else_with_no_explicit_expression_should_also_refine_the_tagged_union
RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil
RefinementTest.fuzz_filtered_refined_types_are_followed
RefinementTest.index_on_a_refined_property
RefinementTest.invert_is_truthy_constraint_ifelse_expression
RefinementTest.is_truthy_constraint_ifelse_expression
RefinementTest.narrow_property_of_a_bounded_variable
RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true
RefinementTest.not_t_or_some_prop_of_t
RefinementTest.refine_a_property_not_to_be_nil_through_an_intersection_table
RefinementTest.refine_unknowns
RefinementTest.type_comparison_ifelse_expression
RefinementTest.type_guard_can_filter_for_intersection_of_tables
RefinementTest.type_guard_narrowed_into_nothingness
RefinementTest.type_narrow_for_all_the_userdata
@ -199,18 +188,18 @@ TableTests.access_index_metamethod_that_returns_variadic
TableTests.accidentally_checked_prop_in_opposite_branch
TableTests.builtin_table_names
TableTests.call_method
TableTests.call_method_with_explicit_self_argument
TableTests.cannot_augment_sealed_table
TableTests.casting_sealed_tables_with_props_into_table_with_indexer
TableTests.casting_tables_with_props_into_table_with_indexer3
TableTests.casting_tables_with_props_into_table_with_indexer4
TableTests.checked_prop_too_early
TableTests.defining_a_method_for_a_builtin_sealed_table_must_fail
TableTests.defining_a_method_for_a_local_sealed_table_must_fail
TableTests.defining_a_self_method_for_a_builtin_sealed_table_must_fail
TableTests.defining_a_self_method_for_a_local_sealed_table_must_fail
TableTests.defining_a_method_for_a_local_unsealed_table_is_ok
TableTests.defining_a_self_method_for_a_local_unsealed_table_is_ok
TableTests.dont_crash_when_setmetatable_does_not_produce_a_metatabletypevar
TableTests.dont_hang_when_trying_to_look_up_in_cyclic_metatable_index
TableTests.dont_quantify_table_that_belongs_to_outer_scope
TableTests.dont_seal_an_unsealed_table_by_passing_it_to_a_function_that_takes_a_sealed_table
TableTests.dont_suggest_exact_match_keys
TableTests.error_detailed_metatable_prop
TableTests.expected_indexer_from_table_union
@ -235,12 +224,11 @@ TableTests.infer_indexer_from_value_property_in_literal
TableTests.inferred_return_type_of_free_table
TableTests.inferring_crazy_table_should_also_be_quick
TableTests.instantiate_table_cloning_3
TableTests.instantiate_tables_at_scope_level
TableTests.invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound
TableTests.invariant_table_properties_means_instantiating_tables_in_call_is_unsound
TableTests.leaking_bad_metatable_errors
TableTests.less_exponential_blowup_please
TableTests.meta_add
TableTests.meta_add_both_ways
TableTests.meta_add_inferred
TableTests.metatable_mismatch_should_fail
TableTests.missing_metatable_for_sealed_tables_do_not_get_inferred
@ -253,7 +241,6 @@ TableTests.oop_indexer_works
TableTests.oop_polymorphic
TableTests.open_table_unification_2
TableTests.persistent_sealed_table_is_immutable
TableTests.prop_access_on_key_whose_types_mismatches
TableTests.property_lookup_through_tabletypevar_metatable
TableTests.quantify_even_that_table_was_never_exported_at_all
TableTests.quantify_metatables_of_metatables_of_table
@ -267,6 +254,7 @@ TableTests.shared_selfs
TableTests.shared_selfs_from_free_param
TableTests.shared_selfs_through_metatables
TableTests.table_call_metamethod_basic
TableTests.table_function_check_use_after_free
TableTests.table_indexing_error_location
TableTests.table_insert_should_cope_with_optional_properties_in_nonstrict
TableTests.table_insert_should_cope_with_optional_properties_in_strict
@ -279,13 +267,17 @@ 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
TableTests.unifying_tables_shouldnt_uaf1
TableTests.unifying_tables_shouldnt_uaf2
TableTests.used_colon_correctly
TableTests.used_colon_instead_of_dot
TableTests.used_dot_instead_of_colon
TableTests.used_dot_instead_of_colon_but_correctly
ToDot.bound_table
ToDot.function
ToDot.table
ToString.exhaustive_toString_of_cyclic_table
ToString.function_type_with_argument_names_and_self
ToString.function_type_with_argument_names_generic
ToString.toStringDetailed2
ToString.toStringErrorPack
@ -303,6 +295,7 @@ TryUnifyTests.typepack_unification_should_trim_free_tails
TryUnifyTests.variadics_should_use_reversed_properly
TypeAliases.cannot_create_cyclic_type_with_unknown_module
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any_2
TypeAliases.generic_param_remap
TypeAliases.mismatched_generic_type_param
TypeAliases.mutually_recursive_types_restriction_not_ok_1
@ -322,6 +315,7 @@ TypeInfer.checking_should_not_ice
TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error
TypeInfer.dont_report_type_errors_within_an_AstExprError
TypeInfer.dont_report_type_errors_within_an_AstStatError
TypeInfer.follow_on_new_types_in_substitution
TypeInfer.fuzz_free_table_type_change_during_index_check
TypeInfer.globals
TypeInfer.globals2
@ -335,11 +329,13 @@ 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
TypeInferClasses.detailed_class_unification_error
TypeInferClasses.higher_order_function_arguments_are_contravariant
TypeInferClasses.index_instance_property
TypeInferClasses.optional_class_field_access_error
TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties
TypeInferClasses.warn_when_prop_almost_matches
@ -349,7 +345,9 @@ TypeInferFunctions.calling_function_with_incorrect_argument_type_yields_errors_s
TypeInferFunctions.cannot_hoist_interior_defns_into_signature
TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists
TypeInferFunctions.dont_infer_parameter_types_for_functions_from_their_call_site
TypeInferFunctions.dont_mutate_the_underlying_head_of_typepack_when_calling_with_self
TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict
TypeInferFunctions.first_argument_can_be_optional
TypeInferFunctions.function_cast_error_uses_correct_language
TypeInferFunctions.function_decl_non_self_sealed_overwrite_2
TypeInferFunctions.function_decl_non_self_unsealed_overwrite
@ -387,36 +385,45 @@ TypeInferLoops.loop_iter_no_indexer_nonstrict
TypeInferLoops.loop_iter_trailing_nil
TypeInferLoops.properly_infer_iteratee_is_a_free_table
TypeInferLoops.unreachable_code_after_infinite_loop
TypeInferLoops.varlist_declared_by_for_in_loop_should_be_free
TypeInferModules.bound_free_table_export_is_ok
TypeInferModules.custom_require_global
TypeInferModules.do_not_modify_imported_types
TypeInferModules.do_not_modify_imported_types_4
TypeInferModules.do_not_modify_imported_types_5
TypeInferModules.module_type_conflict
TypeInferModules.module_type_conflict_instantiated
TypeInferModules.require_a_variadic_function
TypeInferModules.type_error_of_unknown_qualified_type
TypeInferOOP.CheckMethodsOfSealed
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_another_overload_works
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
TypeInferOOP.method_depends_on_table
TypeInferOOP.methods_are_topologically_sorted
TypeInferOOP.nonstrict_self_mismatch_tail
TypeInferOOP.object_constructor_can_refer_to_method_of_self
TypeInferOOP.table_oop
TypeInferOperators.CallAndOrOfFunctions
TypeInferOperators.CallOrOfFunctions
TypeInferOperators.cannot_compare_tables_that_do_not_have_the_same_metatable
TypeInferOperators.cannot_indirectly_compare_types_that_do_not_have_a_metatable
TypeInferOperators.cannot_indirectly_compare_types_that_do_not_offer_overloaded_ordering_operators
TypeInferOperators.cli_38355_recursive_union
TypeInferOperators.compound_assign_metatable
TypeInferOperators.compound_assign_mismatch_metatable
TypeInferOperators.compound_assign_mismatch_op
TypeInferOperators.compound_assign_mismatch_result
TypeInferOperators.disallow_string_and_types_without_metatables_from_arithmetic_binary_ops
TypeInferOperators.in_nonstrict_mode_strip_nil_from_intersections_when_considering_relational_operators
TypeInferOperators.infer_any_in_all_modes_when_lhs_is_unknown
TypeInferOperators.mm_comparisons_must_return_a_boolean
TypeInferOperators.mm_ops_must_return_a_value
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
TypeInferOperators.unrelated_classes_cannot_be_compared
TypeInferOperators.unrelated_primitives_cannot_be_compared
TypeInferPrimitives.CheckMethodsOfNumber
TypeInferPrimitives.string_index
TypeInferUnknownNever.assign_to_global_which_is_never
@ -432,6 +439,7 @@ TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable2
TypeInferUnknownNever.unary_minus_of_never
TypePackTests.detect_cyclic_typepacks2
TypePackTests.pack_tail_unification_check
TypePackTests.self_and_varargs_should_work
TypePackTests.type_alias_backwards_compatible
TypePackTests.type_alias_default_export
TypePackTests.type_alias_default_mixed_self