Sync to upstream/release/548 (#699)
- Fix rare type checking bugs with invalid generic types escaping the module scope - Fix type checking of variadic type packs in certain cases - Implement type normalization, which resolves a large set of various issues with unions/intersections in type checker - Improve parse errors for trailing commas in function calls and type lists - Reduce profiling skew when using --profile with very high frequencies - Improve performance of `lua_getinfo` (`debug.info`, `debug.traceback` and profiling overhead are now 20% faster/smaller) - Improve performance of polymorphic comparisons (1-2% lift on some benchmarks) - Improve performance of closure creation (1-2% lift on some benchmarks) - Improve string comparison performance (4% lift on string sorting)
This commit is contained in:
parent
cc26ef16df
commit
d5a2a1585e
|
@ -7,6 +7,7 @@
|
|||
#include "Luau/Constraint.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Normalize.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -44,6 +45,7 @@ struct ConstraintSolver
|
|||
TypeArena* arena;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
InternalErrorReporter iceReporter;
|
||||
NotNull<Normalizer> normalizer;
|
||||
// The entire set of constraints that the solver is trying to resolve.
|
||||
std::vector<NotNull<Constraint>> constraints;
|
||||
NotNull<Scope> rootScope;
|
||||
|
@ -74,9 +76,12 @@ struct ConstraintSolver
|
|||
|
||||
DcrLogger* logger;
|
||||
|
||||
explicit ConstraintSolver(TypeArena* arena, NotNull<SingletonTypes> singletonTypes, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger);
|
||||
|
||||
// Randomize the order in which to dispatch constraints
|
||||
void randomize(unsigned seed);
|
||||
|
||||
/**
|
||||
* Attempts to dispatch all pending constraints and reach a type solution
|
||||
* that satisfies all of the constraints.
|
||||
|
@ -85,8 +90,9 @@ struct ConstraintSolver
|
|||
|
||||
bool done();
|
||||
|
||||
/** Attempt to dispatch a constraint. Returns true if it was successful.
|
||||
* If tryDispatch() returns false, the constraint remains in the unsolved set and will be retried later.
|
||||
/** Attempt to dispatch a constraint. Returns true if it was successful. If
|
||||
* tryDispatch() returns false, the constraint remains in the unsolved set
|
||||
* and will be retried later.
|
||||
*/
|
||||
bool tryDispatch(NotNull<const Constraint> c, bool force);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ struct TypeMismatch
|
|||
TypeMismatch() = default;
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType);
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error);
|
||||
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error);
|
||||
|
||||
TypeId wantedType = nullptr;
|
||||
TypeId givenType = nullptr;
|
||||
|
|
|
@ -83,8 +83,13 @@ struct FrontendOptions
|
|||
// is complete.
|
||||
bool retainFullTypeGraphs = false;
|
||||
|
||||
// Run typechecking only in mode required for autocomplete (strict mode in order to get more precise type information)
|
||||
// Run typechecking only in mode required for autocomplete (strict mode in
|
||||
// order to get more precise type information)
|
||||
bool forAutocomplete = false;
|
||||
|
||||
// If not empty, randomly shuffle the constraint set before attempting to
|
||||
// solve. Use this value to seed the random number generator.
|
||||
std::optional<unsigned> randomizeConstraintResolutionSeed;
|
||||
};
|
||||
|
||||
struct CheckResult
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/UnifierSharedState.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -29,4 +29,231 @@ std::pair<TypePackId, bool> normalize(
|
|||
std::pair<TypePackId, bool> normalize(TypePackId ty, NotNull<Module> module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, const ModulePtr& module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
|
||||
class TypeIds
|
||||
{
|
||||
private:
|
||||
std::unordered_set<TypeId> types;
|
||||
std::vector<TypeId> order;
|
||||
std::size_t hash = 0;
|
||||
|
||||
public:
|
||||
using iterator = std::vector<TypeId>::iterator;
|
||||
using const_iterator = std::vector<TypeId>::const_iterator;
|
||||
|
||||
TypeIds(const TypeIds&) = delete;
|
||||
TypeIds(TypeIds&&) = default;
|
||||
TypeIds() = default;
|
||||
~TypeIds() = default;
|
||||
TypeIds& operator=(TypeIds&&) = default;
|
||||
|
||||
void insert(TypeId ty);
|
||||
/// Erase every element that does not also occur in tys
|
||||
void retain(const TypeIds& tys);
|
||||
void clear();
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator erase(const_iterator it);
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
size_t count(TypeId ty) const;
|
||||
|
||||
template<class Iterator>
|
||||
void insert(Iterator begin, Iterator end)
|
||||
{
|
||||
for (Iterator it = begin; it != end; ++it)
|
||||
insert(*it);
|
||||
}
|
||||
|
||||
bool operator ==(const TypeIds& there) const;
|
||||
size_t getHash() const;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
||||
template<> struct std::hash<Luau::TypeIds>
|
||||
{
|
||||
std::size_t operator()(const Luau::TypeIds& tys) const
|
||||
{
|
||||
return tys.getHash();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct std::hash<const Luau::TypeIds*>
|
||||
{
|
||||
std::size_t operator()(const Luau::TypeIds* tys) const
|
||||
{
|
||||
return tys->getHash();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct std::equal_to<Luau::TypeIds>
|
||||
{
|
||||
bool operator()(const Luau::TypeIds& here, const Luau::TypeIds& there) const
|
||||
{
|
||||
return here == there;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct std::equal_to<const Luau::TypeIds*>
|
||||
{
|
||||
bool operator()(const Luau::TypeIds* here, const Luau::TypeIds* there) const
|
||||
{
|
||||
return *here == *there;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
// A normalized string type is either `string` (represented by `nullopt`)
|
||||
// or a union of string singletons.
|
||||
using NormalizedStringType = std::optional<std::map<std::string, TypeId>>;
|
||||
|
||||
// A normalized function type is either `never` (represented by `nullopt`)
|
||||
// or an intersection of function types.
|
||||
// NOTE: type normalization can fail on function types with generics
|
||||
// (e.g. because we do not support unions and intersections of generic type packs),
|
||||
// so this type may contain `error`.
|
||||
using NormalizedFunctionType = std::optional<TypeIds>;
|
||||
|
||||
// A normalized generic/free type is a union, where each option is of the form (X & T) where
|
||||
// * X is either a free type or a generic
|
||||
// * T is a normalized type.
|
||||
struct NormalizedType;
|
||||
using NormalizedTyvars = std::unordered_map<TypeId, std::unique_ptr<NormalizedType>>;
|
||||
|
||||
// A normalized type is either any, unknown, or one of the form P | T | F | G where
|
||||
// * P is a union of primitive types (including singletons, classes and the error type)
|
||||
// * T is a union of table types
|
||||
// * F is a union of an intersection of function types
|
||||
// * G is a union of generic/free normalized types, intersected with a normalized type
|
||||
struct NormalizedType
|
||||
{
|
||||
// The top part of the type.
|
||||
// This type is either never, unknown, or any.
|
||||
// If this type is not never, all the other fields are null.
|
||||
TypeId tops;
|
||||
|
||||
// The boolean part of the type.
|
||||
// This type is either never, boolean type, or a boolean singleton.
|
||||
TypeId booleans;
|
||||
|
||||
// The class part of the type.
|
||||
// Each element of this set is a class, and none of the classes are subclasses of each other.
|
||||
TypeIds classes;
|
||||
|
||||
// The error part of the type.
|
||||
// This type is either never or the error type.
|
||||
TypeId errors;
|
||||
|
||||
// The nil part of the type.
|
||||
// This type is either never or nil.
|
||||
TypeId nils;
|
||||
|
||||
// The number part of the type.
|
||||
// This type is either never or number.
|
||||
TypeId numbers;
|
||||
|
||||
// The string part of the type.
|
||||
// This may be the `string` type, or a union of singletons.
|
||||
NormalizedStringType strings = std::map<std::string,TypeId>{};
|
||||
|
||||
// The thread part of the type.
|
||||
// This type is either never or thread.
|
||||
TypeId threads;
|
||||
|
||||
// The (meta)table part of the type.
|
||||
// Each element of this set is a (meta)table type.
|
||||
TypeIds tables;
|
||||
|
||||
// The function part of the type.
|
||||
NormalizedFunctionType functions;
|
||||
|
||||
// The generic/free part of the type.
|
||||
NormalizedTyvars tyvars;
|
||||
|
||||
NormalizedType(NotNull<SingletonTypes> singletonTypes);
|
||||
|
||||
NormalizedType(const NormalizedType&) = delete;
|
||||
NormalizedType(NormalizedType&&) = default;
|
||||
NormalizedType() = delete;
|
||||
~NormalizedType() = default;
|
||||
NormalizedType& operator=(NormalizedType&&) = default;
|
||||
NormalizedType& operator=(NormalizedType&) = delete;
|
||||
};
|
||||
|
||||
class Normalizer
|
||||
{
|
||||
std::unordered_map<TypeId, std::unique_ptr<NormalizedType>> cachedNormals;
|
||||
std::unordered_map<const TypeIds*, TypeId> cachedIntersections;
|
||||
std::unordered_map<const TypeIds*, TypeId> cachedUnions;
|
||||
std::unordered_map<const TypeIds*, std::unique_ptr<TypeIds>> cachedTypeIds;
|
||||
bool withinResourceLimits();
|
||||
|
||||
public:
|
||||
TypeArena* arena;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
NotNull<UnifierSharedState> sharedState;
|
||||
|
||||
Normalizer(TypeArena* arena, NotNull<SingletonTypes> singletonTypes, NotNull<UnifierSharedState> sharedState);
|
||||
Normalizer(const Normalizer&) = delete;
|
||||
Normalizer(Normalizer&&) = delete;
|
||||
Normalizer() = delete;
|
||||
~Normalizer() = default;
|
||||
Normalizer& operator=(Normalizer&&) = delete;
|
||||
Normalizer& operator=(Normalizer&) = delete;
|
||||
|
||||
// If this returns null, the typechecker should emit a "too complex" error
|
||||
const NormalizedType* normalize(TypeId ty);
|
||||
void clearNormal(NormalizedType& norm);
|
||||
|
||||
// ------- Cached TypeIds
|
||||
TypeId unionType(TypeId here, TypeId there);
|
||||
TypeId intersectionType(TypeId here, TypeId there);
|
||||
const TypeIds* cacheTypeIds(TypeIds tys);
|
||||
void clearCaches();
|
||||
|
||||
// ------- Normalizing unions
|
||||
void unionTysWithTy(TypeIds& here, TypeId there);
|
||||
TypeId unionOfTops(TypeId here, TypeId there);
|
||||
TypeId unionOfBools(TypeId here, TypeId there);
|
||||
void unionClassesWithClass(TypeIds& heres, TypeId there);
|
||||
void unionClasses(TypeIds& heres, const TypeIds& theres);
|
||||
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
|
||||
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
|
||||
std::optional<TypeId> unionSaturatedFunctions(TypeId here, TypeId there);
|
||||
void unionFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
|
||||
void unionFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
|
||||
void unionTablesWithTable(TypeIds& heres, TypeId there);
|
||||
void unionTables(TypeIds& heres, const TypeIds& theres);
|
||||
bool unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
|
||||
bool unionNormalWithTy(NormalizedType& here, TypeId there, int ignoreSmallerTyvars = -1);
|
||||
|
||||
// ------- Normalizing intersections
|
||||
void intersectTysWithTy(TypeIds& here, TypeId there);
|
||||
TypeId intersectionOfTops(TypeId here, TypeId there);
|
||||
TypeId intersectionOfBools(TypeId here, TypeId there);
|
||||
void intersectClasses(TypeIds& heres, const TypeIds& theres);
|
||||
void intersectClassesWithClass(TypeIds& heres, TypeId there);
|
||||
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
|
||||
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there);
|
||||
void intersectTablesWithTable(TypeIds& heres, TypeId there);
|
||||
void intersectTables(TypeIds& heres, const TypeIds& theres);
|
||||
std::optional<TypeId> intersectionOfFunctions(TypeId here, TypeId there);
|
||||
void intersectFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
|
||||
void intersectFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
|
||||
bool intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there);
|
||||
bool intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
|
||||
bool intersectNormalWithTy(NormalizedType& here, TypeId there);
|
||||
|
||||
// -------- Convert back from a normalized type to a type
|
||||
TypeId typeFromNormal(const NormalizedType& norm);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -234,6 +234,8 @@ public:
|
|||
TypeId anyify(const ScopePtr& scope, TypeId ty, Location location);
|
||||
TypePackId anyify(const ScopePtr& scope, TypePackId ty, Location location);
|
||||
|
||||
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId ty);
|
||||
|
||||
void reportError(const TypeError& error);
|
||||
void reportError(const Location& location, TypeErrorData error);
|
||||
void reportErrors(const ErrorVec& errors);
|
||||
|
@ -359,6 +361,7 @@ public:
|
|||
InternalErrorReporter* iceHandler;
|
||||
|
||||
UnifierSharedState unifierState;
|
||||
Normalizer normalizer;
|
||||
|
||||
std::vector<RequireCycle> requireCycles;
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ struct Free
|
|||
bool forwardedTypeAlias = false;
|
||||
|
||||
private:
|
||||
static int nextIndex;
|
||||
static int DEPRECATED_nextIndex;
|
||||
};
|
||||
|
||||
template<typename Id>
|
||||
|
@ -127,7 +127,7 @@ struct Generic
|
|||
bool explicitName = false;
|
||||
|
||||
private:
|
||||
static int nextIndex;
|
||||
static int DEPRECATED_nextIndex;
|
||||
};
|
||||
|
||||
struct Error
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/UnifierSharedState.h"
|
||||
#include "Normalize.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
@ -52,6 +53,7 @@ struct Unifier
|
|||
{
|
||||
TypeArena* const types;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
NotNull<Normalizer> normalizer;
|
||||
Mode mode;
|
||||
|
||||
NotNull<Scope> scope; // const Scope maybe
|
||||
|
@ -60,13 +62,14 @@ struct Unifier
|
|||
Location location;
|
||||
Variance variance = Covariant;
|
||||
bool anyIsTop = false; // If true, we consider any to be a top type. If false, it is a familiar but weird mix of top and bottom all at once.
|
||||
bool normalize; // Normalize unions and intersections if necessary
|
||||
bool useScopes = false; // If true, we use the scope hierarchy rather than TypeLevels
|
||||
CountMismatch::Context ctx = CountMismatch::Arg;
|
||||
|
||||
UnifierSharedState& sharedState;
|
||||
|
||||
Unifier(TypeArena* types, NotNull<SingletonTypes> singletonTypes, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance,
|
||||
UnifierSharedState& sharedState, TxnLog* parentLog = nullptr);
|
||||
Unifier(NotNull<Normalizer> normalizer, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance,
|
||||
TxnLog* parentLog = nullptr);
|
||||
|
||||
// Test whether the two type vars unify. Never commits the result.
|
||||
ErrorVec canUnify(TypeId subTy, TypeId superTy);
|
||||
|
@ -84,6 +87,7 @@ private:
|
|||
void tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTypeVar* uv, bool cacheEnabled, bool isFunctionCall);
|
||||
void tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const IntersectionTypeVar* uv);
|
||||
void tryUnifyIntersectionWithType(TypeId subTy, const IntersectionTypeVar* uv, TypeId superTy, bool cacheEnabled, bool isFunctionCall);
|
||||
void tryUnifyNormalizedTypes(TypeId subTy, TypeId superTy, const NormalizedType& subNorm, const NormalizedType& superNorm, std::string reason, std::optional<TypeError> error = std::nullopt);
|
||||
void tryUnifyPrimitives(TypeId subTy, TypeId superTy);
|
||||
void tryUnifySingletons(TypeId subTy, TypeId superTy);
|
||||
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);
|
||||
|
@ -92,6 +96,8 @@ private:
|
|||
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
||||
|
||||
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
|
||||
|
||||
TypeId widen(TypeId ty);
|
||||
TypePackId widen(TypePackId tp);
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
||||
|
@ -139,7 +137,8 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
|||
{
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
Unifier unifier(typeArena, singletonTypes, Mode::Strict, scope, Location(), Variance::Covariant, unifierState);
|
||||
Normalizer normalizer{typeArena, singletonTypes, NotNull{&unifierState}};
|
||||
Unifier unifier(NotNull<Normalizer>{&normalizer}, Mode::Strict, scope, Location(), Variance::Covariant);
|
||||
|
||||
return unifier.canUnify(subTy, superTy).empty();
|
||||
}
|
||||
|
@ -151,18 +150,6 @@ static TypeCorrectKind checkTypeCorrectKind(
|
|||
|
||||
NotNull<Scope> moduleScope{module.getModuleScope().get()};
|
||||
|
||||
auto canUnify = [&typeArena, singletonTypes, moduleScope](TypeId subTy, TypeId superTy) {
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
Unifier unifier(typeArena, singletonTypes, Mode::Strict, moduleScope, Location(), Variance::Covariant, unifierState);
|
||||
|
||||
unifier.tryUnify(subTy, superTy);
|
||||
bool ok = unifier.errors.empty();
|
||||
return ok;
|
||||
};
|
||||
|
||||
auto typeAtPosition = findExpectedTypeAt(module, node, position);
|
||||
|
||||
if (!typeAtPosition)
|
||||
|
@ -170,30 +157,11 @@ static TypeCorrectKind checkTypeCorrectKind(
|
|||
|
||||
TypeId expectedType = follow(*typeAtPosition);
|
||||
|
||||
auto checkFunctionType = [typeArena, singletonTypes, moduleScope, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, singletonTypes);
|
||||
auto checkFunctionType = [typeArena, singletonTypes, moduleScope, &expectedType](const FunctionTypeVar* ftv) {
|
||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, singletonTypes);
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [retHead, retTail] = flatten(ftv->retTypes);
|
||||
|
||||
if (!retHead.empty() && canUnify(retHead.front(), expectedType))
|
||||
return true;
|
||||
|
||||
// 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(vtp->ty, expectedType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// We also want to suggest functions that return compatible result
|
||||
|
@ -212,11 +180,8 @@ static TypeCorrectKind checkTypeCorrectKind(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
return checkTypeMatch(ty, expectedType, NotNull{module.getModuleScope().get()}, typeArena, singletonTypes) ? TypeCorrectKind::Correct
|
||||
: TypeCorrectKind::None;
|
||||
else
|
||||
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
return checkTypeMatch(ty, expectedType, NotNull{module.getModuleScope().get()}, typeArena, singletonTypes) ? TypeCorrectKind::Correct
|
||||
: TypeCorrectKind::None;
|
||||
}
|
||||
|
||||
enum class PropIndexType
|
||||
|
@ -230,51 +195,14 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
PropIndexType indexType, const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
||||
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
|
||||
{
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
rootTy = follow(rootTy);
|
||||
|
||||
rootTy = follow(rootTy);
|
||||
ty = follow(ty);
|
||||
|
||||
if (seen.count(ty))
|
||||
return;
|
||||
seen.insert(ty);
|
||||
|
||||
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
if (indexType == PropIndexType::Key)
|
||||
return false;
|
||||
|
||||
bool colonIndex = indexType == PropIndexType::Colon;
|
||||
|
||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(type))
|
||||
{
|
||||
return useStrictFunctionIndexers ? colonIndex != ftv->hasSelf : false;
|
||||
}
|
||||
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(type))
|
||||
{
|
||||
bool allHaveSelf = true;
|
||||
for (auto subType : itv->parts)
|
||||
{
|
||||
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(Luau::follow(subType)))
|
||||
{
|
||||
allHaveSelf &= ftv->hasSelf;
|
||||
}
|
||||
else
|
||||
{
|
||||
return colonIndex;
|
||||
}
|
||||
}
|
||||
return useStrictFunctionIndexers ? colonIndex != allHaveSelf : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return colonIndex;
|
||||
}
|
||||
};
|
||||
auto isWrongIndexer = [typeArena, singletonTypes, &module, rootTy, indexType](Luau::TypeId type) {
|
||||
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
if (indexType == PropIndexType::Key)
|
||||
return false;
|
||||
|
||||
|
@ -337,7 +265,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
AutocompleteEntryKind::Property,
|
||||
type,
|
||||
prop.deprecated,
|
||||
FFlag::LuauSelfCallAutocompleteFix3 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
|
||||
isWrongIndexer(type),
|
||||
typeCorrect,
|
||||
containingClass,
|
||||
&prop,
|
||||
|
@ -380,31 +308,8 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
{
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, mt->table, indexType, nodes, result, seen);
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (auto mtable = get<TableTypeVar>(mt->metatable))
|
||||
fillMetatableProps(mtable);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto mtable = get<TableTypeVar>(mt->metatable);
|
||||
if (!mtable)
|
||||
return;
|
||||
|
||||
auto indexIt = mtable->props.find("__index");
|
||||
if (indexIt != mtable->props.end())
|
||||
{
|
||||
TypeId followed = follow(indexIt->second.type);
|
||||
if (get<TableTypeVar>(followed) || get<MetatableTypeVar>(followed))
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, followed, indexType, nodes, result, seen);
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(followed))
|
||||
{
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retTypes);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto mtable = get<TableTypeVar>(mt->metatable))
|
||||
fillMetatableProps(mtable);
|
||||
}
|
||||
else if (auto i = get<IntersectionTypeVar>(ty))
|
||||
{
|
||||
|
@ -446,9 +351,6 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
AutocompleteEntryMap inner;
|
||||
std::unordered_set<TypeId> innerSeen;
|
||||
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix3)
|
||||
innerSeen = seen;
|
||||
|
||||
if (isNil(*iter))
|
||||
{
|
||||
++iter;
|
||||
|
@ -472,7 +374,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
++iter;
|
||||
}
|
||||
}
|
||||
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix3)
|
||||
else if (auto pt = get<PrimitiveTypeVar>(ty))
|
||||
{
|
||||
if (pt->metatable)
|
||||
{
|
||||
|
@ -480,7 +382,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNul
|
|||
fillMetatableProps(mtable);
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauSelfCallAutocompleteFix3 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||
else if (get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||
{
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, singletonTypes->stringType, indexType, nodes, result, seen);
|
||||
}
|
||||
|
@ -1416,11 +1318,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
|||
TypeId ty = follow(*it);
|
||||
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
|
||||
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix3 && isString(ty))
|
||||
return {autocompleteProps(*module, &typeArena, singletonTypes, globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry),
|
||||
ancestry, AutocompleteContext::Property};
|
||||
else
|
||||
return {autocompleteProps(*module, &typeArena, singletonTypes, ty, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
||||
return {autocompleteProps(*module, &typeArena, singletonTypes, ty, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
||||
}
|
||||
else if (auto typeReference = node->as<AstTypeReference>())
|
||||
{
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "Luau/VisitTypeVar.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
|
||||
LUAU_FASTFLAG(LuauFixNameMaps)
|
||||
|
@ -251,10 +253,11 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
|||
}
|
||||
}
|
||||
|
||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<SingletonTypes> singletonTypes, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
ConstraintSolver::ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger)
|
||||
: arena(arena)
|
||||
, singletonTypes(singletonTypes)
|
||||
: arena(normalizer->arena)
|
||||
, singletonTypes(normalizer->singletonTypes)
|
||||
, normalizer(normalizer)
|
||||
, constraints(collectConstraints(rootScope))
|
||||
, rootScope(rootScope)
|
||||
, currentModuleName(std::move(moduleName))
|
||||
|
@ -278,6 +281,12 @@ ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<SingletonTypes> sin
|
|||
LUAU_ASSERT(logger);
|
||||
}
|
||||
|
||||
void ConstraintSolver::randomize(unsigned seed)
|
||||
{
|
||||
std::mt19937 g(seed);
|
||||
std::shuffle(begin(unsolvedConstraints), end(unsolvedConstraints), g);
|
||||
}
|
||||
|
||||
void ConstraintSolver::run()
|
||||
{
|
||||
if (done())
|
||||
|
@ -1355,8 +1364,7 @@ bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
|
|||
|
||||
void ConstraintSolver::unify(TypeId subType, TypeId superType, NotNull<Scope> scope)
|
||||
{
|
||||
UnifierSharedState sharedState{&iceReporter};
|
||||
Unifier u{arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{normalizer, Mode::Strict, scope, Location{}, Covariant};
|
||||
u.useScopes = true;
|
||||
|
||||
u.tryUnify(subType, superType);
|
||||
|
@ -1379,7 +1387,7 @@ void ConstraintSolver::unify(TypeId subType, TypeId superType, NotNull<Scope> sc
|
|||
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope)
|
||||
{
|
||||
UnifierSharedState sharedState{&iceReporter};
|
||||
Unifier u{arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{normalizer, Mode::Strict, scope, Location{}, Covariant};
|
||||
u.useScopes = true;
|
||||
|
||||
u.tryUnify(subPack, superPack);
|
||||
|
|
|
@ -511,11 +511,11 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reas
|
|||
{
|
||||
}
|
||||
|
||||
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error)
|
||||
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error)
|
||||
: wantedType(wantedType)
|
||||
, givenType(givenType)
|
||||
, reason(reason)
|
||||
, error(std::make_shared<TypeError>(std::move(error)))
|
||||
, error(error ? std::make_shared<TypeError>(std::move(*error)) : nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -860,12 +860,18 @@ ModulePtr Frontend::check(
|
|||
const NotNull<ModuleResolver> mr{forAutocomplete ? &moduleResolverForAutocomplete : &moduleResolver};
|
||||
const ScopePtr& globalScope{forAutocomplete ? typeCheckerForAutocomplete.globalScope : typeChecker.globalScope};
|
||||
|
||||
Normalizer normalizer{&result->internalTypes, singletonTypes, NotNull{&typeChecker.unifierState}};
|
||||
|
||||
ConstraintGraphBuilder cgb{
|
||||
sourceModule.name, result, &result->internalTypes, mr, singletonTypes, NotNull(&iceHandler), globalScope, logger.get()};
|
||||
cgb.visit(sourceModule.root);
|
||||
result->errors = std::move(cgb.errors);
|
||||
|
||||
ConstraintSolver cs{&result->internalTypes, singletonTypes, NotNull(cgb.rootScope), sourceModule.name, NotNull(&moduleResolver), requireCycles, logger.get()};
|
||||
ConstraintSolver cs{NotNull{&normalizer}, NotNull(cgb.rootScope), sourceModule.name, NotNull(&moduleResolver), requireCycles, logger.get()};
|
||||
|
||||
if (options.randomizeConstraintResolutionSeed)
|
||||
cs.randomize(*options.randomizeConstraintResolutionSeed);
|
||||
|
||||
cs.run();
|
||||
|
||||
for (TypeError& e : cs.errors)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauAnyifyModuleReturnGenerics)
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
|
||||
|
@ -285,13 +286,16 @@ void Module::clonePublicInterface(NotNull<SingletonTypes> singletonTypes, Intern
|
|||
}
|
||||
}
|
||||
|
||||
for (TypeId ty : returnType)
|
||||
if (!FFlag::LuauAnyifyModuleReturnGenerics)
|
||||
{
|
||||
if (get<GenericTypeVar>(follow(ty)))
|
||||
for (TypeId ty : returnType)
|
||||
{
|
||||
auto t = asMutable(ty);
|
||||
t->ty = AnyTypeVar{};
|
||||
t->normal = true;
|
||||
if (get<GenericTypeVar>(follow(ty)))
|
||||
{
|
||||
auto t = asMutable(ty);
|
||||
t->ty = AnyTypeVar{};
|
||||
t->normal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -280,7 +280,8 @@ struct TypeChecker2
|
|||
TypePackId actualRetType = reconstructPack(ret->list, arena);
|
||||
|
||||
UnifierSharedState sharedState{&ice};
|
||||
Unifier u{&arena, singletonTypes, Mode::Strict, stack.back(), ret->location, Covariant, sharedState};
|
||||
Normalizer normalizer{&arena, singletonTypes, NotNull{&sharedState}};
|
||||
Unifier u{NotNull{&normalizer}, Mode::Strict, stack.back(), ret->location, Covariant};
|
||||
u.anyIsTop = true;
|
||||
|
||||
u.tryUnify(actualRetType, expectedRetType);
|
||||
|
@ -1206,7 +1207,8 @@ struct TypeChecker2
|
|||
ErrorVec tryUnify(NotNull<Scope> scope, const Location& location, TID subTy, TID superTy)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
Unifier u{&module->internalTypes, singletonTypes, Mode::Strict, scope, location, Covariant, sharedState};
|
||||
Normalizer normalizer{&module->internalTypes, singletonTypes, NotNull{&sharedState}};
|
||||
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, location, Covariant};
|
||||
u.anyIsTop = true;
|
||||
u.tryUnify(subTy, superTy);
|
||||
|
||||
|
|
|
@ -32,19 +32,21 @@ LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300)
|
|||
LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
|
||||
LUAU_FASTFLAG(LuauTypeNormalization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFunctionArgMismatchDetails, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInplaceDemoteSkipAllBound, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAnyifyModuleReturnGenerics, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCallUnifyPackTails, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixVarargExprHeadType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnionOfTypesFollow, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportShadowedTypeAlias, false)
|
||||
|
@ -255,6 +257,7 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, NotNull<SingletonTypes> singl
|
|||
, singletonTypes(singletonTypes)
|
||||
, iceHandler(iceHandler)
|
||||
, unifierState(iceHandler)
|
||||
, normalizer(nullptr, singletonTypes, NotNull{&unifierState})
|
||||
, nilType(singletonTypes->nilType)
|
||||
, numberType(singletonTypes->numberType)
|
||||
, stringType(singletonTypes->stringType)
|
||||
|
@ -301,12 +304,13 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
|||
LUAU_TIMETRACE_SCOPE("TypeChecker::check", "TypeChecker");
|
||||
LUAU_TIMETRACE_ARGUMENT("module", module.name.c_str());
|
||||
|
||||
currentModule.reset(new Module());
|
||||
currentModule.reset(new Module);
|
||||
currentModule->type = module.type;
|
||||
currentModule->allocator = module.allocator;
|
||||
currentModule->names = module.names;
|
||||
|
||||
iceHandler->moduleName = module.name;
|
||||
normalizer.arena = ¤tModule->internalTypes;
|
||||
|
||||
if (FFlag::LuauAutocompleteDynamicLimits)
|
||||
{
|
||||
|
@ -351,15 +355,23 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
|||
if (get<FreeTypePack>(follow(moduleScope->returnType)))
|
||||
moduleScope->returnType = addTypePack(TypePack{{}, std::nullopt});
|
||||
else
|
||||
{
|
||||
moduleScope->returnType = anyify(moduleScope, moduleScope->returnType, Location{});
|
||||
}
|
||||
|
||||
if (FFlag::LuauAnyifyModuleReturnGenerics)
|
||||
moduleScope->returnType = anyifyModuleReturnTypePackGenerics(moduleScope->returnType);
|
||||
|
||||
for (auto& [_, typeFun] : moduleScope->exportedTypeBindings)
|
||||
typeFun.type = anyify(moduleScope, typeFun.type, Location{});
|
||||
|
||||
prepareErrorsForDisplay(currentModule->errors);
|
||||
|
||||
if (FFlag::LuauTypeNormalization2)
|
||||
{
|
||||
// Clear the normalizer caches, since they contain types from the internal type surface
|
||||
normalizer.clearCaches();
|
||||
normalizer.arena = nullptr;
|
||||
}
|
||||
|
||||
currentModule->clonePublicInterface(singletonTypes, *iceHandler);
|
||||
|
||||
// Clear unifier cache since it's keyed off internal types that get deallocated
|
||||
|
@ -474,7 +486,7 @@ struct InplaceDemoter : TypeVarOnceVisitor
|
|||
TypeArena* arena;
|
||||
|
||||
InplaceDemoter(TypeLevel level, TypeArena* arena)
|
||||
: TypeVarOnceVisitor(/* skipBoundTypes= */ FFlag::LuauInplaceDemoteSkipAllBound)
|
||||
: TypeVarOnceVisitor(/* skipBoundTypes= */ true)
|
||||
, newLevel(level)
|
||||
, arena(arena)
|
||||
{
|
||||
|
@ -494,12 +506,6 @@ struct InplaceDemoter : TypeVarOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const BoundTypeVar& btyRef) override
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauInplaceDemoteSkipAllBound);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
|
@ -1029,8 +1035,11 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assign)
|
|||
|
||||
if (right)
|
||||
{
|
||||
if (!maybeGeneric(left) && isGeneric(right))
|
||||
right = instantiate(scope, right, loc);
|
||||
if (!FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
if (!maybeGeneric(left) && isGeneric(right))
|
||||
right = instantiate(scope, right, loc);
|
||||
}
|
||||
|
||||
// Setting a table entry to nil doesn't mean nil is the type of the indexer, it is just deleting the entry
|
||||
const TableTypeVar* destTableTypeReceivingNil = nullptr;
|
||||
|
@ -1104,7 +1113,9 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
|||
variableTypes.push_back(ty);
|
||||
expectedTypes.push_back(ty);
|
||||
|
||||
instantiateGenerics.push_back(annotation != nullptr && !maybeGeneric(ty));
|
||||
// with FFlag::LuauInstantiateInSubtyping enabled, we shouldn't need to produce instantiateGenerics at all.
|
||||
if (!FFlag::LuauInstantiateInSubtyping)
|
||||
instantiateGenerics.push_back(annotation != nullptr && !maybeGeneric(ty));
|
||||
}
|
||||
|
||||
if (local.values.size > 0)
|
||||
|
@ -1729,9 +1740,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
|||
{
|
||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
ftv->hasSelf = true;
|
||||
ftv->hasSelf = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1905,8 +1914,18 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
|
||||
if (get<TypePack>(varargPack))
|
||||
{
|
||||
std::vector<TypeId> types = flatten(varargPack).first;
|
||||
return {!types.empty() ? types[0] : nilType};
|
||||
if (FFlag::LuauFixVarargExprHeadType)
|
||||
{
|
||||
if (std::optional<TypeId> ty = first(varargPack))
|
||||
return {*ty};
|
||||
|
||||
return {nilType};
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> types = flatten(varargPack).first;
|
||||
return {!types.empty() ? types[0] : nilType};
|
||||
}
|
||||
}
|
||||
else if (get<FreeTypePack>(varargPack))
|
||||
{
|
||||
|
@ -3967,7 +3986,10 @@ void TypeChecker::checkArgumentList(const ScopePtr& scope, const AstExpr& funNam
|
|||
}
|
||||
else
|
||||
{
|
||||
unifyWithInstantiationIfNeeded(*argIter, *paramIter, scope, state);
|
||||
if (FFlag::LuauInstantiateInSubtyping)
|
||||
state.tryUnify(*argIter, *paramIter, /*isFunctionCall*/ false);
|
||||
else
|
||||
unifyWithInstantiationIfNeeded(*argIter, *paramIter, scope, state);
|
||||
++argIter;
|
||||
++paramIter;
|
||||
}
|
||||
|
@ -4523,8 +4545,11 @@ WithPredicate<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, cons
|
|||
|
||||
TypeId actualType = substituteFreeForNil && expr->is<AstExprConstantNil>() ? freshType(scope) : type;
|
||||
|
||||
if (instantiateGenerics.size() > i && instantiateGenerics[i])
|
||||
actualType = instantiate(scope, actualType, expr->location);
|
||||
if (!FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
if (instantiateGenerics.size() > i && instantiateGenerics[i])
|
||||
actualType = instantiate(scope, actualType, expr->location);
|
||||
}
|
||||
|
||||
if (expectedType)
|
||||
{
|
||||
|
@ -4686,6 +4711,8 @@ bool TypeChecker::unifyWithInstantiationIfNeeded(TypeId subTy, TypeId superTy, c
|
|||
|
||||
void TypeChecker::unifyWithInstantiationIfNeeded(TypeId subTy, TypeId superTy, const ScopePtr& scope, Unifier& state)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauInstantiateInSubtyping);
|
||||
|
||||
if (!maybeGeneric(subTy))
|
||||
// Quick check to see if we definitely can't instantiate
|
||||
state.tryUnify(subTy, superTy, /*isFunctionCall*/ false);
|
||||
|
@ -4828,6 +4855,33 @@ TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location lo
|
|||
}
|
||||
}
|
||||
|
||||
TypePackId TypeChecker::anyifyModuleReturnTypePackGenerics(TypePackId tp)
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
|
||||
return get<GenericTypeVar>(vtp->ty) ? anyTypePack : tp;
|
||||
|
||||
if (!get<TypePack>(follow(tp)))
|
||||
return tp;
|
||||
|
||||
std::vector<TypeId> resultTypes;
|
||||
std::optional<TypePackId> resultTail;
|
||||
|
||||
TypePackIterator it = begin(tp);
|
||||
|
||||
for (TypePackIterator e = end(tp); it != e; ++it)
|
||||
{
|
||||
TypeId ty = follow(*it);
|
||||
resultTypes.push_back(get<GenericTypeVar>(ty) ? anyType : ty);
|
||||
}
|
||||
|
||||
if (std::optional<TypePackId> tail = it.tail())
|
||||
resultTail = anyifyModuleReturnTypePackGenerics(*tail);
|
||||
|
||||
return addTypePack(resultTypes, resultTail);
|
||||
}
|
||||
|
||||
void TypeChecker::reportError(const TypeError& error)
|
||||
{
|
||||
if (currentModule->mode == Mode::NoCheck)
|
||||
|
@ -4955,8 +5009,7 @@ void TypeChecker::merge(RefinementMap& l, const RefinementMap& r)
|
|||
|
||||
Unifier TypeChecker::mkUnifier(const ScopePtr& scope, const Location& location)
|
||||
{
|
||||
return Unifier{
|
||||
¤tModule->internalTypes, singletonTypes, currentModule->mode, NotNull{scope.get()}, location, Variance::Covariant, unifierState};
|
||||
return Unifier{NotNull{&normalizer}, currentModule->mode, NotNull{scope.get()}, location, Variance::Covariant};
|
||||
}
|
||||
|
||||
TypeId TypeChecker::freshType(const ScopePtr& scope)
|
||||
|
|
|
@ -27,6 +27,7 @@ LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
|||
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatArgumentErrorFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoMoreGlobalSingletonTypes, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -339,6 +340,8 @@ bool isSubset(const UnionTypeVar& super, const UnionTypeVar& sub)
|
|||
// then instantiate U if `isGeneric(U)` is true, and `maybeGeneric(T)` is false.
|
||||
bool isGeneric(TypeId ty)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauInstantiateInSubtyping);
|
||||
|
||||
ty = follow(ty);
|
||||
if (auto ftv = get<FunctionTypeVar>(ty))
|
||||
return ftv->generics.size() > 0 || ftv->genericPacks.size() > 0;
|
||||
|
@ -350,6 +353,8 @@ bool isGeneric(TypeId ty)
|
|||
|
||||
bool maybeGeneric(TypeId ty)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauInstantiateInSubtyping);
|
||||
|
||||
if (FFlag::LuauMaybeGenericIntersectionTypes)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
|
|
@ -1,60 +1,64 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/Unifiable.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeNormalization2);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Unifiable
|
||||
{
|
||||
|
||||
static int nextIndex = 0;
|
||||
|
||||
Free::Free(TypeLevel level)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, level(level)
|
||||
{
|
||||
}
|
||||
|
||||
Free::Free(Scope* scope)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
Free::Free(Scope* scope, TypeLevel level)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, level(level)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
int Free::nextIndex = 0;
|
||||
int Free::DEPRECATED_nextIndex = 0;
|
||||
|
||||
Generic::Generic()
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, name("g" + std::to_string(index))
|
||||
{
|
||||
}
|
||||
|
||||
Generic::Generic(TypeLevel level)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, level(level)
|
||||
, name("g" + std::to_string(index))
|
||||
{
|
||||
}
|
||||
|
||||
Generic::Generic(const Name& name)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, name(name)
|
||||
, explicitName(true)
|
||||
{
|
||||
}
|
||||
|
||||
Generic::Generic(Scope* scope)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
Generic::Generic(TypeLevel level, const Name& name)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, level(level)
|
||||
, name(name)
|
||||
, explicitName(true)
|
||||
|
@ -62,14 +66,14 @@ Generic::Generic(TypeLevel level, const Name& name)
|
|||
}
|
||||
|
||||
Generic::Generic(Scope* scope, const Name& name)
|
||||
: index(++nextIndex)
|
||||
: index(FFlag::LuauTypeNormalization2 ? ++nextIndex : ++DEPRECATED_nextIndex)
|
||||
, scope(scope)
|
||||
, name(name)
|
||||
, explicitName(true)
|
||||
{
|
||||
}
|
||||
|
||||
int Generic::nextIndex = 0;
|
||||
int Generic::DEPRECATED_nextIndex = 0;
|
||||
|
||||
Error::Error()
|
||||
: index(++nextIndex)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "Luau/Unifier.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Instantiation.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypePack.h"
|
||||
|
@ -20,7 +21,9 @@ LUAU_FASTINTVARIABLE(LuauTypeInferLowerBoundsIterationLimit, 2000);
|
|||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypeNormalizer, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
|
||||
LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
|
||||
LUAU_FASTFLAG(LuauCallUnifyPackTails)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
@ -343,17 +346,19 @@ static bool subsumes(bool useScopes, TY_A* left, TY_B* right)
|
|||
return left->level.subsumes(right->level);
|
||||
}
|
||||
|
||||
Unifier::Unifier(TypeArena* types, NotNull<SingletonTypes> singletonTypes, Mode mode, NotNull<Scope> scope, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, TxnLog* parentLog)
|
||||
: types(types)
|
||||
, singletonTypes(singletonTypes)
|
||||
Unifier::Unifier(NotNull<Normalizer> normalizer, Mode mode, NotNull<Scope> scope, const Location& location,
|
||||
Variance variance, TxnLog* parentLog)
|
||||
: types(normalizer->arena)
|
||||
, singletonTypes(normalizer->singletonTypes)
|
||||
, normalizer(normalizer)
|
||||
, mode(mode)
|
||||
, scope(scope)
|
||||
, log(parentLog)
|
||||
, location(location)
|
||||
, variance(variance)
|
||||
, sharedState(sharedState)
|
||||
, sharedState(*normalizer->sharedState)
|
||||
{
|
||||
normalize = FFlag::LuauSubtypeNormalizer;
|
||||
LUAU_ASSERT(sharedState.iceHandler);
|
||||
}
|
||||
|
||||
|
@ -524,7 +529,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
{
|
||||
tryUnifyUnionWithType(subTy, subUnion, superTy);
|
||||
}
|
||||
else if (const UnionTypeVar* uv = log.getMutable<UnionTypeVar>(superTy))
|
||||
else if (const UnionTypeVar* uv = (FFlag::LuauSubtypeNormalizer? nullptr: log.getMutable<UnionTypeVar>(superTy)))
|
||||
{
|
||||
tryUnifyTypeWithUnion(subTy, superTy, uv, cacheEnabled, isFunctionCall);
|
||||
}
|
||||
|
@ -532,6 +537,10 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
{
|
||||
tryUnifyTypeWithIntersection(subTy, superTy, uv);
|
||||
}
|
||||
else if (const UnionTypeVar* uv = log.getMutable<UnionTypeVar>(superTy))
|
||||
{
|
||||
tryUnifyTypeWithUnion(subTy, superTy, uv, cacheEnabled, isFunctionCall);
|
||||
}
|
||||
else if (const IntersectionTypeVar* uv = log.getMutable<IntersectionTypeVar>(subTy))
|
||||
{
|
||||
tryUnifyIntersectionWithType(subTy, uv, superTy, cacheEnabled, isFunctionCall);
|
||||
|
@ -585,7 +594,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
|
||||
void Unifier::tryUnifyUnionWithType(TypeId subTy, const UnionTypeVar* subUnion, TypeId superTy)
|
||||
{
|
||||
// A | B <: T if A <: T and B <: T
|
||||
// A | B <: T if and only if A <: T and B <: T
|
||||
bool failed = false;
|
||||
std::optional<TypeError> unificationTooComplex;
|
||||
std::optional<TypeError> firstFailedOption;
|
||||
|
@ -715,6 +724,7 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp
|
|||
{
|
||||
TypeId type = uv->options[(i + startIndex) % uv->options.size()];
|
||||
Unifier innerState = makeChildUnifier();
|
||||
innerState.normalize = false;
|
||||
innerState.tryUnify_(subTy, type, isFunctionCall);
|
||||
|
||||
if (innerState.errors.empty())
|
||||
|