Sync to upstream/release/507-pre (#286)
This doesn't contain all changes for 507 yet but we might want to do the Luau 0.507 release a bit earlier to end the year sooner. Changes: - Type ascription (::) now permits casts between related types in both directions, allowing to refine or loosen the type (RFC #56) - Fix type definition for tonumber to return number? since the input string isn't guaranteed to contain a valid number - Fix type refinements for field access via [] - Many stability fixes for type checker - Provide extra information in error messages for type mismatches in more cases - Improve performance of type checking for large unions when union members are string literals - Add coverage reporting support to Repl (--coverage command line argument) and lua_getcoverage C API - Work around code signing issues during Makefile builds on macOS - Improve performance of truthiness checks in some cases, particularly on Apple M1, resulting in 10-25% perf gains on qsort benchmark depending on the CPU/compiler - Fix support for little-endian systems; IBM s390x here we go!
This commit is contained in:
parent
88be067c0b
commit
f2e6a8f4a5
|
@ -277,11 +277,20 @@ struct MissingUnionProperty
|
|||
bool operator==(const MissingUnionProperty& rhs) const;
|
||||
};
|
||||
|
||||
struct TypesAreUnrelated
|
||||
{
|
||||
TypeId left;
|
||||
TypeId right;
|
||||
|
||||
bool operator==(const TypesAreUnrelated& rhs) const;
|
||||
};
|
||||
|
||||
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
|
||||
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
|
||||
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError,
|
||||
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
|
||||
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty>;
|
||||
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
|
||||
TypesAreUnrelated>;
|
||||
|
||||
struct TypeError
|
||||
{
|
||||
|
|
|
@ -36,6 +36,10 @@ std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
|
|||
std::ostream& operator<<(std::ostream& lhs, const ModuleHasCyclicDependency& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const DuplicateGenericParameter& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const CannotInferBinaryOperation& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const SwappedGenericTypeParameter& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const OptionalValueAccess& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const MissingUnionProperty& error);
|
||||
std::ostream& operator<<(std::ostream& lhs, const TypesAreUnrelated& error);
|
||||
|
||||
std::ostream& operator<<(std::ostream& lhs, const TableState& tv);
|
||||
std::ostream& operator<<(std::ostream& lhs, const TypeVar& tv);
|
||||
|
|
|
@ -69,8 +69,8 @@ std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeV
|
|||
|
||||
// It could be useful to see the text representation of a type during a debugging session instead of exploring the content of the class
|
||||
// These functions will dump the type to stdout and can be evaluated in Watch/Immediate windows or as gdb/lldb expression
|
||||
void dump(TypeId ty);
|
||||
void dump(TypePackId ty);
|
||||
std::string dump(TypeId ty);
|
||||
std::string dump(TypePackId ty);
|
||||
|
||||
std::string generateName(size_t n);
|
||||
|
||||
|
|
|
@ -156,13 +156,14 @@ struct TypeChecker
|
|||
|
||||
// Returns both the type of the lvalue and its binding (if the caller wants to mutate the binding).
|
||||
// Note: the binding may be null.
|
||||
// TODO: remove second return value with FFlagLuauUpdateFunctionNameBinding
|
||||
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExpr& expr);
|
||||
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr);
|
||||
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprGlobal& expr);
|
||||
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr);
|
||||
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr);
|
||||
|
||||
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName);
|
||||
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName, TypeLevel level);
|
||||
std::pair<TypeId, ScopePtr> checkFunctionSignature(const ScopePtr& scope, int subLevel, const AstExprFunction& expr,
|
||||
std::optional<Location> originalNameLoc, std::optional<TypeId> expectedType);
|
||||
void checkFunctionBody(const ScopePtr& scope, TypeId type, const AstExprFunction& function);
|
||||
|
@ -174,7 +175,7 @@ struct TypeChecker
|
|||
ExprResult<TypePackId> checkExprPack(const ScopePtr& scope, const AstExprCall& expr);
|
||||
std::vector<std::optional<TypeId>> getExpectedTypesForCall(const std::vector<TypeId>& overloads, size_t argumentCount, bool selfCall);
|
||||
std::optional<ExprResult<TypePackId>> checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack,
|
||||
TypePackId argPack, TypePack* args, const std::vector<Location>& argLocations, const ExprResult<TypePackId>& argListResult,
|
||||
TypePackId argPack, TypePack* args, const std::vector<Location>* argLocations, const ExprResult<TypePackId>& argListResult,
|
||||
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<TypeId>& overloadsThatDont, std::vector<OverloadErrorEntry>& errors);
|
||||
bool handleSelfCallMismatch(const ScopePtr& scope, const AstExprCall& expr, TypePack* args, const std::vector<Location>& argLocations,
|
||||
const std::vector<OverloadErrorEntry>& errors);
|
||||
|
@ -277,7 +278,7 @@ public:
|
|||
[[noreturn]] void ice(const std::string& message);
|
||||
|
||||
ScopePtr childFunctionScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
|
||||
ScopePtr childScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
|
||||
ScopePtr childScope(const ScopePtr& parent, const Location& location);
|
||||
|
||||
// Wrapper for merge(l, r, toUnion) but without the lambda junk.
|
||||
void merge(RefinementMap& l, const RefinementMap& r);
|
||||
|
|
|
@ -499,6 +499,7 @@ struct SingletonTypes
|
|||
const TypePackId anyTypePack;
|
||||
|
||||
SingletonTypes();
|
||||
~SingletonTypes();
|
||||
SingletonTypes(const SingletonTypes&) = delete;
|
||||
void operator=(const SingletonTypes&) = delete;
|
||||
|
||||
|
@ -509,10 +510,12 @@ struct SingletonTypes
|
|||
|
||||
private:
|
||||
std::unique_ptr<struct TypeArena> arena;
|
||||
bool debugFreezeArena = false;
|
||||
|
||||
TypeId makeStringMetatable();
|
||||
};
|
||||
|
||||
extern SingletonTypes singletonTypes;
|
||||
SingletonTypes& getSingletonTypes();
|
||||
|
||||
void persist(TypeId ty);
|
||||
void persist(TypePackId tp);
|
||||
|
@ -523,9 +526,6 @@ TypeLevel* getMutableLevel(TypeId ty);
|
|||
const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name);
|
||||
bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent);
|
||||
|
||||
bool hasGeneric(TypeId ty);
|
||||
bool hasGeneric(TypePackId tp);
|
||||
|
||||
TypeVar* asMutable(TypeId ty);
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -24,7 +24,7 @@ struct TypeLevel
|
|||
int level = 0;
|
||||
int subLevel = 0;
|
||||
|
||||
// Returns true if the typelevel "this" is "bigger" than rhs
|
||||
// Returns true if the level of "this" belongs to an equal or larger scope than that of rhs
|
||||
bool subsumes(const TypeLevel& rhs) const
|
||||
{
|
||||
if (level < rhs.level)
|
||||
|
@ -38,6 +38,15 @@ struct TypeLevel
|
|||
return false;
|
||||
}
|
||||
|
||||
// Returns true if the level of "this" belongs to a larger (not equal) scope than that of rhs
|
||||
bool subsumesStrict(const TypeLevel& rhs) const
|
||||
{
|
||||
if (level == rhs.level && subLevel == rhs.subLevel)
|
||||
return false;
|
||||
else
|
||||
return subsumes(rhs);
|
||||
}
|
||||
|
||||
TypeLevel incr() const
|
||||
{
|
||||
TypeLevel result;
|
||||
|
|
|
@ -91,6 +91,9 @@ private:
|
|||
|
||||
[[noreturn]] void ice(const std::string& message, const Location& location);
|
||||
[[noreturn]] void ice(const std::string& message);
|
||||
|
||||
// Available after regular type pack unification errors
|
||||
std::optional<int> firstPackErrorPos;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompletePreferToCallFunctions, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteFirstArg, false);
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
@ -190,7 +191,48 @@ static ParenthesesRecommendation getParenRecommendation(TypeId id, const std::ve
|
|||
return ParenthesesRecommendation::None;
|
||||
}
|
||||
|
||||
static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typeArena, AstNode* node, TypeId ty)
|
||||
static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* node, Position position)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauAutocompleteFirstArg);
|
||||
|
||||
auto expr = node->asExpr();
|
||||
if (!expr)
|
||||
return std::nullopt;
|
||||
|
||||
// Extra care for first function call argument location
|
||||
// When we don't have anything inside () yet, we also don't have an AST node to base our lookup
|
||||
if (AstExprCall* exprCall = expr->as<AstExprCall>())
|
||||
{
|
||||
if (exprCall->args.size == 0 && exprCall->argLocation.contains(position))
|
||||
{
|
||||
auto it = module.astTypes.find(exprCall->func);
|
||||
|
||||
if (!it)
|
||||
return std::nullopt;
|
||||
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(*it));
|
||||
|
||||
if (!ftv)
|
||||
return std::nullopt;
|
||||
|
||||
auto [head, tail] = flatten(ftv->argTypes);
|
||||
unsigned index = exprCall->self ? 1 : 0;
|
||||
|
||||
if (index < head.size())
|
||||
return head[index];
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto it = module.astExpectedTypes.find(expr);
|
||||
if (!it)
|
||||
return std::nullopt;
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typeArena, AstNode* node, Position position, TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
|
@ -220,15 +262,29 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
|||
}
|
||||
};
|
||||
|
||||
auto expr = node->asExpr();
|
||||
if (!expr)
|
||||
return TypeCorrectKind::None;
|
||||
TypeId expectedType;
|
||||
|
||||
auto it = module.astExpectedTypes.find(expr);
|
||||
if (!it)
|
||||
return TypeCorrectKind::None;
|
||||
if (FFlag::LuauAutocompleteFirstArg)
|
||||
{
|
||||
auto typeAtPosition = findExpectedTypeAt(module, node, position);
|
||||
|
||||
TypeId expectedType = follow(*it);
|
||||
if (!typeAtPosition)
|
||||
return TypeCorrectKind::None;
|
||||
|
||||
expectedType = follow(*typeAtPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto expr = node->asExpr();
|
||||
if (!expr)
|
||||
return TypeCorrectKind::None;
|
||||
|
||||
auto it = module.astExpectedTypes.find(expr);
|
||||
if (!it)
|
||||
return TypeCorrectKind::None;
|
||||
|
||||
expectedType = follow(*it);
|
||||
}
|
||||
|
||||
if (FFlag::LuauAutocompletePreferToCallFunctions)
|
||||
{
|
||||
|
@ -333,8 +389,8 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
if (result.count(name) == 0 && name != Parser::errorName)
|
||||
{
|
||||
Luau::TypeId type = Luau::follow(prop.type);
|
||||
TypeCorrectKind typeCorrect =
|
||||
indexType == PropIndexType::Key ? TypeCorrectKind::Correct : checkTypeCorrectKind(module, typeArena, nodes.back(), type);
|
||||
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
|
||||
: checkTypeCorrectKind(module, typeArena, nodes.back(), {{}, {}}, type);
|
||||
ParenthesesRecommendation parens =
|
||||
indexType == PropIndexType::Key ? ParenthesesRecommendation::None : getParenRecommendation(type, nodes, typeCorrect);
|
||||
|
||||
|
@ -692,17 +748,31 @@ std::optional<const T*> returnFirstNonnullOptionOfType(const UnionTypeVar* utv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* node)
|
||||
static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* node, Position position)
|
||||
{
|
||||
auto expr = node->asExpr();
|
||||
if (!expr)
|
||||
return std::nullopt;
|
||||
TypeId expectedType;
|
||||
|
||||
auto it = module.astExpectedTypes.find(expr);
|
||||
if (!it)
|
||||
return std::nullopt;
|
||||
if (FFlag::LuauAutocompleteFirstArg)
|
||||
{
|
||||
auto typeAtPosition = findExpectedTypeAt(module, node, position);
|
||||
|
||||
TypeId expectedType = follow(*it);
|
||||
if (!typeAtPosition)
|
||||
return std::nullopt;
|
||||
|
||||
expectedType = follow(*typeAtPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto expr = node->asExpr();
|
||||
if (!expr)
|
||||
return std::nullopt;
|
||||
|
||||
auto it = module.astExpectedTypes.find(expr);
|
||||
if (!it)
|
||||
return std::nullopt;
|
||||
|
||||
expectedType = follow(*it);
|
||||
}
|
||||
|
||||
if (get<FunctionTypeVar>(expectedType))
|
||||
return true;
|
||||
|
@ -1171,7 +1241,7 @@ static void autocompleteExpression(const SourceModule& sourceModule, const Modul
|
|||
std::string n = toString(name);
|
||||
if (!result.count(n))
|
||||
{
|
||||
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, node, binding.typeId);
|
||||
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, node, position, binding.typeId);
|
||||
|
||||
result[n] = {AutocompleteEntryKind::Binding, binding.typeId, binding.deprecated, false, typeCorrect, std::nullopt, std::nullopt,
|
||||
binding.documentationSymbol, {}, getParenRecommendation(binding.typeId, ancestry, typeCorrect)};
|
||||
|
@ -1181,9 +1251,10 @@ static void autocompleteExpression(const SourceModule& sourceModule, const Modul
|
|||
scope = scope->parent;
|
||||
}
|
||||
|
||||
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, node, typeChecker.nilType);
|
||||
TypeCorrectKind correctForBoolean = checkTypeCorrectKind(module, typeArena, node, typeChecker.booleanType);
|
||||
TypeCorrectKind correctForFunction = functionIsExpectedAt(module, node).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, node, position, typeChecker.nilType);
|
||||
TypeCorrectKind correctForBoolean = checkTypeCorrectKind(module, typeArena, node, position, typeChecker.booleanType);
|
||||
TypeCorrectKind correctForFunction =
|
||||
functionIsExpectedAt(module, node, position).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
|
||||
if (FFlag::LuauIfElseExpressionAnalysisSupport)
|
||||
result["if"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false};
|
||||
|
|
|
@ -217,9 +217,9 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
|||
|
||||
TypeId genericK = arena.addType(GenericTypeVar{"K"});
|
||||
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level});
|
||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level, TableState::Generic});
|
||||
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(singletonTypes.stringType);
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(getSingletonTypes().stringType);
|
||||
LUAU_ASSERT(stringMetatableTy);
|
||||
const TableTypeVar* stringMetatableTable = get<TableTypeVar>(follow(*stringMetatableTy));
|
||||
LUAU_ASSERT(stringMetatableTable);
|
||||
|
@ -271,7 +271,10 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
|||
persist(pair.second.typeId);
|
||||
|
||||
if (TableTypeVar* ttv = getMutable<TableTypeVar>(pair.second.typeId))
|
||||
ttv->name = toString(pair.first);
|
||||
{
|
||||
if (!ttv->name)
|
||||
ttv->name = toString(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
attachMagicFunction(getGlobalBinding(typeChecker, "assert"), magicFunctionAssert);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixTonumberReturnType, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -113,7 +115,6 @@ declare function gcinfo(): number
|
|||
declare function error<T>(message: T, level: number?)
|
||||
|
||||
declare function tostring<T>(value: T): string
|
||||
declare function tonumber<T>(value: T, radix: number?): number
|
||||
|
||||
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
||||
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
||||
|
@ -204,7 +205,14 @@ declare function gcinfo(): number
|
|||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
return kBuiltinDefinitionLuaSrc;
|
||||
std::string result = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
if (FFlag::LuauFixTonumberReturnType)
|
||||
result += "declare function tonumber<T>(value: T, radix: number?): number?\n";
|
||||
else
|
||||
result += "declare function tonumber<T>(value: T, radix: number?): number\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -58,7 +58,7 @@ struct ErrorConverter
|
|||
result += "\ncaused by:\n ";
|
||||
|
||||
if (!tm.reason.empty())
|
||||
result += tm.reason + ". ";
|
||||
result += tm.reason + " ";
|
||||
|
||||
result += Luau::toString(*tm.error);
|
||||
}
|
||||
|
@ -410,6 +410,11 @@ struct ErrorConverter
|
|||
|
||||
return ss + " in the type '" + toString(e.type) + "'";
|
||||
}
|
||||
|
||||
std::string operator()(const TypesAreUnrelated& e) const
|
||||
{
|
||||
return "Cannot cast '" + toString(e.left) + "' into '" + toString(e.right) + "' because the types are unrelated";
|
||||
}
|
||||
};
|
||||
|
||||
struct InvalidNameChecker
|
||||
|
@ -658,6 +663,11 @@ bool MissingUnionProperty::operator==(const MissingUnionProperty& rhs) const
|
|||
return *type == *rhs.type && key == rhs.key;
|
||||
}
|
||||
|
||||
bool TypesAreUnrelated::operator==(const TypesAreUnrelated& rhs) const
|
||||
{
|
||||
return left == rhs.left && right == rhs.right;
|
||||
}
|
||||
|
||||
std::string toString(const TypeError& error)
|
||||
{
|
||||
ErrorConverter converter;
|
||||
|
@ -793,6 +803,11 @@ void copyError(T& e, TypeArena& destArena, SeenTypes& seenTypes, SeenTypePacks&
|
|||
for (auto& ty : e.missing)
|
||||
ty = clone(ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypesAreUnrelated>)
|
||||
{
|
||||
e.left = clone(e.left);
|
||||
e.right = clone(e.right);
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||
}
|
||||
|
|
|
@ -262,6 +262,12 @@ std::ostream& operator<<(std::ostream& stream, const MissingUnionProperty& error
|
|||
return stream << " }, key = '" + error.key + "' }";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const TypesAreUnrelated& error)
|
||||
{
|
||||
stream << "TypesAreUnrelated { left = '" + toString(error.left) + "', right = '" + toString(error.right) + "' }";
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const TableState& tv)
|
||||
{
|
||||
return stream << static_cast<std::underlying_type<TableState>::type>(tv);
|
||||
|
|
|
@ -379,7 +379,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
if (comma)
|
||||
writeRaw(",");
|
||||
else
|
||||
comma = false;
|
||||
comma = true;
|
||||
write(prop);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
||||
LUAU_FASTFLAG(LuauCaptureBrokenCommentSpans)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 0)
|
||||
|
||||
namespace Luau
|
||||
|
@ -23,7 +22,7 @@ static bool contains(Position pos, Comment comment)
|
|||
{
|
||||
if (comment.location.contains(pos))
|
||||
return true;
|
||||
else if (FFlag::LuauCaptureBrokenCommentSpans && comment.type == Lexeme::BrokenComment &&
|
||||
else if (comment.type == Lexeme::BrokenComment &&
|
||||
comment.location.begin <= pos) // Broken comments are broken specifically because they don't have an end
|
||||
return true;
|
||||
else if (comment.type == Lexeme::Comment && comment.location.end == pos)
|
||||
|
@ -194,7 +193,7 @@ struct TypePackCloner
|
|||
{
|
||||
cloneState.encounteredFreeType = true;
|
||||
|
||||
TypePackId err = singletonTypes.errorRecoveryTypePack(singletonTypes.anyTypePack);
|
||||
TypePackId err = getSingletonTypes().errorRecoveryTypePack(getSingletonTypes().anyTypePack);
|
||||
TypePackId cloned = dest.addTypePack(*err);
|
||||
seenTypePacks[typePackId] = cloned;
|
||||
}
|
||||
|
@ -247,7 +246,7 @@ void TypeCloner::defaultClone(const T& t)
|
|||
void TypeCloner::operator()(const Unifiable::Free& t)
|
||||
{
|
||||
cloneState.encounteredFreeType = true;
|
||||
TypeId err = singletonTypes.errorRecoveryType(singletonTypes.anyType);
|
||||
TypeId err = getSingletonTypes().errorRecoveryType(getSingletonTypes().anyType);
|
||||
TypeId cloned = dest.addType(*err);
|
||||
seenTypes[typeId] = cloned;
|
||||
}
|
||||
|
@ -421,9 +420,6 @@ TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypeP
|
|||
Luau::visit(cloner, tp->ty); // Mutates the storage that 'res' points into.
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
asMutable(res)->owningArena = &dest;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -440,12 +436,11 @@ TypeId clone(TypeId typeId, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks
|
|||
{
|
||||
TypeCloner cloner{dest, typeId, seenTypes, seenTypePacks, cloneState};
|
||||
Luau::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
|
||||
|
||||
// TODO: Make this work when the arena of 'res' might be frozen
|
||||
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
asMutable(res)->owningArena = &dest;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -508,8 +503,8 @@ bool Module::clonePublicInterface()
|
|||
if (moduleScope->varargPack)
|
||||
moduleScope->varargPack = clone(*moduleScope->varargPack, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (auto& pair : moduleScope->exportedTypeBindings)
|
||||
pair.second = clone(pair.second, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
for (auto& [name, tf] : moduleScope->exportedTypeBindings)
|
||||
tf = clone(tf, interfaceTypes, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
for (TypeId ty : moduleScope->returnType)
|
||||
if (get<GenericTypeVar>(follow(ty)))
|
||||
|
|
|
@ -24,7 +24,7 @@ std::optional<LValue> tryGetLValue(const AstExpr& node)
|
|||
else if (auto indexexpr = expr->as<AstExprIndexExpr>())
|
||||
{
|
||||
if (auto lvalue = tryGetLValue(*indexexpr->expr))
|
||||
if (auto string = indexexpr->expr->as<AstExprConstantString>())
|
||||
if (auto string = indexexpr->index->as<AstExprConstantString>())
|
||||
return Field{std::make_shared<LValue>(*lvalue), std::string(string->value.data, string->value.size)};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
LUAU_FASTFLAG(LuauOccursCheckOkWithRecursiveFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFunctionArgumentNameSize, false)
|
||||
|
||||
/*
|
||||
* Prefix generic typenames with gen-
|
||||
* Additionally, free types will be prefixed with free- and suffixed with their level. eg free-a-4
|
||||
* Fair warning: Setting this will break a lot of Luau unit tests.
|
||||
*/
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauVerboseTypeNames, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -290,7 +297,15 @@ struct TypeVarStringifier
|
|||
void operator()(TypeId ty, const Unifiable::Free& ftv)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
if (FFlag::DebugLuauVerboseTypeNames)
|
||||
state.emit("free-");
|
||||
state.emit(state.getName(ty));
|
||||
|
||||
if (FFlag::DebugLuauVerboseTypeNames)
|
||||
{
|
||||
state.emit("-");
|
||||
state.emit(std::to_string(ftv.level.level));
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(TypeId, const BoundTypeVar& btv)
|
||||
|
@ -802,6 +817,8 @@ struct TypePackStringifier
|
|||
|
||||
void operator()(TypePackId tp, const GenericTypePack& pack)
|
||||
{
|
||||
if (FFlag::DebugLuauVerboseTypeNames)
|
||||
state.emit("gen-");
|
||||
if (pack.explicitName)
|
||||
{
|
||||
state.result.nameMap.typePacks[tp] = pack.name;
|
||||
|
@ -817,7 +834,16 @@ struct TypePackStringifier
|
|||
void operator()(TypePackId tp, const FreeTypePack& pack)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
if (FFlag::DebugLuauVerboseTypeNames)
|
||||
state.emit("free-");
|
||||
state.emit(state.getName(tp));
|
||||
|
||||
if (FFlag::DebugLuauVerboseTypeNames)
|
||||
{
|
||||
state.emit("-");
|
||||
state.emit(std::to_string(pack.level.level));
|
||||
}
|
||||
|
||||
state.emit("...");
|
||||
}
|
||||
|
||||
|
@ -1181,20 +1207,24 @@ std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeV
|
|||
return s;
|
||||
}
|
||||
|
||||
void dump(TypeId ty)
|
||||
std::string dump(TypeId ty)
|
||||
{
|
||||
ToStringOptions opts;
|
||||
opts.exhaustive = true;
|
||||
opts.functionTypeArguments = true;
|
||||
printf("%s\n", toString(ty, opts).c_str());
|
||||
std::string s = toString(ty, opts);
|
||||
printf("%s\n", s.c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
void dump(TypePackId ty)
|
||||
std::string dump(TypePackId ty)
|
||||
{
|
||||
ToStringOptions opts;
|
||||
opts.exhaustive = true;
|
||||
opts.functionTypeArguments = true;
|
||||
printf("%s\n", toString(ty, opts).c_str());
|
||||
std::string s = toString(ty, opts);
|
||||
printf("%s\n", s.c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string generateName(size_t i)
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#include "Luau/Scope.h"
|
||||
#include "Luau/Substitution.h"
|
||||
#include "Luau/TopoSortStatements.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/TimeTrace.h"
|
||||
|
||||
|
@ -29,7 +29,6 @@ LUAU_FASTFLAGVARIABLE(LuauCloneCorrectlyBeforeMutatingTableType, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauStoreMatchingOverloadFnType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpressionAnalysisSupport, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStrictRequire, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSingletonTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
||||
|
@ -37,6 +36,12 @@ LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTailArgumentTypeInfo, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauModuleRequireErrorPack, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefiLookupFromIndexExpr, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauProperTypeLevels, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAscribeCorrectLevelToInferredProperitesOfFreeTables, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixRecursiveMetatableCall, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalAsExpr, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUpdateFunctionNameBinding, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -206,14 +211,14 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan
|
|||
: resolver(resolver)
|
||||
, iceHandler(iceHandler)
|
||||
, unifierState(iceHandler)
|
||||
, nilType(singletonTypes.nilType)
|
||||
, numberType(singletonTypes.numberType)
|
||||
, stringType(singletonTypes.stringType)
|
||||
, booleanType(singletonTypes.booleanType)
|
||||
, threadType(singletonTypes.threadType)
|
||||
, anyType(singletonTypes.anyType)
|
||||
, optionalNumberType(singletonTypes.optionalNumberType)
|
||||
, anyTypePack(singletonTypes.anyTypePack)
|
||||
, nilType(getSingletonTypes().nilType)
|
||||
, numberType(getSingletonTypes().numberType)
|
||||
, stringType(getSingletonTypes().stringType)
|
||||
, booleanType(getSingletonTypes().booleanType)
|
||||
, threadType(getSingletonTypes().threadType)
|
||||
, anyType(getSingletonTypes().anyType)
|
||||
, optionalNumberType(getSingletonTypes().optionalNumberType)
|
||||
, anyTypePack(getSingletonTypes().anyTypePack)
|
||||
{
|
||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
|
||||
|
@ -443,7 +448,7 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
|
|||
functionDecls[*protoIter] = pair;
|
||||
++subLevel;
|
||||
|
||||
TypeId leftType = checkFunctionName(scope, *fun->name);
|
||||
TypeId leftType = checkFunctionName(scope, *fun->name, funScope->level);
|
||||
unify(leftType, funTy, fun->location);
|
||||
}
|
||||
else if (auto fun = (*protoIter)->as<AstStatLocalFunction>())
|
||||
|
@ -711,14 +716,15 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
|||
}
|
||||
else if (auto tail = valueIter.tail())
|
||||
{
|
||||
if (get<Unifiable::Error>(*tail))
|
||||
TypePackId tailPack = follow(*tail);
|
||||
if (get<Unifiable::Error>(tailPack))
|
||||
right = errorRecoveryType(scope);
|
||||
else if (auto vtp = get<VariadicTypePack>(*tail))
|
||||
else if (auto vtp = get<VariadicTypePack>(tailPack))
|
||||
right = vtp->ty;
|
||||
else if (get<Unifiable::Free>(*tail))
|
||||
else if (get<Unifiable::Free>(tailPack))
|
||||
{
|
||||
*asMutable(*tail) = TypePack{{left}};
|
||||
growingPack = getMutable<TypePack>(*tail);
|
||||
*asMutable(tailPack) = TypePack{{left}};
|
||||
growingPack = getMutable<TypePack>(tailPack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1107,8 +1113,27 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
|
||||
unify(leftType, ty, function.location);
|
||||
|
||||
if (leftTypeBinding)
|
||||
*leftTypeBinding = follow(quantify(funScope, leftType, function.name->location));
|
||||
if (FFlag::LuauUpdateFunctionNameBinding)
|
||||
{
|
||||
LUAU_ASSERT(function.name->is<AstExprIndexName>() || function.name->is<AstExprError>());
|
||||
|
||||
if (auto exprIndexName = function.name->as<AstExprIndexName>())
|
||||
{
|
||||
if (auto typeIt = currentModule->astTypes.find(exprIndexName->expr))
|
||||
{
|
||||
if (auto ttv = getMutableTableType(*typeIt))
|
||||
{
|
||||
if (auto it = ttv->props.find(exprIndexName->index.value); it != ttv->props.end())
|
||||
it->second.type = follow(quantify(funScope, leftType, function.name->location));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (leftTypeBinding)
|
||||
*leftTypeBinding = follow(quantify(funScope, leftType, function.name->location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,8 +1173,10 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
}
|
||||
else
|
||||
{
|
||||
ScopePtr aliasScope =
|
||||
FFlag::LuauQuantifyInPlace2 ? childScope(scope, typealias.location, subLevel) : childScope(scope, typealias.location);
|
||||
ScopePtr aliasScope = childScope(scope, typealias.location);
|
||||
aliasScope->level = scope->level.incr();
|
||||
if (FFlag::LuauProperTypeLevels)
|
||||
aliasScope->level.subLevel = subLevel;
|
||||
|
||||
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks);
|
||||
|
||||
|
@ -1166,6 +1193,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
ice("Not predeclared");
|
||||
|
||||
ScopePtr aliasScope = childScope(scope, typealias.location);
|
||||
aliasScope->level = scope->level.incr();
|
||||
|
||||
for (TypeId ty : binding->typeParams)
|
||||
{
|
||||
|
@ -1505,9 +1533,9 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprCa
|
|||
else if (auto vtp = get<VariadicTypePack>(retPack))
|
||||
return {vtp->ty, std::move(result.predicates)};
|
||||
else if (get<Unifiable::Generic>(retPack))
|
||||
ice("Unexpected abstract type pack!");
|
||||
ice("Unexpected abstract type pack!", expr.location);
|
||||
else
|
||||
ice("Unknown TypePack type!");
|
||||
ice("Unknown TypePack type!", expr.location);
|
||||
}
|
||||
|
||||
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprIndexName& expr)
|
||||
|
@ -1574,7 +1602,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
|||
}
|
||||
else if (tableType->state == TableState::Free)
|
||||
{
|
||||
TypeId result = freshType(scope);
|
||||
TypeId result = FFlag::LuauAscribeCorrectLevelToInferredProperitesOfFreeTables ? freshType(tableType->level) : freshType(scope);
|
||||
tableType->props[name] = {result};
|
||||
return result;
|
||||
}
|
||||
|
@ -1738,7 +1766,16 @@ TypeId TypeChecker::stripFromNilAndReport(TypeId ty, const Location& location)
|
|||
|
||||
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprIndexExpr& expr)
|
||||
{
|
||||
return {checkLValue(scope, expr)};
|
||||
TypeId ty = checkLValue(scope, expr);
|
||||
|
||||
if (FFlag::LuauRefiLookupFromIndexExpr)
|
||||
{
|
||||
if (std::optional<LValue> lvalue = tryGetLValue(expr))
|
||||
if (std::optional<TypeId> refiTy = resolveLValue(scope, *lvalue))
|
||||
return {*refiTy, {TruthyPredicate{std::move(*lvalue), expr.location}}};
|
||||
}
|
||||
|
||||
return {ty};
|
||||
}
|
||||
|
||||
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprFunction& expr, std::optional<TypeId> expectedType)
|
||||
|
@ -2421,12 +2458,27 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTy
|
|||
TypeId annotationType = resolveType(scope, *expr.annotation);
|
||||
ExprResult<TypeId> result = checkExpr(scope, *expr.expr, annotationType);
|
||||
|
||||
ErrorVec errorVec = canUnify(result.type, annotationType, expr.location);
|
||||
reportErrors(errorVec);
|
||||
if (!errorVec.empty())
|
||||
annotationType = errorRecoveryType(annotationType);
|
||||
if (FFlag::LuauBidirectionalAsExpr)
|
||||
{
|
||||
// Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case.
|
||||
if (canUnify(result.type, annotationType, expr.location).empty())
|
||||
return {annotationType, std::move(result.predicates)};
|
||||
|
||||
return {annotationType, std::move(result.predicates)};
|
||||
if (canUnify(annotationType, result.type, expr.location).empty())
|
||||
return {annotationType, std::move(result.predicates)};
|
||||
|
||||
reportError(expr.location, TypesAreUnrelated{result.type, annotationType});
|
||||
return {errorRecoveryType(annotationType), std::move(result.predicates)};
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorVec errorVec = canUnify(result.type, annotationType, expr.location);
|
||||
reportErrors(errorVec);
|
||||
if (!errorVec.empty())
|
||||
annotationType = errorRecoveryType(annotationType);
|
||||
|
||||
return {annotationType, std::move(result.predicates)};
|
||||
}
|
||||
}
|
||||
|
||||
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprError& expr)
|
||||
|
@ -2674,8 +2726,15 @@ std::pair<TypeId, TypeId*> TypeChecker::checkLValueBinding(const ScopePtr& scope
|
|||
|
||||
// Answers the question: "Can I define another function with this name?"
|
||||
// Primarily about detecting duplicates.
|
||||
TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
||||
TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, TypeLevel level)
|
||||
{
|
||||
auto freshTy = [&]() {
|
||||
if (FFlag::LuauProperTypeLevels)
|
||||
return freshType(level);
|
||||
else
|
||||
return freshType(scope);
|
||||
};
|
||||
|
||||
if (auto globalName = funName.as<AstExprGlobal>())
|
||||
{
|
||||
const ScopePtr& globalScope = currentModule->getModuleScope();
|
||||
|
@ -2689,7 +2748,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
|||
}
|
||||
else
|
||||
{
|
||||
TypeId ty = freshType(scope);
|
||||
TypeId ty = freshTy();
|
||||
globalScope->bindings[name] = {ty, funName.location};
|
||||
return ty;
|
||||
}
|
||||
|
@ -2699,7 +2758,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
|||
Symbol name = localName->local;
|
||||
Binding& binding = scope->bindings[name];
|
||||
if (binding.typeId == nullptr)
|
||||
binding = {freshType(scope), funName.location};
|
||||
binding = {freshTy(), funName.location};
|
||||
|
||||
return binding.typeId;
|
||||
}
|
||||
|
@ -2730,7 +2789,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName)
|
|||
|
||||
Property& property = ttv->props[name];
|
||||
|
||||
property.type = freshType(scope);
|
||||
property.type = freshTy();
|
||||
property.location = indexName->indexLocation;
|
||||
ttv->methodDefinitionLocations[name] = funName.location;
|
||||
return property.type;
|
||||
|
@ -3327,7 +3386,7 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
|
|||
fn = follow(fn);
|
||||
|
||||
if (auto ret = checkCallOverload(
|
||||
scope, expr, fn, retPack, argPack, args, argLocations, argListResult, overloadsThatMatchArgCount, overloadsThatDont, errors))
|
||||
scope, expr, fn, retPack, argPack, args, &argLocations, argListResult, overloadsThatMatchArgCount, overloadsThatDont, errors))
|
||||
return *ret;
|
||||
}
|
||||
|
||||
|
@ -3402,9 +3461,11 @@ std::vector<std::optional<TypeId>> TypeChecker::getExpectedTypesForCall(const st
|
|||
}
|
||||
|
||||
std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack,
|
||||
TypePackId argPack, TypePack* args, const std::vector<Location>& argLocations, const ExprResult<TypePackId>& argListResult,
|
||||
TypePackId argPack, TypePack* args, const std::vector<Location>* argLocations, const ExprResult<TypePackId>& argListResult,
|
||||
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<TypeId>& overloadsThatDont, std::vector<OverloadErrorEntry>& errors)
|
||||
{
|
||||
LUAU_ASSERT(argLocations);
|
||||
|
||||
fn = stripFromNilAndReport(fn, expr.func->location);
|
||||
|
||||
if (get<AnyTypeVar>(fn))
|
||||
|
@ -3428,31 +3489,44 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
|
|||
return {{retPack}};
|
||||
}
|
||||
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fn);
|
||||
if (!ftv)
|
||||
std::vector<Location> metaArgLocations;
|
||||
|
||||
// Might be a callable table
|
||||
if (const MetatableTypeVar* mttv = get<MetatableTypeVar>(fn))
|
||||
{
|
||||
// Might be a callable table
|
||||
if (const MetatableTypeVar* mttv = get<MetatableTypeVar>(fn))
|
||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, false))
|
||||
{
|
||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, false))
|
||||
// Construct arguments with 'self' added in front
|
||||
TypePackId metaCallArgPack = addTypePack(TypePackVar(TypePack{args->head, args->tail}));
|
||||
|
||||
TypePack* metaCallArgs = getMutable<TypePack>(metaCallArgPack);
|
||||
metaCallArgs->head.insert(metaCallArgs->head.begin(), fn);
|
||||
|
||||
metaArgLocations = *argLocations;
|
||||
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
||||
|
||||
if (FFlag::LuauFixRecursiveMetatableCall)
|
||||
{
|
||||
// Construct arguments with 'self' added in front
|
||||
TypePackId metaCallArgPack = addTypePack(TypePackVar(TypePack{args->head, args->tail}));
|
||||
|
||||
TypePack* metaCallArgs = getMutable<TypePack>(metaCallArgPack);
|
||||
metaCallArgs->head.insert(metaCallArgs->head.begin(), fn);
|
||||
|
||||
std::vector<Location> metaArgLocations = argLocations;
|
||||
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
||||
fn = instantiate(scope, *ty, expr.func->location);
|
||||
|
||||
argPack = metaCallArgPack;
|
||||
args = metaCallArgs;
|
||||
argLocations = &metaArgLocations;
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId fn = *ty;
|
||||
fn = instantiate(scope, fn, expr.func->location);
|
||||
|
||||
return checkCallOverload(scope, expr, fn, retPack, metaCallArgPack, metaCallArgs, metaArgLocations, argListResult,
|
||||
return checkCallOverload(scope, expr, fn, retPack, metaCallArgPack, metaCallArgs, &metaArgLocations, argListResult,
|
||||
overloadsThatMatchArgCount, overloadsThatDont, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fn);
|
||||
if (!ftv)
|
||||
{
|
||||
reportError(TypeError{expr.func->location, CannotCallNonFunction{fn}});
|
||||
unify(retPack, errorRecoveryTypePack(scope), expr.func->location);
|
||||
return {{errorRecoveryTypePack(retPack)}};
|
||||
|
@ -3477,7 +3551,7 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
|
|||
return {};
|
||||
}
|
||||
|
||||
checkArgumentList(scope, state, argPack, ftv->argTypes, argLocations);
|
||||
checkArgumentList(scope, state, argPack, ftv->argTypes, *argLocations);
|
||||
|
||||
if (!state.errors.empty())
|
||||
{
|
||||
|
@ -3772,7 +3846,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
|||
|
||||
if (moduleInfo.name.empty())
|
||||
{
|
||||
if (FFlag::LuauStrictRequire && currentModule->mode == Mode::Strict)
|
||||
if (currentModule->mode == Mode::Strict)
|
||||
{
|
||||
reportError(TypeError{location, UnknownRequire{}});
|
||||
return errorRecoveryType(anyType);
|
||||
|
@ -4268,9 +4342,11 @@ ScopePtr TypeChecker::childFunctionScope(const ScopePtr& parent, const Location&
|
|||
}
|
||||
|
||||
// Creates a new Scope and carries forward the varargs from the parent.
|
||||
ScopePtr TypeChecker::childScope(const ScopePtr& parent, const Location& location, int subLevel)
|
||||
ScopePtr TypeChecker::childScope(const ScopePtr& parent, const Location& location)
|
||||
{
|
||||
ScopePtr scope = std::make_shared<Scope>(parent, subLevel);
|
||||
ScopePtr scope = std::make_shared<Scope>(parent);
|
||||
if (FFlag::LuauProperTypeLevels)
|
||||
scope->level = parent->level;
|
||||
scope->varargPack = parent->varargPack;
|
||||
|
||||
currentModule->scopes.push_back(std::make_pair(location, scope));
|
||||
|
@ -4329,22 +4405,22 @@ TypeId TypeChecker::singletonType(std::string value)
|
|||
|
||||
TypeId TypeChecker::errorRecoveryType(const ScopePtr& scope)
|
||||
{
|
||||
return singletonTypes.errorRecoveryType();
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
}
|
||||
|
||||
TypeId TypeChecker::errorRecoveryType(TypeId guess)
|
||||
{
|
||||
return singletonTypes.errorRecoveryType(guess);
|
||||
return getSingletonTypes().errorRecoveryType(guess);
|
||||
}
|
||||
|
||||
TypePackId TypeChecker::errorRecoveryTypePack(const ScopePtr& scope)
|
||||
{
|
||||
return singletonTypes.errorRecoveryTypePack();
|
||||
return getSingletonTypes().errorRecoveryTypePack();
|
||||
}
|
||||
|
||||
TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess)
|
||||
{
|
||||
return singletonTypes.errorRecoveryTypePack(guess);
|
||||
return getSingletonTypes().errorRecoveryTypePack(guess);
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate)
|
||||
|
@ -4547,6 +4623,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
|||
else if (const auto& func = annotation.as<AstTypeFunction>())
|
||||
{
|
||||
ScopePtr funcScope = childScope(scope, func->location);
|
||||
funcScope->level = scope->level.incr();
|
||||
|
||||
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ std::optional<TypeId> findMetatableEntry(ErrorVec& errors, const ScopePtr& globa
|
|||
TypeId unwrapped = follow(*metatable);
|
||||
|
||||
if (get<AnyTypeVar>(unwrapped))
|
||||
return singletonTypes.anyType;
|
||||
return getSingletonTypes().anyType;
|
||||
|
||||
const TableTypeVar* mtt = getTableType(unwrapped);
|
||||
if (!mtt)
|
||||
|
@ -61,12 +61,12 @@ std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, const Sc
|
|||
{
|
||||
std::optional<TypeId> r = first(follow(itf->retType));
|
||||
if (!r)
|
||||
return singletonTypes.nilType;
|
||||
return getSingletonTypes().nilType;
|
||||
else
|
||||
return *r;
|
||||
}
|
||||
else if (get<AnyTypeVar>(index))
|
||||
return singletonTypes.anyType;
|
||||
return getSingletonTypes().anyType;
|
||||
else
|
||||
errors.push_back(TypeError{location, GenericError{"__index should either be a function or table. Got " + toString(index)}});
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
|||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefactorTagging, false)
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -579,11 +580,25 @@ SingletonTypes::SingletonTypes()
|
|||
, arena(new TypeArena)
|
||||
{
|
||||
TypeId stringMetatable = makeStringMetatable();
|
||||
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, makeStringMetatable()};
|
||||
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, stringMetatable};
|
||||
persist(stringMetatable);
|
||||
|
||||
debugFreezeArena = FFlag::DebugLuauFreezeArena;
|
||||
freeze(*arena);
|
||||
}
|
||||
|
||||
SingletonTypes::~SingletonTypes()
|
||||
{
|
||||
// Destroy the arena with the same memory management flags it was created with
|
||||
bool prevFlag = FFlag::DebugLuauFreezeArena;
|
||||
FFlag::DebugLuauFreezeArena.value = debugFreezeArena;
|
||||
|
||||
unfreeze(*arena);
|
||||
arena.reset(nullptr);
|
||||
|
||||
FFlag::DebugLuauFreezeArena.value = prevFlag;
|
||||
}
|
||||
|
||||
TypeId SingletonTypes::makeStringMetatable()
|
||||
{
|
||||
const TypeId optionalNumber = arena->addType(UnionTypeVar{{nilType, numberType}});
|
||||
|
@ -641,6 +656,9 @@ TypeId SingletonTypes::makeStringMetatable()
|
|||
|
||||
TypeId tableType = arena->addType(TableTypeVar{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
|
||||
if (TableTypeVar* ttv = getMutable<TableTypeVar>(tableType))
|
||||
ttv->name = "string";
|
||||
|
||||
return arena->addType(TableTypeVar{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
}
|
||||
|
||||
|
@ -670,7 +688,11 @@ TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess)
|
|||
return &errorTypePack_;
|
||||
}
|
||||
|
||||
SingletonTypes singletonTypes;
|
||||
SingletonTypes& getSingletonTypes()
|
||||
{
|
||||
static SingletonTypes singletonTypes;
|
||||
return singletonTypes;
|
||||
}
|
||||
|
||||
void persist(TypeId ty)
|
||||
{
|
||||
|
@ -719,6 +741,18 @@ void persist(TypeId ty)
|
|||
for (TypeId opt : itv->parts)
|
||||
queue.push_back(opt);
|
||||
}
|
||||
else if (auto mtv = get<MetatableTypeVar>(t))
|
||||
{
|
||||
queue.push_back(mtv->table);
|
||||
queue.push_back(mtv->metatable);
|
||||
}
|
||||
else if (get<GenericTypeVar>(t) || get<AnyTypeVar>(t) || get<FreeTypeVar>(t) || get<SingletonTypeVar>(t) || get<PrimitiveTypeVar>(t))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"TypeId is not supported in a persist call");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -736,6 +770,17 @@ void persist(TypePackId tp)
|
|||
if (p->tail)
|
||||
persist(*p->tail);
|
||||
}
|
||||
else if (auto vtp = get<VariadicTypePack>(tp))
|
||||
{
|
||||
persist(vtp->ty);
|
||||
}
|
||||
else if (get<GenericTypePack>(tp))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!"TypePackId is not supported in a persist call");
|
||||
}
|
||||
}
|
||||
|
||||
const TypeLevel* getLevel(TypeId ty)
|
||||
|