mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/556 (#782)
* The AST JSON encoder will now stringify infinity and NaN according to the JSON5 spec using the tokens `Infinity`, `-Infinity`, and `NaN`. * Improve autocompletion of table keys if the type of that key is a union of string singletons.
This commit is contained in:
parent
e9d4ee7a33
commit
fb2f146123
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "Luau/Location.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Luau
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/FileResolver.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)}));
|
||||
|
@ -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"
|
||||
@ -1582,4 +1584,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
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Luau/TxnLog.h"
|
||||
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypePack.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Luau/AstJsonEncoder.h"
|
||||
#include "Luau/Parser.h"
|
||||
#include "Luau/ParseOptions.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
#include "FileUtils.h"
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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"(
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -393,7 +393,6 @@ TEST_SUITE_END();
|
||||
|
||||
struct NormalizeFixture : Fixture
|
||||
{
|
||||
ScopedFastFlag sff0{"LuauNegatedStringSingletons", true};
|
||||
ScopedFastFlag sff1{"LuauNegatedFunctionTypes", true};
|
||||
|
||||
TypeArena arena;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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"(
|
||||
|
@ -13,8 +13,7 @@ namespace
|
||||
struct NegationFixture : Fixture
|
||||
{
|
||||
TypeArena arena;
|
||||
ScopedFastFlag sff[2]{
|
||||
{"LuauNegatedStringSingletons", true},
|
||||
ScopedFastFlag sff[1]{
|
||||
{"LuauSubtypeNormalizer", true},
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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"(
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user