Sync to upstream/release/506 (#270)

- Fix some cases where type checking would overflow the native stack
- Improve autocomplete behavior when assigning a partially written function call (not currently exposed through command line tools)
- Improve autocomplete type inference feedback for some expressions where previously the type would not be known
- Improve quantification performance during type checking for large types
- Improve type checking for table literals when the expected type of the table is known because of a type annotation
- Fix type checking errors in cases where required module has errors in the resulting type
- Fix debug line information for multi-line chained call sequences (Add function name information for "attempt to call a nil value" #255)
- lua_newuserdata now takes 2 arguments to match Lua/LuaJIT APIs better; lua_newuserdatatagged should be used if the third argument was non-0.
- lua_ref can no longer be used with LUA_REGISTRYINDEX to prevent mistakes when migrating Lua FFI (Inconsistency with lua_ref #247)
- Fix assertions and possible crashes when executing script code indirectly via metatable dispatch from lua_equal/lua_lessthan/lua_getfield/etc. (Hitting a crash in an assert after lua_equal is called. #259)
- Fix flamegraph scripts to run under Python 2
This commit is contained in:
Arseny Kapoulkine 2021-12-02 22:41:04 -08:00 committed by GitHub
parent f5ec6df7ba
commit 32fb6d10a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 2430 additions and 2343 deletions

View File

@ -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

View File

@ -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
{

View 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

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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};

View File

@ -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;

View File

@ -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());

View File

@ -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

View File

@ -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 << ">";

View File

@ -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);
});

View File

@ -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

View File

@ -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);

View File

@ -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;
}