mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/506
This commit is contained in:
parent
60e6e86adb
commit
eed18acec8
@ -51,13 +51,6 @@ struct FileResolver
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// DEPRECATED APIS
|
||||
// These are going to be removed with LuauNewRequireTrace2
|
||||
virtual bool moduleExists(const ModuleName& name) const = 0;
|
||||
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
|
||||
virtual ModuleName concat(const ModuleName& lhs, std::string_view rhs) const = 0;
|
||||
virtual std::optional<ModuleName> getParentModuleName(const ModuleName& name) const = 0;
|
||||
};
|
||||
|
||||
struct NullFileResolver : FileResolver
|
||||
@ -66,22 +59,6 @@ struct NullFileResolver : FileResolver
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
bool moduleExists(const ModuleName& name) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::optional<ModuleName> fromAstFragment(AstExpr* expr) const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
ModuleName concat(const ModuleName& lhs, std::string_view rhs) const override
|
||||
{
|
||||
return lhs;
|
||||
}
|
||||
std::optional<ModuleName> getParentModuleName(const ModuleName& name) const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -78,9 +78,15 @@ void unfreeze(TypeArena& arena);
|
||||
using SeenTypes = std::unordered_map<TypeId, TypeId>;
|
||||
using SeenTypePacks = std::unordered_map<TypePackId, TypePackId>;
|
||||
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
|
||||
TypeId clone(TypeId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
|
||||
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
|
||||
struct CloneState
|
||||
{
|
||||
int recursionCount = 0;
|
||||
bool encounteredFreeType = false;
|
||||
};
|
||||
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState);
|
||||
TypeId clone(TypeId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState);
|
||||
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState);
|
||||
|
||||
struct Module
|
||||
{
|
||||
|
31
Analysis/include/Luau/ToDot.h
Normal file
31
Analysis/include/Luau/ToDot.h
Normal file
@ -0,0 +1,31 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
struct TypeVar;
|
||||
using TypeId = const TypeVar*;
|
||||
|
||||
struct TypePackVar;
|
||||
using TypePackId = const TypePackVar*;
|
||||
|
||||
struct ToDotOptions
|
||||
{
|
||||
bool showPointers = true; // Show pointer value in the node label
|
||||
bool duplicatePrimitives = true; // Display primitive types and 'any' as separate nodes
|
||||
};
|
||||
|
||||
std::string toDot(TypeId ty, const ToDotOptions& opts);
|
||||
std::string toDot(TypePackId tp, const ToDotOptions& opts);
|
||||
|
||||
std::string toDot(TypeId ty);
|
||||
std::string toDot(TypePackId tp);
|
||||
|
||||
void dumpDot(TypeId ty);
|
||||
void dumpDot(TypePackId tp);
|
||||
|
||||
} // namespace Luau
|
@ -25,15 +25,6 @@ struct TxnLog
|
||||
{
|
||||
}
|
||||
|
||||
explicit TxnLog(const std::vector<std::pair<TypeId, TypeId>>& ownedSeen)
|
||||
: originalSeenSize(ownedSeen.size())
|
||||
, ownedSeen(ownedSeen)
|
||||
, sharedSeen(nullptr)
|
||||
{
|
||||
// This is deprecated!
|
||||
LUAU_ASSERT(!FFlag::LuauShareTxnSeen);
|
||||
}
|
||||
|
||||
TxnLog(const TxnLog&) = delete;
|
||||
TxnLog& operator=(const TxnLog&) = delete;
|
||||
|
||||
|
@ -297,7 +297,6 @@ private:
|
||||
|
||||
private:
|
||||
Unifier mkUnifier(const Location& location);
|
||||
Unifier mkUnifier(const std::vector<std::pair<TypeId, TypeId>>& seen, const Location& location);
|
||||
|
||||
// These functions are only safe to call when we are in the process of typechecking a module.
|
||||
|
||||
|
@ -517,21 +517,6 @@ extern SingletonTypes singletonTypes;
|
||||
void persist(TypeId ty);
|
||||
void persist(TypePackId tp);
|
||||
|
||||
struct ToDotOptions
|
||||
{
|
||||
bool showPointers = true; // Show pointer value in the node label
|
||||
bool duplicatePrimitives = true; // Display primitive types and 'any' as separate nodes
|
||||
};
|
||||
|
||||
std::string toDot(TypeId ty, const ToDotOptions& opts);
|
||||
std::string toDot(TypePackId tp, const ToDotOptions& opts);
|
||||
|
||||
std::string toDot(TypeId ty);
|
||||
std::string toDot(TypePackId tp);
|
||||
|
||||
void dumpDot(TypeId ty);
|
||||
void dumpDot(TypePackId tp);
|
||||
|
||||
const TypeLevel* getLevel(TypeId ty);
|
||||
TypeLevel* getMutableLevel(TypeId ty);
|
||||
|
||||
|
@ -19,12 +19,6 @@ enum Variance
|
||||
Invariant
|
||||
};
|
||||
|
||||
struct UnifierCounters
|
||||
{
|
||||
int recursionCount = 0;
|
||||
int iterationCount = 0;
|
||||
};
|
||||
|
||||
struct Unifier
|
||||
{
|
||||
TypeArena* const types;
|
||||
@ -37,20 +31,11 @@ struct Unifier
|
||||
Variance variance = Covariant;
|
||||
CountMismatch::Context ctx = CountMismatch::Arg;
|
||||
|
||||
UnifierCounters* counters;
|
||||
UnifierCounters countersData;
|
||||
|
||||
std::shared_ptr<UnifierCounters> counters_DEPRECATED;
|
||||
|
||||
UnifierSharedState& sharedState;
|
||||
|
||||
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const Location& location, Variance variance, UnifierSharedState& sharedState);
|
||||
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const std::vector<std::pair<TypeId, TypeId>>& ownedSeen, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED = nullptr,
|
||||
UnifierCounters* counters = nullptr);
|
||||
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, std::vector<std::pair<TypeId, TypeId>>* sharedSeen, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED = nullptr,
|
||||
UnifierCounters* counters = nullptr);
|
||||
Variance variance, UnifierSharedState& sharedState);
|
||||
|
||||
// Test whether the two type vars unify. Never commits the result.
|
||||
ErrorVec canUnify(TypeId superTy, TypeId subTy);
|
||||
@ -92,9 +77,9 @@ private:
|
||||
public:
|
||||
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
|
||||
void occursCheck(TypeId needle, TypeId haystack);
|
||||
void occursCheck(std::unordered_set<TypeId>& seen_DEPRECATED, DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
|
||||
void occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
|
||||
void occursCheck(TypePackId needle, TypePackId haystack);
|
||||
void occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
|
||||
void occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
|
||||
|
||||
Unifier makeChildUnifier();
|
||||
|
||||
@ -106,10 +91,6 @@ private:
|
||||
|
||||
[[noreturn]] void ice(const std::string& message, const Location& location);
|
||||
[[noreturn]] void ice(const std::string& message);
|
||||
|
||||
// Remove with FFlagLuauCacheUnifyTableResults
|
||||
DenseHashSet<TypeId> tempSeenTy_DEPRECATED{nullptr};
|
||||
DenseHashSet<TypePackId> tempSeenTp_DEPRECATED{nullptr};
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -24,6 +24,12 @@ struct TypeIdPairHash
|
||||
}
|
||||
};
|
||||
|
||||
struct UnifierCounters
|
||||
{
|
||||
int recursionCount = 0;
|
||||
int iterationCount = 0;
|
||||
};
|
||||
|
||||
struct UnifierSharedState
|
||||
{
|
||||
UnifierSharedState(InternalErrorReporter* iceHandler)
|
||||
@ -39,6 +45,8 @@ struct UnifierSharedState
|
||||
|
||||
DenseHashSet<TypeId> tempSeenTy{nullptr};
|
||||
DenseHashSet<TypePackId> tempSeenTp{nullptr};
|
||||
|
||||
UnifierCounters counters;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -5,8 +5,6 @@
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/TypePack.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCacheUnifyTableResults)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -101,7 +99,7 @@ void visit(TypeId ty, F& f, Set& seen)
|
||||
// Some visitors want to see bound tables, that's why we visit the original type
|
||||
if (apply(ty, *ttv, seen, f))
|
||||
{
|
||||
if (FFlag::LuauCacheUnifyTableResults && ttv->boundTo)
|
||||
if (ttv->boundTo)
|
||||
{
|
||||
visit(*ttv->boundTo, f, seen);
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
|
||||
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompletePreferToCallFunctions, false);
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
@ -203,8 +203,9 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||
{
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
expectedType = clone(expectedType, *typeArena, seenTypes, seenTypePacks, nullptr);
|
||||
actualType = clone(actualType, *typeArena, seenTypes, seenTypePacks, nullptr);
|
||||
CloneState cloneState;
|
||||
expectedType = clone(expectedType, *typeArena, seenTypes, seenTypePacks, cloneState);
|
||||
actualType = clone(actualType, *typeArena, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
auto errors = unifier.canUnify(expectedType, actualType);
|
||||
return errors.empty();
|
||||
@ -229,28 +230,51 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||
|
||||
TypeId expectedType = follow(*it);
|
||||
|
||||
if (canUnify(expectedType, ty))
|
||||
return TypeCorrectKind::Correct;
|
||||
|
||||
// We also want to suggest functions that return compatible result
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
|
||||
|
||||
if (!ftv)
|
||||
return TypeCorrectKind::None;
|
||||
|
||||
auto [retHead, retTail] = flatten(ftv->retType);
|
||||
|
||||
if (!retHead.empty())
|
||||
return canUnify(expectedType, retHead.front()) ? TypeCorrectKind::CorrectFunctionResult : TypeCorrectKind::None;
|
||||
|
||||
// We might only have a variadic tail pack, check if the element is compatible
|
||||
if (retTail)
|
||||
if (FFlag::LuauAutocompletePreferToCallFunctions)
|
||||
{
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*retTail)))
|
||||
return canUnify(expectedType, vtp->ty) ? TypeCorrectKind::CorrectFunctionResult : TypeCorrectKind::None;
|
||||
}
|
||||
// We also want to suggest functions that return compatible result
|
||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||
{
|
||||
auto [retHead, retTail] = flatten(ftv->retType);
|
||||
|
||||
return TypeCorrectKind::None;
|
||||
if (!retHead.empty() && canUnify(expectedType, retHead.front()))
|
||||
return TypeCorrectKind::CorrectFunctionResult;
|
||||
|
||||
// We might only have a variadic tail pack, check if the element is compatible
|
||||
if (retTail)
|
||||
{
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*retTail)); vtp && canUnify(expectedType, vtp->ty))
|
||||
return TypeCorrectKind::CorrectFunctionResult;
|
||||
}
|
||||
}
|
||||
|
||||
return canUnify(expectedType, ty) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (canUnify(expectedType, ty))
|
||||
return TypeCorrectKind::Correct;
|
||||
|
||||
// We also want to suggest functions that return compatible result
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
|
||||
|
||||
if (!ftv)
|
||||
return TypeCorrectKind::None;
|
||||
|
||||
auto [retHead, retTail] = flatten(ftv->retType);
|
||||
|
||||
if (!retHead.empty())
|
||||
return canUnify(expectedType, retHead.front()) ? TypeCorrectKind::CorrectFunctionResult : TypeCorrectKind::None;
|
||||
|
||||
// We might only have a variadic tail pack, check if the element is compatible
|
||||
if (retTail)
|
||||
{
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*retTail)))
|
||||
return canUnify(expectedType, vtp->ty) ? TypeCorrectKind::CorrectFunctionResult : TypeCorrectKind::None;
|
||||
}
|
||||
|
||||
return TypeCorrectKind::None;
|
||||
}
|
||||
}
|
||||
|
||||
enum class PropIndexType
|
||||
@ -1413,7 +1437,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(finder.ancestry); statWhile && !statWhile->hasDo)
|
||||
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
||||
|
||||
else if (AstStatIf* statIf = node->as<AstStatIf>(); FFlag::ElseElseIfCompletionImprovements && statIf && !statIf->hasElse)
|
||||
else if (AstStatIf* statIf = node->as<AstStatIf>(); statIf && !statIf->hasElse)
|
||||
{
|
||||
return {{{"else", AutocompleteEntry{AutocompleteEntryKind::Keyword}}, {"elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword}}},
|
||||
finder.ancestry};
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
*
|
||||
* Some of them require richer generics than we have. For instance, we do not yet have a way to talk
|
||||
@ -473,9 +471,7 @@ static std::optional<ExprResult<TypePackId>> magicFunctionRequire(
|
||||
if (!checkRequirePath(typechecker, expr.args.data[0]))
|
||||
return std::nullopt;
|
||||
|
||||
const AstExpr* require = FFlag::LuauNewRequireTrace2 ? &expr : expr.args.data[0];
|
||||
|
||||
if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, *require))
|
||||
if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, expr))
|
||||
return ExprResult<TypePackId>{arena.addTypePack({typechecker.checkRequire(scope, *moduleInfo, expr.location)})};
|
||||
|
||||
return std::nullopt;
|
||||
|
@ -7,57 +7,14 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
static std::string wrongNumberOfArgsString_DEPRECATED(size_t expectedCount, size_t actualCount, bool isTypeArgs = false)
|
||||
{
|
||||
std::string s = "expects " + std::to_string(expectedCount) + " ";
|
||||
|
||||
if (isTypeArgs)
|
||||
s += "type ";
|
||||
|
||||
s += "argument";
|
||||
if (expectedCount != 1)
|
||||
s += "s";
|
||||
|
||||
s += ", but ";
|
||||
|
||||
if (actualCount == 0)
|
||||
{
|
||||
s += "none";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualCount < expectedCount)
|
||||
s += "only ";
|
||||
|
||||
s += std::to_string(actualCount);
|
||||
}
|
||||
|
||||
s += (actualCount == 1) ? " is" : " are";
|
||||
|
||||
s += " specified";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string wrongNumberOfArgsString(size_t expectedCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false)
|
||||
{
|
||||
std::string s;
|
||||
std::string s = "expects ";
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
s = "expects ";
|
||||
if (isVariadic)
|
||||
s += "at least ";
|
||||
|
||||
if (isVariadic)
|
||||
s += "at least ";
|
||||
|
||||
s += std::to_string(expectedCount) + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "expects " + std::to_string(expectedCount) + " ";
|
||||
}
|
||||
s += std::to_string(expectedCount) + " ";
|
||||
|
||||
if (argPrefix)
|
||||
s += std::string(argPrefix) + " ";
|
||||
@ -188,10 +145,7 @@ struct ErrorConverter
|
||||
return "Function only returns " + std::to_string(e.expected) + " value" + expectedS + ". " + std::to_string(e.actual) +
|
||||
" are required here";
|
||||
case CountMismatch::Arg:
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
return "Argument count mismatch. Function " + wrongNumberOfArgsString(e.expected, e.actual);
|
||||
else
|
||||
return "Argument count mismatch. Function " + wrongNumberOfArgsString_DEPRECATED(e.expected, e.actual);
|
||||
return "Argument count mismatch. Function " + wrongNumberOfArgsString(e.expected, e.actual);
|
||||
}
|
||||
|
||||
LUAU_ASSERT(!"Unknown context");
|
||||
@ -232,7 +186,7 @@ struct ErrorConverter
|
||||
std::string operator()(const Luau::IncorrectGenericParameterCount& e) const
|
||||
{
|
||||
std::string name = e.name;
|
||||
if (!e.typeFun.typeParams.empty() || (FFlag::LuauTypeAliasPacks && !e.typeFun.typePackParams.empty()))
|
||||
if (!e.typeFun.typeParams.empty() || !e.typeFun.typePackParams.empty())
|
||||
{
|
||||
name += "<";
|
||||
bool first = true;
|
||||
@ -246,36 +200,25 @@ struct ErrorConverter
|
||||
name += toString(t);
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (TypePackId t : e.typeFun.typePackParams)
|
||||
{
|
||||
for (TypePackId t : e.typeFun.typePackParams)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
name += ", ";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
name += ", ";
|
||||
|
||||
name += toString(t);
|
||||
}
|
||||
name += toString(t);
|
||||
}
|
||||
|
||||
name += ">";
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
if (e.typeFun.typeParams.size() != e.actualParameters)
|
||||
return "Generic type '" + name + "' " +
|
||||
wrongNumberOfArgsString(e.typeFun.typeParams.size(), e.actualParameters, "type", !e.typeFun.typePackParams.empty());
|
||||
if (e.typeFun.typeParams.size() != e.actualParameters)
|
||||
return "Generic type '" + name + "' " +
|
||||
wrongNumberOfArgsString(e.typeFun.typeParams.size(), e.actualParameters, "type", !e.typeFun.typePackParams.empty());
|
||||
|
||||
return "Generic type '" + name + "' " +
|
||||
wrongNumberOfArgsString(e.typeFun.typePackParams.size(), e.actualPackParameters, "type pack", /*isVariadic*/ false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Generic type '" + name + "' " +
|
||||
wrongNumberOfArgsString_DEPRECATED(e.typeFun.typeParams.size(), e.actualParameters, /*isTypeArgs*/ true);
|
||||
}
|
||||
return "Generic type '" + name + "' " +
|
||||
wrongNumberOfArgsString(e.typeFun.typePackParams.size(), e.actualPackParameters, "type pack", /*isVariadic*/ false);
|
||||
}
|
||||
|
||||
std::string operator()(const Luau::SyntaxError& e) const
|
||||
@ -591,11 +534,8 @@ bool IncorrectGenericParameterCount::operator==(const IncorrectGenericParameterC
|
||||
if (typeFun.typeParams.size() != rhs.typeFun.typeParams.size())
|
||||
return false;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
if (typeFun.typePackParams.size() != rhs.typeFun.typePackParams.size())
|
||||
return false;
|
||||
}
|
||||
if (typeFun.typePackParams.size() != rhs.typeFun.typePackParams.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < typeFun.typeParams.size(); ++i)
|
||||
{
|
||||
@ -603,13 +543,10 @@ bool IncorrectGenericParameterCount::operator==(const IncorrectGenericParameterC
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (size_t i = 0; i < typeFun.typePackParams.size(); ++i)
|
||||
{
|
||||
for (size_t i = 0; i < typeFun.typePackParams.size(); ++i)
|
||||
{
|
||||
if (typeFun.typePackParams[i] != rhs.typeFun.typePackParams[i])
|
||||
return false;
|
||||
}
|
||||
if (typeFun.typePackParams[i] != rhs.typeFun.typePackParams[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -733,14 +670,14 @@ bool containsParseErrorName(const TypeError& error)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
|
||||
void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState cloneState)
|
||||
{
|
||||
auto clone = [&](auto&& ty) {
|
||||
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks);
|
||||
return ::Luau::clone(ty, destArena, seenTypes, seenTypePacks, cloneState);
|
||||
};
|
||||
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
copyError(e, destArena, seenTypes, seenTypePacks);
|
||||
copyError(e, destArena, seenTypes, seenTypePacks, cloneState);
|
||||
};
|
||||
|
||||
if constexpr (false)
|
||||
@ -864,9 +801,10 @@ void copyErrors(ErrorVec& errors, TypeArena& destArena)
|
||||
{
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
copyError(e, destArena, seenTypes, seenTypePacks);
|
||||
copyError(e, destArena, seenTypes, seenTypePacks, cloneState);
|
||||
};
|
||||
|
||||
LUAU_ASSERT(!destArena.typeVars.isFrozen());
|
||||
|
@ -18,10 +18,7 @@
|
||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
|
||||
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -96,10 +93,11 @@ LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, ScopePtr t
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
for (const auto& [name, ty] : checkedModule->declaredGlobals)
|
||||
{
|
||||
TypeId globalTy = clone(ty, typeChecker.globalTypes, seenTypes, seenTypePacks);
|
||||
TypeId globalTy = clone(ty, typeChecker.globalTypes, seenTypes, seenTypePacks, cloneState);
|
||||
std::string documentationSymbol = packageName + "/global/" + name;
|
||||
generateDocumentationSymbols(globalTy, documentationSymbol);
|
||||
targetScope->bindings[typeChecker.globalNames.names->getOrAdd(name.c_str())] = {globalTy, Location(), false, {}, documentationSymbol};
|
||||
@ -110,7 +108,7 @@ LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, ScopePtr t
|
||||
|
||||
for (const auto& [name, ty] : checkedModule->getModuleScope()->exportedTypeBindings)
|
||||
{
|
||||
TypeFun globalTy = clone(ty, typeChecker.globalTypes, seenTypes, seenTypePacks);
|
||||
TypeFun globalTy = clone(ty, typeChecker.globalTypes, seenTypes, seenTypePacks, cloneState);
|
||||
std::string documentationSymbol = packageName + "/globaltype/" + name;
|
||||
generateDocumentationSymbols(globalTy.type, documentationSymbol);
|
||||
targetScope->exportedTypeBindings[name] = globalTy;
|
||||
@ -427,15 +425,16 @@ CheckResult Frontend::check(const ModuleName& name)
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
for (const auto& [expr, strictTy] : strictModule->astTypes)
|
||||
module->astTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks);
|
||||
module->astTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (const auto& [expr, strictTy] : strictModule->astOriginalCallTypes)
|
||||
module->astOriginalCallTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks);
|
||||
module->astOriginalCallTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (const auto& [expr, strictTy] : strictModule->astExpectedTypes)
|
||||
module->astExpectedTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks);
|
||||
module->astExpectedTypes[expr] = clone(strictTy, module->interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
|
||||
stats.timeCheck += getTimestamp() - timestamp;
|
||||
@ -885,16 +884,13 @@ std::optional<ModuleInfo> FrontendModuleResolver::resolveModuleInfo(const Module
|
||||
// If we can't find the current module name, that's because we bypassed the frontend's initializer
|
||||
// and called typeChecker.check directly. (This is done by autocompleteSource, for example).
|
||||
// In that case, requires will always fail.
|
||||
if (FFlag::LuauResolveModuleNameWithoutACurrentModule)
|
||||
return std::nullopt;
|
||||
else
|
||||
throw std::runtime_error("Frontend::resolveModuleName: Unknown currentModuleName '" + currentModuleName + "'");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto& exprs = it->second.exprs;
|
||||
|
||||
const ModuleInfo* info = exprs.find(&pathExpr);
|
||||
if (!info || (!FFlag::LuauNewRequireTrace2 && info->name.empty()))
|
||||
if (!info)
|
||||
return std::nullopt;
|
||||
|
||||
return *info;
|
||||
@ -911,10 +907,7 @@ const ModulePtr FrontendModuleResolver::getModule(const ModuleName& moduleName)
|
||||
|
||||
bool FrontendModuleResolver::moduleExists(const ModuleName& moduleName) const
|
||||
{
|
||||
if (FFlag::LuauNewRequireTrace2)
|
||||
return frontend->sourceNodes.count(moduleName) != 0;
|
||||
else
|
||||
return frontend->fileResolver->moduleExists(moduleName);
|
||||
return frontend->sourceNodes.count(moduleName) != 0;
|
||||
}
|
||||
|
||||
std::string FrontendModuleResolver::getHumanReadableModuleName(const ModuleName& moduleName) const
|
||||
|
@ -2,8 +2,6 @@
|
||||
#include "Luau/IostreamHelpers.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -94,7 +92,7 @@ std::ostream& operator<<(std::ostream& stream, const IncorrectGenericParameterCo
|
||||
{
|
||||
stream << "IncorrectGenericParameterCount { name = " << error.name;
|
||||
|
||||
if (!error.typeFun.typeParams.empty() || (FFlag::LuauTypeAliasPacks && !error.typeFun.typePackParams.empty()))
|
||||
if (!error.typeFun.typeParams.empty() || !error.typeFun.typePackParams.empty())
|
||||
{
|
||||
stream << "<";
|
||||
bool first = true;
|
||||
@ -108,17 +106,14 @@ std::ostream& operator<<(std::ostream& stream, const IncorrectGenericParameterCo
|
||||
stream << toString(t);
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (TypePackId t : error.typeFun.typePackParams)
|
||||
{
|
||||
for (TypePackId t : error.typeFun.typePackParams)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
stream << ", ";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
stream << ", ";
|
||||
|
||||
stream << toString(t);
|
||||
}
|
||||
stream << toString(t);
|
||||
}
|
||||
|
||||
stream << ">";
|
||||
|
@ -5,8 +5,6 @@
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/Common.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -615,12 +613,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||
writeNode(node, "AstStatTypeAlias", [&]() {
|
||||
PROP(name);
|
||||
PROP(generics);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
PROP(genericPacks);
|
||||
}
|
||||
|
||||
PROP(genericPacks);
|
||||
PROP(type);
|
||||
PROP(exported);
|
||||
});
|
||||
|
@ -1,20 +1,20 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/Module.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
||||
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCloneBoundTables, false)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 0)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -120,12 +120,6 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
|
||||
return allocated;
|
||||
}
|
||||
|
||||
using SeenTypes = std::unordered_map<TypeId, TypeId>;
|
||||
using SeenTypePacks = std::unordered_map<TypePackId, TypePackId>;
|
||||
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType);
|
||||
TypeId clone(TypeId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType);
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -138,11 +132,12 @@ struct TypePackCloner;
|
||||
|
||||
struct TypeCloner
|
||||
{
|
||||
TypeCloner(TypeArena& dest, TypeId typeId, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
|
||||
TypeCloner(TypeArena& dest, TypeId typeId, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState)
|
||||
: dest(dest)
|
||||
, typeId(typeId)
|
||||
, seenTypes(seenTypes)
|
||||
, seenTypePacks(seenTypePacks)
|
||||
, cloneState(cloneState)
|
||||
{
|
||||
}
|
||||
|
||||
@ -150,8 +145,7 @@ struct TypeCloner
|
||||
TypeId typeId;
|
||||
SeenTypes& seenTypes;
|
||||
SeenTypePacks& seenTypePacks;
|
||||
|
||||
bool* encounteredFreeType = nullptr;
|
||||
CloneState& cloneState;
|
||||
|
||||
template<typename T>
|
||||
void defaultClone(const T& t);
|
||||
@ -178,13 +172,14 @@ struct TypePackCloner
|
||||
TypePackId typePackId;
|
||||
SeenTypes& seenTypes;
|
||||
SeenTypePacks& seenTypePacks;
|
||||
bool* encounteredFreeType = nullptr;
|
||||
CloneState& cloneState;
|
||||
|
||||
TypePackCloner(TypeArena& dest, TypePackId typePackId, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks)
|
||||
TypePackCloner(TypeArena& dest, TypePackId typePackId, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState)
|
||||
: dest(dest)
|
||||
, typePackId(typePackId)
|
||||
, seenTypes(seenTypes)
|
||||
, seenTypePacks(seenTypePacks)
|
||||
, cloneState(cloneState)
|
||||
{
|
||||
}
|
||||
|
||||
@ -197,8 +192,7 @@ struct TypePackCloner
|
||||
|
||||
void operator()(const Unifiable::Free& t)
|
||||
{
|
||||
if (encounteredFreeType)
|
||||
*encounteredFreeType = true;
|
||||
cloneState.encounteredFreeType = true;
|
||||
|
||||
TypePackId err = singletonTypes.errorRecoveryTypePack(singletonTypes.anyTypePack);
|
||||
TypePackId cloned = dest.addTypePack(*err);
|
||||
@ -218,13 +212,13 @@ struct TypePackCloner
|
||||
// We just need to be sure that we rewrite pointers both to the binder and the bindee to the same pointer.
|
||||
void operator()(const Unifiable::Bound<TypePackId>& t)
|
||||
{
|
||||
TypePackId cloned = clone(t.boundTo, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
TypePackId cloned = clone(t.boundTo, dest, seenTypes, seenTypePacks, cloneState);
|
||||
seenTypePacks[typePackId] = cloned;
|
||||
}
|
||||
|
||||
void operator()(const VariadicTypePack& t)
|
||||
{
|
||||
TypePackId cloned = dest.addTypePack(TypePackVar{VariadicTypePack{clone(t.ty, dest, seenTypes, seenTypePacks, encounteredFreeType)}});
|
||||
TypePackId cloned = dest.addTypePack(TypePackVar{VariadicTypePack{clone(t.ty, dest, seenTypes, seenTypePacks, cloneState)}});
|
||||
seenTypePacks[typePackId] = cloned;
|
||||
}
|
||||
|
||||
@ -236,10 +230,10 @@ struct TypePackCloner
|
||||
seenTypePacks[typePackId] = cloned;
|
||||
|
||||
for (TypeId ty : t.head)
|
||||
destTp->head.push_back(clone(ty, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
destTp->head.push_back(clone(ty, dest, seenTypes, seenTypePacks, cloneState));
|
||||
|
||||
if (t.tail)
|
||||
destTp->tail = clone(*t.tail, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
destTp->tail = clone(*t.tail, dest, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
};
|
||||
|
||||
@ -252,8 +246,7 @@ void TypeCloner::defaultClone(const T& t)
|
||||
|
||||
void TypeCloner::operator()(const Unifiable::Free& t)
|
||||
{
|
||||
if (encounteredFreeType)
|
||||
*encounteredFreeType = true;
|
||||
cloneState.encounteredFreeType = true;
|
||||
TypeId err = singletonTypes.errorRecoveryType(singletonTypes.anyType);
|
||||
TypeId cloned = dest.addType(*err);
|
||||
seenTypes[typeId] = cloned;
|
||||
@ -266,7 +259,7 @@ void TypeCloner::operator()(const Unifiable::Generic& t)
|
||||
|
||||
void TypeCloner::operator()(const Unifiable::Bound<TypeId>& t)
|
||||
{
|
||||
TypeId boundTo = clone(t.boundTo, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
TypeId boundTo = clone(t.boundTo, dest, seenTypes, seenTypePacks, cloneState);
|
||||
seenTypes[typeId] = boundTo;
|
||||
}
|
||||
|
||||
@ -294,23 +287,23 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
|
||||
seenTypes[typeId] = result;
|
||||
|
||||
for (TypeId generic : t.generics)
|
||||
ftv->generics.push_back(clone(generic, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
ftv->generics.push_back(clone(generic, dest, seenTypes, seenTypePacks, cloneState));
|
||||
|
||||
for (TypePackId genericPack : t.genericPacks)
|
||||
ftv->genericPacks.push_back(clone(genericPack, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
ftv->genericPacks.push_back(clone(genericPack, dest, seenTypes, seenTypePacks, cloneState));
|
||||
|
||||
ftv->tags = t.tags;
|
||||
ftv->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
ftv->argTypes = clone(t.argTypes, dest, seenTypes, seenTypePacks, cloneState);
|
||||
ftv->argNames = t.argNames;
|
||||
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
ftv->retType = clone(t.retType, dest, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
|
||||
void TypeCloner::operator()(const TableTypeVar& t)
|
||||
{
|
||||
// If table is now bound to another one, we ignore the content of the original
|
||||
if (FFlag::LuauCloneBoundTables && t.boundTo)
|
||||
if (t.boundTo)
|
||||
{
|
||||
TypeId boundTo = clone(*t.boundTo, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
TypeId boundTo = clone(*t.boundTo, dest, seenTypes, seenTypePacks, cloneState);
|
||||
seenTypes[typeId] = boundTo;
|
||||
return;
|
||||
}
|
||||
@ -326,34 +319,21 @@ void TypeCloner::operator()(const TableTypeVar& t)
|
||||
ttv->level = TypeLevel{0, 0};
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
ttv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, cloneState), prop.deprecated, {}, prop.location, prop.tags};
|
||||
|
||||
if (t.indexer)
|
||||
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, encounteredFreeType),
|
||||
clone(t.indexer->indexResultType, dest, seenTypes, seenTypePacks, encounteredFreeType)};
|
||||
|
||||
if (!FFlag::LuauCloneBoundTables)
|
||||
{
|
||||
if (t.boundTo)
|
||||
ttv->boundTo = clone(*t.boundTo, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
}
|
||||
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, seenTypes, seenTypePacks, cloneState),
|
||||
clone(t.indexer->indexResultType, dest, seenTypes, seenTypePacks, cloneState)};
|
||||
|
||||
for (TypeId& arg : ttv->instantiatedTypeParams)
|
||||
arg = clone(arg, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
arg = clone(arg, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId& arg : ttv->instantiatedTypePackParams)
|
||||
arg = clone(arg, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
}
|
||||
for (TypePackId& arg : ttv->instantiatedTypePackParams)
|
||||
arg = clone(arg, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
if (ttv->state == TableState::Free)
|
||||
{
|
||||
if (FFlag::LuauCloneBoundTables || !t.boundTo)
|
||||
{
|
||||
if (encounteredFreeType)
|
||||
*encounteredFreeType = true;
|
||||
}
|
||||
cloneState.encounteredFreeType = true;
|
||||
|
||||
ttv->state = TableState::Sealed;
|
||||
}
|
||||
@ -369,8 +349,8 @@ void TypeCloner::operator()(const MetatableTypeVar& t)
|
||||
MetatableTypeVar* mtv = getMutable<MetatableTypeVar>(result);
|
||||
seenTypes[typeId] = result;
|
||||
|
||||
mtv->table = clone(t.table, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
mtv->metatable = clone(t.metatable, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
mtv->table = clone(t.table, dest, seenTypes, seenTypePacks, cloneState);
|
||||
mtv->metatable = clone(t.metatable, dest, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
|
||||
void TypeCloner::operator()(const ClassTypeVar& t)
|
||||
@ -381,13 +361,13 @@ void TypeCloner::operator()(const ClassTypeVar& t)
|
||||
seenTypes[typeId] = result;
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, encounteredFreeType), prop.deprecated, {}, prop.location, prop.tags};
|
||||
ctv->props[name] = {clone(prop.type, dest, seenTypes, seenTypePacks, cloneState), prop.deprecated, {}, prop.location, prop.tags};
|
||||
|
||||
if (t.parent)
|
||||
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
ctv->parent = clone(*t.parent, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
if (t.metatable)
|
||||
ctv->metatable = clone(*t.metatable, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
ctv->metatable = clone(*t.metatable, dest, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
|
||||
void TypeCloner::operator()(const AnyTypeVar& t)
|
||||
@ -404,7 +384,7 @@ void TypeCloner::operator()(const UnionTypeVar& t)
|
||||
LUAU_ASSERT(option != nullptr);
|
||||
|
||||
for (TypeId ty : t.options)
|
||||
option->options.push_back(clone(ty, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
option->options.push_back(clone(ty, dest, seenTypes, seenTypePacks, cloneState));
|
||||
}
|
||||
|
||||
void TypeCloner::operator()(const IntersectionTypeVar& t)
|
||||
@ -416,7 +396,7 @@ void TypeCloner::operator()(const IntersectionTypeVar& t)
|
||||
LUAU_ASSERT(option != nullptr);
|
||||
|
||||
for (TypeId ty : t.parts)
|
||||
option->parts.push_back(clone(ty, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
option->parts.push_back(clone(ty, dest, seenTypes, seenTypePacks, cloneState));
|
||||
}
|
||||
|
||||
void TypeCloner::operator()(const LazyTypeVar& t)
|
||||
@ -426,17 +406,18 @@ void TypeCloner::operator()(const LazyTypeVar& t)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType)
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState)
|
||||
{
|
||||
if (tp->persistent)
|
||||
return tp;
|
||||
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||
|
||||
TypePackId& res = seenTypePacks[tp];
|
||||
|
||||
if (res == nullptr)
|
||||
{
|
||||
TypePackCloner cloner{dest, tp, seenTypes, seenTypePacks};
|
||||
cloner.encounteredFreeType = encounteredFreeType;
|
||||
TypePackCloner cloner{dest, tp, seenTypes, seenTypePacks, cloneState};
|
||||
Luau::visit(cloner, tp->ty); // Mutates the storage that 'res' points into.
|
||||
}
|
||||
|
||||
@ -446,17 +427,18 @@ TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypeP
|
||||
return res;
|
||||
}
|
||||
|
||||
TypeId clone(TypeId typeId, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType)
|
||||
TypeId clone(TypeId typeId, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState)
|
||||
{
|
||||
if (typeId->persistent)
|
||||
return typeId;
|
||||
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||
|
||||
TypeId& res = seenTypes[typeId];
|
||||
|
||||
if (res == nullptr)
|
||||
{
|
||||
TypeCloner cloner{dest, typeId, seenTypes, seenTypePacks};
|
||||
cloner.encounteredFreeType = encounteredFreeType;
|
||||
TypeCloner cloner{dest, typeId, seenTypes, seenTypePacks, cloneState};
|
||||
Luau::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
|
||||
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
|
||||
}
|
||||
@ -467,19 +449,16 @@ TypeId clone(TypeId typeId, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks
|
||||
return res;
|
||||
}
|
||||
|
||||
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType)
|
||||
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, CloneState& cloneState)
|
||||
{
|
||||
TypeFun result;
|
||||
for (TypeId ty : typeFun.typeParams)
|
||||
result.typeParams.push_back(clone(ty, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
result.typeParams.push_back(clone(ty, dest, seenTypes, seenTypePacks, cloneState));
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId tp : typeFun.typePackParams)
|
||||
result.typePackParams.push_back(clone(tp, dest, seenTypes, seenTypePacks, encounteredFreeType));
|
||||
}
|
||||
for (TypePackId tp : typeFun.typePackParams)
|
||||
result.typePackParams.push_back(clone(tp, dest, seenTypes, seenTypePacks, cloneState));
|
||||
|
||||
result.type = clone(typeFun.type, dest, seenTypes, seenTypePacks, encounteredFreeType);
|
||||
result.type = clone(typeFun.type, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -519,19 +498,18 @@ bool Module::clonePublicInterface()
|
||||
LUAU_ASSERT(interfaceTypes.typeVars.empty());
|
||||
LUAU_ASSERT(interfaceTypes.typePacks.empty());
|
||||
|
||||
bool encounteredFreeType = false;
|
||||
|
||||
SeenTypePacks seenTypePacks;
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
ScopePtr moduleScope = getModuleScope();
|
||||
|
||||
moduleScope->returnType = clone(moduleScope->returnType, interfaceTypes, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
moduleScope->returnType = clone(moduleScope->returnType, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
if (moduleScope->varargPack)
|
||||
moduleScope->varargPack = clone(*moduleScope->varargPack, interfaceTypes, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
moduleScope->varargPack = clone(*moduleScope->varargPack, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (auto& pair : moduleScope->exportedTypeBindings)
|
||||
pair.second = clone(pair.second, interfaceTypes, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
pair.second = clone(pair.second, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (TypeId ty : moduleScope->returnType)
|
||||
if (get<GenericTypeVar>(follow(ty)))
|
||||
@ -540,7 +518,7 @@ bool Module::clonePublicInterface()
|
||||
freeze(internalTypes);
|
||||
freeze(interfaceTypes);
|
||||
|
||||
return encounteredFreeType;
|
||||
return cloneState.encounteredFreeType;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyVisitOnce, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -79,7 +81,16 @@ struct Quantifier
|
||||
void quantify(ModulePtr module, TypeId ty, TypeLevel level)
|
||||
{
|
||||
Quantifier q{std::move(module), level};
|
||||
visitTypeVar(ty, q);
|
||||
|
||||
if (FFlag::LuauQuantifyVisitOnce)
|
||||
{
|
||||
DenseHashSet<void*> seen{nullptr};
|
||||
visitTypeVarOnce(ty, q, seen);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitTypeVar(ty, q);
|
||||
}
|
||||
|
||||
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
|
@ -4,182 +4,9 @@
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Module.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTraceRequireLookupChild, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewRequireTrace2, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct RequireTracerOld : AstVisitor
|
||||
{
|
||||
explicit RequireTracerOld(FileResolver* fileResolver, const ModuleName& currentModuleName)
|
||||
: fileResolver(fileResolver)
|
||||
, currentModuleName(currentModuleName)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauNewRequireTrace2);
|
||||
}
|
||||
|
||||
FileResolver* const fileResolver;
|
||||
ModuleName currentModuleName;
|
||||
DenseHashMap<AstLocal*, ModuleName> locals{nullptr};
|
||||
RequireTraceResult result;
|
||||
|
||||
std::optional<ModuleName> fromAstFragment(AstExpr* expr)
|
||||
{
|
||||
if (auto g = expr->as<AstExprGlobal>(); g && g->name == "script")
|
||||
return currentModuleName;
|
||||
|
||||
return fileResolver->fromAstFragment(expr);
|
||||
}
|
||||
|
||||
bool visit(AstStatLocal* stat) override
|
||||
{
|
||||
for (size_t i = 0; i < stat->vars.size; ++i)
|
||||
{
|
||||
AstLocal* local = stat->vars.data[i];
|
||||
|
||||
if (local->annotation)
|
||||
{
|
||||
if (AstTypeTypeof* ann = local->annotation->as<AstTypeTypeof>())
|
||||
ann->expr->visit(this);
|
||||
}
|
||||
|
||||
if (i < stat->values.size)
|
||||
{
|
||||
AstExpr* expr = stat->values.data[i];
|
||||
expr->visit(this);
|
||||
|
||||
const ModuleInfo* info = result.exprs.find(expr);
|
||||
if (info)
|
||||
locals[local] = info->name;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprGlobal* global) override
|
||||
{
|
||||
std::optional<ModuleName> name = fromAstFragment(global);
|
||||
if (name)
|
||||
result.exprs[global] = {*name};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprLocal* local) override
|
||||
{
|
||||
const ModuleName* name = locals.find(local->local);
|
||||
if (name)
|
||||
result.exprs[local] = {*name};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprIndexName* indexName) override
|
||||
{
|
||||
indexName->expr->visit(this);
|
||||
|
||||
const ModuleInfo* info = result.exprs.find(indexName->expr);
|
||||
if (info)
|
||||
{
|
||||
if (indexName->index == "parent" || indexName->index == "Parent")
|
||||
{
|
||||
if (auto parent = fileResolver->getParentModuleName(info->name))
|
||||
result.exprs[indexName] = {*parent};
|
||||
}
|
||||
else
|
||||
result.exprs[indexName] = {fileResolver->concat(info->name, indexName->index.value)};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprIndexExpr* indexExpr) override
|
||||
{
|
||||
indexExpr->expr->visit(this);
|
||||
|
||||
const ModuleInfo* info = result.exprs.find(indexExpr->expr);
|
||||
const AstExprConstantString* str = indexExpr->index->as<AstExprConstantString>();
|
||||
if (info && str)
|
||||
{
|
||||
result.exprs[indexExpr] = {fileResolver->concat(info->name, std::string_view(str->value.data, str->value.size))};
|
||||
}
|
||||
|
||||
indexExpr->index->visit(this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprTypeAssertion* expr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we see game:GetService("StringLiteral") or Game:GetService("StringLiteral"), then rewrite to game.StringLiteral.
|
||||
// Else traverse arguments and trace requires to them.
|
||||
bool visit(AstExprCall* call) override
|
||||
{
|
||||
for (AstExpr* arg : call->args)
|
||||
arg->visit(this);
|
||||
|
||||
call->func->visit(this);
|
||||
|
||||
AstExprGlobal* globalName = call->func->as<AstExprGlobal>();
|
||||
if (globalName && globalName->name == "require" && call->args.size >= 1)
|
||||
{
|
||||
if (const ModuleInfo* moduleInfo = result.exprs.find(call->args.data[0]))
|
||||
result.requires.push_back({moduleInfo->name, call->location});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AstExprIndexName* indexName = call->func->as<AstExprIndexName>();
|
||||
if (!indexName)
|
||||
return false;
|
||||
|
||||
std::optional<ModuleName> rootName = fromAstFragment(indexName->expr);
|
||||
|
||||
if (FFlag::LuauTraceRequireLookupChild && !rootName)
|
||||
{
|
||||
if (const ModuleInfo* moduleInfo = result.exprs.find(indexName->expr))
|
||||
rootName = moduleInfo->name;
|
||||
}
|
||||
|
||||
if (!rootName)
|
||||
return false;
|
||||
|
||||
bool supportedLookup = indexName->index == "GetService" ||
|
||||
(FFlag::LuauTraceRequireLookupChild && (indexName->index == "FindFirstChild" || indexName->index == "WaitForChild"));
|
||||
|
||||
if (!supportedLookup)
|
||||
return false;
|
||||
|
||||
if (call->args.size != 1)
|
||||
return false;
|
||||
|
||||
AstExprConstantString* name = call->args.data[0]->as<AstExprConstantString>();
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
std::string_view v{name->value.data, name->value.size};
|
||||
if (v.end() != std::find(v.begin(), v.end(), '/'))
|
||||
return false;
|
||||
|
||||
result.exprs[call] = {fileResolver->concat(*rootName, v)};
|
||||
|
||||
// 'WaitForChild' can be used on modules that are not available at the typecheck time, but will be available at runtime
|
||||
// If we fail to find such module, we will not report an UnknownRequire error
|
||||
if (FFlag::LuauTraceRequireLookupChild && indexName->index == "WaitForChild")
|
||||
result.exprs[call].optional = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct RequireTracer : AstVisitor
|
||||
{
|
||||
RequireTracer(RequireTraceResult& result, FileResolver* fileResolver, const ModuleName& currentModuleName)
|
||||
@ -188,7 +15,6 @@ struct RequireTracer : AstVisitor
|
||||
, currentModuleName(currentModuleName)
|
||||
, locals(nullptr)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewRequireTrace2);
|
||||
}
|
||||
|
||||
bool visit(AstExprTypeAssertion* expr) override
|
||||
@ -328,24 +154,13 @@ struct RequireTracer : AstVisitor
|
||||
std::vector<AstExprCall*> requires;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName)
|
||||
{
|
||||
if (FFlag::LuauNewRequireTrace2)
|
||||
{
|
||||
RequireTraceResult result;
|
||||
RequireTracer tracer{result, fileResolver, currentModuleName};
|
||||
root->visit(&tracer);
|
||||
tracer.process();
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
RequireTracerOld tracer{fileResolver, currentModuleName};
|
||||
root->visit(&tracer);
|
||||
return tracer.result;
|
||||
}
|
||||
RequireTraceResult result;
|
||||
RequireTracer tracer{result, fileResolver, currentModuleName};
|
||||
root->visit(&tracer);
|
||||
tracer.process();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -7,8 +7,6 @@
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionDontReplaceIgnoredTypes, false)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -39,11 +37,8 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
||||
for (TypeId itp : ttv->instantiatedTypeParams)
|
||||
visitChild(itp);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId itp : ttv->instantiatedTypePackParams)
|
||||
visitChild(itp);
|
||||
}
|
||||
for (TypePackId itp : ttv->instantiatedTypePackParams)
|
||||
visitChild(itp);
|
||||
}
|
||||
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
||||
{
|
||||
@ -339,10 +334,10 @@ std::optional<TypeId> Substitution::substitute(TypeId ty)
|
||||
return std::nullopt;
|
||||
|
||||
for (auto [oldTy, newTy] : newTypes)
|
||||
if (!FFlag::LuauSubstitutionDontReplaceIgnoredTypes || !ignoreChildren(oldTy))
|
||||
if (!ignoreChildren(oldTy))
|
||||
replaceChildren(newTy);
|
||||
for (auto [oldTp, newTp] : newPacks)
|
||||
if (!FFlag::LuauSubstitutionDontReplaceIgnoredTypes || !ignoreChildren(oldTp))
|
||||
if (!ignoreChildren(oldTp))
|
||||
replaceChildren(newTp);
|
||||
TypeId newTy = replace(ty);
|
||||
return newTy;
|
||||
@ -359,10 +354,10 @@ std::optional<TypePackId> Substitution::substitute(TypePackId tp)
|
||||
return std::nullopt;
|
||||
|
||||
for (auto [oldTy, newTy] : newTypes)
|
||||
if (!FFlag::LuauSubstitutionDontReplaceIgnoredTypes || !ignoreChildren(oldTy))
|
||||
if (!ignoreChildren(oldTy))
|
||||
replaceChildren(newTy);
|
||||
for (auto [oldTp, newTp] : newPacks)
|
||||
if (!FFlag::LuauSubstitutionDontReplaceIgnoredTypes || !ignoreChildren(oldTp))
|
||||
if (!ignoreChildren(oldTp))
|
||||
replaceChildren(newTp);
|
||||
TypePackId newTp = replace(tp);
|
||||
return newTp;
|
||||
@ -393,10 +388,7 @@ TypeId Substitution::clone(TypeId ty)
|
||||
clone.name = ttv->name;
|
||||
clone.syntheticName = ttv->syntheticName;
|
||||
clone.instantiatedTypeParams = ttv->instantiatedTypeParams;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
|
||||
|
||||
clone.instantiatedTypePackParams = ttv->instantiatedTypePackParams;
|
||||
clone.tags = ttv->tags;
|
||||
result = addType(std::move(clone));
|
||||
}
|
||||
@ -505,11 +497,8 @@ void Substitution::replaceChildren(TypeId ty)
|
||||
for (TypeId& itp : ttv->instantiatedTypeParams)
|
||||
itp = replace(itp);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId& itp : ttv->instantiatedTypePackParams)
|
||||
itp = replace(itp);
|
||||
}
|
||||
for (TypePackId& itp : ttv->instantiatedTypePackParams)
|
||||
itp = replace(itp);
|
||||
}
|
||||
else if (MetatableTypeVar* mtv = getMutable<MetatableTypeVar>(ty))
|
||||
{
|
||||
|
378
Analysis/src/ToDot.cpp
Normal file
378
Analysis/src/ToDot.cpp
Normal file
@ -0,0 +1,378 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/ToDot.h"
|
||||
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct StateDot
|
||||
{
|
||||
StateDot(ToDotOptions opts)
|
||||
: opts(opts)
|
||||
{
|
||||
}
|
||||
|
||||
ToDotOptions opts;
|
||||
|
||||
std::unordered_set<TypeId> seenTy;
|
||||
std::unordered_set<TypePackId> seenTp;
|
||||
std::unordered_map<TypeId, int> tyToIndex;
|
||||
std::unordered_map<TypePackId, int> tpToIndex;
|
||||
int nextIndex = 1;
|
||||
std::string result;
|
||||
|
||||
bool canDuplicatePrimitive(TypeId ty);
|
||||
|
||||
void visitChildren(TypeId ty, int index);
|
||||
void visitChildren(TypePackId ty, int index);
|
||||
|
||||
void visitChild(TypeId ty, int parentIndex, const char* linkName = nullptr);
|
||||
void visitChild(TypePackId tp, int parentIndex, const char* linkName = nullptr);
|
||||
|
||||
void startNode(int index);
|
||||
void finishNode();
|
||||
|
||||
void startNodeLabel();
|
||||
void finishNodeLabel(TypeId ty);
|
||||
void finishNodeLabel(TypePackId tp);
|
||||
};
|
||||
|
||||
bool StateDot::canDuplicatePrimitive(TypeId ty)
|
||||
{
|
||||
if (get<BoundTypeVar>(ty))
|
||||
return false;
|
||||
|
||||
return get<PrimitiveTypeVar>(ty) || get<AnyTypeVar>(ty);
|
||||
}
|
||||
|
||||
void StateDot::visitChild(TypeId ty, int parentIndex, const char* linkName)
|
||||
{
|
||||
if (!tyToIndex.count(ty) || (opts.duplicatePrimitives && canDuplicatePrimitive(ty)))
|
||||
tyToIndex[ty] = nextIndex++;
|
||||
|
||||
int index = tyToIndex[ty];
|
||||
|
||||
if (parentIndex != 0)
|
||||
{
|
||||
if (linkName)
|
||||
formatAppend(result, "n%d -> n%d [label=\"%s\"];\n", parentIndex, index, linkName);
|
||||
else
|
||||
formatAppend(result, "n%d -> n%d;\n", parentIndex, index);
|
||||
}
|
||||
|
||||
if (opts.duplicatePrimitives && canDuplicatePrimitive(ty))
|
||||
{
|
||||
if (get<PrimitiveTypeVar>(ty))
|
||||
formatAppend(result, "n%d [label=\"%s\"];\n", index, toStringDetailed(ty, {}).name.c_str());
|
||||
else if (get<AnyTypeVar>(ty))
|
||||
formatAppend(result, "n%d [label=\"any\"];\n", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitChildren(ty, index);
|
||||
}
|
||||
}
|
||||
|
||||
void StateDot::visitChild(TypePackId tp, int parentIndex, const char* linkName)
|
||||
{
|
||||
if (!tpToIndex.count(tp))
|
||||
tpToIndex[tp] = nextIndex++;
|
||||
|
||||
if (parentIndex != 0)
|
||||
{
|
||||
if (linkName)
|
||||
formatAppend(result, "n%d -> n%d [label=\"%s\"];\n", parentIndex, tpToIndex[tp], linkName);
|
||||
else
|
||||
formatAppend(result, "n%d -> n%d;\n", parentIndex, tpToIndex[tp]);
|
||||
}
|
||||
|
||||
visitChildren(tp, tpToIndex[tp]);
|
||||
}
|
||||
|
||||
void StateDot::startNode(int index)
|
||||
{
|
||||
formatAppend(result, "n%d [", index);
|
||||
}
|
||||
|
||||
void StateDot::finishNode()
|
||||
{
|
||||
formatAppend(result, "];\n");
|
||||
}
|
||||
|
||||
void StateDot::startNodeLabel()
|
||||
{
|
||||
formatAppend(result, "label=\"");
|
||||
}
|
||||
|
||||
void StateDot::finishNodeLabel(TypeId ty)
|
||||
{
|
||||
if (opts.showPointers)
|
||||
formatAppend(result, "\n0x%p", ty);
|
||||
// additional common attributes can be added here as well
|
||||
result += "\"";
|
||||
}
|
||||
|
||||
void StateDot::finishNodeLabel(TypePackId tp)
|
||||
{
|
||||
if (opts.showPointers)
|
||||
formatAppend(result, "\n0x%p", tp);
|
||||
// additional common attributes can be added here as well
|
||||
result += "\"";
|
||||
}
|
||||
|
||||
void StateDot::visitChildren(TypeId ty, int index)
|
||||
{
|
||||
if (seenTy.count(ty))
|
||||
return;
|
||||
seenTy.insert(ty);
|
||||
|
||||
startNode(index);
|
||||
startNodeLabel();
|
||||
|
||||
if (const BoundTypeVar* btv = get<BoundTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "BoundTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(btv->boundTo, index);
|
||||
}
|
||||
else if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "FunctionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(ftv->argTypes, index, "arg");
|
||||
visitChild(ftv->retType, index, "ret");
|
||||
}
|
||||
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
||||
{
|
||||
if (ttv->name)
|
||||
formatAppend(result, "TableTypeVar %s", ttv->name->c_str());
|
||||
else if (ttv->syntheticName)
|
||||
formatAppend(result, "TableTypeVar %s", ttv->syntheticName->c_str());
|
||||
else
|
||||
formatAppend(result, "TableTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
if (ttv->boundTo)
|
||||
return visitChild(*ttv->boundTo, index, "boundTo");
|
||||
|
||||
for (const auto& [name, prop] : ttv->props)
|
||||
visitChild(prop.type, index, name.c_str());
|
||||
if (ttv->indexer)
|
||||
{
|
||||
visitChild(ttv->indexer->indexType, index, "[index]");
|
||||
visitChild(ttv->indexer->indexResultType, index, "[value]");
|
||||
}
|
||||
for (TypeId itp : ttv->instantiatedTypeParams)
|
||||
visitChild(itp, index, "typeParam");
|
||||
|
||||
for (TypePackId itp : ttv->instantiatedTypePackParams)
|
||||
visitChild(itp, index, "typePackParam");
|
||||
}
|
||||
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "MetatableTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(mtv->table, index, "table");
|
||||
visitChild(mtv->metatable, index, "metatable");
|
||||
}
|
||||
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "UnionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (TypeId opt : utv->options)
|
||||
visitChild(opt, index);
|
||||
}
|
||||
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "IntersectionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (TypeId part : itv->parts)
|
||||
visitChild(part, index);
|
||||
}
|
||||
else if (const GenericTypeVar* gtv = get<GenericTypeVar>(ty))
|
||||
{
|
||||
if (gtv->explicitName)
|
||||
formatAppend(result, "GenericTypeVar %s", gtv->name.c_str());
|
||||
else
|
||||
formatAppend(result, "GenericTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "FreeTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<AnyTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "AnyTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<PrimitiveTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "PrimitiveTypeVar %s", toStringDetailed(ty, {}).name.c_str());
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<ErrorTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "ErrorTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (const ClassTypeVar* ctv = get<ClassTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "ClassTypeVar %s", ctv->name.c_str());
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (const auto& [name, prop] : ctv->props)
|
||||
visitChild(prop.type, index, name.c_str());
|
||||
|
||||
if (ctv->parent)
|
||||
visitChild(*ctv->parent, index, "[parent]");
|
||||
|
||||
if (ctv->metatable)
|
||||
visitChild(*ctv->metatable, index, "[metatable]");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"unknown type kind");
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
}
|
||||
|
||||
void StateDot::visitChildren(TypePackId tp, int index)
|
||||
{
|
||||
if (seenTp.count(tp))
|
||||
return;
|
||||
seenTp.insert(tp);
|
||||
|
||||
startNode(index);
|
||||
startNodeLabel();
|
||||
|
||||
if (const BoundTypePack* btp = get<BoundTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "BoundTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
visitChild(btp->boundTo, index);
|
||||
}
|
||||
else if (const TypePack* tpp = get<TypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "TypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
for (TypeId tv : tpp->head)
|
||||
visitChild(tv, index);
|
||||
if (tpp->tail)
|
||||
visitChild(*tpp->tail, index, "tail");
|
||||
}
|
||||
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "VariadicTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
visitChild(vtp->ty, index);
|
||||
}
|
||||
else if (const FreeTypePack* ftp = get<FreeTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "FreeTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else if (const GenericTypePack* gtp = get<GenericTypePack>(tp))
|
||||
{
|
||||
if (gtp->explicitName)
|
||||
formatAppend(result, "GenericTypePack %s", gtp->name.c_str());
|
||||
else
|
||||
formatAppend(result, "GenericTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<Unifiable::Error>(tp))
|
||||
{
|
||||
formatAppend(result, "ErrorTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"unknown type pack kind");
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string toDot(TypeId ty, const ToDotOptions& opts)
|
||||
{
|
||||
StateDot state{opts};
|
||||
|
||||
state.result = "digraph graphname {\n";
|
||||
state.visitChild(ty, 0);
|
||||
state.result += "}";
|
||||
|
||||
return state.result;
|
||||
}
|
||||
|
||||
std::string toDot(TypePackId tp, const ToDotOptions& opts)
|
||||
{
|
||||
StateDot state{opts};
|
||||
|
||||
state.result = "digraph graphname {\n";
|
||||
state.visitChild(tp, 0);
|
||||
state.result += "}";
|
||||
|
||||
return state.result;
|
||||
}
|
||||
|
||||
std::string toDot(TypeId ty)
|
||||
{
|
||||
return toDot(ty, {});
|
||||
}
|
||||
|
||||
std::string toDot(TypePackId tp)
|
||||
{
|
||||
return toDot(tp, {});
|
||||
}
|
||||
|
||||
void dumpDot(TypeId ty)
|
||||
{
|
||||
printf("%s\n", toDot(ty).c_str());
|
||||
}
|
||||
|
||||
void dumpDot(TypePackId tp)
|
||||
{
|
||||
printf("%s\n", toDot(tp).c_str());
|
||||
}
|
||||
|
||||
} // namespace Luau
|
@ -11,7 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFunctionArgumentNameSize, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -59,11 +59,8 @@ struct FindCyclicTypes
|
||||
for (TypeId itp : ttv.instantiatedTypeParams)
|
||||
visitTypeVar(itp, *this, seen);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId itp : ttv.instantiatedTypePackParams)
|
||||
visitTypeVar(itp, *this, seen);
|
||||
}
|
||||
for (TypePackId itp : ttv.instantiatedTypePackParams)
|
||||
visitTypeVar(itp, *this, seen);
|
||||
|
||||
return exhaustive;
|
||||
}
|
||||
@ -248,58 +245,45 @@ struct TypeVarStringifier
|
||||
|
||||
void stringify(const std::vector<TypeId>& types, const std::vector<TypePackId>& typePacks)
|
||||
{
|
||||
if (types.size() == 0 && (!FFlag::LuauTypeAliasPacks || typePacks.size() == 0))
|
||||
if (types.size() == 0 && typePacks.size() == 0)
|
||||
return;
|
||||
|
||||
if (types.size() || (FFlag::LuauTypeAliasPacks && typePacks.size()))
|
||||
if (types.size() || typePacks.size())
|
||||
state.emit("<");
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
bool first = true;
|
||||
bool first = true;
|
||||
|
||||
for (TypeId ty : types)
|
||||
{
|
||||
if (!first)
|
||||
state.emit(", ");
|
||||
for (TypeId ty : types)
|
||||
{
|
||||
if (!first)
|
||||
state.emit(", ");
|
||||
first = false;
|
||||
|
||||
stringify(ty);
|
||||
}
|
||||
|
||||
bool singleTp = typePacks.size() == 1;
|
||||
|
||||
for (TypePackId tp : typePacks)
|
||||
{
|
||||
if (isEmpty(tp) && singleTp)
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
state.emit(", ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
stringify(ty);
|
||||
}
|
||||
if (!singleTp)
|
||||
state.emit("(");
|
||||
|
||||
bool singleTp = typePacks.size() == 1;
|
||||
stringify(tp);
|
||||
|
||||
for (TypePackId tp : typePacks)
|
||||
{
|
||||
if (isEmpty(tp) && singleTp)
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
state.emit(", ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
if (!singleTp)
|
||||
state.emit("(");
|
||||
|
||||
stringify(tp);
|
||||
|
||||
if (!singleTp)
|
||||
state.emit(")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < types.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
state.emit(", ");
|
||||
|
||||
stringify(types[i]);
|
||||
}
|
||||
if (!singleTp)
|
||||
state.emit(")");
|
||||
}
|
||||
|
||||
if (types.size() || (FFlag::LuauTypeAliasPacks && typePacks.size()))
|
||||
if (types.size() || typePacks.size())
|
||||
state.emit(">");
|
||||
}
|
||||
|
||||
@ -767,12 +751,23 @@ struct TypePackStringifier
|
||||
else
|
||||
state.emit(", ");
|
||||
|
||||
LUAU_ASSERT(elemNames.empty() || elemIndex < elemNames.size());
|
||||
|
||||
if (!elemNames.empty() && elemNames[elemIndex])
|
||||
if (FFlag::LuauFunctionArgumentNameSize)
|
||||
{
|
||||
state.emit(elemNames[elemIndex]->name);
|
||||
state.emit(": ");
|
||||
if (elemIndex < elemNames.size() && elemNames[elemIndex])
|
||||
{
|
||||
state.emit(elemNames[elemIndex]->name);
|
||||
state.emit(": ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(elemNames.empty() || elemIndex < elemNames.size());
|
||||
|
||||
if (!elemNames.empty() && elemNames[elemIndex])
|
||||
{
|
||||
state.emit(elemNames[elemIndex]->name);
|
||||
state.emit(": ");
|
||||
}
|
||||
}
|
||||
elemIndex++;
|
||||
|
||||
@ -929,38 +924,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||
|
||||
result.name += ttv->name ? *ttv->name : *ttv->syntheticName;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
tvs.stringify(ttv->instantiatedTypeParams, ttv->instantiatedTypePackParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ttv->instantiatedTypeParams.empty() && (!FFlag::LuauTypeAliasPacks || ttv->instantiatedTypePackParams.empty()))
|
||||
return result;
|
||||
|
||||
result.name += "<";
|
||||
|
||||
bool first = true;
|
||||
for (TypeId ty : ttv->instantiatedTypeParams)
|
||||
{
|
||||
if (!first)
|
||||
result.name += ", ";
|
||||
else
|
||||
first = false;
|
||||
|
||||
tvs.stringify(ty);
|
||||
}
|
||||
|
||||
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
||||
{
|
||||
result.truncated = true;
|
||||
result.name += "... <TRUNCATED>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.name += ">";
|
||||
}
|
||||
}
|
||||
tvs.stringify(ttv->instantiatedTypeParams, ttv->instantiatedTypePackParams);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1161,17 +1125,37 @@ std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeV
|
||||
s += ", ";
|
||||
first = false;
|
||||
|
||||
// argNames is guaranteed to be equal to argTypes iff argNames is not empty.
|
||||
// We don't currently respect opts.functionTypeArguments. I don't think this function should.
|
||||
if (!ftv.argNames.empty())
|
||||
s += (*argNameIter ? (*argNameIter)->name : "_") + ": ";
|
||||
s += toString_(*argPackIter);
|
||||
|
||||
++argPackIter;
|
||||
if (!ftv.argNames.empty())
|
||||
if (FFlag::LuauFunctionArgumentNameSize)
|
||||
{
|
||||
LUAU_ASSERT(argNameIter != ftv.argNames.end());
|
||||
++argNameIter;
|
||||
// We don't currently respect opts.functionTypeArguments. I don't think this function should.
|
||||
if (argNameIter != ftv.argNames.end())
|
||||
{
|
||||
s += (*argNameIter ? (*argNameIter)->name : "_") + ": ";
|
||||
++argNameIter;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "_: ";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// argNames is guaranteed to be equal to argTypes iff argNames is not empty.
|
||||
// We don't currently respect opts.functionTypeArguments. I don't think this function should.
|
||||
if (!ftv.argNames.empty())
|
||||
s += (*argNameIter ? (*argNameIter)->name : "_") + ": ";
|
||||
}
|
||||
|
||||
s += toString_(*argPackIter);
|
||||
++argPackIter;
|
||||
|
||||
if (!FFlag::LuauFunctionArgumentNameSize)
|
||||
{
|
||||
if (!ftv.argNames.empty())
|
||||
{
|
||||
LUAU_ASSERT(argNameIter != ftv.argNames.end());
|
||||
++argNameIter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isIdentifierStartChar(char c)
|
||||
@ -787,7 +785,7 @@ struct Printer
|
||||
|
||||
writer.keyword("type");
|
||||
writer.identifier(a->name.value);
|
||||
if (a->generics.size > 0 || (FFlag::LuauTypeAliasPacks && a->genericPacks.size > 0))
|
||||
if (a->generics.size > 0 || a->genericPacks.size > 0)
|
||||
{
|
||||
writer.symbol("<");
|
||||
CommaSeparatorInserter comma(writer);
|
||||
@ -798,14 +796,11 @@ struct Printer
|
||||
writer.identifier(o.value);
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (auto o : a->genericPacks)
|
||||
{
|
||||
for (auto o : a->genericPacks)
|
||||
{
|
||||
comma();
|
||||
writer.identifier(o.value);
|
||||
writer.symbol("...");
|
||||
}
|
||||
comma();
|
||||
writer.identifier(o.value);
|
||||
writer.symbol("...");
|
||||
}
|
||||
|
||||
writer.symbol(">");
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauShareTxnSeen, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -36,11 +34,8 @@ void TxnLog::rollback()
|
||||
for (auto it = tableChanges.rbegin(); it != tableChanges.rend(); ++it)
|
||||
std::swap(it->first->boundTo, it->second);
|
||||
|
||||
if (FFlag::LuauShareTxnSeen)
|
||||
{
|
||||
LUAU_ASSERT(originalSeenSize <= sharedSeen->size());
|
||||
sharedSeen->resize(originalSeenSize);
|
||||
}
|
||||
LUAU_ASSERT(originalSeenSize <= sharedSeen->size());
|
||||
sharedSeen->resize(originalSeenSize);
|
||||
}
|
||||
|
||||
void TxnLog::concat(TxnLog rhs)
|
||||
@ -53,45 +48,25 @@ void TxnLog::concat(TxnLog rhs)
|
||||
|
||||
tableChanges.insert(tableChanges.end(), rhs.tableChanges.begin(), rhs.tableChanges.end());
|
||||
rhs.tableChanges.clear();
|
||||
|
||||
if (!FFlag::LuauShareTxnSeen)
|
||||
{
|
||||
ownedSeen.swap(rhs.ownedSeen);
|
||||
rhs.ownedSeen.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool TxnLog::haveSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
if (FFlag::LuauShareTxnSeen)
|
||||
return (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair));
|
||||
else
|
||||
return (ownedSeen.end() != std::find(ownedSeen.begin(), ownedSeen.end(), sortedPair));
|
||||
return (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair));
|
||||
}
|
||||
|
||||
void TxnLog::pushSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
if (FFlag::LuauShareTxnSeen)
|
||||
sharedSeen->push_back(sortedPair);
|
||||
else
|
||||
ownedSeen.push_back(sortedPair);
|
||||
sharedSeen->push_back(sortedPair);
|
||||
}
|
||||
|
||||
void TxnLog::popSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
if (FFlag::LuauShareTxnSeen)
|
||||
{
|
||||
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
||||
sharedSeen->pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(sortedPair == ownedSeen.back());
|
||||
ownedSeen.pop_back();
|
||||
}
|
||||
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
||||
sharedSeen->pop_back();
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
|
||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||
{
|
||||
char* result = (char*)allocator.allocate(contents.size() + 1);
|
||||
@ -131,12 +129,9 @@ public:
|
||||
parameters.data[i] = {Luau::visit(*this, ttv.instantiatedTypeParams[i]->ty), {}};
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (size_t i = 0; i < ttv.instantiatedTypePackParams.size(); ++i)
|
||||
{
|
||||
for (size_t i = 0; i < ttv.instantiatedTypePackParams.size(); ++i)
|
||||
{
|
||||
parameters.data[i] = {{}, rehydrate(ttv.instantiatedTypePackParams[i])};
|
||||
}
|
||||
parameters.data[i] = {{}, rehydrate(ttv.instantiatedTypePackParams[i])};
|
||||
}
|
||||
|
||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName(ttv.name->c_str()), parameters.size != 0, parameters);
|
||||
@ -250,20 +245,7 @@ public:
|
||||
|
||||
AstTypePack* argTailAnnotation = nullptr;
|
||||
if (argTail)
|
||||
{
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
argTailAnnotation = rehydrate(*argTail);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypePackId tail = *argTail;
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tail))
|
||||
{
|
||||
argTailAnnotation = allocator->alloc<AstTypePackVariadic>(Location(), Luau::visit(*this, vtp->ty->ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
argTailAnnotation = rehydrate(*argTail);
|
||||
|
||||
AstArray<std::optional<AstArgumentName>> argNames;
|
||||
argNames.size = ftv.argNames.size();
|
||||
@ -292,20 +274,7 @@ public:
|
||||
|
||||
AstTypePack* retTailAnnotation = nullptr;
|
||||
if (retTail)
|
||||
{
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
retTailAnnotation = rehydrate(*retTail);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypePackId tail = *retTail;
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tail))
|
||||
{
|
||||
retTailAnnotation = allocator->alloc<AstTypePackVariadic>(Location(), Luau::visit(*this, vtp->ty->ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
retTailAnnotation = rehydrate(*retTail);
|
||||
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation});
|
||||
@ -518,18 +487,7 @@ public:
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
{
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypePackId tailPack = *tail;
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tailPack))
|
||||
variadicAnnotation = allocator->alloc<AstTypePackVariadic>(Location(), typeAst(vtp->ty));
|
||||
}
|
||||
}
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
|
@ -23,22 +23,20 @@ LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 500)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
|
||||
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClassPropertyAccessAsString, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCloneCorrectlyBeforeMutatingTableType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreMatchingOverloadFnType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionAnalysisSupport, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStrictRequire, false)
|
||||
LUAU_FASTFLAG(LuauSubstitutionDontReplaceIgnoredTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
||||
LUAU_FASTFLAG(LuauNewRequireTrace2)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSingletonTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTailArgumentTypeInfo, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauModuleRequireErrorPack, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -562,12 +560,6 @@ ErrorVec TypeChecker::canUnify(TypePackId left, TypePackId right, const Location
|
||||
return canUnify_(left, right, location);
|
||||
}
|
||||
|
||||
ErrorVec TypeChecker::canUnify(const std::vector<std::pair<TypeId, TypeId>>& seen, TypeId superTy, TypeId subTy, const Location& location)
|
||||
{
|
||||
Unifier state = mkUnifier(seen, location);
|
||||
return state.canUnify(superTy, subTy);
|
||||
}
|
||||
|
||||
template<typename Id>
|
||||
ErrorVec TypeChecker::canUnify_(Id superTy, Id subTy, const Location& location)
|
||||
{
|
||||
@ -1152,61 +1144,20 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||
Location location = scope->typeAliasLocations[name];
|
||||
reportError(TypeError{typealias.location, DuplicateTypeDefinition{name, location}});
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
bindingsMap[name] = TypeFun{binding->typeParams, binding->typePackParams, errorRecoveryType(anyType)};
|
||||
else
|
||||
bindingsMap[name] = TypeFun{binding->typeParams, errorRecoveryType(anyType)};
|
||||
bindingsMap[name] = TypeFun{binding->typeParams, binding->typePackParams, errorRecoveryType(anyType)};
|
||||
}
|
||||
else
|
||||
{
|
||||
ScopePtr aliasScope =
|
||||
FFlag::LuauQuantifyInPlace2 ? childScope(scope, typealias.location, subLevel) : childScope(scope, typealias.location);
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks);
|
||||
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks);
|
||||
|
||||
TypeId ty = freshType(aliasScope);
|
||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> generics;
|
||||
for (AstName generic : typealias.generics)
|
||||
{
|
||||
Name n = generic.value;
|
||||
|
||||
// These generics are the only thing that will ever be added to aliasScope, so we can be certain that
|
||||
// a collision can only occur when two generic typevars have the same name.
|
||||
if (aliasScope->privateTypeBindings.end() != aliasScope->privateTypeBindings.find(n))
|
||||
{
|
||||
// TODO(jhuelsman): report the exact span of the generic type parameter whose name is a duplicate.
|
||||
reportError(TypeError{typealias.location, DuplicateGenericParameter{n}});
|
||||
}
|
||||
|
||||
TypeId g;
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction)
|
||||
{
|
||||
TypeId& cached = scope->typeAliasTypeParameters[n];
|
||||
if (!cached)
|
||||
cached = addType(GenericTypeVar{aliasScope->level, n});
|
||||
g = cached;
|
||||
}
|
||||
else
|
||||
g = addType(GenericTypeVar{aliasScope->level, n});
|
||||
generics.push_back(g);
|
||||
aliasScope->privateTypeBindings[n] = TypeFun{{}, g};
|
||||
}
|
||||
|
||||
TypeId ty = freshType(aliasScope);
|
||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
bindingsMap[name] = {std::move(generics), ty};
|
||||
}
|
||||
TypeId ty = freshType(aliasScope);
|
||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1223,14 +1174,11 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||
aliasScope->privateTypeBindings[generic->name] = TypeFun{{}, ty};
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
for (TypePackId tp : binding->typePackParams)
|
||||
{
|
||||
for (TypePackId tp : binding->typePackParams)
|
||||
{
|
||||
auto generic = get<GenericTypePack>(tp);
|
||||
LUAU_ASSERT(generic);
|
||||
aliasScope->privateTypePackBindings[generic->name] = tp;
|
||||
}
|
||||
auto generic = get<GenericTypePack>(tp);
|
||||
LUAU_ASSERT(generic);
|
||||
aliasScope->privateTypePackBindings[generic->name] = tp;
|
||||
}
|
||||
|
||||
TypeId ty = resolveType(aliasScope, *typealias.type);
|
||||
@ -1241,19 +1189,16 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||
{
|
||||
// Copy can be skipped if this is an identical alias
|
||||
if (ttv->name != name || ttv->instantiatedTypeParams != binding->typeParams ||
|
||||
(FFlag::LuauTypeAliasPacks && ttv->instantiatedTypePackParams != binding->typePackParams))
|
||||
ttv->instantiatedTypePackParams != binding->typePackParams)
|
||||
{
|
||||
// This is a shallow clone, original recursive links to self are not updated
|
||||
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, ttv->state};
|
||||
|
||||
clone.methodDefinitionLocations = ttv->methodDefinitionLocations;
|
||||
clone.definitionModuleName = ttv->definitionModuleName;
|
||||
|
||||
clone.name = name;
|
||||
clone.instantiatedTypeParams = binding->typeParams;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
clone.instantiatedTypePackParams = binding->typePackParams;
|
||||
clone.instantiatedTypePackParams = binding->typePackParams;
|
||||
|
||||
ty = addType(std::move(clone));
|
||||
}
|
||||
@ -1262,9 +1207,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||
{
|
||||
ttv->name = name;
|
||||
ttv->instantiatedTypeParams = binding->typeParams;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
ttv->instantiatedTypePackParams = binding->typePackParams;
|
||||
ttv->instantiatedTypePackParams = binding->typePackParams;
|
||||
}
|
||||
}
|
||||
else if (auto mtv = getMutable<MetatableTypeVar>(follow(ty)))
|
||||
@ -1289,7 +1232,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
||||
}
|
||||
|
||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && (!FFlag::LuauTypeAliasPacks || lookupType->typePackParams.size() == 0));
|
||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||
superTy = lookupType->type;
|
||||
|
||||
if (!get<ClassTypeVar>(follow(*superTy)))
|
||||
@ -1851,6 +1794,24 @@ TypeId TypeChecker::checkExprTable(
|
||||
if (isNonstrictMode() && !getTableType(exprType) && !get<FunctionTypeVar>(exprType))
|
||||
exprType = anyType;
|
||||
|
||||
if (FFlag::LuauPropertiesGetExpectedType && expectedTable)
|
||||
{
|
||||
auto it = expectedTable->props.find(key->value.data);
|
||||
if (it != expectedTable->props.end())
|
||||
{
|
||||
Property expectedProp = it->second;
|
||||
ErrorVec errors = tryUnify(expectedProp.type, exprType, k->location);
|
||||
if (errors.empty())
|
||||
exprType = expectedProp.type;
|
||||
}
|
||||
else if (expectedTable->indexer && isString(expectedTable->indexer->indexType))
|
||||
{
|
||||
ErrorVec errors = tryUnify(expectedTable->indexer->indexResultType, exprType, k->location);
|
||||
if (errors.empty())
|
||||
exprType = expectedTable->indexer->indexResultType;
|
||||
}
|
||||
}
|
||||
|
||||
props[key->value.data] = {exprType, /* deprecated */ false, {}, k->location};
|
||||
}
|
||||
else
|
||||
@ -3744,17 +3705,29 @@ ExprResult<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, const L
|
||||
for (size_t i = 0; i < exprs.size; ++i)
|
||||
{
|
||||
AstExpr* expr = exprs.data[i];
|
||||
std::optional<TypeId> expectedType = i < expectedTypes.size() ? expectedTypes[i] : std::nullopt;
|
||||
|
||||
if (i == lastIndex && (expr->is<AstExprCall>() || expr->is<AstExprVarargs>()))
|
||||
{
|
||||
auto [typePack, exprPredicates] = checkExprPack(scope, *expr);
|
||||
insert(exprPredicates);
|
||||
|
||||
if (FFlag::LuauTailArgumentTypeInfo)
|
||||
{
|
||||
if (std::optional<TypeId> firstTy = first(typePack))
|
||||
{
|
||||
if (!currentModule->astTypes.find(expr))
|
||||
currentModule->astTypes[expr] = follow(*firstTy);
|
||||
}
|
||||
|
||||
if (expectedType)
|
||||
currentModule->astExpectedTypes[expr] = *expectedType;
|
||||
}
|
||||
|
||||
tp->tail = typePack;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::optional<TypeId> expectedType = i < expectedTypes.size() ? expectedTypes[i] : std::nullopt;
|
||||
auto [type, exprPredicates] = checkExpr(scope, *expr, expectedType);
|
||||
insert(exprPredicates);
|
||||
|
||||
@ -3797,7 +3770,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
||||
LUAU_TIMETRACE_SCOPE("TypeChecker::checkRequire", "TypeChecker");
|
||||
LUAU_TIMETRACE_ARGUMENT("moduleInfo", moduleInfo.name.c_str());
|
||||
|
||||
if (FFlag::LuauNewRequireTrace2 && moduleInfo.name.empty())
|
||||
if (moduleInfo.name.empty())
|
||||
{
|
||||
if (FFlag::LuauStrictRequire && currentModule->mode == Mode::Strict)
|
||||
{
|
||||
@ -3814,7 +3787,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
||||
// There are two reasons why we might fail to find the module:
|
||||
// either the file does not exist or there's a cycle. If there's a cycle
|
||||
// we will already have reported the error.
|
||||
if (!resolver->moduleExists(moduleInfo.name) && (FFlag::LuauTraceRequireLookupChild ? !moduleInfo.optional : true))
|
||||
if (!resolver->moduleExists(moduleInfo.name) && !moduleInfo.optional)
|
||||
{
|
||||
std::string reportedModulePath = resolver->getHumanReadableModuleName(moduleInfo.name);
|
||||
reportError(TypeError{location, UnknownRequire{reportedModulePath}});
|
||||
@ -3830,7 +3803,12 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
std::optional<TypeId> moduleType = first(module->getModuleScope()->returnType);
|
||||
TypePackId modulePack = module->getModuleScope()->returnType;
|
||||
|
||||
if (FFlag::LuauModuleRequireErrorPack && get<Unifiable::Error>(modulePack))
|
||||
return errorRecoveryType(scope);
|
||||
|
||||
std::optional<TypeId> moduleType = first(modulePack);
|
||||
if (!moduleType)
|
||||
{
|
||||
std::string humanReadableName = resolver->getHumanReadableModuleName(moduleInfo.name);
|
||||
@ -3840,7 +3818,8 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
return clone(*moduleType, currentModule->internalTypes, seenTypes, seenTypePacks);
|
||||
CloneState cloneState;
|
||||
return clone(*moduleType, currentModule->internalTypes, seenTypes, seenTypePacks, cloneState);
|
||||
}
|
||||
|
||||
void TypeChecker::tablify(TypeId type)
|
||||
@ -4326,11 +4305,6 @@ Unifier TypeChecker::mkUnifier(const Location& location)
|
||||
return Unifier{¤tModule->internalTypes, currentModule->mode, globalScope, location, Variance::Covariant, unifierState};
|
||||
}
|
||||
|
||||
Unifier TypeChecker::mkUnifier(const std::vector<std::pair<TypeId, TypeId>>& seen, const Location& location)
|
||||
{
|
||||
return Unifier{¤tModule->internalTypes, currentModule->mode, globalScope, seen, location, Variance::Covariant, unifierState};
|
||||
}
|
||||
|
||||
TypeId TypeChecker::freshType(const ScopePtr& scope)
|
||||
{
|
||||
return freshType(scope->level);
|
||||
@ -4477,117 +4451,82 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (lit->parameters.size == 0 && tf->typeParams.empty() && (!FFlag::LuauTypeAliasPacks || tf->typePackParams.empty()))
|
||||
{
|
||||
if (lit->parameters.size == 0 && tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
return tf->type;
|
||||
}
|
||||
else if (!FFlag::LuauTypeAliasPacks && lit->parameters.size != tf->typeParams.size())
|
||||
|
||||
if (!lit->hasParameterList && !tf->typePackParams.empty())
|
||||
{
|
||||
reportError(TypeError{annotation.location, IncorrectGenericParameterCount{lit->name.value, *tf, lit->parameters.size, 0}});
|
||||
reportError(TypeError{annotation.location, GenericError{"Type parameter list is required"}});
|
||||
if (!FFlag::LuauErrorRecoveryType)
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
std::vector<TypeId> typeParams;
|
||||
std::vector<TypeId> extraTypes;
|
||||
std::vector<TypePackId> typePackParams;
|
||||
|
||||
for (size_t i = 0; i < lit->parameters.size; ++i)
|
||||
{
|
||||
if (!lit->hasParameterList && !tf->typePackParams.empty())
|
||||
if (AstType* type = lit->parameters.data[i].type)
|
||||
{
|
||||
reportError(TypeError{annotation.location, GenericError{"Type parameter list is required"}});
|
||||
if (!FFlag::LuauErrorRecoveryType)
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
TypeId ty = resolveType(scope, *type);
|
||||
|
||||
std::vector<TypeId> typeParams;
|
||||
std::vector<TypeId> extraTypes;
|
||||
std::vector<TypePackId> typePackParams;
|
||||
|
||||
for (size_t i = 0; i < lit->parameters.size; ++i)
|
||||
{
|
||||
if (AstType* type = lit->parameters.data[i].type)
|
||||
{
|
||||
TypeId ty = resolveType(scope, *type);
|
||||
|
||||
if (typeParams.size() < tf->typeParams.size() || tf->typePackParams.empty())
|
||||
typeParams.push_back(ty);
|
||||
else if (typePackParams.empty())
|
||||
extraTypes.push_back(ty);
|
||||
else
|
||||
reportError(TypeError{annotation.location, GenericError{"Type parameters must come before type pack parameters"}});
|
||||
}
|
||||
else if (AstTypePack* typePack = lit->parameters.data[i].typePack)
|
||||
{
|
||||
TypePackId tp = resolveTypePack(scope, *typePack);
|
||||
|
||||
// If we have collected an implicit type pack, materialize it
|
||||
if (typePackParams.empty() && !extraTypes.empty())
|
||||
typePackParams.push_back(addTypePack(extraTypes));
|
||||
|
||||
// If we need more regular types, we can use single element type packs to fill those in
|
||||
if (typeParams.size() < tf->typeParams.size() && size(tp) == 1 && finite(tp) && first(tp))
|
||||
typeParams.push_back(*first(tp));
|
||||
else
|
||||
typePackParams.push_back(tp);
|
||||
}
|
||||
}
|
||||
|
||||
// If we still haven't meterialized an implicit type pack, do it now
|
||||
if (typePackParams.empty() && !extraTypes.empty())
|
||||
typePackParams.push_back(addTypePack(extraTypes));
|
||||
|
||||
// If we didn't combine regular types into a type pack and we're still one type pack short, provide an empty type pack
|
||||
if (extraTypes.empty() && typePackParams.size() + 1 == tf->typePackParams.size())
|
||||
typePackParams.push_back(addTypePack({}));
|
||||
|
||||
if (typeParams.size() != tf->typeParams.size() || typePackParams.size() != tf->typePackParams.size())
|
||||
{
|
||||
reportError(
|
||||
TypeError{annotation.location, IncorrectGenericParameterCount{lit->name.value, *tf, typeParams.size(), typePackParams.size()}});
|
||||
|
||||
if (FFlag::LuauErrorRecoveryType)
|
||||
{
|
||||
// Pad the types out with error recovery types
|
||||
while (typeParams.size() < tf->typeParams.size())
|
||||
typeParams.push_back(errorRecoveryType(scope));
|
||||
while (typePackParams.size() < tf->typePackParams.size())
|
||||
typePackParams.push_back(errorRecoveryTypePack(scope));
|
||||
}
|
||||
if (typeParams.size() < tf->typeParams.size() || tf->typePackParams.empty())
|
||||
typeParams.push_back(ty);
|
||||
else if (typePackParams.empty())
|
||||
extraTypes.push_back(ty);
|
||||
else
|
||||
return errorRecoveryType(scope);
|
||||
reportError(TypeError{annotation.location, GenericError{"Type parameters must come before type pack parameters"}});
|
||||
}
|
||||
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction && typeParams == tf->typeParams && typePackParams == tf->typePackParams)
|
||||
else if (AstTypePack* typePack = lit->parameters.data[i].typePack)
|
||||
{
|
||||
// If the generic parameters and the type arguments are the same, we are about to
|
||||
// perform an identity substitution, which we can just short-circuit.
|
||||
return tf->type;
|
||||
TypePackId tp = resolveTypePack(scope, *typePack);
|
||||
|
||||
// If we have collected an implicit type pack, materialize it
|
||||
if (typePackParams.empty() && !extraTypes.empty())
|
||||
typePackParams.push_back(addTypePack(extraTypes));
|
||||
|
||||
// If we need more regular types, we can use single element type packs to fill those in
|
||||
if (typeParams.size() < tf->typeParams.size() && size(tp) == 1 && finite(tp) && first(tp))
|
||||
typeParams.push_back(*first(tp));
|
||||
else
|
||||
typePackParams.push_back(tp);
|
||||
}
|
||||
|
||||
return instantiateTypeFun(scope, *tf, typeParams, typePackParams, annotation.location);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> typeParams;
|
||||
|
||||
for (const auto& param : lit->parameters)
|
||||
typeParams.push_back(resolveType(scope, *param.type));
|
||||
// If we still haven't meterialized an implicit type pack, do it now
|
||||
if (typePackParams.empty() && !extraTypes.empty())
|
||||
typePackParams.push_back(addTypePack(extraTypes));
|
||||
|
||||
// If we didn't combine regular types into a type pack and we're still one type pack short, provide an empty type pack
|
||||
if (extraTypes.empty() && typePackParams.size() + 1 == tf->typePackParams.size())
|
||||
typePackParams.push_back(addTypePack({}));
|
||||
|
||||
if (typeParams.size() != tf->typeParams.size() || typePackParams.size() != tf->typePackParams.size())
|
||||
{
|
||||
reportError(
|
||||
TypeError{annotation.location, IncorrectGenericParameterCount{lit->name.value, *tf, typeParams.size(), typePackParams.size()}});
|
||||
|
||||
if (FFlag::LuauErrorRecoveryType)
|
||||
{
|
||||
// If there aren't enough type parameters, pad them out with error recovery types
|
||||
// (we've already reported the error)
|
||||
while (typeParams.size() < lit->parameters.size)
|
||||
// Pad the types out with error recovery types
|
||||
while (typeParams.size() < tf->typeParams.size())
|
||||
typeParams.push_back(errorRecoveryType(scope));
|
||||
while (typePackParams.size() < tf->typePackParams.size())
|
||||
typePackParams.push_back(errorRecoveryTypePack(scope));
|
||||
}
|
||||
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction && typeParams == tf->typeParams)
|
||||
{
|
||||
// If the generic parameters and the type arguments are the same, we are about to
|
||||
// perform an identity substitution, which we can just short-circuit.
|
||||
return tf->type;
|
||||
}
|
||||
|
||||
return instantiateTypeFun(scope, *tf, typeParams, {}, annotation.location);
|
||||
else
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction && typeParams == tf->typeParams && typePackParams == tf->typePackParams)
|
||||
{
|
||||
// If the generic parameters and the type arguments are the same, we are about to
|
||||
// perform an identity substitution, which we can just short-circuit.
|
||||
return tf->type;
|
||||
}
|
||||
|
||||
return instantiateTypeFun(scope, *tf, typeParams, typePackParams, annotation.location);
|
||||
}
|
||||
else if (const auto& table = annotation.as<AstTypeTable>())
|
||||
{
|
||||
@ -4757,7 +4696,7 @@ bool ApplyTypeFunction::isDirty(TypePackId tp)
|
||||
|
||||
bool ApplyTypeFunction::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (FFlag::LuauSubstitutionDontReplaceIgnoredTypes && get<GenericTypeVar>(ty))
|
||||
if (get<GenericTypeVar>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@ -4765,7 +4704,7 @@ bool ApplyTypeFunction::ignoreChildren(TypeId ty)
|
||||
|
||||
bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
|
||||
{
|
||||
if (FFlag::LuauSubstitutionDontReplaceIgnoredTypes && get<GenericTypePack>(tp))
|
||||
if (get<GenericTypePack>(tp))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@ -4788,36 +4727,26 @@ TypePackId ApplyTypeFunction::clean(TypePackId tp)
|
||||
// Really this should just replace the arguments,
|
||||
// but for bug-compatibility with existing code, we replace
|
||||
// all generics by free type variables.
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
TypePackId& arg = typePackArguments[tp];
|
||||
if (arg)
|
||||
return arg;
|
||||
else
|
||||
return addTypePack(FreeTypePack{level});
|
||||
}
|
||||
TypePackId& arg = typePackArguments[tp];
|
||||
if (arg)
|
||||
return arg;
|
||||
else
|
||||
{
|
||||
return addTypePack(FreeTypePack{level});
|
||||
}
|
||||
}
|
||||
|
||||
TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
||||
const std::vector<TypePackId>& typePackParams, const Location& location)
|
||||
{
|
||||
if (tf.typeParams.empty() && (!FFlag::LuauTypeAliasPacks || tf.typePackParams.empty()))
|
||||
if (tf.typeParams.empty() && tf.typePackParams.empty())
|
||||
return tf.type;
|
||||
|
||||
applyTypeFunction.typeArguments.clear();
|
||||
for (size_t i = 0; i < tf.typeParams.size(); ++i)
|
||||
applyTypeFunction.typeArguments[tf.typeParams[i]] = typeParams[i];
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
applyTypeFunction.typePackArguments.clear();
|
||||
for (size_t i = 0; i < tf.typePackParams.size(); ++i)
|
||||
applyTypeFunction.typePackArguments[tf.typePackParams[i]] = typePackParams[i];
|
||||
}
|
||||
applyTypeFunction.typePackArguments.clear();
|
||||
for (size_t i = 0; i < tf.typePackParams.size(); ++i)
|
||||
applyTypeFunction.typePackArguments[tf.typePackParams[i]] = typePackParams[i];
|
||||
|
||||
applyTypeFunction.currentModule = currentModule;
|
||||
applyTypeFunction.level = scope->level;
|
||||
@ -4866,9 +4795,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
|
||||
if (ttv)
|
||||
{
|
||||
ttv->instantiatedTypeParams = typeParams;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
ttv->instantiatedTypePackParams = typePackParams;
|
||||
ttv->instantiatedTypePackParams = typePackParams;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -4884,9 +4811,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
|
||||
}
|
||||
|
||||
ttv->instantiatedTypeParams = typeParams;
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
ttv->instantiatedTypePackParams = typePackParams;
|
||||
ttv->instantiatedTypePackParams = typePackParams;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4914,7 +4839,7 @@ std::pair<std::vector<TypeId>, std::vector<TypePackId>> TypeChecker::createGener
|
||||
}
|
||||
|
||||
TypeId g;
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction && FFlag::LuauTypeAliasPacks)
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction)
|
||||
{
|
||||
TypeId& cached = scope->parent->typeAliasTypeParameters[n];
|
||||
if (!cached)
|
||||
@ -4944,7 +4869,7 @@ std::pair<std::vector<TypeId>, std::vector<TypePackId>> TypeChecker::createGener
|
||||
}
|
||||
|
||||
TypePackId g;
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction && FFlag::LuauTypeAliasPacks)
|
||||
if (FFlag::LuauRecursiveTypeParameterRestriction)
|
||||
{
|
||||
TypePackId& cached = scope->parent->typeAliasTypePackParameters[n];
|
||||
if (!cached)
|
||||
@ -5245,7 +5170,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec
|
||||
return fail(UnknownSymbol{typeguardP.kind, UnknownSymbol::Type});
|
||||
|
||||
auto typeFun = globalScope->lookupType(typeguardP.kind);
|
||||
if (!typeFun || !typeFun->typeParams.empty() || (FFlag::LuauTypeAliasPacks && !typeFun->typePackParams.empty()))
|
||||
if (!typeFun || !typeFun->typeParams.empty() || !typeFun->typePackParams.empty())
|
||||
return fail(UnknownSymbol{typeguardP.kind, UnknownSymbol::Type});
|
||||
|
||||
TypeId type = follow(typeFun->type);
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||
LUAU_FASTFLAG(LuauTypeAliasPacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
||||
|
||||
@ -739,369 +738,6 @@ void persist(TypePackId tp)
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct StateDot
|
||||
{
|
||||
StateDot(ToDotOptions opts)
|
||||
: opts(opts)
|
||||
{
|
||||
}
|
||||
|
||||
ToDotOptions opts;
|
||||
|
||||
std::unordered_set<TypeId> seenTy;
|
||||
std::unordered_set<TypePackId> seenTp;
|
||||
std::unordered_map<TypeId, int> tyToIndex;
|
||||
std::unordered_map<TypePackId, int> tpToIndex;
|
||||
int nextIndex = 1;
|
||||
std::string result;
|
||||
|
||||
bool canDuplicatePrimitive(TypeId ty);
|
||||
|
||||
void visitChildren(TypeId ty, int index);
|
||||
void visitChildren(TypePackId ty, int index);
|
||||
|
||||
void visitChild(TypeId ty, int parentIndex, const char* linkName = nullptr);
|
||||
void visitChild(TypePackId tp, int parentIndex, const char* linkName = nullptr);
|
||||
|
||||
void startNode(int index);
|
||||
void finishNode();
|
||||
|
||||
void startNodeLabel();
|
||||
void finishNodeLabel(TypeId ty);
|
||||
void finishNodeLabel(TypePackId tp);
|
||||
};
|
||||
|
||||
bool StateDot::canDuplicatePrimitive(TypeId ty)
|
||||
{
|
||||
if (get<BoundTypeVar>(ty))
|
||||
return false;
|
||||
|
||||
return get<PrimitiveTypeVar>(ty) || get<AnyTypeVar>(ty);
|
||||
}
|
||||
|
||||
void StateDot::visitChild(TypeId ty, int parentIndex, const char* linkName)
|
||||
{
|
||||
if (!tyToIndex.count(ty) || (opts.duplicatePrimitives && canDuplicatePrimitive(ty)))
|
||||
tyToIndex[ty] = nextIndex++;
|
||||
|
||||
int index = tyToIndex[ty];
|
||||
|
||||
if (parentIndex != 0)
|
||||
{
|
||||
if (linkName)
|
||||
formatAppend(result, "n%d -> n%d [label=\"%s\"];\n", parentIndex, index, linkName);
|
||||
else
|
||||
formatAppend(result, "n%d -> n%d;\n", parentIndex, index);
|
||||
}
|
||||
|
||||
if (opts.duplicatePrimitives && canDuplicatePrimitive(ty))
|
||||
{
|
||||
if (get<PrimitiveTypeVar>(ty))
|
||||
formatAppend(result, "n%d [label=\"%s\"];\n", index, toStringDetailed(ty, {}).name.c_str());
|
||||
else if (get<AnyTypeVar>(ty))
|
||||
formatAppend(result, "n%d [label=\"any\"];\n", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitChildren(ty, index);
|
||||
}
|
||||
}
|
||||
|
||||
void StateDot::visitChild(TypePackId tp, int parentIndex, const char* linkName)
|
||||
{
|
||||
if (!tpToIndex.count(tp))
|
||||
tpToIndex[tp] = nextIndex++;
|
||||
|
||||
if (linkName)
|
||||
formatAppend(result, "n%d -> n%d [label=\"%s\"];\n", parentIndex, tpToIndex[tp], linkName);
|
||||
else
|
||||
formatAppend(result, "n%d -> n%d;\n", parentIndex, tpToIndex[tp]);
|
||||
|
||||
visitChildren(tp, tpToIndex[tp]);
|
||||
}
|
||||
|
||||
void StateDot::startNode(int index)
|
||||
{
|
||||
formatAppend(result, "n%d [", index);
|
||||
}
|
||||
|
||||
void StateDot::finishNode()
|
||||
{
|
||||
formatAppend(result, "];\n");
|
||||
}
|
||||
|
||||
void StateDot::startNodeLabel()
|
||||
{
|
||||
formatAppend(result, "label=\"");
|
||||
}
|
||||
|
||||
void StateDot::finishNodeLabel(TypeId ty)
|
||||
{
|
||||
if (opts.showPointers)
|
||||
formatAppend(result, "\n0x%p", ty);
|
||||
// additional common attributes can be added here as well
|
||||
result += "\"";
|
||||
}
|
||||
|
||||
void StateDot::finishNodeLabel(TypePackId tp)
|
||||
{
|
||||
if (opts.showPointers)
|
||||
formatAppend(result, "\n0x%p", tp);
|
||||
// additional common attributes can be added here as well
|
||||
result += "\"";
|
||||
}
|
||||
|
||||
void StateDot::visitChildren(TypeId ty, int index)
|
||||
{
|
||||
if (seenTy.count(ty))
|
||||
return;
|
||||
seenTy.insert(ty);
|
||||
|
||||
startNode(index);
|
||||
startNodeLabel();
|
||||
|
||||
if (const BoundTypeVar* btv = get<BoundTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "BoundTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(btv->boundTo, index);
|
||||
}
|
||||
else if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "FunctionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(ftv->argTypes, index, "arg");
|
||||
visitChild(ftv->retType, index, "ret");
|
||||
}
|
||||
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
|
||||
{
|
||||
if (ttv->name)
|
||||
formatAppend(result, "TableTypeVar %s", ttv->name->c_str());
|
||||
else if (ttv->syntheticName)
|
||||
formatAppend(result, "TableTypeVar %s", ttv->syntheticName->c_str());
|
||||
else
|
||||
formatAppend(result, "TableTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
if (ttv->boundTo)
|
||||
return visitChild(*ttv->boundTo, index, "boundTo");
|
||||
|
||||
for (const auto& [name, prop] : ttv->props)
|
||||
visitChild(prop.type, index, name.c_str());
|
||||
if (ttv->indexer)
|
||||
{
|
||||
visitChild(ttv->indexer->indexType, index, "[index]");
|
||||
visitChild(ttv->indexer->indexResultType, index, "[value]");
|
||||
}
|
||||
for (TypeId itp : ttv->instantiatedTypeParams)
|
||||
visitChild(itp, index, "typeParam");
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks)
|
||||
{
|
||||
for (TypePackId itp : ttv->instantiatedTypePackParams)
|
||||
visitChild(itp, index, "typePackParam");
|
||||
}
|
||||
}
|
||||
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "MetatableTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
visitChild(mtv->table, index, "table");
|
||||
visitChild(mtv->metatable, index, "metatable");
|
||||
}
|
||||
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "UnionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (TypeId opt : utv->options)
|
||||
visitChild(opt, index);
|
||||
}
|
||||
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "IntersectionTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (TypeId part : itv->parts)
|
||||
visitChild(part, index);
|
||||
}
|
||||
else if (const GenericTypeVar* gtv = get<GenericTypeVar>(ty))
|
||||
{
|
||||
if (gtv->explicitName)
|
||||
formatAppend(result, "GenericTypeVar %s", gtv->name.c_str());
|
||||
else
|
||||
formatAppend(result, "GenericTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "FreeTypeVar %d", ftv->index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<AnyTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "AnyTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<PrimitiveTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "PrimitiveTypeVar %s", toStringDetailed(ty, {}).name.c_str());
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<ErrorTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "ErrorTypeVar %d", index);
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if (const ClassTypeVar* ctv = get<ClassTypeVar>(ty))
|
||||
{
|
||||
formatAppend(result, "ClassTypeVar %s", ctv->name.c_str());
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
for (const auto& [name, prop] : ctv->props)
|
||||
visitChild(prop.type, index, name.c_str());
|
||||
|
||||
if (ctv->parent)
|
||||
visitChild(*ctv->parent, index, "[parent]");
|
||||
|
||||
if (ctv->metatable)
|
||||
visitChild(*ctv->metatable, index, "[metatable]");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"unknown type kind");
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
}
|
||||
|
||||
void StateDot::visitChildren(TypePackId tp, int index)
|
||||
{
|
||||
if (seenTp.count(tp))
|
||||
return;
|
||||
seenTp.insert(tp);
|
||||
|
||||
startNode(index);
|
||||
startNodeLabel();
|
||||
|
||||
if (const BoundTypePack* btp = get<BoundTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "BoundTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
visitChild(btp->boundTo, index);
|
||||
}
|
||||
else if (const TypePack* tpp = get<TypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "TypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
for (TypeId tv : tpp->head)
|
||||
visitChild(tv, index);
|
||||
if (tpp->tail)
|
||||
visitChild(*tpp->tail, index, "tail");
|
||||
}
|
||||
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "VariadicTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
|
||||
visitChild(vtp->ty, index);
|
||||
}
|
||||
else if (const FreeTypePack* ftp = get<FreeTypePack>(tp))
|
||||
{
|
||||
formatAppend(result, "FreeTypePack %d", ftp->index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else if (const GenericTypePack* gtp = get<GenericTypePack>(tp))
|
||||
{
|
||||
if (gtp->explicitName)
|
||||
formatAppend(result, "GenericTypePack %s", gtp->name.c_str());
|
||||
else
|
||||
formatAppend(result, "GenericTypePack %d", gtp->index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else if (get<Unifiable::Error>(tp))
|
||||
{
|
||||
formatAppend(result, "ErrorTypePack %d", index);
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"unknown type pack kind");
|
||||
finishNodeLabel(tp);
|
||||
finishNode();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string toDot(TypeId ty, const ToDotOptions& opts)
|
||||
{
|
||||
StateDot state{opts};
|
||||
|
||||
state.result = "digraph graphname {\n";
|
||||
state.visitChild(ty, 0);
|
||||
state.result += "}";
|
||||
|
||||
return state.result;
|
||||
}
|
||||
|
||||
std::string toDot(TypePackId tp, const ToDotOptions& opts)
|
||||
{
|
||||
StateDot state{opts};
|
||||
|
||||
state.result = "digraph graphname {\n";
|
||||
state.visitChild(tp, 0);
|
||||
state.result += "}";
|
||||
|
||||
return state.result;
|
||||
}
|
||||
|
||||
std::string toDot(TypeId ty)
|
||||
{
|
||||
return toDot(ty, {});
|
||||
}
|
||||
|
||||
std::string toDot(TypePackId tp)
|
||||
{
|
||||
return toDot(tp, {});
|
||||
}
|
||||
|
||||
void dumpDot(TypeId ty)
|
||||
{
|
||||
printf("%s\n", toDot(ty).c_str());
|
||||
}
|
||||
|
||||
void dumpDot(TypePackId tp)
|
||||
{
|
||||
printf("%s\n", toDot(tp).c_str());
|
||||
}
|
||||
|
||||
const TypeLevel* getLevel(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
@ -18,9 +18,6 @@ LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnionHeuristic, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOccursCheckOkWithRecursiveFunctions, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypecheckOpts, false)
|
||||
LUAU_FASTFLAG(LuauShareTxnSeen);
|
||||
LUAU_FASTFLAGVARIABLE(LuauCacheUnifyTableResults, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExtendedTypeMismatchError, false)
|
||||
LUAU_FASTFLAG(LuauSingletonTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExtendedClassMismatchError, false)
|
||||
@ -136,38 +133,19 @@ Unifier::Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const Locati
|
||||
, globalScope(std::move(globalScope))
|
||||
, location(location)
|
||||
, variance(variance)
|
||||
, counters(&countersData)
|
||||
, counters_DEPRECATED(std::make_shared<UnifierCounters>())
|
||||
, sharedState(sharedState)
|
||||
{
|
||||
LUAU_ASSERT(sharedState.iceHandler);
|
||||
}
|
||||
|
||||
Unifier::Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const std::vector<std::pair<TypeId, TypeId>>& ownedSeen, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED, UnifierCounters* counters)
|
||||
: types(types)
|
||||
, mode(mode)
|
||||
, globalScope(std::move(globalScope))
|
||||
, log(ownedSeen)
|
||||
, location(location)
|
||||
, variance(variance)
|
||||
, counters(counters ? counters : &countersData)
|
||||
, counters_DEPRECATED(counters_DEPRECATED ? counters_DEPRECATED : std::make_shared<UnifierCounters>())
|
||||
, sharedState(sharedState)
|
||||
{
|
||||
LUAU_ASSERT(sharedState.iceHandler);
|
||||
}
|
||||
|
||||
Unifier::Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, std::vector<std::pair<TypeId, TypeId>>* sharedSeen, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED, UnifierCounters* counters)
|
||||
Variance variance, UnifierSharedState& sharedState)
|
||||
: types(types)
|
||||
, mode(mode)
|
||||
, globalScope(std::move(globalScope))
|
||||
, log(sharedSeen)
|
||||
, location(location)
|
||||
, variance(variance)
|
||||
, counters(counters ? counters : &countersData)
|
||||
, counters_DEPRECATED(counters_DEPRECATED ? counters_DEPRECATED : std::make_shared<UnifierCounters>())
|
||||
, sharedState(sharedState)
|
||||
{
|
||||
LUAU_ASSERT(sharedState.iceHandler);
|
||||
@ -175,26 +153,18 @@ Unifier::Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, std::vector<
|
||||
|
||||
void Unifier::tryUnify(TypeId superTy, TypeId subTy, bool isFunctionCall, bool isIntersection)
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
counters->iterationCount = 0;
|
||||
else
|
||||
counters_DEPRECATED->iterationCount = 0;
|
||||
sharedState.counters.iterationCount = 0;
|
||||
|
||||
tryUnify_(superTy, subTy, isFunctionCall, isIntersection);
|
||||
}
|
||||
|
||||
void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool isIntersection)
|
||||
{
|
||||
RecursionLimiter _ra(
|
||||
FFlag::LuauTypecheckOpts ? &counters->recursionCount : &counters_DEPRECATED->recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
++counters->iterationCount;
|
||||
else
|
||||
++counters_DEPRECATED->iterationCount;
|
||||
++sharedState.counters.iterationCount;
|
||||
|
||||
if (FInt::LuauTypeInferIterationLimit > 0 &&
|
||||
FInt::LuauTypeInferIterationLimit < (FFlag::LuauTypecheckOpts ? counters->iterationCount : counters_DEPRECATED->iterationCount))
|
||||
if (FInt::LuauTypeInferIterationLimit > 0 && FInt::LuauTypeInferIterationLimit < sharedState.counters.iterationCount)
|
||||
{
|
||||
errors.push_back(TypeError{location, UnificationTooComplex{}});
|
||||
return;
|
||||
@ -302,7 +272,7 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
if (get<ErrorTypeVar>(subTy) || get<AnyTypeVar>(subTy))
|
||||
return tryUnifyWithAny(subTy, superTy);
|
||||
|
||||
bool cacheEnabled = FFlag::LuauCacheUnifyTableResults && !isFunctionCall && !isIntersection;
|
||||
bool cacheEnabled = !isFunctionCall && !isIntersection;
|
||||
auto& cache = sharedState.cachedUnify;
|
||||
|
||||
// What if the types are immutable and we proved their relation before
|
||||
@ -563,8 +533,6 @@ void Unifier::tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall, bool
|
||||
|
||||
void Unifier::cacheResult(TypeId superTy, TypeId subTy)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauCacheUnifyTableResults);
|
||||
|
||||
bool* superTyInfo = sharedState.skipCacheForType.find(superTy);
|
||||
|
||||
if (superTyInfo && *superTyInfo)
|
||||
@ -686,10 +654,7 @@ ErrorVec Unifier::canUnify(TypePackId superTy, TypePackId subTy, bool isFunction
|
||||
|
||||
void Unifier::tryUnify(TypePackId superTp, TypePackId subTp, bool isFunctionCall)
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
counters->iterationCount = 0;
|
||||
else
|
||||
counters_DEPRECATED->iterationCount = 0;
|
||||
sharedState.counters.iterationCount = 0;
|
||||
|
||||
tryUnify_(superTp, subTp, isFunctionCall);
|
||||
}
|
||||
@ -700,16 +665,11 @@ void Unifier::tryUnify(TypePackId superTp, TypePackId subTp, bool isFunctionCall
|
||||
*/
|
||||
void Unifier::tryUnify_(TypePackId superTp, TypePackId subTp, bool isFunctionCall)
|
||||
{
|
||||
RecursionLimiter _ra(
|
||||
FFlag::LuauTypecheckOpts ? &counters->recursionCount : &counters_DEPRECATED->recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
++counters->iterationCount;
|
||||
else
|
||||
++counters_DEPRECATED->iterationCount;
|
||||
++sharedState.counters.iterationCount;
|
||||
|
||||
if (FInt::LuauTypeInferIterationLimit > 0 &&
|
||||
FInt::LuauTypeInferIterationLimit < (FFlag::LuauTypecheckOpts ? counters->iterationCount : counters_DEPRECATED->iterationCount))
|
||||
if (FInt::LuauTypeInferIterationLimit > 0 && FInt::LuauTypeInferIterationLimit < sharedState.counters.iterationCount)
|
||||
{
|
||||
errors.push_back(TypeError{location, UnificationTooComplex{}});
|
||||
return;
|
||||
@ -1727,39 +1687,8 @@ void Unifier::tryUnify(const TableIndexer& superIndexer, const TableIndexer& sub
|
||||
tryUnify_(superIndexer.indexResultType, subIndexer.indexResultType);
|
||||
}
|
||||
|
||||
static void queueTypePack_DEPRECATED(
|
||||
std::vector<TypeId>& queue, std::unordered_set<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauTypecheckOpts);
|
||||
|
||||
while (true)
|
||||
{
|
||||
a = follow(a);
|
||||
|
||||
if (seenTypePacks.count(a))
|
||||
break;
|
||||
seenTypePacks.insert(a);
|
||||
|
||||
if (get<Unifiable::Free>(a))
|
||||
{
|
||||
state.log(a);
|
||||
*asMutable(a) = Unifiable::Bound{anyTypePack};
|
||||
}
|
||||
else if (auto tp = get<TypePack>(a))
|
||||
{
|
||||
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
|
||||
if (tp->tail)
|
||||
a = *tp->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauTypecheckOpts);
|
||||
|
||||
while (true)
|
||||
{
|
||||
a = follow(a);
|
||||
@ -1837,66 +1766,9 @@ void Unifier::tryUnifyVariadics(TypePackId superTp, TypePackId subTp, bool rever
|
||||
}
|
||||
}
|
||||
|
||||
static void tryUnifyWithAny_DEPRECATED(
|
||||
std::vector<TypeId>& queue, Unifier& state, std::unordered_set<TypePackId>& seenTypePacks, TypeId anyType, TypePackId anyTypePack)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauTypecheckOpts);
|
||||
|
||||
std::unordered_set<TypeId> seen;
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
TypeId ty = follow(queue.back());
|
||||
queue.pop_back();
|
||||
if (seen.count(ty))
|
||||
continue;
|
||||
seen.insert(ty);
|
||||
|
||||
if (get<FreeTypeVar>(ty))
|
||||
{
|
||||
state.log(ty);
|
||||
*asMutable(ty) = BoundTypeVar{anyType};
|
||||
}
|
||||
else if (auto fun = get<FunctionTypeVar>(ty))
|
||||
{
|
||||
queueTypePack_DEPRECATED(queue, seenTypePacks, state, fun->argTypes, anyTypePack);
|
||||
queueTypePack_DEPRECATED(queue, seenTypePacks, state, fun->retType, anyTypePack);
|
||||
}
|
||||
else if (auto table = get<TableTypeVar>(ty))
|
||||
{
|
||||
for (const auto& [_name, prop] : table->props)
|
||||
queue.push_back(prop.type);
|
||||
|
||||
if (table->indexer)
|
||||
{
|
||||
queue.push_back(table->indexer->indexType);
|
||||
queue.push_back(table->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
else if (auto mt = get<MetatableTypeVar>(ty))
|
||||
{
|
||||
queue.push_back(mt->table);
|
||||
queue.push_back(mt->metatable);
|
||||
}
|
||||
else if (get<ClassTypeVar>(ty))
|
||||
{
|
||||
// ClassTypeVars never contain free typevars.
|
||||
}
|
||||
else if (auto union_ = get<UnionTypeVar>(ty))
|
||||
queue.insert(queue.end(), union_->options.begin(), union_->options.end());
|
||||
else if (auto intersection = get<IntersectionTypeVar>(ty))
|
||||
queue.insert(queue.end(), intersection->parts.begin(), intersection->parts.end());
|
||||
else
|
||||
{
|
||||
} // Primitives, any, errors, and generics are left untouched.
|
||||
}
|
||||
}
|
||||
|
||||
static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHashSet<TypeId>& seen, DenseHashSet<TypePackId>& seenTypePacks,
|
||||
TypeId anyType, TypePackId anyTypePack)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauTypecheckOpts);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
TypeId ty = follow(queue.back());
|
||||
@ -1949,43 +1821,20 @@ void Unifier::tryUnifyWithAny(TypeId any, TypeId ty)
|
||||
{
|
||||
LUAU_ASSERT(get<AnyTypeVar>(any) || get<ErrorTypeVar>(any));
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
{
|
||||
// These types are not visited in general loop below
|
||||
if (get<PrimitiveTypeVar>(ty) || get<AnyTypeVar>(ty) || get<ClassTypeVar>(ty))
|
||||
return;
|
||||
}
|
||||
// These types are not visited in general loop below
|
||||
if (get<PrimitiveTypeVar>(ty) || get<AnyTypeVar>(ty) || get<ClassTypeVar>(ty))
|
||||
return;
|
||||
|
||||
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{singletonTypes.anyType}});
|
||||
|
||||
const TypePackId anyTP = get<AnyTypeVar>(any) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}});
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
{
|
||||
std::vector<TypeId> queue = {ty};
|
||||
std::vector<TypeId> queue = {ty};
|
||||
|
||||
if (FFlag::LuauCacheUnifyTableResults)
|
||||
{
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, singletonTypes.anyType, anyTP);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempSeenTy_DEPRECATED.clear();
|
||||
tempSeenTp_DEPRECATED.clear();
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, tempSeenTy_DEPRECATED, tempSeenTp_DEPRECATED, singletonTypes.anyType, anyTP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unordered_set<TypePackId> seenTypePacks;
|
||||
std::vector<TypeId> queue = {ty};
|
||||
|
||||
Luau::tryUnifyWithAny_DEPRECATED(queue, *this, seenTypePacks, singletonTypes.anyType, anyTP);
|
||||
}
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, singletonTypes.anyType, anyTP);
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithAny(TypePackId any, TypePackId ty)
|
||||
@ -1994,38 +1843,14 @@ void Unifier::tryUnifyWithAny(TypePackId any, TypePackId ty)
|
||||
|
||||
const TypeId anyTy = singletonTypes.errorRecoveryType();
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
{
|
||||
std::vector<TypeId> queue;
|
||||
std::vector<TypeId> queue;
|
||||
|
||||
if (FFlag::LuauCacheUnifyTableResults)
|
||||
{
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
queueTypePack(queue, sharedState.tempSeenTp, *this, ty, any);
|
||||
queueTypePack(queue, sharedState.tempSeenTp, *this, ty, any);
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, anyTy, any);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempSeenTy_DEPRECATED.clear();
|
||||
tempSeenTp_DEPRECATED.clear();
|
||||
|
||||
queueTypePack(queue, tempSeenTp_DEPRECATED, *this, ty, any);
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, tempSeenTy_DEPRECATED, tempSeenTp_DEPRECATED, anyTy, any);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unordered_set<TypePackId> seenTypePacks;
|
||||
std::vector<TypeId> queue;
|
||||
|
||||
queueTypePack_DEPRECATED(queue, seenTypePacks, *this, ty, any);
|
||||
|
||||
Luau::tryUnifyWithAny_DEPRECATED(queue, *this, seenTypePacks, anyTy, any);
|
||||
}
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, anyTy, any);
|
||||
}
|
||||
|
||||
std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, Name name)
|
||||
@ -2035,46 +1860,22 @@ std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N
|
||||
|
||||
void Unifier::occursCheck(TypeId needle, TypeId haystack)
|
||||
{
|
||||
std::unordered_set<TypeId> seen_DEPRECATED;
|
||||
sharedState.tempSeenTy.clear();
|
||||
|
||||
if (FFlag::LuauCacheUnifyTableResults)
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
sharedState.tempSeenTy.clear();
|
||||
|
||||
return occursCheck(seen_DEPRECATED, sharedState.tempSeenTy, needle, haystack);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
tempSeenTy_DEPRECATED.clear();
|
||||
|
||||
return occursCheck(seen_DEPRECATED, tempSeenTy_DEPRECATED, needle, haystack);
|
||||
}
|
||||
return occursCheck(sharedState.tempSeenTy, needle, haystack);
|
||||
}
|
||||
|
||||
void Unifier::occursCheck(std::unordered_set<TypeId>& seen_DEPRECATED, DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
|
||||
void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
|
||||
{
|
||||
RecursionLimiter _ra(
|
||||
FFlag::LuauTypecheckOpts ? &counters->recursionCount : &counters_DEPRECATED->recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
needle = follow(needle);
|
||||
haystack = follow(haystack);
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
{
|
||||
if (seen.find(haystack))
|
||||
return;
|
||||
if (seen.find(haystack))
|
||||
return;
|
||||
|
||||
seen.insert(haystack);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (seen_DEPRECATED.end() != seen_DEPRECATED.find(haystack))
|
||||
return;
|
||||
|
||||
seen_DEPRECATED.insert(haystack);
|
||||
}
|
||||
seen.insert(haystack);
|
||||
|
||||
if (get<Unifiable::Error>(needle))
|
||||
return;
|
||||
@ -2091,7 +1892,7 @@ void Unifier::occursCheck(std::unordered_set<TypeId>& seen_DEPRECATED, DenseHash
|
||||
}
|
||||
|
||||
auto check = [&](TypeId tv) {
|
||||
occursCheck(seen_DEPRECATED, seen, needle, tv);
|
||||
occursCheck(seen, needle, tv);
|
||||
};
|
||||
|
||||
if (get<FreeTypeVar>(haystack))
|
||||
@ -2121,43 +1922,20 @@ void Unifier::occursCheck(std::unordered_set<TypeId>& seen_DEPRECATED, DenseHash
|
||||
|
||||
void Unifier::occursCheck(TypePackId needle, TypePackId haystack)
|
||||
{
|
||||
std::unordered_set<TypePackId> seen_DEPRECATED;
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
if (FFlag::LuauCacheUnifyTableResults)
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
return occursCheck(seen_DEPRECATED, sharedState.tempSeenTp, needle, haystack);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
tempSeenTp_DEPRECATED.clear();
|
||||
|
||||
return occursCheck(seen_DEPRECATED, tempSeenTp_DEPRECATED, needle, haystack);
|
||||
}
|
||||
return occursCheck(sharedState.tempSeenTp, needle, haystack);
|
||||
}
|
||||
|
||||
void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack)
|
||||
void Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack)
|
||||
{
|
||||
needle = follow(needle);
|
||||
haystack = follow(haystack);
|
||||
|
||||
if (FFlag::LuauTypecheckOpts)
|
||||
{
|
||||
if (seen.find(haystack))
|
||||
return;
|
||||
if (seen.find(haystack))
|
||||
return;
|
||||
|
||||
seen.insert(haystack);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (seen_DEPRECATED.end() != seen_DEPRECATED.find(haystack))
|
||||
return;
|
||||
|
||||
seen_DEPRECATED.insert(haystack);
|
||||
}
|
||||
seen.insert(haystack);
|
||||
|
||||
if (get<Unifiable::Error>(needle))
|
||||
return;
|
||||
@ -2165,8 +1943,7 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
|
||||
if (!get<Unifiable::Free>(needle))
|
||||
ice("Expected needle pack to be free");
|
||||
|
||||
RecursionLimiter _ra(
|
||||
FFlag::LuauTypecheckOpts ? &counters->recursionCount : &counters_DEPRECATED->recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
while (!get<ErrorTypeVar>(haystack))
|
||||
{
|
||||
@ -2186,8 +1963,8 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
|
||||
{
|
||||
if (auto f = get<FunctionTypeVar>(follow(ty)))
|
||||
{
|
||||
occursCheck(seen_DEPRECATED, seen, needle, f->argTypes);
|
||||
occursCheck(seen_DEPRECATED, seen, needle, f->retType);
|
||||
occursCheck(seen, needle, f->argTypes);
|
||||
occursCheck(seen, needle, f->retType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2204,10 +1981,7 @@ void Unifier::occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, Dense
|
||||
|
||||
Unifier Unifier::makeChildUnifier()
|
||||
{
|
||||
if (FFlag::LuauShareTxnSeen)
|
||||
return Unifier{types, mode, globalScope, log.sharedSeen, location, variance, sharedState, counters_DEPRECATED, counters};
|
||||
else
|
||||
return Unifier{types, mode, globalScope, log.ownedSeen, location, variance, sharedState, counters_DEPRECATED, counters};
|
||||
return Unifier{types, mode, globalScope, log.sharedSeen, location, variance, sharedState};
|
||||
}
|
||||
|
||||
bool Unifier::isNonstrictMode() const
|
||||
|
@ -13,8 +13,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCaptureBrokenCommentSpans, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionBaseSupport, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfStatementRecursionGuard, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeAliasPacks, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseTypePackTypeParameters, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixAmbiguousErrorRecoveryInAssign, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseGenericFunctionTypeBegin, false)
|
||||
@ -782,8 +780,7 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||
|
||||
AstType* type = parseTypeAnnotation();
|
||||
|
||||
return allocator.alloc<AstStatTypeAlias>(
|
||||
Location(start, type->location), name->name, generics, FFlag::LuauTypeAliasPacks ? genericPacks : AstArray<AstName>{}, type, exported);
|
||||
return allocator.alloc<AstStatTypeAlias>(Location(start, type->location), name->name, generics, genericPacks, type, exported);
|
||||
}
|
||||
|
||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||
@ -1602,30 +1599,18 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||
return {allocator.alloc<AstTypeTypeof>(Location(begin, end), expr), {}};
|
||||
}
|
||||
|
||||
if (FFlag::LuauParseTypePackTypeParameters)
|
||||
bool hasParameters = false;
|
||||
AstArray<AstTypeOrPack> parameters{};
|
||||
|
||||
if (lexer.current().type == '<')
|
||||
{
|
||||
bool hasParameters = false;
|
||||
AstArray<AstTypeOrPack> parameters{};
|
||||
|
||||
if (lexer.current().type == '<')
|
||||
{
|
||||
hasParameters = true;
|
||||
parameters = parseTypeParams();
|
||||
}
|
||||
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
return {allocator.alloc<AstTypeReference>(Location(begin, end), prefix, name.name, hasParameters, parameters), {}};
|
||||
hasParameters = true;
|
||||
parameters = parseTypeParams();
|
||||
}
|
||||
else
|
||||
{
|
||||
AstArray<AstTypeOrPack> generics = parseTypeParams();
|
||||
|
||||
Location end = lexer.previousLocation();
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
// false in 'hasParameterList' as it is not used without FFlagLuauTypeAliasPacks
|
||||
return {allocator.alloc<AstTypeReference>(Location(begin, end), prefix, name.name, false, generics), {}};
|
||||
}
|
||||
return {allocator.alloc<AstTypeReference>(Location(begin, end), prefix, name.name, hasParameters, parameters), {}};
|
||||
}
|
||||
else if (lexer.current().type == '{')
|
||||
{
|
||||
@ -2414,37 +2399,24 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams()
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (FFlag::LuauParseTypePackTypeParameters)
|
||||
if (shouldParseTypePackAnnotation(lexer))
|
||||
{
|
||||
if (shouldParseTypePackAnnotation(lexer))
|
||||
{
|
||||
auto typePack = parseTypePackAnnotation();
|
||||
auto typePack = parseTypePackAnnotation();
|
||||
|
||||
if (FFlag::LuauTypeAliasPacks) // Type packs are recorded only is we can handle them
|
||||
parameters.push_back({{}, typePack});
|
||||
}
|
||||
else if (lexer.current().type == '(')
|
||||
{
|
||||
auto [type, typePack] = parseTypeOrPackAnnotation();
|
||||
parameters.push_back({{}, typePack});
|
||||
}
|
||||
else if (lexer.current().type == '(')
|
||||
{
|
||||
auto [type, typePack] = parseTypeOrPackAnnotation();
|
||||
|
||||
if (typePack)
|
||||
{
|
||||
if (FFlag::LuauTypeAliasPacks) // Type packs are recorded only is we can handle them
|
||||
parameters.push_back({{}, typePack});
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.push_back({type, {}});
|
||||
}
|
||||
}
|
||||
else if (lexer.current().type == '>' && parameters.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (typePack)
|
||||
parameters.push_back({{}, typePack});
|
||||
else
|
||||
{
|
||||
parameters.push_back({parseTypeAnnotation(), {}});
|
||||
}
|
||||
parameters.push_back({type, {}});
|
||||
}
|
||||
else if (lexer.current().type == '>' && parameters.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ struct CliFileResolver : Luau::FileResolver
|
||||
if (Luau::AstExprConstantString* expr = node->as<Luau::AstExprConstantString>())
|
||||
{
|
||||
Luau::ModuleName name = std::string(expr->value.data, expr->value.size) + ".luau";
|
||||
if (!moduleExists(name))
|
||||
if (!readFile(name))
|
||||
{
|
||||
// fall back to .lua if a module with .luau doesn't exist
|
||||
name = std::string(expr->value.data, expr->value.size) + ".lua";
|
||||
@ -132,27 +132,6 @@ struct CliFileResolver : Luau::FileResolver
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool moduleExists(const Luau::ModuleName& name) const override
|
||||
{
|
||||
return !!readFile(name);
|
||||
}
|
||||
|
||||
|
||||
std::optional<Luau::ModuleName> fromAstFragment(Luau::AstExpr* expr) const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Luau::ModuleName concat(const Luau::ModuleName& lhs, std::string_view rhs) const override
|
||||
{
|
||||
return lhs + "/" + std::string(rhs);
|
||||
}
|
||||
|
||||
std::optional<Luau::ModuleName> getParentModuleName(const Luau::ModuleName& name) const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
struct CliConfigResolver : Luau::ConfigResolver
|
||||
|
39
CLI/Repl.cpp
39
CLI/Repl.cpp
@ -198,11 +198,6 @@ static std::string runCode(lua_State* L, const std::string& source)
|
||||
error += "\nstack backtrace:\n";
|
||||
error += lua_debugtrace(T);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// nicer formatting for errors in web repl
|
||||
error = "Error:" + error;
|
||||
#endif
|
||||
|
||||
fprintf(stdout, "%s", error.c_str());
|
||||
}
|
||||
|
||||
@ -210,39 +205,6 @@ static std::string runCode(lua_State* L, const std::string& source)
|
||||
return std::string();
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
extern "C"
|
||||
{
|
||||
const char* executeScript(const char* source)
|
||||
{
|
||||
// setup flags
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||
flag->value = true;
|
||||
|
||||
// create new state
|
||||
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
|
||||
lua_State* L = globalState.get();
|
||||
|
||||
// setup state
|
||||
setupState(L);
|
||||
|
||||
// sandbox thread
|
||||
luaL_sandboxthread(L);
|
||||
|
||||
// static string for caching result (prevents dangling ptr on function exit)
|
||||
static std::string result;
|
||||
|
||||
// run code + collect error
|
||||
result = runCode(L, source);
|
||||
|
||||
return result.empty() ? NULL : result.c_str();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Excluded from emscripten compilation to avoid -Wunused-function errors.
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static void completeIndexer(lua_State* L, const char* editBuffer, size_t start, std::vector<std::string>& completions)
|
||||
{
|
||||
std::string_view lookup = editBuffer + start;
|
||||
@ -564,6 +526,5 @@ int main(int argc, char** argv)
|
||||
return failed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
106
CLI/Web.cpp
Normal file
106
CLI/Web.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "luacode.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void setupState(lua_State* L)
|
||||
{
|
||||
luaL_openlibs(L);
|
||||
|
||||
luaL_sandbox(L);
|
||||
}
|
||||
|
||||
static std::string runCode(lua_State* L, const std::string& source)
|
||||
{
|
||||
size_t bytecodeSize = 0;
|
||||
char* bytecode = luau_compile(source.data(), source.length(), nullptr, &bytecodeSize);
|
||||
int result = luau_load(L, "=stdin", bytecode, bytecodeSize, 0);
|
||||
free(bytecode);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
size_t len;
|
||||
const char* msg = lua_tolstring(L, -1, &len);
|
||||
|
||||
std::string error(msg, len);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
lua_State* T = lua_newthread(L);
|
||||
|
||||
lua_pushvalue(L, -2);
|
||||
lua_remove(L, -3);
|
||||
lua_xmove(L, T, 1);
|
||||
|
||||
int status = lua_resume(T, NULL, 0);
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
int n = lua_gettop(T);
|
||||
|
||||
if (n)
|
||||
{
|
||||
luaL_checkstack(T, LUA_MINSTACK, "too many results to print");
|
||||
lua_getglobal(T, "print");
|
||||
lua_insert(T, 1);
|
||||
lua_pcall(T, n, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error;
|
||||
|
||||
if (status == LUA_YIELD)
|
||||
{
|
||||
error = "thread yielded unexpectedly";
|
||||
}
|
||||
else if (const char* str = lua_tostring(T, -1))
|
||||
{
|
||||
error = str;
|
||||
}
|
||||
|
||||
error += "\nstack backtrace:\n";
|
||||
error += lua_debugtrace(T);
|
||||
|
||||
error = "Error:" + error;
|
||||
|
||||
fprintf(stdout, "%s", error.c_str());
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
extern "C" const char* executeScript(const char* source)
|
||||
{
|
||||
// setup flags
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||
flag->value = true;
|
||||
|
||||
// create new state
|
||||
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
|
||||
lua_State* L = globalState.get();
|
||||
|
||||
// setup state
|
||||
setupState(L);
|
||||
|
||||
// sandbox thread
|
||||
luaL_sandboxthread(L);
|
||||
|
||||
// static string for caching result (prevents dangling ptr on function exit)
|
||||
static std::string result;
|
||||
|
||||
// run code + collect error
|
||||
result = runCode(L, source);
|
||||
|
||||
return result.empty() ? NULL : result.c_str();
|
||||
}
|
@ -9,6 +9,7 @@ project(Luau LANGUAGES CXX)
|
||||
|
||||
option(LUAU_BUILD_CLI "Build CLI" ON)
|
||||
option(LUAU_BUILD_TESTS "Build tests" ON)
|
||||
option(LUAU_BUILD_WEB "Build Web module" OFF)
|
||||
option(LUAU_WERROR "Warnings as errors" OFF)
|
||||
|
||||
add_library(Luau.Ast STATIC)
|
||||
@ -18,26 +19,22 @@ add_library(Luau.VM STATIC)
|
||||
|
||||
if(LUAU_BUILD_CLI)
|
||||
add_executable(Luau.Repl.CLI)
|
||||
if(NOT EMSCRIPTEN)
|
||||
add_executable(Luau.Analyze.CLI)
|
||||
else()
|
||||
# add -fexceptions for emscripten to allow exceptions to be caught in C++
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
||||
endif()
|
||||
add_executable(Luau.Analyze.CLI)
|
||||
|
||||
# This also adds target `name` on Linux/macOS and `name.exe` on Windows
|
||||
set_target_properties(Luau.Repl.CLI PROPERTIES OUTPUT_NAME luau)
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
|
||||
endif()
|
||||
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_TESTS AND NOT EMSCRIPTEN)
|
||||
if(LUAU_BUILD_TESTS)
|
||||
add_executable(Luau.UnitTest)
|
||||
add_executable(Luau.Conformance)
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_WEB)
|
||||
add_executable(Luau.Web)
|
||||
endif()
|
||||
|
||||
include(Sources.cmake)
|
||||
|
||||
target_compile_features(Luau.Ast PUBLIC cxx_std_17)
|
||||
@ -72,16 +69,18 @@ if(LUAU_WERROR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_WEB)
|
||||
# add -fexceptions for emscripten to allow exceptions to be caught in C++
|
||||
list(APPEND LUAU_OPTIONS -fexceptions)
|
||||
endif()
|
||||
|
||||
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
||||
target_compile_options(Luau.Analysis PRIVATE ${LUAU_OPTIONS})
|
||||
target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
||||
|
||||
if(LUAU_BUILD_CLI)
|
||||
target_compile_options(Luau.Repl.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
endif()
|
||||
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
|
||||
target_include_directories(Luau.Repl.CLI PRIVATE extern)
|
||||
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.VM)
|
||||
@ -93,20 +92,10 @@ if(LUAU_BUILD_CLI)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
# declare exported functions to emscripten
|
||||
target_link_options(Luau.Repl.CLI PRIVATE -sEXPORTED_FUNCTIONS=['_executeScript'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -fexceptions)
|
||||
|
||||
# custom output directory for wasm + js file
|
||||
set_target_properties(Luau.Repl.CLI PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/assets/luau)
|
||||
endif()
|
||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_TESTS AND NOT EMSCRIPTEN)
|
||||
if(LUAU_BUILD_TESTS)
|
||||
target_compile_options(Luau.UnitTest PRIVATE ${LUAU_OPTIONS})
|
||||
target_include_directories(Luau.UnitTest PRIVATE extern)
|
||||
target_link_libraries(Luau.UnitTest PRIVATE Luau.Analysis Luau.Compiler)
|
||||
@ -115,3 +104,17 @@ if(LUAU_BUILD_TESTS AND NOT EMSCRIPTEN)
|
||||
target_include_directories(Luau.Conformance PRIVATE extern)
|
||||
target_link_libraries(Luau.Conformance PRIVATE Luau.Analysis Luau.Compiler Luau.VM)
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_WEB)
|
||||
target_compile_options(Luau.Web PRIVATE ${LUAU_OPTIONS})
|
||||
target_link_libraries(Luau.Web PRIVATE Luau.Compiler Luau.VM)
|
||||
|
||||
# declare exported functions to emscripten
|
||||
target_link_options(Luau.Web PRIVATE -sEXPORTED_FUNCTIONS=['_executeScript'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap'])
|
||||
|
||||
# add -fexceptions for emscripten to allow exceptions to be caught in C++
|
||||
target_link_options(Luau.Web PRIVATE -fexceptions)
|
||||
|
||||
# the output is a single .js file with an embedded wasm blob
|
||||
target_link_options(Luau.Web PRIVATE -sSINGLE_FILE=1)
|
||||
endif()
|
||||
|
@ -321,13 +321,15 @@ struct Compiler
|
||||
compileExprTempTop(expr->args.data[i], uint8_t(regs + 1 + expr->self + i));
|
||||
}
|
||||
|
||||
setDebugLine(expr->func);
|
||||
setDebugLineEnd(expr->func);
|
||||
|
||||
if (expr->self)
|
||||
{
|
||||
AstExprIndexName* fi = expr->func->as<AstExprIndexName>();
|
||||
LUAU_ASSERT(fi);
|
||||
|
||||
setDebugLine(fi->indexLocation);
|
||||
|
||||
BytecodeBuilder::StringRef iname = sref(fi->index);
|
||||
int32_t cid = bytecode.addConstantString(iname);
|
||||
if (cid < 0)
|
||||
@ -1313,6 +1315,8 @@ struct Compiler
|
||||
RegScope rs(this);
|
||||
uint8_t reg = compileExprAuto(expr->expr, rs);
|
||||
|
||||
setDebugLine(expr->indexLocation);
|
||||
|
||||
BytecodeBuilder::StringRef iname = sref(expr->index);
|
||||
int32_t cid = bytecode.addConstantString(iname);
|
||||
if (cid < 0)
|
||||
@ -2710,6 +2714,12 @@ struct Compiler
|
||||
bytecode.setDebugLine(node->location.begin.line + 1);
|
||||
}
|
||||
|
||||
void setDebugLine(const Location& location)
|
||||
{
|
||||
if (options.debugLevel >= 1)
|
||||
bytecode.setDebugLine(location.begin.line + 1);
|
||||
}
|
||||
|
||||
void setDebugLineEnd(AstNode* node)
|
||||
{
|
||||
if (options.debugLevel >= 1)
|
||||
@ -3650,7 +3660,7 @@ struct Compiler
|
||||
{
|
||||
if (options.vectorLib)
|
||||
{
|
||||
if (builtin.object == options.vectorLib && builtin.method == options.vectorCtor)
|
||||
if (builtin.isMethod(options.vectorLib, options.vectorCtor))
|
||||
return LBF_VECTOR;
|
||||
}
|
||||
else
|
||||
|
6
Makefile
6
Makefile
@ -35,7 +35,7 @@ ANALYZE_CLI_SOURCES=CLI/FileUtils.cpp CLI/Analyze.cpp
|
||||
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
|
||||
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
|
||||
|
||||
FUZZ_SOURCES=$(wildcard fuzz/*.cpp)
|
||||
FUZZ_SOURCES=$(wildcard fuzz/*.cpp) fuzz/luau.pb.cpp
|
||||
FUZZ_OBJECTS=$(FUZZ_SOURCES:%=$(BUILD)/%.o)
|
||||
|
||||
TESTS_ARGS=
|
||||
@ -167,8 +167,8 @@ fuzz/luau.pb.cpp: fuzz/luau.proto build/libprotobuf-mutator
|
||||
cd fuzz && ../build/libprotobuf-mutator/external.protobuf/bin/protoc luau.proto --cpp_out=.
|
||||
mv fuzz/luau.pb.cc fuzz/luau.pb.cpp
|
||||
|
||||
$(BUILD)/fuzz/proto.cpp.o: build/libprotobuf-mutator
|
||||
$(BUILD)/fuzz/protoprint.cpp.o: build/libprotobuf-mutator
|
||||
$(BUILD)/fuzz/proto.cpp.o: fuzz/luau.pb.cpp
|
||||
$(BUILD)/fuzz/protoprint.cpp.o: fuzz/luau.pb.cpp
|
||||
|
||||
build/libprotobuf-mutator:
|
||||
git clone https://github.com/google/libprotobuf-mutator build/libprotobuf-mutator
|
||||
|
@ -54,6 +54,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||
Analysis/include/Luau/Scope.h
|
||||
Analysis/include/Luau/Substitution.h
|
||||
Analysis/include/Luau/Symbol.h
|
||||
Analysis/include/Luau/ToDot.h
|
||||
Analysis/include/Luau/TopoSortStatements.h
|
||||
Analysis/include/Luau/ToString.h
|
||||
Analysis/include/Luau/Transpiler.h
|
||||
@ -86,6 +87,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||
Analysis/src/Scope.cpp
|
||||
Analysis/src/Substitution.cpp
|
||||
Analysis/src/Symbol.cpp
|
||||
Analysis/src/ToDot.cpp
|
||||
Analysis/src/TopoSortStatements.cpp
|
||||
Analysis/src/ToString.cpp
|
||||
Analysis/src/Transpiler.cpp
|
||||
@ -118,6 +120,7 @@ target_sources(Luau.VM PRIVATE
|
||||
VM/src/ldo.cpp
|
||||
VM/src/lfunc.cpp
|
||||
VM/src/lgc.cpp
|
||||
VM/src/lgcdebug.cpp
|
||||
VM/src/linit.cpp
|
||||
VM/src/lmathlib.cpp
|
||||
VM/src/lmem.cpp
|
||||
@ -194,6 +197,7 @@ if(TARGET Luau.UnitTest)
|
||||
tests/RequireTracer.test.cpp
|
||||
tests/StringUtils.test.cpp
|
||||
tests/Symbol.test.cpp
|
||||
tests/ToDot.test.cpp
|
||||
tests/TopoSort.test.cpp
|
||||
tests/ToString.test.cpp
|
||||
tests/Transpiler.test.cpp
|
||||
@ -224,3 +228,9 @@ if(TARGET Luau.Conformance)
|
||||
tests/Conformance.test.cpp
|
||||
tests/main.cpp)
|
||||
endif()
|
||||
|
||||
if(TARGET Luau.Web)
|
||||
# Luau.Web Sources
|
||||
target_sources(Luau.Web PRIVATE
|
||||
CLI/Web.cpp)
|
||||
endif()
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define LUA_ENVIRONINDEX (-10001)
|
||||
#define LUA_GLOBALSINDEX (-10002)
|
||||
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX - (i))
|
||||
#define lua_ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
|
||||
|
||||
/* thread status; 0 is OK */
|
||||
enum lua_Status
|
||||
@ -108,6 +109,7 @@ LUA_API int lua_isthreadreset(lua_State* L);
|
||||
/*
|
||||
** basic stack manipulation
|
||||
*/
|
||||
LUA_API int lua_absindex(lua_State* L, int idx);
|
||||
LUA_API int lua_gettop(lua_State* L);
|
||||
LUA_API void lua_settop(lua_State* L, int idx);
|
||||
LUA_API void lua_pushvalue(lua_State* L, int idx);
|
||||
@ -159,7 +161,11 @@ LUA_API void lua_pushnil(lua_State* L);
|
||||
LUA_API void lua_pushnumber(lua_State* L, double n);
|
||||
LUA_API void lua_pushinteger(lua_State* L, int n);
|
||||
LUA_API void lua_pushunsigned(lua_State* L, unsigned n);
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
LUA_API void lua_pushvector(lua_State* L, float x, float y, float z, float w);
|
||||
#else
|
||||
LUA_API void lua_pushvector(lua_State* L, float x, float y, float z);
|
||||
#endif
|
||||
LUA_API void lua_pushlstring(lua_State* L, const char* s, size_t l);
|
||||
LUA_API void lua_pushstring(lua_State* L, const char* s);
|
||||
LUA_API const char* lua_pushvfstring(lua_State* L, const char* fmt, va_list argp);
|
||||
@ -183,7 +189,7 @@ LUA_API void lua_setreadonly(lua_State* L, int idx, int enabled);
|
||||
LUA_API int lua_getreadonly(lua_State* L, int idx);
|
||||
LUA_API void lua_setsafeenv(lua_State* L, int idx, int enabled);
|
||||
|
||||
LUA_API void* lua_newuserdata(lua_State* L, size_t sz, int tag);
|
||||
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
|
||||
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
|
||||
LUA_API int lua_getmetatable(lua_State* L, int objindex);
|
||||
LUA_API void lua_getfenv(lua_State* L, int idx);
|
||||
@ -227,6 +233,7 @@ enum lua_GCOp
|
||||
LUA_GCRESTART,
|
||||
LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT,
|
||||
LUA_GCCOUNTB,
|
||||
LUA_GCISRUNNING,
|
||||
|
||||
// garbage collection is handled by 'assists' that perform some amount of GC work matching pace of allocation
|
||||
@ -281,6 +288,7 @@ LUA_API void lua_unref(lua_State* L, int ref);
|
||||
#define lua_pop(L, n) lua_settop(L, -(n)-1)
|
||||
|
||||
#define lua_newtable(L) lua_createtable(L, 0, 0)
|
||||
#define lua_newuserdata(L, s) lua_newuserdatatagged(L, s, 0)
|
||||
|
||||
#define lua_strlen(L, i) lua_objlen(L, (i))
|
||||
|
||||
@ -289,6 +297,7 @@ LUA_API void lua_unref(lua_State* L, int ref);
|
||||
#define lua_islightuserdata(L, n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
|
||||
#define lua_isnil(L, n) (lua_type(L, (n)) == LUA_TNIL)
|
||||
#define lua_isboolean(L, n) (lua_type(L, (n)) == LUA_TBOOLEAN)
|
||||
#define lua_isvector(L, n) (lua_type(L, (n)) == LUA_TVECTOR)
|
||||
#define lua_isthread(L, n) (lua_type(L, (n)) == LUA_TTHREAD)
|
||||
#define lua_isnone(L, n) (lua_type(L, (n)) == LUA_TNONE)
|
||||
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= LUA_TNIL)
|
||||
|
@ -34,7 +34,10 @@
|
||||
#endif
|
||||
|
||||
/* Can be used to reconfigure visibility/exports for public APIs */
|
||||
#ifndef LUA_API
|
||||
#define LUA_API extern
|
||||
#endif
|
||||
|
||||
#define LUALIB_API LUA_API
|
||||
|
||||
/* Can be used to reconfigure visibility for internal APIs */
|
||||
@ -47,10 +50,14 @@
|
||||
#endif
|
||||
|
||||
/* Can be used to reconfigure internal error handling to use longjmp instead of C++ EH */
|
||||
#ifndef LUA_USE_LONGJMP
|
||||
#define LUA_USE_LONGJMP 0
|
||||
#endif
|
||||
|
||||
/* LUA_IDSIZE gives the maximum size for the description of the source */
|
||||
#ifndef LUA_IDSIZE
|
||||
#define LUA_IDSIZE 256
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ LUAI_GCGOAL defines the desired top heap size in relation to the live heap
|
||||
@ -59,7 +66,9 @@
|
||||
** mean larger GC pauses which mean slower collection.) You can also change
|
||||
** this value dynamically.
|
||||
*/
|
||||
#ifndef LUAI_GCGOAL
|
||||
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ LUAI_GCSTEPMUL / LUAI_GCSTEPSIZE define the default speed of garbage collection
|
||||
@ -69,38 +78,63 @@
|
||||
** CHANGE it if you want to change the granularity of the garbage
|
||||
** collection.
|
||||
*/
|
||||
#ifndef LUAI_GCSTEPMUL
|
||||
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
||||
#endif
|
||||
|
||||
#ifndef LUAI_GCSTEPSIZE
|
||||
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
||||
#endif
|
||||
|
||||
/* LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function */
|
||||
#ifndef LUA_MINSTACK
|
||||
#define LUA_MINSTACK 20
|
||||
#endif
|
||||
|
||||
/* LUAI_MAXCSTACK limits the number of Lua stack slots that a C function can use */
|
||||
#ifndef LUAI_MAXCSTACK
|
||||
#define LUAI_MAXCSTACK 8000
|
||||
#endif
|
||||
|
||||
/* LUAI_MAXCALLS limits the number of nested calls */
|
||||
#ifndef LUAI_MAXCALLS
|
||||
#define LUAI_MAXCALLS 20000
|
||||
#endif
|
||||
|
||||
/* LUAI_MAXCCALLS is the maximum depth for nested C calls; this limit depends on native stack size */
|
||||
#ifndef LUAI_MAXCCALLS
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#endif
|
||||
|
||||
/* buffer size used for on-stack string operations; this limit depends on native stack size */
|
||||
#ifndef LUA_BUFFERSIZE
|
||||
#define LUA_BUFFERSIZE 512
|
||||
#endif
|
||||
|
||||
/* number of valid Lua userdata tags */
|
||||
#ifndef LUA_UTAG_LIMIT
|
||||
#define LUA_UTAG_LIMIT 128
|
||||
#endif
|
||||
|
||||
/* upper bound for number of size classes used by page allocator */
|
||||
#ifndef LUA_SIZECLASSES
|
||||
#define LUA_SIZECLASSES 32
|
||||
#endif
|
||||
|
||||
/* available number of separate memory categories */
|
||||
#ifndef LUA_MEMORY_CATEGORIES
|
||||
#define LUA_MEMORY_CATEGORIES 256
|
||||
#endif
|
||||
|
||||
/* minimum size for the string table (must be power of 2) */
|
||||
#ifndef LUA_MINSTRTABSIZE
|
||||
#define LUA_MINSTRTABSIZE 32
|
||||
#endif
|
||||
|
||||
/* maximum number of captures supported by pattern matching */
|
||||
#ifndef LUA_MAXCAPTURES
|
||||
#define LUA_MAXCAPTURES 32
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
@ -122,3 +156,7 @@
|
||||
void* s; \
|
||||
long l; \
|
||||
}
|
||||
|
||||
#define LUA_VECTOR_SIZE 3 /* must be 3 or 4 */
|
||||
|
||||
#define LUA_EXTRA_SIZE LUA_VECTOR_SIZE - 2
|
||||
|
@ -25,11 +25,17 @@ LUALIB_API const char* luaL_optlstring(lua_State* L, int numArg, const char* def
|
||||
LUALIB_API double luaL_checknumber(lua_State* L, int numArg);
|
||||
LUALIB_API double luaL_optnumber(lua_State* L, int nArg, double def);
|
||||
|
||||
LUALIB_API int luaL_checkboolean(lua_State* L, int narg);
|
||||
LUALIB_API int luaL_optboolean(lua_State* L, int narg, int def);
|
||||
|
||||
LUALIB_API int luaL_checkinteger(lua_State* L, int numArg);
|
||||
LUALIB_API int luaL_optinteger(lua_State* L, int nArg, int def);
|
||||
LUALIB_API unsigned luaL_checkunsigned(lua_State* L, int numArg);
|
||||
LUALIB_API unsigned luaL_optunsigned(lua_State* L, int numArg, unsigned def);
|
||||
|
||||
LUALIB_API const float* luaL_checkvector(lua_State* L, int narg);
|
||||
LUALIB_API const float* luaL_optvector(lua_State* L, int narg, const float* def);
|
||||
|
||||
LUALIB_API void luaL_checkstack(lua_State* L, int sz, const char* msg);
|
||||
LUALIB_API void luaL_checktype(lua_State* L, int narg, int t);
|
||||
LUALIB_API void luaL_checkany(lua_State* L, int narg);
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauActivateBeforeExec)
|
||||
|
||||
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
||||
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
||||
"$URL: www.lua.org $\n";
|
||||
@ -170,6 +172,12 @@ lua_State* lua_mainthread(lua_State* L)
|
||||
** basic stack manipulation
|
||||
*/
|
||||
|
||||
int lua_absindex(lua_State* L, int idx)
|
||||
{
|
||||
api_check(L, (idx > 0 && idx <= L->top - L->base) || (idx < 0 && -idx <= L->top - L->base) || lua_ispseudo(idx));
|
||||
return idx > 0 || lua_ispseudo(idx) ? idx : cast_int(L->top - L->base) + idx + 1;
|
||||
}
|
||||
|
||||
int lua_gettop(lua_State* L)
|
||||
{
|
||||
return cast_int(L->top - L->base);
|
||||
@ -550,12 +558,21 @@ void lua_pushunsigned(lua_State* L, unsigned u)
|
||||
return;
|
||||
}
|
||||
|
||||
void lua_pushvector(lua_State* L, float x, float y, float z)
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
void lua_pushvector(lua_State* L, float x, float y, float z, float w)
|
||||
{
|
||||
setvvalue(L->top, x, y, z);
|
||||
setvvalue(L->top, x, y, z, w);
|
||||
api_incr_top(L);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
void lua_pushvector(lua_State* L, float x, float y, float z)
|
||||
{
|
||||
setvvalue(L->top, x, y, z, 0.0f);
|
||||
api_incr_top(L);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
void lua_pushlstring(lua_State* L, const char* s, size_t len)
|
||||
{
|
||||
@ -922,14 +939,21 @@ void lua_call(lua_State* L, int nargs, int nresults)
|
||||
checkresults(L, nargs, nresults);
|
||||
func = L->top - (nargs + 1);
|
||||
|
||||
int wasActive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
if (FFlag::LuauActivateBeforeExec)
|
||||
{
|
||||
luaD_call(L, func, nresults);
|
||||
}
|
||||
else
|
||||
{
|
||||
int oldactive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
|
||||
luaD_call(L, func, nresults);
|
||||
luaD_call(L, func, nresults);
|
||||
|
||||
if (!wasActive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
if (!oldactive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
}
|
||||
|
||||
adjustresults(L, nresults);
|
||||
return;
|
||||
@ -970,14 +994,21 @@ int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc)
|
||||
c.func = L->top - (nargs + 1); /* function to be called */
|
||||
c.nresults = nresults;
|
||||
|
||||
int wasActive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
if (FFlag::LuauActivateBeforeExec)
|
||||
{
|
||||
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
}
|
||||
else
|
||||
{
|
||||
int oldactive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
|
||||
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
|
||||
if (!wasActive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
if (!oldactive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
}
|
||||
|
||||
adjustresults(L, nresults);
|
||||
return status;
|
||||
@ -1030,6 +1061,11 @@ int lua_gc(lua_State* L, int what, int data)
|
||||
res = cast_int(g->totalbytes >> 10);
|
||||
break;
|
||||
}
|
||||
case LUA_GCCOUNTB:
|
||||
{
|
||||
res = cast_int(g->totalbytes & 1023);
|
||||
break;
|
||||
}
|
||||
case LUA_GCISRUNNING:
|
||||
{
|
||||
res = (g->GCthreshold != SIZE_MAX);
|
||||
@ -1146,7 +1182,7 @@ void lua_concat(lua_State* L, int n)
|
||||
return;
|
||||
}
|
||||
|
||||
void* lua_newuserdata(lua_State* L, size_t sz, int tag)
|
||||
void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag)
|
||||
{
|
||||
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
||||
luaC_checkGC(L);
|
||||
@ -1231,6 +1267,7 @@ uintptr_t lua_encodepointer(lua_State* L, uintptr_t p)
|
||||
|
||||
int lua_ref(lua_State* L, int idx)
|
||||
{
|
||||
api_check(L, idx != LUA_REGISTRYINDEX); /* idx is a stack index for value */
|
||||
int ref = LUA_REFNIL;
|
||||
global_State* g = L->global;
|
||||
StkId p = index2adr(L, idx);
|
||||
|
@ -30,7 +30,7 @@ static const char* currfuncname(lua_State* L)
|
||||
return debugname;
|
||||
}
|
||||
|
||||
LUALIB_API l_noret luaL_argerrorL(lua_State* L, int narg, const char* extramsg)
|
||||
l_noret luaL_argerrorL(lua_State* L, int narg, const char* extramsg)
|
||||
{
|
||||
const char* fname = currfuncname(L);
|
||||
|
||||
@ -40,7 +40,7 @@ LUALIB_API l_noret luaL_argerrorL(lua_State* L, int narg, const char* extramsg)
|
||||
luaL_error(L, "invalid argument #%d (%s)", narg, extramsg);
|
||||
}
|
||||
|
||||
LUALIB_API l_noret luaL_typeerrorL(lua_State* L, int narg, const char* tname)
|
||||
l_noret luaL_typeerrorL(lua_State* L, int narg, const char* tname)
|
||||
{
|
||||
const char* fname = currfuncname(L);
|
||||
const TValue* obj = luaA_toobject(L, narg);
|
||||
@ -66,7 +66,7 @@ static l_noret tag_error(lua_State* L, int narg, int tag)
|
||||
luaL_typeerrorL(L, narg, lua_typename(L, tag));
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_where(lua_State* L, int level)
|
||||
void luaL_where(lua_State* L, int level)
|
||||
{
|
||||
lua_Debug ar;
|
||||
if (lua_getinfo(L, level, "sl", &ar) && ar.currentline > 0)
|
||||
@ -77,7 +77,7 @@ LUALIB_API void luaL_where(lua_State* L, int level)
|
||||
lua_pushliteral(L, ""); /* else, no information available... */
|
||||
}
|
||||
|
||||
LUALIB_API l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
|
||||
l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
@ -90,7 +90,7 @@ LUALIB_API l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
LUALIB_API int luaL_checkoption(lua_State* L, int narg, const char* def, const char* const lst[])
|
||||
int luaL_checkoption(lua_State* L, int narg, const char* def, const char* const lst[])
|
||||
{
|
||||
const char* name = (def) ? luaL_optstring(L, narg, def) : luaL_checkstring(L, narg);
|
||||
int i;
|
||||
@ -101,7 +101,7 @@ LUALIB_API int luaL_checkoption(lua_State* L, int narg, const char* def, const c
|
||||
luaL_argerrorL(L, narg, msg);
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_newmetatable(lua_State* L, const char* tname)
|
||||
int luaL_newmetatable(lua_State* L, const char* tname)
|
||||
{
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
|
||||
if (!lua_isnil(L, -1)) /* name already in use? */
|
||||
@ -113,7 +113,7 @@ LUALIB_API int luaL_newmetatable(lua_State* L, const char* tname)
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUALIB_API void* luaL_checkudata(lua_State* L, int ud, const char* tname)
|
||||
void* luaL_checkudata(lua_State* L, int ud, const char* tname)
|
||||
{
|
||||
void* p = lua_touserdata(L, ud);
|
||||
if (p != NULL)
|
||||
@ -131,25 +131,25 @@ LUALIB_API void* luaL_checkudata(lua_State* L, int ud, const char* tname)
|
||||
luaL_typeerrorL(L, ud, tname); /* else error */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_checkstack(lua_State* L, int space, const char* mes)
|
||||
void luaL_checkstack(lua_State* L, int space, const char* mes)
|
||||
{
|
||||
if (!lua_checkstack(L, space))
|
||||
luaL_error(L, "stack overflow (%s)", mes);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_checktype(lua_State* L, int narg, int t)
|
||||
void luaL_checktype(lua_State* L, int narg, int t)
|
||||
{
|
||||
if (lua_type(L, narg) != t)
|
||||
tag_error(L, narg, t);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_checkany(lua_State* L, int narg)
|
||||
void luaL_checkany(lua_State* L, int narg)
|
||||
{
|
||||
if (lua_type(L, narg) == LUA_TNONE)
|
||||
luaL_error(L, "missing argument #%d", narg);
|
||||
}
|
||||
|
||||
LUALIB_API const char* luaL_checklstring(lua_State* L, int narg, size_t* len)
|
||||
const char* luaL_checklstring(lua_State* L, int narg, size_t* len)
|
||||
{
|
||||
const char* s = lua_tolstring(L, narg, len);
|
||||
if (!s)
|
||||
@ -157,7 +157,7 @@ LUALIB_API const char* luaL_checklstring(lua_State* L, int narg, size_t* len)
|
||||
return s;
|
||||
}
|
||||
|
||||
LUALIB_API const char* luaL_optlstring(lua_State* L, int narg, const char* def, size_t* len)
|
||||
const char* luaL_optlstring(lua_State* L, int narg, const char* def, size_t* len)
|
||||
{
|
||||
if (lua_isnoneornil(L, narg))
|
||||
{
|
||||
@ -169,7 +169,7 @@ LUALIB_API const char* luaL_optlstring(lua_State* L, int narg, const char* def,
|
||||
return luaL_checklstring(L, narg, len);
|
||||
}
|
||||
|
||||
LUALIB_API double luaL_checknumber(lua_State* L, int narg)
|
||||
double luaL_checknumber(lua_State* L, int narg)
|
||||
{
|
||||
int isnum;
|
||||
double d = lua_tonumberx(L, narg, &isnum);
|
||||
@ -178,12 +178,28 @@ LUALIB_API double luaL_checknumber(lua_State* L, int narg)
|
||||
return d;
|
||||
}
|
||||
|
||||
LUALIB_API double luaL_optnumber(lua_State* L, int narg, double def)
|
||||
double luaL_optnumber(lua_State* L, int narg, double def)
|
||||
{
|
||||
return luaL_opt(L, luaL_checknumber, narg, def);
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_checkinteger(lua_State* L, int narg)
|
||||
int luaL_checkboolean(lua_State* L, int narg)
|
||||
{
|
||||
// This checks specifically for boolean values, ignoring
|
||||
// all other truthy/falsy values. If the desired result
|
||||
// is true if value is present then lua_toboolean should
|
||||
// directly be used instead.
|
||||
if (!lua_isboolean(L, narg))
|
||||
tag_error(L, narg, LUA_TBOOLEAN);
|
||||
return lua_toboolean(L, narg);
|
||||
}
|
||||
|
||||
int luaL_optboolean(lua_State* L, int narg, int def)
|
||||
{
|
||||
return luaL_opt(L, luaL_checkboolean, narg, def);
|
||||
}
|
||||
|
||||
int luaL_checkinteger(lua_State* L, int narg)
|
||||
{
|
||||
int isnum;
|
||||
int d = lua_tointegerx(L, narg, &isnum);
|
||||
@ -192,12 +208,12 @@ LUALIB_API int luaL_checkinteger(lua_State* L, int narg)
|
||||
return d;
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_optinteger(lua_State* L, int narg, int def)
|
||||
int luaL_optinteger(lua_State* L, int narg, int def)
|
||||
{
|
||||
return luaL_opt(L, luaL_checkinteger, narg, def);
|
||||
}
|
||||
|
||||
LUALIB_API unsigned luaL_checkunsigned(lua_State* L, int narg)
|
||||
unsigned luaL_checkunsigned(lua_State* L, int narg)
|
||||
{
|
||||
int isnum;
|
||||
unsigned d = lua_tounsignedx(L, narg, &isnum);
|
||||
@ -206,12 +222,25 @@ LUALIB_API unsigned luaL_checkunsigned(lua_State* L, int narg)
|
||||
return d;
|
||||
}
|
||||
|
||||
LUALIB_API unsigned luaL_optunsigned(lua_State* L, int narg, unsigned def)
|
||||
unsigned luaL_optunsigned(lua_State* L, int narg, unsigned def)
|
||||
{
|
||||
return luaL_opt(L, luaL_checkunsigned, narg, def);
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_getmetafield(lua_State* L, int obj, const char* event)
|
||||
const float* luaL_checkvector(lua_State* L, int narg)
|
||||
{
|
||||
const float* v = lua_tovector(L, narg);
|
||||
if (!v)
|
||||
tag_error(L, narg, LUA_TVECTOR);
|
||||
return v;
|
||||
}
|
||||
|
||||
const float* luaL_optvector(lua_State* L, int narg, const float* def)
|
||||
{
|
||||
return luaL_opt(L, luaL_checkvector, narg, def);
|
||||
}
|
||||
|
||||
int luaL_getmetafield(lua_State* L, int obj, const char* event)
|
||||
{
|
||||
if (!lua_getmetatable(L, obj)) /* no metatable? */
|
||||
return 0;
|
||||
@ -229,7 +258,7 @@ LUALIB_API int luaL_getmetafield(lua_State* L, int obj, const char* event)
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_callmeta(lua_State* L, int obj, const char* event)
|
||||
int luaL_callmeta(lua_State* L, int obj, const char* event)
|
||||
{
|
||||
obj = abs_index(L, obj);
|
||||
if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
|
||||
@ -247,7 +276,7 @@ static int libsize(const luaL_Reg* l)
|
||||
return size;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_register(lua_State* L, const char* libname, const luaL_Reg* l)
|
||||
void luaL_register(lua_State* L, const char* libname, const luaL_Reg* l)
|
||||
{
|
||||
if (libname)
|
||||
{
|
||||
@ -273,7 +302,7 @@ LUALIB_API void luaL_register(lua_State* L, const char* libname, const luaL_Reg*
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
|
||||
const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
|
||||
{
|
||||
const char* e;
|
||||
lua_pushvalue(L, idx);
|
||||
@ -324,7 +353,7 @@ static size_t getnextbuffersize(lua_State* L, size_t currentsize, size_t desired
|
||||
return newsize;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_buffinit(lua_State* L, luaL_Buffer* B)
|
||||
void luaL_buffinit(lua_State* L, luaL_Buffer* B)
|
||||
{
|
||||
// start with an internal buffer
|
||||
B->p = B->buffer;
|
||||
@ -334,14 +363,14 @@ LUALIB_API void luaL_buffinit(lua_State* L, luaL_Buffer* B)
|
||||
B->storage = nullptr;
|
||||
}
|
||||
|
||||
LUALIB_API char* luaL_buffinitsize(lua_State* L, luaL_Buffer* B, size_t size)
|
||||
char* luaL_buffinitsize(lua_State* L, luaL_Buffer* B, size_t size)
|
||||
{
|
||||
luaL_buffinit(L, B);
|
||||
luaL_reservebuffer(B, size, -1);
|
||||
return B->p;
|
||||
}
|
||||
|
||||
LUALIB_API char* luaL_extendbuffer(luaL_Buffer* B, size_t additionalsize, int boxloc)
|
||||
char* luaL_extendbuffer(luaL_Buffer* B, size_t additionalsize, int boxloc)
|
||||
{
|
||||
lua_State* L = B->L;
|
||||
|
||||
@ -372,13 +401,13 @@ LUALIB_API char* luaL_extendbuffer(luaL_Buffer* B, size_t additionalsize, int bo
|
||||
return B->p;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_reservebuffer(luaL_Buffer* B, size_t size, int boxloc)
|
||||
void luaL_reservebuffer(luaL_Buffer* B, size_t size, int boxloc)
|
||||
{
|
||||
if (size_t(B->end - B->p) < size)
|
||||
luaL_extendbuffer(B, size - (B->end - B->p), boxloc);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addlstring(luaL_Buffer* B, const char* s, size_t len)
|
||||
void luaL_addlstring(luaL_Buffer* B, const char* s, size_t len)
|
||||
{
|
||||
if (size_t(B->end - B->p) < len)
|
||||
luaL_extendbuffer(B, len - (B->end - B->p), -1);
|
||||
@ -387,7 +416,7 @@ LUALIB_API void luaL_addlstring(luaL_Buffer* B, const char* s, size_t len)
|
||||
B->p += len;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addvalue(luaL_Buffer* B)
|
||||
void luaL_addvalue(luaL_Buffer* B)
|
||||
{
|
||||
lua_State* L = B->L;
|
||||
|
||||
@ -404,7 +433,7 @@ LUALIB_API void luaL_addvalue(luaL_Buffer* B)
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushresult(luaL_Buffer* B)
|
||||
void luaL_pushresult(luaL_Buffer* B)
|
||||
{
|
||||
lua_State* L = B->L;
|
||||
|
||||
@ -428,7 +457,7 @@ LUALIB_API void luaL_pushresult(luaL_Buffer* B)
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushresultsize(luaL_Buffer* B, size_t size)
|
||||
void luaL_pushresultsize(luaL_Buffer* B, size_t size)
|
||||
{
|
||||
B->p += size;
|
||||
luaL_pushresult(B);
|
||||
@ -436,7 +465,7 @@ LUALIB_API void luaL_pushresultsize(luaL_Buffer* B, size_t size)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
LUALIB_API const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
|
||||
const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
|
||||
{
|
||||
if (luaL_callmeta(L, idx, "__tostring")) /* is there a metafield? */
|
||||
{
|
||||
@ -462,7 +491,11 @@ LUALIB_API const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
|
||||
case LUA_TVECTOR:
|
||||
{
|
||||
const float* v = lua_tovector(L, idx);
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2], v[3]);
|
||||
#else
|
||||
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -401,7 +401,7 @@ static int luaB_newproxy(lua_State* L)
|
||||
|
||||
bool needsmt = lua_toboolean(L, 1);
|
||||
|
||||
lua_newuserdata(L, 0, 0);
|
||||
lua_newuserdata(L, 0);
|
||||
|
||||
if (needsmt)
|
||||
{
|
||||
@ -441,7 +441,7 @@ static void auxopen(lua_State* L, const char* name, lua_CFunction f, lua_CFuncti
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_base(lua_State* L)
|
||||
int luaopen_base(lua_State* L)
|
||||
{
|
||||
/* set global _G */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
|
@ -236,7 +236,7 @@ static const luaL_Reg bitlib[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_bit32(lua_State* L)
|
||||
int luaopen_bit32(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_BITLIBNAME, bitlib);
|
||||
|
||||
|
@ -1018,13 +1018,23 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St
|
||||
|
||||
static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||
{
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
if (nparams >= 4 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1) && ttisnumber(args + 2))
|
||||
#else
|
||||
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
|
||||
#endif
|
||||
{
|
||||
double x = nvalue(arg0);
|
||||
double y = nvalue(args);
|
||||
double z = nvalue(args + 1);
|
||||
|
||||
setvvalue(res, float(x), float(y), float(z));
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
double w = nvalue(args + 2);
|
||||
setvvalue(res, float(x), float(y), float(z), float(w));
|
||||
#else
|
||||
setvvalue(res, float(x), float(y), float(z), 0.0f);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ static const luaL_Reg co_funcs[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_coroutine(lua_State* L)
|
||||
int luaopen_coroutine(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_COLIBNAME, co_funcs);
|
||||
|
||||
|
@ -160,7 +160,7 @@ static const luaL_Reg dblib[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_debug(lua_State* L)
|
||||
int luaopen_debug(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_DBLIBNAME, dblib);
|
||||
return 1;
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauExceptionMessageFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCcallRestoreFix, false)
|
||||
LUAU_FASTFLAG(LuauCoroutineClose)
|
||||
LUAU_FASTFLAGVARIABLE(LuauActivateBeforeExec, false)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
@ -74,35 +74,28 @@ public:
|
||||
|
||||
const char* what() const throw() override
|
||||
{
|
||||
if (FFlag::LuauExceptionMessageFix)
|
||||
// LUA_ERRRUN/LUA_ERRSYNTAX pass an object on the stack which is intended to describe the error.
|
||||
if (status == LUA_ERRRUN || status == LUA_ERRSYNTAX)
|
||||
{
|
||||
// LUA_ERRRUN/LUA_ERRSYNTAX pass an object on the stack which is intended to describe the error.
|
||||
if (status == LUA_ERRRUN || status == LUA_ERRSYNTAX)
|
||||
// Conversion to a string could still fail. For example if a user passes a non-string/non-number argument to `error()`.
|
||||
if (const char* str = lua_tostring(L, -1))
|
||||
{
|
||||
// Conversion to a string could still fail. For example if a user passes a non-string/non-number argument to `error()`.
|
||||
if (const char* str = lua_tostring(L, -1))
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case LUA_ERRRUN:
|
||||
return "lua_exception: LUA_ERRRUN (no string/number provided as description)";
|
||||
case LUA_ERRSYNTAX:
|
||||
return "lua_exception: LUA_ERRSYNTAX (no string/number provided as description)";
|
||||
case LUA_ERRMEM:
|
||||
return "lua_exception: " LUA_MEMERRMSG;
|
||||
case LUA_ERRERR:
|
||||
return "lua_exception: " LUA_ERRERRMSG;
|
||||
default:
|
||||
return "lua_exception: unexpected exception status";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
switch (status)
|
||||
{
|
||||
return lua_tostring(L, -1);
|
||||
case LUA_ERRRUN:
|
||||
return "lua_exception: LUA_ERRRUN (no string/number provided as description)";
|
||||
case LUA_ERRSYNTAX:
|
||||
return "lua_exception: LUA_ERRSYNTAX (no string/number provided as description)";
|
||||
case LUA_ERRMEM:
|
||||
return "lua_exception: " LUA_MEMERRMSG;
|
||||
case LUA_ERRERR:
|
||||
return "lua_exception: " LUA_ERRERRMSG;
|
||||
default:
|
||||
return "lua_exception: unexpected exception status";
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,7 +227,22 @@ void luaD_call(lua_State* L, StkId func, int nResults)
|
||||
if (luau_precall(L, func, nResults) == PCRLUA)
|
||||
{ /* is a Lua function? */
|
||||
L->ci->flags |= LUA_CALLINFO_RETURN; /* luau_execute will stop after returning from the stack frame */
|
||||
luau_execute(L); /* call it */
|
||||
|
||||
if (FFlag::LuauActivateBeforeExec)
|
||||
{
|
||||
int oldactive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
|
||||
luau_execute(L); /* call it */
|
||||
|
||||
if (!oldactive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
luau_execute(L); /* call it */
|
||||
}
|
||||
}
|
||||
L->nCcalls--;
|
||||
luaC_checkGC(L);
|
||||
@ -527,10 +535,10 @@ static void restore_stack_limit(lua_State* L)
|
||||
|
||||
int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t ef)
|
||||
{
|
||||
int status;
|
||||
unsigned short oldnCcalls = L->nCcalls;
|
||||
ptrdiff_t old_ci = saveci(L, L->ci);
|
||||
status = luaD_rawrunprotected(L, func, u);
|
||||
int oldactive = luaC_threadactive(L);
|
||||
int status = luaD_rawrunprotected(L, func, u);
|
||||
if (status != 0)
|
||||
{
|
||||
// call user-defined error function (used in xpcall)
|
||||
@ -541,6 +549,13 @@ int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t e
|
||||
status = LUA_ERRERR;
|
||||
}
|
||||
|
||||
if (FFlag::LuauActivateBeforeExec)
|
||||
{
|
||||
// since the call failed with an error, we might have to reset the 'active' thread state
|
||||
if (!oldactive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
}
|
||||
|
||||
if (FFlag::LuauCcallRestoreFix)
|
||||
{
|
||||
// Restore nCcalls before calling the debugprotectederror callback which may rely on the proper value to have been restored.
|
||||
|
548
VM/src/lgc.cpp
548
VM/src/lgc.cpp
@ -10,9 +10,7 @@
|
||||
#include "ldo.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauRescanGrayAgainForwardBarrier, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSeparateAtomic, false)
|
||||
|
||||
LUAU_FASTFLAG(LuauArrayBoundary)
|
||||
@ -988,7 +986,7 @@ void luaC_barriertable(lua_State* L, Table* t, GCObject* v)
|
||||
GCObject* o = obj2gco(t);
|
||||
|
||||
// in the second propagation stage, table assignment barrier works as a forward barrier
|
||||
if (FFlag::LuauRescanGrayAgainForwardBarrier && g->gcstate == GCSpropagateagain)
|
||||
if (g->gcstate == GCSpropagateagain)
|
||||
{
|
||||
LUAU_ASSERT(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
|
||||
reallymarkobject(g, v);
|
||||
@ -1044,550 +1042,6 @@ void luaC_linkupval(lua_State* L, UpVal* uv)
|
||||
}
|
||||
}
|
||||
|
||||
static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
||||
{
|
||||
LUAU_ASSERT(!isdead(g, t));
|
||||
|
||||
if (keepinvariant(g))
|
||||
{
|
||||
/* basic incremental invariant: black can't point to white */
|
||||
LUAU_ASSERT(!(isblack(f) && iswhite(t)));
|
||||
}
|
||||
}
|
||||
|
||||
static void validateref(global_State* g, GCObject* f, TValue* v)
|
||||
{
|
||||
if (iscollectable(v))
|
||||
{
|
||||
LUAU_ASSERT(ttype(v) == gcvalue(v)->gch.tt);
|
||||
validateobjref(g, f, gcvalue(v));
|
||||
}
|
||||
}
|
||||
|
||||
static void validatetable(global_State* g, Table* h)
|
||||
{
|
||||
int sizenode = 1 << h->lsizenode;
|
||||
|
||||
if (FFlag::LuauArrayBoundary)
|
||||
LUAU_ASSERT(h->lastfree <= sizenode);
|
||||
else
|
||||
LUAU_ASSERT(h->lastfree >= 0 && h->lastfree <= sizenode);
|
||||
|
||||
if (h->metatable)
|
||||
validateobjref(g, obj2gco(h), obj2gco(h->metatable));
|
||||
|
||||
for (int i = 0; i < h->sizearray; ++i)
|
||||
validateref(g, obj2gco(h), &h->array[i]);
|
||||
|
||||
for (int i = 0; i < sizenode; ++i)
|
||||
{
|
||||
LuaNode* n = &h->node[i];
|
||||
|
||||
LUAU_ASSERT(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
|
||||
LUAU_ASSERT(i + gnext(n) >= 0 && i + gnext(n) < sizenode);
|
||||
|
||||
if (!ttisnil(gval(n)))
|
||||
{
|
||||
TValue k = {};
|
||||
k.tt = gkey(n)->tt;
|
||||
k.value = gkey(n)->value;
|
||||
|
||||
validateref(g, obj2gco(h), &k);
|
||||
validateref(g, obj2gco(h), gval(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void validateclosure(global_State* g, Closure* cl)
|
||||
{
|
||||
validateobjref(g, obj2gco(cl), obj2gco(cl->env));
|
||||
|
||||
if (cl->isC)
|
||||
{
|
||||
for (int i = 0; i < cl->nupvalues; ++i)
|
||||
validateref(g, obj2gco(cl), &cl->c.upvals[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(cl->nupvalues == cl->l.p->nups);
|
||||
|
||||
validateobjref(g, obj2gco(cl), obj2gco(cl->l.p));
|
||||
|
||||
for (int i = 0; i < cl->nupvalues; ++i)
|
||||
validateref(g, obj2gco(cl), &cl->l.uprefs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void validatestack(global_State* g, lua_State* l)
|
||||
{
|
||||
validateref(g, obj2gco(l), gt(l));
|
||||
|
||||
for (CallInfo* ci = l->base_ci; ci <= l->ci; ++ci)
|
||||
{
|
||||
LUAU_ASSERT(l->stack <= ci->base);
|
||||
LUAU_ASSERT(ci->func <= ci->base && ci->base <= ci->top);
|
||||
LUAU_ASSERT(ci->top <= l->stack_last);
|
||||
}
|
||||
|
||||
// note: stack refs can violate gc invariant so we only check for liveness
|
||||
for (StkId o = l->stack; o < l->top; ++o)
|
||||
checkliveness(g, o);
|
||||
|
||||
if (l->namecall)
|
||||
validateobjref(g, obj2gco(l), obj2gco(l->namecall));
|
||||
|
||||
for (GCObject* uv = l->openupval; uv; uv = uv->gch.next)
|
||||
{
|
||||
LUAU_ASSERT(uv->gch.tt == LUA_TUPVAL);
|
||||
LUAU_ASSERT(gco2uv(uv)->v != &gco2uv(uv)->u.value);
|
||||
}
|
||||
}
|
||||
|
||||
static void validateproto(global_State* g, Proto* f)
|
||||
{
|
||||
if (f->source)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->source));
|
||||
|
||||
if (f->debugname)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->debugname));
|
||||
|
||||
for (int i = 0; i < f->sizek; ++i)
|
||||
validateref(g, obj2gco(f), &f->k[i]);
|
||||
|
||||
for (int i = 0; i < f->sizeupvalues; ++i)
|
||||
if (f->upvalues[i])
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->upvalues[i]));
|
||||
|
||||
for (int i = 0; i < f->sizep; ++i)
|
||||
if (f->p[i])
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->p[i]));
|
||||
|
||||
for (int i = 0; i < f->sizelocvars; i++)
|
||||
if (f->locvars[i].varname)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->locvars[i].varname));
|
||||
}
|
||||
|
||||
static void validateobj(global_State* g, GCObject* o)
|
||||
{
|
||||
/* dead objects can only occur during sweep */
|
||||
if (isdead(g, o))
|
||||
{
|
||||
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TSTRING:
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
validatetable(g, gco2h(o));
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
validateclosure(g, gco2cl(o));
|
||||
break;
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
if (gco2u(o)->metatable)
|
||||
validateobjref(g, o, obj2gco(gco2u(o)->metatable));
|
||||
break;
|
||||
|
||||
case LUA_TTHREAD:
|
||||
validatestack(g, gco2th(o));
|
||||
break;
|
||||
|
||||
case LUA_TPROTO:
|
||||
validateproto(g, gco2p(o));
|
||||
break;
|
||||
|
||||
case LUA_TUPVAL:
|
||||
validateref(g, o, gco2uv(o)->v);
|
||||
break;
|
||||
|
||||
default:
|
||||
LUAU_ASSERT(!"unexpected object type");
|
||||
}
|
||||
}
|
||||
|
||||
static void validatelist(global_State* g, GCObject* o)
|
||||
{
|
||||
while (o)
|
||||
{
|
||||
validateobj(g, o);
|
||||
|
||||
o = o->gch.next;
|
||||
}
|
||||
}
|
||||
|
||||
static void validategraylist(global_State* g, GCObject* o)
|
||||
{
|
||||
if (!keepinvariant(g))
|
||||
return;
|
||||
|
||||
while (o)
|
||||
{
|
||||
LUAU_ASSERT(isgray(o));
|
||||
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TTABLE:
|
||||
o = gco2h(o)->gclist;
|
||||
break;
|
||||
case LUA_TFUNCTION:
|
||||
o = gco2cl(o)->gclist;
|
||||
break;
|
||||
case LUA_TTHREAD:
|
||||
o = gco2th(o)->gclist;
|
||||
break;
|
||||
case LUA_TPROTO:
|
||||
o = gco2p(o)->gclist;
|
||||
break;
|
||||
default:
|
||||
LUAU_ASSERT(!"unknown object in gray list");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void luaC_validate(lua_State* L)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
|
||||
LUAU_ASSERT(!isdead(g, obj2gco(g->mainthread)));
|
||||
checkliveness(g, &g->registry);
|
||||
|
||||
for (int i = 0; i < LUA_T_COUNT; ++i)
|
||||
if (g->mt[i])
|
||||
LUAU_ASSERT(!isdead(g, obj2gco(g->mt[i])));
|
||||
|
||||
validategraylist(g, g->weak);
|
||||
validategraylist(g, g->gray);
|
||||
validategraylist(g, g->grayagain);
|
||||
|
||||
for (int i = 0; i < g->strt.size; ++i)
|
||||
validatelist(g, g->strt.hash[i]);
|
||||
|
||||
validatelist(g, g->rootgc);
|
||||
validatelist(g, g->strbufgc);
|
||||
|
||||
for (UpVal* uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next)
|
||||
{
|
||||
LUAU_ASSERT(uv->tt == LUA_TUPVAL);
|
||||
LUAU_ASSERT(uv->v != &uv->u.value);
|
||||
LUAU_ASSERT(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool safejson(char ch)
|
||||
{
|
||||
return unsigned(ch) < 128 && ch >= 32 && ch != '\\' && ch != '\"';
|
||||
}
|
||||
|
||||
static void dumpref(FILE* f, GCObject* o)
|
||||
{
|
||||
fprintf(f, "\"%p\"", o);
|
||||
}
|
||||
|
||||
static void dumprefs(FILE* f, TValue* data, size_t size)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (iscollectable(&data[i]))
|
||||
{
|
||||
if (!first)
|
||||
fputc(',', f);
|
||||
first = false;
|
||||
|
||||
dumpref(f, gcvalue(&data[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpstringdata(FILE* f, const char* data, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
fputc(safejson(data[i]) ? data[i] : '?', f);
|
||||
}
|
||||
|
||||
static void dumpstring(FILE* f, TString* ts)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"string\",\"cat\":%d,\"size\":%d,\"data\":\"", ts->memcat, int(sizestring(ts->len)));
|
||||
dumpstringdata(f, ts->data, ts->len);
|
||||
fprintf(f, "\"}");
|
||||
}
|
||||
|
||||
static void dumptable(FILE* f, Table* h)
|
||||
{
|
||||
size_t size = sizeof(Table) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
||||
|
||||
fprintf(f, "{\"type\":\"table\",\"cat\":%d,\"size\":%d", h->memcat, int(size));
|
||||
|
||||
if (h->node != &luaH_dummynode)
|
||||
{
|
||||
fprintf(f, ",\"pairs\":[");
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int i = 0; i < sizenode(h); ++i)
|
||||
{
|
||||
const LuaNode& n = h->node[i];
|
||||
|
||||
if (!ttisnil(&n.val) && (iscollectable(&n.key) || iscollectable(&n.val)))
|
||||
{
|
||||
if (!first)
|
||||
fputc(',', f);
|
||||
first = false;
|
||||
|
||||
if (iscollectable(&n.key))
|
||||
dumpref(f, gcvalue(&n.key));
|
||||
else
|
||||
fprintf(f, "null");
|
||||
|
||||
fputc(',', f);
|
||||
|
||||
if (iscollectable(&n.val))
|
||||
dumpref(f, gcvalue(&n.val));
|
||||
else
|
||||
fprintf(f, "null");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "]");
|
||||
}
|
||||
if (h->sizearray)
|
||||
{
|
||||
fprintf(f, ",\"array\":[");
|
||||
dumprefs(f, h->array, h->sizearray);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
if (h->metatable)
|
||||
{
|
||||
fprintf(f, ",\"metatable\":");
|
||||
dumpref(f, obj2gco(h->metatable));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpclosure(FILE* f, Closure* cl)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"function\",\"cat\":%d,\"size\":%d", cl->memcat,
|
||||
cl->isC ? int(sizeCclosure(cl->nupvalues)) : int(sizeLclosure(cl->nupvalues)));
|
||||
|
||||
fprintf(f, ",\"env\":");
|
||||
dumpref(f, obj2gco(cl->env));
|
||||
if (cl->isC)
|
||||
{
|
||||
if (cl->nupvalues)
|
||||
{
|
||||
fprintf(f, ",\"upvalues\":[");
|
||||
dumprefs(f, cl->c.upvals, cl->nupvalues);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(f, ",\"proto\":");
|
||||
dumpref(f, obj2gco(cl->l.p));
|
||||
if (cl->nupvalues)
|
||||
{
|
||||
fprintf(f, ",\"upvalues\":[");
|
||||
dumprefs(f, cl->l.uprefs, cl->nupvalues);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpudata(FILE* f, Udata* u)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"userdata\",\"cat\":%d,\"size\":%d,\"tag\":%d", u->memcat, int(sizeudata(u->len)), u->tag);
|
||||
|
||||
if (u->metatable)
|
||||
{
|
||||
fprintf(f, ",\"metatable\":");
|
||||
dumpref(f, obj2gco(u->metatable));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpthread(FILE* f, lua_State* th)
|
||||
{
|
||||
size_t size = sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->size_ci;
|
||||
|
||||
fprintf(f, "{\"type\":\"thread\",\"cat\":%d,\"size\":%d", th->memcat, int(size));
|
||||
|
||||
if (iscollectable(&th->l_gt))
|
||||
{
|
||||
fprintf(f, ",\"env\":");
|
||||
dumpref(f, gcvalue(&th->l_gt));
|
||||
}
|
||||
|
||||
Closure* tcl = 0;
|
||||
for (CallInfo* ci = th->base_ci; ci <= th->ci; ++ci)
|
||||
{
|
||||
if (ttisfunction(ci->func))
|
||||
{
|
||||
tcl = clvalue(ci->func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcl && !tcl->isC && tcl->l.p->source)
|
||||
{
|
||||
Proto* p = tcl->l.p;
|
||||
|
||||
fprintf(f, ",\"source\":\"");
|
||||
dumpstringdata(f, p->source->data, p->source->len);
|
||||
fprintf(f, "\",\"line\":%d", p->abslineinfo ? p->abslineinfo[0] : 0);
|
||||
}
|
||||
|
||||
if (th->top > th->stack)
|
||||
{
|
||||
fprintf(f, ",\"stack\":[");
|
||||
dumprefs(f, th->stack, th->top - th->stack);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpproto(FILE* f, Proto* p)
|
||||
{
|
||||
size_t size = sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto*) * p->sizep + sizeof(TValue) * p->sizek + p->sizelineinfo +
|
||||
sizeof(LocVar) * p->sizelocvars + sizeof(TString*) * p->sizeupvalues;
|
||||
|
||||
fprintf(f, "{\"type\":\"proto\",\"cat\":%d,\"size\":%d", p->memcat, int(size));
|
||||
|
||||
if (p->source)
|
||||
{
|
||||
fprintf(f, ",\"source\":\"");
|
||||
dumpstringdata(f, p->source->data, p->source->len);
|
||||
fprintf(f, "\",\"line\":%d", p->abslineinfo ? p->abslineinfo[0] : 0);
|
||||
}
|
||||
|
||||
if (p->sizek)
|
||||
{
|
||||
fprintf(f, ",\"constants\":[");
|
||||
dumprefs(f, p->k, p->sizek);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
|
||||
if (p->sizep)
|
||||
{
|
||||
fprintf(f, ",\"protos\":[");
|
||||
for (int i = 0; i < p->sizep; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
fputc(',', f);
|
||||
dumpref(f, obj2gco(p->p[i]));
|
||||
}
|
||||
fprintf(f, "]");
|
||||
}
|
||||
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpupval(FILE* f, UpVal* uv)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"upvalue\",\"cat\":%d,\"size\":%d", uv->memcat, int(sizeof(UpVal)));
|
||||
|
||||
if (iscollectable(uv->v))
|
||||
{
|
||||
fprintf(f, ",\"object\":");
|
||||
dumpref(f, gcvalue(uv->v));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpobj(FILE* f, GCObject* o)
|
||||
{
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TSTRING:
|
||||
return dumpstring(f, gco2ts(o));
|
||||
|
||||
case LUA_TTABLE:
|
||||
return dumptable(f, gco2h(o));
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
return dumpclosure(f, gco2cl(o));
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
return dumpudata(f, gco2u(o));
|
||||
|
||||
case LUA_TTHREAD:
|
||||
return dumpthread(f, gco2th(o));
|
||||
|
||||
case LUA_TPROTO:
|
||||
return dumpproto(f, gco2p(o));
|
||||
|
||||
case LUA_TUPVAL:
|
||||
return dumpupval(f, gco2uv(o));
|
||||
|
||||
default:
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void dumplist(FILE* f, GCObject* o)
|
||||
{
|
||||
while (o)
|
||||
{
|
||||
dumpref(f, o);
|
||||
fputc(':', f);
|
||||
dumpobj(f, o);
|
||||
fputc(',', f);
|
||||
fputc('\n', f);
|
||||
|
||||
// thread has additional list containing collectable objects that are not present in rootgc
|
||||
if (o->gch.tt == LUA_TTHREAD)
|
||||
dumplist(f, gco2th(o)->openupval);
|
||||
|
||||
o = o->gch.next;
|
||||
}
|
||||
}
|
||||
|
||||
void luaC_dump(lua_State* L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat))
|
||||
{
|
||||
global_State* g = L->global;
|
||||
FILE* f = static_cast<FILE*>(file);
|
||||
|
||||
fprintf(f, "{\"objects\":{\n");
|
||||
dumplist(f, g->rootgc);
|
||||
dumplist(f, g->strbufgc);
|
||||
for (int i = 0; i < g->strt.size; ++i)
|
||||
dumplist(f, g->strt.hash[i]);
|
||||
|
||||
fprintf(f, "\"0\":{\"type\":\"userdata\",\"cat\":0,\"size\":0}\n"); // to avoid issues with trailing ,
|
||||
fprintf(f, "},\"roots\":{\n");
|
||||
fprintf(f, "\"mainthread\":");
|
||||
dumpref(f, obj2gco(g->mainthread));
|
||||
fprintf(f, ",\"registry\":");
|
||||
dumpref(f, gcvalue(&g->registry));
|
||||
|
||||
fprintf(f, "},\"stats\":{\n");
|
||||
|
||||
fprintf(f, "\"size\":%d,\n", int(g->totalbytes));
|
||||
|
||||
fprintf(f, "\"categories\":{\n");
|
||||
for (int i = 0; i < LUA_MEMORY_CATEGORIES; i++)
|
||||
{
|
||||
if (size_t bytes = g->memcatbytes[i])
|
||||
{
|
||||
if (categoryName)
|
||||
fprintf(f, "\"%d\":{\"name\":\"%s\", \"size\":%d},\n", i, categoryName(L, i), int(bytes));
|
||||
else
|
||||
fprintf(f, "\"%d\":{\"size\":%d},\n", i, int(bytes));
|
||||
}
|
||||
}
|
||||
fprintf(f, "\"none\":{}\n"); // to avoid issues with trailing ,
|
||||
fprintf(f, "}\n");
|
||||
fprintf(f, "}}\n");
|
||||
}
|
||||
|
||||
// measure the allocation rate in bytes/sec
|
||||
// returns -1 if allocation rate cannot be measured
|
||||
int64_t luaC_allocationrate(lua_State* L)
|
||||
|
558
VM/src/lgcdebug.cpp
Normal file
558
VM/src/lgcdebug.cpp
Normal file
@ -0,0 +1,558 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
||||
#include "lgc.h"
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "ltable.h"
|
||||
#include "lfunc.h"
|
||||
#include "lstring.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauArrayBoundary)
|
||||
|
||||
static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
||||
{
|
||||
LUAU_ASSERT(!isdead(g, t));
|
||||
|
||||
if (keepinvariant(g))
|
||||
{
|
||||
/* basic incremental invariant: black can't point to white */
|
||||
LUAU_ASSERT(!(isblack(f) && iswhite(t)));
|
||||
}
|
||||
}
|
||||
|
||||
static void validateref(global_State* g, GCObject* f, TValue* v)
|
||||
{
|
||||
if (iscollectable(v))
|
||||
{
|
||||
LUAU_ASSERT(ttype(v) == gcvalue(v)->gch.tt);
|
||||
validateobjref(g, f, gcvalue(v));
|
||||
}
|
||||
}
|
||||
|
||||
static void validatetable(global_State* g, Table* h)
|
||||
{
|
||||
int sizenode = 1 << h->lsizenode;
|
||||
|
||||
if (FFlag::LuauArrayBoundary)
|
||||
LUAU_ASSERT(h->lastfree <= sizenode);
|
||||
else
|
||||
LUAU_ASSERT(h->lastfree >= 0 && h->lastfree <= sizenode);
|
||||
|
||||
if (h->metatable)
|
||||
validateobjref(g, obj2gco(h), obj2gco(h->metatable));
|
||||
|
||||
for (int i = 0; i < h->sizearray; ++i)
|
||||
validateref(g, obj2gco(h), &h->array[i]);
|
||||
|
||||
for (int i = 0; i < sizenode; ++i)
|
||||
{
|
||||
LuaNode* n = &h->node[i];
|
||||
|
||||
LUAU_ASSERT(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
|
||||
LUAU_ASSERT(i + gnext(n) >= 0 && i + gnext(n) < sizenode);
|
||||
|
||||
if (!ttisnil(gval(n)))
|
||||
{
|
||||
TValue k = {};
|
||||
k.tt = gkey(n)->tt;
|
||||
k.value = gkey(n)->value;
|
||||
|
||||
validateref(g, obj2gco(h), &k);
|
||||
validateref(g, obj2gco(h), gval(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void validateclosure(global_State* g, Closure* cl)
|
||||
{
|
||||
validateobjref(g, obj2gco(cl), obj2gco(cl->env));
|
||||
|
||||
if (cl->isC)
|
||||
{
|
||||
for (int i = 0; i < cl->nupvalues; ++i)
|
||||
validateref(g, obj2gco(cl), &cl->c.upvals[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(cl->nupvalues == cl->l.p->nups);
|
||||
|
||||
validateobjref(g, obj2gco(cl), obj2gco(cl->l.p));
|
||||
|
||||
for (int i = 0; i < cl->nupvalues; ++i)
|
||||
validateref(g, obj2gco(cl), &cl->l.uprefs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void validatestack(global_State* g, lua_State* l)
|
||||
{
|
||||
validateref(g, obj2gco(l), gt(l));
|
||||
|
||||
for (CallInfo* ci = l->base_ci; ci <= l->ci; ++ci)
|
||||
{
|
||||
LUAU_ASSERT(l->stack <= ci->base);
|
||||
LUAU_ASSERT(ci->func <= ci->base && ci->base <= ci->top);
|
||||
LUAU_ASSERT(ci->top <= l->stack_last);
|
||||
}
|
||||
|
||||
// note: stack refs can violate gc invariant so we only check for liveness
|
||||
for (StkId o = l->stack; o < l->top; ++o)
|
||||
checkliveness(g, o);
|
||||
|
||||
if (l->namecall)
|
||||
validateobjref(g, obj2gco(l), obj2gco(l->namecall));
|
||||
|
||||
for (GCObject* uv = l->openupval; uv; uv = uv->gch.next)
|
||||
{
|
||||
LUAU_ASSERT(uv->gch.tt == LUA_TUPVAL);
|
||||
LUAU_ASSERT(gco2uv(uv)->v != &gco2uv(uv)->u.value);
|
||||
}
|
||||
}
|
||||
|
||||
static void validateproto(global_State* g, Proto* f)
|
||||
{
|
||||
if (f->source)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->source));
|
||||
|
||||
if (f->debugname)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->debugname));
|
||||
|
||||
for (int i = 0; i < f->sizek; ++i)
|
||||
validateref(g, obj2gco(f), &f->k[i]);
|
||||
|
||||
for (int i = 0; i < f->sizeupvalues; ++i)
|
||||
if (f->upvalues[i])
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->upvalues[i]));
|
||||
|
||||
for (int i = 0; i < f->sizep; ++i)
|
||||
if (f->p[i])
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->p[i]));
|
||||
|
||||
for (int i = 0; i < f->sizelocvars; i++)
|
||||
if (f->locvars[i].varname)
|
||||
validateobjref(g, obj2gco(f), obj2gco(f->locvars[i].varname));
|
||||
}
|
||||
|
||||
static void validateobj(global_State* g, GCObject* o)
|
||||
{
|
||||
/* dead objects can only occur during sweep */
|
||||
if (isdead(g, o))
|
||||
{
|
||||
LUAU_ASSERT(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TSTRING:
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
validatetable(g, gco2h(o));
|
||||
break;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
validateclosure(g, gco2cl(o));
|
||||
break;
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
if (gco2u(o)->metatable)
|
||||
validateobjref(g, o, obj2gco(gco2u(o)->metatable));
|
||||
break;
|
||||
|
||||
case LUA_TTHREAD:
|
||||
validatestack(g, gco2th(o));
|
||||
break;
|
||||
|
||||
case LUA_TPROTO:
|
||||
validateproto(g, gco2p(o));
|
||||
break;
|
||||
|
||||
case LUA_TUPVAL:
|
||||
validateref(g, o, gco2uv(o)->v);
|
||||
break;
|
||||
|
||||
default:
|
||||
LUAU_ASSERT(!"unexpected object type");
|
||||
}
|
||||
}
|
||||
|
||||
static void validatelist(global_State* g, GCObject* o)
|
||||
{
|
||||
while (o)
|
||||
{
|
||||
validateobj(g, o);
|
||||
|
||||
o = o->gch.next;
|
||||
}
|
||||
}
|
||||
|
||||
static void validategraylist(global_State* g, GCObject* o)
|
||||
{
|
||||
if (!keepinvariant(g))
|
||||
return;
|
||||
|
||||
while (o)
|
||||
{
|
||||
LUAU_ASSERT(isgray(o));
|
||||
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TTABLE:
|
||||
o = gco2h(o)->gclist;
|
||||
break;
|
||||
case LUA_TFUNCTION:
|
||||
o = gco2cl(o)->gclist;
|
||||
break;
|
||||
case LUA_TTHREAD:
|
||||
o = gco2th(o)->gclist;
|
||||
break;
|
||||
case LUA_TPROTO:
|
||||
o = gco2p(o)->gclist;
|
||||
break;
|
||||
default:
|
||||
LUAU_ASSERT(!"unknown object in gray list");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void luaC_validate(lua_State* L)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
|
||||
LUAU_ASSERT(!isdead(g, obj2gco(g->mainthread)));
|
||||
checkliveness(g, &g->registry);
|
||||
|
||||
for (int i = 0; i < LUA_T_COUNT; ++i)
|
||||
if (g->mt[i])
|
||||
LUAU_ASSERT(!isdead(g, obj2gco(g->mt[i])));
|
||||
|
||||
validategraylist(g, g->weak);
|
||||
validategraylist(g, g->gray);
|
||||
validategraylist(g, g->grayagain);
|
||||
|
||||
for (int i = 0; i < g->strt.size; ++i)
|
||||
validatelist(g, g->strt.hash[i]);
|
||||
|
||||
validatelist(g, g->rootgc);
|
||||
validatelist(g, g->strbufgc);
|
||||
|
||||
for (UpVal* uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next)
|
||||
{
|
||||
LUAU_ASSERT(uv->tt == LUA_TUPVAL);
|
||||
LUAU_ASSERT(uv->v != &uv->u.value);
|
||||
LUAU_ASSERT(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool safejson(char ch)
|
||||
{
|
||||
return unsigned(ch) < 128 && ch >= 32 && ch != '\\' && ch != '\"';
|
||||
}
|
||||
|
||||
static void dumpref(FILE* f, GCObject* o)
|
||||
{
|
||||
fprintf(f, "\"%p\"", o);
|
||||
}
|
||||
|
||||
static void dumprefs(FILE* f, TValue* data, size_t size)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (iscollectable(&data[i]))
|
||||
{
|
||||
if (!first)
|
||||
fputc(',', f);
|
||||
first = false;
|
||||
|
||||
dumpref(f, gcvalue(&data[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpstringdata(FILE* f, const char* data, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
fputc(safejson(data[i]) ? data[i] : '?', f);
|
||||
}
|
||||
|
||||
static void dumpstring(FILE* f, TString* ts)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"string\",\"cat\":%d,\"size\":%d,\"data\":\"", ts->memcat, int(sizestring(ts->len)));
|
||||
dumpstringdata(f, ts->data, ts->len);
|
||||
fprintf(f, "\"}");
|
||||
}
|
||||
|
||||
static void dumptable(FILE* f, Table* h)
|
||||
{
|
||||
size_t size = sizeof(Table) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
||||
|
||||
fprintf(f, "{\"type\":\"table\",\"cat\":%d,\"size\":%d", h->memcat, int(size));
|
||||
|
||||
if (h->node != &luaH_dummynode)
|
||||
{
|
||||
fprintf(f, ",\"pairs\":[");
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int i = 0; i < sizenode(h); ++i)
|
||||
{
|
||||
const LuaNode& n = h->node[i];
|
||||
|
||||
if (!ttisnil(&n.val) && (iscollectable(&n.key) || iscollectable(&n.val)))
|
||||
{
|
||||
if (!first)
|
||||
fputc(',', f);
|
||||
first = false;
|
||||
|
||||
if (iscollectable(&n.key))
|
||||
dumpref(f, gcvalue(&n.key));
|
||||
else
|
||||
fprintf(f, "null");
|
||||
|
||||
fputc(',', f);
|
||||
|
||||
if (iscollectable(&n.val))
|
||||
dumpref(f, gcvalue(&n.val));
|
||||
else
|
||||
fprintf(f, "null");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "]");
|
||||
}
|
||||
if (h->sizearray)
|
||||
{
|
||||
fprintf(f, ",\"array\":[");
|
||||
dumprefs(f, h->array, h->sizearray);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
if (h->metatable)
|
||||
{
|
||||
fprintf(f, ",\"metatable\":");
|
||||
dumpref(f, obj2gco(h->metatable));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpclosure(FILE* f, Closure* cl)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"function\",\"cat\":%d,\"size\":%d", cl->memcat,
|
||||
cl->isC ? int(sizeCclosure(cl->nupvalues)) : int(sizeLclosure(cl->nupvalues)));
|
||||
|
||||
fprintf(f, ",\"env\":");
|
||||
dumpref(f, obj2gco(cl->env));
|
||||
if (cl->isC)
|
||||
{
|
||||
if (cl->nupvalues)
|
||||
{
|
||||
fprintf(f, ",\"upvalues\":[");
|
||||
dumprefs(f, cl->c.upvals, cl->nupvalues);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(f, ",\"proto\":");
|
||||
dumpref(f, obj2gco(cl->l.p));
|
||||
if (cl->nupvalues)
|
||||
{
|
||||
fprintf(f, ",\"upvalues\":[");
|
||||
dumprefs(f, cl->l.uprefs, cl->nupvalues);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpudata(FILE* f, Udata* u)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"userdata\",\"cat\":%d,\"size\":%d,\"tag\":%d", u->memcat, int(sizeudata(u->len)), u->tag);
|
||||
|
||||
if (u->metatable)
|
||||
{
|
||||
fprintf(f, ",\"metatable\":");
|
||||
dumpref(f, obj2gco(u->metatable));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpthread(FILE* f, lua_State* th)
|
||||
{
|
||||
size_t size = sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->size_ci;
|
||||
|
||||
fprintf(f, "{\"type\":\"thread\",\"cat\":%d,\"size\":%d", th->memcat, int(size));
|
||||
|
||||
if (iscollectable(&th->l_gt))
|
||||
{
|
||||
fprintf(f, ",\"env\":");
|
||||
dumpref(f, gcvalue(&th->l_gt));
|
||||
}
|
||||
|
||||
Closure* tcl = 0;
|
||||
for (CallInfo* ci = th->base_ci; ci <= th->ci; ++ci)
|
||||
{
|
||||
if (ttisfunction(ci->func))
|
||||
{
|
||||
tcl = clvalue(ci->func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcl && !tcl->isC && tcl->l.p->source)
|
||||
{
|
||||
Proto* p = tcl->l.p;
|
||||
|
||||
fprintf(f, ",\"source\":\"");
|
||||
dumpstringdata(f, p->source->data, p->source->len);
|
||||
fprintf(f, "\",\"line\":%d", p->abslineinfo ? p->abslineinfo[0] : 0);
|
||||
}
|
||||
|
||||
if (th->top > th->stack)
|
||||
{
|
||||
fprintf(f, ",\"stack\":[");
|
||||
dumprefs(f, th->stack, th->top - th->stack);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpproto(FILE* f, Proto* p)
|
||||
{
|
||||
size_t size = sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto*) * p->sizep + sizeof(TValue) * p->sizek + p->sizelineinfo +
|
||||
sizeof(LocVar) * p->sizelocvars + sizeof(TString*) * p->sizeupvalues;
|
||||
|
||||
fprintf(f, "{\"type\":\"proto\",\"cat\":%d,\"size\":%d", p->memcat, int(size));
|
||||
|
||||
if (p->source)
|
||||
{
|
||||
fprintf(f, ",\"source\":\"");
|
||||
dumpstringdata(f, p->source->data, p->source->len);
|
||||
fprintf(f, "\",\"line\":%d", p->abslineinfo ? p->abslineinfo[0] : 0);
|
||||
}
|
||||
|
||||
if (p->sizek)
|
||||
{
|
||||
fprintf(f, ",\"constants\":[");
|
||||
dumprefs(f, p->k, p->sizek);
|
||||
fprintf(f, "]");
|
||||
}
|
||||
|
||||
if (p->sizep)
|
||||
{
|
||||
fprintf(f, ",\"protos\":[");
|
||||
for (int i = 0; i < p->sizep; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
fputc(',', f);
|
||||
dumpref(f, obj2gco(p->p[i]));
|
||||
}
|
||||
fprintf(f, "]");
|
||||
}
|
||||
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpupval(FILE* f, UpVal* uv)
|
||||
{
|
||||
fprintf(f, "{\"type\":\"upvalue\",\"cat\":%d,\"size\":%d", uv->memcat, int(sizeof(UpVal)));
|
||||
|
||||
if (iscollectable(uv->v))
|
||||
{
|
||||
fprintf(f, ",\"object\":");
|
||||
dumpref(f, gcvalue(uv->v));
|
||||
}
|
||||
fprintf(f, "}");
|
||||
}
|
||||
|
||||
static void dumpobj(FILE* f, GCObject* o)
|
||||
{
|
||||
switch (o->gch.tt)
|
||||
{
|
||||
case LUA_TSTRING:
|
||||
return dumpstring(f, gco2ts(o));
|
||||
|
||||
case LUA_TTABLE:
|
||||
return dumptable(f, gco2h(o));
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
return dumpclosure(f, gco2cl(o));
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
return dumpudata(f, gco2u(o));
|
||||
|
||||
case LUA_TTHREAD:
|
||||
return dumpthread(f, gco2th(o));
|
||||
|
||||
case LUA_TPROTO:
|
||||
return dumpproto(f, gco2p(o));
|
||||
|
||||
case LUA_TUPVAL:
|
||||
return dumpupval(f, gco2uv(o));
|
||||
|
||||
default:
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void dumplist(FILE* f, GCObject* o)
|
||||
{
|
||||
while (o)
|
||||
{
|
||||
dumpref(f, o);
|
||||
fputc(':', f);
|
||||
dumpobj(f, o);
|
||||
fputc(',', f);
|
||||
fputc('\n', f);
|
||||
|
||||
// thread has additional list containing collectable objects that are not present in rootgc
|
||||
if (o->gch.tt == LUA_TTHREAD)
|
||||
dumplist(f, gco2th(o)->openupval);
|
||||
|
||||
o = o->gch.next;
|
||||
}
|
||||
}
|
||||
|
||||
void luaC_dump(lua_State* L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat))
|
||||
{
|
||||
global_State* g = L->global;
|
||||
FILE* f = static_cast<FILE*>(file);
|
||||
|
||||
fprintf(f, "{\"objects\":{\n");
|
||||
dumplist(f, g->rootgc);
|
||||
dumplist(f, g->strbufgc);
|
||||
for (int i = 0; i < g->strt.size; ++i)
|
||||
dumplist(f, g->strt.hash[i]);
|
||||
|
||||
fprintf(f, "\"0\":{\"type\":\"userdata\",\"cat\":0,\"size\":0}\n"); // to avoid issues with trailing ,
|
||||
fprintf(f, "},\"roots\":{\n");
|
||||
fprintf(f, "\"mainthread\":");
|
||||
dumpref(f, obj2gco(g->mainthread));
|
||||
fprintf(f, ",\"registry\":");
|
||||
dumpref(f, gcvalue(&g->registry));
|
||||
|
||||
fprintf(f, "},\"stats\":{\n");
|
||||
|
||||
fprintf(f, "\"size\":%d,\n", int(g->totalbytes));
|
||||
|
||||
fprintf(f, "\"categories\":{\n");
|
||||
for (int i = 0; i < LUA_MEMORY_CATEGORIES; i++)
|
||||
{
|
||||
if (size_t bytes = g->memcatbytes[i])
|
||||
{
|
||||
if (categoryName)
|
||||
fprintf(f, "\"%d\":{\"name\":\"%s\", \"size\":%d},\n", i, categoryName(L, i), int(bytes));
|
||||
else
|
||||
fprintf(f, "\"%d\":{\"size\":%d},\n", i, int(bytes));
|
||||
}
|
||||
}
|
||||
fprintf(f, "\"none\":{}\n"); // to avoid issues with trailing ,
|
||||
fprintf(f, "}\n");
|
||||
fprintf(f, "}}\n");
|
||||
}
|
@ -17,7 +17,7 @@ static const luaL_Reg lualibs[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API void luaL_openlibs(lua_State* L)
|
||||
void luaL_openlibs(lua_State* L)
|
||||
{
|
||||
const luaL_Reg* lib = lualibs;
|
||||
for (; lib->func; lib++)
|
||||
@ -28,7 +28,7 @@ LUALIB_API void luaL_openlibs(lua_State* L)
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_sandbox(lua_State* L)
|
||||
void luaL_sandbox(lua_State* L)
|
||||
{
|
||||
// set all libraries to read-only
|
||||
lua_pushnil(L);
|
||||
@ -44,14 +44,14 @@ LUALIB_API void luaL_sandbox(lua_State* L)
|
||||
lua_pushliteral(L, "");
|
||||
lua_getmetatable(L, -1);
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 2);
|
||||
|
||||
// set globals to readonly and activate safeenv since the env is immutable
|
||||
lua_setreadonly(L, LUA_GLOBALSINDEX, true);
|
||||
lua_setsafeenv(L, LUA_GLOBALSINDEX, true);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_sandboxthread(lua_State* L)
|
||||
void luaL_sandboxthread(lua_State* L)
|
||||
{
|
||||
// create new global table that proxies reads to original table
|
||||
lua_newtable(L);
|
||||
@ -81,7 +81,7 @@ static void* l_alloc(lua_State* L, void* ud, void* ptr, size_t osize, size_t nsi
|
||||
return realloc(ptr, nsize);
|
||||
}
|
||||
|
||||
LUALIB_API lua_State* luaL_newstate(void)
|
||||
lua_State* luaL_newstate(void)
|
||||
{
|
||||
return lua_newstate(l_alloc, NULL);
|
||||
}
|
||||
|
@ -385,8 +385,7 @@ static int math_sign(lua_State* L)
|
||||
|
||||
static int math_round(lua_State* L)
|
||||
{
|
||||
double v = luaL_checknumber(L, 1);
|
||||
lua_pushnumber(L, round(v));
|
||||
lua_pushnumber(L, round(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -429,7 +428,7 @@ static const luaL_Reg mathlib[] = {
|
||||
/*
|
||||
** Open math library
|
||||
*/
|
||||
LUALIB_API int luaopen_math(lua_State* L)
|
||||
int luaopen_math(lua_State* L)
|
||||
{
|
||||
uint64_t seed = uintptr_t(L);
|
||||
seed ^= time(NULL);
|
||||
|
@ -33,11 +33,17 @@
|
||||
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : ms32)
|
||||
#endif
|
||||
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
static_assert(sizeof(TValue) == ABISWITCH(24, 24, 24), "size mismatch for value");
|
||||
static_assert(sizeof(LuaNode) == ABISWITCH(48, 48, 48), "size mismatch for table entry");
|
||||
#else
|
||||
static_assert(sizeof(TValue) == ABISWITCH(16, 16, 16), "size mismatch for value");
|
||||
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
|
||||
#endif
|
||||
|
||||
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
|
||||
static_assert(offsetof(Udata, data) == ABISWITCH(24, 16, 16), "size mismatch for userdata header");
|
||||
static_assert(sizeof(Table) == ABISWITCH(56, 36, 36), "size mismatch for table header");
|
||||
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
|
||||
|
||||
const size_t kSizeClasses = LUA_SIZECLASSES;
|
||||
const size_t kMaxSmallSize = 512;
|
||||
|
@ -18,12 +18,20 @@
|
||||
|
||||
inline bool luai_veceq(const float* a, const float* b)
|
||||
{
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
|
||||
#else
|
||||
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool luai_vecisnan(const float* a)
|
||||
{
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
return a[0] != a[0] || a[1] != a[1] || a[2] != a[2] || a[3] != a[3];
|
||||
#else
|
||||
return a[0] != a[0] || a[1] != a[1] || a[2] != a[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
LUAU_FASTMATH_BEGIN
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
|
||||
const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
|
||||
const TValue luaO_nilobject_ = {{NULL}, {0}, LUA_TNIL};
|
||||
|
||||
int luaO_log2(unsigned int x)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ typedef union
|
||||
typedef struct lua_TValue
|
||||
{
|
||||
Value value;
|
||||
int extra;
|
||||
int extra[LUA_EXTRA_SIZE];
|
||||
int tt;
|
||||
} TValue;
|
||||
|
||||
@ -105,7 +105,19 @@ typedef struct lua_TValue
|
||||
i_o->tt = LUA_TNUMBER; \
|
||||
}
|
||||
|
||||
#define setvvalue(obj, x, y, z) \
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
#define setvvalue(obj, x, y, z, w) \
|
||||
{ \
|
||||
TValue* i_o = (obj); \
|
||||
float* i_v = i_o->value.v; \
|
||||
i_v[0] = (x); \
|
||||
i_v[1] = (y); \
|
||||
i_v[2] = (z); \
|
||||
i_v[3] = (w); \
|
||||
i_o->tt = LUA_TVECTOR; \
|
||||
}
|
||||
#else
|
||||
#define setvvalue(obj, x, y, z, w) \
|
||||
{ \
|
||||
TValue* i_o = (obj); \
|
||||
float* i_v = i_o->value.v; \
|
||||
@ -114,6 +126,7 @@ typedef struct lua_TValue
|
||||
i_v[2] = (z); \
|
||||
i_o->tt = LUA_TVECTOR; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define setpvalue(obj, x) \
|
||||
{ \
|
||||
@ -364,7 +377,7 @@ typedef struct Closure
|
||||
typedef struct TKey
|
||||
{
|
||||
::Value value;
|
||||
int extra;
|
||||
int extra[LUA_EXTRA_SIZE];
|
||||
unsigned tt : 4;
|
||||
int next : 28; /* for chaining */
|
||||
} TKey;
|
||||
@ -381,7 +394,7 @@ typedef struct LuaNode
|
||||
LuaNode* n_ = (node); \
|
||||
const TValue* i_o = (obj); \
|
||||
n_->key.value = i_o->value; \
|
||||
n_->key.extra = i_o->extra; \
|
||||
memcpy(n_->key.extra, i_o->extra, sizeof(n_->key.extra)); \
|
||||
n_->key.tt = i_o->tt; \
|
||||
checkliveness(L->global, i_o); \
|
||||
}
|
||||
@ -392,7 +405,7 @@ typedef struct LuaNode
|
||||
TValue* i_o = (obj); \
|
||||
const LuaNode* n_ = (node); \
|
||||
i_o->value = n_->key.value; \
|
||||
i_o->extra = n_->key.extra; \
|
||||
memcpy(i_o->extra, n_->key.extra, sizeof(i_o->extra)); \
|
||||
i_o->tt = n_->key.tt; \
|
||||
checkliveness(L->global, i_o); \
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ static const luaL_Reg syslib[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_os(lua_State* L)
|
||||
int luaopen_os(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_OSLIBNAME, syslib);
|
||||
return 1;
|
||||
|
@ -1657,7 +1657,7 @@ static void createmetatable(lua_State* L)
|
||||
/*
|
||||
** Open string library
|
||||
*/
|
||||
LUALIB_API int luaopen_string(lua_State* L)
|
||||
int luaopen_string(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_STRLIBNAME, strlib);
|
||||
createmetatable(L);
|
||||
|
@ -31,18 +31,19 @@ LUAU_FASTFLAGVARIABLE(LuauArrayBoundary, false)
|
||||
#define MAXSIZE (1 << MAXBITS)
|
||||
|
||||
static_assert(offsetof(LuaNode, val) == 0, "Unexpected Node memory layout, pointer cast in gval2slot is incorrect");
|
||||
|
||||
// TKey is bitpacked for memory efficiency so we need to validate bit counts for worst case
|
||||
static_assert(TKey{{NULL}, 0, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
|
||||
static_assert(TKey{{NULL}, 0, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
|
||||
static_assert(TKey{{NULL}, 0, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
|
||||
static_assert(TKey{{NULL}, {0}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
|
||||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
|
||||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
|
||||
|
||||
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
||||
#define invalidateTMcache(t) t->flags = 0
|
||||
|
||||
// empty hash data points to dummynode so that we can always dereference it
|
||||
const LuaNode luaH_dummynode = {
|
||||
{{NULL}, 0, LUA_TNIL}, /* value */
|
||||
{{NULL}, 0, LUA_TNIL, 0} /* key */
|
||||
{{NULL}, {0}, LUA_TNIL}, /* value */
|
||||
{{NULL}, {0}, LUA_TNIL, 0} /* key */
|
||||
};
|
||||
|
||||
#define dummynode (&luaH_dummynode)
|
||||
@ -96,7 +97,7 @@ static LuaNode* hashnum(const Table* t, double n)
|
||||
|
||||
static LuaNode* hashvec(const Table* t, const float* v)
|
||||
{
|
||||
unsigned int i[3];
|
||||
unsigned int i[LUA_VECTOR_SIZE];
|
||||
memcpy(i, v, sizeof(i));
|
||||
|
||||
// convert -0 to 0 to make sure they hash to the same value
|
||||
@ -112,6 +113,12 @@ static LuaNode* hashvec(const Table* t, const float* v)
|
||||
// Optimized Spatial Hashing for Collision Detection of Deformable Objects
|
||||
unsigned int h = (i[0] * 73856093) ^ (i[1] * 19349663) ^ (i[2] * 83492791);
|
||||
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
i[3] = (i[3] == 0x8000000) ? 0 : i[3];
|
||||
i[3] ^= i[3] >> 17;
|
||||
h ^= i[3] * 39916801;
|
||||
#endif
|
||||
|
||||
return hashpow2(t, h);
|
||||
}
|
||||
|
||||
|
@ -527,7 +527,7 @@ static const luaL_Reg tab_funcs[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_table(lua_State* L)
|
||||
int luaopen_table(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_TABLIBNAME, tab_funcs);
|
||||
|
||||
|
@ -283,7 +283,7 @@ static const luaL_Reg funcs[] = {
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_utf8(lua_State* L)
|
||||
int luaopen_utf8(lua_State* L)
|
||||
{
|
||||
luaL_register(L, LUA_UTF8LIBNAME, funcs);
|
||||
|
||||
|
@ -601,7 +601,13 @@ static void luau_execute(lua_State* L)
|
||||
const char* name = getstr(tsvalue(kv));
|
||||
int ic = (name[0] | ' ') - 'x';
|
||||
|
||||
if (unsigned(ic) < 3 && name[1] == '\0')
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
// 'w' is before 'x' in ascii, so ic is -1 when indexing with 'w'
|
||||
if (ic == -1)
|
||||
ic = 3;
|
||||
#endif
|
||||
|
||||
if (unsigned(ic) < LUA_VECTOR_SIZE && name[1] == '\0')
|
||||
{
|
||||
setnvalue(ra, rb->value.v[ic]);
|
||||
VM_NEXT();
|
||||
@ -1526,7 +1532,7 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2]);
|
||||
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -1572,7 +1578,7 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2]);
|
||||
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -1618,21 +1624,21 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
float vc = cast_to(float, nvalue(rc));
|
||||
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc);
|
||||
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc, vb[3] * vc);
|
||||
VM_NEXT();
|
||||
}
|
||||
else if (ttisvector(rb) && ttisvector(rc))
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2]);
|
||||
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else if (ttisnumber(rb) && ttisvector(rc))
|
||||
{
|
||||
float vb = cast_to(float, nvalue(rb));
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb * vc[0], vb * vc[1], vb * vc[2]);
|
||||
setvvalue(ra, vb * vc[0], vb * vc[1], vb * vc[2], vb * vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -1679,21 +1685,21 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
float vc = cast_to(float, nvalue(rc));
|
||||
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc);
|
||||
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc, vb[3] / vc);
|
||||
VM_NEXT();
|
||||
}
|
||||
else if (ttisvector(rb) && ttisvector(rc))
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2]);
|
||||
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else if (ttisnumber(rb) && ttisvector(rc))
|
||||
{
|
||||
float vb = cast_to(float, nvalue(rb));
|
||||
const float* vc = rc->value.v;
|
||||
setvvalue(ra, vb / vc[0], vb / vc[1], vb / vc[2]);
|
||||
setvvalue(ra, vb / vc[0], vb / vc[1], vb / vc[2], vb / vc[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -1826,7 +1832,7 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
float vc = cast_to(float, nvalue(kv));
|
||||
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc);
|
||||
setvvalue(ra, vb[0] * vc, vb[1] * vc, vb[2] * vc, vb[3] * vc);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -1872,7 +1878,7 @@ static void luau_execute(lua_State* L)
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
float vc = cast_to(float, nvalue(kv));
|
||||
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc);
|
||||
setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc, vb[3] / vc);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
@ -2037,7 +2043,7 @@ static void luau_execute(lua_State* L)
|
||||
else if (ttisvector(rb))
|
||||
{
|
||||
const float* vb = rb->value.v;
|
||||
setvvalue(ra, -vb[0], -vb[1], -vb[2]);
|
||||
setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lbytecode.h"
|
||||
#include "lapi.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -162,9 +163,8 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||
size_t GCthreshold = L->global->GCthreshold;
|
||||
L->global->GCthreshold = SIZE_MAX;
|
||||
|
||||
// env is 0 for current environment and a stack relative index otherwise
|
||||
LUAU_ASSERT(env <= 0 && L->top - L->base >= -env);
|
||||
Table* envt = (env == 0) ? hvalue(gt(L)) : hvalue(L->top + env);
|
||||
// env is 0 for current environment and a stack index otherwise
|
||||
Table* envt = (env == 0) ? hvalue(gt(L)) : hvalue(luaA_toobject(L, env));
|
||||
|
||||
TString* source = luaS_new(L, chunkname);
|
||||
|
||||
|
@ -401,19 +401,19 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
|
||||
switch (op)
|
||||
{
|
||||
case TM_ADD:
|
||||
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2]);
|
||||
setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]);
|
||||
return;
|
||||
case TM_SUB:
|
||||
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2]);
|
||||
setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]);
|
||||
return;
|
||||
case TM_MUL:
|
||||
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2]);
|
||||
setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2]);
|
||||
setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]);
|
||||
return;
|
||||
case TM_UNM:
|
||||
setvvalue(ra, -vb[0], -vb[1], -vb[2]);
|
||||
setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
@ -430,10 +430,10 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
|
||||
switch (op)
|
||||
{
|
||||
case TM_MUL:
|
||||
setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc);
|
||||
setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc, vb[3] * nc);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc);
|
||||
setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
@ -451,10 +451,10 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
|
||||
switch (op)
|
||||
{
|
||||
case TM_MUL:
|
||||
setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2]);
|
||||
setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2], nb * vc[3]);
|
||||
return;
|
||||
case TM_DIV:
|
||||
setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2]);
|
||||
setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
|
@ -88,7 +88,7 @@ for i=1,N do
|
||||
local y=ymin+(j-1)*dy
|
||||
S = S + level(x,y)
|
||||
end
|
||||
-- if i % 10 == 0 then print(collectgarbage"count") end
|
||||
-- if i % 10 == 0 then print(collectgarbage("count")) end
|
||||
end
|
||||
print(S)
|
||||
|
||||
|
@ -88,7 +88,7 @@ for i=1,N do
|
||||
local y=ymin+(j-1)*dy
|
||||
S = S + level(x,y)
|
||||
end
|
||||
-- if i % 10 == 0 then print(collectgarbage"count") end
|
||||
-- if i % 10 == 0 then print(collectgarbage("count")) end
|
||||
end
|
||||
print(S)
|
||||
|
||||
|
@ -275,7 +275,7 @@ local function memory(s)
|
||||
local t=os.clock()
|
||||
--local dt=string.format("%f",t-t0)
|
||||
local dt=t-t0
|
||||
--io.stdout:write(s,"\t",dt," sec\t",t," sec\t",math.floor(collectgarbage"count"/1024),"M\n")
|
||||
--io.stdout:write(s,"\t",dt," sec\t",t," sec\t",math.floor(collectgarbage("count")/1024),"M\n")
|
||||
t0=t
|
||||
end
|
||||
|
||||
@ -286,7 +286,7 @@ local function do_(f,s)
|
||||
end
|
||||
|
||||
local function julia(l,a,b)
|
||||
memory"begin"
|
||||
memory("begin")
|
||||
cx=a cy=b
|
||||
root=newcell()
|
||||
exterior=newcell() exterior.color=white
|
||||
@ -297,14 +297,14 @@ memory"begin"
|
||||
do_(update,"update")
|
||||
repeat
|
||||
N=0 color(root,Rxmin,Rxmax,Rymin,Rymax) --print("color",N)
|
||||
until N==0 memory"color"
|
||||
until N==0 memory("color")
|
||||
repeat
|
||||
N=0 prewhite(root,Rxmin,Rxmax,Rymin,Rymax) --print("prewhite",N)
|
||||
until N==0 memory"prewhite"
|
||||
until N==0 memory("prewhite")
|
||||
do_(recolor,"recolor")
|
||||
do_(colorup,"colorup") --print("colorup",N)
|
||||
local g,b=do_(area,"area") --print("area",g,b,g+b)
|
||||
show(i) memory"output"
|
||||
show(i) memory("output")
|
||||
--print("edges",nE)
|
||||
end
|
||||
end
|
||||
|
@ -23,9 +23,11 @@ const bool kFuzzCompiler = true;
|
||||
const bool kFuzzLinter = true;
|
||||
const bool kFuzzTypeck = true;
|
||||
const bool kFuzzVM = true;
|
||||
const bool kFuzzTypes = true;
|
||||
const bool kFuzzTranspile = true;
|
||||
|
||||
// Should we generate type annotations?
|
||||
const bool kFuzzTypes = true;
|
||||
|
||||
static_assert(!(kFuzzVM && !kFuzzCompiler), "VM requires the compiler!");
|
||||
|
||||
std::string protoprint(const luau::StatBlock& stat, bool types);
|
||||
|
@ -78,3 +78,26 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "overloaded_fn")
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
TEST_SUITE_BEGIN("AstQuery");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "last_argument_function_call_type")
|
||||
{
|
||||
ScopedFastFlag luauTailArgumentTypeInfo{"LuauTailArgumentTypeInfo", true};
|
||||
|
||||
check(R"(
|
||||
local function foo() return 2 end
|
||||
local function bar(a: number) return -a end
|
||||
bar(foo())
|
||||
)");
|
||||
|
||||
auto oty = findTypeAtPosition(Position(3, 7));
|
||||
REQUIRE(oty);
|
||||
CHECK_EQ("number", toString(*oty));
|
||||
|
||||
auto expectedOty = findExpectedTypeAtPosition(Position(3, 7));
|
||||
REQUIRE(expectedOty);
|
||||
CHECK_EQ("number", toString(*expectedOty));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -1935,6 +1935,39 @@ return target(b@1
|
||||
CHECK(ac.entryMap["bar2"].typeCorrect == TypeCorrectKind::None);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "function_in_assignment_has_parentheses")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
ScopedFastFlag luauAutocompletePreferToCallFunctions("LuauAutocompletePreferToCallFunctions", true);
|
||||
|
||||
check(R"(
|
||||
local function bar(a: number) return -a end
|
||||
local abc = b@1
|
||||
)");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
CHECK(ac.entryMap.count("bar"));
|
||||
CHECK(ac.entryMap["bar"].parens == ParenthesesRecommendation::CursorInside);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "function_result_passed_to_function_has_parentheses")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
ScopedFastFlag luauAutocompletePreferToCallFunctions("LuauAutocompletePreferToCallFunctions", true);
|
||||
|
||||
check(R"(
|
||||
local function foo() return 1 end
|
||||
local function bar(a: number) return -a end
|
||||
local abc = bar(@1)
|
||||
)");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
CHECK(ac.entryMap.count("foo"));
|
||||
CHECK(ac.entryMap["foo"].parens == ParenthesesRecommendation::CursorAfter);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "type_correct_sealed_table")
|
||||
{
|
||||
check(R"(
|
||||
@ -2210,8 +2243,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocompleteSource")
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocompleteSource_require")
|
||||
{
|
||||
ScopedFastFlag luauResolveModuleNameWithoutACurrentModule("LuauResolveModuleNameWithoutACurrentModule", true);
|
||||
|
||||
std::string_view source = R"(
|
||||
local a = require(w -- Line 1
|
||||
-- | Column 27
|
||||
@ -2287,8 +2318,6 @@ until
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "if_then_else_elseif_completions")
|
||||
{
|
||||
ScopedFastFlag sff{"ElseElseIfCompletionImprovements", true};
|
||||
|
||||
check(R"(
|
||||
local elsewhere = false
|
||||
|
||||
@ -2585,9 +2614,6 @@ a = if temp then even elseif true then temp else e@9
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_explicit_type_pack")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
check(R"(
|
||||
type A<T...> = () -> T...
|
||||
local a: A<(number, s@1>
|
||||
|
@ -1057,6 +1057,18 @@ RETURN R0 1
|
||||
CHECK_EQ("\n" + compileFunction0("return if false then 10 else 20"), R"(
|
||||
LOADN R0 20
|
||||
RETURN R0 1
|
||||
)");
|
||||
|
||||
// codegen for a true constant condition with non-constant expressions
|
||||
CHECK_EQ("\n" + compileFunction0("return if true then {} else error()"), R"(
|
||||
NEWTABLE R0 0 0
|
||||
RETURN R0 1
|
||||
)");
|
||||
|
||||
// codegen for a false constant condition with non-constant expressions
|
||||
CHECK_EQ("\n" + compileFunction0("return if false then error() else {}"), R"(
|
||||
NEWTABLE R0 0 0
|
||||
RETURN R0 1
|
||||
)");
|
||||
|
||||
// codegen for a false (in this case 'nil') constant condition
|
||||
@ -2360,6 +2372,58 @@ Foo:Bar(
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("DebugLineInfoCallChain")
|
||||
{
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Lines);
|
||||
Luau::compileOrThrow(bcb, R"(
|
||||
local Foo = ...
|
||||
|
||||
Foo
|
||||
:Bar(1)
|
||||
:Baz(2)
|
||||
.Qux(3)
|
||||
)");
|
||||
|
||||
CHECK_EQ("\n" + bcb.dumpFunction(0), R"(
|
||||
2: GETVARARGS R0 1
|
||||
5: LOADN R4 1
|
||||
5: NAMECALL R2 R0 K0
|
||||
5: CALL R2 2 1
|
||||
6: LOADN R4 2
|
||||
6: NAMECALL R2 R2 K1
|
||||
6: CALL R2 2 1
|
||||
7: GETTABLEKS R1 R2 K2
|
||||
7: LOADN R2 3
|
||||
7: CALL R1 1 0
|
||||
8: RETURN R0 0
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("DebugLineInfoFastCall")
|
||||
{
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Lines);
|
||||
Luau::compileOrThrow(bcb, R"(
|
||||
local Foo, Bar = ...
|
||||
|
||||
return
|
||||
math.max(
|
||||
Foo,
|
||||
Bar)
|
||||
)");
|
||||
|
||||
CHECK_EQ("\n" + bcb.dumpFunction(0), R"(
|
||||
2: GETVARARGS R0 2
|
||||
5: FASTCALL2 18 R0 R1 +5
|
||||
5: MOVE R3 R0
|
||||
5: MOVE R4 R1
|
||||
5: GETIMPORT R2 2
|
||||
5: CALL R2 2 -1
|
||||
5: RETURN R2 -1
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("DebugSource")
|
||||
{
|
||||
const char* source = R"(
|
||||
@ -3742,4 +3806,108 @@ RETURN R0 0
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("ConstantsNoFolding")
|
||||
{
|
||||
const char* source = "return nil, true, 42, 'hello'";
|
||||
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code);
|
||||
Luau::CompileOptions options;
|
||||
options.optimizationLevel = 0;
|
||||
Luau::compileOrThrow(bcb, source, options);
|
||||
|
||||
CHECK_EQ("\n" + bcb.dumpFunction(0), R"(
|
||||
LOADNIL R0
|
||||
LOADB R1 1
|
||||
LOADK R2 K0
|
||||
LOADK R3 K1
|
||||
RETURN R0 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("VectorFastCall")
|
||||
{
|
||||
const char* source = "return Vector3.new(1, 2, 3)";
|
||||
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code);
|
||||
Luau::CompileOptions options;
|
||||
options.vectorLib = "Vector3";
|
||||
options.vectorCtor = "new";
|
||||
Luau::compileOrThrow(bcb, source, options);
|
||||
|
||||
CHECK_EQ("\n" + bcb.dumpFunction(0), R"(
|
||||
LOADN R1 1
|
||||
LOADN R2 2
|
||||
LOADN R3 3
|
||||
FASTCALL 54 +2
|
||||
GETIMPORT R0 2
|
||||
CALL R0 3 -1
|
||||
RETURN R0 -1
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("TypeAssertion")
|
||||
{
|
||||
// validate that type assertions work with the compiler and that the code inside type assertion isn't evaluated
|
||||
CHECK_EQ("\n" + compileFunction0(R"(
|
||||
print(foo() :: typeof(error("compile time")))
|
||||
)"),
|
||||
R"(
|
||||
GETIMPORT R0 1
|
||||
GETIMPORT R1 3
|
||||
CALL R1 0 1
|
||||
CALL R0 1 0
|
||||
RETURN R0 0
|
||||
)");
|
||||
|
||||
// note that above, foo() is treated as single-arg function; removing type assertion changes the bytecode
|
||||
CHECK_EQ("\n" + compileFunction0(R"(
|
||||
print(foo())
|
||||
)"),
|
||||
R"(
|
||||
GETIMPORT R0 1
|
||||
GETIMPORT R1 3
|
||||
CALL R1 0 -1
|
||||
CALL R0 -1 0
|
||||
RETURN R0 0
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("Arithmetics")
|
||||
{
|
||||
// basic arithmetics codegen with non-constants
|
||||
CHECK_EQ("\n" + compileFunction0(R"(
|
||||
local a, b = ...
|
||||
return a + b, a - b, a / b, a * b, a % b, a ^ b
|
||||
)"),
|
||||
R"(
|
||||
GETVARARGS R0 2
|
||||
ADD R2 R0 R1
|
||||
SUB R3 R0 R1
|
||||
DIV R4 R0 R1
|
||||
MUL R5 R0 R1
|
||||
MOD R6 R0 R1
|
||||
POW R7 R0 R1
|
||||
RETURN R2 6
|
||||
)");
|
||||
|
||||
// basic arithmetics codegen with constants on the right side
|
||||
// note that we don't simplify these expressions as we don't know the type of a
|
||||
CHECK_EQ("\n" + compileFunction0(R"(
|
||||
local a = ...
|
||||
return a + 1, a - 1, a / 1, a * 1, a % 1, a ^ 1
|
||||
)"),
|
||||
R"(
|
||||
GETVARARGS R0 1
|
||||
ADDK R1 R0 K0
|
||||
SUBK R2 R0 K0
|
||||
DIVK R3 R0 K0
|
||||
MULK R4 R0 K0
|
||||
MODK R5 R0 K0
|
||||
POWK R6 R0 K0
|
||||
RETURN R1 6
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -67,44 +67,42 @@ static int lua_vector(lua_State* L)
|
||||
double y = luaL_checknumber(L, 2);
|
||||
double z = luaL_checknumber(L, 3);
|
||||
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
double w = luaL_optnumber(L, 4, 0.0);
|
||||
lua_pushvector(L, float(x), float(y), float(z), float(w));
|
||||
#else
|
||||
lua_pushvector(L, float(x), float(y), float(z));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_vector_dot(lua_State* L)
|
||||
{
|
||||
const float* a = lua_tovector(L, 1);
|
||||
const float* b = lua_tovector(L, 2);
|
||||
const float* a = luaL_checkvector(L, 1);
|
||||
const float* b = luaL_checkvector(L, 2);
|
||||
|
||||
if (a && b)
|
||||
{
|
||||
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw std::runtime_error("invalid arguments to vector:Dot");
|
||||
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_vector_index(lua_State* L)
|
||||
{
|
||||
const float* v = luaL_checkvector(L, 1);
|
||||
const char* name = luaL_checkstring(L, 2);
|
||||
|
||||
if (const float* v = lua_tovector(L, 1))
|
||||
if (strcmp(name, "Magnitude") == 0)
|
||||
{
|
||||
if (strcmp(name, "Magnitude") == 0)
|
||||
{
|
||||
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "Dot") == 0)
|
||||
{
|
||||
lua_pushcfunction(L, lua_vector_dot, "Dot");
|
||||
return 1;
|
||||
}
|
||||
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw std::runtime_error(Luau::format("%s is not a valid member of vector", name));
|
||||
if (strcmp(name, "Dot") == 0)
|
||||
{
|
||||
lua_pushcfunction(L, lua_vector_dot, "Dot");
|
||||
return 1;
|
||||
}
|
||||
|
||||
luaL_error(L, "%s is not a valid member of vector", name);
|
||||
}
|
||||
|
||||
static int lua_vector_namecall(lua_State* L)
|
||||
@ -115,7 +113,7 @@ static int lua_vector_namecall(lua_State* L)
|
||||
return lua_vector_dot(L);
|
||||
}
|
||||
|
||||
throw std::runtime_error(Luau::format("%s is not a valid method of vector", luaL_checkstring(L, 1)));
|
||||
luaL_error(L, "%s is not a valid method of vector", luaL_checkstring(L, 1));
|
||||
}
|
||||
|
||||
int lua_silence(lua_State* L)
|
||||
@ -373,11 +371,17 @@ TEST_CASE("Pack")
|
||||
|
||||
TEST_CASE("Vector")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauIfElseExpressionBaseSupport", true};
|
||||
|
||||
runConformance("vector.lua", [](lua_State* L) {
|
||||
lua_pushcfunction(L, lua_vector, "vector");
|
||||
lua_setglobal(L, "vector");
|
||||
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
#else
|
||||
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
luaL_newmetatable(L, "vector");
|
||||
|
||||
lua_pushstring(L, "__index");
|
||||
@ -504,6 +508,9 @@ TEST_CASE("Debugger")
|
||||
cb->debugbreak = [](lua_State* L, lua_Debug* ar) {
|
||||
breakhits++;
|
||||
|
||||
// make sure we can trace the stack for every breakpoint we hit
|
||||
lua_debugtrace(L);
|
||||
|
||||
// for every breakpoint, we break on the first invocation and continue on second
|
||||
// this allows us to easily step off breakpoints
|
||||
// (real implementaiton may require singlestepping)
|
||||
@ -524,7 +531,7 @@ TEST_CASE("Debugger")
|
||||
L,
|
||||
[](lua_State* L) -> int {
|
||||
int line = luaL_checkinteger(L, 1);
|
||||
bool enabled = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : true;
|
||||
bool enabled = luaL_optboolean(L, 2, true);
|
||||
|
||||
lua_Debug ar = {};
|
||||
lua_getinfo(L, 1, "f", &ar);
|
||||
@ -699,21 +706,52 @@ TEST_CASE("ApiFunctionCalls")
|
||||
StateRef globalState = runConformance("apicalls.lua");
|
||||
lua_State* L = globalState.get();
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "add");
|
||||
lua_pushnumber(L, 40);
|
||||
lua_pushnumber(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
CHECK(lua_isnumber(L, -1));
|
||||
CHECK(lua_tonumber(L, -1) == 42);
|
||||
lua_pop(L, 1);
|
||||
// lua_call
|
||||
{
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "add");
|
||||
lua_pushnumber(L, 40);
|
||||
lua_pushnumber(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
CHECK(lua_isnumber(L, -1));
|
||||
CHECK(lua_tonumber(L, -1) == 42);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "add");
|
||||
lua_pushnumber(L, 40);
|
||||
lua_pushnumber(L, 2);
|
||||
lua_pcall(L, 2, 1, 0);
|
||||
CHECK(lua_isnumber(L, -1));
|
||||
CHECK(lua_tonumber(L, -1) == 42);
|
||||
lua_pop(L, 1);
|
||||
// lua_pcall
|
||||
{
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "add");
|
||||
lua_pushnumber(L, 40);
|
||||
lua_pushnumber(L, 2);
|
||||
lua_pcall(L, 2, 1, 0);
|
||||
CHECK(lua_isnumber(L, -1));
|
||||
CHECK(lua_tonumber(L, -1) == 42);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// lua_equal with a sleeping thread wake up
|
||||
{
|
||||
ScopedFastFlag luauActivateBeforeExec("LuauActivateBeforeExec", true);
|
||||
|
||||
lua_State* L2 = lua_newthread(L);
|
||||
|
||||
lua_getfield(L2, LUA_GLOBALSINDEX, "create_with_tm");
|
||||
lua_pushnumber(L2, 42);
|
||||
lua_pcall(L2, 1, 1, 0);
|
||||
|
||||
lua_getfield(L2, LUA_GLOBALSINDEX, "create_with_tm");
|
||||
lua_pushnumber(L2, 42);
|
||||
lua_pcall(L2, 1, 1, 0);
|
||||
|
||||
// Reset GC
|
||||
lua_gc(L2, LUA_GCCOLLECT, 0);
|
||||
|
||||
// Try to mark 'L2' as sleeping
|
||||
// Can't control GC precisely, even in tests
|
||||
lua_gc(L2, LUA_GCSTEP, 8);
|
||||
|
||||
CHECK(lua_equal(L2, -1, -2) == 1);
|
||||
lua_pop(L2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
static bool endsWith(const std::string& str, const std::string& suffix)
|
||||
@ -727,8 +765,6 @@ static bool endsWith(const std::string& str, const std::string& suffix)
|
||||
#if !LUA_USE_LONGJMP
|
||||
TEST_CASE("ExceptionObject")
|
||||
{
|
||||
ScopedFastFlag sff("LuauExceptionMessageFix", true);
|
||||
|
||||
struct ExceptionResult
|
||||
{
|
||||
bool exceptionGenerated;
|
||||
|
@ -19,19 +19,6 @@ static const char* mainModuleName = "MainModule";
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
std::optional<ModuleName> TestFileResolver::fromAstFragment(AstExpr* expr) const
|
||||
{
|
||||
auto g = expr->as<AstExprGlobal>();
|
||||
if (!g)
|
||||
return std::nullopt;
|
||||
|
||||
std::string_view value = g->name.value;
|
||||
if (value == "game" || value == "Game" || value == "workspace" || value == "Workspace" || value == "script" || value == "Script")
|
||||
return ModuleName(value);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ModuleInfo> TestFileResolver::resolveModule(const ModuleInfo* context, AstExpr* expr)
|
||||
{
|
||||
if (AstExprGlobal* g = expr->as<AstExprGlobal>())
|
||||
@ -81,24 +68,6 @@ std::optional<ModuleInfo> TestFileResolver::resolveModule(const ModuleInfo* cont
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ModuleName TestFileResolver::concat(const ModuleName& lhs, std::string_view rhs) const
|
||||
{
|
||||
return lhs + "/" + ModuleName(rhs);
|
||||
}
|
||||
|
||||
std::optional<ModuleName> TestFileResolver::getParentModuleName(const ModuleName& name) const
|
||||
{
|
||||
std::string_view view = name;
|
||||
const size_t lastSeparatorIndex = view.find_last_of('/');
|
||||
|
||||
if (lastSeparatorIndex != std::string_view::npos)
|
||||
{
|
||||
return ModuleName(view.substr(0, lastSeparatorIndex));
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string TestFileResolver::getHumanReadableModuleName(const ModuleName& name) const
|
||||
{
|
||||
return name;
|
||||
@ -324,6 +293,13 @@ std::optional<TypeId> Fixture::findTypeAtPosition(Position position)
|
||||
return Luau::findTypeAtPosition(*module, *sourceModule, position);
|
||||
}
|
||||
|
||||
std::optional<TypeId> Fixture::findExpectedTypeAtPosition(Position position)
|
||||
{
|
||||
ModulePtr module = getMainModule();
|
||||
SourceModule* sourceModule = getMainSourceModule();
|
||||
return Luau::findExpectedTypeAtPosition(*module, *sourceModule, position);
|
||||
}
|
||||
|
||||
TypeId Fixture::requireTypeAtPosition(Position position)
|
||||
{
|
||||
auto ty = findTypeAtPosition(position);
|
||||
|
@ -64,12 +64,8 @@ struct TestFileResolver
|
||||
return SourceCode{it->second, sourceType};
|
||||
}
|
||||
|
||||
std::optional<ModuleName> fromAstFragment(AstExpr* expr) const override;
|
||||
std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* expr) override;
|
||||
|
||||
ModuleName concat(const ModuleName& lhs, std::string_view rhs) const override;
|
||||
std::optional<ModuleName> getParentModuleName(const ModuleName& name) const override;
|
||||
|
||||
std::string getHumanReadableModuleName(const ModuleName& name) const override;
|
||||
|
||||
std::optional<std::string> getEnvironmentForModule(const ModuleName& name) const override;
|
||||
@ -126,6 +122,7 @@ struct Fixture
|
||||
|
||||
std::optional<TypeId> findTypeAtPosition(Position position);
|
||||
TypeId requireTypeAtPosition(Position position);
|
||||
std::optional<TypeId> findExpectedTypeAtPosition(Position position);
|
||||
|
||||
std::optional<TypeId> lookupType(const std::string& name);
|
||||
std::optional<TypeId> lookupImportedType(const std::string& moduleAlias, const std::string& name);
|
||||
|
@ -46,18 +46,6 @@ NaiveModuleResolver naiveModuleResolver;
|
||||
|
||||
struct NaiveFileResolver : NullFileResolver
|
||||
{
|
||||
std::optional<ModuleName> fromAstFragment(AstExpr* expr) const override
|
||||
{
|
||||
AstExprGlobal* g = expr->as<AstExprGlobal>();
|
||||
if (g && g->name == "Modules")
|
||||
return "Modules";
|
||||
|
||||
if (g && g->name == "game")
|
||||
return "game";
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* expr) override
|
||||
{
|
||||
if (AstExprGlobal* g = expr->as<AstExprGlobal>())
|
||||
@ -86,11 +74,6 @@ struct NaiveFileResolver : NullFileResolver
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ModuleName concat(const ModuleName& lhs, std::string_view rhs) const override
|
||||
{
|
||||
return lhs + "/" + ModuleName(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -1469,6 +1469,22 @@ _ = true and true or false -- no warning since this is is a common pattern used
|
||||
CHECK_EQ(result.warnings[6].location.begin.line + 1, 19);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "DuplicateConditionsExpr")
|
||||
{
|
||||
LintResult result = lint(R"(
|
||||
local correct, opaque = ...
|
||||
|
||||
if correct({a = 1, b = 2 * (-2), c = opaque.path['with']("calls")}) then
|
||||
elseif correct({a = 1, b = 2 * (-2), c = opaque.path['with']("calls")}) then
|
||||
elseif correct({a = 1, b = 2 * (-2), c = opaque.path['with']("calls", false)}) then
|
||||
end
|
||||
)");
|
||||
|
||||
REQUIRE_EQ(result.warnings.size(), 1);
|
||||
CHECK_EQ(result.warnings[0].text, "Condition has already been checked on line 4");
|
||||
CHECK_EQ(result.warnings[0].location.begin.line + 1, 5);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "DuplicateLocal")
|
||||
{
|
||||
LintResult result = lint(R"(
|
||||
|
@ -44,9 +44,10 @@ TEST_CASE_FIXTURE(Fixture, "dont_clone_persistent_primitive")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
// numberType is persistent. We leave it as-is.
|
||||
TypeId newNumber = clone(typeChecker.numberType, dest, seenTypes, seenTypePacks);
|
||||
TypeId newNumber = clone(typeChecker.numberType, dest, seenTypes, seenTypePacks, cloneState);
|
||||
CHECK_EQ(newNumber, typeChecker.numberType);
|
||||
}
|
||||
|
||||
@ -56,12 +57,13 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_non_persistent_primitive")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
// Create a new number type that isn't persistent
|
||||
unfreeze(typeChecker.globalTypes);
|
||||
TypeId oldNumber = typeChecker.globalTypes.addType(PrimitiveTypeVar{PrimitiveTypeVar::Number});
|
||||
freeze(typeChecker.globalTypes);
|
||||
TypeId newNumber = clone(oldNumber, dest, seenTypes, seenTypePacks);
|
||||
TypeId newNumber = clone(oldNumber, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
CHECK_NE(newNumber, oldNumber);
|
||||
CHECK_EQ(*oldNumber, *newNumber);
|
||||
@ -89,9 +91,10 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
TypeArena dest;
|
||||
TypeId counterCopy = clone(counterType, dest, seenTypes, seenTypePacks);
|
||||
TypeId counterCopy = clone(counterType, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
TableTypeVar* ttv = getMutable<TableTypeVar>(counterCopy);
|
||||
REQUIRE(ttv != nullptr);
|
||||
@ -142,11 +145,12 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_union")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
unfreeze(typeChecker.globalTypes);
|
||||
TypeId oldUnion = typeChecker.globalTypes.addType(UnionTypeVar{{typeChecker.numberType, typeChecker.stringType}});
|
||||
freeze(typeChecker.globalTypes);
|
||||
TypeId newUnion = clone(oldUnion, dest, seenTypes, seenTypePacks);
|
||||
TypeId newUnion = clone(oldUnion, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
CHECK_NE(newUnion, oldUnion);
|
||||
CHECK_EQ("number | string", toString(newUnion));
|
||||
@ -159,11 +163,12 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_intersection")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
unfreeze(typeChecker.globalTypes);
|
||||
TypeId oldIntersection = typeChecker.globalTypes.addType(IntersectionTypeVar{{typeChecker.numberType, typeChecker.stringType}});
|
||||
freeze(typeChecker.globalTypes);
|
||||
TypeId newIntersection = clone(oldIntersection, dest, seenTypes, seenTypePacks);
|
||||
TypeId newIntersection = clone(oldIntersection, dest, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
CHECK_NE(newIntersection, oldIntersection);
|
||||
CHECK_EQ("number & string", toString(newIntersection));
|
||||
@ -188,8 +193,9 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
TypeId cloned = clone(&exampleClass, dest, seenTypes, seenTypePacks);
|
||||
TypeId cloned = clone(&exampleClass, dest, seenTypes, seenTypePacks, cloneState);
|
||||
const ClassTypeVar* ctv = get<ClassTypeVar>(cloned);
|
||||
REQUIRE(ctv != nullptr);
|
||||
|
||||
@ -211,16 +217,16 @@ TEST_CASE_FIXTURE(Fixture, "clone_sanitize_free_types")
|
||||
TypeArena dest;
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
bool encounteredFreeType = false;
|
||||
TypeId clonedTy = clone(&freeTy, dest, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
TypeId clonedTy = clone(&freeTy, dest, seenTypes, seenTypePacks, cloneState);
|
||||
CHECK_EQ("any", toString(clonedTy));
|
||||
CHECK(encounteredFreeType);
|
||||
CHECK(cloneState.encounteredFreeType);
|
||||
|
||||
encounteredFreeType = false;
|
||||
TypePackId clonedTp = clone(&freeTp, dest, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
cloneState = {};
|
||||
TypePackId clonedTp = clone(&freeTp, dest, seenTypes, seenTypePacks, cloneState);
|
||||
CHECK_EQ("...any", toString(clonedTp));
|
||||
CHECK(encounteredFreeType);
|
||||
CHECK(cloneState.encounteredFreeType);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "clone_seal_free_tables")
|
||||
@ -232,12 +238,12 @@ TEST_CASE_FIXTURE(Fixture, "clone_seal_free_tables")
|
||||
TypeArena dest;
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
bool encounteredFreeType = false;
|
||||
TypeId cloned = clone(&tableTy, dest, seenTypes, seenTypePacks, &encounteredFreeType);
|
||||
TypeId cloned = clone(&tableTy, dest, seenTypes, seenTypePacks, cloneState);
|
||||
const TableTypeVar* clonedTtv = get<TableTypeVar>(cloned);
|
||||
CHECK_EQ(clonedTtv->state, TableState::Sealed);
|
||||
CHECK(encounteredFreeType);
|
||||
CHECK(cloneState.encounteredFreeType);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "clone_self_property")
|
||||
@ -267,4 +273,34 @@ TEST_CASE_FIXTURE(Fixture, "clone_self_property")
|
||||
"dot or pass 1 extra nil to suppress this warning");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "clone_recursion_limit")
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_NOOPT)
|
||||
int limit = 250;
|
||||
#else
|
||||
int limit = 500;
|
||||
#endif
|
||||
ScopedFastInt luauTypeCloneRecursionLimit{"LuauTypeCloneRecursionLimit", limit};
|
||||
|
||||
TypeArena src;
|
||||
|
||||
TypeId table = src.addType(TableTypeVar{});
|
||||
TypeId nested = table;
|
||||
|
||||
for (unsigned i = 0; i < limit + 100; i++)
|
||||
{
|
||||
TableTypeVar* ttv = getMutable<TableTypeVar>(nested);
|
||||
|
||||
ttv->props["a"].type = src.addType(TableTypeVar{});
|
||||
nested = ttv->props["a"].type;
|
||||
}
|
||||
|
||||
TypeArena dest;
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
||||
CHECK_THROWS_AS(clone(table, dest, seenTypes, seenTypePacks, cloneState), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -2518,8 +2518,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
|
||||
{
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
AstStat* stat = parse(R"(
|
||||
type Packed<T...> = () -> T...
|
||||
|
||||
|
366
tests/ToDot.test.cpp
Normal file
366
tests/ToDot.test.cpp
Normal file
@ -0,0 +1,366 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/ToDot.h"
|
||||
|
||||
#include "Fixture.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
struct ToDotClassFixture : Fixture
|
||||
{
|
||||
ToDotClassFixture()
|
||||
{
|
||||
TypeArena& arena = typeChecker.globalTypes;
|
||||
|
||||
unfreeze(arena);
|
||||
|
||||
TypeId baseClassMetaType = arena.addType(TableTypeVar{});
|
||||
|
||||
TypeId baseClassInstanceType = arena.addType(ClassTypeVar{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}});
|
||||
getMutable<ClassTypeVar>(baseClassInstanceType)->props = {
|
||||
{"BaseField", {typeChecker.numberType}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||
|
||||
TypeId childClassInstanceType = arena.addType(ClassTypeVar{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}});
|
||||
getMutable<ClassTypeVar>(childClassInstanceType)->props = {
|
||||
{"ChildField", {typeChecker.stringType}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
||||
|
||||
freeze(arena);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE_BEGIN("ToDot");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "primitive")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a: nil
|
||||
local b: number
|
||||
local c: any
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK_NE("nil", toDot(requireType("a")));
|
||||
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="number"];
|
||||
})",
|
||||
toDot(requireType("b")));
|
||||
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="any"];
|
||||
})",
|
||||
toDot(requireType("c")));
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
opts.duplicatePrimitives = false;
|
||||
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="PrimitiveTypeVar number"];
|
||||
})",
|
||||
toDot(requireType("b"), opts));
|
||||
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="AnyTypeVar 1"];
|
||||
})",
|
||||
toDot(requireType("c"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "bound")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a = 444
|
||||
local b = a
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
std::optional<TypeId> ty = getType("b");
|
||||
REQUIRE(bool(ty));
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="BoundTypeVar 1"];
|
||||
n1 -> n2;
|
||||
n2 [label="number"];
|
||||
})",
|
||||
toDot(*ty, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function")
|
||||
{
|
||||
ScopedFastFlag luauQuantifyInPlace2{"LuauQuantifyInPlace2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(a, ...: string) return a end
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="FunctionTypeVar 1"];
|
||||
n1 -> n2 [label="arg"];
|
||||
n2 [label="TypePack 2"];
|
||||
n2 -> n3;
|
||||
n3 [label="GenericTypeVar 3"];
|
||||
n2 -> n4 [label="tail"];
|
||||
n4 [label="VariadicTypePack 4"];
|
||||
n4 -> n5;
|
||||
n5 [label="string"];
|
||||
n1 -> n6 [label="ret"];
|
||||
n6 [label="BoundTypePack 6"];
|
||||
n6 -> n7;
|
||||
n7 [label="TypePack 7"];
|
||||
n7 -> n3;
|
||||
})",
|
||||
toDot(requireType("f"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a: string | number
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="UnionTypeVar 1"];
|
||||
n1 -> n2;
|
||||
n2 [label="string"];
|
||||
n1 -> n3;
|
||||
n3 [label="number"];
|
||||
})",
|
||||
toDot(requireType("a"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "intersection")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a: string & number -- uninhabited
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="IntersectionTypeVar 1"];
|
||||
n1 -> n2;
|
||||
n2 [label="string"];
|
||||
n1 -> n3;
|
||||
n3 [label="number"];
|
||||
})",
|
||||
toDot(requireType("a"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "table")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type A<T, U...> = { x: T, y: (U...) -> (), [string]: any }
|
||||
local a: A<number, ...string>
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="TableTypeVar A"];
|
||||
n1 -> n2 [label="x"];
|
||||
n2 [label="number"];
|
||||
n1 -> n3 [label="y"];
|
||||
n3 [label="FunctionTypeVar 3"];
|
||||
n3 -> n4 [label="arg"];
|
||||
n4 [label="VariadicTypePack 4"];
|
||||
n4 -> n5;
|
||||
n5 [label="string"];
|
||||
n3 -> n6 [label="ret"];
|
||||
n6 [label="TypePack 6"];
|
||||
n1 -> n7 [label="[index]"];
|
||||
n7 [label="string"];
|
||||
n1 -> n8 [label="[value]"];
|
||||
n8 [label="any"];
|
||||
n1 -> n9 [label="typeParam"];
|
||||
n9 [label="number"];
|
||||
n1 -> n4 [label="typePackParam"];
|
||||
})",
|
||||
toDot(requireType("a"), opts));
|
||||
|
||||
// Extra coverage with pointers (unstable values)
|
||||
(void)toDot(requireType("a"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "metatable")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a: typeof(setmetatable({}, {}))
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="MetatableTypeVar 1"];
|
||||
n1 -> n2 [label="table"];
|
||||
n2 [label="TableTypeVar 2"];
|
||||
n1 -> n3 [label="metatable"];
|
||||
n3 [label="TableTypeVar 3"];
|
||||
})",
|
||||
toDot(requireType("a"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "free")
|
||||
{
|
||||
TypeVar type{TypeVariant{FreeTypeVar{TypeLevel{0, 0}}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="FreeTypeVar 1"];
|
||||
})",
|
||||
toDot(&type, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error")
|
||||
{
|
||||
TypeVar type{TypeVariant{ErrorTypeVar{}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="ErrorTypeVar 1"];
|
||||
})",
|
||||
toDot(&type, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic")
|
||||
{
|
||||
TypeVar type{TypeVariant{GenericTypeVar{"T"}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="GenericTypeVar T"];
|
||||
})",
|
||||
toDot(&type, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ToDotClassFixture, "class")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a: ChildClass
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="ClassTypeVar ChildClass"];
|
||||
n1 -> n2 [label="ChildField"];
|
||||
n2 [label="string"];
|
||||
n1 -> n3 [label="[parent]"];
|
||||
n3 [label="ClassTypeVar BaseClass"];
|
||||
n3 -> n4 [label="BaseField"];
|
||||
n4 [label="number"];
|
||||
n3 -> n5 [label="[metatable]"];
|
||||
n5 [label="TableTypeVar 5"];
|
||||
})",
|
||||
toDot(requireType("a"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "free_pack")
|
||||
{
|
||||
TypePackVar pack{TypePackVariant{FreeTypePack{TypeLevel{0, 0}}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="FreeTypePack 1"];
|
||||
})",
|
||||
toDot(&pack, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_pack")
|
||||
{
|
||||
TypePackVar pack{TypePackVariant{Unifiable::Error{}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="ErrorTypePack 1"];
|
||||
})",
|
||||
toDot(&pack, opts));
|
||||
|
||||
// Extra coverage with pointers (unstable values)
|
||||
(void)toDot(&pack);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_pack")
|
||||
{
|
||||
TypePackVar pack1{TypePackVariant{GenericTypePack{}}};
|
||||
TypePackVar pack2{TypePackVariant{GenericTypePack{"T"}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="GenericTypePack 1"];
|
||||
})",
|
||||
toDot(&pack1, opts));
|
||||
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="GenericTypePack T"];
|
||||
})",
|
||||
toDot(&pack2, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "bound_pack")
|
||||
{
|
||||
TypePackVar pack{TypePackVariant{TypePack{{typeChecker.numberType}, {}}}};
|
||||
TypePackVar bound{TypePackVariant{BoundTypePack{&pack}}};
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="BoundTypePack 1"];
|
||||
n1 -> n2;
|
||||
n2 [label="TypePack 2"];
|
||||
n2 -> n3;
|
||||
n3 [label="number"];
|
||||
})",
|
||||
toDot(&bound, opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "bound_table")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local a = {x=2}
|
||||
local b
|
||||
b.x = 2
|
||||
b = a
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
std::optional<TypeId> ty = getType("b");
|
||||
REQUIRE(bool(ty));
|
||||
|
||||
ToDotOptions opts;
|
||||
opts.showPointers = false;
|
||||
CHECK_EQ(R"(digraph graphname {
|
||||
n1 [label="TableTypeVar 1"];
|
||||
n1 -> n2 [label="boundTo"];
|
||||
n2 [label="TableTypeVar a"];
|
||||
n2 -> n3 [label="x"];
|
||||
n3 [label="number"];
|
||||
})",
|
||||
toDot(*ty, opts));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
@ -445,9 +445,6 @@ local a: Import.Type
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_packs")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
std::string code = R"(
|
||||
type Packed<T...> = (T...)->(T...)
|
||||
local a: Packed<>
|
||||
|
@ -537,8 +537,6 @@ TEST_CASE_FIXTURE(Fixture, "free_variables_from_typeof_in_aliases")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "non_recursive_aliases_that_reuse_a_generic_name")
|
||||
{
|
||||
ScopedFastFlag sff1{"LuauSubstitutionDontReplaceIgnoredTypes", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Array<T> = { [number]: T }
|
||||
type Tuple<T, V> = Array<T | V>
|
||||
|
@ -609,8 +609,6 @@ TEST_CASE_FIXTURE(Fixture, "typefuns_sharing_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "bound_tables_do_not_clone_original_fields")
|
||||
{
|
||||
ScopedFastFlag luauCloneBoundTables{"LuauCloneBoundTables", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local exports = {}
|
||||
local nested = {}
|
||||
@ -627,4 +625,23 @@ return exports
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names")
|
||||
{
|
||||
ScopedFastFlag luauFunctionArgumentNameSize{"LuauFunctionArgumentNameSize", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f<T, U...>(a: T, ...: U...) end
|
||||
|
||||
f(1, 2, 3)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
auto ty = findTypeAtPosition(Position(3, 0));
|
||||
REQUIRE(ty);
|
||||
ToStringOptions opts;
|
||||
opts.functionTypeArguments = true;
|
||||
CHECK_EQ(toString(*ty, opts), "(a: number, number, number) -> ()");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -31,8 +31,6 @@ TEST_SUITE_BEGIN("ProvisionalTests");
|
||||
*/
|
||||
TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
|
||||
const std::string code = R"(
|
||||
function f(a)
|
||||
if type(a) == "boolean" then
|
||||
|
@ -2022,4 +2022,74 @@ caused by:
|
||||
Property '__call' is not compatible. Type '(a, b) -> ()' could not be converted into '<a>(a) -> ()')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
|
||||
{
|
||||
ScopedFastFlag sffs[] {
|
||||
{"LuauPropertiesGetExpectedType", true},
|
||||
{"LuauExpectedTypesOfProperties", true},
|
||||
{"LuauTableSubtypingVariance", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
type Sub = { x : number, y: number }
|
||||
type HasSuper = { p : Super }
|
||||
type HasSub = { p : Sub }
|
||||
local a: HasSuper = { p = { x = 5, y = 7 }}
|
||||
a.p = { x = 9 }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
|
||||
{
|
||||
ScopedFastFlag sffs[] {
|
||||
{"LuauPropertiesGetExpectedType", true},
|
||||
{"LuauExpectedTypesOfProperties", true},
|
||||
{"LuauTableSubtypingVariance", true},
|
||||
{"LuauExtendedTypeMismatchError", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
type Sub = { x : number, y: number }
|
||||
type HasSuper = { p : Super }
|
||||
type HasSub = { p : Sub }
|
||||
local tmp = { p = { x = 5, y = 7 }}
|
||||
local a: HasSuper = tmp
|
||||
a.p = { x = 9 }
|
||||
-- needs to be an error because
|
||||
local y: number = tmp.p.y
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'tmp' could not be converted into 'HasSuper'
|
||||
caused by:
|
||||
Property 'p' is not compatible. Table type '{| x: number, y: number |}' not compatible with type 'Super' because the former has extra field 'y')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
|
||||
{
|
||||
ScopedFastFlag sffs[] {
|
||||
{"LuauPropertiesGetExpectedType", true},
|
||||
{"LuauExpectedTypesOfProperties", true},
|
||||
{"LuauTableSubtypingVariance", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
type Sub = { x : number, y: number }
|
||||
type HasSuper = { [string] : Super }
|
||||
type HasSub = { [string] : Sub }
|
||||
local a: HasSuper = { p = { x = 5, y = 7 }}
|
||||
a.p = { x = 9 }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -4779,4 +4779,24 @@ local bar = foo.nutrition + 100
|
||||
// CHECK_EQ("Unknown type used in + operation; consider adding a type annotation to 'foo'", toString(result.errors[1]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "require_failed_module")
|
||||
{
|
||||
ScopedFastFlag luauModuleRequireErrorPack{"LuauModuleRequireErrorPack", true};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
return unfortunately()
|
||||
)";
|
||||
|
||||
CheckResult aResult = frontend.check("game/A");
|
||||
LUAU_REQUIRE_ERRORS(aResult);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local ModuleA = require(game.A)
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
std::optional<TypeId> oty = requireType("ModuleA");
|
||||
CHECK_EQ("*unknown*", toString(*oty));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -296,9 +296,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Packed<T...> = (T...) -> T...
|
||||
local a: Packed<>
|
||||
@ -360,9 +357,6 @@ local c: Packed<string, number, boolean>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_import")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
export type Packed<T, U...> = { a: T, b: (U...) -> () }
|
||||
return {}
|
||||
@ -393,9 +387,6 @@ local d: { a: typeof(c) }
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_pack_type_parameters")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
export type Packed<T, U...> = { a: T, b: (U...) -> () }
|
||||
return {}
|
||||
@ -431,9 +422,6 @@ type C<X...> = Import.Packed<string, (number, X...)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_nested")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Packed1<T...> = (T...) -> (T...)
|
||||
type Packed2<T...> = (Packed1<T...>, T...) -> (Packed1<T...>, T...)
|
||||
@ -452,9 +440,6 @@ type Packed4<T...> = (Packed3<T...>, T...) -> (Packed3<T...>, T...)
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_variadic")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X<T...> = (T...) -> (string, T...)
|
||||
|
||||
@ -470,9 +455,6 @@ type E = X<(number, ...string)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_multi")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T..., U...> = (T...) -> (U...)
|
||||
type A<S...> = Y<S..., S...>
|
||||
@ -501,9 +483,6 @@ type I<S..., R...> = W<number, (string, S...), R...>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X<T...> = (T...) -> (T...)
|
||||
|
||||
@ -527,9 +506,6 @@ type F = X<(string, ...number)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T..., U...> = (T...) -> (U...)
|
||||
|
||||
@ -549,9 +525,6 @@ type D<X...> = Y<X..., (number, string, X...)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_pack_explicit_multi_tostring")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T..., U...> = { f: (T...) -> (U...) }
|
||||
|
||||
@ -567,9 +540,6 @@ local b: Y<(), ()>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_backwards_compatible")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X<T> = () -> T
|
||||
type Y<T, U> = (T) -> U
|
||||
@ -588,9 +558,6 @@ type C = Y<(number), boolean>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_type_packs_errors")
|
||||
{
|
||||
ScopedFastFlag luauTypeAliasPacks("LuauTypeAliasPacks", true);
|
||||
ScopedFastFlag luauParseTypePackTypeParameters("LuauParseTypePackTypeParameters", true);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Packed<T, U, V...> = (T, U) -> (V...)
|
||||
local b: Packed<number>
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
|
||||
#include "Fixture.h"
|
||||
#include "ScopedFlags.h"
|
||||
@ -323,4 +324,48 @@ TEST_CASE("tagging_props")
|
||||
CHECK(Luau::hasTag(prop, "foo"));
|
||||
}
|
||||
|
||||
struct VisitCountTracker
|
||||
{
|
||||
std::unordered_map<TypeId, unsigned> tyVisits;
|
||||
std::unordered_map<TypePackId, unsigned> tpVisits;
|
||||
|
||||
void cycle(TypeId) {}
|
||||
void cycle(TypePackId) {}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(TypeId ty, const T& t)
|
||||
{
|
||||
tyVisits[ty]++;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(TypePackId tp, const T&)
|
||||
{
|
||||
tpVisits[tp]++;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "visit_once")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type T = { a: number, b: () -> () }
|
||||
local b: (T, T, T) -> T
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
TypeId bType = requireType("b");
|
||||
|
||||
VisitCountTracker tester;
|
||||
DenseHashSet<void*> seen{nullptr};
|
||||
visitTypeVarOnce(bType, tester, seen);
|
||||
|
||||
for (auto [_, count] : tester.tyVisits)
|
||||
CHECK_EQ(count, 1);
|
||||
|
||||
for (auto [_, count] : tester.tpVisits)
|
||||
CHECK_EQ(count, 1);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -2,7 +2,13 @@
|
||||
print('testing function calls through API')
|
||||
|
||||
function add(a, b)
|
||||
return a + b
|
||||
return a + b
|
||||
end
|
||||
|
||||
local m = { __eq = function(a, b) return a.a == b.a end }
|
||||
|
||||
function create_with_tm(x)
|
||||
return setmetatable({ a = x }, m)
|
||||
end
|
||||
|
||||
return('OK')
|
||||
|
@ -441,7 +441,8 @@ assert((function() a = {} b = {} mt = { __eq = function(l, r) return #l == #r en
|
||||
assert((function() a = {} b = {} function eq(l, r) return #l == #r end setmetatable(a, {__eq = eq}) setmetatable(b, {__eq = eq}) return concat(a == b, a ~= b) end)() == "true,false")
|
||||
assert((function() a = {} b = {} setmetatable(a, {__eq = function(l, r) return #l == #r end}) setmetatable(b, {__eq = function(l, r) return #l == #r end}) return concat(a == b, a ~= b) end)() == "false,true")
|
||||
|
||||
-- userdata, reference equality (no mt)
|
||||
-- userdata, reference equality (no mt or mt.__eq)
|
||||
assert((function() a = newproxy() return concat(a == newproxy(),a ~= newproxy()) end)() == "false,true")
|
||||
assert((function() a = newproxy(true) return concat(a == newproxy(true),a ~= newproxy(true)) end)() == "false,true")
|
||||
|
||||
-- rawequal
|
||||
@ -876,4 +877,4 @@ assert(concat(typeof(5), typeof(nil), typeof({}), typeof(newproxy())) == "number
|
||||
|
||||
testgetfenv() -- DONT MOVE THIS LINE
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -419,11 +419,5 @@ co = coroutine.create(function ()
|
||||
return loadstring("return a")()
|
||||
end)
|
||||
|
||||
a = {a = 15}
|
||||
-- debug.setfenv(co, a)
|
||||
-- assert(debug.getfenv(co) == a)
|
||||
-- assert(select(2, coroutine.resume(co)) == a)
|
||||
-- assert(select(2, coroutine.resume(co)) == a.a)
|
||||
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -237,4 +237,4 @@ repeat
|
||||
i = i+1
|
||||
until i==c
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -373,4 +373,4 @@ do
|
||||
assert(f() == 42)
|
||||
end
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -74,4 +74,4 @@ assert(os.difftime(t1,t2) == 60*2-19)
|
||||
|
||||
assert(os.time({ year = 1970, day = 1, month = 1, hour = 0}) == 0)
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -98,4 +98,4 @@ assert(quuz(function(...) end) == "0 true")
|
||||
assert(quuz(function(a, b) end) == "2 false")
|
||||
assert(quuz(function(a, b, ...) end) == "2 true")
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -34,15 +34,15 @@ assert(doit("error('hi', 0)") == 'hi')
|
||||
assert(doit("unpack({}, 1, n=2^30)"))
|
||||
assert(doit("a=math.sin()"))
|
||||
assert(not doit("tostring(1)") and doit("tostring()"))
|
||||
assert(doit"tonumber()")
|
||||
assert(doit"repeat until 1; a")
|
||||
assert(doit("tonumber()"))
|
||||
assert(doit("repeat until 1; a"))
|
||||
checksyntax("break label", "", "label", 1)
|
||||
assert(doit";")
|
||||
assert(doit"a=1;;")
|
||||
assert(doit"return;;")
|
||||
assert(doit"assert(false)")
|
||||
assert(doit"assert(nil)")
|
||||
assert(doit"a=math.sin\n(3)")
|
||||
assert(doit(";"))
|
||||
assert(doit("a=1;;"))
|
||||
assert(doit("return;;"))
|
||||
assert(doit("assert(false)"))
|
||||
assert(doit("assert(nil)"))
|
||||
assert(doit("a=math.sin\n(3)"))
|
||||
assert(doit("function a (... , ...) end"))
|
||||
assert(doit("function a (, ...) end"))
|
||||
|
||||
@ -59,7 +59,7 @@ checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
|
||||
"local 'bbbb'")
|
||||
checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'")
|
||||
checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'")
|
||||
assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
|
||||
assert(not string.find(doit("a={13}; local bbbb=1; a[bbbb](3)"), "'bbbb'"))
|
||||
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
|
||||
|
||||
aaa = nil
|
||||
@ -67,14 +67,14 @@ checkmessage("aaa.bbb:ddd(9)", "global 'aaa'")
|
||||
checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'")
|
||||
checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'")
|
||||
checkmessage("local a,b,c; (function () a = b+1 end)()", "upvalue 'b'")
|
||||
assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)")
|
||||
assert(not doit("local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)"))
|
||||
|
||||
checkmessage("b=1; local aaa='a'; x=aaa+b", "local 'aaa'")
|
||||
checkmessage("aaa={}; x=3/aaa", "global 'aaa'")
|
||||
checkmessage("aaa='2'; b=nil;x=aaa*b", "global 'b'")
|
||||
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
|
||||
assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'"))
|
||||
assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'"))
|
||||
assert(not string.find(doit("aaa={}; x=(aaa or aaa)+(aaa and aaa)"), "'aaa'"))
|
||||
assert(not string.find(doit("aaa={}; (aaa or aaa)()"), "'aaa'"))
|
||||
|
||||
checkmessage([[aaa=9
|
||||
repeat until 3==3
|
||||
@ -122,10 +122,10 @@ function lineerror (s)
|
||||
return line and line+0
|
||||
end
|
||||
|
||||
assert(lineerror"local a\n for i=1,'a' do \n print(i) \n end" == 2)
|
||||
-- assert(lineerror"\n local a \n for k,v in 3 \n do \n print(k) \n end" == 3)
|
||||
-- assert(lineerror"\n\n for k,v in \n 3 \n do \n print(k) \n end" == 4)
|
||||
assert(lineerror"function a.x.y ()\na=a+1\nend" == 1)
|
||||
assert(lineerror("local a\n for i=1,'a' do \n print(i) \n end") == 2)
|
||||
-- assert(lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end") == 3)
|
||||
-- assert(lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end") == 4)
|
||||
assert(lineerror("function a.x.y ()\na=a+1\nend") == 1)
|
||||
|
||||
local p = [[
|
||||
function g() f() end
|
||||
|
@ -77,7 +77,7 @@ end
|
||||
|
||||
local function dosteps (siz)
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
collectgarbage("stop")
|
||||
local a = {}
|
||||
for i=1,100 do a[i] = {{}}; local b = {} end
|
||||
local x = gcinfo()
|
||||
@ -99,11 +99,11 @@ assert(dosteps(10000) == 1)
|
||||
do
|
||||
local x = gcinfo()
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
collectgarbage("stop")
|
||||
repeat
|
||||
local a = {}
|
||||
until gcinfo() > 1000
|
||||
collectgarbage"restart"
|
||||
collectgarbage("restart")
|
||||
repeat
|
||||
local a = {}
|
||||
until gcinfo() < 1000
|
||||
@ -123,7 +123,7 @@ for n in pairs(b) do
|
||||
end
|
||||
b = nil
|
||||
collectgarbage()
|
||||
for n in pairs(a) do error'cannot be here' end
|
||||
for n in pairs(a) do error("cannot be here") end
|
||||
for i=1,lim do a[i] = i end
|
||||
for i=1,lim do assert(a[i] == i) end
|
||||
|
||||
|
@ -368,9 +368,9 @@ assert(next(a,nil) == 1000 and next(a,1000) == nil)
|
||||
assert(next({}) == nil)
|
||||
assert(next({}, nil) == nil)
|
||||
|
||||
for a,b in pairs{} do error"not here" end
|
||||
for i=1,0 do error'not here' end
|
||||
for i=0,1,-1 do error'not here' end
|
||||
for a,b in pairs{} do error("not here") end
|
||||
for i=1,0 do error("not here") end
|
||||
for i=0,1,-1 do error("not here") end
|
||||
a = nil; for i=1,1 do assert(not a); a=1 end; assert(a)
|
||||
a = nil; for i=1,1,-1 do assert(not a); a=1 end; assert(a)
|
||||
|
||||
|
@ -144,4 +144,4 @@ coroutine.resume(co)
|
||||
resumeerror(co, "fail")
|
||||
checkresults({ true, false, "fail" }, coroutine.resume(co))
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -205,4 +205,4 @@ for p, c in string.gmatch(x, "()(" .. utf8.charpattern .. ")") do
|
||||
end
|
||||
end
|
||||
|
||||
return'OK'
|
||||
return 'OK'
|
||||
|
@ -1,6 +1,9 @@
|
||||
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
print('testing vectors')
|
||||
|
||||
-- detect vector size
|
||||
local vector_size = if pcall(function() return vector(0, 0, 0).w end) then 4 else 3
|
||||
|
||||
-- equality
|
||||
assert(vector(1, 2, 3) == vector(1, 2, 3))
|
||||
assert(vector(0, 1, 2) == vector(-0, 1, 2))
|
||||
@ -13,8 +16,14 @@ assert(not rawequal(vector(1, 2, 3), vector(1, 2, 4)))
|
||||
|
||||
-- type & tostring
|
||||
assert(type(vector(1, 2, 3)) == "vector")
|
||||
assert(tostring(vector(1, 2, 3)) == "1, 2, 3")
|
||||
assert(tostring(vector(-1, 2, 0.5)) == "-1, 2, 0.5")
|
||||
|
||||
if vector_size == 4 then
|
||||
assert(tostring(vector(1, 2, 3, 4)) == "1, 2, 3, 4")
|
||||
assert(tostring(vector(-1, 2, 0.5, 0)) == "-1, 2, 0.5, 0")
|
||||
else
|
||||
assert(tostring(vector(1, 2, 3)) == "1, 2, 3")
|
||||
assert(tostring(vector(-1, 2, 0.5)) == "-1, 2, 0.5")
|
||||
end
|
||||
|
||||
local t = {}
|
||||
|
||||
@ -42,12 +51,19 @@ assert(8 * vector(8, 16, 24) == vector(64, 128, 192));
|
||||
assert(vector(1, 2, 4) * '8' == vector(8, 16, 32));
|
||||
assert('8' * vector(8, 16, 24) == vector(64, 128, 192));
|
||||
|
||||
assert(vector(1, 2, 4) / vector(8, 16, 24) == vector(1/8, 2/16, 4/24));
|
||||
if vector_size == 4 then
|
||||
assert(vector(1, 2, 4, 8) / vector(8, 16, 24, 32) == vector(1/8, 2/16, 4/24, 8/32));
|
||||
assert(8 / vector(8, 16, 24, 32) == vector(1, 1/2, 1/3, 1/4));
|
||||
assert('8' / vector(8, 16, 24, 32) == vector(1, 1/2, 1/3, 1/4));
|
||||
else
|
||||
assert(vector(1, 2, 4) / vector(8, 16, 24, 1) == vector(1/8, 2/16, 4/24));
|
||||
assert(8 / vector(8, 16, 24) == vector(1, 1/2, 1/3));
|
||||
assert('8' / vector(8, 16, 24) == vector(1, 1/2, 1/3));
|
||||
end
|
||||
|
||||
assert(vector(1, 2, 4) / 8 == vector(1/8, 1/4, 1/2));
|
||||
assert(vector(1, 2, 4) / (1 / val) == vector(1/8, 2/8, 4/8));
|
||||
assert(8 / vector(8, 16, 24) == vector(1, 1/2, 1/3));
|
||||
assert(vector(1, 2, 4) / '8' == vector(1/8, 1/4, 1/2));
|
||||
assert('8' / vector(8, 16, 24) == vector(1, 1/2, 1/3));
|
||||
|
||||
assert(-vector(1, 2, 4) == vector(-1, -2, -4));
|
||||
|
||||
@ -71,4 +87,9 @@ assert(pcall(function() local t = {} rawset(t, vector(0/0, 2, 3), 1) end) == fal
|
||||
-- make sure we cover both builtin and C impl
|
||||
assert(vector(1, 2, 4) == vector("1", "2", "4"))
|
||||
|
||||
-- additional checks for 4-component vectors
|
||||
if vector_size == 4 then
|
||||
assert(vector(1, 2, 3, 4).w == 4)
|
||||
end
|
||||
|
||||
return 'OK'
|
||||
|
@ -458,13 +458,16 @@ def display(root, title, colors, flip = False):
|
||||
|
||||
framewidth = 1200 - 20
|
||||
|
||||
def pixels(x):
|
||||
return float(x) / root.width * framewidth if root.width > 0 else 0
|
||||
|
||||
for n in root.subtree():
|
||||
if n.width / root.width * framewidth < 0.1:
|
||||
if pixels(n.width) < 0.1:
|
||||
continue
|
||||
|
||||
x = 10 + n.offset / root.width * framewidth
|
||||
x = 10 + pixels(n.offset)
|
||||
y = (maxdepth - 1 - n.depth if flip else n.depth) * 16 + 3 * 16
|
||||
width = n.width / root.width * framewidth
|
||||
width = pixels(n.width)
|
||||
height = 15
|
||||
|
||||
if colors == "cold":
|
||||
|
Loading…
Reference in New Issue
Block a user