mirror of
https://github.com/luau-lang/luau.git
synced 2025-03-05 11:41:40 +00:00
Sync to upstream/release/663 (#1699)
Hey folks, another week means another Luau release! This one features a number of bug fixes in the New Type Solver including improvements to user-defined type functions and a bunch of work to untangle some of the outstanding issues we've been seeing with constraint solving not completing in real world use. We're also continuing to make progress on crashes and other problems that affect the stability of fragment autocomplete, as we work towards delivering consistent, low-latency autocomplete for any editor environment. ## New Type Solver - Fix a bug in user-defined type functions where `print` would incorrectly insert `\1` a number of times. - Fix a bug where attempting to refine an optional generic with a type test will cause a false positive type error (fixes #1666) - Fix a bug where the `refine` type family would not skip over `*no-refine*` discriminants (partial resolution for #1424) - Fix a constraint solving bug where recursive function calls would consistently produce cyclic constraints leading to incomplete or inaccurate type inference. - Implement `readparent` and `writeparent` for class types in user-defined type functions, replacing the incorrectly included `parent` method. - Add initial groundwork (under a debug flag) for eager free type generalization, moving us towards further improvements to constraint solving incomplete errors. ## Fragment Autocomplete - Ease up some assertions to improve stability of mixed-mode use of the two type solvers (i.e. using Fragment Autocomplete on a type graph originally produced by the old type solver) - Resolve a bug with type compatibility checks causing internal compiler errors in autocomplete. ## Lexer and Parser - Improve the accuracy of the roundtrippable AST parsing mode by correctly placing closing parentheses on type groupings. - Add a getter for `offset` in the Lexer by @aduermael in #1688 - Add a second entry point to the parser to parse an expression, `parseExpr` ## Internal Contributors Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Menarul Alam <malam@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
6a21dba682
commit
640ebbc0a5
62 changed files with 927 additions and 1069 deletions
|
@ -96,6 +96,9 @@ struct ConstraintGenerator
|
||||||
// will enqueue them during solving.
|
// will enqueue them during solving.
|
||||||
std::vector<ConstraintPtr> unqueuedConstraints;
|
std::vector<ConstraintPtr> unqueuedConstraints;
|
||||||
|
|
||||||
|
// Map a function's signature scope back to its signature type.
|
||||||
|
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||||
|
|
||||||
// The private scope of type aliases for which the type parameters belong to.
|
// The private scope of type aliases for which the type parameters belong to.
|
||||||
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
|
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ struct ConstraintSolver
|
||||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||||
// The entire set of constraints that the solver is trying to resolve.
|
// The entire set of constraints that the solver is trying to resolve.
|
||||||
std::vector<NotNull<Constraint>> constraints;
|
std::vector<NotNull<Constraint>> constraints;
|
||||||
|
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
|
||||||
NotNull<Scope> rootScope;
|
NotNull<Scope> rootScope;
|
||||||
ModuleName currentModuleName;
|
ModuleName currentModuleName;
|
||||||
|
|
||||||
|
@ -119,6 +120,7 @@ struct ConstraintSolver
|
||||||
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
||||||
|
|
||||||
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;
|
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;
|
||||||
|
std::unordered_map<TypeId, DenseHashSet<const Constraint*>> mutatedFreeTypeToConstraint;
|
||||||
|
|
||||||
// Irreducible/uninhabited type functions or type pack functions.
|
// Irreducible/uninhabited type functions or type pack functions.
|
||||||
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
|
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
|
||||||
|
@ -144,6 +146,7 @@ struct ConstraintSolver
|
||||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<Scope> rootScope,
|
NotNull<Scope> rootScope,
|
||||||
std::vector<NotNull<Constraint>> constraints,
|
std::vector<NotNull<Constraint>> constraints,
|
||||||
|
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
|
||||||
ModuleName moduleName,
|
ModuleName moduleName,
|
||||||
NotNull<ModuleResolver> moduleResolver,
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
std::vector<RequireCycle> requireCycles,
|
std::vector<RequireCycle> requireCycles,
|
||||||
|
@ -171,6 +174,8 @@ struct ConstraintSolver
|
||||||
bool isDone() const;
|
bool isDone() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void generalizeOneType(TypeId ty);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind a type variable to another type.
|
* Bind a type variable to another type.
|
||||||
*
|
*
|
||||||
|
|
|
@ -82,5 +82,65 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
std::optional<Position> fragmentEndPosition = std::nullopt
|
std::optional<Position> fragmentEndPosition = std::nullopt
|
||||||
);
|
);
|
||||||
|
|
||||||
|
enum class FragmentAutocompleteStatus
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
FragmentTypeCheckFail,
|
||||||
|
InternalIce
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FragmentAutocompleteStatusResult
|
||||||
|
{
|
||||||
|
FragmentAutocompleteStatus status;
|
||||||
|
std::optional<FragmentAutocompleteResult> result;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FragmentContext
|
||||||
|
{
|
||||||
|
std::string_view newSrc;
|
||||||
|
const ParseResult& newAstRoot;
|
||||||
|
std::optional<FrontendOptions> opts;
|
||||||
|
std::optional<Position> DEPRECATED_fragmentEndPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempts to compute autocomplete suggestions from the fragment context.
|
||||||
|
*
|
||||||
|
* This function computes autocomplete suggestions using outdated frontend typechecking data
|
||||||
|
* by patching the fragment context of the new script source content.
|
||||||
|
*
|
||||||
|
* @param frontend The Luau Frontend data structure, which may contain outdated typechecking data.
|
||||||
|
*
|
||||||
|
* @param moduleName The name of the target module, specifying which script the caller wants to request autocomplete for.
|
||||||
|
*
|
||||||
|
* @param cursorPosition The position in the script where the caller wants to trigger autocomplete.
|
||||||
|
*
|
||||||
|
* @param context The fragment context that this API will use to patch the outdated typechecking data.
|
||||||
|
*
|
||||||
|
* @param stringCompletionCB A callback function that provides autocomplete suggestions for string contexts.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The status indicating whether `fragmentAutocomplete` ran successfully or failed, along with the reason for failure.
|
||||||
|
* Also includes autocomplete suggestions if the status is successful.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* FragmentAutocompleteStatusResult acStatusResult;
|
||||||
|
* if (shouldFragmentAC)
|
||||||
|
* acStatusResult = Luau::tryFragmentAutocomplete(...);
|
||||||
|
*
|
||||||
|
* if (acStatusResult.status != Successful)
|
||||||
|
* {
|
||||||
|
* frontend.check(moduleName, options);
|
||||||
|
* acStatusResult.acResult = Luau::autocomplete(...);
|
||||||
|
* }
|
||||||
|
* return convertResultWithContext(acStatusResult.acResult);
|
||||||
|
*/
|
||||||
|
FragmentAutocompleteStatusResult tryFragmentAutocomplete(
|
||||||
|
Frontend& frontend,
|
||||||
|
const ModuleName& moduleName,
|
||||||
|
Position cursorPosition,
|
||||||
|
FragmentContext context,
|
||||||
|
StringCompletionCallback stringCompletionCB
|
||||||
|
);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/TypeFwd.h"
|
#include "Luau/TypeFwd.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,10 @@ T* getMutable(PendingTypePack* pending)
|
||||||
// Log of what TypeIds we are rebinding, to be committed later.
|
// Log of what TypeIds we are rebinding, to be committed later.
|
||||||
struct TxnLog
|
struct TxnLog
|
||||||
{
|
{
|
||||||
explicit TxnLog(bool useScopes = false)
|
explicit TxnLog()
|
||||||
: typeVarChanges(nullptr)
|
: typeVarChanges(nullptr)
|
||||||
, typePackChanges(nullptr)
|
, typePackChanges(nullptr)
|
||||||
, ownedSeen()
|
, ownedSeen()
|
||||||
, useScopes(useScopes)
|
|
||||||
, sharedSeen(&ownedSeen)
|
, sharedSeen(&ownedSeen)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,7 +216,11 @@ struct TypeFunctionClassType
|
||||||
|
|
||||||
std::optional<TypeFunctionTypeId> metatable; // metaclass?
|
std::optional<TypeFunctionTypeId> metatable; // metaclass?
|
||||||
|
|
||||||
std::optional<TypeFunctionTypeId> parent;
|
// this was mistaken, and we should actually be keeping separate read/write types here.
|
||||||
|
std::optional<TypeFunctionTypeId> parent_DEPRECATED;
|
||||||
|
|
||||||
|
std::optional<TypeFunctionTypeId> readParent;
|
||||||
|
std::optional<TypeFunctionTypeId> writeParent;
|
||||||
|
|
||||||
TypeId classTy;
|
TypeId classTy;
|
||||||
|
|
||||||
|
|
|
@ -93,10 +93,6 @@ struct Unifier
|
||||||
|
|
||||||
Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
|
Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
|
||||||
|
|
||||||
// Configure the Unifier to test for scope subsumption via embedded Scope
|
|
||||||
// pointers rather than TypeLevels.
|
|
||||||
void enableNewSolver();
|
|
||||||
|
|
||||||
// Test whether the two type vars unify. Never commits the result.
|
// Test whether the two type vars unify. Never commits the result.
|
||||||
ErrorVec canUnify(TypeId subTy, TypeId superTy);
|
ErrorVec canUnify(TypeId subTy, TypeId superTy);
|
||||||
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
|
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
|
||||||
|
@ -169,7 +165,6 @@ private:
|
||||||
|
|
||||||
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
||||||
|
|
||||||
TxnLog combineLogsIntoIntersection(std::vector<TxnLog> logs);
|
|
||||||
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);
|
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -195,11 +190,6 @@ private:
|
||||||
|
|
||||||
// Available after regular type pack unification errors
|
// Available after regular type pack unification errors
|
||||||
std::optional<int> firstPackErrorPos;
|
std::optional<int> firstPackErrorPos;
|
||||||
|
|
||||||
// If true, we do a bunch of small things differently to work better with
|
|
||||||
// the new type inference engine. Most notably, we use the Scope hierarchy
|
|
||||||
// directly rather than using TypeLevels.
|
|
||||||
bool useNewSolver = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp);
|
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "Luau/Autocomplete.h"
|
#include "Luau/Autocomplete.h"
|
||||||
|
|
||||||
#include "Luau/AstQuery.h"
|
#include "Luau/AstQuery.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
|
@ -15,6 +16,9 @@ namespace Luau
|
||||||
|
|
||||||
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback)
|
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::autocomplete", "Autocomplete");
|
||||||
|
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||||
|
|
||||||
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
||||||
if (!sourceModule)
|
if (!sourceModule)
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/FileResolver.h"
|
#include "Luau/FileResolver.h"
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/Subtyping.h"
|
#include "Luau/Subtyping.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
@ -24,7 +25,8 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUseLimits)
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -146,44 +148,91 @@ static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* n
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, TypeArena* typeArena, NotNull<BuiltinTypes> builtinTypes)
|
static bool checkTypeMatch(
|
||||||
|
const Module& module,
|
||||||
|
TypeId subTy,
|
||||||
|
TypeId superTy,
|
||||||
|
NotNull<Scope> scope,
|
||||||
|
TypeArena* typeArena,
|
||||||
|
NotNull<BuiltinTypes> builtinTypes
|
||||||
|
)
|
||||||
{
|
{
|
||||||
InternalErrorReporter iceReporter;
|
InternalErrorReporter iceReporter;
|
||||||
UnifierSharedState unifierState(&iceReporter);
|
UnifierSharedState unifierState(&iceReporter);
|
||||||
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
|
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
|
||||||
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};
|
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};
|
||||||
|
if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
{
|
{
|
||||||
TypeCheckLimits limits;
|
if (module.checkedInNewSolver)
|
||||||
TypeFunctionRuntime typeFunctionRuntime{
|
{
|
||||||
NotNull{&iceReporter}, NotNull{&limits}
|
TypeCheckLimits limits;
|
||||||
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
TypeFunctionRuntime typeFunctionRuntime{
|
||||||
|
NotNull{&iceReporter}, NotNull{&limits}
|
||||||
|
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
||||||
|
|
||||||
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||||
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
|
|
||||||
Subtyping subtyping{
|
Subtyping subtyping{
|
||||||
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
|
builtinTypes,
|
||||||
};
|
NotNull{typeArena},
|
||||||
|
NotNull{simplifier.get()},
|
||||||
|
NotNull{&normalizer},
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
|
NotNull{&iceReporter}
|
||||||
|
};
|
||||||
|
|
||||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
|
||||||
|
|
||||||
|
// Cost of normalization can be too high for autocomplete response time requirements
|
||||||
|
unifier.normalize = false;
|
||||||
|
unifier.checkInhabited = false;
|
||||||
|
|
||||||
|
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||||
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
|
|
||||||
|
return unifier.canUnify(subTy, superTy).empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
|
if (FFlag::LuauSolverV2)
|
||||||
|
|
||||||
// Cost of normalization can be too high for autocomplete response time requirements
|
|
||||||
unifier.normalize = false;
|
|
||||||
unifier.checkInhabited = false;
|
|
||||||
|
|
||||||
if (FFlag::LuauAutocompleteUseLimits)
|
|
||||||
{
|
{
|
||||||
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{
|
||||||
|
NotNull{&iceReporter}, NotNull{&limits}
|
||||||
|
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
||||||
|
|
||||||
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||||
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
}
|
|
||||||
|
|
||||||
return unifier.canUnify(subTy, superTy).empty();
|
Subtyping subtyping{
|
||||||
|
builtinTypes,
|
||||||
|
NotNull{typeArena},
|
||||||
|
NotNull{simplifier.get()},
|
||||||
|
NotNull{&normalizer},
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
|
NotNull{&iceReporter}
|
||||||
|
};
|
||||||
|
|
||||||
|
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
|
||||||
|
|
||||||
|
// Cost of normalization can be too high for autocomplete response time requirements
|
||||||
|
unifier.normalize = false;
|
||||||
|
unifier.checkInhabited = false;
|
||||||
|
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||||
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
|
|
||||||
|
return unifier.canUnify(subTy, superTy).empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,10 +258,10 @@ static TypeCorrectKind checkTypeCorrectKind(
|
||||||
|
|
||||||
TypeId expectedType = follow(*typeAtPosition);
|
TypeId expectedType = follow(*typeAtPosition);
|
||||||
|
|
||||||
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType](const FunctionType* ftv)
|
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType, &module](const FunctionType* ftv)
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||||
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
|
return checkTypeMatch(module, *firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -235,7 +284,7 @@ static TypeCorrectKind checkTypeCorrectKind(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkTypeMatch(ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
return checkTypeMatch(module, ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class PropIndexType
|
enum class PropIndexType
|
||||||
|
@ -286,7 +335,7 @@ static void autocompleteProps(
|
||||||
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
|
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
|
||||||
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
||||||
{
|
{
|
||||||
if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
|
if (checkTypeMatch(module, rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
|
||||||
return calledWithSelf;
|
return calledWithSelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1714,6 +1763,7 @@ AutocompleteResult autocomplete_(
|
||||||
StringCompletionCallback callback
|
StringCompletionCallback callback
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::autocomplete_", "AutocompleteCore");
|
||||||
AstNode* node = ancestry.back();
|
AstNode* node = ancestry.back();
|
||||||
|
|
||||||
AstExprConstantNil dummy{Location{}};
|
AstExprConstantNil dummy{Location{}};
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -111,6 +113,11 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
{
|
{
|
||||||
rci.traverse(fchc->argsPack);
|
rci.traverse(fchc->argsPack);
|
||||||
}
|
}
|
||||||
|
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
rci.traverse(fcc->fn);
|
||||||
|
rci.traverse(fcc->argsPack);
|
||||||
|
}
|
||||||
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
|
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(ptc->freeType);
|
rci.traverse(ptc->freeType);
|
||||||
|
@ -118,7 +125,8 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
else if (auto hpc = get<HasPropConstraint>(*this))
|
else if (auto hpc = get<HasPropConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(hpc->resultType);
|
rci.traverse(hpc->resultType);
|
||||||
// `HasPropConstraints` should not mutate `subjectType`.
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
rci.traverse(hpc->subjectType);
|
||||||
}
|
}
|
||||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,11 +32,11 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment)
|
LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
||||||
|
@ -785,9 +785,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
}
|
}
|
||||||
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
|
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauNewSolverPrePopulateClasses)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
||||||
{
|
{
|
||||||
auto it = classDefinitionLocations.find(classDeclaration->name.value);
|
auto it = classDefinitionLocations.find(classDeclaration->name.value);
|
||||||
|
@ -1384,6 +1381,28 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||||
bool sigFullyDefined = !hasFreeType(sig.signature);
|
bool sigFullyDefined = !hasFreeType(sig.signature);
|
||||||
|
|
||||||
|
DefId def = dfg->getDef(function->name);
|
||||||
|
|
||||||
|
if (FFlag::LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
|
{
|
||||||
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
||||||
|
{
|
||||||
|
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
||||||
|
sig.bodyScope->lvalueTypes[def] = sig.signature;
|
||||||
|
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
||||||
|
}
|
||||||
|
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
||||||
|
{
|
||||||
|
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
||||||
|
sig.bodyScope->lvalueTypes[def] = sig.signature;
|
||||||
|
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
||||||
|
}
|
||||||
|
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
||||||
|
{
|
||||||
|
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
|
@ -1417,7 +1436,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefId def = dfg->getDef(function->name);
|
|
||||||
std::optional<TypeId> existingFunctionTy = follow(lookup(scope, function->name->location, def));
|
std::optional<TypeId> existingFunctionTy = follow(lookup(scope, function->name->location, def));
|
||||||
|
|
||||||
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
||||||
|
@ -1691,7 +1709,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
{
|
{
|
||||||
// If a class with the same name was already defined, we skip over
|
// If a class with the same name was already defined, we skip over
|
||||||
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
|
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
|
||||||
if (FFlag::LuauNewSolverPrePopulateClasses && bindingIt == scope->exportedTypeBindings.end())
|
if (bindingIt == scope->exportedTypeBindings.end())
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
|
|
||||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
||||||
|
@ -1708,10 +1726,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
|
|
||||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||||
if (FFlag::LuauNewSolverPrePopulateClasses)
|
superTy = follow(lookupType->type);
|
||||||
superTy = follow(lookupType->type);
|
|
||||||
else
|
|
||||||
superTy = lookupType->type;
|
|
||||||
|
|
||||||
if (!get<ClassType>(follow(*superTy)))
|
if (!get<ClassType>(follow(*superTy)))
|
||||||
{
|
{
|
||||||
|
@ -1734,14 +1749,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
|
|
||||||
ctv->metatable = metaTy;
|
ctv->metatable = metaTy;
|
||||||
|
|
||||||
|
TypeId classBindTy = bindingIt->second.type;
|
||||||
if (FFlag::LuauNewSolverPrePopulateClasses)
|
emplaceType<BoundType>(asMutable(classBindTy), classTy);
|
||||||
{
|
|
||||||
TypeId classBindTy = bindingIt->second.type;
|
|
||||||
emplaceType<BoundType>(asMutable(classBindTy), classTy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
|
||||||
|
|
||||||
if (declaredClass->indexer)
|
if (declaredClass->indexer)
|
||||||
{
|
{
|
||||||
|
@ -2930,10 +2939,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
|
|
||||||
ttv->state = TableState::Unsealed;
|
ttv->state = TableState::Unsealed;
|
||||||
ttv->definitionModuleName = module->name;
|
ttv->definitionModuleName = module->name;
|
||||||
if (FFlag::LuauNewSolverPopulateTableLocations)
|
ttv->definitionLocation = expr->location;
|
||||||
{
|
|
||||||
ttv->definitionLocation = expr->location;
|
|
||||||
}
|
|
||||||
ttv->scope = scope.get();
|
ttv->scope = scope.get();
|
||||||
|
|
||||||
interiorTypes.back().push_back(ty);
|
interiorTypes.back().push_back(ty);
|
||||||
|
@ -3230,6 +3236,9 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
if (expectedType && get<FreeType>(*expectedType))
|
if (expectedType && get<FreeType>(*expectedType))
|
||||||
bindFreeType(*expectedType, actualFunctionType);
|
bindFreeType(*expectedType, actualFunctionType);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
scopeToFunction[signatureScope.get()] = actualFunctionType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/* signature */ actualFunctionType,
|
/* signature */ actualFunctionType,
|
||||||
/* signatureScope */ signatureScope,
|
/* signatureScope */ signatureScope,
|
||||||
|
@ -3392,11 +3401,8 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
|
||||||
TypeId tableTy = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
|
TypeId tableTy = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
|
||||||
TableType* ttv = getMutable<TableType>(tableTy);
|
TableType* ttv = getMutable<TableType>(tableTy);
|
||||||
|
|
||||||
if (FFlag::LuauNewSolverPopulateTableLocations)
|
ttv->definitionModuleName = module->name;
|
||||||
{
|
ttv->definitionLocation = tab->location;
|
||||||
ttv->definitionModuleName = module->name;
|
|
||||||
ttv->definitionLocation = tab->location;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableTy;
|
return tableTy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,17 +27,16 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
||||||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2)
|
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -328,6 +327,7 @@ ConstraintSolver::ConstraintSolver(
|
||||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<Scope> rootScope,
|
NotNull<Scope> rootScope,
|
||||||
std::vector<NotNull<Constraint>> constraints,
|
std::vector<NotNull<Constraint>> constraints,
|
||||||
|
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
|
||||||
ModuleName moduleName,
|
ModuleName moduleName,
|
||||||
NotNull<ModuleResolver> moduleResolver,
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
std::vector<RequireCycle> requireCycles,
|
std::vector<RequireCycle> requireCycles,
|
||||||
|
@ -341,6 +341,7 @@ ConstraintSolver::ConstraintSolver(
|
||||||
, simplifier(simplifier)
|
, simplifier(simplifier)
|
||||||
, typeFunctionRuntime(typeFunctionRuntime)
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
, constraints(std::move(constraints))
|
, constraints(std::move(constraints))
|
||||||
|
, scopeToFunction(scopeToFunction)
|
||||||
, rootScope(rootScope)
|
, rootScope(rootScope)
|
||||||
, currentModuleName(std::move(moduleName))
|
, currentModuleName(std::move(moduleName))
|
||||||
, dfg(dfg)
|
, dfg(dfg)
|
||||||
|
@ -362,6 +363,12 @@ ConstraintSolver::ConstraintSolver(
|
||||||
{
|
{
|
||||||
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
refCount += 1;
|
refCount += 1;
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, DenseHashSet<const Constraint*>{nullptr});
|
||||||
|
it->second.insert(c.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
||||||
}
|
}
|
||||||
|
@ -453,6 +460,9 @@ void ConstraintSolver::run()
|
||||||
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauAssertOnForcedConstraint)
|
||||||
|
LUAU_ASSERT(!force);
|
||||||
|
|
||||||
bool success = tryDispatch(c, force);
|
bool success = tryDispatch(c, force);
|
||||||
|
|
||||||
progress |= success;
|
progress |= success;
|
||||||
|
@ -467,12 +477,21 @@ void ConstraintSolver::run()
|
||||||
const auto maybeMutated = maybeMutatedFreeTypes.find(c);
|
const auto maybeMutated = maybeMutatedFreeTypes.find(c);
|
||||||
if (maybeMutated != maybeMutatedFreeTypes.end())
|
if (maybeMutated != maybeMutatedFreeTypes.end())
|
||||||
{
|
{
|
||||||
|
DenseHashSet<TypeId> seen{nullptr};
|
||||||
for (auto ty : maybeMutated->second)
|
for (auto ty : maybeMutated->second)
|
||||||
{
|
{
|
||||||
// There is a high chance that this type has been rebound
|
// There is a high chance that this type has been rebound
|
||||||
// across blocked types, rebound free types, pending
|
// across blocked types, rebound free types, pending
|
||||||
// expansion types, etc, so we need to follow it.
|
// expansion types, etc, so we need to follow it.
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
if (seen.contains(ty))
|
||||||
|
continue;
|
||||||
|
seen.insert(ty);
|
||||||
|
}
|
||||||
|
|
||||||
size_t& refCount = unresolvedConstraints[ty];
|
size_t& refCount = unresolvedConstraints[ty];
|
||||||
if (refCount > 0)
|
if (refCount > 0)
|
||||||
refCount -= 1;
|
refCount -= 1;
|
||||||
|
@ -484,6 +503,9 @@ void ConstraintSolver::run()
|
||||||
// refcount to be 1 or 0.
|
// refcount to be 1 or 0.
|
||||||
if (refCount <= 1)
|
if (refCount <= 1)
|
||||||
unblock(ty, Location{});
|
unblock(ty, Location{});
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization && refCount == 0)
|
||||||
|
generalizeOneType(ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -600,16 +622,154 @@ bool ConstraintSolver::isDone() const
|
||||||
return unsolvedConstraints.empty();
|
return unsolvedConstraints.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
struct TypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
|
enum struct Polarity: uint8_t
|
||||||
|
{
|
||||||
|
None = 0b00,
|
||||||
|
Positive = 0b01,
|
||||||
|
Negative = 0b10,
|
||||||
|
Mixed = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
struct TypeAndLocation
|
TypeId needle;
|
||||||
{
|
Polarity current = Polarity::Positive;
|
||||||
TypeId typeId;
|
|
||||||
Location location;
|
Polarity result = Polarity::None;
|
||||||
|
|
||||||
|
explicit TypeSearcher(TypeId needle)
|
||||||
|
: TypeSearcher(needle, Polarity::Positive)
|
||||||
|
{}
|
||||||
|
|
||||||
|
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
|
||||||
|
: needle(needle)
|
||||||
|
, current(initialPolarity)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool visit(TypeId ty) override
|
||||||
|
{
|
||||||
|
if (ty == needle)
|
||||||
|
result = Polarity(int(result) | int(current));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flip()
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case Polarity::Positive:
|
||||||
|
current = Polarity::Negative;
|
||||||
|
break;
|
||||||
|
case Polarity::Negative:
|
||||||
|
current = Polarity::Positive;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const FunctionType& ft) override
|
||||||
|
{
|
||||||
|
flip();
|
||||||
|
traverse(ft.argTypes);
|
||||||
|
|
||||||
|
flip();
|
||||||
|
traverse(ft.retTypes);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool visit(TypeId ty, const TableType& tt) override
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const ClassType&) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
const FreeType* freeTy = get<FreeType>(ty);
|
||||||
|
|
||||||
|
std::string saveme = toString(ty, opts);
|
||||||
|
|
||||||
|
// Some constraints (like prim) will also replace a free type with something
|
||||||
|
// concrete. If so, our work is already done.
|
||||||
|
if (!freeTy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NotNull<Scope> tyScope{freeTy->scope};
|
||||||
|
|
||||||
|
// TODO: If freeTy occurs within the enclosing function's type, we need to
|
||||||
|
// check to see whether this type should instead be generic.
|
||||||
|
|
||||||
|
TypeId newBound = follow(freeTy->upperBound);
|
||||||
|
|
||||||
|
TypeId* functionTyPtr = nullptr;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
functionTyPtr = scopeToFunction->find(tyScope);
|
||||||
|
if (functionTyPtr || !tyScope->parent)
|
||||||
|
break;
|
||||||
|
else if (tyScope->parent)
|
||||||
|
tyScope = NotNull{tyScope->parent.get()};
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ty == newBound)
|
||||||
|
ty = builtinTypes->unknownType;
|
||||||
|
|
||||||
|
if (!functionTyPtr)
|
||||||
|
{
|
||||||
|
asMutable(ty)->reassign(Type{BoundType{follow(freeTy->upperBound)}});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const TypeId functionTy = follow(*functionTyPtr);
|
||||||
|
FunctionType* const function = getMutable<FunctionType>(functionTy);
|
||||||
|
LUAU_ASSERT(function);
|
||||||
|
|
||||||
|
TypeSearcher ts{ty};
|
||||||
|
ts.traverse(functionTy);
|
||||||
|
|
||||||
|
const TypeId upperBound = follow(freeTy->upperBound);
|
||||||
|
const TypeId lowerBound = follow(freeTy->lowerBound);
|
||||||
|
|
||||||
|
switch (ts.result)
|
||||||
|
{
|
||||||
|
case TypeSearcher::Polarity::None:
|
||||||
|
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeSearcher::Polarity::Negative:
|
||||||
|
case TypeSearcher::Polarity::Mixed:
|
||||||
|
if (get<UnknownType>(upperBound))
|
||||||
|
{
|
||||||
|
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
||||||
|
function->generics.emplace_back(ty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeSearcher::Polarity::Positive:
|
||||||
|
if (get<UnknownType>(lowerBound))
|
||||||
|
{
|
||||||
|
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
||||||
|
function->generics.emplace_back(ty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
asMutable(ty)->reassign(Type{BoundType{lowerBound}});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo)
|
void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo)
|
||||||
{
|
{
|
||||||
|
@ -1170,12 +1330,9 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||||
target = follow(instantiated);
|
target = follow(instantiated);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNewSolverPopulateTableLocations)
|
// This is a new type - redefine the location.
|
||||||
{
|
ttv->definitionLocation = constraint->location;
|
||||||
// This is a new type - redefine the location.
|
ttv->definitionModuleName = currentModuleName;
|
||||||
ttv->definitionLocation = constraint->location;
|
|
||||||
ttv->definitionModuleName = currentModuleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
ttv->instantiatedTypeParams = typeArguments;
|
ttv->instantiatedTypeParams = typeArguments;
|
||||||
ttv->instantiatedTypePackParams = packArguments;
|
ttv->instantiatedTypePackParams = packArguments;
|
||||||
|
@ -1222,8 +1379,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
{
|
{
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
|
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
|
||||||
unblock(c.result, constraint->location);
|
unblock(c.result, constraint->location);
|
||||||
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,16 +1387,14 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
if (get<ErrorType>(fn))
|
if (get<ErrorType>(fn))
|
||||||
{
|
{
|
||||||
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack());
|
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack());
|
||||||
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<NeverType>(fn))
|
if (get<NeverType>(fn))
|
||||||
{
|
{
|
||||||
bind(constraint, c.result, builtinTypes->neverTypePack);
|
bind(constraint, c.result, builtinTypes->neverTypePack);
|
||||||
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1321,30 +1475,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
{
|
|
||||||
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// NOTE: This is the body of the `fillInDiscriminantTypes` helper.
|
|
||||||
for (std::optional<TypeId> ty : c.discriminantTypes)
|
|
||||||
{
|
|
||||||
if (!ty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If the discriminant type has been transmuted, we need to unblock them.
|
|
||||||
if (!isBlocked(*ty))
|
|
||||||
{
|
|
||||||
unblock(*ty, constraint->location);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
|
||||||
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
OverloadResolver resolver{
|
OverloadResolver resolver{
|
||||||
builtinTypes,
|
builtinTypes,
|
||||||
|
@ -1912,7 +2043,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
bind(
|
bind(
|
||||||
constraint,
|
constraint,
|
||||||
c.propType,
|
c.propType,
|
||||||
isIndex && FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy
|
isIndex ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy
|
||||||
);
|
);
|
||||||
unify(constraint, rhsType, propTy);
|
unify(constraint, rhsType, propTy);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2010,8 +2141,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
bind(
|
bind(
|
||||||
constraint,
|
constraint,
|
||||||
c.propType,
|
c.propType,
|
||||||
FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}})
|
arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}})
|
||||||
: lhsTable->indexer->indexResultType
|
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2064,8 +2194,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
bind(
|
bind(
|
||||||
constraint,
|
constraint,
|
||||||
c.propType,
|
c.propType,
|
||||||
FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}})
|
arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}})
|
||||||
: lhsClass->indexer->indexResultType
|
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3113,6 +3242,24 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
|
||||||
|
|
||||||
auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0);
|
auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0);
|
||||||
targetRefs += count;
|
targetRefs += count;
|
||||||
|
|
||||||
|
// Any constraint that might have mutated source may now mutate target
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
auto it = mutatedFreeTypeToConstraint.find(source);
|
||||||
|
if (it != mutatedFreeTypeToConstraint.end())
|
||||||
|
{
|
||||||
|
auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet<const Constraint*>{nullptr});
|
||||||
|
for (const Constraint* constraint : it->second)
|
||||||
|
{
|
||||||
|
it2->second.insert(constraint);
|
||||||
|
|
||||||
|
auto [it3, fresh2] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet<TypeId>{nullptr});
|
||||||
|
it3->second.insert(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables)
|
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn)
|
LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -239,37 +237,6 @@ declare utf8: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBufferSrc_DEPRECATED = R"BUILTIN_SRC(
|
|
||||||
--- Buffer API
|
|
||||||
declare buffer: {
|
|
||||||
create: @checked (size: number) -> buffer,
|
|
||||||
fromstring: @checked (str: string) -> buffer,
|
|
||||||
tostring: @checked (b: buffer) -> string,
|
|
||||||
len: @checked (b: buffer) -> number,
|
|
||||||
copy: @checked (target: buffer, targetOffset: number, source: buffer, sourceOffset: number?, count: number?) -> (),
|
|
||||||
fill: @checked (b: buffer, offset: number, value: number, count: number?) -> (),
|
|
||||||
readi8: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu8: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readi16: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu16: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readi32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readf32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readf64: @checked (b: buffer, offset: number) -> number,
|
|
||||||
writei8: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu8: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writei16: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu16: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writei32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writef32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writef64: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
readstring: @checked (b: buffer, offset: number, count: number) -> string,
|
|
||||||
writestring: @checked (b: buffer, offset: number, value: string, count: number?) -> (),
|
|
||||||
}
|
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
||||||
--- Buffer API
|
--- Buffer API
|
||||||
declare buffer: {
|
declare buffer: {
|
||||||
|
@ -303,36 +270,6 @@ declare buffer: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC(
|
|
||||||
|
|
||||||
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
|
||||||
declare class vector
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
z: number
|
|
||||||
end
|
|
||||||
|
|
||||||
declare vector: {
|
|
||||||
create: @checked (x: number, y: number, z: number) -> vector,
|
|
||||||
magnitude: @checked (vec: vector) -> number,
|
|
||||||
normalize: @checked (vec: vector) -> vector,
|
|
||||||
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
|
||||||
dot: @checked (vec1: vector, vec2: vector) -> number,
|
|
||||||
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
|
||||||
floor: @checked (vec: vector) -> vector,
|
|
||||||
ceil: @checked (vec: vector) -> vector,
|
|
||||||
abs: @checked (vec: vector) -> vector,
|
|
||||||
sign: @checked (vec: vector) -> vector,
|
|
||||||
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
|
||||||
max: @checked (vector, ...vector) -> vector,
|
|
||||||
min: @checked (vector, ...vector) -> vector,
|
|
||||||
|
|
||||||
zero: vector,
|
|
||||||
one: vector,
|
|
||||||
}
|
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
||||||
|
@ -374,13 +311,8 @@ std::string getBuiltinDefinitionSource()
|
||||||
result += kBuiltinDefinitionTableSrc;
|
result += kBuiltinDefinitionTableSrc;
|
||||||
result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED;
|
result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED;
|
||||||
result += kBuiltinDefinitionUtf8Src;
|
result += kBuiltinDefinitionUtf8Src;
|
||||||
|
result += kBuiltinDefinitionBufferSrc;
|
||||||
result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
result += kBuiltinDefinitionVectorSrc;
|
||||||
|
|
||||||
if (FFlag::LuauVector2Constructor)
|
|
||||||
result += kBuiltinDefinitionVectorSrc;
|
|
||||||
else
|
|
||||||
result += kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauAllowFragmentParsing);
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes)
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes)
|
||||||
|
@ -98,6 +97,7 @@ void cloneAndSquashScopes(
|
||||||
Scope* destScope
|
Scope* destScope
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::cloneAndSquashScopes", "FragmentAutocomplete");
|
||||||
std::vector<const Scope*> scopes;
|
std::vector<const Scope*> scopes;
|
||||||
for (const Scope* current = staleScope; current; current = current->parent.get())
|
for (const Scope* current = staleScope; current; current = current->parent.get())
|
||||||
{
|
{
|
||||||
|
@ -364,6 +364,7 @@ std::optional<FragmentParseResult> parseFragment(
|
||||||
|
|
||||||
ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::unique_ptr<Allocator> alloc)
|
ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::unique_ptr<Allocator> alloc)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::cloneModule", "FragmentAutocomplete");
|
||||||
freeze(source->internalTypes);
|
freeze(source->internalTypes);
|
||||||
freeze(source->interfaceTypes);
|
freeze(source->interfaceTypes);
|
||||||
ModulePtr incremental = std::make_shared<Module>();
|
ModulePtr incremental = std::make_shared<Module>();
|
||||||
|
@ -445,6 +446,8 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
const FrontendOptions& opts
|
const FrontendOptions& opts
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::typecheckFragment_", "FragmentAutocomplete");
|
||||||
|
|
||||||
freeze(stale->internalTypes);
|
freeze(stale->internalTypes);
|
||||||
freeze(stale->interfaceTypes);
|
freeze(stale->interfaceTypes);
|
||||||
CloneState cloneState{frontend.builtinTypes};
|
CloneState cloneState{frontend.builtinTypes};
|
||||||
|
@ -533,6 +536,7 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
NotNull{&typeFunctionRuntime},
|
NotNull{&typeFunctionRuntime},
|
||||||
NotNull(cg.rootScope),
|
NotNull(cg.rootScope),
|
||||||
borrowConstraints(cg.constraints),
|
borrowConstraints(cg.constraints),
|
||||||
|
NotNull{&cg.scopeToFunction},
|
||||||
incrementalModule->name,
|
incrementalModule->name,
|
||||||
NotNull{&resolver},
|
NotNull{&resolver},
|
||||||
{},
|
{},
|
||||||
|
@ -573,6 +577,8 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
std::optional<Position> fragmentEndPosition
|
std::optional<Position> fragmentEndPosition
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::typecheckFragment", "FragmentAutocomplete");
|
||||||
|
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||||
|
|
||||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||||
{
|
{
|
||||||
|
@ -621,6 +627,35 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
return {FragmentTypeCheckStatus::Success, result};
|
return {FragmentTypeCheckStatus::Success, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FragmentAutocompleteStatusResult tryFragmentAutocomplete(
|
||||||
|
Frontend& frontend,
|
||||||
|
const ModuleName& moduleName,
|
||||||
|
Position cursorPosition,
|
||||||
|
FragmentContext context,
|
||||||
|
StringCompletionCallback stringCompletionCB
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// TODO: we should calculate fragmentEnd position here, by using context.newAstRoot and cursorPosition
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Luau::FragmentAutocompleteResult fragmentAutocomplete = Luau::fragmentAutocomplete(
|
||||||
|
frontend,
|
||||||
|
context.newSrc,
|
||||||
|
moduleName,
|
||||||
|
cursorPosition,
|
||||||
|
context.opts,
|
||||||
|
std::move(stringCompletionCB),
|
||||||
|
context.DEPRECATED_fragmentEndPosition
|
||||||
|
);
|
||||||
|
return {FragmentAutocompleteStatus::Success, std::move(fragmentAutocomplete)};
|
||||||
|
}
|
||||||
|
catch (const Luau::InternalCompilerError& e)
|
||||||
|
{
|
||||||
|
if (FFlag::LogFragmentsFromAutocomplete)
|
||||||
|
logLuau(e.what());
|
||||||
|
return {FragmentAutocompleteStatus::InternalIce, std::nullopt};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FragmentAutocompleteResult fragmentAutocomplete(
|
FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
Frontend& frontend,
|
Frontend& frontend,
|
||||||
|
@ -632,8 +667,9 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
std::optional<Position> fragmentEndPosition
|
std::optional<Position> fragmentEndPosition
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauAllowFragmentParsing);
|
|
||||||
LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
|
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
||||||
|
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||||
|
|
||||||
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
||||||
if (!sourceModule)
|
if (!sourceModule)
|
||||||
|
|
|
@ -1481,6 +1481,7 @@ ModulePtr check(
|
||||||
NotNull{&typeFunctionRuntime},
|
NotNull{&typeFunctionRuntime},
|
||||||
NotNull(cg.rootScope),
|
NotNull(cg.rootScope),
|
||||||
borrowConstraints(cg.constraints),
|
borrowConstraints(cg.constraints),
|
||||||
|
NotNull{&cg.scopeToFunction},
|
||||||
result->name,
|
result->name,
|
||||||
moduleResolver,
|
moduleResolver,
|
||||||
requireCycles,
|
requireCycles,
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCountSelfCallsNonstrict)
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
|
@ -628,17 +627,6 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstExprCall* call)
|
NonStrictContext visit(AstExprCall* call)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauCountSelfCallsNonstrict)
|
|
||||||
return visitCall(call);
|
|
||||||
else
|
|
||||||
return visitCall_DEPRECATED(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rename this to `visit` when `FFlag::LuauCountSelfCallsNonstrict` is removed, and clean up above `visit`.
|
|
||||||
NonStrictContext visitCall(AstExprCall* call)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauCountSelfCallsNonstrict);
|
|
||||||
|
|
||||||
NonStrictContext fresh{};
|
NonStrictContext fresh{};
|
||||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func);
|
TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func);
|
||||||
if (!originalCallTy)
|
if (!originalCallTy)
|
||||||
|
@ -747,109 +735,6 @@ struct NonStrictTypeChecker
|
||||||
return fresh;
|
return fresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove with `FFlag::LuauCountSelfCallsNonstrict` clean up.
|
|
||||||
NonStrictContext visitCall_DEPRECATED(AstExprCall* call)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauCountSelfCallsNonstrict);
|
|
||||||
|
|
||||||
NonStrictContext fresh{};
|
|
||||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func);
|
|
||||||
if (!originalCallTy)
|
|
||||||
return fresh;
|
|
||||||
|
|
||||||
TypeId fnTy = *originalCallTy;
|
|
||||||
if (auto fn = get<FunctionType>(follow(fnTy)))
|
|
||||||
{
|
|
||||||
if (fn->isCheckedFunction)
|
|
||||||
{
|
|
||||||
// We know fn is a checked function, which means it looks like:
|
|
||||||
// (S1, ... SN) -> T &
|
|
||||||
// (~S1, unknown^N-1) -> error &
|
|
||||||
// (unknown, ~S2, unknown^N-2) -> error
|
|
||||||
// ...
|
|
||||||
// ...
|
|
||||||
// (unknown^N-1, ~S_N) -> error
|
|
||||||
std::vector<TypeId> argTypes;
|
|
||||||
argTypes.reserve(call->args.size);
|
|
||||||
// Pad out the arg types array with the types you would expect to see
|
|
||||||
TypePackIterator curr = begin(fn->argTypes);
|
|
||||||
TypePackIterator fin = end(fn->argTypes);
|
|
||||||
while (curr != fin)
|
|
||||||
{
|
|
||||||
argTypes.push_back(*curr);
|
|
||||||
++curr;
|
|
||||||
}
|
|
||||||
if (auto argTail = curr.tail())
|
|
||||||
{
|
|
||||||
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*argTail)))
|
|
||||||
{
|
|
||||||
while (argTypes.size() < call->args.size)
|
|
||||||
{
|
|
||||||
argTypes.push_back(vtp->ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string functionName = getFunctionNameAsString(*call->func).value_or("");
|
|
||||||
if (call->args.size > argTypes.size())
|
|
||||||
{
|
|
||||||
// We are passing more arguments than we expect, so we should error
|
|
||||||
reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), call->args.size}, call->location);
|
|
||||||
return fresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < call->args.size; i++)
|
|
||||||
{
|
|
||||||
// For example, if the arg is "hi"
|
|
||||||
// The actual arg type is string
|
|
||||||
// The expected arg type is number
|
|
||||||
// The type of the argument in the overload is ~number
|
|
||||||
// We will compare arg and ~number
|
|
||||||
AstExpr* arg = call->args.data[i];
|
|
||||||
TypeId expectedArgType = argTypes[i];
|
|
||||||
std::shared_ptr<const NormalizedType> norm = normalizer.normalize(expectedArgType);
|
|
||||||
DefId def = dfg->getDef(arg);
|
|
||||||
TypeId runTimeErrorTy;
|
|
||||||
// If we're dealing with any, negating any will cause all subtype tests to fail
|
|
||||||
// However, when someone calls this function, they're going to want to be able to pass it anything,
|
|
||||||
// for that reason, we manually inject never into the context so that the runtime test will always pass.
|
|
||||||
if (!norm)
|
|
||||||
reportError(NormalizationTooComplex{}, arg->location);
|
|
||||||
|
|
||||||
if (norm && get<AnyType>(norm->tops))
|
|
||||||
runTimeErrorTy = builtinTypes->neverType;
|
|
||||||
else
|
|
||||||
runTimeErrorTy = getOrCreateNegation(expectedArgType);
|
|
||||||
fresh.addContext(def, runTimeErrorTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the context and now iterate through each of the arguments to the call to find out if we satisfy the types
|
|
||||||
for (size_t i = 0; i < call->args.size; i++)
|
|
||||||
{
|
|
||||||
AstExpr* arg = call->args.data[i];
|
|
||||||
if (auto runTimeFailureType = willRunTimeError(arg, fresh))
|
|
||||||
reportError(CheckedFunctionCallError{argTypes[i], *runTimeFailureType, functionName, i}, arg->location);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (call->args.size < argTypes.size())
|
|
||||||
{
|
|
||||||
// We are passing fewer arguments than we expect
|
|
||||||
// so we need to ensure that the rest of the args are optional.
|
|
||||||
bool remainingArgsOptional = true;
|
|
||||||
for (size_t i = call->args.size; i < argTypes.size(); i++)
|
|
||||||
remainingArgsOptional = remainingArgsOptional && isOptional(argTypes[i]);
|
|
||||||
if (!remainingArgsOptional)
|
|
||||||
{
|
|
||||||
reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), call->args.size}, call->location);
|
|
||||||
return fresh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstExprIndexName* indexName, ValueContext context)
|
NonStrictContext visit(AstExprIndexName* indexName, ValueContext context)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -330,7 +330,7 @@ struct Printer_DEPRECATED
|
||||||
else if (typeCount == 1)
|
else if (typeCount == 1)
|
||||||
{
|
{
|
||||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||||
if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize)
|
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||||
writer.symbol("(");
|
writer.symbol("(");
|
||||||
|
|
||||||
// Only variadic tail
|
// Only variadic tail
|
||||||
|
@ -343,7 +343,7 @@ struct Printer_DEPRECATED
|
||||||
visualizeTypeAnnotation(*list.types.data[0]);
|
visualizeTypeAnnotation(*list.types.data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize)
|
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1349,7 +1349,7 @@ struct Printer
|
||||||
else if (typeCount == 1)
|
else if (typeCount == 1)
|
||||||
{
|
{
|
||||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||||
if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize)
|
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||||
writer.symbol("(");
|
writer.symbol("(");
|
||||||
|
|
||||||
// Only variadic tail
|
// Only variadic tail
|
||||||
|
@ -1362,7 +1362,7 @@ struct Printer
|
||||||
visualizeTypeAnnotation(*list.types.data[0]);
|
visualizeTypeAnnotation(*list.types.data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize)
|
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2599,6 +2599,7 @@ struct Printer
|
||||||
{
|
{
|
||||||
writer.symbol("(");
|
writer.symbol("(");
|
||||||
visualizeTypeAnnotation(*a->type);
|
visualizeTypeAnnotation(*a->type);
|
||||||
|
advance(Position{a->location.end.line, a->location.end.column - 1});
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeSingletonBool>())
|
else if (const auto& a = typeAnnotation.as<AstTypeSingletonBool>())
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableKeysAreRValues)
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1848,16 +1847,8 @@ void TypeChecker2::visit(AstExprTable* expr)
|
||||||
{
|
{
|
||||||
for (const AstExprTable::Item& item : expr->items)
|
for (const AstExprTable::Item& item : expr->items)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauTableKeysAreRValues)
|
if (item.key)
|
||||||
{
|
visit(item.key, ValueContext::RValue);
|
||||||
if (item.key)
|
|
||||||
visit(item.key, ValueContext::RValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (item.key)
|
|
||||||
visit(item.key, ValueContext::LValue);
|
|
||||||
}
|
|
||||||
visit(item.value, ValueContext::RValue);
|
visit(item.value, ValueContext::RValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauClipNestedAndRecursiveUnion)
|
LUAU_FASTFLAGVARIABLE(LuauClipNestedAndRecursiveUnion)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotGeneralizeInTypeFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauDoNotGeneralizeInTypeFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreventReentrantTypeFunctionReduction)
|
LUAU_FASTFLAGVARIABLE(LuauPreventReentrantTypeFunctionReduction)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -2005,17 +2007,9 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* HACK: Refinements sometimes produce a type T & ~any under the assumption
|
if (FFlag::LuauSkipNoRefineDuringRefinement)
|
||||||
* that ~any is the same as any. This is so so weird, but refinements needs
|
if (get<NoRefineType>(discriminant))
|
||||||
* some way to say "I may refine this, but I'm not sure."
|
return {target, {}};
|
||||||
*
|
|
||||||
* It does this by refining on a blocked type and deferring the decision
|
|
||||||
* until it is unblocked.
|
|
||||||
*
|
|
||||||
* Refinements also get negated, so we wind up with types like T & ~*blocked*
|
|
||||||
*
|
|
||||||
* We need to treat T & ~any as T in this case.
|
|
||||||
*/
|
|
||||||
if (auto nt = get<NegationType>(discriminant))
|
if (auto nt = get<NegationType>(discriminant))
|
||||||
{
|
{
|
||||||
if (get<NoRefineType>(follow(nt->ty)))
|
if (get<NoRefineType>(follow(nt->ty)))
|
||||||
|
@ -2292,8 +2286,20 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
||||||
if (!result.blockedTypes.empty())
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
if (FFlag::LuauIntersectNotNil)
|
||||||
|
{
|
||||||
|
for (TypeId blockedType : result.blockedTypes)
|
||||||
|
{
|
||||||
|
if (!get<GenericType>(blockedType))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!result.blockedTypes.empty())
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
resultTy = result.result;
|
resultTy = result.result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunSingletonEquality)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunSingletonEquality)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1110,7 +1111,7 @@ static int getFunctionGenerics(lua_State* L)
|
||||||
|
|
||||||
// Luau: `self:parent() -> type`
|
// Luau: `self:parent() -> type`
|
||||||
// Returns the parent of a class type
|
// Returns the parent of a class type
|
||||||
static int getClassParent(lua_State* L)
|
static int getClassParent_DEPRECATED(lua_State* L)
|
||||||
{
|
{
|
||||||
int argumentCount = lua_gettop(L);
|
int argumentCount = lua_gettop(L);
|
||||||
if (argumentCount != 1)
|
if (argumentCount != 1)
|
||||||
|
@ -1122,10 +1123,54 @@ static int getClassParent(lua_State* L)
|
||||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
// If the parent does not exist, we should return nil
|
// If the parent does not exist, we should return nil
|
||||||
if (!tfct->parent)
|
if (!tfct->parent_DEPRECATED)
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
else
|
else
|
||||||
allocTypeUserData(L, (*tfct->parent)->type);
|
allocTypeUserData(L, (*tfct->parent_DEPRECATED)->type);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Luau: `self:readparent() -> type`
|
||||||
|
// Returns the read type of the class' parent
|
||||||
|
static int getReadParent(lua_State* L)
|
||||||
|
{
|
||||||
|
int argumentCount = lua_gettop(L);
|
||||||
|
if (argumentCount != 1)
|
||||||
|
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfct = get<TypeFunctionClassType>(self);
|
||||||
|
if (!tfct)
|
||||||
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
// If the parent does not exist, we should return nil
|
||||||
|
if (!tfct->readParent)
|
||||||
|
lua_pushnil(L);
|
||||||
|
else
|
||||||
|
allocTypeUserData(L, (*tfct->readParent)->type);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Luau: `self:writeparent() -> type`
|
||||||
|
// Returns the write type of the class' parent
|
||||||
|
static int getWriteParent(lua_State* L)
|
||||||
|
{
|
||||||
|
int argumentCount = lua_gettop(L);
|
||||||
|
if (argumentCount != 1)
|
||||||
|
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfct = get<TypeFunctionClassType>(self);
|
||||||
|
if (!tfct)
|
||||||
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
// If the parent does not exist, we should return nil
|
||||||
|
if (!tfct->writeParent)
|
||||||
|
lua_pushnil(L);
|
||||||
|
else
|
||||||
|
allocTypeUserData(L, (*tfct->writeParent)->type);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1553,7 +1598,7 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"components", getComponents},
|
{"components", getComponents},
|
||||||
|
|
||||||
// Class type methods
|
// Class type methods
|
||||||
{"parent", getClassParent},
|
{FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED},
|
||||||
|
|
||||||
// Function type methods (cont.)
|
// Function type methods (cont.)
|
||||||
{"setgenerics", setFunctionGenerics},
|
{"setgenerics", setFunctionGenerics},
|
||||||
|
@ -1563,6 +1608,9 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"name", getGenericName},
|
{"name", getGenericName},
|
||||||
{"ispack", getGenericIsPack},
|
{"ispack", getGenericIsPack},
|
||||||
|
|
||||||
|
// move this under Class type methods when removing FFlagLuauTypeFunReadWriteParents
|
||||||
|
{FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr},
|
||||||
|
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
// currently, controls serialization, deserialization, and `type.copy`
|
// currently, controls serialization, deserialization, and `type.copy`
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||||
LUAU_FASTFLAG(LuauTypeFunFixHydratedClasses)
|
LUAU_FASTFLAG(LuauTypeFunFixHydratedClasses)
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunReadWriteParents)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -212,13 +213,13 @@ private:
|
||||||
{
|
{
|
||||||
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the
|
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the
|
||||||
// original class
|
// original class
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, ty});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state->classesSerialized_DEPRECATED[c->name] = ty;
|
state->classesSerialized_DEPRECATED[c->name] = ty;
|
||||||
target = typeFunctionRuntime->typeArena.allocate(
|
target = typeFunctionRuntime->typeArena.allocate(
|
||||||
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name}
|
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,7 +442,20 @@ private:
|
||||||
c2->metatable = shallowSerialize(*c1->metatable);
|
c2->metatable = shallowSerialize(*c1->metatable);
|
||||||
|
|
||||||
if (c1->parent)
|
if (c1->parent)
|
||||||
c2->parent = shallowSerialize(*c1->parent);
|
{
|
||||||
|
TypeFunctionTypeId parent = shallowSerialize(*c1->parent);
|
||||||
|
|
||||||
|
if (FFlag::LuauTypeFunReadWriteParents)
|
||||||
|
{
|
||||||
|
// we don't yet have read/write parents in the type inference engine.
|
||||||
|
c2->readParent = parent;
|
||||||
|
c2->writeParent = parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c2->parent_DEPRECATED = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2)
|
void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDisableNewSolverAssertsInMixedMode);
|
||||||
// Maximum number of steps to follow when traversing a path. May not always
|
// Maximum number of steps to follow when traversing a path. May not always
|
||||||
// equate to the number of components in a path, depending on the traversal
|
// equate to the number of components in a path, depending on the traversal
|
||||||
// logic.
|
// logic.
|
||||||
|
@ -156,14 +156,16 @@ Path PathBuilder::build()
|
||||||
|
|
||||||
PathBuilder& PathBuilder::readProp(std::string name)
|
PathBuilder& PathBuilder::readProp(std::string name)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSolverV2);
|
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
LUAU_ASSERT(FFlag::LuauSolverV2);
|
||||||
components.push_back(Property{std::move(name), true});
|
components.push_back(Property{std::move(name), true});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PathBuilder& PathBuilder::writeProp(std::string name)
|
PathBuilder& PathBuilder::writeProp(std::string name)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSolverV2);
|
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
LUAU_ASSERT(FFlag::LuauSolverV2);
|
||||||
components.push_back(Property{std::move(name), false});
|
components.push_back(Property{std::move(name), false});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -550,7 +550,10 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
||||||
|
|
||||||
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSolverV2 && FFlag::LuauTrackInteriorFreeTypesOnScope);
|
if (FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
LUAU_ASSERT(FFlag::LuauTrackInteriorFreeTypesOnScope);
|
||||||
|
else
|
||||||
|
LUAU_ASSERT(FFlag::LuauSolverV2 && FFlag::LuauTrackInteriorFreeTypesOnScope);
|
||||||
for (; scope; scope = scope->parent.get())
|
for (; scope; scope = scope->parent.get())
|
||||||
{
|
{
|
||||||
if (scope->interiorFreeTypes)
|
if (scope->interiorFreeTypes)
|
||||||
|
|
|
@ -33,38 +33,20 @@ struct PromoteTypeLevels final : TypeOnceVisitor
|
||||||
const TypeArena* typeArena = nullptr;
|
const TypeArena* typeArena = nullptr;
|
||||||
TypeLevel minLevel;
|
TypeLevel minLevel;
|
||||||
|
|
||||||
Scope* outerScope = nullptr;
|
PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel)
|
||||||
bool useScopes;
|
|
||||||
|
|
||||||
PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes)
|
|
||||||
: log(log)
|
: log(log)
|
||||||
, typeArena(typeArena)
|
, typeArena(typeArena)
|
||||||
, minLevel(minLevel)
|
, minLevel(minLevel)
|
||||||
, outerScope(outerScope)
|
|
||||||
, useScopes(useScopes)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TID, typename T>
|
template<typename TID, typename T>
|
||||||
void promote(TID ty, T* t)
|
void promote(TID ty, T* t)
|
||||||
{
|
{
|
||||||
if (useScopes && !t)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LUAU_ASSERT(t);
|
LUAU_ASSERT(t);
|
||||||
|
|
||||||
if (useScopes)
|
if (minLevel.subsumesStrict(t->level))
|
||||||
{
|
log.changeLevel(ty, minLevel);
|
||||||
if (subsumesStrict(outerScope, t->scope))
|
|
||||||
log.changeScope(ty, NotNull{outerScope});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (minLevel.subsumesStrict(t->level))
|
|
||||||
{
|
|
||||||
log.changeLevel(ty, minLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override
|
||||||
|
@ -141,23 +123,23 @@ struct PromoteTypeLevels final : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypeId ty)
|
static void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypeId ty)
|
||||||
{
|
{
|
||||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||||
if (ty->owningArena != typeArena)
|
if (ty->owningArena != typeArena)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes};
|
PromoteTypeLevels ptl{log, typeArena, minLevel};
|
||||||
ptl.traverse(ty);
|
ptl.traverse(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypePackId tp)
|
void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypePackId tp)
|
||||||
{
|
{
|
||||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||||
if (tp->owningArena != typeArena)
|
if (tp->owningArena != typeArena)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes};
|
PromoteTypeLevels ptl{log, typeArena, minLevel};
|
||||||
ptl.traverse(tp);
|
ptl.traverse(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,12 +352,9 @@ static std::optional<std::pair<Luau::Name, const SingletonType*>> getTableMatchT
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TY_A, typename TY_B>
|
template<typename TY_A, typename TY_B>
|
||||||
static bool subsumes(bool useScopes, TY_A* left, TY_B* right)
|
static bool subsumes(TY_A* left, TY_B* right)
|
||||||
{
|
{
|
||||||
if (useScopes)
|
return left->level.subsumes(right->level);
|
||||||
return subsumes(left->scope, right->scope);
|
|
||||||
else
|
|
||||||
return left->level.subsumes(right->level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeMismatch::Context Unifier::mismatchContext()
|
TypeMismatch::Context Unifier::mismatchContext()
|
||||||
|
@ -464,7 +443,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
auto superFree = log.getMutable<FreeType>(superTy);
|
auto superFree = log.getMutable<FreeType>(superTy);
|
||||||
auto subFree = log.getMutable<FreeType>(subTy);
|
auto subFree = log.getMutable<FreeType>(subTy);
|
||||||
|
|
||||||
if (superFree && subFree && subsumes(useNewSolver, superFree, subFree))
|
if (superFree && subFree && subsumes(superFree, subFree))
|
||||||
{
|
{
|
||||||
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
||||||
log.replace(subTy, BoundType(superTy));
|
log.replace(subTy, BoundType(superTy));
|
||||||
|
@ -475,7 +454,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
{
|
{
|
||||||
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
||||||
{
|
{
|
||||||
if (subsumes(useNewSolver, superFree, subFree))
|
if (subsumes(superFree, subFree))
|
||||||
{
|
{
|
||||||
log.changeLevel(subTy, superFree->level);
|
log.changeLevel(subTy, superFree->level);
|
||||||
}
|
}
|
||||||
|
@ -489,7 +468,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
{
|
{
|
||||||
// Unification can't change the level of a generic.
|
// Unification can't change the level of a generic.
|
||||||
auto subGeneric = log.getMutable<GenericType>(subTy);
|
auto subGeneric = log.getMutable<GenericType>(subTy);
|
||||||
if (subGeneric && !subsumes(useNewSolver, subGeneric, superFree))
|
if (subGeneric && !subsumes(subGeneric, superFree))
|
||||||
{
|
{
|
||||||
// TODO: a more informative error message? CLI-39912
|
// TODO: a more informative error message? CLI-39912
|
||||||
reportError(location, GenericError{"Generic subtype escaping scope"});
|
reportError(location, GenericError{"Generic subtype escaping scope"});
|
||||||
|
@ -498,7 +477,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
|
|
||||||
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
||||||
{
|
{
|
||||||
promoteTypeLevels(log, types, superFree->level, superFree->scope, useNewSolver, subTy);
|
promoteTypeLevels(log, types, superFree->level, subTy);
|
||||||
|
|
||||||
Widen widen{types, builtinTypes};
|
Widen widen{types, builtinTypes};
|
||||||
log.replace(superTy, BoundType(widen(subTy)));
|
log.replace(superTy, BoundType(widen(subTy)));
|
||||||
|
@ -515,7 +494,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
|
|
||||||
// Unification can't change the level of a generic.
|
// Unification can't change the level of a generic.
|
||||||
auto superGeneric = log.getMutable<GenericType>(superTy);
|
auto superGeneric = log.getMutable<GenericType>(superTy);
|
||||||
if (superGeneric && !subsumes(useNewSolver, superGeneric, subFree))
|
if (superGeneric && !subsumes(superGeneric, subFree))
|
||||||
{
|
{
|
||||||
// TODO: a more informative error message? CLI-39912
|
// TODO: a more informative error message? CLI-39912
|
||||||
reportError(location, GenericError{"Generic supertype escaping scope"});
|
reportError(location, GenericError{"Generic supertype escaping scope"});
|
||||||
|
@ -524,7 +503,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
|
|
||||||
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
||||||
{
|
{
|
||||||
promoteTypeLevels(log, types, subFree->level, subFree->scope, useNewSolver, superTy);
|
promoteTypeLevels(log, types, subFree->level, superTy);
|
||||||
log.replace(subTy, BoundType(superTy));
|
log.replace(subTy, BoundType(superTy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +515,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
auto superGeneric = log.getMutable<GenericType>(superTy);
|
auto superGeneric = log.getMutable<GenericType>(superTy);
|
||||||
auto subGeneric = log.getMutable<GenericType>(subTy);
|
auto subGeneric = log.getMutable<GenericType>(subTy);
|
||||||
|
|
||||||
if (superGeneric && subGeneric && subsumes(useNewSolver, superGeneric, subGeneric))
|
if (superGeneric && subGeneric && subsumes(superGeneric, subGeneric))
|
||||||
{
|
{
|
||||||
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
||||||
log.replace(subTy, BoundType(superTy));
|
log.replace(subTy, BoundType(superTy));
|
||||||
|
@ -753,9 +732,6 @@ void Unifier::tryUnifyUnionWithType(TypeId subTy, const UnionType* subUnion, Typ
|
||||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||||
innerState->tryUnify_(type, superTy);
|
innerState->tryUnify_(type, superTy);
|
||||||
|
|
||||||
if (useNewSolver)
|
|
||||||
logs.push_back(std::move(innerState->log));
|
|
||||||
|
|
||||||
if (auto e = hasUnificationTooComplex(innerState->errors))
|
if (auto e = hasUnificationTooComplex(innerState->errors))
|
||||||
unificationTooComplex = e;
|
unificationTooComplex = e;
|
||||||
else if (innerState->failure)
|
else if (innerState->failure)
|
||||||
|
@ -870,13 +846,8 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp
|
||||||
if (!innerState->failure)
|
if (!innerState->failure)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
if (useNewSolver)
|
log.concat(std::move(innerState->log));
|
||||||
logs.push_back(std::move(innerState->log));
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
log.concat(std::move(innerState->log));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (innerState->errors.empty())
|
else if (innerState->errors.empty())
|
||||||
{
|
{
|
||||||
|
@ -895,9 +866,6 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver)
|
|
||||||
log.concatAsUnion(combineLogsIntoUnion(std::move(logs)), NotNull{types});
|
|
||||||
|
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
{
|
{
|
||||||
reportError(*unificationTooComplex);
|
reportError(*unificationTooComplex);
|
||||||
|
@ -975,16 +943,10 @@ void Unifier::tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const I
|
||||||
firstFailedOption = {innerState->errors.front()};
|
firstFailedOption = {innerState->errors.front()};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver)
|
log.concat(std::move(innerState->log));
|
||||||
logs.push_back(std::move(innerState->log));
|
|
||||||
else
|
|
||||||
log.concat(std::move(innerState->log));
|
|
||||||
failure |= innerState->failure;
|
failure |= innerState->failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver)
|
|
||||||
log.concat(combineLogsIntoIntersection(std::move(logs)));
|
|
||||||
|
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
reportError(*unificationTooComplex);
|
reportError(*unificationTooComplex);
|
||||||
else if (firstFailedOption)
|
else if (firstFailedOption)
|
||||||
|
@ -1032,28 +994,6 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver && normalize)
|
|
||||||
{
|
|
||||||
// Sometimes a negation type is inside one of the types, e.g. { p: number } & { p: ~number }.
|
|
||||||
NegationTypeFinder finder;
|
|
||||||
finder.traverse(subTy);
|
|
||||||
|
|
||||||
if (finder.found)
|
|
||||||
{
|
|
||||||
// It is possible that A & B <: T even though A </: T and B </: T
|
|
||||||
// for example (string?) & ~nil <: string.
|
|
||||||
// We deal with this by type normalization.
|
|
||||||
std::shared_ptr<const NormalizedType> subNorm = normalizer->normalize(subTy);
|
|
||||||
std::shared_ptr<const NormalizedType> superNorm = normalizer->normalize(superTy);
|
|
||||||
if (subNorm && superNorm)
|
|
||||||
tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the intersection parts are compatible");
|
|
||||||
else
|
|
||||||
reportError(location, NormalizationTooComplex{});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TxnLog> logs;
|
std::vector<TxnLog> logs;
|
||||||
|
|
||||||
for (size_t i = 0; i < uv->parts.size(); ++i)
|
for (size_t i = 0; i < uv->parts.size(); ++i)
|
||||||
|
@ -1070,7 +1010,7 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType*
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
errorsSuppressed = innerState->failure;
|
errorsSuppressed = innerState->failure;
|
||||||
if (useNewSolver || innerState->failure)
|
if (innerState->failure)
|
||||||
logs.push_back(std::move(innerState->log));
|
logs.push_back(std::move(innerState->log));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1085,9 +1025,7 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver)
|
if (errorsSuppressed)
|
||||||
log.concat(combineLogsIntoIntersection(std::move(logs)));
|
|
||||||
else if (errorsSuppressed)
|
|
||||||
log.concat(std::move(logs.front()));
|
log.concat(std::move(logs.front()));
|
||||||
|
|
||||||
if (unificationTooComplex)
|
if (unificationTooComplex)
|
||||||
|
@ -1201,24 +1139,6 @@ void Unifier::tryUnifyNormalizedTypes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useNewSolver)
|
|
||||||
{
|
|
||||||
for (TypeId superTable : superNorm.tables)
|
|
||||||
{
|
|
||||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
|
||||||
innerState->tryUnify(subClass, superTable);
|
|
||||||
|
|
||||||
if (innerState->errors.empty())
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
log.concat(std::move(innerState->log));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (auto e = hasUnificationTooComplex(innerState->errors))
|
|
||||||
return reportError(*e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
|
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
|
||||||
|
@ -1503,12 +1423,6 @@ struct WeirdIter
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Unifier::enableNewSolver()
|
|
||||||
{
|
|
||||||
useNewSolver = true;
|
|
||||||
log.useScopes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorVec Unifier::canUnify(TypeId subTy, TypeId superTy)
|
ErrorVec Unifier::canUnify(TypeId subTy, TypeId superTy)
|
||||||
{
|
{
|
||||||
std::unique_ptr<Unifier> s = makeChildUnifier();
|
std::unique_ptr<Unifier> s = makeChildUnifier();
|
||||||
|
@ -1588,8 +1502,6 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
if (!occursCheck(superTp, subTp, /* reversed = */ true))
|
if (!occursCheck(superTp, subTp, /* reversed = */ true))
|
||||||
{
|
{
|
||||||
Widen widen{types, builtinTypes};
|
Widen widen{types, builtinTypes};
|
||||||
if (useNewSolver)
|
|
||||||
promoteTypeLevels(log, types, superFree->level, superFree->scope, /*useScopes*/ true, subTp);
|
|
||||||
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
|
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1597,8 +1509,6 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
{
|
{
|
||||||
if (!occursCheck(subTp, superTp, /* reversed = */ false))
|
if (!occursCheck(subTp, superTp, /* reversed = */ false))
|
||||||
{
|
{
|
||||||
if (useNewSolver)
|
|
||||||
promoteTypeLevels(log, types, subFree->level, subFree->scope, /*useScopes*/ true, superTp);
|
|
||||||
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
|
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1688,74 +1598,28 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
// If both are at the end, we're done
|
// If both are at the end, we're done
|
||||||
if (!superIter.good() && !subIter.good())
|
if (!superIter.good() && !subIter.good())
|
||||||
{
|
{
|
||||||
if (useNewSolver)
|
const bool lFreeTail = superTpv->tail && log.getMutable<FreeTypePack>(log.follow(*superTpv->tail)) != nullptr;
|
||||||
|
const bool rFreeTail = subTpv->tail && log.getMutable<FreeTypePack>(log.follow(*subTpv->tail)) != nullptr;
|
||||||
|
if (lFreeTail && rFreeTail)
|
||||||
{
|
{
|
||||||
if (subIter.tail() && superIter.tail())
|
tryUnify_(*subTpv->tail, *superTpv->tail);
|
||||||
tryUnify_(*subIter.tail(), *superIter.tail());
|
|
||||||
else if (subIter.tail())
|
|
||||||
{
|
|
||||||
const TypePackId subTail = log.follow(*subIter.tail());
|
|
||||||
|
|
||||||
if (log.get<FreeTypePack>(subTail))
|
|
||||||
tryUnify_(subTail, emptyTp);
|
|
||||||
else if (log.get<GenericTypePack>(subTail))
|
|
||||||
reportError(location, TypePackMismatch{subTail, emptyTp});
|
|
||||||
else if (log.get<VariadicTypePack>(subTail) || log.get<ErrorTypePack>(subTail))
|
|
||||||
{
|
|
||||||
// Nothing. This is ok.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ice("Unexpected subtype tail pack " + toString(subTail), location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (superIter.tail())
|
|
||||||
{
|
|
||||||
const TypePackId superTail = log.follow(*superIter.tail());
|
|
||||||
|
|
||||||
if (log.get<FreeTypePack>(superTail))
|
|
||||||
tryUnify_(emptyTp, superTail);
|
|
||||||
else if (log.get<GenericTypePack>(superTail))
|
|
||||||
reportError(location, TypePackMismatch{emptyTp, superTail});
|
|
||||||
else if (log.get<VariadicTypePack>(superTail) || log.get<ErrorTypePack>(superTail))
|
|
||||||
{
|
|
||||||
// Nothing. This is ok.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ice("Unexpected supertype tail pack " + toString(superTail), location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Nothing. This is ok.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (lFreeTail)
|
||||||
{
|
{
|
||||||
const bool lFreeTail = superTpv->tail && log.getMutable<FreeTypePack>(log.follow(*superTpv->tail)) != nullptr;
|
tryUnify_(emptyTp, *superTpv->tail);
|
||||||
const bool rFreeTail = subTpv->tail && log.getMutable<FreeTypePack>(log.follow(*subTpv->tail)) != nullptr;
|
}
|
||||||
if (lFreeTail && rFreeTail)
|
else if (rFreeTail)
|
||||||
{
|
{
|
||||||
|
tryUnify_(emptyTp, *subTpv->tail);
|
||||||
|
}
|
||||||
|
else if (subTpv->tail && superTpv->tail)
|
||||||
|
{
|
||||||
|
if (log.getMutable<VariadicTypePack>(superIter.packId))
|
||||||
|
tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index));
|
||||||
|
else if (log.getMutable<VariadicTypePack>(subIter.packId))
|
||||||
|
tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index));
|
||||||
|
else
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
tryUnify_(*subTpv->tail, *superTpv->tail);
|
||||||
}
|
|
||||||
else if (lFreeTail)
|
|
||||||
{
|
|
||||||
tryUnify_(emptyTp, *superTpv->tail);
|
|
||||||
}
|
|
||||||
else if (rFreeTail)
|
|
||||||
{
|
|
||||||
tryUnify_(emptyTp, *subTpv->tail);
|
|
||||||
}
|
|
||||||
else if (subTpv->tail && superTpv->tail)
|
|
||||||
{
|
|
||||||
if (log.getMutable<VariadicTypePack>(superIter.packId))
|
|
||||||
tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index));
|
|
||||||
else if (log.getMutable<VariadicTypePack>(subIter.packId))
|
|
||||||
tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index));
|
|
||||||
else
|
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -2212,7 +2076,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
||||||
variance = Invariant;
|
variance = Invariant;
|
||||||
|
|
||||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||||
if (useNewSolver || FFlag::LuauFixIndexerSubtypingOrdering)
|
if (FFlag::LuauFixIndexerSubtypingOrdering)
|
||||||
innerState->tryUnify_(prop.type(), superTable->indexer->indexResultType);
|
innerState->tryUnify_(prop.type(), superTable->indexer->indexResultType);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2497,49 +2361,8 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
{
|
{
|
||||||
case TableState::Free:
|
case TableState::Free:
|
||||||
{
|
{
|
||||||
if (useNewSolver)
|
tryUnify_(subTy, superMetatable->table);
|
||||||
{
|
log.bindTable(subTy, superTy);
|
||||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
|
||||||
bool missingProperty = false;
|
|
||||||
|
|
||||||
for (const auto& [propName, prop] : subTable->props)
|
|
||||||
{
|
|
||||||
if (std::optional<TypeId> mtPropTy = findTablePropertyRespectingMeta(superTy, propName))
|
|
||||||
{
|
|
||||||
innerState->tryUnify(prop.type(), *mtPropTy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reportError(mismatchError);
|
|
||||||
missingProperty = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const TableType* superTable = log.get<TableType>(log.follow(superMetatable->table)))
|
|
||||||
{
|
|
||||||
// TODO: Unify indexers.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto e = hasUnificationTooComplex(innerState->errors))
|
|
||||||
reportError(*e);
|
|
||||||
else if (!innerState->errors.empty())
|
|
||||||
reportError(TypeError{
|
|
||||||
location,
|
|
||||||
TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy, "", innerState->errors.front(), mismatchContext()}
|
|
||||||
});
|
|
||||||
else if (!missingProperty)
|
|
||||||
{
|
|
||||||
log.concat(std::move(innerState->log));
|
|
||||||
log.bindTable(subTy, superTy);
|
|
||||||
failure |= innerState->failure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tryUnify_(subTy, superMetatable->table);
|
|
||||||
log.bindTable(subTy, superTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2865,18 +2688,9 @@ std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N
|
||||||
return Luau::findTablePropertyRespectingMeta(builtinTypes, errors, lhsType, name, location);
|
return Luau::findTablePropertyRespectingMeta(builtinTypes, errors, lhsType, name, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
TxnLog Unifier::combineLogsIntoIntersection(std::vector<TxnLog> logs)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(useNewSolver);
|
|
||||||
TxnLog result(useNewSolver);
|
|
||||||
for (TxnLog& log : logs)
|
|
||||||
result.concatAsIntersections(std::move(log), NotNull{types});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TxnLog Unifier::combineLogsIntoUnion(std::vector<TxnLog> logs)
|
TxnLog Unifier::combineLogsIntoUnion(std::vector<TxnLog> logs)
|
||||||
{
|
{
|
||||||
TxnLog result(useNewSolver);
|
TxnLog result;
|
||||||
for (TxnLog& log : logs)
|
for (TxnLog& log : logs)
|
||||||
result.concatAsUnion(std::move(log), NotNull{types});
|
result.concatAsUnion(std::move(log), NotNull{types});
|
||||||
return result;
|
return result;
|
||||||
|
@ -3021,9 +2835,6 @@ std::unique_ptr<Unifier> Unifier::makeChildUnifier()
|
||||||
u->normalize = normalize;
|
u->normalize = normalize;
|
||||||
u->checkInhabited = checkInhabited;
|
u->checkInhabited = checkInhabited;
|
||||||
|
|
||||||
if (useNewSolver)
|
|
||||||
u->enableNewSolver();
|
|
||||||
|
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,19 @@ struct ParseResult
|
||||||
CstNodeMap cstNodeMap{nullptr};
|
CstNodeMap cstNodeMap{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ParseExprResult
|
||||||
|
{
|
||||||
|
AstExpr* expr;
|
||||||
|
size_t lines = 0;
|
||||||
|
|
||||||
|
std::vector<HotComment> hotcomments;
|
||||||
|
std::vector<ParseError> errors;
|
||||||
|
|
||||||
|
std::vector<Comment> commentLocations;
|
||||||
|
|
||||||
|
CstNodeMap cstNodeMap{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr const char* kParseNameError = "%error-id%";
|
static constexpr const char* kParseNameError = "%error-id%";
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -63,6 +63,14 @@ public:
|
||||||
ParseOptions options = ParseOptions()
|
ParseOptions options = ParseOptions()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static ParseExprResult parseExpr(
|
||||||
|
const char* buffer,
|
||||||
|
std::size_t bufferSize,
|
||||||
|
AstNameTable& names,
|
||||||
|
Allocator& allocator,
|
||||||
|
ParseOptions options = ParseOptions()
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Name;
|
struct Name;
|
||||||
struct Binding;
|
struct Binding;
|
||||||
|
|
|
@ -18,7 +18,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
// flag so that we don't break production games by reverting syntax changes.
|
// flag so that we don't break production games by reverting syntax changes.
|
||||||
// See docs/SyntaxChanges.md for an explanation.
|
// See docs/SyntaxChanges.md for an explanation.
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames)
|
||||||
|
@ -26,7 +25,7 @@ LUAU_FASTFLAGVARIABLE(LuauFixFunctionNameStartPosition)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAGVARIABLE(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData)
|
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup)
|
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup2)
|
||||||
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
|
@ -182,6 +181,28 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseExprResult Parser::parseExpr(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options)
|
||||||
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
|
||||||
|
|
||||||
|
Parser p(buffer, bufferSize, names, allocator, options);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AstExpr* expr = p.parseExpr();
|
||||||
|
size_t lines = p.lexer.current().location.end.line + (bufferSize > 0 && buffer[bufferSize - 1] != '\n');
|
||||||
|
|
||||||
|
return ParseExprResult{expr, lines, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations), std::move(p.cstNodeMap)};
|
||||||
|
}
|
||||||
|
catch (ParseError& err)
|
||||||
|
{
|
||||||
|
// when catching a fatal error, append it to the list of non-fatal errors and return
|
||||||
|
p.parseErrors.push_back(err);
|
||||||
|
|
||||||
|
return ParseExprResult{nullptr, 0, {}, p.parseErrors, {}, std::move(p.cstNodeMap)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options)
|
Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options)
|
||||||
: options(options)
|
: options(options)
|
||||||
, lexer(buffer, bufferSize, names, options.parseFragment ? options.parseFragment->resumePosition : Position(0, 0))
|
, lexer(buffer, bufferSize, names, options.parseFragment ? options.parseFragment->resumePosition : Position(0, 0))
|
||||||
|
@ -197,18 +218,9 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc
|
||||||
functionStack.reserve(8);
|
functionStack.reserve(8);
|
||||||
functionStack.push_back(top);
|
functionStack.push_back(top);
|
||||||
|
|
||||||
if (FFlag::LuauAllowFragmentParsing)
|
nameSelf = names.getOrAdd("self");
|
||||||
{
|
nameNumber = names.getOrAdd("number");
|
||||||
nameSelf = names.getOrAdd("self");
|
nameError = names.getOrAdd(kParseNameError);
|
||||||
nameNumber = names.getOrAdd("number");
|
|
||||||
nameError = names.getOrAdd(kParseNameError);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nameSelf = names.addStatic("self");
|
|
||||||
nameNumber = names.addStatic("number");
|
|
||||||
nameError = names.addStatic(kParseNameError);
|
|
||||||
}
|
|
||||||
nameNil = names.getOrAdd("nil"); // nil is a reserved keyword
|
nameNil = names.getOrAdd("nil"); // nil is a reserved keyword
|
||||||
|
|
||||||
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
||||||
|
@ -231,13 +243,10 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc
|
||||||
scratchLocal.reserve(16);
|
scratchLocal.reserve(16);
|
||||||
scratchBinding.reserve(16);
|
scratchBinding.reserve(16);
|
||||||
|
|
||||||
if (FFlag::LuauAllowFragmentParsing)
|
if (options.parseFragment)
|
||||||
{
|
{
|
||||||
if (options.parseFragment)
|
localMap = options.parseFragment->localMap;
|
||||||
{
|
localStack = options.parseFragment->localStack;
|
||||||
localMap = options.parseFragment->localMap;
|
|
||||||
localStack = options.parseFragment->localStack;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1723,7 +1732,7 @@ std::pair<Location, AstTypeList> Parser::parseReturnType()
|
||||||
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
|
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
|
||||||
{
|
{
|
||||||
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
|
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
if (result.size() == 1 && varargAnnotation == nullptr)
|
if (result.size() == 1 && varargAnnotation == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -2034,6 +2043,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray<AstAttr*>
|
||||||
if (lexer.current().type != ')')
|
if (lexer.current().type != ')')
|
||||||
varargAnnotation = parseTypeList(params, names);
|
varargAnnotation = parseTypeList(params, names);
|
||||||
|
|
||||||
|
Location closeArgsLocation = lexer.current().location;
|
||||||
expectMatchAndConsume(')', parameterStart, true);
|
expectMatchAndConsume(')', parameterStart, true);
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
||||||
|
@ -2052,8 +2062,8 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray<AstAttr*>
|
||||||
return {{}, allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, nullptr})};
|
return {{}, allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, nullptr})};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
return {allocator.alloc<AstTypeGroup>(Location(parameterStart.location, params[0]->location), params[0]), {}};
|
return {allocator.alloc<AstTypeGroup>(Location(parameterStart.location, closeArgsLocation), params[0]), {}};
|
||||||
else
|
else
|
||||||
return {params[0], {}};
|
return {params[0], {}};
|
||||||
}
|
}
|
||||||
|
@ -3562,7 +3572,7 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams(Position* openingPosition, TempV
|
||||||
// the next lexeme is one that follows a type
|
// the next lexeme is one that follows a type
|
||||||
// (&, |, ?), then assume that this was actually a
|
// (&, |, ?), then assume that this was actually a
|
||||||
// parenthesized type.
|
// parenthesized type.
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
||||||
parameters.push_back(
|
parameters.push_back(
|
||||||
|
|
|
@ -791,8 +791,6 @@ int replMain(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Luau::assertHandler() = assertionHandler;
|
Luau::assertHandler() = assertionHandler;
|
||||||
|
|
||||||
setLuauFlagsDefault();
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Repl.h"
|
#include "Luau/Repl.h"
|
||||||
|
#include "Luau/Flags.h"
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
setLuauFlagsDefault();
|
||||||
|
|
||||||
return replMain(argc, argv);
|
return replMain(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering)
|
||||||
LUAU_FASTFLAGVARIABLE(CodegenWiderLoweringStats)
|
|
||||||
|
|
||||||
// Per-module IR instruction count limit
|
// Per-module IR instruction count limit
|
||||||
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M
|
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
LUAU_FASTFLAG(DebugCodegenNoOpt)
|
LUAU_FASTFLAG(DebugCodegenNoOpt)
|
||||||
LUAU_FASTFLAG(DebugCodegenOptSize)
|
LUAU_FASTFLAG(DebugCodegenOptSize)
|
||||||
LUAU_FASTFLAG(DebugCodegenSkipNumbering)
|
LUAU_FASTFLAG(DebugCodegenSkipNumbering)
|
||||||
LUAU_FASTFLAG(CodegenWiderLoweringStats)
|
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_FASTINT(CodegenHeuristicsBlockLimit)
|
LUAU_FASTINT(CodegenHeuristicsBlockLimit)
|
||||||
LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit)
|
||||||
|
@ -300,8 +299,7 @@ inline bool lowerFunction(
|
||||||
CodeGenCompilationResult& codeGenCompilationResult
|
CodeGenCompilationResult& codeGenCompilationResult
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (FFlag::CodegenWiderLoweringStats)
|
ir.function.stats = stats;
|
||||||
ir.function.stats = stats;
|
|
||||||
|
|
||||||
killUnusedBlocks(ir.function);
|
killUnusedBlocks(ir.function);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAG(LuauCodeGenLerp)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -706,7 +705,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
}
|
}
|
||||||
case IrCmd::SELECT_NUM:
|
case IrCmd::SELECT_NUM:
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauCodeGenLerp);
|
|
||||||
inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a, inst.b, inst.c, inst.d});
|
inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a, inst.b, inst.c, inst.d});
|
||||||
|
|
||||||
RegisterA64 temp1 = tempDouble(inst.a);
|
RegisterA64 temp1 = tempDouble(inst.a);
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAG(LuauCodeGenLerp)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -625,7 +624,6 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
}
|
}
|
||||||
case IrCmd::SELECT_NUM:
|
case IrCmd::SELECT_NUM:
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauCodeGenLerp);
|
|
||||||
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.c, inst.d}); // can't reuse b if a is a memory operand
|
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.c, inst.d}); // can't reuse b if a is a memory operand
|
||||||
|
|
||||||
ScopedRegX64 tmp{regs, SizeX64::xmmword};
|
ScopedRegX64 tmp{regs, SizeX64::xmmword};
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
static const int kMinMaxUnrolledParams = 5;
|
static const int kMinMaxUnrolledParams = 5;
|
||||||
static const int kBit32BinaryOpUnrolledParams = 5;
|
static const int kBit32BinaryOpUnrolledParams = 5;
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeCodegen);
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeDot);
|
LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeDot);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenLerp);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -297,8 +295,6 @@ static BuiltinImplResult translateBuiltinMathLerp(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauCodeGenLerp);
|
|
||||||
|
|
||||||
if (nparams < 3 || nresults > 1)
|
if (nparams < 3 || nresults > 1)
|
||||||
return {BuiltinImplType::None, -1};
|
return {BuiltinImplType::None, -1};
|
||||||
|
|
||||||
|
@ -936,8 +932,6 @@ static BuiltinImplResult translateBuiltinVectorMagnitude(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
||||||
|
@ -985,8 +979,6 @@ static BuiltinImplResult translateBuiltinVectorNormalize(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
||||||
|
@ -1037,8 +1029,6 @@ static BuiltinImplResult translateBuiltinVectorNormalize(
|
||||||
|
|
||||||
static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
|
static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
||||||
|
@ -1076,8 +1066,6 @@ static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int npara
|
||||||
|
|
||||||
static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
|
static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
||||||
|
@ -1130,8 +1118,6 @@ static BuiltinImplResult translateBuiltinVectorMap1(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
|
||||||
|
@ -1165,8 +1151,6 @@ static BuiltinImplResult translateBuiltinVectorClamp(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 3 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant || arg3.kind == IrOpKind::Constant)
|
if (nparams != 3 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant || arg3.kind == IrOpKind::Constant)
|
||||||
|
@ -1231,8 +1215,6 @@ static BuiltinImplResult translateBuiltinVectorMap2(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
|
|
||||||
|
|
||||||
IrOp arg1 = build.vmReg(arg);
|
IrOp arg1 = build.vmReg(arg);
|
||||||
|
|
||||||
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
|
||||||
|
@ -1273,8 +1255,6 @@ BuiltinImplResult translateBuiltin(
|
||||||
int pcpos
|
int pcpos
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BuiltinImplResult noneResult = {BuiltinImplType::None, -1};
|
|
||||||
|
|
||||||
// Builtins are not allowed to handle variadic arguments
|
// Builtins are not allowed to handle variadic arguments
|
||||||
if (nparams == LUA_MULTRET)
|
if (nparams == LUA_MULTRET)
|
||||||
return {BuiltinImplType::None, -1};
|
return {BuiltinImplType::None, -1};
|
||||||
|
@ -1396,36 +1376,29 @@ BuiltinImplResult translateBuiltin(
|
||||||
case LBF_BUFFER_WRITEF64:
|
case LBF_BUFFER_WRITEF64:
|
||||||
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP);
|
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP);
|
||||||
case LBF_VECTOR_MAGNITUDE:
|
case LBF_VECTOR_MAGNITUDE:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
|
return translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
case LBF_VECTOR_NORMALIZE:
|
case LBF_VECTOR_NORMALIZE:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
|
return translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
case LBF_VECTOR_CROSS:
|
case LBF_VECTOR_CROSS:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
|
return translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
case LBF_VECTOR_DOT:
|
case LBF_VECTOR_DOT:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
|
return translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
case LBF_VECTOR_FLOOR:
|
case LBF_VECTOR_FLOOR:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::FLOOR_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap1(build, IrCmd::FLOOR_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_CEIL:
|
case LBF_VECTOR_CEIL:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::CEIL_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap1(build, IrCmd::CEIL_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_ABS:
|
case LBF_VECTOR_ABS:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::ABS_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap1(build, IrCmd::ABS_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_SIGN:
|
case LBF_VECTOR_SIGN:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::SIGN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap1(build, IrCmd::SIGN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_CLAMP:
|
case LBF_VECTOR_CLAMP:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos)
|
return translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap2(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
|
return translateBuiltinVectorMap2(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
|
||||||
: noneResult;
|
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
return FFlag::LuauCodeGenLerp ? translateBuiltinMathLerp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos) : noneResult;
|
return translateBuiltinMathLerp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos);
|
||||||
default:
|
default:
|
||||||
return {BuiltinImplType::None, -1};
|
return {BuiltinImplType::None, -1};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
||||||
LUAU_FASTFLAG(LuauCodeGenLerp);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -663,7 +662,6 @@ void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint3
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IrCmd::SELECT_NUM:
|
case IrCmd::SELECT_NUM:
|
||||||
LUAU_ASSERT(FFlag::LuauCodeGenLerp);
|
|
||||||
if (inst.c.kind == IrOpKind::Constant && inst.d.kind == IrOpKind::Constant)
|
if (inst.c.kind == IrOpKind::Constant && inst.d.kind == IrOpKind::Constant)
|
||||||
{
|
{
|
||||||
double c = function.doubleOp(inst.c);
|
double c = function.doubleOp(inst.c);
|
||||||
|
|
|
@ -22,7 +22,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenLimitLiveSlotReuse)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -200,11 +199,7 @@ struct ConstPropState
|
||||||
// Same goes for table array elements as well
|
// Same goes for table array elements as well
|
||||||
void invalidateHeapTableData()
|
void invalidateHeapTableData()
|
||||||
{
|
{
|
||||||
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
getSlotNodeCache.clear();
|
||||||
getSlotNodeCache.clear();
|
|
||||||
else
|
|
||||||
getSlotNodeCache_DEPRECATED.clear();
|
|
||||||
|
|
||||||
checkSlotMatchCache.clear();
|
checkSlotMatchCache.clear();
|
||||||
|
|
||||||
getArrAddrCache.clear();
|
getArrAddrCache.clear();
|
||||||
|
@ -428,8 +423,6 @@ struct ConstPropState
|
||||||
// Note that this pressure is approximate, as some values that might have been live at one point could have been marked dead later
|
// Note that this pressure is approximate, as some values that might have been live at one point could have been marked dead later
|
||||||
int getMaxInternalOverlap(std::vector<NumberedInstruction>& set, size_t slot)
|
int getMaxInternalOverlap(std::vector<NumberedInstruction>& set, size_t slot)
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(FFlag::LuauCodeGenLimitLiveSlotReuse);
|
|
||||||
|
|
||||||
// Start with one live range for the slot we want to reuse
|
// Start with one live range for the slot we want to reuse
|
||||||
int curr = 1;
|
int curr = 1;
|
||||||
|
|
||||||
|
@ -487,9 +480,7 @@ struct ConstPropState
|
||||||
regs[i] = RegisterInfo();
|
regs[i] = RegisterInfo();
|
||||||
|
|
||||||
maxReg = 0;
|
maxReg = 0;
|
||||||
|
instPos = 0u;
|
||||||
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
|
||||||
instPos = 0u;
|
|
||||||
|
|
||||||
inSafeEnv = false;
|
inSafeEnv = false;
|
||||||
checkedGc = false;
|
checkedGc = false;
|
||||||
|
@ -526,7 +517,6 @@ struct ConstPropState
|
||||||
|
|
||||||
// Heap changes might affect table state
|
// Heap changes might affect table state
|
||||||
std::vector<NumberedInstruction> getSlotNodeCache; // Additionally, pcpos argument might be different
|
std::vector<NumberedInstruction> getSlotNodeCache; // Additionally, pcpos argument might be different
|
||||||
std::vector<uint32_t> getSlotNodeCache_DEPRECATED; // Additionally, pcpos argument might be different
|
|
||||||
std::vector<uint32_t> checkSlotMatchCache; // Additionally, fallback block argument might be different
|
std::vector<uint32_t> checkSlotMatchCache; // Additionally, fallback block argument might be different
|
||||||
|
|
||||||
std::vector<uint32_t> getArrAddrCache;
|
std::vector<uint32_t> getArrAddrCache;
|
||||||
|
@ -651,8 +641,7 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
|
||||||
|
|
||||||
static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& function, IrBlock& block, IrInst& inst, uint32_t index)
|
static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& function, IrBlock& block, IrInst& inst, uint32_t index)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
state.instPos++;
|
||||||
state.instPos++;
|
|
||||||
|
|
||||||
switch (inst.cmd)
|
switch (inst.cmd)
|
||||||
{
|
{
|
||||||
|
@ -1260,49 +1249,30 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
state.getArrAddrCache.push_back(index);
|
state.getArrAddrCache.push_back(index);
|
||||||
break;
|
break;
|
||||||
case IrCmd::GET_SLOT_NODE_ADDR:
|
case IrCmd::GET_SLOT_NODE_ADDR:
|
||||||
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
for (size_t i = 0; i < state.getSlotNodeCache.size(); i++)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < state.getSlotNodeCache.size(); i++)
|
auto&& [prevIdx, num, lastNum] = state.getSlotNodeCache[i];
|
||||||
|
|
||||||
|
const IrInst& prev = function.instructions[prevIdx];
|
||||||
|
|
||||||
|
if (prev.a == inst.a && prev.c == inst.c)
|
||||||
{
|
{
|
||||||
auto&& [prevIdx, num, lastNum] = state.getSlotNodeCache[i];
|
// Check if this reuse will increase the overall register pressure over the limit
|
||||||
|
int limit = FInt::LuauCodeGenLiveSlotReuseLimit;
|
||||||
|
|
||||||
const IrInst& prev = function.instructions[prevIdx];
|
if (int(state.getSlotNodeCache.size()) > limit && state.getMaxInternalOverlap(state.getSlotNodeCache, i) > limit)
|
||||||
|
return;
|
||||||
|
|
||||||
if (prev.a == inst.a && prev.c == inst.c)
|
// Update live range of the value from the optimization standpoint
|
||||||
{
|
lastNum = state.instPos;
|
||||||
// Check if this reuse will increase the overall register pressure over the limit
|
|
||||||
int limit = FInt::LuauCodeGenLiveSlotReuseLimit;
|
|
||||||
|
|
||||||
if (int(state.getSlotNodeCache.size()) > limit && state.getMaxInternalOverlap(state.getSlotNodeCache, i) > limit)
|
substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx});
|
||||||
return;
|
return; // Break out from both the loop and the switch
|
||||||
|
|
||||||
// Update live range of the value from the optimization standpoint
|
|
||||||
lastNum = state.instPos;
|
|
||||||
|
|
||||||
substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx});
|
|
||||||
return; // Break out from both the loop and the switch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
|
||||||
state.getSlotNodeCache.push_back({index, state.instPos, state.instPos});
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
for (uint32_t prevIdx : state.getSlotNodeCache_DEPRECATED)
|
|
||||||
{
|
|
||||||
const IrInst& prev = function.instructions[prevIdx];
|
|
||||||
|
|
||||||
if (prev.a == inst.a && prev.c == inst.c)
|
if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
||||||
{
|
state.getSlotNodeCache.push_back({index, state.instPos, state.instPos});
|
||||||
substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx});
|
|
||||||
return; // Break out from both the loop and the switch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int(state.getSlotNodeCache_DEPRECATED.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
|
||||||
state.getSlotNodeCache_DEPRECATED.push_back(index);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case IrCmd::GET_HASH_NODE_ADDR:
|
case IrCmd::GET_HASH_NODE_ADDR:
|
||||||
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
|
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVector2Constants)
|
|
||||||
LUAU_FASTFLAG(LuauCompileMathLerp)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -476,7 +473,7 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count)
|
||||||
case LBF_VECTOR:
|
case LBF_VECTOR:
|
||||||
if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
|
if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
|
||||||
{
|
{
|
||||||
if (count == 2 && FFlag::LuauVector2Constants)
|
if (count == 2)
|
||||||
return cvector(args[0].valueNumber, args[1].valueNumber, 0.0, 0.0);
|
return cvector(args[0].valueNumber, args[1].valueNumber, 0.0, 0.0);
|
||||||
else if (count == 3 && args[2].type == Constant::Type_Number)
|
else if (count == 3 && args[2].type == Constant::Type_Number)
|
||||||
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, 0.0);
|
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, 0.0);
|
||||||
|
@ -486,8 +483,7 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
if (FFlag::LuauCompileMathLerp && count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number &&
|
if (count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number)
|
||||||
args[2].type == Constant::Type_Number)
|
|
||||||
{
|
{
|
||||||
double a = args[0].valueNumber;
|
double a = args[0].valueNumber;
|
||||||
double b = args[1].valueNumber;
|
double b = args[1].valueNumber;
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileMathLerp)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -139,7 +137,7 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
||||||
return LBF_MATH_SIGN;
|
return LBF_MATH_SIGN;
|
||||||
if (builtin.method == "round")
|
if (builtin.method == "round")
|
||||||
return LBF_MATH_ROUND;
|
return LBF_MATH_ROUND;
|
||||||
if (FFlag::LuauCompileMathLerp && builtin.method == "lerp")
|
if (builtin.method == "lerp")
|
||||||
return LBF_MATH_LERP;
|
return LBF_MATH_LERP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +554,6 @@ BuiltinInfo getBuiltinInfo(int bfid)
|
||||||
return {-1, 1}; // variadic
|
return {-1, 1}; // variadic
|
||||||
|
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
LUAU_ASSERT(FFlag::LuauCompileMathLerp);
|
|
||||||
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBufferBitMethods2)
|
|
||||||
|
|
||||||
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
||||||
// in the current implementation, length and offset are limited to 31 bits
|
// in the current implementation, length and offset are limited to 31 bits
|
||||||
// because offset is limited to an integer, a single 64bit comparison can be used and will not overflow
|
// because offset is limited to an integer, a single 64bit comparison can be used and will not overflow
|
||||||
|
@ -330,34 +328,6 @@ static int buffer_writebits(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg bufferlib_DEPRECATED[] = {
|
|
||||||
{"create", buffer_create},
|
|
||||||
{"fromstring", buffer_fromstring},
|
|
||||||
{"tostring", buffer_tostring},
|
|
||||||
{"readi8", buffer_readinteger<int8_t>},
|
|
||||||
{"readu8", buffer_readinteger<uint8_t>},
|
|
||||||
{"readi16", buffer_readinteger<int16_t>},
|
|
||||||
{"readu16", buffer_readinteger<uint16_t>},
|
|
||||||
{"readi32", buffer_readinteger<int32_t>},
|
|
||||||
{"readu32", buffer_readinteger<uint32_t>},
|
|
||||||
{"readf32", buffer_readfp<float, uint32_t>},
|
|
||||||
{"readf64", buffer_readfp<double, uint64_t>},
|
|
||||||
{"writei8", buffer_writeinteger<int8_t>},
|
|
||||||
{"writeu8", buffer_writeinteger<uint8_t>},
|
|
||||||
{"writei16", buffer_writeinteger<int16_t>},
|
|
||||||
{"writeu16", buffer_writeinteger<uint16_t>},
|
|
||||||
{"writei32", buffer_writeinteger<int32_t>},
|
|
||||||
{"writeu32", buffer_writeinteger<uint32_t>},
|
|
||||||
{"writef32", buffer_writefp<float, uint32_t>},
|
|
||||||
{"writef64", buffer_writefp<double, uint64_t>},
|
|
||||||
{"readstring", buffer_readstring},
|
|
||||||
{"writestring", buffer_writestring},
|
|
||||||
{"len", buffer_len},
|
|
||||||
{"copy", buffer_copy},
|
|
||||||
{"fill", buffer_fill},
|
|
||||||
{NULL, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const luaL_Reg bufferlib[] = {
|
static const luaL_Reg bufferlib[] = {
|
||||||
{"create", buffer_create},
|
{"create", buffer_create},
|
||||||
{"fromstring", buffer_fromstring},
|
{"fromstring", buffer_fromstring},
|
||||||
|
@ -390,7 +360,7 @@ static const luaL_Reg bufferlib[] = {
|
||||||
|
|
||||||
int luaopen_buffer(lua_State* L)
|
int luaopen_buffer(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_register(L, LUA_BUFFERLIBNAME, FFlag::LuauBufferBitMethods2 ? bufferlib : bufferlib_DEPRECATED);
|
luaL_register(L, LUA_BUFFERLIBNAME, bufferlib);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
|
||||||
|
|
||||||
// luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM
|
// luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM
|
||||||
// The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack.
|
// The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack.
|
||||||
// If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path
|
// If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path
|
||||||
|
@ -1057,60 +1055,33 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St
|
||||||
|
|
||||||
static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauVector2Constructor)
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
{
|
||||||
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
float x = (float)nvalue(arg0);
|
||||||
{
|
float y = (float)nvalue(args);
|
||||||
float x = (float)nvalue(arg0);
|
float z = 0.0f;
|
||||||
float y = (float)nvalue(args);
|
|
||||||
float z = 0.0f;
|
|
||||||
|
|
||||||
if (nparams >= 3)
|
if (nparams >= 3)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + 1))
|
if (!ttisnumber(args + 1))
|
||||||
return -1;
|
return -1;
|
||||||
z = (float)nvalue(args + 1);
|
z = (float)nvalue(args + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LUA_VECTOR_SIZE == 4
|
#if LUA_VECTOR_SIZE == 4
|
||||||
float w = 0.0f;
|
float w = 0.0f;
|
||||||
if (nparams >= 4)
|
if (nparams >= 4)
|
||||||
{
|
|
||||||
if (!ttisnumber(args + 2))
|
|
||||||
return -1;
|
|
||||||
w = (float)nvalue(args + 2);
|
|
||||||
}
|
|
||||||
setvvalue(res, x, y, z, w);
|
|
||||||
#else
|
|
||||||
setvvalue(res, x, y, z, 0.0f);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
|
|
||||||
{
|
{
|
||||||
double x = nvalue(arg0);
|
if (!ttisnumber(args + 2))
|
||||||
double y = nvalue(args);
|
return -1;
|
||||||
double z = nvalue(args + 1);
|
w = (float)nvalue(args + 2);
|
||||||
|
}
|
||||||
#if LUA_VECTOR_SIZE == 4
|
setvvalue(res, x, y, z, w);
|
||||||
double w = 0.0;
|
|
||||||
if (nparams >= 4)
|
|
||||||
{
|
|
||||||
if (!ttisnumber(args + 2))
|
|
||||||
return -1;
|
|
||||||
w = nvalue(args + 2);
|
|
||||||
}
|
|
||||||
setvvalue(res, float(x), float(y), float(z), float(w));
|
|
||||||
#else
|
#else
|
||||||
setvvalue(res, float(x), float(y), float(z), 0.0f);
|
setvvalue(res, x, y, z, 0.0f);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMathLerp)
|
|
||||||
|
|
||||||
#undef PI
|
#undef PI
|
||||||
#define PI (3.14159265358979323846)
|
#define PI (3.14159265358979323846)
|
||||||
#define RADIANS_PER_DEGREE (PI / 180.0)
|
#define RADIANS_PER_DEGREE (PI / 180.0)
|
||||||
|
@ -463,6 +461,7 @@ static const luaL_Reg mathlib[] = {
|
||||||
{"sign", math_sign},
|
{"sign", math_sign},
|
||||||
{"round", math_round},
|
{"round", math_round},
|
||||||
{"map", math_map},
|
{"map", math_map},
|
||||||
|
{"lerp", math_lerp},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -483,11 +482,5 @@ int luaopen_math(lua_State* L)
|
||||||
lua_pushnumber(L, HUGE_VAL);
|
lua_pushnumber(L, HUGE_VAL);
|
||||||
lua_setfield(L, -2, "huge");
|
lua_setfield(L, -2, "huge");
|
||||||
|
|
||||||
if (FFlag::LuauMathLerp)
|
|
||||||
{
|
|
||||||
lua_pushcfunction(L, math_lerp, "lerp");
|
|
||||||
lua_setfield(L, -2, "lerp");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVector2Constructor)
|
|
||||||
|
|
||||||
static int vector_create(lua_State* L)
|
static int vector_create(lua_State* L)
|
||||||
{
|
{
|
||||||
// checking argument count to avoid accepting 'nil' as a valid value
|
// checking argument count to avoid accepting 'nil' as a valid value
|
||||||
|
@ -15,7 +13,7 @@ static int vector_create(lua_State* L)
|
||||||
|
|
||||||
double x = luaL_checknumber(L, 1);
|
double x = luaL_checknumber(L, 1);
|
||||||
double y = luaL_checknumber(L, 2);
|
double y = luaL_checknumber(L, 2);
|
||||||
double z = FFlag::LuauVector2Constructor ? (count >= 3 ? luaL_checknumber(L, 3) : 0.0) : luaL_checknumber(L, 3);
|
double z = count >= 3 ? luaL_checknumber(L, 3) : 0.0;
|
||||||
|
|
||||||
#if LUA_VECTOR_SIZE == 4
|
#if LUA_VECTOR_SIZE == 4
|
||||||
double w = count >= 4 ? luaL_checknumber(L, 4) : 0.0;
|
double w = count >= 4 ? luaL_checknumber(L, 4) : 0.0;
|
||||||
|
|
|
@ -19,10 +19,10 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAG(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||||
LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment)
|
LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
|
||||||
|
|
||||||
|
|
||||||
struct ATSFixture : BuiltinsFixture
|
struct ATSFixture : BuiltinsFixture
|
||||||
|
@ -76,7 +76,7 @@ export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))
|
||||||
|
|
||||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||||
if (FFlag::LuauStoreCSTData && FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauStoreCSTData && FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0& (<t0...>( true | any)->(''))");
|
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0& (<t0...>( true | any)->(''))");
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0 &(<t0...>( true | any)->(''))");
|
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0 &(<t0...>( true | any)->(''))");
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauAstTypeGroup)
|
else if (FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0& (<t0 ...>(true | any)->(''))");
|
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0& (<t0 ...>(true | any)->(''))");
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,6 @@ TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
{FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -449,7 +448,14 @@ end
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
if (FFlag::LuauSkipNoRefineDuringRefinement)
|
||||||
|
{
|
||||||
|
REQUIRE_EQ(module->ats.typeInfo.size(), 1);
|
||||||
|
CHECK_EQ(module->ats.typeInfo[0].code, Pattern::Assign);
|
||||||
|
CHECK_EQ(module->ats.typeInfo[0].node, "descendant.CollisionGroup = CAR_COLLISION_GROUP");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol")
|
TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol")
|
||||||
|
@ -522,7 +528,6 @@ TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
{FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -590,7 +595,10 @@ initialize()
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
LUAU_ASSERT(module->ats.typeInfo.size() == 11);
|
if (FFlag::LuauSkipNoRefineDuringRefinement)
|
||||||
|
CHECK_EQ(module->ats.typeInfo.size(), 12);
|
||||||
|
else
|
||||||
|
LUAU_ASSERT(module->ats.typeInfo.size() == 11);
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||||
if (FFlag::LuauStoreCSTData)
|
if (FFlag::LuauStoreCSTData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||||
|
|
||||||
struct JsonEncoderFixture
|
struct JsonEncoderFixture
|
||||||
{
|
{
|
||||||
|
@ -473,10 +473,10 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
||||||
{
|
{
|
||||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
std::string_view expected =
|
std::string_view expected =
|
||||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeGroup","location":"0,9 - 0,36","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,55","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
||||||
CHECK(toJson(statement) == expected);
|
CHECK(toJson(statement) == expected);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -23,7 +23,6 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauVector2Constants)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -5093,8 +5092,6 @@ L0: RETURN R3 -1
|
||||||
|
|
||||||
TEST_CASE("VectorConstants")
|
TEST_CASE("VectorConstants")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVector2Constants{FFlag::LuauVector2Constants, true};
|
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction("return vector.create(1, 2)", 0, 2, 0), R"(
|
CHECK_EQ("\n" + compileFunction("return vector.create(1, 2)", 0, 2, 0), R"(
|
||||||
LOADK R0 K0 [1, 2, 0]
|
LOADK R0 K0 [1, 2, 0]
|
||||||
RETURN R0 1
|
RETURN R0 1
|
||||||
|
|
|
@ -32,15 +32,10 @@ void luaC_fullgc(lua_State* L);
|
||||||
void luaC_validate(lua_State* L);
|
void luaC_validate(lua_State* L);
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
||||||
LUAU_FASTFLAG(LuauMathLerp)
|
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
|
||||||
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
|
||||||
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
|
@ -655,15 +650,11 @@ TEST_CASE("Basic")
|
||||||
|
|
||||||
TEST_CASE("Buffers")
|
TEST_CASE("Buffers")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauBufferBitMethods{FFlag::LuauBufferBitMethods2, true};
|
|
||||||
|
|
||||||
runConformance("buffers.luau");
|
runConformance("buffers.luau");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Math")
|
TEST_CASE("Math")
|
||||||
{
|
{
|
||||||
ScopedFastFlag LuauMathLerp{FFlag::LuauMathLerp, true};
|
|
||||||
|
|
||||||
runConformance("math.luau");
|
runConformance("math.luau");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,9 +887,7 @@ TEST_CASE("Vector")
|
||||||
|
|
||||||
TEST_CASE("VectorLibrary")
|
TEST_CASE("VectorLibrary")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
|
|
||||||
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
||||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
|
||||||
|
|
||||||
lua_CompileOptions copts = defaultOptions();
|
lua_CompileOptions copts = defaultOptions();
|
||||||
|
|
||||||
|
@ -989,9 +978,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
|
|
||||||
TEST_CASE("Types")
|
TEST_CASE("Types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
|
||||||
ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, true};
|
|
||||||
|
|
||||||
runConformance(
|
runConformance(
|
||||||
"types.luau",
|
"types.luau",
|
||||||
[](lua_State* L)
|
[](lua_State* L)
|
||||||
|
@ -2622,8 +2608,6 @@ TEST_CASE("SafeEnv")
|
||||||
|
|
||||||
TEST_CASE("Native")
|
TEST_CASE("Native")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauCodeGenLimitLiveSlotReuse{FFlag::LuauCodeGenLimitLiveSlotReuse, true};
|
|
||||||
|
|
||||||
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
||||||
if (!codegen || !luau_codegen_supported())
|
if (!codegen || !luau_codegen_supported())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -53,6 +53,7 @@ void ConstraintGeneratorFixture::solve(const std::string& code)
|
||||||
NotNull{&typeFunctionRuntime},
|
NotNull{&typeFunctionRuntime},
|
||||||
NotNull{rootScope},
|
NotNull{rootScope},
|
||||||
constraints,
|
constraints,
|
||||||
|
NotNull{&cg->scopeToFunction},
|
||||||
"MainModule",
|
"MainModule",
|
||||||
NotNull(&moduleResolver),
|
NotNull(&moduleResolver),
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
static const char* mainModuleName = "MainModule";
|
static const char* mainModuleName = "MainModule";
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests);
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests);
|
||||||
|
@ -593,8 +592,6 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool
|
||||||
BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete)
|
BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete)
|
||||||
: Fixture(prepareAutocomplete)
|
: Fixture(prepareAutocomplete)
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
|
||||||
|
|
||||||
Luau::unfreeze(frontend.globals.globalTypes);
|
Luau::unfreeze(frontend.globals.globalTypes);
|
||||||
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
|
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
#include "Luau/AutocompleteTypes.h"
|
#include "Luau/AutocompleteTypes.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
|
#include "ScopedFlags.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -21,7 +22,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAllowFragmentParsing);
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
LUAU_FASTFLAG(LuauSymbolEquality);
|
LUAU_FASTFLAG(LuauSymbolEquality);
|
||||||
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
|
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
|
||||||
|
@ -35,6 +35,7 @@ LUAU_FASTFLAG(LuauMixedModeDefFinderTraversesTypeOf)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
||||||
|
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
||||||
{
|
{
|
||||||
|
@ -64,8 +65,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
||||||
{
|
{
|
||||||
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
|
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
|
||||||
|
|
||||||
ScopedFastFlag sffs[7] = {
|
ScopedFastFlag sffs[6] = {
|
||||||
{FFlag::LuauAllowFragmentParsing, true},
|
|
||||||
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
|
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
|
||||||
{FFlag::LuauStoreSolverTypeOnModule, true},
|
{FFlag::LuauStoreSolverTypeOnModule, true},
|
||||||
{FFlag::LuauSymbolEquality, true},
|
{FFlag::LuauSymbolEquality, true},
|
||||||
|
@ -859,17 +859,16 @@ return module)";
|
||||||
|
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||||
|
ScopedFastFlag sff2{FFlag::LuauCloneIncrementalModule, true};
|
||||||
|
ScopedFastFlag sff3{FFlag::LuauFreeTypesMustHaveBounds, true};
|
||||||
checkAndExamine(source, "module", "{ }");
|
checkAndExamine(source, "module", "{ }");
|
||||||
// [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment
|
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
||||||
// early return since the following checking will fail, which it shouldn't!
|
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
||||||
// fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
|
||||||
// fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
checkAndExamine(source, "module", "{ }");
|
checkAndExamine(source, "module", "{ }");
|
||||||
// [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment
|
// [TODO] CLI-140762 Fragment autocomplete still doesn't return correct result when LuauSolverV2 is on
|
||||||
// early return since the following checking will fail, which it shouldn't!
|
|
||||||
return;
|
return;
|
||||||
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
||||||
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
||||||
|
@ -2092,5 +2091,15 @@ return module
|
||||||
autocompleteFragmentInBothSolvers(source, updated, Position{1, 18}, [](FragmentAutocompleteResult& result) {});
|
autocompleteFragmentInBothSolvers(source, updated, Position{1, 18}, [](FragmentAutocompleteResult& result) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "ice_caused_by_mixed_mode_use")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
|
||||||
|
const std::string source = "--[[\n\tPackage link auto-generated by Rotriever\n]]\nlocal PackageIndex = script.Parent._Index\n\nlocal Package = "
|
||||||
|
"require(PackageIndex[\"ReactOtter\"][\"ReactOtter\"])\n\nexport type Goal = Package.Goal\nexport type SpringOptions "
|
||||||
|
"= Package.SpringOptions\n\n\nreturn Pa";
|
||||||
|
autocompleteFragmentInBothSolvers(source, source, Position{11,9}, [](auto& _){
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauCountSelfCallsNonstrict)
|
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
||||||
|
|
||||||
|
@ -619,9 +617,6 @@ buffer.readi8(b, 0)
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauCountSelfCallsNonstrict{FFlag::LuauCountSelfCallsNonstrict, true};
|
|
||||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
|
||||||
|
|
||||||
Luau::unfreeze(frontend.globals.globalTypes);
|
Luau::unfreeze(frontend.globals.globalTypes);
|
||||||
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
|
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes);
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauErrorRecoveryForClassNames)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionNameStartPosition)
|
LUAU_FASTFLAG(LuauFixFunctionNameStartPosition)
|
||||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -372,7 +372,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_
|
||||||
|
|
||||||
AstTypeIntersection* returnAnnotation = annotation->returnTypes.types.data[0]->as<AstTypeIntersection>();
|
AstTypeIntersection* returnAnnotation = annotation->returnTypes.types.data[0]->as<AstTypeIntersection>();
|
||||||
REQUIRE(returnAnnotation != nullptr);
|
REQUIRE(returnAnnotation != nullptr);
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
|
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
|
||||||
else
|
else
|
||||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeReference>());
|
CHECK(returnAnnotation->types.data[0]->as<AstTypeReference>());
|
||||||
|
@ -2451,7 +2451,7 @@ TEST_CASE_FIXTURE(Fixture, "leading_union_intersection_with_single_type_preserve
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
|
TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup, true};
|
ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true};
|
||||||
|
|
||||||
AstStatBlock* stat = parse(R"(
|
AstStatBlock* stat = parse(R"(
|
||||||
type Foo = (string)
|
type Foo = (string)
|
||||||
|
@ -2469,7 +2469,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
|
TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup, true};
|
ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true};
|
||||||
|
|
||||||
AstStatBlock* stat = parse(R"(
|
AstStatBlock* stat = parse(R"(
|
||||||
type Foo = ((string))
|
type Foo = ((string))
|
||||||
|
@ -2490,7 +2490,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
|
TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup, true};
|
ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true};
|
||||||
|
|
||||||
AstStatBlock* stat = parse(R"(
|
AstStatBlock* stat = parse(R"(
|
||||||
type Foo = () -> (string)
|
type Foo = () -> (string)
|
||||||
|
@ -3813,7 +3813,7 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
||||||
auto unionTy = paramTy.type->as<AstTypeUnion>();
|
auto unionTy = paramTy.type->as<AstTypeUnion>();
|
||||||
LUAU_ASSERT(unionTy);
|
LUAU_ASSERT(unionTy);
|
||||||
CHECK_EQ(unionTy->types.size, 2);
|
CHECK_EQ(unionTy->types.size, 2);
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
{
|
{
|
||||||
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
|
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
|
||||||
REQUIRE(groupTy);
|
REQUIRE(groupTy);
|
||||||
|
|
|
@ -14,7 +14,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup);
|
LUAU_FASTFLAG(LuauAstTypeGroup2);
|
||||||
LUAU_FASTFLAG(LexerFixInterpStringStart)
|
LUAU_FASTFLAG(LexerFixInterpStringStart)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TranspilerTests");
|
TEST_SUITE_BEGIN("TranspilerTests");
|
||||||
|
@ -1175,7 +1175,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
|
||||||
{
|
{
|
||||||
std::string code = "local a: nil | (string & number)";
|
std::string code = "local a: nil | (string & number)";
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
|
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
|
||||||
else
|
else
|
||||||
CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code);
|
CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code);
|
||||||
|
@ -1732,4 +1732,24 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style")
|
||||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transpile_types_preserve_parentheses_style")
|
||||||
|
{
|
||||||
|
ScopedFastFlag flags[] = {
|
||||||
|
{FFlag::LuauStoreCSTData, true},
|
||||||
|
{FFlag::LuauAstTypeGroup2, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string code = R"( type Foo = number )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = (number) )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = ((number)) )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = ( (number) ) )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -16,8 +16,8 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
struct TxnLogFixture
|
struct TxnLogFixture
|
||||||
{
|
{
|
||||||
TxnLog log{/*useScopes*/ true};
|
TxnLog log;
|
||||||
TxnLog log2{/*useScopes*/ true};
|
TxnLog log2;
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
BuiltinTypes builtinTypes;
|
BuiltinTypes builtinTypes;
|
||||||
|
|
||||||
|
@ -33,39 +33,6 @@ struct TxnLogFixture
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TxnLog");
|
TEST_SUITE_BEGIN("TxnLog");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_greater_scope")
|
|
||||||
{
|
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
|
||||||
|
|
||||||
log.replace(c, BoundType{a});
|
|
||||||
log2.replace(a, BoundType{c});
|
|
||||||
|
|
||||||
CHECK(nullptr != log.pending(c));
|
|
||||||
|
|
||||||
log.concatAsUnion(std::move(log2), NotNull{&arena});
|
|
||||||
|
|
||||||
// 'a has greater scope than 'c, so we expect the incoming binding of 'a to
|
|
||||||
// be discarded.
|
|
||||||
|
|
||||||
CHECK(nullptr == log.pending(a));
|
|
||||||
|
|
||||||
const PendingType* pt = log.pending(c);
|
|
||||||
REQUIRE(pt != nullptr);
|
|
||||||
|
|
||||||
CHECK(!pt->dead);
|
|
||||||
const BoundType* bt = get_if<BoundType>(&pt->pending.ty);
|
|
||||||
|
|
||||||
CHECK(a == bt->boundTo);
|
|
||||||
|
|
||||||
log.commit();
|
|
||||||
|
|
||||||
REQUIRE(get<FreeType>(a));
|
|
||||||
|
|
||||||
const BoundType* bound = get<BoundType>(c);
|
|
||||||
REQUIRE(bound);
|
|
||||||
CHECK(a == bound->boundTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_lesser_scope")
|
TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_lesser_scope")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
|
|
|
@ -12,6 +12,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauTypeFunSingletonEquality)
|
LUAU_FASTFLAG(LuauTypeFunSingletonEquality)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypeofReturnsType)
|
LUAU_FASTFLAG(LuauUserTypeFunTypeofReturnsType)
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunReadWriteParents)
|
||||||
LUAU_FASTFLAG(LuauTypeFunPrintFix)
|
LUAU_FASTFLAG(LuauTypeFunPrintFix)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
@ -1960,4 +1961,25 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_print_tab_char_fix")
|
||||||
CHECK_EQ("1\t2", toString(result.errors[0]));
|
CHECK_EQ("1\t2", toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_parent_ops")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag readWriteParents{FFlag::LuauTypeFunReadWriteParents, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function readparentof(arg)
|
||||||
|
return arg:readparent()
|
||||||
|
end
|
||||||
|
|
||||||
|
type function writeparentof(arg)
|
||||||
|
return arg:writeparent()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok1(idx: readparentof<ChildClass>): BaseClass return idx end
|
||||||
|
local function ok2(idx: writeparentof<ChildClass>): BaseClass return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNewSolverPrePopulateClasses)
|
|
||||||
LUAU_FASTFLAG(LuauClipNestedAndRecursiveUnion)
|
LUAU_FASTFLAG(LuauClipNestedAndRecursiveUnion)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauPreventReentrantTypeFunctionReduction)
|
LUAU_FASTFLAG(LuauPreventReentrantTypeFunctionReduction)
|
||||||
|
@ -497,7 +496,6 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauNewSolverPrePopulateClasses, true};
|
|
||||||
loadDefinition(R"(
|
loadDefinition(R"(
|
||||||
declare class Channel
|
declare class Channel
|
||||||
Messages: { Message }
|
Messages: { Message }
|
||||||
|
|
|
@ -16,11 +16,14 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauSubtypingFixTailPack)
|
LUAU_FASTFLAG(LuauSubtypingFixTailPack)
|
||||||
|
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -3041,4 +3044,48 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_wrap_result_call")
|
||||||
// New solver still reports an error in this case, but the main goal of the test is to not crash
|
// New solver still reports an error in this case, but the main goal of the test is to not crash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generalized_type")
|
||||||
|
{
|
||||||
|
ScopedFastFlag crashOnForce{FFlag::DebugLuauAssertOnForcedConstraint, true};
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUngeneralizedTypesForRecursiveFunctions, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
function random()
|
||||||
|
return true -- chosen by fair coin toss
|
||||||
|
end
|
||||||
|
|
||||||
|
local f
|
||||||
|
f = 5
|
||||||
|
function f()
|
||||||
|
if random() then f() end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
else
|
||||||
|
LUAU_REQUIRE_ERRORS(result); // errors without typestate, obviously
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generalized_type_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag crashOnForce{FFlag::DebugLuauAssertOnForcedConstraint, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
function random()
|
||||||
|
return true -- chosen by fair coin toss
|
||||||
|
end
|
||||||
|
|
||||||
|
local function f()
|
||||||
|
if random() then f() end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -464,13 +463,10 @@ local b: B.T = a
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewSolverPopulateTableLocations)
|
CHECK(
|
||||||
CHECK(
|
toString(result.errors.at(0)) ==
|
||||||
toString(result.errors.at(0)) ==
|
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string"
|
||||||
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string"
|
);
|
||||||
);
|
|
||||||
else
|
|
||||||
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -513,13 +509,10 @@ local b: B.T = a
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewSolverPopulateTableLocations)
|
CHECK(
|
||||||
CHECK(
|
toString(result.errors.at(0)) ==
|
||||||
toString(result.errors.at(0)) ==
|
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string"
|
||||||
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string"
|
);
|
||||||
);
|
|
||||||
else
|
|
||||||
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -557,7 +557,7 @@ TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
|
TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
TypeId nilType = builtinTypes->nilType;
|
TypeId nilType = builtinTypes->nilType;
|
||||||
|
@ -575,9 +575,6 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant};
|
Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant};
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
u.enableNewSolver();
|
|
||||||
|
|
||||||
u.tryUnify(option1, option2);
|
u.tryUnify(option1, option2);
|
||||||
|
|
||||||
CHECK(!u.failure);
|
CHECK(!u.failure);
|
||||||
|
@ -987,7 +984,7 @@ TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together")
|
TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
TypeId nilType = builtinTypes->nilType;
|
TypeId nilType = builtinTypes->nilType;
|
||||||
|
@ -1005,9 +1002,6 @@ TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together")
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant};
|
Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant};
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
u.enableNewSolver();
|
|
||||||
|
|
||||||
u.tryUnify(option1, option2);
|
u.tryUnify(option1, option2);
|
||||||
|
|
||||||
CHECK(!u.failure);
|
CHECK(!u.failure);
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauGeneralizationRemoveRecursiveUpperBound2)
|
LUAU_FASTFLAG(LuauGeneralizationRemoveRecursiveUpperBound2)
|
||||||
|
LUAU_FASTFLAG(LuauIntersectNotNil)
|
||||||
|
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -2459,4 +2461,69 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
|
||||||
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24})));
|
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "nonnil_refinement_on_generic")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIntersectNotNil, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local function printOptional<T>(item: T?, printer: (T) -> string): string
|
||||||
|
if item ~= nil then
|
||||||
|
return printer(item)
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
CHECK_EQ("T & ~nil", toString(requireTypeAtPosition({3, 31})));
|
||||||
|
else
|
||||||
|
CHECK_EQ("T", toString(requireTypeAtPosition({3, 31})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "truthy_refinement_on_generic")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIntersectNotNil, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local function printOptional<T>(item: T?, printer: (T) -> string): string
|
||||||
|
if item then
|
||||||
|
return printer(item)
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
CHECK_EQ("T & ~(false?)", toString(requireTypeAtPosition({3, 31})));
|
||||||
|
else
|
||||||
|
CHECK_EQ("T", toString(requireTypeAtPosition({3, 31})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "truthy_call_of_function_with_table_value_as_argument_should_not_refine_value_as_never")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauSkipNoRefineDuringRefinement, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type Item = {}
|
||||||
|
|
||||||
|
local function predicate(value: Item): boolean
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkValue(value: Item)
|
||||||
|
if predicate(value) then
|
||||||
|
local _ = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
CHECK_EQ("Item", toString(requireTypeAtPosition({8, 27})));
|
||||||
|
CHECK_EQ("Item", toString(requireTypeAtPosition({9, 28})));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -18,8 +18,6 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauTableKeysAreRValues)
|
|
||||||
LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer)
|
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAG(LuauDontInPlaceMutateTableType)
|
LUAU_FASTFLAG(LuauDontInPlaceMutateTableType)
|
||||||
|
@ -1932,7 +1930,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr")
|
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local function f(): { [string]: number }
|
local function f(): { [string]: number }
|
||||||
|
@ -1991,9 +1989,6 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map")
|
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map")
|
||||||
{
|
{
|
||||||
|
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
type MyMap<K, V> = { [K]: V }
|
type MyMap<K, V> = { [K]: V }
|
||||||
function set<K, V>(m: MyMap<K, V>, k: K, v: V)
|
function set<K, V>(m: MyMap<K, V>, k: K, v: V)
|
||||||
|
@ -2010,8 +2005,7 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound")
|
TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local function setkey_object(t: { [string]: number }, v)
|
local function setkey_object(t: { [string]: number }, v)
|
||||||
t.foo = v
|
t.foo = v
|
||||||
|
@ -2942,16 +2936,12 @@ TEST_CASE_FIXTURE(Fixture, "table_length")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer")
|
TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer")
|
||||||
{
|
{
|
||||||
// CLI-100076 - Assigning a table key to `nil` in the presence of an indexer should always be permitted
|
LUAU_REQUIRE_NO_ERRORS(check("local a = {} a[0] = 7 a[0] = nil"));
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check("local a = {} a[0] = 7 a[0] = nil");
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(0, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer")
|
TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = {}
|
local a = {}
|
||||||
|
@ -5055,7 +5045,6 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager")
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "read_only_property_reads")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "read_only_property_reads")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag sff{FFlag::LuauTableKeysAreRValues, true};
|
|
||||||
|
|
||||||
// none of the `t.id` accesses here should error
|
// none of the `t.id` accesses here should error
|
||||||
auto result = check(R"(
|
auto result = check(R"(
|
||||||
|
|
|
@ -23,7 +23,7 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeCacheLimit)
|
LUAU_FASTINT(LuauNormalizeCacheLimit)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAG(LuauInferLocalTypesInMultipleAssignments)
|
LUAU_FASTFLAG(LuauInferLocalTypesInMultipleAssignments)
|
||||||
|
|
||||||
|
@ -1201,10 +1201,13 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
CHECK(3 == result.errors.size());
|
CHECK(3 == result.errors.size());
|
||||||
CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location);
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
|
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
|
||||||
|
else
|
||||||
|
CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location);
|
||||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup2)
|
||||||
CHECK(Location{{3, 22}, {3, 40}} == result.errors[2].location);
|
CHECK(Location{{3, 22}, {3, 41}} == result.errors[2].location);
|
||||||
else
|
else
|
||||||
CHECK(Location{{3, 23}, {3, 40}} == result.errors[2].location);
|
CHECK(Location{{3, 23}, {3, 40}} == result.errors[2].location);
|
||||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||||
|
|
|
@ -341,43 +341,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_pack_owner")
|
||||||
CHECK_EQ(a->owningArena, &arena);
|
CHECK_EQ(a->owningArena, &arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TryUnifyFixture, "metatables_unify_against_shape_of_free_table")
|
|
||||||
{
|
|
||||||
TableType::Props freeProps{
|
|
||||||
{"foo", {builtinTypes->numberType}},
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeId free = arena.addType(TableType{freeProps, std::nullopt, TypeLevel{}, TableState::Free});
|
|
||||||
|
|
||||||
TableType::Props indexProps{
|
|
||||||
{"foo", {builtinTypes->stringType}},
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeId index = arena.addType(TableType{indexProps, std::nullopt, TypeLevel{}, TableState::Sealed});
|
|
||||||
|
|
||||||
TableType::Props mtProps{
|
|
||||||
{"__index", {index}},
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeId mt = arena.addType(TableType{mtProps, std::nullopt, TypeLevel{}, TableState::Sealed});
|
|
||||||
|
|
||||||
TypeId target = arena.addType(TableType{TableState::Unsealed, TypeLevel{}});
|
|
||||||
TypeId metatable = arena.addType(MetatableType{target, mt});
|
|
||||||
|
|
||||||
state.enableNewSolver();
|
|
||||||
state.tryUnify(metatable, free);
|
|
||||||
state.log.commit();
|
|
||||||
|
|
||||||
REQUIRE_EQ(state.errors.size(), 1);
|
|
||||||
const std::string expected = R"(Type
|
|
||||||
'{ @metatable {| __index: {| foo: string |} |}, { } }'
|
|
||||||
could not be converted into
|
|
||||||
'{- foo: number -}'
|
|
||||||
caused by:
|
|
||||||
Type 'number' could not be converted into 'string')";
|
|
||||||
CHECK_EQ(expected, toString(state.errors[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TryUnifyFixture, "fuzz_tail_unification_issue")
|
TEST_CASE_FIXTURE(TryUnifyFixture, "fuzz_tail_unification_issue")
|
||||||
{
|
{
|
||||||
TypePackVar variadicAny{VariadicTypePack{builtinTypes->anyType}};
|
TypePackVar variadicAny{VariadicTypePack{builtinTypes->anyType}};
|
||||||
|
@ -403,101 +366,6 @@ local l0:(any)&(typeof(_)),l0:(any)|(any) = _,_
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeId createTheType(TypeArena& arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope, TypeId freeTy)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
({|
|
|
||||||
render: (
|
|
||||||
(('a) -> ()) | {| current: 'a |}
|
|
||||||
) -> nil
|
|
||||||
|}) -> ()
|
|
||||||
*/
|
|
||||||
TypePackId emptyPack = arena.addTypePack({});
|
|
||||||
|
|
||||||
return arena.addType(FunctionType{
|
|
||||||
arena.addTypePack({arena.addType(TableType{
|
|
||||||
TableType::Props{
|
|
||||||
{{"render",
|
|
||||||
Property(arena.addType(FunctionType{
|
|
||||||
arena.addTypePack({arena.addType(UnionType{
|
|
||||||
{arena.addType(FunctionType{arena.addTypePack({freeTy}), emptyPack}),
|
|
||||||
arena.addType(TableType{TableType::Props{{"current", {freeTy}}}, std::nullopt, TypeLevel{}, scope, TableState::Sealed})}
|
|
||||||
})}),
|
|
||||||
arena.addTypePack({builtinTypes->nilType})
|
|
||||||
}))}}
|
|
||||||
},
|
|
||||||
std::nullopt,
|
|
||||||
TypeLevel{},
|
|
||||||
scope,
|
|
||||||
TableState::Sealed
|
|
||||||
})}),
|
|
||||||
emptyPack
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// See CLI-71190
|
|
||||||
TEST_CASE_FIXTURE(TryUnifyFixture, "unifying_two_unions_under_dcr_does_not_create_a_BoundType_cycle")
|
|
||||||
{
|
|
||||||
const std::shared_ptr<Scope> scope = globalScope;
|
|
||||||
const std::shared_ptr<Scope> nestedScope = std::make_shared<Scope>(scope);
|
|
||||||
|
|
||||||
const TypeId outerType = arena.freshType(builtinTypes, scope.get());
|
|
||||||
const TypeId outerType2 = arena.freshType(builtinTypes, scope.get());
|
|
||||||
|
|
||||||
const TypeId innerType = arena.freshType(builtinTypes, nestedScope.get());
|
|
||||||
|
|
||||||
state.enableNewSolver();
|
|
||||||
|
|
||||||
SUBCASE("equal_scopes")
|
|
||||||
{
|
|
||||||
TypeId one = createTheType(arena, builtinTypes, scope.get(), outerType);
|
|
||||||
TypeId two = createTheType(arena, builtinTypes, scope.get(), outerType2);
|
|
||||||
|
|
||||||
state.tryUnify(one, two);
|
|
||||||
state.log.commit();
|
|
||||||
|
|
||||||
ToStringOptions opts;
|
|
||||||
|
|
||||||
CHECK(follow(outerType) == follow(outerType2));
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("outer_scope_is_subtype")
|
|
||||||
{
|
|
||||||
TypeId one = createTheType(arena, builtinTypes, scope.get(), outerType);
|
|
||||||
TypeId two = createTheType(arena, builtinTypes, scope.get(), innerType);
|
|
||||||
|
|
||||||
state.tryUnify(one, two);
|
|
||||||
state.log.commit();
|
|
||||||
|
|
||||||
ToStringOptions opts;
|
|
||||||
|
|
||||||
CHECK(follow(outerType) == follow(innerType));
|
|
||||||
|
|
||||||
// The scope of outerType exceeds that of innerType. The latter should be bound to the former.
|
|
||||||
const BoundType* bt = get_if<BoundType>(&innerType->ty);
|
|
||||||
REQUIRE(bt);
|
|
||||||
CHECK(bt->boundTo == outerType);
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("outer_scope_is_supertype")
|
|
||||||
{
|
|
||||||
TypeId one = createTheType(arena, builtinTypes, scope.get(), innerType);
|
|
||||||
TypeId two = createTheType(arena, builtinTypes, scope.get(), outerType);
|
|
||||||
|
|
||||||
state.tryUnify(one, two);
|
|
||||||
state.log.commit();
|
|
||||||
|
|
||||||
ToStringOptions opts;
|
|
||||||
|
|
||||||
CHECK(follow(outerType) == follow(innerType));
|
|
||||||
|
|
||||||
// The scope of outerType exceeds that of innerType. The latter should be bound to the former.
|
|
||||||
const BoundType* bt = get_if<BoundType>(&innerType->ty);
|
|
||||||
REQUIRE(bt);
|
|
||||||
CHECK(bt->boundTo == outerType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_full_restart_recursion")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_full_restart_recursion")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauUnifierRecursionOnRestart{FFlag::LuauUnifierRecursionOnRestart, true};
|
ScopedFastFlag luauUnifierRecursionOnRestart{FFlag::LuauUnifierRecursionOnRestart, true};
|
||||||
|
|
Loading…
Add table
Reference in a new issue