mirror of
https://github.com/luau-lang/luau.git
synced 2025-03-03 18:51:42 +00:00
Sync to upstream/release/662 (#1681)
## What's new This update brings improvements to the new type solver, roundtrippable AST parsing mode and closes multiple issues reported in this repository. * `require` dependency tracing for non-string requires now supports `()` groups in expressions and types as well as an ability to type annotate a value with a `typeof` of a different module path * Fixed rare misaligned memory access in Compiler/Typechecker on 32 bit platforms (Closes #1572) ## New Solver * Fixed crash/UB in subtyping of type packs (Closes #1449) * Fixed incorrect type errors when calling `debug.info` (Closes #1534 and Resolves #966) * Fixed incorrect boolean and string equality comparison result in user-defined type functions (Closes #1623) * Fixed incorrect class types being produced in user-defined type functions when multiple classes share the same name (Closes #1639) * Improved bidirectional typechecking for table literals containing elements that have not been solved yet (Closes #1641) ## Roundtrippable AST * Added source information for `AstStatTypeAlias` * Fixed an issue with `AstTypeGroup` node (added in #1643) producing invalid AST json. Contained type is now named 'inner' instead of 'type' * Fixed end location of the `do ... end` statement --- Internal Contributors: Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
86bf4ae42d
commit
c2e72666d9
51 changed files with 1048 additions and 291 deletions
|
@ -109,6 +109,21 @@ struct FunctionCheckConstraint
|
||||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// table_check expectedType exprType
|
||||||
|
//
|
||||||
|
// If `expectedType` is a table type and `exprType` is _also_ a table type,
|
||||||
|
// propogate the member types of `expectedType` into the types of `exprType`.
|
||||||
|
// This is used to implement bidirectional inference on table assignment.
|
||||||
|
// Also see: FunctionCheckConstraint.
|
||||||
|
struct TableCheckConstraint
|
||||||
|
{
|
||||||
|
TypeId expectedType;
|
||||||
|
TypeId exprType;
|
||||||
|
AstExprTable* table = nullptr;
|
||||||
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
|
||||||
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
|
||||||
|
};
|
||||||
|
|
||||||
// prim FreeType ExpectedType PrimitiveType
|
// prim FreeType ExpectedType PrimitiveType
|
||||||
//
|
//
|
||||||
// FreeType is bounded below by the singleton type and above by PrimitiveType
|
// FreeType is bounded below by the singleton type and above by PrimitiveType
|
||||||
|
@ -273,7 +288,8 @@ using ConstraintV = Variant<
|
||||||
UnpackConstraint,
|
UnpackConstraint,
|
||||||
ReduceConstraint,
|
ReduceConstraint,
|
||||||
ReducePackConstraint,
|
ReducePackConstraint,
|
||||||
EqualityConstraint>;
|
EqualityConstraint,
|
||||||
|
TableCheckConstraint>;
|
||||||
|
|
||||||
struct Constraint
|
struct Constraint
|
||||||
{
|
{
|
||||||
|
|
|
@ -118,6 +118,8 @@ struct ConstraintSolver
|
||||||
// A mapping from free types to the number of unresolved constraints that mention them.
|
// A mapping from free types to the number of unresolved constraints that mention them.
|
||||||
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
||||||
|
|
||||||
|
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;
|
||||||
|
|
||||||
// Irreducible/uninhabited type functions or type pack functions.
|
// Irreducible/uninhabited type functions or type pack functions.
|
||||||
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
|
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
|
||||||
|
|
||||||
|
@ -201,6 +203,7 @@ public:
|
||||||
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
|
||||||
|
bool tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
|
||||||
|
@ -421,10 +424,7 @@ public:
|
||||||
|
|
||||||
ToStringOptions opts;
|
ToStringOptions opts;
|
||||||
|
|
||||||
void fillInDiscriminantTypes(
|
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
|
||||||
NotNull<const Constraint> constraint,
|
|
||||||
const std::vector<std::optional<TypeId>>& discriminantTypes
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||||
|
|
|
@ -105,7 +105,7 @@ private:
|
||||||
std::vector<Id> storage;
|
std::vector<Id> storage;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename L>
|
template<typename L>
|
||||||
using Node = EqSat::Node<L>;
|
using Node = EqSat::Node<L>;
|
||||||
|
|
||||||
using EType = EqSat::Language<
|
using EType = EqSat::Language<
|
||||||
|
|
|
@ -11,14 +11,12 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
class AstStat;
|
class AstNode;
|
||||||
class AstExpr;
|
|
||||||
class AstStatBlock;
|
class AstStatBlock;
|
||||||
struct AstLocal;
|
|
||||||
|
|
||||||
struct RequireTraceResult
|
struct RequireTraceResult
|
||||||
{
|
{
|
||||||
DenseHashMap<const AstExpr*, ModuleInfo> exprs{nullptr};
|
DenseHashMap<const AstNode*, ModuleInfo> exprs{nullptr};
|
||||||
|
|
||||||
std::vector<std::pair<ModuleName, Location>> requireList;
|
std::vector<std::pair<ModuleName, Location>> requireList;
|
||||||
};
|
};
|
||||||
|
|
|
@ -310,7 +310,8 @@ struct MagicFunctionTypeCheckContext
|
||||||
|
|
||||||
struct MagicFunction
|
struct MagicFunction
|
||||||
{
|
{
|
||||||
virtual std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) = 0;
|
virtual std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) = 0;
|
||||||
|
|
||||||
// Callback to allow custom typechecking of builtin function calls whose argument types
|
// Callback to allow custom typechecking of builtin function calls whose argument types
|
||||||
// will only be resolved after constraint solving. For example, the arguments to string.format
|
// will only be resolved after constraint solving. For example, the arguments to string.format
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
|
#include "Luau/TypeFwd.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -217,7 +218,9 @@ struct TypeFunctionClassType
|
||||||
|
|
||||||
std::optional<TypeFunctionTypeId> parent;
|
std::optional<TypeFunctionTypeId> parent;
|
||||||
|
|
||||||
std::string name;
|
TypeId classTy;
|
||||||
|
|
||||||
|
std::string name_DEPRECATED;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeFunctionGenericType
|
struct TypeFunctionGenericType
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct TypeFunctionRuntimeBuilderState
|
||||||
// Invariant: users can not create a new class types -> any class types that get deserialized must have been an argument to the type function
|
// Invariant: users can not create a new class types -> any class types that get deserialized must have been an argument to the type function
|
||||||
// Using this invariant, whenever a ClassType is serialized, we can put it into this map
|
// Using this invariant, whenever a ClassType is serialized, we can put it into this map
|
||||||
// whenever a ClassType is deserialized, we can use this map to return the corresponding value
|
// whenever a ClassType is deserialized, we can use this map to return the corresponding value
|
||||||
DenseHashMap<std::string, TypeId> classesSerialized{{}};
|
DenseHashMap<std::string, TypeId> classesSerialized_DEPRECATED{{}};
|
||||||
|
|
||||||
// List of errors that occur during serialization/deserialization
|
// List of errors that occur during serialization/deserialization
|
||||||
// At every iteration of serialization/deserialzation, if this list.size() != 0, we halt the process
|
// At every iteration of serialization/deserialzation, if this list.size() != 0, we halt the process
|
||||||
|
@ -40,8 +40,6 @@ struct TypeFunctionRuntimeBuilderState
|
||||||
|
|
||||||
TypeFunctionRuntimeBuilderState(NotNull<TypeFunctionContext> ctx)
|
TypeFunctionRuntimeBuilderState(NotNull<TypeFunctionContext> ctx)
|
||||||
: ctx(ctx)
|
: ctx(ctx)
|
||||||
, classesSerialized({})
|
|
||||||
, errors({})
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,7 +51,6 @@ struct UnifierSharedState
|
||||||
UnifierCounters counters;
|
UnifierCounters counters;
|
||||||
|
|
||||||
bool reentrantTypeReduction = false;
|
bool reentrantTypeReduction = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeReductionRentrancyGuard final
|
struct TypeReductionRentrancyGuard final
|
||||||
|
|
|
@ -1168,7 +1168,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
"AstTypeGroup",
|
"AstTypeGroup",
|
||||||
[&]()
|
[&]()
|
||||||
{
|
{
|
||||||
write("type", node->type);
|
write("inner", node->type);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions2)
|
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
|
|
||||||
|
@ -1521,12 +1520,9 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
|
||||||
{
|
{
|
||||||
for (const std::string& tag : funcType->tags)
|
for (const std::string& tag : funcType->tags)
|
||||||
{
|
{
|
||||||
if (FFlag::AutocompleteRequirePathSuggestions2)
|
if (tag == kRequireTagName && fileResolver)
|
||||||
{
|
{
|
||||||
if (tag == kRequireTagName && fileResolver)
|
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
|
||||||
{
|
|
||||||
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString))
|
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString))
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
|
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
|
||||||
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFreezeIgnorePersistent)
|
LUAU_FASTFLAGVARIABLE(LuauFreezeIgnorePersistent)
|
||||||
|
@ -41,68 +40,79 @@ namespace Luau
|
||||||
|
|
||||||
struct MagicSelect final : MagicFunction
|
struct MagicSelect final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicSetMetatable final : MagicFunction
|
struct MagicSetMetatable final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicAssert final : MagicFunction
|
struct MagicAssert final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicPack final : MagicFunction
|
struct MagicPack final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicRequire final : MagicFunction
|
struct MagicRequire final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicClone final : MagicFunction
|
struct MagicClone final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicFreeze final : MagicFunction
|
struct MagicFreeze final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicFormat final : MagicFunction
|
struct MagicFormat final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
bool typeCheck(const MagicFunctionTypeCheckContext& ctx) override;
|
bool typeCheck(const MagicFunctionTypeCheckContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicMatch final : MagicFunction
|
struct MagicMatch final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicGmatch final : MagicFunction
|
struct MagicGmatch final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MagicFind final : MagicFunction
|
struct MagicFind final : MagicFunction
|
||||||
{
|
{
|
||||||
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
std::optional<WithPredicate<TypePackId>>
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
|
||||||
bool infer(const MagicFunctionCallContext& ctx) override;
|
bool infer(const MagicFunctionCallContext& ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,16 +464,9 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
attachMagicFunction(ttv->props["freeze"].type(), std::make_shared<MagicFreeze>());
|
attachMagicFunction(ttv->props["freeze"].type(), std::make_shared<MagicFreeze>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::AutocompleteRequirePathSuggestions2)
|
TypeId requireTy = getGlobalBinding(globals, "require");
|
||||||
{
|
attachTag(requireTy, kRequireTagName);
|
||||||
TypeId requireTy = getGlobalBinding(globals, "require");
|
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
|
||||||
attachTag(requireTy, kRequireTagName);
|
|
||||||
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attachMagicFunction(getGlobalBinding(globals, "require"), std::make_shared<MagicRequire>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<TypeId> parseFormatString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
|
static std::vector<TypeId> parseFormatString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
|
||||||
|
@ -637,15 +640,15 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
{
|
{
|
||||||
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
||||||
{
|
{
|
||||||
case ErrorSuppression::Suppress:
|
case ErrorSuppression::Suppress:
|
||||||
break;
|
break;
|
||||||
case ErrorSuppression::NormalizationFailed:
|
case ErrorSuppression::NormalizationFailed:
|
||||||
break;
|
break;
|
||||||
case ErrorSuppression::DoNotSuppress:
|
case ErrorSuppression::DoNotSuppress:
|
||||||
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||||
|
|
||||||
if (!reasonings.suppressed)
|
if (!reasonings.suppressed)
|
||||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1503,7 +1506,8 @@ static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCa
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WithPredicate<TypePackId>> MagicFreeze::handleOldSolver(struct TypeChecker &, const std::shared_ptr<struct Scope> &, const class AstExprCall &, WithPredicate<TypePackId>)
|
std::optional<WithPredicate<TypePackId>> MagicFreeze::
|
||||||
|
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
{
|
{
|
||||||
rci.traverse(rpc->tp);
|
rci.traverse(rpc->tp);
|
||||||
}
|
}
|
||||||
|
else if (auto tcc = get<TableCheckConstraint>(*this))
|
||||||
|
{
|
||||||
|
rci.traverse(tcc->exprType);
|
||||||
|
}
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
||||||
|
@ -2998,30 +2999,46 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
|
|
||||||
if (expectedType)
|
if (expectedType)
|
||||||
{
|
{
|
||||||
Unifier2 unifier{arena, builtinTypes, NotNull{scope.get()}, ice};
|
if (FFlag::LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
std::vector<TypeId> toBlock;
|
|
||||||
// This logic is incomplete as we want to re-run this
|
|
||||||
// _after_ blocked types have resolved, but this
|
|
||||||
// allows us to do some bidirectional inference.
|
|
||||||
toBlock = findBlockedTypesIn(expr, NotNull{&module->astTypes});
|
|
||||||
|
|
||||||
if (toBlock.empty())
|
|
||||||
{
|
{
|
||||||
matchLiteralType(
|
addConstraint(
|
||||||
NotNull{&module->astTypes},
|
scope,
|
||||||
NotNull{&module->astExpectedTypes},
|
expr->location,
|
||||||
builtinTypes,
|
TableCheckConstraint{
|
||||||
arena,
|
*expectedType,
|
||||||
NotNull{&unifier},
|
ty,
|
||||||
*expectedType,
|
expr,
|
||||||
ty,
|
NotNull{&module->astTypes},
|
||||||
expr,
|
NotNull{&module->astExpectedTypes},
|
||||||
toBlock
|
}
|
||||||
);
|
);
|
||||||
// The visitor we ran prior should ensure that there are no
|
}
|
||||||
// blocked types that we would encounter while matching on
|
else
|
||||||
// this expression.
|
{
|
||||||
LUAU_ASSERT(toBlock.empty());
|
Unifier2 unifier{arena, builtinTypes, NotNull{scope.get()}, ice};
|
||||||
|
std::vector<TypeId> toBlock;
|
||||||
|
// This logic is incomplete as we want to re-run this
|
||||||
|
// _after_ blocked types have resolved, but this
|
||||||
|
// allows us to do some bidirectional inference.
|
||||||
|
toBlock = findBlockedTypesIn(expr, NotNull{&module->astTypes});
|
||||||
|
if (toBlock.empty())
|
||||||
|
{
|
||||||
|
matchLiteralType(
|
||||||
|
NotNull{&module->astTypes},
|
||||||
|
NotNull{&module->astExpectedTypes},
|
||||||
|
builtinTypes,
|
||||||
|
arena,
|
||||||
|
NotNull{&unifier},
|
||||||
|
*expectedType,
|
||||||
|
ty,
|
||||||
|
expr,
|
||||||
|
toBlock
|
||||||
|
);
|
||||||
|
// The visitor we ran prior should ensure that there are no
|
||||||
|
// blocked types that we would encounter while matching on
|
||||||
|
// this expression.
|
||||||
|
LUAU_ASSERT(toBlock.empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes)
|
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -355,13 +355,27 @@ ConstraintSolver::ConstraintSolver(
|
||||||
{
|
{
|
||||||
unsolvedConstraints.emplace_back(c);
|
unsolvedConstraints.emplace_back(c);
|
||||||
|
|
||||||
// initialize the reference counts for the free types in this constraint.
|
if (FFlag::LuauPrecalculateMutatedFreeTypes2)
|
||||||
for (auto ty : c->getMaybeMutatedFreeTypes())
|
|
||||||
{
|
{
|
||||||
// increment the reference count for `ty`
|
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes();
|
||||||
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
for (auto ty : maybeMutatedTypesPerConstraint)
|
||||||
refCount += 1;
|
{
|
||||||
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
|
refCount += 1;
|
||||||
|
}
|
||||||
|
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// initialize the reference counts for the free types in this constraint.
|
||||||
|
for (auto ty : c->getMaybeMutatedFreeTypes())
|
||||||
|
{
|
||||||
|
// increment the reference count for `ty`
|
||||||
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
|
refCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (NotNull<const Constraint> dep : c->dependencies)
|
for (NotNull<const Constraint> dep : c->dependencies)
|
||||||
{
|
{
|
||||||
|
@ -439,10 +453,6 @@ void ConstraintSolver::run()
|
||||||
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<DenseHashSet<TypeId>> mutatedFreeTypes = std::nullopt;
|
|
||||||
if (FFlag::LuauPrecalculateMutatedFreeTypes)
|
|
||||||
mutatedFreeTypes = c->getMaybeMutatedFreeTypes();
|
|
||||||
|
|
||||||
bool success = tryDispatch(c, force);
|
bool success = tryDispatch(c, force);
|
||||||
|
|
||||||
progress |= success;
|
progress |= success;
|
||||||
|
@ -452,23 +462,29 @@ void ConstraintSolver::run()
|
||||||
unblock(c);
|
unblock(c);
|
||||||
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
|
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
|
||||||
|
|
||||||
if (FFlag::LuauPrecalculateMutatedFreeTypes)
|
if (FFlag::LuauPrecalculateMutatedFreeTypes2)
|
||||||
{
|
{
|
||||||
for (auto ty : c->getMaybeMutatedFreeTypes())
|
const auto maybeMutated = maybeMutatedFreeTypes.find(c);
|
||||||
mutatedFreeTypes->insert(ty);
|
if (maybeMutated != maybeMutatedFreeTypes.end())
|
||||||
for (auto ty : *mutatedFreeTypes)
|
|
||||||
{
|
{
|
||||||
size_t& refCount = unresolvedConstraints[ty];
|
for (auto ty : maybeMutated->second)
|
||||||
if (refCount > 0)
|
{
|
||||||
refCount -= 1;
|
// There is a high chance that this type has been rebound
|
||||||
|
// across blocked types, rebound free types, pending
|
||||||
|
// expansion types, etc, so we need to follow it.
|
||||||
|
ty = follow(ty);
|
||||||
|
size_t& refCount = unresolvedConstraints[ty];
|
||||||
|
if (refCount > 0)
|
||||||
|
refCount -= 1;
|
||||||
|
|
||||||
// We have two constraints that are designed to wait for the
|
// We have two constraints that are designed to wait for the
|
||||||
// refCount on a free type to be equal to 1: the
|
// refCount on a free type to be equal to 1: the
|
||||||
// PrimitiveTypeConstraint and ReduceConstraint. We
|
// PrimitiveTypeConstraint and ReduceConstraint. We
|
||||||
// therefore wake any constraint waiting for a free type's
|
// therefore wake any constraint waiting for a free type's
|
||||||
// refcount to be 1 or 0.
|
// refcount to be 1 or 0.
|
||||||
if (refCount <= 1)
|
if (refCount <= 1)
|
||||||
unblock(ty, Location{});
|
unblock(ty, Location{});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -668,6 +684,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
||||||
success = tryDispatch(*fcc, constraint);
|
success = tryDispatch(*fcc, constraint);
|
||||||
else if (auto fcc = get<FunctionCheckConstraint>(*constraint))
|
else if (auto fcc = get<FunctionCheckConstraint>(*constraint))
|
||||||
success = tryDispatch(*fcc, constraint);
|
success = tryDispatch(*fcc, constraint);
|
||||||
|
else if (auto tcc = get<TableCheckConstraint>(*constraint))
|
||||||
|
success = tryDispatch(*tcc, constraint);
|
||||||
else if (auto fcc = get<PrimitiveTypeConstraint>(*constraint))
|
else if (auto fcc = get<PrimitiveTypeConstraint>(*constraint))
|
||||||
success = tryDispatch(*fcc, constraint);
|
success = tryDispatch(*fcc, constraint);
|
||||||
else if (auto hpc = get<HasPropConstraint>(*constraint))
|
else if (auto hpc = get<HasPropConstraint>(*constraint))
|
||||||
|
@ -1170,10 +1188,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::fillInDiscriminantTypes(
|
void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes)
|
||||||
NotNull<const Constraint> constraint,
|
|
||||||
const std::vector<std::optional<TypeId>>& discriminantTypes
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
for (std::optional<TypeId> ty : discriminantTypes)
|
for (std::optional<TypeId> ty : discriminantTypes)
|
||||||
{
|
{
|
||||||
|
@ -1521,6 +1536,28 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConstraintSolver::tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint)
|
||||||
|
{
|
||||||
|
// This is expensive as we need to traverse a (potentially large)
|
||||||
|
// literal up front in order to determine if there are any blocked
|
||||||
|
// types, otherwise we may run `matchTypeLiteral` multiple times,
|
||||||
|
// which right now may fail due to being non-idempotent (it
|
||||||
|
// destructively updates the underlying literal type).
|
||||||
|
auto blockedTypes = findBlockedTypesIn(c.table, c.astTypes);
|
||||||
|
for (const auto ty : blockedTypes)
|
||||||
|
{
|
||||||
|
block(ty, constraint);
|
||||||
|
}
|
||||||
|
if (!blockedTypes.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
||||||
|
std::vector<TypeId> toBlock;
|
||||||
|
(void)matchLiteralType(c.astTypes, c.astExpectedTypes, builtinTypes, arena, NotNull{&u2}, c.expectedType, c.exprType, c.table, toBlock);
|
||||||
|
LUAU_ASSERT(toBlock.empty());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
|
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
|
||||||
{
|
{
|
||||||
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
|
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
LUAU_FASTFLAG(LuauVector2Constructor)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -209,6 +210,15 @@ declare table: {
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare debug: {
|
||||||
|
info: ((thread: thread, level: number, options: string) -> ...any) & ((level: number, options: string) -> ...any) & (<A..., R1...>(func: (A...) -> R1..., options: string) -> ...any),
|
||||||
|
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionDebugSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare debug: {
|
declare debug: {
|
||||||
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
||||||
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||||
|
@ -362,7 +372,7 @@ std::string getBuiltinDefinitionSource()
|
||||||
result += kBuiltinDefinitionOsSrc;
|
result += kBuiltinDefinitionOsSrc;
|
||||||
result += kBuiltinDefinitionCoroutineSrc;
|
result += kBuiltinDefinitionCoroutineSrc;
|
||||||
result += kBuiltinDefinitionTableSrc;
|
result += kBuiltinDefinitionTableSrc;
|
||||||
result += kBuiltinDefinitionDebugSrc;
|
result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED;
|
||||||
result += kBuiltinDefinitionUtf8Src;
|
result += kBuiltinDefinitionUtf8Src;
|
||||||
|
|
||||||
result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
||||||
|
|
|
@ -396,7 +396,8 @@ Id toId(
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(tfun->packArguments.empty());
|
LUAU_ASSERT(tfun->packArguments.empty());
|
||||||
|
|
||||||
if (tfun->userFuncName) {
|
if (tfun->userFuncName)
|
||||||
|
{
|
||||||
// TODO: User defined type functions are pseudo-effectful: error
|
// TODO: User defined type functions are pseudo-effectful: error
|
||||||
// reporting is done via the `print` statement, so running a
|
// reporting is done via the `print` statement, so running a
|
||||||
// UDTF multiple times may end up double erroring. egraphs
|
// UDTF multiple times may end up double erroring. egraphs
|
||||||
|
|
|
@ -30,7 +30,6 @@ LUAU_FASTFLAG(LuauAllowFragmentParsing);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes)
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes)
|
||||||
LUAU_FASTFLAG(LuauReferenceAllocatorInNewSolver)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
||||||
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
||||||
|
@ -596,7 +595,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauIncrementalAutocompleteBugfixes && FFlag::LuauReferenceAllocatorInNewSolver)
|
if (FFlag::LuauIncrementalAutocompleteBugfixes)
|
||||||
{
|
{
|
||||||
if (sourceModule->allocator.get() != module->allocator.get())
|
if (sourceModule->allocator.get() != module->allocator.get())
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,7 +52,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterReverseDependencyTracking)
|
||||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule)
|
LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReferenceAllocatorInNewSolver)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1430,11 +1429,8 @@ ModulePtr check(
|
||||||
result->mode = mode;
|
result->mode = mode;
|
||||||
result->internalTypes.owningModule = result.get();
|
result->internalTypes.owningModule = result.get();
|
||||||
result->interfaceTypes.owningModule = result.get();
|
result->interfaceTypes.owningModule = result.get();
|
||||||
if (FFlag::LuauReferenceAllocatorInNewSolver)
|
result->allocator = sourceModule.allocator;
|
||||||
{
|
result->names = sourceModule.names;
|
||||||
result->allocator = sourceModule.allocator;
|
|
||||||
result->names = sourceModule.names;
|
|
||||||
}
|
|
||||||
|
|
||||||
iceHandler->moduleName = sourceModule.name;
|
iceHandler->moduleName = sourceModule.name;
|
||||||
|
|
||||||
|
@ -1751,7 +1747,7 @@ std::pair<SourceNode*, SourceModule*> Frontend::getSourceNode(const ModuleName&
|
||||||
|
|
||||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||||
{
|
{
|
||||||
// clear all prior dependents. we will re-add them after parsing the rest of the graph
|
// clear all prior dependents. we will re-add them after parsing the rest of the graph
|
||||||
for (const auto& [moduleName, _] : sourceNode->requireLocations)
|
for (const auto& [moduleName, _] : sourceNode->requireLocations)
|
||||||
{
|
{
|
||||||
if (auto depIt = sourceNodes.find(moduleName); depIt != sourceNodes.end())
|
if (auto depIt = sourceNodes.find(moduleName); depIt != sourceNodes.end())
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "Luau/Generalization.h"
|
#include "Luau/Generalization.h"
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
|
@ -10,7 +12,7 @@
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGeneralizationRemoveRecursiveUpperBound)
|
LUAU_FASTFLAGVARIABLE(LuauGeneralizationRemoveRecursiveUpperBound2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -50,7 +52,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void replace(DenseHashSet<TypeId>& seen, TypeId haystack, TypeId needle, TypeId replacement)
|
void replace(DenseHashSet<TypeId>& seen, TypeId haystack, TypeId needle, TypeId replacement)
|
||||||
{
|
{
|
||||||
haystack = follow(haystack);
|
haystack = follow(haystack);
|
||||||
|
|
||||||
|
@ -97,6 +99,10 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
LUAU_ASSERT(onlyType != haystack);
|
LUAU_ASSERT(onlyType != haystack);
|
||||||
emplaceType<BoundType>(asMutable(haystack), onlyType);
|
emplaceType<BoundType>(asMutable(haystack), onlyType);
|
||||||
}
|
}
|
||||||
|
else if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound2 && ut->options.empty())
|
||||||
|
{
|
||||||
|
emplaceType<BoundType>(asMutable(haystack), builtinTypes->neverType);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +145,10 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
TypeId onlyType = it->parts[0];
|
TypeId onlyType = it->parts[0];
|
||||||
LUAU_ASSERT(onlyType != needle);
|
LUAU_ASSERT(onlyType != needle);
|
||||||
emplaceType<BoundType>(asMutable(needle), onlyType);
|
emplaceType<BoundType>(asMutable(needle), onlyType);
|
||||||
|
}
|
||||||
|
else if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound2 && it->parts.empty())
|
||||||
|
{
|
||||||
|
emplaceType<BoundType>(asMutable(needle), builtinTypes->unknownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -233,53 +243,6 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId ub = follow(ft->upperBound);
|
TypeId ub = follow(ft->upperBound);
|
||||||
if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound)
|
|
||||||
{
|
|
||||||
|
|
||||||
// If the upper bound is a union type or an intersection type,
|
|
||||||
// and one of it's members is the free type we're
|
|
||||||
// generalizing, don't include it in the upper bound. For a
|
|
||||||
// free type such as:
|
|
||||||
//
|
|
||||||
// t1 where t1 = D <: 'a <: (A | B | C | t1)
|
|
||||||
//
|
|
||||||
// Naively replacing it with it's upper bound creates:
|
|
||||||
//
|
|
||||||
// t1 where t1 = A | B | C | t1
|
|
||||||
//
|
|
||||||
// It makes sense to just optimize this and exclude the
|
|
||||||
// recursive component by semantic subtyping rules.
|
|
||||||
|
|
||||||
if (auto itv = get<IntersectionType>(ub))
|
|
||||||
{
|
|
||||||
std::vector<TypeId> newIds;
|
|
||||||
newIds.reserve(itv->parts.size());
|
|
||||||
for (auto part : itv)
|
|
||||||
{
|
|
||||||
if (part != ty)
|
|
||||||
newIds.push_back(part);
|
|
||||||
}
|
|
||||||
if (newIds.size() == 1)
|
|
||||||
ub = newIds[0];
|
|
||||||
else if (newIds.size() > 0)
|
|
||||||
ub = arena->addType(IntersectionType{std::move(newIds)});
|
|
||||||
}
|
|
||||||
else if (auto utv = get<UnionType>(ub))
|
|
||||||
{
|
|
||||||
std::vector<TypeId> newIds;
|
|
||||||
newIds.reserve(utv->options.size());
|
|
||||||
for (auto part : utv)
|
|
||||||
{
|
|
||||||
if (part != ty)
|
|
||||||
newIds.push_back(part);
|
|
||||||
}
|
|
||||||
if (newIds.size() == 1)
|
|
||||||
ub = newIds[0];
|
|
||||||
else if (newIds.size() > 0)
|
|
||||||
ub = arena->addType(UnionType{std::move(newIds)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FreeType* upperFree = getMutable<FreeType>(ub); upperFree && upperFree->lowerBound == ty)
|
if (FreeType* upperFree = getMutable<FreeType>(ub); upperFree && upperFree->lowerBound == ty)
|
||||||
upperFree->lowerBound = builtinTypes->neverType;
|
upperFree->lowerBound = builtinTypes->neverType;
|
||||||
else
|
else
|
||||||
|
|
|
@ -2296,7 +2296,7 @@ void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId th
|
||||||
//
|
//
|
||||||
// Dog & ~Animal
|
// Dog & ~Animal
|
||||||
//
|
//
|
||||||
// Clearly this intersects to never, so we mark this class as
|
// Clearly this intersects to never, so we mark this class as
|
||||||
// being removed from the normalized class type.
|
// being removed from the normalized class type.
|
||||||
emptyIntersectWithNegation = true;
|
emptyIntersectWithNegation = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauExtendedSimpleRequire)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ struct RequireTracer : AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstExpr* getDependent(AstExpr* node)
|
AstExpr* getDependent_DEPRECATED(AstExpr* node)
|
||||||
{
|
{
|
||||||
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
||||||
return locals[expr->local];
|
return locals[expr->local];
|
||||||
|
@ -78,50 +80,122 @@ struct RequireTracer : AstVisitor
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
AstNode* getDependent(AstNode* node)
|
||||||
|
{
|
||||||
|
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
||||||
|
return locals[expr->local];
|
||||||
|
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
|
||||||
|
return expr->expr;
|
||||||
|
else if (AstExprIndexExpr* expr = node->as<AstExprIndexExpr>())
|
||||||
|
return expr->expr;
|
||||||
|
else if (AstExprCall* expr = node->as<AstExprCall>(); expr && expr->self)
|
||||||
|
return expr->func->as<AstExprIndexName>()->expr;
|
||||||
|
else if (AstExprGroup* expr = node->as<AstExprGroup>())
|
||||||
|
return expr->expr;
|
||||||
|
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
|
||||||
|
return expr->annotation;
|
||||||
|
else if (AstTypeGroup* expr = node->as<AstTypeGroup>())
|
||||||
|
return expr->type;
|
||||||
|
else if (AstTypeTypeof* expr = node->as<AstTypeTypeof>())
|
||||||
|
return expr->expr;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void process()
|
void process()
|
||||||
{
|
{
|
||||||
ModuleInfo moduleContext{currentModuleName};
|
ModuleInfo moduleContext{currentModuleName};
|
||||||
|
|
||||||
// seed worklist with require arguments
|
if (FFlag::LuauExtendedSimpleRequire)
|
||||||
work.reserve(requireCalls.size());
|
|
||||||
|
|
||||||
for (AstExprCall* require : requireCalls)
|
|
||||||
work.push_back(require->args.data[0]);
|
|
||||||
|
|
||||||
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
|
||||||
for (size_t i = 0; i < work.size(); ++i)
|
|
||||||
if (AstExpr* dep = getDependent(work[i]))
|
|
||||||
work.push_back(dep);
|
|
||||||
|
|
||||||
// resolve all expressions to a module info
|
|
||||||
for (size_t i = work.size(); i > 0; --i)
|
|
||||||
{
|
{
|
||||||
AstExpr* expr = work[i - 1];
|
// seed worklist with require arguments
|
||||||
|
work.reserve(requireCalls.size());
|
||||||
|
|
||||||
// when multiple expressions depend on the same one we push it to work queue multiple times
|
for (AstExprCall* require : requireCalls)
|
||||||
if (result.exprs.contains(expr))
|
work.push_back(require->args.data[0]);
|
||||||
continue;
|
|
||||||
|
|
||||||
std::optional<ModuleInfo> info;
|
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
||||||
|
for (size_t i = 0; i < work.size(); ++i)
|
||||||
if (AstExpr* dep = getDependent(expr))
|
|
||||||
{
|
{
|
||||||
const ModuleInfo* context = result.exprs.find(dep);
|
if (AstNode* dep = getDependent(work[i]))
|
||||||
|
work.push_back(dep);
|
||||||
|
}
|
||||||
|
|
||||||
// locals just inherit their dependent context, no resolution required
|
// resolve all expressions to a module info
|
||||||
if (expr->is<AstExprLocal>())
|
for (size_t i = work.size(); i > 0; --i)
|
||||||
info = context ? std::optional<ModuleInfo>(*context) : std::nullopt;
|
{
|
||||||
|
AstNode* expr = work[i - 1];
|
||||||
|
|
||||||
|
// when multiple expressions depend on the same one we push it to work queue multiple times
|
||||||
|
if (result.exprs.contains(expr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::optional<ModuleInfo> info;
|
||||||
|
|
||||||
|
if (AstNode* dep = getDependent(expr))
|
||||||
|
{
|
||||||
|
const ModuleInfo* context = result.exprs.find(dep);
|
||||||
|
|
||||||
|
if (context && expr->is<AstExprLocal>())
|
||||||
|
info = *context; // locals just inherit their dependent context, no resolution required
|
||||||
|
else if (context && (expr->is<AstExprGroup>() || expr->is<AstTypeGroup>()))
|
||||||
|
info = *context; // simple group nodes propagate their value
|
||||||
|
else if (context && (expr->is<AstTypeTypeof>() || expr->is<AstExprTypeAssertion>()))
|
||||||
|
info = *context; // typeof type annotations will resolve to the typeof content
|
||||||
|
else if (AstExpr* asExpr = expr->asExpr())
|
||||||
|
info = fileResolver->resolveModule(context, asExpr);
|
||||||
|
}
|
||||||
|
else if (AstExpr* asExpr = expr->asExpr())
|
||||||
|
{
|
||||||
|
info = fileResolver->resolveModule(&moduleContext, asExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info)
|
||||||
|
result.exprs[expr] = std::move(*info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// seed worklist with require arguments
|
||||||
|
work_DEPRECATED.reserve(requireCalls.size());
|
||||||
|
|
||||||
|
for (AstExprCall* require : requireCalls)
|
||||||
|
work_DEPRECATED.push_back(require->args.data[0]);
|
||||||
|
|
||||||
|
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
||||||
|
for (size_t i = 0; i < work_DEPRECATED.size(); ++i)
|
||||||
|
if (AstExpr* dep = getDependent_DEPRECATED(work_DEPRECATED[i]))
|
||||||
|
work_DEPRECATED.push_back(dep);
|
||||||
|
|
||||||
|
// resolve all expressions to a module info
|
||||||
|
for (size_t i = work_DEPRECATED.size(); i > 0; --i)
|
||||||
|
{
|
||||||
|
AstExpr* expr = work_DEPRECATED[i - 1];
|
||||||
|
|
||||||
|
// when multiple expressions depend on the same one we push it to work queue multiple times
|
||||||
|
if (result.exprs.contains(expr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::optional<ModuleInfo> info;
|
||||||
|
|
||||||
|
if (AstExpr* dep = getDependent_DEPRECATED(expr))
|
||||||
|
{
|
||||||
|
const ModuleInfo* context = result.exprs.find(dep);
|
||||||
|
|
||||||
|
// locals just inherit their dependent context, no resolution required
|
||||||
|
if (expr->is<AstExprLocal>())
|
||||||
|
info = context ? std::optional<ModuleInfo>(*context) : std::nullopt;
|
||||||
|
else
|
||||||
|
info = fileResolver->resolveModule(context, expr);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
info = fileResolver->resolveModule(context, expr);
|
{
|
||||||
}
|
info = fileResolver->resolveModule(&moduleContext, expr);
|
||||||
else
|
}
|
||||||
{
|
|
||||||
info = fileResolver->resolveModule(&moduleContext, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info)
|
if (info)
|
||||||
result.exprs[expr] = std::move(*info);
|
result.exprs[expr] = std::move(*info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve all requires according to their argument
|
// resolve all requires according to their argument
|
||||||
|
@ -150,7 +224,8 @@ struct RequireTracer : AstVisitor
|
||||||
ModuleName currentModuleName;
|
ModuleName currentModuleName;
|
||||||
|
|
||||||
DenseHashMap<AstLocal*, AstExpr*> locals;
|
DenseHashMap<AstLocal*, AstExpr*> locals;
|
||||||
std::vector<AstExpr*> work;
|
std::vector<AstExpr*> work_DEPRECATED;
|
||||||
|
std::vector<AstNode*> work;
|
||||||
std::vector<AstExprCall*> requireCalls;
|
std::vector<AstExprCall*> requireCalls;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSubtypingFixTailPack)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -858,7 +859,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||||
else
|
else
|
||||||
return SubtypingResult{false}
|
return SubtypingResult{false}
|
||||||
.withSuperComponent(TypePath::PackField::Tail)
|
.withSuperComponent(TypePath::PackField::Tail)
|
||||||
.withError({scope->location, UnexpectedTypePackInSubtyping{*subTail}});
|
.withError({scope->location, UnexpectedTypePackInSubtyping{FFlag::LuauSubtypingFixTailPack ? *superTail : *subTail}});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return {false};
|
return {false};
|
||||||
|
|
|
@ -408,7 +408,7 @@ TypeId matchLiteralType(
|
||||||
|
|
||||||
if (FFlag::LuauDontInPlaceMutateTableType)
|
if (FFlag::LuauDontInPlaceMutateTableType)
|
||||||
{
|
{
|
||||||
for (const auto& key: keysToDelete)
|
for (const auto& key : keysToDelete)
|
||||||
{
|
{
|
||||||
const AstArray<char>& s = key->value;
|
const AstArray<char>& s = key->value;
|
||||||
std::string keyStr{s.data, s.data + s.size};
|
std::string keyStr{s.data, s.data + s.size};
|
||||||
|
|
|
@ -1865,6 +1865,8 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, EqualityConstraint>)
|
else if constexpr (std::is_same_v<T, EqualityConstraint>)
|
||||||
return "equality: " + tos(c.resultType) + " ~ " + tos(c.assignmentType);
|
return "equality: " + tos(c.resultType) + " ~ " + tos(c.assignmentType);
|
||||||
|
else if constexpr (std::is_same_v<T, TableCheckConstraint>)
|
||||||
|
return "table_check " + tos(c.expectedType) + " :> " + tos(c.exprType);
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
|
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup)
|
||||||
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -666,7 +667,8 @@ struct Printer_DEPRECATED
|
||||||
writer.keyword("do");
|
writer.keyword("do");
|
||||||
for (const auto& s : block->body)
|
for (const auto& s : block->body)
|
||||||
visualize(*s);
|
visualize(*s);
|
||||||
writer.advance(block->location.end);
|
if (!FFlag::LuauFixDoBlockEndLocation)
|
||||||
|
writer.advance(block->location.end);
|
||||||
writeEnd(program.location);
|
writeEnd(program.location);
|
||||||
}
|
}
|
||||||
else if (const auto& a = program.as<AstStatIf>())
|
else if (const auto& a = program.as<AstStatIf>())
|
||||||
|
@ -2036,15 +2038,23 @@ struct Printer
|
||||||
{
|
{
|
||||||
if (writeTypes)
|
if (writeTypes)
|
||||||
{
|
{
|
||||||
|
const auto* cstNode = lookupCstNode<CstStatTypeAlias>(a);
|
||||||
|
|
||||||
if (a->exported)
|
if (a->exported)
|
||||||
writer.keyword("export");
|
writer.keyword("export");
|
||||||
|
|
||||||
|
if (cstNode)
|
||||||
|
advance(cstNode->typeKeywordPosition);
|
||||||
|
|
||||||
writer.keyword("type");
|
writer.keyword("type");
|
||||||
|
advance(a->nameLocation.begin);
|
||||||
writer.identifier(a->name.value);
|
writer.identifier(a->name.value);
|
||||||
if (a->generics.size > 0 || a->genericPacks.size > 0)
|
if (a->generics.size > 0 || a->genericPacks.size > 0)
|
||||||
{
|
{
|
||||||
|
if (cstNode)
|
||||||
|
advance(cstNode->genericsOpenPosition);
|
||||||
writer.symbol("<");
|
writer.symbol("<");
|
||||||
CommaSeparatorInserter comma(writer);
|
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
||||||
|
|
||||||
for (auto o : a->generics)
|
for (auto o : a->generics)
|
||||||
{
|
{
|
||||||
|
@ -2055,7 +2065,15 @@ struct Printer
|
||||||
|
|
||||||
if (o->defaultValue)
|
if (o->defaultValue)
|
||||||
{
|
{
|
||||||
writer.maybeSpace(o->defaultValue->location.begin, 2);
|
const auto* genericTypeCstNode = lookupCstNode<CstGenericType>(o);
|
||||||
|
|
||||||
|
if (genericTypeCstNode)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(genericTypeCstNode->defaultEqualsPosition.has_value());
|
||||||
|
advance(*genericTypeCstNode->defaultEqualsPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
writer.maybeSpace(o->defaultValue->location.begin, 2);
|
||||||
writer.symbol("=");
|
writer.symbol("=");
|
||||||
visualizeTypeAnnotation(*o->defaultValue);
|
visualizeTypeAnnotation(*o->defaultValue);
|
||||||
}
|
}
|
||||||
|
@ -2065,21 +2083,36 @@ struct Printer
|
||||||
{
|
{
|
||||||
comma();
|
comma();
|
||||||
|
|
||||||
|
const auto* genericTypePackCstNode = lookupCstNode<CstGenericTypePack>(o);
|
||||||
|
|
||||||
writer.advance(o->location.begin);
|
writer.advance(o->location.begin);
|
||||||
writer.identifier(o->name.value);
|
writer.identifier(o->name.value);
|
||||||
|
if (genericTypePackCstNode)
|
||||||
|
advance(genericTypePackCstNode->ellipsisPosition);
|
||||||
writer.symbol("...");
|
writer.symbol("...");
|
||||||
|
|
||||||
if (o->defaultValue)
|
if (o->defaultValue)
|
||||||
{
|
{
|
||||||
writer.maybeSpace(o->defaultValue->location.begin, 2);
|
if (cstNode)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(genericTypePackCstNode->defaultEqualsPosition.has_value());
|
||||||
|
advance(*genericTypePackCstNode->defaultEqualsPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
writer.maybeSpace(o->defaultValue->location.begin, 2);
|
||||||
writer.symbol("=");
|
writer.symbol("=");
|
||||||
visualizeTypePackAnnotation(*o->defaultValue, false);
|
visualizeTypePackAnnotation(*o->defaultValue, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cstNode)
|
||||||
|
advance(cstNode->genericsClosePosition);
|
||||||
writer.symbol(">");
|
writer.symbol(">");
|
||||||
}
|
}
|
||||||
writer.maybeSpace(a->type->location.begin, 2);
|
if (cstNode)
|
||||||
|
advance(cstNode->equalsPosition);
|
||||||
|
else
|
||||||
|
writer.maybeSpace(a->type->location.begin, 2);
|
||||||
writer.symbol("=");
|
writer.symbol("=");
|
||||||
visualizeTypeAnnotation(*a->type);
|
visualizeTypeAnnotation(*a->type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -489,7 +489,6 @@ static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||||
|
|
||||||
return std::move(reducer.result);
|
return std::move(reducer.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
||||||
|
@ -744,11 +743,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
|
|
||||||
resetTypeFunctionState(L);
|
resetTypeFunctionState(L);
|
||||||
|
|
||||||
// Push serialized arguments onto the stack
|
|
||||||
|
|
||||||
// Since there aren't any new class types being created in type functions, there isn't a deserialization function
|
|
||||||
// class types. Instead, we can keep this map and return the mapping as the "deserialized value"
|
|
||||||
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
|
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
|
||||||
|
|
||||||
|
// Push serialized arguments onto the stack
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
{
|
{
|
||||||
TypeId ty = follow(typeParam);
|
TypeId ty = follow(typeParam);
|
||||||
|
@ -2839,9 +2836,9 @@ TypeFunctionReductionResult<TypeId> setmetatableTypeFunction(
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// we're trying to reject any type that has not normalized to a table or a union/intersection of tables.
|
// we're trying to reject any type that has not normalized to a table or a union/intersection of tables.
|
||||||
if (targetNorm->hasTops() || targetNorm->hasBooleans() || targetNorm->hasErrors() || targetNorm->hasNils() ||
|
if (targetNorm->hasTops() || targetNorm->hasBooleans() || targetNorm->hasErrors() || targetNorm->hasNils() || targetNorm->hasNumbers() ||
|
||||||
targetNorm->hasNumbers() || targetNorm->hasStrings() || targetNorm->hasThreads() || targetNorm->hasBuffers() ||
|
targetNorm->hasStrings() || targetNorm->hasThreads() || targetNorm->hasBuffers() || targetNorm->hasFunctions() || targetNorm->hasTyvars() ||
|
||||||
targetNorm->hasFunctions() || targetNorm->hasTyvars() || targetNorm->hasClasses())
|
targetNorm->hasClasses())
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// if the supposed metatable is not a table, we will fail to reduce.
|
// if the supposed metatable is not a table, we will fail to reduce.
|
||||||
|
@ -2899,11 +2896,7 @@ TypeFunctionReductionResult<TypeId> setmetatableTypeFunction(
|
||||||
return {result, Reduction::MaybeOk, {}, {}};
|
return {result, Reduction::MaybeOk, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeFunctionReductionResult<TypeId> getmetatableHelper(
|
static TypeFunctionReductionResult<TypeId> getmetatableHelper(TypeId targetTy, const Location& location, NotNull<TypeFunctionContext> ctx)
|
||||||
TypeId targetTy,
|
|
||||||
const Location& location,
|
|
||||||
NotNull<TypeFunctionContext> ctx
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
targetTy = follow(targetTy);
|
targetTy = follow(targetTy);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunFixHydratedClasses)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunSingletonEquality)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
||||||
|
|
||||||
|
@ -1566,7 +1568,7 @@ void registerTypeUserData(lua_State* L)
|
||||||
|
|
||||||
// Create and register metatable for type userdata
|
// Create and register metatable for type userdata
|
||||||
luaL_newmetatable(L, "type");
|
luaL_newmetatable(L, "type");
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypeofReturnsType)
|
if (FFlag::LuauUserTypeFunTypeofReturnsType)
|
||||||
{
|
{
|
||||||
lua_pushstring(L, "type");
|
lua_pushstring(L, "type");
|
||||||
|
@ -1708,14 +1710,14 @@ bool areEqual(SeenSet& seen, const TypeFunctionSingletonType& lhs, const TypeFun
|
||||||
|
|
||||||
{
|
{
|
||||||
const TypeFunctionBooleanSingleton* lp = get<TypeFunctionBooleanSingleton>(&lhs);
|
const TypeFunctionBooleanSingleton* lp = get<TypeFunctionBooleanSingleton>(&lhs);
|
||||||
const TypeFunctionBooleanSingleton* rp = get<TypeFunctionBooleanSingleton>(&lhs);
|
const TypeFunctionBooleanSingleton* rp = get<TypeFunctionBooleanSingleton>(FFlag::LuauTypeFunSingletonEquality ? &rhs : &lhs);
|
||||||
if (lp && rp)
|
if (lp && rp)
|
||||||
return lp->value == rp->value;
|
return lp->value == rp->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const TypeFunctionStringSingleton* lp = get<TypeFunctionStringSingleton>(&lhs);
|
const TypeFunctionStringSingleton* lp = get<TypeFunctionStringSingleton>(&lhs);
|
||||||
const TypeFunctionStringSingleton* rp = get<TypeFunctionStringSingleton>(&lhs);
|
const TypeFunctionStringSingleton* rp = get<TypeFunctionStringSingleton>(FFlag::LuauTypeFunSingletonEquality ? &rhs : &lhs);
|
||||||
if (lp && rp)
|
if (lp && rp)
|
||||||
return lp->value == rp->value;
|
return lp->value == rp->value;
|
||||||
}
|
}
|
||||||
|
@ -1868,7 +1870,10 @@ bool areEqual(SeenSet& seen, const TypeFunctionClassType& lhs, const TypeFunctio
|
||||||
if (seenSetContains(seen, &lhs, &rhs))
|
if (seenSetContains(seen, &lhs, &rhs))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return lhs.name == rhs.name;
|
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||||
|
return lhs.classTy == rhs.classTy;
|
||||||
|
else
|
||||||
|
return lhs.name_DEPRECATED == rhs.name_DEPRECATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
// used to control the recursion limit of any operations done by user-defined type functions
|
// used to control the recursion limit of any operations done by user-defined type functions
|
||||||
// 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)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -207,8 +208,19 @@ private:
|
||||||
}
|
}
|
||||||
else if (auto c = get<ClassType>(ty))
|
else if (auto c = get<ClassType>(ty))
|
||||||
{
|
{
|
||||||
state->classesSerialized[c->name] = ty;
|
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, c->name});
|
{
|
||||||
|
// 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
|
||||||
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, ty});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->classesSerialized_DEPRECATED[c->name] = ty;
|
||||||
|
target = typeFunctionRuntime->typeArena.allocate(
|
||||||
|
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (auto g = get<GenericType>(ty))
|
else if (auto g = get<GenericType>(ty))
|
||||||
{
|
{
|
||||||
|
@ -687,10 +699,17 @@ private:
|
||||||
}
|
}
|
||||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
else if (auto c = get<TypeFunctionClassType>(ty))
|
||||||
{
|
{
|
||||||
if (auto result = state->classesSerialized.find(c->name))
|
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||||
target = *result;
|
{
|
||||||
|
target = c->classTy;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious class type is being deserialized");
|
{
|
||||||
|
if (auto result = state->classesSerialized_DEPRECATED.find(c->name_DEPRECATED))
|
||||||
|
target = *result;
|
||||||
|
else
|
||||||
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious class type is being deserialized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,7 +38,7 @@ private:
|
||||||
{
|
{
|
||||||
Page* next;
|
Page* next;
|
||||||
|
|
||||||
char data[8192];
|
alignas(8) char data[8192];
|
||||||
};
|
};
|
||||||
|
|
||||||
Page* root;
|
Page* root;
|
||||||
|
|
|
@ -270,6 +270,47 @@ public:
|
||||||
Position functionKeywordPosition;
|
Position functionKeywordPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CstGenericType : public CstNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LUAU_CST_RTTI(CstGenericType)
|
||||||
|
|
||||||
|
CstGenericType(std::optional<Position> defaultEqualsPosition);
|
||||||
|
|
||||||
|
std::optional<Position> defaultEqualsPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CstGenericTypePack : public CstNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LUAU_CST_RTTI(CstGenericTypePack)
|
||||||
|
|
||||||
|
CstGenericTypePack(Position ellipsisPosition, std::optional<Position> defaultEqualsPosition);
|
||||||
|
|
||||||
|
Position ellipsisPosition;
|
||||||
|
std::optional<Position> defaultEqualsPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CstStatTypeAlias : public CstNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LUAU_CST_RTTI(CstStatTypeAlias)
|
||||||
|
|
||||||
|
CstStatTypeAlias(
|
||||||
|
Position typeKeywordPosition,
|
||||||
|
Position genericsOpenPosition,
|
||||||
|
AstArray<Position> genericsCommaPositions,
|
||||||
|
Position genericsClosePosition,
|
||||||
|
Position equalsPosition
|
||||||
|
);
|
||||||
|
|
||||||
|
Position typeKeywordPosition;
|
||||||
|
Position genericsOpenPosition;
|
||||||
|
AstArray<Position> genericsCommaPositions;
|
||||||
|
Position genericsClosePosition;
|
||||||
|
Position equalsPosition;
|
||||||
|
};
|
||||||
|
|
||||||
class CstTypeReference : public CstNode
|
class CstTypeReference : public CstNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -144,7 +144,7 @@ private:
|
||||||
AstStat* parseReturn();
|
AstStat* parseReturn();
|
||||||
|
|
||||||
// type Name `=' Type
|
// type Name `=' Type
|
||||||
AstStat* parseTypeAlias(const Location& start, bool exported);
|
AstStat* parseTypeAlias(const Location& start, bool exported, Position typeKeywordPosition);
|
||||||
|
|
||||||
// type function Name ... end
|
// type function Name ... end
|
||||||
AstStat* parseTypeFunction(const Location& start, bool exported);
|
AstStat* parseTypeFunction(const Location& start, bool exported);
|
||||||
|
@ -294,7 +294,12 @@ private:
|
||||||
Name parseIndexName(const char* context, const Position& previous);
|
Name parseIndexName(const char* context, const Position& previous);
|
||||||
|
|
||||||
// `<' namelist `>'
|
// `<' namelist `>'
|
||||||
std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> parseGenericTypeList(bool withDefaultValues);
|
std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> parseGenericTypeList(
|
||||||
|
bool withDefaultValues,
|
||||||
|
Position* openPosition = nullptr,
|
||||||
|
TempVector<Position>* commaPositions = nullptr,
|
||||||
|
Position* closePosition = nullptr
|
||||||
|
);
|
||||||
|
|
||||||
// `<' Type[, ...] `>'
|
// `<' Type[, ...] `>'
|
||||||
AstArray<AstTypeOrPack> parseTypeParams(
|
AstArray<AstTypeOrPack> parseTypeParams(
|
||||||
|
|
|
@ -111,11 +111,7 @@ CstStatForIn::CstStatForIn(AstArray<Position> varsCommaPositions, AstArray<Posit
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CstStatAssign::CstStatAssign(
|
CstStatAssign::CstStatAssign(AstArray<Position> varsCommaPositions, Position equalsPosition, AstArray<Position> valuesCommaPositions)
|
||||||
AstArray<Position> varsCommaPositions,
|
|
||||||
Position equalsPosition,
|
|
||||||
AstArray<Position> valuesCommaPositions
|
|
||||||
)
|
|
||||||
: CstNode(CstClassIndex())
|
: CstNode(CstClassIndex())
|
||||||
, varsCommaPositions(varsCommaPositions)
|
, varsCommaPositions(varsCommaPositions)
|
||||||
, equalsPosition(equalsPosition)
|
, equalsPosition(equalsPosition)
|
||||||
|
@ -135,6 +131,35 @@ CstStatLocalFunction::CstStatLocalFunction(Position functionKeywordPosition)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CstGenericType::CstGenericType(std::optional<Position> defaultEqualsPosition)
|
||||||
|
: CstNode(CstClassIndex())
|
||||||
|
, defaultEqualsPosition(defaultEqualsPosition)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CstGenericTypePack::CstGenericTypePack(Position ellipsisPosition, std::optional<Position> defaultEqualsPosition)
|
||||||
|
: CstNode(CstClassIndex())
|
||||||
|
, ellipsisPosition(ellipsisPosition)
|
||||||
|
, defaultEqualsPosition(defaultEqualsPosition)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CstStatTypeAlias::CstStatTypeAlias(
|
||||||
|
Position typeKeywordPosition,
|
||||||
|
Position genericsOpenPosition,
|
||||||
|
AstArray<Position> genericsCommaPositions,
|
||||||
|
Position genericsClosePosition,
|
||||||
|
Position equalsPosition
|
||||||
|
)
|
||||||
|
: CstNode(CstClassIndex())
|
||||||
|
, typeKeywordPosition(typeKeywordPosition)
|
||||||
|
, genericsOpenPosition(genericsOpenPosition)
|
||||||
|
, genericsCommaPositions(genericsCommaPositions)
|
||||||
|
, genericsClosePosition(genericsClosePosition)
|
||||||
|
, equalsPosition(equalsPosition)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CstTypeReference::CstTypeReference(
|
CstTypeReference::CstTypeReference(
|
||||||
std::optional<Position> prefixPointPosition,
|
std::optional<Position> prefixPointPosition,
|
||||||
Position openParametersPosition,
|
Position openParametersPosition,
|
||||||
|
|
|
@ -28,6 +28,7 @@ LUAU_FASTFLAGVARIABLE(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup)
|
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup)
|
||||||
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -374,12 +375,13 @@ AstStat* Parser::parseStat()
|
||||||
AstName ident = getIdentifier(expr);
|
AstName ident = getIdentifier(expr);
|
||||||
|
|
||||||
if (ident == "type")
|
if (ident == "type")
|
||||||
return parseTypeAlias(expr->location, /* exported= */ false);
|
return parseTypeAlias(expr->location, /* exported= */ false, expr->location.begin);
|
||||||
|
|
||||||
if (ident == "export" && lexer.current().type == Lexeme::Name && AstName(lexer.current().name) == "type")
|
if (ident == "export" && lexer.current().type == Lexeme::Name && AstName(lexer.current().name) == "type")
|
||||||
{
|
{
|
||||||
|
Position typeKeywordPosition = lexer.current().location.begin;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
return parseTypeAlias(expr->location, /* exported= */ true);
|
return parseTypeAlias(expr->location, /* exported= */ true, typeKeywordPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ident == "continue")
|
if (ident == "continue")
|
||||||
|
@ -534,11 +536,13 @@ AstStat* Parser::parseDo()
|
||||||
|
|
||||||
body->location.begin = start.begin;
|
body->location.begin = start.begin;
|
||||||
|
|
||||||
Position endPosition = lexer.current().location.begin;
|
Location endLocation = lexer.current().location;
|
||||||
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
|
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
|
||||||
|
if (FFlag::LuauFixDoBlockEndLocation && body->hasEnd)
|
||||||
|
body->location.end = endLocation.end;
|
||||||
|
|
||||||
if (FFlag::LuauStoreCSTData && options.storeCstData)
|
if (FFlag::LuauStoreCSTData && options.storeCstData)
|
||||||
cstNodeMap[body] = allocator.alloc<CstStatDo>(endPosition);
|
cstNodeMap[body] = allocator.alloc<CstStatDo>(endLocation.begin);
|
||||||
|
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
@ -721,7 +725,12 @@ AstExpr* Parser::parseFunctionName(Location start_DEPRECATED, bool& hasself, Ast
|
||||||
debugname = name.name;
|
debugname = name.name;
|
||||||
|
|
||||||
expr = allocator.alloc<AstExprIndexName>(
|
expr = allocator.alloc<AstExprIndexName>(
|
||||||
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location), expr, name.name, name.location, opPosition, '.'
|
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location),
|
||||||
|
expr,
|
||||||
|
name.name,
|
||||||
|
name.location,
|
||||||
|
opPosition,
|
||||||
|
'.'
|
||||||
);
|
);
|
||||||
|
|
||||||
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
|
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
|
||||||
|
@ -742,7 +751,12 @@ AstExpr* Parser::parseFunctionName(Location start_DEPRECATED, bool& hasself, Ast
|
||||||
debugname = name.name;
|
debugname = name.name;
|
||||||
|
|
||||||
expr = allocator.alloc<AstExprIndexName>(
|
expr = allocator.alloc<AstExprIndexName>(
|
||||||
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location), expr, name.name, name.location, opPosition, ':'
|
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location),
|
||||||
|
expr,
|
||||||
|
name.name,
|
||||||
|
name.location,
|
||||||
|
opPosition,
|
||||||
|
':'
|
||||||
);
|
);
|
||||||
|
|
||||||
hasself = true;
|
hasself = true;
|
||||||
|
@ -1007,7 +1021,7 @@ AstStat* Parser::parseReturn()
|
||||||
}
|
}
|
||||||
|
|
||||||
// type Name [`<' varlist `>'] `=' Type
|
// type Name [`<' varlist `>'] `=' Type
|
||||||
AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position typeKeywordPosition)
|
||||||
{
|
{
|
||||||
// parsing a type function
|
// parsing a type function
|
||||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||||
|
@ -1023,13 +1037,34 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||||
if (!name)
|
if (!name)
|
||||||
name = Name(nameError, lexer.current().location);
|
name = Name(nameError, lexer.current().location);
|
||||||
|
|
||||||
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ true);
|
Position genericsOpenPosition{0, 0};
|
||||||
|
TempVector<Position> genericsCommaPositions(scratchPosition);
|
||||||
|
Position genericsClosePosition{0, 0};
|
||||||
|
auto [generics, genericPacks] = FFlag::LuauStoreCSTData && options.storeCstData
|
||||||
|
? parseGenericTypeList(
|
||||||
|
/* withDefaultValues= */ true, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition
|
||||||
|
)
|
||||||
|
: parseGenericTypeList(/* withDefaultValues= */ true);
|
||||||
|
|
||||||
|
Position equalsPosition = lexer.current().location.begin;
|
||||||
expectAndConsume('=', "type alias");
|
expectAndConsume('=', "type alias");
|
||||||
|
|
||||||
AstType* type = parseType();
|
AstType* type = parseType();
|
||||||
|
|
||||||
return allocator.alloc<AstStatTypeAlias>(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported);
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstStatTypeAlias* node =
|
||||||
|
allocator.alloc<AstStatTypeAlias>(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstStatTypeAlias>(
|
||||||
|
typeKeywordPosition, genericsOpenPosition, copy(genericsCommaPositions), genericsClosePosition, equalsPosition
|
||||||
|
);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return allocator.alloc<AstStatTypeAlias>(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type function Name `(' arglist `)' `=' funcbody `end'
|
// type function Name `(' arglist `)' `=' funcbody `end'
|
||||||
|
@ -1733,8 +1768,8 @@ std::pair<CstExprConstantString::QuoteStyle, unsigned int> Parser::extractString
|
||||||
switch (lexer.current().type)
|
switch (lexer.current().type)
|
||||||
{
|
{
|
||||||
case Lexeme::QuotedString:
|
case Lexeme::QuotedString:
|
||||||
style = lexer.current().getQuoteStyle() == Lexeme::QuoteStyle::Double ? CstExprConstantString::QuotedDouble
|
style =
|
||||||
: CstExprConstantString::QuotedSingle;
|
lexer.current().getQuoteStyle() == Lexeme::QuoteStyle::Double ? CstExprConstantString::QuotedDouble : CstExprConstantString::QuotedSingle;
|
||||||
break;
|
break;
|
||||||
case Lexeme::InterpStringSimple:
|
case Lexeme::InterpStringSimple:
|
||||||
style = CstExprConstantString::QuotedInterp;
|
style = CstExprConstantString::QuotedInterp;
|
||||||
|
@ -3026,9 +3061,7 @@ AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self)
|
||||||
{
|
{
|
||||||
AstExprCall* node = allocator.alloc<AstExprCall>(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd));
|
AstExprCall* node = allocator.alloc<AstExprCall>(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd));
|
||||||
if (options.storeCstData)
|
if (options.storeCstData)
|
||||||
cstNodeMap[node] = allocator.alloc<CstExprCall>(
|
cstNodeMap[node] = allocator.alloc<CstExprCall>(matchParen.position, lexer.previousLocation().begin, copy(commaPositions));
|
||||||
matchParen.position, lexer.previousLocation().begin, copy(commaPositions)
|
|
||||||
);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3314,7 +3347,12 @@ Parser::Name Parser::parseIndexName(const char* context, const Position& previou
|
||||||
return Name(nameError, location);
|
return Name(nameError, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::parseGenericTypeList(bool withDefaultValues)
|
std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::parseGenericTypeList(
|
||||||
|
bool withDefaultValues,
|
||||||
|
Position* openPosition,
|
||||||
|
TempVector<Position>* commaPositions,
|
||||||
|
Position* closePosition
|
||||||
|
)
|
||||||
{
|
{
|
||||||
TempVector<AstGenericType*> names{scratchGenericTypes};
|
TempVector<AstGenericType*> names{scratchGenericTypes};
|
||||||
TempVector<AstGenericTypePack*> namePacks{scratchGenericTypePacks};
|
TempVector<AstGenericTypePack*> namePacks{scratchGenericTypePacks};
|
||||||
|
@ -3322,6 +3360,8 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
if (lexer.current().type == '<')
|
if (lexer.current().type == '<')
|
||||||
{
|
{
|
||||||
Lexeme begin = lexer.current();
|
Lexeme begin = lexer.current();
|
||||||
|
if (FFlag::LuauStoreCSTData && openPosition)
|
||||||
|
*openPosition = begin.location.begin;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
bool seenPack = false;
|
bool seenPack = false;
|
||||||
|
@ -3335,6 +3375,7 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
{
|
{
|
||||||
seenPack = true;
|
seenPack = true;
|
||||||
|
|
||||||
|
Position ellipsisPosition = lexer.current().location.begin;
|
||||||
if (lexer.current().type != Lexeme::Dot3)
|
if (lexer.current().type != Lexeme::Dot3)
|
||||||
report(lexer.current().location, "Generic types come before generic type packs");
|
report(lexer.current().location, "Generic types come before generic type packs");
|
||||||
else
|
else
|
||||||
|
@ -3343,13 +3384,24 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
if (withDefaultValues && lexer.current().type == '=')
|
if (withDefaultValues && lexer.current().type == '=')
|
||||||
{
|
{
|
||||||
seenDefault = true;
|
seenDefault = true;
|
||||||
|
Position equalsPosition = lexer.current().location.begin;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
if (shouldParseTypePack(lexer))
|
if (shouldParseTypePack(lexer))
|
||||||
{
|
{
|
||||||
AstTypePack* typePack = parseTypePack();
|
AstTypePack* typePack = parseTypePack();
|
||||||
|
|
||||||
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack));
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, equalsPosition);
|
||||||
|
namePacks.push_back(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3358,7 +3410,17 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
if (type)
|
if (type)
|
||||||
report(type->location, "Expected type pack after '=', got type");
|
report(type->location, "Expected type pack after '=', got type");
|
||||||
|
|
||||||
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack));
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, equalsPosition);
|
||||||
|
namePacks.push_back(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3366,7 +3428,17 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
if (seenDefault)
|
if (seenDefault)
|
||||||
report(lexer.current().location, "Expected default type pack after type pack name");
|
report(lexer.current().location, "Expected default type pack after type pack name");
|
||||||
|
|
||||||
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, nullptr));
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, nullptr);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, std::nullopt);
|
||||||
|
namePacks.push_back(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
namePacks.push_back(allocator.alloc<AstGenericTypePack>(nameLocation, name, nullptr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3374,23 +3446,46 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
if (withDefaultValues && lexer.current().type == '=')
|
if (withDefaultValues && lexer.current().type == '=')
|
||||||
{
|
{
|
||||||
seenDefault = true;
|
seenDefault = true;
|
||||||
|
Position equalsPosition = lexer.current().location.begin;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
AstType* defaultType = parseType();
|
AstType* defaultType = parseType();
|
||||||
|
|
||||||
names.push_back(allocator.alloc<AstGenericType>(nameLocation, name, defaultType));
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstGenericType* node = allocator.alloc<AstGenericType>(nameLocation, name, defaultType);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstGenericType>(equalsPosition);
|
||||||
|
names.push_back(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
names.push_back(allocator.alloc<AstGenericType>(nameLocation, name, defaultType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (seenDefault)
|
if (seenDefault)
|
||||||
report(lexer.current().location, "Expected default type after type name");
|
report(lexer.current().location, "Expected default type after type name");
|
||||||
|
|
||||||
names.push_back(allocator.alloc<AstGenericType>(nameLocation, name, nullptr));
|
if (FFlag::LuauStoreCSTData)
|
||||||
|
{
|
||||||
|
AstGenericType* node = allocator.alloc<AstGenericType>(nameLocation, name, nullptr);
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstGenericType>(std::nullopt);
|
||||||
|
names.push_back(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
names.push_back(allocator.alloc<AstGenericType>(nameLocation, name, nullptr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lexer.current().type == ',')
|
if (lexer.current().type == ',')
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauStoreCSTData && commaPositions)
|
||||||
|
commaPositions->push_back(lexer.current().location.begin);
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
if (lexer.current().type == '>')
|
if (lexer.current().type == '>')
|
||||||
|
@ -3403,6 +3498,8 @@ std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::pars
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreCSTData && closePosition)
|
||||||
|
*closePosition = lexer.current().location.begin;
|
||||||
expectMatchAndConsume('>', begin);
|
expectMatchAndConsume('>', begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
|
|
||||||
// __register_frame and __deregister_frame are defined in libgcc or libc++
|
// __register_frame and __deregister_frame are defined in libgcc or libc++
|
||||||
// (depending on how it's built). We want to declare them as weak symbols
|
// (depending on how it's built). We want to declare them as weak symbols
|
||||||
// so that if they're provided by a shared library, we'll use them, and if
|
// so that if they're provided by a shared library, we'll use them, and if
|
||||||
// not, we'll disable some c++ exception handling support. However, if they're
|
// not, we'll disable some c++ exception handling support. However, if they're
|
||||||
// declared as weak and the definitions are linked in a static library
|
// declared as weak and the definitions are linked in a static library
|
||||||
// that's not linked with whole-archive, then the symbols will technically be defined here,
|
// that's not linked with whole-archive, then the symbols will technically be defined here,
|
||||||
// and the linker won't look for the strong ones in the library.
|
// and the linker won't look for the strong ones in the library.
|
||||||
#ifndef LUAU_ENABLE_REGISTER_FRAME
|
#ifndef LUAU_ENABLE_REGISTER_FRAME
|
||||||
#define REGISTER_FRAME_WEAK __attribute__((weak))
|
#define REGISTER_FRAME_WEAK __attribute__((weak))
|
||||||
|
|
|
@ -66,7 +66,7 @@ struct Node
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename L>
|
template<typename L>
|
||||||
struct NodeIterator
|
struct NodeIterator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -22,6 +22,7 @@ LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAG(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
LUAU_FASTFLAG(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup)
|
||||||
|
LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
|
||||||
|
|
||||||
struct ATSFixture : BuiltinsFixture
|
struct ATSFixture : BuiltinsFixture
|
||||||
|
@ -693,6 +694,7 @@ TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
|
{FFlag::LuauDeferBidirectionalInferenceForTableAssignment, true}
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -705,8 +707,7 @@ TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic")
|
||||||
y.g.i = y
|
y.g.i = y
|
||||||
)";
|
)";
|
||||||
|
|
||||||
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
LUAU_REQUIRE_NO_ERRORS(frontend.check("game/Gui/Modules/A"));
|
||||||
LUAU_REQUIRE_ERROR_COUNT(2, result1);
|
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
|
|
|
@ -475,7 +475,8 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup)
|
if (FFlag::LuauAstTypeGroup)
|
||||||
{
|
{
|
||||||
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","type":{"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","type":{"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","type":{"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})";
|
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})";
|
||||||
CHECK(toJson(statement) == expected);
|
CHECK(toJson(statement) == expected);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -132,6 +132,15 @@ ClassFixture::ClassFixture()
|
||||||
// IndexableNumericKeyClass has a table indexer with a key type of 'number' and a return type of 'number'
|
// IndexableNumericKeyClass has a table indexer with a key type of 'number' and a return type of 'number'
|
||||||
addIndexableClass("IndexableNumericKeyClass", numberType, numberType);
|
addIndexableClass("IndexableNumericKeyClass", numberType, numberType);
|
||||||
|
|
||||||
|
// Add a confusing derived class which shares the same name internally, but has a unique alias
|
||||||
|
TypeId duplicateBaseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
|
getMutable<ClassType>(duplicateBaseClassInstanceType)->props = {
|
||||||
|
{"Method", {makeFunction(arena, duplicateBaseClassInstanceType, {}, {stringType})}},
|
||||||
|
};
|
||||||
|
|
||||||
|
addGlobalBinding(globals, "confusingBaseClassInstance", duplicateBaseClassInstanceType, "@test");
|
||||||
|
|
||||||
for (const auto& [name, tf] : globals.globalScope->exportedTypeBindings)
|
for (const auto& [name, tf] : globals.globalScope->exportedTypeBindings)
|
||||||
persist(tf.type);
|
persist(tf.type);
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
|
||||||
void registerHiddenTypes(Frontend* frontend);
|
void registerHiddenTypes(Frontend* frontend);
|
||||||
void createSomeClasses(Frontend* frontend);
|
void createSomeClasses(Frontend* frontend);
|
||||||
|
|
||||||
template <typename E>
|
template<typename E>
|
||||||
const E* findError(const CheckResult& result)
|
const E* findError(const CheckResult& result)
|
||||||
{
|
{
|
||||||
for (const auto& e : result.errors)
|
for (const auto& e : result.errors)
|
||||||
|
|
|
@ -31,7 +31,6 @@ LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauCloneIncrementalModule)
|
LUAU_FASTFLAG(LuauCloneIncrementalModule)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteBugfixes)
|
LUAU_FASTFLAG(LuauIncrementalAutocompleteBugfixes)
|
||||||
LUAU_FASTFLAG(LuauReferenceAllocatorInNewSolver)
|
|
||||||
LUAU_FASTFLAG(LuauMixedModeDefFinderTraversesTypeOf)
|
LUAU_FASTFLAG(LuauMixedModeDefFinderTraversesTypeOf)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
|
|
||||||
|
@ -65,13 +64,12 @@ 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[8] = {
|
ScopedFastFlag sffs[7] = {
|
||||||
{FFlag::LuauAllowFragmentParsing, true},
|
{FFlag::LuauAllowFragmentParsing, true},
|
||||||
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
|
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
|
||||||
{FFlag::LuauStoreSolverTypeOnModule, true},
|
{FFlag::LuauStoreSolverTypeOnModule, true},
|
||||||
{FFlag::LuauSymbolEquality, true},
|
{FFlag::LuauSymbolEquality, true},
|
||||||
{FFlag::LexerResumesFromPosition2, true},
|
{FFlag::LexerResumesFromPosition2, true},
|
||||||
{FFlag::LuauReferenceAllocatorInNewSolver, true},
|
|
||||||
{FFlag::LuauIncrementalAutocompleteBugfixes, true},
|
{FFlag::LuauIncrementalAutocompleteBugfixes, true},
|
||||||
{FFlag::LuauBetterReverseDependencyTracking, true},
|
{FFlag::LuauBetterReverseDependencyTracking, true},
|
||||||
};
|
};
|
||||||
|
@ -929,6 +927,148 @@ tbl.abc.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "multiple_functions_complex")
|
||||||
|
{
|
||||||
|
const std::string text = R"( local function f1(a1)
|
||||||
|
local l1 = 1;"
|
||||||
|
g1 = 1;"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function f2(a2)
|
||||||
|
local l2 = 1;
|
||||||
|
g2 = 1;
|
||||||
|
end
|
||||||
|
)";
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{0, 0},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") == 0);
|
||||||
|
CHECK(strings.count("a1") == 0);
|
||||||
|
CHECK(strings.count("l1") == 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") == 0);
|
||||||
|
CHECK(strings.count("a2") == 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{0, 22},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
CHECK(strings.count("a1") != 0);
|
||||||
|
CHECK(strings.count("l1") == 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") == 0);
|
||||||
|
CHECK(strings.count("a2") == 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{1, 17},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
CHECK(strings.count("a1") != 0);
|
||||||
|
CHECK(strings.count("l1") != 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") == 0);
|
||||||
|
CHECK(strings.count("a2") == 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{2, 11},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
CHECK(strings.count("a1") != 0);
|
||||||
|
CHECK(strings.count("l1") != 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") == 0);
|
||||||
|
CHECK(strings.count("a2") == 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{4, 0},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
// FIXME: RIDE-11123: This should be zero counts of `a1`.
|
||||||
|
CHECK(strings.count("a1") != 0);
|
||||||
|
CHECK(strings.count("l1") == 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") == 0);
|
||||||
|
CHECK(strings.count("a2") == 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{6, 17},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
CHECK(strings.count("a1") == 0);
|
||||||
|
CHECK(strings.count("l1") == 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") != 0);
|
||||||
|
CHECK(strings.count("a2") != 0);
|
||||||
|
CHECK(strings.count("l2") != 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
text,
|
||||||
|
text,
|
||||||
|
Position{8, 4},
|
||||||
|
[](FragmentAutocompleteResult& fragment)
|
||||||
|
{
|
||||||
|
auto strings = fragment.acResults.entryMap;
|
||||||
|
CHECK(strings.count("f1") != 0);
|
||||||
|
CHECK(strings.count("a1") == 0);
|
||||||
|
CHECK(strings.count("l1") == 0);
|
||||||
|
CHECK(strings.count("g1") != 0);
|
||||||
|
CHECK(strings.count("f2") != 0);
|
||||||
|
// FIXME: RIDE-11123: This should be zero counts of `a2`.
|
||||||
|
CHECK(strings.count("a2") != 0);
|
||||||
|
CHECK(strings.count("l2") == 0);
|
||||||
|
CHECK(strings.count("g2") != 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "inline_autocomplete_picks_the_right_scope")
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "inline_autocomplete_picks_the_right_scope")
|
||||||
{
|
{
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
|
|
|
@ -16,7 +16,6 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||||
LUAU_FASTFLAG(LuauReferenceAllocatorInNewSolver);
|
|
||||||
LUAU_FASTFLAG(LuauSelectivelyRetainDFGArena)
|
LUAU_FASTFLAG(LuauSelectivelyRetainDFGArena)
|
||||||
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking);
|
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking);
|
||||||
|
|
||||||
|
@ -1528,7 +1527,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts_dirty")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator")
|
TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauReferenceAllocatorInNewSolver, true};
|
|
||||||
fileResolver.source["game/workspace/MyScript"] = R"(
|
fileResolver.source["game/workspace/MyScript"] = R"(
|
||||||
print("Hello World")
|
print("Hello World")
|
||||||
)";
|
)";
|
||||||
|
@ -1546,10 +1544,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FrontendFixture, "dfg_data_cleared_on_retain_type_graphs_unset")
|
TEST_CASE_FIXTURE(FrontendFixture, "dfg_data_cleared_on_retain_type_graphs_unset")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauSelectivelyRetainDFGArena, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauSelectivelyRetainDFGArena, true}
|
|
||||||
};
|
|
||||||
fileResolver.source["game/A"] = R"(
|
fileResolver.source["game/A"] = R"(
|
||||||
local a = 1
|
local a = 1
|
||||||
local b = 2
|
local b = 2
|
||||||
|
@ -1760,7 +1755,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_invalid_dependency_tracking_per_module_
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = "return {hello=5, world=true}";
|
fileResolver.source["game/Gui/Modules/A"] = "return {hello=5, world=true}";
|
||||||
fileResolver.source["game/Gui/Modules/B"] = "return require(game:GetService('Gui').Modules.A)";
|
fileResolver.source["game/Gui/Modules/B"] = "return require(game:GetService('Gui').Modules.A)";
|
||||||
|
|
||||||
FrontendOptions opts;
|
FrontendOptions opts;
|
||||||
opts.forAutocomplete = false;
|
opts.forAutocomplete = false;
|
||||||
|
|
||||||
|
|
|
@ -641,10 +641,7 @@ TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict")
|
TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict")
|
||||||
{
|
{
|
||||||
ScopedFastFlag flags[] = {
|
ScopedFastFlag flags[] = {{FFlag::LuauNonStrictVisitorImprovements, true}, {FFlag::LuauNewNonStrictWarnOnUnknownGlobals, true}};
|
||||||
{FFlag::LuauNonStrictVisitorImprovements, true},
|
|
||||||
{FFlag::LuauNewNonStrictWarnOnUnknownGlobals, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(Mode::Nonstrict, R"(
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
foo = 5
|
foo = 5
|
||||||
|
|
|
@ -23,6 +23,7 @@ LUAU_FASTFLAG(LuauFixFunctionNameStartPosition)
|
||||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup)
|
LUAU_FASTFLAG(LuauAstTypeGroup)
|
||||||
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -2508,6 +2509,40 @@ TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
|
||||||
CHECK(funcType->returnTypes.types.data[0]->is<AstTypeGroup>());
|
CHECK(funcType->returnTypes.types.data[0]->is<AstTypeGroup>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "inner_and_outer_scope_of_functions_have_correct_end_position")
|
||||||
|
{
|
||||||
|
|
||||||
|
AstStatBlock* stat = parse(R"(
|
||||||
|
local function foo()
|
||||||
|
local x = 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
REQUIRE(stat);
|
||||||
|
REQUIRE_EQ(1, stat->body.size);
|
||||||
|
|
||||||
|
auto func = stat->body.data[0]->as<AstStatLocalFunction>();
|
||||||
|
REQUIRE(func);
|
||||||
|
CHECK_EQ(func->func->body->location, Location{{1, 28}, {3, 8}});
|
||||||
|
CHECK_EQ(func->location, Location{{1, 8}, {3, 11}});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "do_block_end_location_is_after_end_token")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauFixDoBlockEndLocation, true};
|
||||||
|
|
||||||
|
AstStatBlock* stat = parse(R"(
|
||||||
|
do
|
||||||
|
local x = 1
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
REQUIRE(stat);
|
||||||
|
REQUIRE_EQ(1, stat->body.size);
|
||||||
|
|
||||||
|
auto block = stat->body.data[0]->as<AstStatBlock>();
|
||||||
|
REQUIRE(block);
|
||||||
|
CHECK_EQ(block->location, Location{{1, 8}, {3, 11}});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
||||||
|
@ -3786,7 +3821,7 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CHECK(unionTy->types.data[0]->is<AstTypeFunction>()); // () -> ()
|
CHECK(unionTy->types.data[0]->is<AstTypeFunction>()); // () -> ()
|
||||||
CHECK(unionTy->types.data[1]->is<AstTypeReference>()); // nil
|
CHECK(unionTy->types.data[1]->is<AstTypeReference>()); // nil
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauExtendedSimpleRequire)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -178,4 +180,59 @@ TEST_CASE_FIXTURE(RequireTracerFixture, "follow_string_indexexpr")
|
||||||
CHECK_EQ("game/Test", result.exprs[local->values.data[0]].name);
|
CHECK_EQ("game/Test", result.exprs[local->values.data[0]].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RequireTracerFixture, "follow_group")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauExtendedSimpleRequire{FFlag::LuauExtendedSimpleRequire, true};
|
||||||
|
|
||||||
|
AstStatBlock* block = parse(R"(
|
||||||
|
local R = (((game).Test))
|
||||||
|
require(R)
|
||||||
|
)");
|
||||||
|
REQUIRE_EQ(2, block->body.size);
|
||||||
|
|
||||||
|
RequireTraceResult result = traceRequires(&fileResolver, block, "ModuleName");
|
||||||
|
|
||||||
|
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
|
||||||
|
REQUIRE(local != nullptr);
|
||||||
|
|
||||||
|
CHECK_EQ("game/Test", result.exprs[local->values.data[0]].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RequireTracerFixture, "follow_type_annotation")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauExtendedSimpleRequire{FFlag::LuauExtendedSimpleRequire, true};
|
||||||
|
|
||||||
|
AstStatBlock* block = parse(R"(
|
||||||
|
local R = game.Test :: (typeof(game.Redirect))
|
||||||
|
require(R)
|
||||||
|
)");
|
||||||
|
REQUIRE_EQ(2, block->body.size);
|
||||||
|
|
||||||
|
RequireTraceResult result = traceRequires(&fileResolver, block, "ModuleName");
|
||||||
|
|
||||||
|
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
|
||||||
|
REQUIRE(local != nullptr);
|
||||||
|
|
||||||
|
CHECK_EQ("game/Redirect", result.exprs[local->values.data[0]].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RequireTracerFixture, "follow_type_annotation_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauExtendedSimpleRequire{FFlag::LuauExtendedSimpleRequire, true};
|
||||||
|
|
||||||
|
AstStatBlock* block = parse(R"(
|
||||||
|
local R = game.Test :: (typeof(game.Redirect))
|
||||||
|
local N = R.Nested
|
||||||
|
require(N)
|
||||||
|
)");
|
||||||
|
REQUIRE_EQ(3, block->body.size);
|
||||||
|
|
||||||
|
RequireTraceResult result = traceRequires(&fileResolver, block, "ModuleName");
|
||||||
|
|
||||||
|
AstStatLocal* local = block->body.data[1]->as<AstStatLocal>();
|
||||||
|
REQUIRE(local != nullptr);
|
||||||
|
|
||||||
|
CHECK_EQ("game/Redirect/Nested", result.exprs[local->values.data[0]].name);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -317,6 +317,74 @@ TEST_CASE("returns_spaces_around_tokens")
|
||||||
CHECK_EQ(three, transpile(three).code);
|
CHECK_EQ(three, transpile(three).code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauStoreCSTData, true};
|
||||||
|
std::string code = R"( type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( export type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( export type Foo = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo <X, Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo< X, Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X , Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y , Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y, Z...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y, Z ...> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X, Y, Z... > = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauStoreCSTData, true};
|
||||||
|
std::string code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("table_literals")
|
TEST_CASE("table_literals")
|
||||||
{
|
{
|
||||||
const std::string code = R"( local t={1, 2, 3, foo='bar', baz=99,[5.5]='five point five', 'end'} )";
|
const std::string code = R"( local t={1, 2, 3, foo='bar', baz=99,[5.5]='five point five', 'end'} )";
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunFixHydratedClasses)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunSingletonEquality)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypeofReturnsType)
|
LUAU_FASTFLAG(LuauUserTypeFunTypeofReturnsType)
|
||||||
LUAU_FASTFLAG(LuauTypeFunPrintFix)
|
LUAU_FASTFLAG(LuauTypeFunPrintFix)
|
||||||
|
|
||||||
|
@ -489,7 +491,10 @@ local function notok(idx: fail<number>): never return idx end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
CHECK(toString(result.errors[0]) == R"('fail' type function errored at runtime: [string "fail"]:7: type.inner: cannot call inner method on non-negation type: `number` type)");
|
CHECK(
|
||||||
|
toString(result.errors[0]) ==
|
||||||
|
R"('fail' type function errored at runtime: [string "fail"]:7: type.inner: cannot call inner method on non-negation type: `number` type)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
|
||||||
|
@ -639,6 +644,21 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauTypeFunFixHydratedClasses{FFlag::LuauTypeFunFixHydratedClasses, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function serialize_class(arg)
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
local function ok(idx: serialize_class<typeof(confusingBaseClassInstance)>): typeof(confusingBaseClassInstance) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
@ -1868,6 +1888,40 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_eqsat_opaque")
|
||||||
CHECK_EQ("t0<number & string>", toString(simplified->result)); // NOLINT(bugprone-unchecked-optional-access)
|
CHECK_EQ("t0<number & string>", toString(simplified->result)); // NOLINT(bugprone-unchecked-optional-access)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauTypeFunSingletonEquality{FFlag::LuauTypeFunSingletonEquality, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function compare(arg)
|
||||||
|
return types.singleton(types.singleton(false) == arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok(idx: compare<false>): true return idx end
|
||||||
|
local function ok(idx: compare<true>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauTypeFunSingletonEquality{FFlag::LuauTypeFunSingletonEquality, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function compare(arg)
|
||||||
|
return types.singleton(types.singleton("") == arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok(idx: compare<"">): true return idx end
|
||||||
|
local function ok(idx: compare<"a">): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "typeof_type_userdata_returns_type")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "typeof_type_userdata_returns_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||||
|
|
|
@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingFixTailPack)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -3027,4 +3028,17 @@ TEST_CASE_FIXTURE(Fixture, "hidden_variadics_should_not_break_subtyping")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_wrap_result_call")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauSubtypingFixTailPack{FFlag::LuauSubtypingFixTailPack, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
function foo(a, b)
|
||||||
|
coroutine.wrap(a)(b)
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
// New solver still reports an error in this case, but the main goal of the test is to not crash
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping);
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping);
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
|
LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -854,6 +855,8 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
|
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDeferBidirectionalInferenceForTableAssignment, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
-- At one point this produced a UAF
|
-- At one point this produced a UAF
|
||||||
|
@ -865,15 +868,19 @@ local y: T<string> = { a = { c = nil, d = 5 }, b = 37 }
|
||||||
y.a.c = y
|
y.a.c = y
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
CHECK(
|
{
|
||||||
toString(result.errors.at(0)) ==
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
R"(Type '{ a: { c: nil, d: number }, b: number }' could not be converted into 'T<number>'; type { a: { c: nil, d: number }, b: number }[read "a"][read "c"] (nil) is not exactly T<number>[read "a"][read "c"][0] (T<number>))"
|
auto mismatch = get<TypeMismatch>(result.errors.at(0));
|
||||||
);
|
CHECK(mismatch);
|
||||||
|
CHECK_EQ(toString(mismatch->givenType), "{ a: { c: T<string>?, d: number }, b: number }");
|
||||||
|
CHECK_EQ(toString(mismatch->wantedType), "T<string>");
|
||||||
|
std::string reason = "at [read \"a\"][read \"d\"], number is not exactly string\n\tat [read \"b\"], number is not exactly string";
|
||||||
|
CHECK_EQ(mismatch->reason, reason);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
const std::string expected = R"(Type 'y' could not be converted into 'T<string>'
|
const std::string expected = R"(Type 'y' could not be converted into 'T<string>'
|
||||||
caused by:
|
caused by:
|
||||||
Property 'a' is not compatible.
|
Property 'a' is not compatible.
|
||||||
|
|
|
@ -815,11 +815,11 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "and_binexps_dont_unify")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "and_binexps_dont_unify")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauDoNotGeneralizeInTypeFunctions, true};
|
ScopedFastFlag _{FFlag::LuauDoNotGeneralizeInTypeFunctions, true};
|
||||||
|
|
||||||
// `t` will be inferred to be of type `{ { test: unknown } }` which is
|
// `t` will be inferred to be of type `{ { test: unknown } }` which is
|
||||||
// reasonable, in that it's empty with no bounds on its members. Optimally
|
// reasonable, in that it's empty with no bounds on its members. Optimally
|
||||||
// we might emit an error here that the `print(...)` expression is
|
// we might emit an error here that the `print(...)` expression is
|
||||||
// unreachable.
|
// unreachable.
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauGeneralizationRemoveRecursiveUpperBound)
|
LUAU_FASTFLAG(LuauGeneralizationRemoveRecursiveUpperBound2)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -2445,7 +2445,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::DebugLuauEqSatSimplification, true},
|
{FFlag::DebugLuauEqSatSimplification, true},
|
||||||
{FFlag::LuauGeneralizationRemoveRecursiveUpperBound, true},
|
{FFlag::LuauGeneralizationRemoveRecursiveUpperBound2, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
|
|
@ -25,7 +25,8 @@ LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAG(LuauDontInPlaceMutateTableType)
|
LUAU_FASTFLAG(LuauDontInPlaceMutateTableType)
|
||||||
LUAU_FASTFLAG(LuauAllowNonSharedTableTypesInLiteral)
|
LUAU_FASTFLAG(LuauAllowNonSharedTableTypesInLiteral)
|
||||||
LUAU_FASTFLAG(LuauFollowTableFreeze)
|
LUAU_FASTFLAG(LuauFollowTableFreeze)
|
||||||
LUAU_FASTFLAG(LuauPrecalculateMutatedFreeTypes)
|
LUAU_FASTFLAG(LuauPrecalculateMutatedFreeTypes2)
|
||||||
|
LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
|
|
||||||
|
@ -5012,7 +5013,7 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager")
|
||||||
// bidirectional inference is known to be broken.
|
// bidirectional inference is known to be broken.
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauPrecalculateMutatedFreeTypes, true},
|
{FFlag::LuauPrecalculateMutatedFreeTypes2, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto result = check(R"(
|
auto result = check(R"(
|
||||||
|
@ -5153,4 +5154,44 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert")
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "optional_property_with_call")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDeferBidirectionalInferenceForTableAssignment, true};
|
||||||
|
|
||||||
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
|
type t = {
|
||||||
|
key: boolean?,
|
||||||
|
time: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function num(): number
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local _: t = {
|
||||||
|
time = num(),
|
||||||
|
}
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "empty_union_container_overflow")
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
--!strict
|
||||||
|
local CellRenderer = {}
|
||||||
|
function CellRenderer:init(props)
|
||||||
|
self._separators = {
|
||||||
|
unhighlight = function()
|
||||||
|
local cellKey, prevCellKey = self.props.cellKey, self.props.prevCellKey
|
||||||
|
self.props.onUpdateSeparators({ cellKey, prevCellKey })
|
||||||
|
end,
|
||||||
|
updateProps = function (select, newProps)
|
||||||
|
local cellKey, prevCellKey = self.props.cellKey, self.props.prevCellKey
|
||||||
|
self.props.onUpdateSeparators({ if select == 'leading' then prevCellKey else cellKey })
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
Loading…
Add table
Reference in a new issue