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:
Arseny Kapoulkine 2022-10-06 17:23:29 -07:00 committed by GitHub
parent cc26ef16df
commit d5a2a1585e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 4001 additions and 481 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = &currentModule->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{
&currentModule->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)

View File

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

View File

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

View File

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