mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
Sync to upstream/release/674 (#1832)
# General * Expose an optional `get_alias` API as an alternative to `get_config` in Luau.Require and Luau.RequireNavigator. * Improve the Luau CLI's virtual filesystem implementation to fix bugs related to `init.luau`. Fixes https://github.com/luau-lang/luau/issues/1816 # New Type Solver * Avoid double reporting errors when erroneous arguments are provided to type functions. * Fix some instances of unresovable cyclic type functions in loops by only considering the first loop cycles. This results in some type inference inaccuracies when the type of a variable in loop through multiple iterations. Fixes https://github.com/luau-lang/luau/issues/1413. * Better generalize free types that have meaningful lower and upper bounds, especially for table indexers. * Report more specific errors when assigning or returning table literal types, instead of citing the *entire* table type. * Inference for functions with generic type packs is greatly improved. * Fix some internal compiler exceptions when using type-stating functions like `table.freeze` in `if _ then _ else _` expressions and short circuiting binary operations. * More consistently simplify unions of primitive types, especially in array-like and dictionary-like tables. * Fix a crash when type checking an erroneous type alias containing `typeof` with a type assertion expression, as in: ``` type MyTable = {} -- This will error at type checking time as it's a duplicate type MyTable = typeof(setmetatable(SomeTable :: {}, SomeMetaTable)); ``` * Fix a crash when inferring the type of an index expression where the indexee is invalid (e.g. `nil`). # Runtime * Avoid throwing an exception from `luau_load` if we run out of memory. * Type functions are no longer compiled and included in bytecode. Fixes #1817. * Fix some instances of Luau C API functions reading invalid debug information (generally when the first or last instruction of a block was being inspected). Fixes #1369. * Avoid potential signed integer overflow when doing bounds checks on tables. * Support 16 byte aligned userdata objects when system allocation alignment is also 16 bytes. * Fix memory leaks in `Luau.Require` when using VM build with no exceptions. Fixes #1827. --------- Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
3eb0c13678
commit
92cce5776c
129 changed files with 4607 additions and 4575 deletions
|
@ -9,7 +9,7 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr char kRequireTagName[] = "require";
|
inline constexpr char kRequireTagName[] = "require";
|
||||||
|
|
||||||
struct Frontend;
|
struct Frontend;
|
||||||
struct GlobalTypes;
|
struct GlobalTypes;
|
||||||
|
|
|
@ -276,6 +276,12 @@ struct ReducePackConstraint
|
||||||
TypePackId tp;
|
TypePackId tp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// simplify ty
|
||||||
|
struct SimplifyConstraint
|
||||||
|
{
|
||||||
|
TypeId ty;
|
||||||
|
};
|
||||||
|
|
||||||
using ConstraintV = Variant<
|
using ConstraintV = Variant<
|
||||||
SubtypeConstraint,
|
SubtypeConstraint,
|
||||||
PackSubtypeConstraint,
|
PackSubtypeConstraint,
|
||||||
|
@ -294,7 +300,8 @@ using ConstraintV = Variant<
|
||||||
ReduceConstraint,
|
ReduceConstraint,
|
||||||
ReducePackConstraint,
|
ReducePackConstraint,
|
||||||
EqualityConstraint,
|
EqualityConstraint,
|
||||||
TableCheckConstraint>;
|
TableCheckConstraint,
|
||||||
|
SimplifyConstraint>;
|
||||||
|
|
||||||
struct Constraint
|
struct Constraint
|
||||||
{
|
{
|
||||||
|
|
|
@ -173,6 +173,8 @@ private:
|
||||||
std::vector<std::vector<TypeId>> DEPRECATED_interiorTypes;
|
std::vector<std::vector<TypeId>> DEPRECATED_interiorTypes;
|
||||||
std::vector<InteriorFreeTypes> interiorFreeTypes;
|
std::vector<InteriorFreeTypes> interiorFreeTypes;
|
||||||
|
|
||||||
|
std::vector<TypeId> unionsToSimplify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fabricates a new free type belonging to a given scope.
|
* Fabricates a new free type belonging to a given scope.
|
||||||
* @param scope the scope the free type belongs to.
|
* @param scope the scope the free type belongs to.
|
||||||
|
@ -447,6 +449,12 @@ private:
|
||||||
|
|
||||||
// make a union type function of these two types
|
// make a union type function of these two types
|
||||||
TypeId makeUnion(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
|
TypeId makeUnion(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
|
||||||
|
|
||||||
|
// Make a union type and add it to `unionsToSimplify`, ensuring that
|
||||||
|
// later we will attempt to simplify this union in order to keep types
|
||||||
|
// small.
|
||||||
|
TypeId makeUnion(std::vector<TypeId> options);
|
||||||
|
|
||||||
// make an intersect type function of these two types
|
// make an intersect type function of these two types
|
||||||
TypeId makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
|
TypeId makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
|
||||||
void prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program);
|
void prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program);
|
||||||
|
|
|
@ -249,6 +249,8 @@ public:
|
||||||
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint);
|
||||||
|
|
||||||
|
bool tryDispatch(const SimplifyConstraint& c, NotNull<const Constraint> constraint);
|
||||||
|
|
||||||
// for a, ... in some_table do
|
// for a, ... in some_table do
|
||||||
// also handles __iter metamethod
|
// also handles __iter metamethod
|
||||||
bool tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
|
|
|
@ -462,6 +462,11 @@ struct ReservedIdentifier
|
||||||
bool operator==(const ReservedIdentifier& rhs) const;
|
bool operator==(const ReservedIdentifier& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UnexpectedArrayLikeTableItem
|
||||||
|
{
|
||||||
|
bool operator==(const UnexpectedArrayLikeTableItem&) const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
using TypeErrorData = Variant<
|
using TypeErrorData = Variant<
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
UnknownSymbol,
|
UnknownSymbol,
|
||||||
|
@ -512,7 +517,8 @@ using TypeErrorData = Variant<
|
||||||
UnexpectedTypePackInSubtyping,
|
UnexpectedTypePackInSubtyping,
|
||||||
ExplicitFunctionAnnotationRecommended,
|
ExplicitFunctionAnnotationRecommended,
|
||||||
UserDefinedTypeFunctionError,
|
UserDefinedTypeFunctionError,
|
||||||
ReservedIdentifier>;
|
ReservedIdentifier,
|
||||||
|
UnexpectedArrayLikeTableItem>;
|
||||||
|
|
||||||
struct TypeErrorSummary
|
struct TypeErrorSummary
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::unordered_map<AstExprBinary::Op, const char*> kBinaryOpMetamethods{
|
inline const std::unordered_map<AstExprBinary::Op, const char*> kBinaryOpMetamethods{
|
||||||
{AstExprBinary::Op::CompareEq, "__eq"},
|
{AstExprBinary::Op::CompareEq, "__eq"},
|
||||||
{AstExprBinary::Op::CompareNe, "__eq"},
|
{AstExprBinary::Op::CompareNe, "__eq"},
|
||||||
{AstExprBinary::Op::CompareGe, "__lt"},
|
{AstExprBinary::Op::CompareGe, "__lt"},
|
||||||
|
@ -25,7 +25,7 @@ static const std::unordered_map<AstExprBinary::Op, const char*> kBinaryOpMetamet
|
||||||
{AstExprBinary::Op::Concat, "__concat"},
|
{AstExprBinary::Op::Concat, "__concat"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_map<AstExprUnary::Op, const char*> kUnaryOpMetamethods{
|
inline const std::unordered_map<AstExprUnary::Op, const char*> kUnaryOpMetamethods{
|
||||||
{AstExprUnary::Op::Minus, "__unm"},
|
{AstExprUnary::Op::Minus, "__unm"},
|
||||||
{AstExprUnary::Op::Len, "__len"},
|
{AstExprUnary::Op::Len, "__len"},
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,7 +60,7 @@ struct SubtypingReasoningHash
|
||||||
};
|
};
|
||||||
|
|
||||||
using SubtypingReasonings = DenseHashSet<SubtypingReasoning, SubtypingReasoningHash>;
|
using SubtypingReasonings = DenseHashSet<SubtypingReasoning, SubtypingReasoningHash>;
|
||||||
static const SubtypingReasoning kEmptyReasoning = SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invalid};
|
inline const SubtypingReasoning kEmptyReasoning = SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invalid};
|
||||||
|
|
||||||
struct SubtypingResult
|
struct SubtypingResult
|
||||||
{
|
{
|
||||||
|
|
|
@ -1206,7 +1206,7 @@ std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||||
|
|
||||||
// A tag to mark a type which doesn't derive directly from the root type as overriding the return of `typeof`.
|
// A tag to mark a type which doesn't derive directly from the root type as overriding the return of `typeof`.
|
||||||
// Any classes which derive from this type will have typeof return this type.
|
// Any classes which derive from this type will have typeof return this type.
|
||||||
static constexpr char kTypeofRootTag[] = "typeofRoot";
|
inline constexpr char kTypeofRootTag[] = "typeofRoot";
|
||||||
|
|
||||||
void attachTag(TypeId ty, const std::string& tagName);
|
void attachTag(TypeId ty, const std::string& tagName);
|
||||||
void attachTag(Property& prop, const std::string& tagName);
|
void attachTag(Property& prop, const std::string& tagName);
|
||||||
|
|
|
@ -193,6 +193,9 @@ private:
|
||||||
|
|
||||||
void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result);
|
void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result);
|
||||||
void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result);
|
void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result);
|
||||||
|
|
||||||
|
bool testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedTy);
|
||||||
|
|
||||||
bool testIsSubtype(TypeId subTy, TypeId superTy, Location location);
|
bool testIsSubtype(TypeId subTy, TypeId superTy, Location location);
|
||||||
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location);
|
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location);
|
||||||
void reportError(TypeError e);
|
void reportError(TypeError e);
|
||||||
|
|
|
@ -216,9 +216,6 @@ struct TypeFunctionExternType
|
||||||
|
|
||||||
std::optional<TypeFunctionTypeId> metatable; // metaclass?
|
std::optional<TypeFunctionTypeId> metatable; // metaclass?
|
||||||
|
|
||||||
// this was mistaken, and we should actually be keeping separate read/write types here.
|
|
||||||
std::optional<TypeFunctionTypeId> parent_DEPRECATED;
|
|
||||||
|
|
||||||
std::optional<TypeFunctionTypeId> readParent;
|
std::optional<TypeFunctionTypeId> readParent;
|
||||||
std::optional<TypeFunctionTypeId> writeParent;
|
std::optional<TypeFunctionTypeId> writeParent;
|
||||||
|
|
||||||
|
|
|
@ -183,7 +183,7 @@ struct PathHash
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The canonical "empty" Path, meaning a Path with no components.
|
/// The canonical "empty" Path, meaning a Path with no components.
|
||||||
static const Path kEmpty{};
|
inline const Path kEmpty{};
|
||||||
|
|
||||||
struct PathBuilder
|
struct PathBuilder
|
||||||
{
|
{
|
||||||
|
|
|
@ -291,4 +291,14 @@ void trackInteriorFreeType(Scope* scope, TypeId ty);
|
||||||
|
|
||||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp);
|
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp);
|
||||||
|
|
||||||
|
// A fast approximation of subTy <: superTy
|
||||||
|
bool fastIsSubtype(TypeId subTy, TypeId superTy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tables A list of potential table parts of a union
|
||||||
|
* @param exprType Type of the expression to match
|
||||||
|
* @return An element of `tables` that best matches `exprType`.
|
||||||
|
*/
|
||||||
|
std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, TypeId exprType, NotNull<BuiltinTypes> builtinTypes);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -25,7 +25,6 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
|
@ -490,14 +489,11 @@ static void autocompleteProps(
|
||||||
// t1 where t1 = t1 | ExternType
|
// t1 where t1 = t1 | ExternType
|
||||||
//
|
//
|
||||||
// Then we are on a one way journey to a stack overflow.
|
// Then we are on a one way journey to a stack overflow.
|
||||||
if (FFlag::LuauAutocompleteUnionCopyPreviousSeen)
|
|
||||||
{
|
|
||||||
for (auto ty : seen)
|
for (auto ty : seen)
|
||||||
{
|
{
|
||||||
if (is<UnionType, IntersectionType>(ty))
|
if (is<UnionType, IntersectionType>(ty))
|
||||||
innerSeen.insert(ty);
|
innerSeen.insert(ty);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isNil(*iter))
|
if (isNil(*iter))
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,9 +30,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||||
|
|
||||||
|
@ -292,8 +291,6 @@ void assignPropDocumentationSymbols(TableType::Props& props, const std::string&
|
||||||
|
|
||||||
static void finalizeGlobalBindings(ScopePtr scope)
|
static void finalizeGlobalBindings(ScopePtr scope)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauUserTypeFunTypecheck);
|
|
||||||
|
|
||||||
for (const auto& pair : scope->bindings)
|
for (const auto& pair : scope->bindings)
|
||||||
{
|
{
|
||||||
persist(pair.second.typeId);
|
persist(pair.second.typeId);
|
||||||
|
@ -313,8 +310,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
|
|
||||||
TypeArena& arena = globals.globalTypes;
|
TypeArena& arena = globals.globalTypes;
|
||||||
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
||||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization2
|
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization3
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
globalScope = globals.globalScope.get();
|
globalScope = globals.globalScope.get();
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -420,23 +417,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
finalizeGlobalBindings(globals.globalScope);
|
finalizeGlobalBindings(globals.globalScope);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto& pair : globals.globalScope->bindings)
|
|
||||||
{
|
|
||||||
persist(pair.second.typeId);
|
|
||||||
|
|
||||||
if (TableType* ttv = getMutable<TableType>(pair.second.typeId))
|
|
||||||
{
|
|
||||||
if (!ttv->name)
|
|
||||||
ttv->name = "typeof(" + toString(pair.first) + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attachMagicFunction(getGlobalBinding(globals, "assert"), std::make_shared<MagicAssert>());
|
attachMagicFunction(getGlobalBinding(globals, "assert"), std::make_shared<MagicAssert>());
|
||||||
|
|
||||||
|
@ -500,8 +481,6 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
attachTag(requireTy, kRequireTagName);
|
attachTag(requireTy, kRequireTagName);
|
||||||
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
|
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
// Global scope cannot be the parent of the type checking environment because it can be changed by the embedder
|
// Global scope cannot be the parent of the type checking environment because it can be changed by the embedder
|
||||||
globals.globalTypeFunctionScope->exportedTypeBindings = globals.globalScope->exportedTypeBindings;
|
globals.globalTypeFunctionScope->exportedTypeBindings = globals.globalScope->exportedTypeBindings;
|
||||||
globals.globalTypeFunctionScope->builtinTypeNames = globals.globalScope->builtinTypeNames;
|
globals.globalTypeFunctionScope->builtinTypeNames = globals.globalScope->builtinTypeNames;
|
||||||
|
@ -552,7 +531,6 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
|
|
||||||
finalizeGlobalBindings(globals.globalTypeFunctionScope);
|
finalizeGlobalBindings(globals.globalTypeFunctionScope);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -143,10 +143,6 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
rci.traverse(ty);
|
rci.traverse(ty);
|
||||||
// `UnpackConstraint` should not mutate `sourcePack`.
|
// `UnpackConstraint` should not mutate `sourcePack`.
|
||||||
}
|
}
|
||||||
else if (auto rpc = get<ReduceConstraint>(*this); FFlag::DebugLuauGreedyGeneralization && rpc)
|
|
||||||
{
|
|
||||||
rci.traverse(rpc->ty);
|
|
||||||
}
|
|
||||||
else if (auto rpc = get<ReducePackConstraint>(*this))
|
else if (auto rpc = get<ReducePackConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(rpc->tp);
|
rci.traverse(rpc->tp);
|
||||||
|
|
|
@ -33,25 +33,21 @@
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
|
||||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
|
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine)
|
||||||
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -255,18 +251,15 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
rootScope->location = block->location;
|
rootScope->location = block->location;
|
||||||
module->astScopes[block] = NotNull{scope.get()};
|
module->astScopes[block] = NotNull{scope.get()};
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
DEPRECATED_interiorTypes.emplace_back();
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
// Create module-local scope for the type function environment
|
// Create module-local scope for the type function environment
|
||||||
ScopePtr localTypeFunctionScope = std::make_shared<Scope>(typeFunctionScope);
|
ScopePtr localTypeFunctionScope = std::make_shared<Scope>(typeFunctionScope);
|
||||||
localTypeFunctionScope->location = block->location;
|
localTypeFunctionScope->location = block->location;
|
||||||
typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
||||||
}
|
|
||||||
|
|
||||||
rootScope->returnType = freshTypePack(scope, Polarity::Positive);
|
rootScope->returnType = freshTypePack(scope, Polarity::Positive);
|
||||||
TypeId moduleFnTy = arena->addType(FunctionType{TypeLevel{}, builtinTypes->anyTypePack, rootScope->returnType});
|
TypeId moduleFnTy = arena->addType(FunctionType{TypeLevel{}, builtinTypes->anyTypePack, rootScope->returnType});
|
||||||
|
@ -294,7 +287,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
|
@ -313,7 +306,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -338,6 +331,12 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
LUAU_ASSERT(get<BlockedType>(ty));
|
LUAU_ASSERT(get<BlockedType>(ty));
|
||||||
asMutable(ty)->ty.emplace<BoundType>(domainTy);
|
asMutable(ty)->ty.emplace<BoundType>(domainTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplifyOutOfLine)
|
||||||
|
{
|
||||||
|
for (TypeId ty : unionsToSimplify)
|
||||||
|
addConstraint(scope, block->location, SimplifyConstraint{ty});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block)
|
void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block)
|
||||||
|
@ -345,13 +344,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
||||||
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
||||||
// Pre
|
// Pre
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
DEPRECATED_interiorTypes.emplace_back();
|
||||||
visitBlockWithoutChildScope(resumeScope, block);
|
visitBlockWithoutChildScope(resumeScope, block);
|
||||||
// Post
|
// Post
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -381,7 +380,7 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
|
|
||||||
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
||||||
interiorFreeTypes.back().types.push_back(ft);
|
interiorFreeTypes.back().types.push_back(ft);
|
||||||
|
@ -403,7 +402,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
|
||||||
{
|
{
|
||||||
FreeTypePack f{scope.get(), polarity};
|
FreeTypePack f{scope.get(), polarity};
|
||||||
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.back().typePacks.push_back(result);
|
interiorFreeTypes.back().typePacks.push_back(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -829,7 +828,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
}
|
}
|
||||||
else if (auto function = stat->as<AstStatTypeFunction>())
|
else if (auto function = stat->as<AstStatTypeFunction>())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
hasTypeFunction = true;
|
hasTypeFunction = true;
|
||||||
|
|
||||||
// If a type function w/ same name has already been defined, error for having duplicates
|
// If a type function w/ same name has already been defined, error for having duplicates
|
||||||
|
@ -841,11 +839,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable becomes unused with the removal of FFlag::LuauUserTypeFunTypecheck
|
|
||||||
ScopePtr defnScope = FFlag::LuauUserTypeFunTypecheck ? nullptr : childScope(function, scope);
|
|
||||||
|
|
||||||
// Create TypeFunctionInstanceType
|
// Create TypeFunctionInstanceType
|
||||||
|
|
||||||
std::vector<TypeId> typeParams;
|
std::vector<TypeId> typeParams;
|
||||||
typeParams.reserve(function->body->args.size);
|
typeParams.reserve(function->body->args.size);
|
||||||
|
|
||||||
|
@ -914,21 +908,18 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck && hasTypeFunction)
|
if (hasTypeFunction)
|
||||||
typeFunctionEnvScope = std::make_shared<Scope>(typeFunctionRuntime->rootScope);
|
typeFunctionEnvScope = std::make_shared<Scope>(typeFunctionRuntime->rootScope);
|
||||||
|
|
||||||
// Additional pass for user-defined type functions to fill in their environments completely
|
// Additional pass for user-defined type functions to fill in their environments completely
|
||||||
for (AstStat* stat : block->body)
|
for (AstStat* stat : block->body)
|
||||||
{
|
{
|
||||||
if (auto function = stat->as<AstStatTypeFunction>())
|
if (auto function = stat->as<AstStatTypeFunction>())
|
||||||
{
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
{
|
||||||
// Similar to global pre-population, create a binding for each type function in the scope upfront
|
// Similar to global pre-population, create a binding for each type function in the scope upfront
|
||||||
TypeId bt = arena->addType(BlockedType{});
|
TypeId bt = arena->addType(BlockedType{});
|
||||||
typeFunctionEnvScope->bindings[function->name] = Binding{bt, function->location};
|
typeFunctionEnvScope->bindings[function->name] = Binding{bt, function->location};
|
||||||
astTypeFunctionEnvironmentScopes[function] = typeFunctionEnvScope;
|
astTypeFunctionEnvironmentScopes[function] = typeFunctionEnvScope;
|
||||||
}
|
|
||||||
|
|
||||||
// Find the type function we have already created
|
// Find the type function we have already created
|
||||||
TypeFunctionInstanceType* mainTypeFun = nullptr;
|
TypeFunctionInstanceType* mainTypeFun = nullptr;
|
||||||
|
@ -948,8 +939,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||||
size_t level = 0;
|
size_t level = 0;
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
auto addToEnvironment = [this](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeId type, size_t level)
|
auto addToEnvironment = [this](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeId type, size_t level)
|
||||||
{
|
{
|
||||||
if (userFuncData.environment.find(name))
|
if (userFuncData.environment.find(name))
|
||||||
|
@ -962,8 +951,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
if (auto it = astTypeFunctionEnvironmentScopes.find(ty->userFuncData.definition))
|
if (auto it = astTypeFunctionEnvironmentScopes.find(ty->userFuncData.definition))
|
||||||
{
|
{
|
||||||
if (auto existing = (*it)->linearSearchForBinding(name, /* traverseScopeChain */ false))
|
if (auto existing = (*it)->linearSearchForBinding(name, /* traverseScopeChain */ false))
|
||||||
scope->bindings[ty->userFuncData.definition->name] =
|
scope->bindings[ty->userFuncData.definition->name] = Binding{existing->typeId, ty->userFuncData.definition->location};
|
||||||
Binding{existing->typeId, ty->userFuncData.definition->location};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -979,32 +967,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
level++;
|
level++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
|
||||||
{
|
|
||||||
for (auto& [name, tf] : curr->privateTypeBindings)
|
|
||||||
{
|
|
||||||
if (userFuncData.environment.find(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
|
||||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& [name, tf] : curr->exportedTypeBindings)
|
|
||||||
{
|
|
||||||
if (userFuncData.environment.find(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
|
||||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1289,6 +1251,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||||
|
|
||||||
visit(forScope, for_->body);
|
visit(forScope, for_->body);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
scope->inheritAssignments(forScope);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,6 +1315,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
|
||||||
visit(loopScope, forIn->body);
|
visit(loopScope, forIn->body);
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
scope->inheritAssignments(loopScope);
|
||||||
|
|
||||||
// This iter constraint must dispatch first.
|
// This iter constraint must dispatch first.
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
start,
|
start,
|
||||||
|
@ -1373,6 +1341,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatWhile* whil
|
||||||
|
|
||||||
visit(whileScope, while_->body);
|
visit(whileScope, while_->body);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
scope->inheritAssignments(whileScope);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1384,6 +1355,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatRepeat* rep
|
||||||
|
|
||||||
check(repeatScope, repeat->condition);
|
check(repeatScope, repeat->condition);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
scope->inheritAssignments(repeatScope);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1483,8 +1457,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
|
|
||||||
DefId def = dfg->getDef(function->name);
|
DefId def = dfg->getDef(function->name);
|
||||||
|
|
||||||
if (FFlag::LuauUngeneralizedTypesForRecursiveFunctions)
|
|
||||||
{
|
|
||||||
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
||||||
{
|
{
|
||||||
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
||||||
|
@ -1501,7 +1473,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
{
|
{
|
||||||
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
@ -1781,17 +1752,11 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeAlias*
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunction* function)
|
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunction* function)
|
||||||
{
|
|
||||||
if (!FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
return ControlFlow::None;
|
|
||||||
|
|
||||||
if (FFlag::LuauNoTypeFunctionsNamedTypeOf)
|
|
||||||
{
|
{
|
||||||
if (function->name == "typeof")
|
if (function->name == "typeof")
|
||||||
{
|
{
|
||||||
reportError(function->location, ReservedIdentifier{"typeof"});
|
reportError(function->location, ReservedIdentifier{"typeof"});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
||||||
LUAU_ASSERT(scopePtr);
|
LUAU_ASSERT(scopePtr);
|
||||||
|
@ -1802,7 +1767,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
// Place this function as a child of the non-type function scope
|
// Place this function as a child of the non-type function scope
|
||||||
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
|
@ -1820,7 +1785,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
|
@ -1829,7 +1794,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -2229,7 +2194,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
|
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
|
||||||
{
|
{
|
||||||
std::optional<TypeId> expectedType = std::nullopt;
|
std::optional<TypeId> expectedType = std::nullopt;
|
||||||
if (FFlag::LuauPropagateExpectedTypesForCalls && i < expectedTypesForCall.size())
|
if (i < expectedTypesForCall.size())
|
||||||
{
|
{
|
||||||
expectedType = expectedTypesForCall[i];
|
expectedType = expectedTypesForCall[i];
|
||||||
}
|
}
|
||||||
|
@ -2240,7 +2205,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<std::optional<Luau::TypeId>> expectedTypes = {};
|
std::vector<std::optional<Luau::TypeId>> expectedTypes = {};
|
||||||
if (FFlag::LuauPropagateExpectedTypesForCalls && i < expectedTypesForCall.size())
|
if (i < expectedTypesForCall.size())
|
||||||
{
|
{
|
||||||
expectedTypes.insert(expectedTypes.end(), expectedTypesForCall.begin() + int(i), expectedTypesForCall.end());
|
expectedTypes.insert(expectedTypes.end(), expectedTypesForCall.begin() + int(i), expectedTypesForCall.end());
|
||||||
}
|
}
|
||||||
|
@ -2423,7 +2388,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||||
// a[b] += c
|
// a[b] += c
|
||||||
//
|
//
|
||||||
// We only solve _one_ set of constraints for `b`.
|
// We only solve _one_ set of constraints for `b`.
|
||||||
if (FFlag::LuauCacheInferencePerAstExpr && inferredExprCache.contains(expr))
|
if (inferredExprCache.contains(expr))
|
||||||
return inferredExprCache[expr];
|
return inferredExprCache[expr];
|
||||||
|
|
||||||
Inference result;
|
Inference result;
|
||||||
|
@ -2478,7 +2443,6 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||||
result = Inference{freshType(scope)};
|
result = Inference{freshType(scope)};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauCacheInferencePerAstExpr)
|
|
||||||
inferredExprCache[expr] = result;
|
inferredExprCache[expr] = result;
|
||||||
|
|
||||||
LUAU_ASSERT(result.ty);
|
LUAU_ASSERT(result.ty);
|
||||||
|
@ -2494,7 +2458,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
||||||
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
TypeId freeTy = nullptr;
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2521,7 +2485,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
return Inference{singletonType};
|
return Inference{singletonType};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
TypeId freeTy = nullptr;
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2682,7 +2646,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
Checkpoint startCheckpoint = checkpoint(this);
|
Checkpoint startCheckpoint = checkpoint(this);
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
|
@ -2700,7 +2664,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
|
@ -2840,11 +2804,17 @@ Inference ConstraintGenerator::checkAstExprBinary(
|
||||||
}
|
}
|
||||||
case AstExprBinary::Op::CompareLt:
|
case AstExprBinary::Op::CompareLt:
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
addConstraint(scope, location, EqualityConstraint{leftType, rightType});
|
||||||
|
|
||||||
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, location);
|
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, location);
|
||||||
return Inference{resultType, std::move(refinement)};
|
return Inference{resultType, std::move(refinement)};
|
||||||
}
|
}
|
||||||
case AstExprBinary::Op::CompareGe:
|
case AstExprBinary::Op::CompareGe:
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
addConstraint(scope, location, EqualityConstraint{leftType, rightType});
|
||||||
|
|
||||||
TypeId resultType = createTypeFunctionInstance(
|
TypeId resultType = createTypeFunctionInstance(
|
||||||
builtinTypeFunctions().ltFunc,
|
builtinTypeFunctions().ltFunc,
|
||||||
{rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)`
|
{rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)`
|
||||||
|
@ -2856,11 +2826,17 @@ Inference ConstraintGenerator::checkAstExprBinary(
|
||||||
}
|
}
|
||||||
case AstExprBinary::Op::CompareLe:
|
case AstExprBinary::Op::CompareLe:
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
addConstraint(scope, location, EqualityConstraint{leftType, rightType});
|
||||||
|
|
||||||
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, location);
|
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, location);
|
||||||
return Inference{resultType, std::move(refinement)};
|
return Inference{resultType, std::move(refinement)};
|
||||||
}
|
}
|
||||||
case AstExprBinary::Op::CompareGt:
|
case AstExprBinary::Op::CompareGt:
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
addConstraint(scope, location, EqualityConstraint{leftType, rightType});
|
||||||
|
|
||||||
TypeId resultType = createTypeFunctionInstance(
|
TypeId resultType = createTypeFunctionInstance(
|
||||||
builtinTypeFunctions().leFunc,
|
builtinTypeFunctions().leFunc,
|
||||||
{rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)`
|
{rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)`
|
||||||
|
@ -3192,7 +3168,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
ttv->definitionLocation = expr->location;
|
ttv->definitionLocation = expr->location;
|
||||||
ttv->scope = scope.get();
|
ttv->scope = scope.get();
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
interiorFreeTypes.back().types.push_back(ty);
|
interiorFreeTypes.back().types.push_back(ty);
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.back().push_back(ty);
|
DEPRECATED_interiorTypes.back().push_back(ty);
|
||||||
|
@ -3245,6 +3221,35 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!indexValueLowerBound.empty());
|
LUAU_ASSERT(!indexValueLowerBound.empty());
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplifyOutOfLine)
|
||||||
|
{
|
||||||
|
TypeId indexKey = nullptr;
|
||||||
|
TypeId indexValue = nullptr;
|
||||||
|
|
||||||
|
if (indexKeyLowerBound.size() == 1)
|
||||||
|
{
|
||||||
|
indexKey = *indexKeyLowerBound.begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indexKey = arena->addType(UnionType{std::vector(indexKeyLowerBound.begin(), indexKeyLowerBound.end())});
|
||||||
|
unionsToSimplify.push_back(indexKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexValueLowerBound.size() == 1)
|
||||||
|
{
|
||||||
|
indexValue = *indexValueLowerBound.begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indexValue = arena->addType(UnionType{std::vector(indexValueLowerBound.begin(), indexValueLowerBound.end())});
|
||||||
|
unionsToSimplify.push_back(indexValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ttv->indexer = TableIndexer{indexKey, indexValue};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
TypeId indexKey = indexKeyLowerBound.size() == 1
|
TypeId indexKey = indexKeyLowerBound.size() == 1
|
||||||
? *indexKeyLowerBound.begin()
|
? *indexKeyLowerBound.begin()
|
||||||
: arena->addType(UnionType{std::vector(indexKeyLowerBound.begin(), indexKeyLowerBound.end())});
|
: arena->addType(UnionType{std::vector(indexKeyLowerBound.begin(), indexKeyLowerBound.end())});
|
||||||
|
@ -3252,11 +3257,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
TypeId indexValue = indexValueLowerBound.size() == 1
|
TypeId indexValue = indexValueLowerBound.size() == 1
|
||||||
? *indexValueLowerBound.begin()
|
? *indexValueLowerBound.begin()
|
||||||
: arena->addType(UnionType{std::vector(indexValueLowerBound.begin(), indexValueLowerBound.end())});
|
: arena->addType(UnionType{std::vector(indexValueLowerBound.begin(), indexValueLowerBound.end())});
|
||||||
|
|
||||||
ttv->indexer = TableIndexer{indexKey, indexValue};
|
ttv->indexer = TableIndexer{indexKey, indexValue};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (expectedType)
|
if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck)
|
||||||
{
|
{
|
||||||
addConstraint(
|
addConstraint(
|
||||||
scope,
|
scope,
|
||||||
|
@ -3769,14 +3774,9 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
}
|
}
|
||||||
else if (ty->is<AstTypeOptional>())
|
else if (ty->is<AstTypeOptional>())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
result = builtinTypes->nilType;
|
result = builtinTypes->nilType;
|
||||||
else
|
|
||||||
return builtinTypes->nilType;
|
|
||||||
}
|
}
|
||||||
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
||||||
{
|
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
{
|
{
|
||||||
if (unionAnnotation->types.size == 1)
|
if (unionAnnotation->types.size == 1)
|
||||||
result = resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
result = resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||||
|
@ -3791,22 +3791,7 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
result = arena->addType(UnionType{parts});
|
result = arena->addType(UnionType{parts});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (unionAnnotation->types.size == 1)
|
|
||||||
return resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
|
||||||
std::vector<TypeId> parts;
|
|
||||||
for (AstType* part : unionAnnotation->types)
|
|
||||||
{
|
|
||||||
parts.push_back(resolveType(scope, part, inTypeArguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = arena->addType(UnionType{parts});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
||||||
{
|
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
{
|
{
|
||||||
if (intersectionAnnotation->types.size == 1)
|
if (intersectionAnnotation->types.size == 1)
|
||||||
result = resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
result = resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||||
|
@ -3821,19 +3806,6 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
result = arena->addType(IntersectionType{parts});
|
result = arena->addType(IntersectionType{parts});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (intersectionAnnotation->types.size == 1)
|
|
||||||
return resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
|
||||||
std::vector<TypeId> parts;
|
|
||||||
for (AstType* part : intersectionAnnotation->types)
|
|
||||||
{
|
|
||||||
parts.push_back(resolveType(scope, part, inTypeArguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = arena->addType(IntersectionType{parts});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
|
else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
|
||||||
{
|
{
|
||||||
result = resolveType_(scope, typeGroupAnnotation->type, inTypeArguments);
|
result = resolveType_(scope, typeGroupAnnotation->type, inTypeArguments);
|
||||||
|
@ -4036,10 +4008,27 @@ TypeId ConstraintGenerator::makeUnion(const ScopePtr& scope, Location location,
|
||||||
if (get<NeverType>(follow(rhs)))
|
if (get<NeverType>(follow(rhs)))
|
||||||
return lhs;
|
return lhs;
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplifyOutOfLine)
|
||||||
|
{
|
||||||
|
TypeId result = simplifyUnion(scope, location, lhs, rhs);
|
||||||
|
if (is<UnionType>(follow(result)))
|
||||||
|
unionsToSimplify.push_back(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, {lhs, rhs}, {}, scope, location);
|
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, {lhs, rhs}, {}, scope, location);
|
||||||
|
|
||||||
return resultType;
|
return resultType;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeId ConstraintGenerator::makeUnion(std::vector<TypeId> options)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauSimplifyOutOfLine);
|
||||||
|
TypeId result = arena->addType(UnionType{std::move(options)});
|
||||||
|
unionsToSimplify.push_back(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs)
|
TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs)
|
||||||
{
|
{
|
||||||
|
@ -4183,13 +4172,10 @@ void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
// Handle type function globals as well, without preparing a module scope since they have a separate environment
|
// Handle type function globals as well, without preparing a module scope since they have a separate environment
|
||||||
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
|
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
|
||||||
program->visit(&tfgp);
|
program->visit(&tfgp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ConstraintGenerator::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
void ConstraintGenerator::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
||||||
{
|
{
|
||||||
|
@ -4200,13 +4186,10 @@ void ConstraintGenerator::prepopulateGlobalScope(const ScopePtr& globalScope, As
|
||||||
|
|
||||||
program->visit(&gp);
|
program->visit(&gp);
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
// Handle type function globals as well, without preparing a module scope since they have a separate environment
|
// Handle type function globals as well, without preparing a module scope since they have a separate environment
|
||||||
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
|
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
|
||||||
program->visit(&tfgp);
|
program->visit(&tfgp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool ConstraintGenerator::recordPropertyAssignment(TypeId ty)
|
bool ConstraintGenerator::recordPropertyAssignment(TypeId ty)
|
||||||
{
|
{
|
||||||
|
@ -4265,10 +4248,17 @@ void ConstraintGenerator::fillInInferredBindings(const ScopePtr& globalScope, As
|
||||||
scope->bindings[symbol] = Binding{tys.front(), location};
|
scope->bindings[symbol] = Binding{tys.front(), location};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId ty = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, std::move(tys), {}, globalScope, location);
|
if (FFlag::LuauSimplifyOutOfLine)
|
||||||
|
{
|
||||||
|
TypeId ty = makeUnion(std::move(tys));
|
||||||
scope->bindings[symbol] = Binding{ty, location};
|
scope->bindings[symbol] = Binding{ty, location};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId ty = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, std::move(tys), {}, globalScope, location);
|
||||||
|
scope->bindings[symbol] = Binding{ty, location};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4304,10 +4294,15 @@ std::vector<std::optional<TypeId>> ConstraintGenerator::getExpectedCallTypesForF
|
||||||
el = builtinTypes->neverType;
|
el = builtinTypes->neverType;
|
||||||
else if (result.size() == 1)
|
else if (result.size() == 1)
|
||||||
el = result[0];
|
el = result[0];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (FFlag::LuauSimplifyOutOfLine)
|
||||||
|
el = makeUnion(std::move(result));
|
||||||
else
|
else
|
||||||
el = module->internalTypes.addType(UnionType{std::move(result)});
|
el = module->internalTypes.addType(UnionType{std::move(result)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const TypeId overload : funTys)
|
for (const TypeId overload : funTys)
|
||||||
|
|
|
@ -35,11 +35,14 @@ LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion)
|
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||||
|
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -686,6 +689,13 @@ void ConstraintSolver::initFreeTypeTracking()
|
||||||
block(dep, c);
|
block(dep, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also check flag integrity while we're here
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations);
|
||||||
|
LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::generalizeOneType(TypeId ty)
|
void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
|
@ -729,7 +739,7 @@ void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, Typ
|
||||||
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
|
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
|
||||||
); // FIXME? Is this the right polarity?
|
); // FIXME? Is this the right polarity?
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
trackInteriorFreeType(constraint->scope, ty);
|
trackInteriorFreeType(constraint->scope, ty);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -819,6 +829,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
||||||
success = tryDispatch(*rpc, constraint, force);
|
success = tryDispatch(*rpc, constraint, force);
|
||||||
else if (auto eqc = get<EqualityConstraint>(*constraint))
|
else if (auto eqc = get<EqualityConstraint>(*constraint))
|
||||||
success = tryDispatch(*eqc, constraint);
|
success = tryDispatch(*eqc, constraint);
|
||||||
|
else if (auto sc = get<SimplifyConstraint>(*constraint))
|
||||||
|
success = tryDispatch(*sc, constraint);
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
|
|
||||||
|
@ -888,7 +900,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
{
|
{
|
||||||
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
if (auto freeTy = get<FreeType>(ty))
|
if (auto freeTy = get<FreeType>(ty))
|
||||||
|
@ -910,7 +922,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
if (constraint->scope->interiorFreeTypePacks)
|
if (constraint->scope->interiorFreeTypePacks)
|
||||||
{
|
{
|
||||||
|
@ -1519,7 +1531,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
for (TypeId freeTy : u2.newFreshTypes)
|
for (TypeId freeTy : u2.newFreshTypes)
|
||||||
trackInteriorFreeType(constraint->scope, freeTy);
|
trackInteriorFreeType(constraint->scope, freeTy);
|
||||||
|
@ -1540,6 +1552,51 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
if (c.result != result)
|
if (c.result != result)
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(c.result), result);
|
emplaceTypePack<BoundTypePack>(asMutable(c.result), result);
|
||||||
|
|
||||||
|
if (FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
{
|
||||||
|
FunctionType* inferredFuncTy = getMutable<FunctionType>(inferredTy);
|
||||||
|
LUAU_ASSERT(inferredFuncTy);
|
||||||
|
|
||||||
|
// Strip variadic anys from the argTypes of any functionType arguments
|
||||||
|
const auto [argsHead, argsTail] = flatten(inferredFuncTy->argTypes);
|
||||||
|
TypePack clippedArgs = {{}, argsTail};
|
||||||
|
bool clippedAny = false;
|
||||||
|
|
||||||
|
for (TypeId t : argsHead)
|
||||||
|
{
|
||||||
|
const FunctionType* f = get<FunctionType>(follow(t));
|
||||||
|
if (!f || !f->argTypes)
|
||||||
|
{
|
||||||
|
clippedArgs.head.push_back(t);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypePack* argTp = get<TypePack>(follow(f->argTypes));
|
||||||
|
if (!argTp || !argTp->tail)
|
||||||
|
{
|
||||||
|
clippedArgs.head.push_back(t);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const VariadicTypePack* argTpTail = get<VariadicTypePack>(follow(argTp->tail));
|
||||||
|
argTpTail && argTpTail->hidden && argTpTail->ty == builtinTypes->anyType)
|
||||||
|
{
|
||||||
|
const TypePackId anyLessArgTp = arena->addTypePack(TypePack{argTp->head});
|
||||||
|
// Mint a new FunctionType in case the original came from another module
|
||||||
|
const TypeId newFuncTypeId = arena->addType(FunctionType{anyLessArgTp, f->retTypes});
|
||||||
|
FunctionType* newFunc = getMutable<FunctionType>(newFuncTypeId);
|
||||||
|
newFunc->argNames = f->argNames;
|
||||||
|
clippedArgs.head.push_back(newFuncTypeId);
|
||||||
|
clippedAny = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
clippedArgs.head.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clippedAny)
|
||||||
|
inferredFuncTy->argTypes = arena->addTypePack(std::move(clippedArgs));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
|
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
|
||||||
|
@ -1683,12 +1740,32 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
|
|
||||||
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
||||||
|
|
||||||
|
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
||||||
// Generic types are skipped over entirely, for now.
|
// Generic types are skipped over entirely, for now.
|
||||||
if (containsGenerics.hasGeneric(expectedArgTy))
|
if (containsGenerics.hasGeneric(expectedArgTy))
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2 || !lambdaTy || !lambdaTy->argTypes)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
const TypePack* argTp = get<TypePack>(follow(lambdaTy->argTypes));
|
||||||
|
if (!argTp || !argTp->tail)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (const VariadicTypePack* argTpTail = get<VariadicTypePack>(follow(argTp->tail));
|
||||||
|
argTpTail && argTpTail->hidden && argTpTail->ty == builtinTypes->anyType)
|
||||||
|
{
|
||||||
|
// Strip variadic any
|
||||||
|
const TypePackId anyLessArgTp = arena->addTypePack(TypePack{argTp->head});
|
||||||
|
const TypeId newFuncTypeId = arena->addType(FunctionType{anyLessArgTp, lambdaTy->retTypes});
|
||||||
|
FunctionType* newFunc = getMutable<FunctionType>(newFuncTypeId);
|
||||||
|
newFunc->argNames = lambdaTy->argNames;
|
||||||
|
(*c.astTypes)[expr] = newFuncTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
||||||
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
|
||||||
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
||||||
|
|
||||||
if (expectedLambdaTy && lambdaTy && lambdaExpr)
|
if (expectedLambdaTy && lambdaTy && lambdaExpr)
|
||||||
|
@ -1899,7 +1976,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
|
|
||||||
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
||||||
emplace<FreeType>(constraint, resultType, freeResult);
|
emplace<FreeType>(constraint, resultType, freeResult);
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
trackInteriorFreeType(constraint->scope, resultType);
|
trackInteriorFreeType(constraint->scope, resultType);
|
||||||
|
|
||||||
tt->indexer = TableIndexer{indexType, resultType};
|
tt->indexer = TableIndexer{indexType, resultType};
|
||||||
|
@ -1980,8 +2057,9 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
r = follow(r);
|
r = follow(r);
|
||||||
if (!get<ErrorType>(r))
|
if (FFlag::LuauInsertErrorTypesIntoIndexerResult || !get<ErrorType>(r))
|
||||||
results.insert(r);
|
results.insert(r);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == results.size())
|
if (0 == results.size())
|
||||||
|
@ -2443,18 +2521,15 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
|
||||||
for (TypePackId r : result.reducedPacks)
|
for (TypePackId r : result.reducedPacks)
|
||||||
unblock(r, constraint->location);
|
unblock(r, constraint->location);
|
||||||
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
|
||||||
for (TypeId ity : result.irreducibleTypes)
|
for (TypeId ity : result.irreducibleTypes)
|
||||||
uninhabitedTypeFunctions.insert(ity);
|
uninhabitedTypeFunctions.insert(ity);
|
||||||
}
|
|
||||||
|
|
||||||
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
|
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
// If we couldn't reduce this type function, stick it in the set!
|
// If we couldn't reduce this type function, stick it in the set!
|
||||||
if (get<TypeFunctionInstanceType>(ty) && (!FFlag::LuauNewTypeFunReductionChecks2 || !result.irreducibleTypes.find(ty)))
|
if (get<TypeFunctionInstanceType>(ty) && !result.irreducibleTypes.find(ty))
|
||||||
typeFunctionsToFinalize[ty] = constraint;
|
typeFunctionsToFinalize[ty] = constraint;
|
||||||
|
|
||||||
if (force || reductionFinished)
|
if (force || reductionFinished)
|
||||||
|
@ -2531,6 +2606,83 @@ bool ConstraintSolver::tryDispatch(const EqualityConstraint& c, NotNull<const Co
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FindAllUnionMembers : TypeOnceVisitor
|
||||||
|
{
|
||||||
|
DenseHashSet<TypeId> recordedTys{nullptr};
|
||||||
|
DenseHashSet<TypeId> blockedTys{nullptr};
|
||||||
|
|
||||||
|
FindAllUnionMembers()
|
||||||
|
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty) override
|
||||||
|
{
|
||||||
|
recordedTys.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const BlockedType&) override
|
||||||
|
{
|
||||||
|
blockedTys.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(TypeId ty, const PendingExpansionType&) override
|
||||||
|
{
|
||||||
|
blockedTys.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(TypeId ty, const FreeType&) override
|
||||||
|
{
|
||||||
|
blockedTys.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
||||||
|
{
|
||||||
|
blockedTys.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId, const UnionType&) override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ConstraintSolver::tryDispatch(const SimplifyConstraint& c, NotNull<const Constraint> constraint)
|
||||||
|
{
|
||||||
|
TypeId target = follow(c.ty);
|
||||||
|
|
||||||
|
if (target->persistent || target->owningArena != arena || !is<UnionType>(target))
|
||||||
|
{
|
||||||
|
// If our target ends up being:
|
||||||
|
// - A persistent union like `false?`
|
||||||
|
// - A union from another arena
|
||||||
|
// - Something other than a union type
|
||||||
|
// Then it's either harmful or useless to fire this constraint, so we exit early.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FindAllUnionMembers finder;
|
||||||
|
finder.traverse(target);
|
||||||
|
if (!finder.blockedTys.empty())
|
||||||
|
{
|
||||||
|
for (TypeId ty : finder.blockedTys)
|
||||||
|
block(ty, constraint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TypeId result = builtinTypes->neverType;
|
||||||
|
for (TypeId ty : finder.recordedTys)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
if (ty == target)
|
||||||
|
continue;
|
||||||
|
result = simplifyUnion(constraint->scope, constraint->location, result, ty);
|
||||||
|
}
|
||||||
|
emplaceType<BoundType>(asMutable(target), result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
|
bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
|
||||||
{
|
{
|
||||||
iteratorTy = follow(iteratorTy);
|
iteratorTy = follow(iteratorTy);
|
||||||
|
|
|
@ -14,11 +14,13 @@
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDfgMatchCGScopes)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -39,8 +41,6 @@ struct PushScope
|
||||||
}
|
}
|
||||||
|
|
||||||
~PushScope()
|
~PushScope()
|
||||||
{
|
|
||||||
if (FFlag::LuauDfgScopeStackTrueReset)
|
|
||||||
{
|
{
|
||||||
// If somehow this stack has _shrunk_ to be smaller than we expect,
|
// If somehow this stack has _shrunk_ to be smaller than we expect,
|
||||||
// something very strange has happened.
|
// something very strange has happened.
|
||||||
|
@ -48,11 +48,6 @@ struct PushScope
|
||||||
while (stack.size() > previousSize)
|
while (stack.size() > previousSize)
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
stack.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const RefinementKey* RefinementKeyArena::leaf(DefId def)
|
const RefinementKey* RefinementKeyArena::leaf(DefId def)
|
||||||
|
@ -157,6 +152,9 @@ void DfgScope::inherit(const DfgScope* childScope)
|
||||||
|
|
||||||
bool DfgScope::canUpdateDefinition(Symbol symbol) const
|
bool DfgScope::canUpdateDefinition(Symbol symbol) const
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
return true;
|
||||||
|
|
||||||
for (const DfgScope* current = this; current; current = current->parent)
|
for (const DfgScope* current = this; current; current = current->parent)
|
||||||
{
|
{
|
||||||
if (current->bindings.find(symbol))
|
if (current->bindings.find(symbol))
|
||||||
|
@ -170,6 +168,9 @@ bool DfgScope::canUpdateDefinition(Symbol symbol) const
|
||||||
|
|
||||||
bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
|
bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
return true;
|
||||||
|
|
||||||
for (const DfgScope* current = this; current; current = current->parent)
|
for (const DfgScope* current = this; current; current = current->parent)
|
||||||
{
|
{
|
||||||
if (auto props = current->props.find(def))
|
if (auto props = current->props.find(def))
|
||||||
|
@ -533,9 +534,40 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
|
||||||
{
|
{
|
||||||
// TODO(controlflow): entry point has a back edge from exit point
|
// FIXME: This is unsound, as it does not consider the _second_ loop
|
||||||
|
// iteration. Consider something like:
|
||||||
|
//
|
||||||
|
// local function f(_: number) end
|
||||||
|
// local x = 42
|
||||||
|
// while math.random () > 0.5 do
|
||||||
|
// f(x)
|
||||||
|
// x = ""
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// While the first iteration is fine, the second iteration would
|
||||||
|
// allow a string to flow into a position that expects.
|
||||||
DfgScope* whileScope = makeChildScope(DfgScope::Loop);
|
DfgScope* whileScope = makeChildScope(DfgScope::Loop);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
{
|
||||||
|
|
||||||
|
ControlFlow cf;
|
||||||
|
{
|
||||||
|
PushScope ps{scopeStack, whileScope};
|
||||||
|
visitExpr(w->condition);
|
||||||
|
cf = visit(w->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scope = currentScope();
|
||||||
|
// If the inner loop unconditioanlly returns or throws we shouldn't
|
||||||
|
// consume any type state from the loop body.
|
||||||
|
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
|
||||||
|
join(scope, scope, whileScope);
|
||||||
|
|
||||||
|
return ControlFlow::None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
{
|
{
|
||||||
PushScope ps{scopeStack, whileScope};
|
PushScope ps{scopeStack, whileScope};
|
||||||
visitExpr(w->condition);
|
visitExpr(w->condition);
|
||||||
|
@ -549,12 +581,36 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
|
||||||
{
|
{
|
||||||
// TODO(controlflow): entry point has a back edge from exit point
|
// See comment in visit(AstStatWhile*): this is unsound as it
|
||||||
|
// does not consider the _second_ loop iteration.
|
||||||
DfgScope* repeatScope = makeChildScope(DfgScope::Loop);
|
DfgScope* repeatScope = makeChildScope(DfgScope::Loop);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
{
|
||||||
|
ControlFlow cf;
|
||||||
|
|
||||||
|
{
|
||||||
|
PushScope ps{scopeStack, repeatScope};
|
||||||
|
cf = visitBlockWithoutChildScope(r->body);
|
||||||
|
visitExpr(r->condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ultimately: the options for a repeat-until loop are more
|
||||||
|
// straightforward.
|
||||||
|
currentScope()->inherit(repeatScope);
|
||||||
|
|
||||||
|
// `repeat` loops will unconditionally fire: if the internal control
|
||||||
|
// flow is unconditionally a break or continue, then we have linear
|
||||||
|
// control flow, but if it's throws or returns, then we need to
|
||||||
|
// return _that_ to the parent.
|
||||||
|
return matches(cf, ControlFlow::Breaks | ControlFlow::Continues) ? ControlFlow::None : cf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
{
|
{
|
||||||
PushScope ps{scopeStack, repeatScope};
|
PushScope ps{scopeStack, repeatScope};
|
||||||
visitBlockWithoutChildScope(r->body);
|
visitBlockWithoutChildScope(r->body);
|
||||||
|
@ -568,6 +624,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatBreak* b)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatBreak* b)
|
||||||
{
|
{
|
||||||
|
@ -635,6 +692,8 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
||||||
{
|
{
|
||||||
|
// See comment in visit(AstStatWhile*): this is unsound as it
|
||||||
|
// does not consider the _second_ loop iteration.
|
||||||
DfgScope* forScope = makeChildScope(DfgScope::Loop);
|
DfgScope* forScope = makeChildScope(DfgScope::Loop);
|
||||||
|
|
||||||
visitExpr(f->from);
|
visitExpr(f->from);
|
||||||
|
@ -642,6 +701,34 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
||||||
if (f->step)
|
if (f->step)
|
||||||
visitExpr(f->step);
|
visitExpr(f->step);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
{
|
||||||
|
|
||||||
|
ControlFlow cf;
|
||||||
|
{
|
||||||
|
PushScope ps{scopeStack, forScope};
|
||||||
|
|
||||||
|
if (f->var->annotation)
|
||||||
|
visitType(f->var->annotation);
|
||||||
|
|
||||||
|
DefId def = defArena->freshCell(f->var, f->var->location);
|
||||||
|
graph.localDefs[f->var] = def;
|
||||||
|
currentScope()->bindings[f->var] = def;
|
||||||
|
captures[f->var].allVersions.push_back(def);
|
||||||
|
|
||||||
|
cf = visit(f->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scope = currentScope();
|
||||||
|
// If the inner loop unconditioanlly returns or throws we shouldn't
|
||||||
|
// consume any type state from the loop body.
|
||||||
|
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
|
||||||
|
join(scope, scope, forScope);
|
||||||
|
|
||||||
|
return ControlFlow::None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
{
|
{
|
||||||
PushScope ps{scopeStack, forScope};
|
PushScope ps{scopeStack, forScope};
|
||||||
|
|
||||||
|
@ -667,11 +754,51 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
||||||
{
|
{
|
||||||
DfgScope* forScope = makeChildScope(DfgScope::Loop);
|
DfgScope* forScope = makeChildScope(DfgScope::Loop);
|
||||||
|
|
||||||
|
if (FFlag::LuauDfgAllowUpdatesInLoops)
|
||||||
|
{
|
||||||
|
|
||||||
|
ControlFlow cf;
|
||||||
|
{
|
||||||
|
PushScope ps{scopeStack, forScope};
|
||||||
|
|
||||||
|
for (AstLocal* local : f->vars)
|
||||||
|
{
|
||||||
|
if (local->annotation)
|
||||||
|
visitType(local->annotation);
|
||||||
|
|
||||||
|
DefId def = defArena->freshCell(local, local->location);
|
||||||
|
graph.localDefs[local] = def;
|
||||||
|
if (FFlag::LuauDfgScopeStackNotNull)
|
||||||
|
currentScope()->bindings[local] = def;
|
||||||
|
else
|
||||||
|
currentScope_DEPRECATED()->bindings[local] = def;
|
||||||
|
captures[local].allVersions.push_back(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(controlflow): entry point has a back edge from exit point
|
||||||
|
// We're gonna need a `visitExprList` and `visitVariadicExpr` (function calls and `...`)
|
||||||
|
for (AstExpr* e : f->values)
|
||||||
|
visitExpr(e);
|
||||||
|
|
||||||
|
cf = visit(f->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scope = currentScope();
|
||||||
|
// If the inner loop unconditioanlly returns or throws we shouldn't
|
||||||
|
// consume any type state from the loop body.
|
||||||
|
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
|
||||||
|
join(scope, scope, forScope);
|
||||||
|
|
||||||
|
return ControlFlow::None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
{
|
{
|
||||||
PushScope ps{scopeStack, forScope};
|
PushScope ps{scopeStack, forScope};
|
||||||
|
|
||||||
|
@ -703,6 +830,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)
|
||||||
{
|
{
|
||||||
|
@ -1112,10 +1240,19 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u)
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
||||||
{
|
{
|
||||||
visitExpr(b->left);
|
visitExpr(b->left);
|
||||||
|
if (FFlag::LuauDfgMatchCGScopes)
|
||||||
|
{
|
||||||
|
PushScope _{scopeStack, makeChildScope()};
|
||||||
visitExpr(b->right);
|
visitExpr(b->right);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, b->location), nullptr};
|
return {defArena->freshCell(Symbol{}, b->location), nullptr};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
visitExpr(b->right);
|
||||||
|
return {defArena->freshCell(Symbol{}, b->location), nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
||||||
{
|
{
|
||||||
|
@ -1126,10 +1263,30 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauDfgMatchCGScopes)
|
||||||
|
{
|
||||||
|
// In the constraint generator, the condition, consequence, and
|
||||||
|
// alternative all have distinct scopes.
|
||||||
|
{
|
||||||
|
PushScope _{scopeStack, makeChildScope()};
|
||||||
|
visitExpr(i->condition);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PushScope _{scopeStack, makeChildScope()};
|
||||||
|
visitExpr(i->trueExpr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PushScope _{scopeStack, makeChildScope()};
|
||||||
|
visitExpr(i->falseExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
visitExpr(i->condition);
|
visitExpr(i->condition);
|
||||||
visitExpr(i->trueExpr);
|
visitExpr(i->trueExpr);
|
||||||
visitExpr(i->falseExpr);
|
visitExpr(i->falseExpr);
|
||||||
|
}
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBaseSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionBaseSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
@checked declare function require(target: any): any
|
@checked declare function require(target: any): any
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBit32Src = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionBit32Src = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare bit32: {
|
declare bit32: {
|
||||||
band: @checked (...number) -> number,
|
band: @checked (...number) -> number,
|
||||||
|
@ -82,7 +82,7 @@ declare bit32: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionMathSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionMathSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare math: {
|
declare math: {
|
||||||
frexp: @checked (n: number) -> (number, number),
|
frexp: @checked (n: number) -> (number, number),
|
||||||
|
@ -133,7 +133,7 @@ declare math: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionOsSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionOsSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
type DateTypeArg = {
|
type DateTypeArg = {
|
||||||
year: number,
|
year: number,
|
||||||
|
@ -166,7 +166,7 @@ declare os: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionCoroutineSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionCoroutineSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare coroutine: {
|
declare coroutine: {
|
||||||
create: <A..., R...>(f: (A...) -> R...) -> thread,
|
create: <A..., R...>(f: (A...) -> R...) -> thread,
|
||||||
|
@ -181,7 +181,7 @@ declare coroutine: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionTableSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionTableSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare table: {
|
declare table: {
|
||||||
concat: <V>(t: {V}, sep: string?, i: number?, j: number?) -> string,
|
concat: <V>(t: {V}, sep: string?, i: number?, j: number?) -> string,
|
||||||
|
@ -207,7 +207,7 @@ declare table: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare debug: {
|
declare debug: {
|
||||||
info: ((thread: thread, level: number, options: string) -> ...any) & ((level: number, options: string) -> ...any) & (<A..., R1...>(func: (A...) -> R1..., options: string) -> ...any),
|
info: ((thread: thread, level: number, options: string) -> ...any) & ((level: number, options: string) -> ...any) & (<A..., R1...>(func: (A...) -> R1..., options: string) -> ...any),
|
||||||
|
@ -216,7 +216,7 @@ declare debug: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare utf8: {
|
declare utf8: {
|
||||||
char: @checked (...number) -> string,
|
char: @checked (...number) -> string,
|
||||||
|
@ -229,7 +229,7 @@ declare utf8: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
||||||
--- Buffer API
|
--- Buffer API
|
||||||
declare buffer: {
|
declare buffer: {
|
||||||
create: @checked (size: number) -> buffer,
|
create: @checked (size: number) -> buffer,
|
||||||
|
@ -262,7 +262,7 @@ declare buffer: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionVectorSrc = (FFlag::LuauDeclareExternType)
|
static const char* const kBuiltinDefinitionVectorSrc = (FFlag::LuauDeclareExternType)
|
||||||
? R"BUILTIN_SRC(
|
? R"BUILTIN_SRC(
|
||||||
|
|
||||||
-- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties
|
-- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties
|
||||||
|
@ -340,7 +340,7 @@ std::string getBuiltinDefinitionSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
||||||
static const std::string kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
export type type = {
|
export type type = {
|
||||||
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
||||||
|
@ -393,7 +393,7 @@ export type type = {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare types: {
|
declare types: {
|
||||||
unknown: type,
|
unknown: type,
|
||||||
|
@ -416,7 +416,7 @@ declare types: {
|
||||||
}
|
}
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
|
static constexpr const char* kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare types: {
|
declare types: {
|
||||||
unknown: type,
|
unknown: type,
|
||||||
|
|
|
@ -839,6 +839,11 @@ struct ErrorConverter
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string operator()(const UnexpectedArrayLikeTableItem&) const
|
||||||
|
{
|
||||||
|
return "Unexpected array-like table item: the indexer key type of this table is not `number`.";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InvalidNameChecker
|
struct InvalidNameChecker
|
||||||
|
@ -1432,6 +1437,9 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
||||||
for (auto& ty : e.cause)
|
for (auto& ty : e.cause)
|
||||||
ty = clone(ty);
|
ty = clone(ty);
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, UnexpectedArrayLikeTableItem>)
|
||||||
|
{
|
||||||
|
}
|
||||||
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||||
|
@ -833,7 +831,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto typeFun = stat->as<AstStatTypeFunction>(); typeFun && FFlag::LuauUserTypeFunTypecheck)
|
else if (auto typeFun = stat->as<AstStatTypeFunction>())
|
||||||
{
|
{
|
||||||
if (typeFun->location.contains(cursorPos))
|
if (typeFun->location.contains(cursorPos))
|
||||||
{
|
{
|
||||||
|
@ -1107,7 +1105,6 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
/// User defined type functions runtime
|
/// User defined type functions runtime
|
||||||
TypeFunctionRuntime typeFunctionRuntime(iceHandler, NotNull{&limits});
|
TypeFunctionRuntime typeFunctionRuntime(iceHandler, NotNull{&limits});
|
||||||
|
|
||||||
if (FFlag::LuauFragmentNoTypeFunEval || FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
typeFunctionRuntime.allowEvaluation = false;
|
typeFunctionRuntime.allowEvaluation = false;
|
||||||
|
|
||||||
/// Create a DataFlowGraph just for the surrounding context
|
/// Create a DataFlowGraph just for the surrounding context
|
||||||
|
@ -1141,13 +1138,10 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
freshChildOfNearestScope->interiorFreeTypePacks.emplace();
|
freshChildOfNearestScope->interiorFreeTypePacks.emplace();
|
||||||
cg.rootScope = freshChildOfNearestScope.get();
|
cg.rootScope = freshChildOfNearestScope.get();
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
{
|
|
||||||
// Create module-local scope for the type function environment
|
// Create module-local scope for the type function environment
|
||||||
ScopePtr localTypeFunctionScope = std::make_shared<Scope>(cg.typeFunctionScope);
|
ScopePtr localTypeFunctionScope = std::make_shared<Scope>(cg.typeFunctionScope);
|
||||||
localTypeFunctionScope->location = root->location;
|
localTypeFunctionScope->location = root->location;
|
||||||
cg.typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
cg.typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
||||||
}
|
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
||||||
cloneTypesFromFragment(
|
cloneTypesFromFragment(
|
||||||
|
|
|
@ -39,7 +39,6 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRethrowKnownExceptions, false)
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||||
|
@ -47,8 +46,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1098,8 +1095,6 @@ void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state,
|
||||||
{
|
{
|
||||||
BuildQueueItem& item = state->buildQueueItems[itemPos];
|
BuildQueueItem& item = state->buildQueueItems[itemPos];
|
||||||
|
|
||||||
if (DFFlag::LuauRethrowKnownExceptions)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
checkBuildQueueItem(item);
|
checkBuildQueueItem(item);
|
||||||
|
@ -1108,18 +1103,6 @@ void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state,
|
||||||
{
|
{
|
||||||
item.exception = std::current_exception();
|
item.exception = std::current_exception();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
checkBuildQueueItem(item);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
item.exception = std::current_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock guard(state->mtx);
|
std::unique_lock guard(state->mtx);
|
||||||
|
@ -1402,7 +1385,7 @@ ModulePtr check(
|
||||||
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
|
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
|
||||||
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
||||||
|
|
||||||
typeFunctionRuntime.allowEvaluation = FFlag::LuauTypeFunResultInAutocomplete || sourceModule.parseErrors.empty();
|
typeFunctionRuntime.allowEvaluation = true;
|
||||||
|
|
||||||
ConstraintGenerator cg{
|
ConstraintGenerator cg{
|
||||||
result,
|
result,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization3)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -469,7 +469,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
if (!subsumes(scope, ft.scope))
|
if (!subsumes(scope, ft.scope))
|
||||||
return true;
|
return true;
|
||||||
|
@ -520,7 +520,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
unsealedTables.insert(ty);
|
unsealedTables.insert(ty);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -559,7 +559,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if (tt.indexer)
|
if (tt.indexer)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
||||||
//
|
//
|
||||||
|
@ -617,7 +617,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
if (!subsumes(scope, ftp.scope))
|
if (!subsumes(scope, ftp.scope))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
||||||
++params.useCount;
|
++params.useCount;
|
||||||
|
@ -1159,7 +1159,7 @@ struct RemoveType : Substitution // NOLINT
|
||||||
|
|
||||||
for (TypeId ty : ut)
|
for (TypeId ty : ut)
|
||||||
{
|
{
|
||||||
if (ty != needle)
|
if (ty != needle && !is<NeverType>(ty))
|
||||||
newParts.insert(ty);
|
newParts.insert(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,7 +1180,7 @@ struct RemoveType : Substitution // NOLINT
|
||||||
|
|
||||||
for (TypeId ty : it)
|
for (TypeId ty : it)
|
||||||
{
|
{
|
||||||
if (ty != needle)
|
if (ty != needle && !is<UnknownType>(ty))
|
||||||
newParts.insert(ty);
|
newParts.insert(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,7 +1301,23 @@ GeneralizationResult<TypeId> generalizeType(
|
||||||
if (follow(ub) != freeTy)
|
if (follow(ub) != freeTy)
|
||||||
emplaceType<BoundType>(asMutable(freeTy), ub);
|
emplaceType<BoundType>(asMutable(freeTy), ub);
|
||||||
else if (!isWithinFunction || params.useCount == 1)
|
else if (!isWithinFunction || params.useCount == 1)
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
{
|
||||||
|
// If we have some free type:
|
||||||
|
//
|
||||||
|
// A <: 'b < C
|
||||||
|
//
|
||||||
|
// We can approximately generalize this to the intersection of it's
|
||||||
|
// bounds, taking care to avoid constructing a degenerate
|
||||||
|
// union or intersection by clipping the free type from the upper
|
||||||
|
// and lower bounds, then also cleaning the resulting intersection.
|
||||||
|
std::optional<TypeId> removedLb = removeType(arena, builtinTypes, ft->lowerBound, freeTy);
|
||||||
|
if (!removedLb)
|
||||||
|
return {std::nullopt, false, true};
|
||||||
|
std::optional<TypeId> cleanedTy = removeType(arena, builtinTypes, arena->addType(IntersectionType{{*removedLb, ub}}), freeTy);
|
||||||
|
if (!cleanedTy)
|
||||||
|
return {std::nullopt, false, true};
|
||||||
|
emplaceType<BoundType>(asMutable(freeTy), *cleanedTy);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if the upper bound is the type in question, we don't actually have an upper bound.
|
// if the upper bound is the type in question, we don't actually have an upper bound.
|
||||||
|
@ -1374,7 +1390,7 @@ std::optional<TypeId> generalize(
|
||||||
FreeTypeSearcher fts{scope, cachedTypes};
|
FreeTypeSearcher fts{scope, cachedTypes};
|
||||||
fts.traverse(ty);
|
fts.traverse(ty);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
||||||
auto pushGeneric = [&](TypeId t)
|
auto pushGeneric = [&](TypeId t)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -133,7 +133,7 @@ struct InferPolarity : TypeVisitor
|
||||||
template<typename TID>
|
template<typename TID>
|
||||||
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauNonReentrantGeneralization2)
|
if (!FFlag::LuauNonReentrantGeneralization3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InferPolarity infer{arena, scope};
|
InferPolarity infer{arena, scope};
|
||||||
|
|
|
@ -248,6 +248,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
||||||
|
|
||||||
stream << " } } ";
|
stream << " } } ";
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, UnexpectedArrayLikeTableItem>)
|
||||||
|
stream << "UnexpectedArrayLikeTableItem {}";
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3240,7 +3240,7 @@ static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* fuzzyMatch(std::string_view str, const char** array, size_t size)
|
static const char* fuzzyMatch(std::string_view str, const char* const* array, size_t size)
|
||||||
{
|
{
|
||||||
if (FInt::LuauSuggestionDistance == 0)
|
if (FInt::LuauSuggestionDistance == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
|
@ -367,28 +365,18 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatWhile* whileStatement)
|
NonStrictContext visit(AstStatWhile* whileStatement)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
NonStrictContext condition = visit(whileStatement->condition, ValueContext::RValue);
|
NonStrictContext condition = visit(whileStatement->condition, ValueContext::RValue);
|
||||||
NonStrictContext body = visit(whileStatement->body);
|
NonStrictContext body = visit(whileStatement->body);
|
||||||
return NonStrictContext::disjunction(builtinTypes, arena, condition, body);
|
return NonStrictContext::disjunction(builtinTypes, arena, condition, body);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstStatRepeat* repeatStatement)
|
NonStrictContext visit(AstStatRepeat* repeatStatement)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
NonStrictContext body = visit(repeatStatement->body);
|
NonStrictContext body = visit(repeatStatement->body);
|
||||||
NonStrictContext condition = visit(repeatStatement->condition, ValueContext::RValue);
|
NonStrictContext condition = visit(repeatStatement->condition, ValueContext::RValue);
|
||||||
return NonStrictContext::disjunction(builtinTypes, arena, body, condition);
|
return NonStrictContext::disjunction(builtinTypes, arena, body, condition);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstStatBreak* breakStatement)
|
NonStrictContext visit(AstStatBreak* breakStatement)
|
||||||
{
|
{
|
||||||
|
@ -401,14 +389,11 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatReturn* returnStatement)
|
NonStrictContext visit(AstStatReturn* returnStatement)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
// TODO: this is believing existing code, but i'm not sure if this makes sense
|
// TODO: this is believing existing code, but i'm not sure if this makes sense
|
||||||
// for how the contexts are handled
|
// for how the contexts are handled
|
||||||
for (AstExpr* expr : returnStatement->list)
|
for (AstExpr* expr : returnStatement->list)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -430,8 +415,6 @@ struct NonStrictTypeChecker
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
visit(forStatement->var->annotation);
|
visit(forStatement->var->annotation);
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
|
||||||
// TODO: throwing out context based on same principle as existing code?
|
// TODO: throwing out context based on same principle as existing code?
|
||||||
if (forStatement->from)
|
if (forStatement->from)
|
||||||
visit(forStatement->from, ValueContext::RValue);
|
visit(forStatement->from, ValueContext::RValue);
|
||||||
|
@ -441,11 +424,6 @@ struct NonStrictTypeChecker
|
||||||
visit(forStatement->step, ValueContext::RValue);
|
visit(forStatement->step, ValueContext::RValue);
|
||||||
return visit(forStatement->body);
|
return visit(forStatement->body);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstStatForIn* forInStatement)
|
NonStrictContext visit(AstStatForIn* forInStatement)
|
||||||
{
|
{
|
||||||
|
@ -455,38 +433,25 @@ struct NonStrictTypeChecker
|
||||||
visit(var->annotation);
|
visit(var->annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
|
||||||
for (AstExpr* rhs : forInStatement->values)
|
for (AstExpr* rhs : forInStatement->values)
|
||||||
visit(rhs, ValueContext::RValue);
|
visit(rhs, ValueContext::RValue);
|
||||||
return visit(forInStatement->body);
|
return visit(forInStatement->body);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstStatAssign* assign)
|
NonStrictContext visit(AstStatAssign* assign)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
for (AstExpr* lhs : assign->vars)
|
for (AstExpr* lhs : assign->vars)
|
||||||
visit(lhs, ValueContext::LValue);
|
visit(lhs, ValueContext::LValue);
|
||||||
for (AstExpr* rhs : assign->values)
|
for (AstExpr* rhs : assign->values)
|
||||||
visit(rhs, ValueContext::RValue);
|
visit(rhs, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatCompoundAssign* compoundAssign)
|
NonStrictContext visit(AstStatCompoundAssign* compoundAssign)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
visit(compoundAssign->var, ValueContext::LValue);
|
visit(compoundAssign->var, ValueContext::LValue);
|
||||||
visit(compoundAssign->value, ValueContext::RValue);
|
visit(compoundAssign->value, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -555,14 +520,11 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatError* error)
|
NonStrictContext visit(AstStatError* error)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
for (AstStat* stat : error->statements)
|
for (AstStat* stat : error->statements)
|
||||||
visit(stat);
|
visit(stat);
|
||||||
for (AstExpr* expr : error->expressions)
|
for (AstExpr* expr : error->expressions)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -617,10 +579,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstExprGroup* group, ValueContext context)
|
NonStrictContext visit(AstExprGroup* group, ValueContext context)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
return visit(group->expr, context);
|
return visit(group->expr, context);
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprConstantNil* expr)
|
NonStrictContext visit(AstExprConstantNil* expr)
|
||||||
|
@ -649,8 +608,6 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprGlobal* global, ValueContext context)
|
NonStrictContext visit(AstExprGlobal* global, ValueContext context)
|
||||||
{
|
|
||||||
if (FFlag::LuauNewNonStrictWarnOnUnknownGlobals)
|
|
||||||
{
|
{
|
||||||
// We don't file unknown symbols for LValues.
|
// We don't file unknown symbols for LValues.
|
||||||
if (context == ValueContext::LValue)
|
if (context == ValueContext::LValue)
|
||||||
|
@ -661,7 +618,6 @@ struct NonStrictTypeChecker
|
||||||
{
|
{
|
||||||
reportError(UnknownSymbol{global->name.value, UnknownSymbol::Binding}, global->location);
|
reportError(UnknownSymbol{global->name.value, UnknownSymbol::Binding}, global->location);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -783,23 +739,16 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstExprIndexName* indexName, ValueContext context)
|
NonStrictContext visit(AstExprIndexName* indexName, ValueContext context)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
return visit(indexName->expr, context);
|
return visit(indexName->expr, context);
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprIndexExpr* indexExpr, ValueContext context)
|
NonStrictContext visit(AstExprIndexExpr* indexExpr, ValueContext context)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
NonStrictContext expr = visit(indexExpr->expr, context);
|
NonStrictContext expr = visit(indexExpr->expr, context);
|
||||||
NonStrictContext index = visit(indexExpr->index, ValueContext::RValue);
|
NonStrictContext index = visit(indexExpr->index, ValueContext::RValue);
|
||||||
return NonStrictContext::disjunction(builtinTypes, arena, expr, index);
|
return NonStrictContext::disjunction(builtinTypes, arena, expr, index);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstExprFunction* exprFn)
|
NonStrictContext visit(AstExprFunction* exprFn)
|
||||||
{
|
{
|
||||||
|
@ -839,8 +788,6 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprTable* table)
|
NonStrictContext visit(AstExprTable* table)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
for (auto [_, key, value] : table->items)
|
for (auto [_, key, value] : table->items)
|
||||||
{
|
{
|
||||||
|
@ -848,40 +795,28 @@ struct NonStrictTypeChecker
|
||||||
visit(key, ValueContext::RValue);
|
visit(key, ValueContext::RValue);
|
||||||
visit(value, ValueContext::RValue);
|
visit(value, ValueContext::RValue);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprUnary* unary)
|
NonStrictContext visit(AstExprUnary* unary)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
return visit(unary->expr, ValueContext::RValue);
|
return visit(unary->expr, ValueContext::RValue);
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprBinary* binary)
|
NonStrictContext visit(AstExprBinary* binary)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
NonStrictContext lhs = visit(binary->left, ValueContext::RValue);
|
NonStrictContext lhs = visit(binary->left, ValueContext::RValue);
|
||||||
NonStrictContext rhs = visit(binary->right, ValueContext::RValue);
|
NonStrictContext rhs = visit(binary->right, ValueContext::RValue);
|
||||||
return NonStrictContext::disjunction(builtinTypes, arena, lhs, rhs);
|
return NonStrictContext::disjunction(builtinTypes, arena, lhs, rhs);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
visit(typeAssertion->annotation);
|
visit(typeAssertion->annotation);
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
return visit(typeAssertion->expr, ValueContext::RValue);
|
return visit(typeAssertion->expr, ValueContext::RValue);
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprIfElse* ifElse)
|
NonStrictContext visit(AstExprIfElse* ifElse)
|
||||||
|
@ -893,23 +828,17 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprInterpString* interpString)
|
NonStrictContext visit(AstExprInterpString* interpString)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
for (AstExpr* expr : interpString->expressions)
|
for (AstExpr* expr : interpString->expressions)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstExprError* error)
|
NonStrictContext visit(AstExprError* error)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
|
||||||
{
|
{
|
||||||
for (AstExpr* expr : error->expressions)
|
for (AstExpr* expr : error->expressions)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,6 @@ LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixInfiniteRecursionInNormalization)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationCatchMetatableCycles)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -2605,8 +2603,6 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||||
if (hprop.readTy.has_value())
|
if (hprop.readTy.has_value())
|
||||||
{
|
{
|
||||||
if (tprop.readTy.has_value())
|
if (tprop.readTy.has_value())
|
||||||
{
|
|
||||||
if (FFlag::LuauFixInfiniteRecursionInNormalization)
|
|
||||||
{
|
{
|
||||||
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
|
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
|
||||||
|
|
||||||
|
@ -2626,42 +2622,6 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
||||||
thereSubHere &= (ty == tprop.readTy);
|
thereSubHere &= (ty == tprop.readTy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
// if the intersection of the read types of a property is uninhabited, the whole table is `never`.
|
|
||||||
// We've seen these table prop elements before and we're about to ask if their intersection
|
|
||||||
// is inhabited
|
|
||||||
|
|
||||||
auto pair1 = std::pair{*hprop.readTy, *tprop.readTy};
|
|
||||||
auto pair2 = std::pair{*tprop.readTy, *hprop.readTy};
|
|
||||||
if (seenTablePropPairs.contains(pair1) || seenTablePropPairs.contains(pair2))
|
|
||||||
{
|
|
||||||
seenTablePropPairs.erase(pair1);
|
|
||||||
seenTablePropPairs.erase(pair2);
|
|
||||||
return {builtinTypes->neverType};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
seenTablePropPairs.insert(pair1);
|
|
||||||
seenTablePropPairs.insert(pair2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(ariel): this is being added in a flag removal, so not changing the semantics here, but worth noting that this
|
|
||||||
// fresh `seenSet` is definitely a bug. we already have `seenSet` from the parameter that _should_ have been used here.
|
|
||||||
Set<TypeId> seenSet{nullptr};
|
|
||||||
NormalizationResult res = isIntersectionInhabited(*hprop.readTy, *tprop.readTy, seenTablePropPairs, seenSet);
|
|
||||||
|
|
||||||
seenTablePropPairs.erase(pair1);
|
|
||||||
seenTablePropPairs.erase(pair2);
|
|
||||||
if (NormalizationResult::True != res)
|
|
||||||
return {builtinTypes->neverType};
|
|
||||||
|
|
||||||
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
|
|
||||||
prop.readTy = ty;
|
|
||||||
hereSubThere &= (ty == hprop.readTy);
|
|
||||||
thereSubHere &= (ty == tprop.readTy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
prop.readTy = *hprop.readTy;
|
prop.readTy = *hprop.readTy;
|
||||||
thereSubHere = false;
|
thereSubHere = false;
|
||||||
|
@ -3352,21 +3312,6 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeTableShared_DEPRECATED(TypeId ty)
|
|
||||||
{
|
|
||||||
ty = follow(ty);
|
|
||||||
if (auto tableTy = getMutable<TableType>(ty))
|
|
||||||
{
|
|
||||||
for (auto& [_, prop] : tableTy->props)
|
|
||||||
prop.makeShared();
|
|
||||||
}
|
|
||||||
else if (auto metatableTy = get<MetatableType>(ty))
|
|
||||||
{
|
|
||||||
makeTableShared_DEPRECATED(metatableTy->metatable);
|
|
||||||
makeTableShared_DEPRECATED(metatableTy->table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void makeTableShared(TypeId ty, DenseHashSet<TypeId>& seen)
|
void makeTableShared(TypeId ty, DenseHashSet<TypeId>& seen)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -3490,10 +3435,7 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
||||||
result.reserve(result.size() + norm.tables.size());
|
result.reserve(result.size() + norm.tables.size());
|
||||||
for (auto table : norm.tables)
|
for (auto table : norm.tables)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNormalizationCatchMetatableCycles)
|
|
||||||
makeTableShared(table);
|
makeTableShared(table);
|
||||||
else
|
|
||||||
makeTableShared_DEPRECATED(table);
|
|
||||||
result.push_back(table);
|
result.push_back(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -287,6 +288,25 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||||
|
|
||||||
return {Analysis::Ok, {}};
|
return {Analysis::Ok, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
{
|
||||||
|
if (reason.subPath == TypePath::Path{{TypePath::PackField::Arguments, TypePath::PackField::Tail}} && reason.superPath == justArguments)
|
||||||
|
{
|
||||||
|
// We have an arity mismatch if the argument tail is a generic type pack
|
||||||
|
if (auto fnArgs = get<TypePack>(fn->argTypes))
|
||||||
|
{
|
||||||
|
// TODO: Determine whether arguments have incorrect type, incorrect count, or both (CLI-152070)
|
||||||
|
if (get<GenericTypePack>(fnArgs->tail))
|
||||||
|
{
|
||||||
|
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||||
|
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg}};
|
||||||
|
|
||||||
|
return {Analysis::ArityMismatch, {error}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorVec errors;
|
ErrorVec errors;
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
||||||
LUAU_FASTFLAG(LuauSyntheticErrors)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -56,8 +55,6 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, ErrorType>)
|
else if constexpr (std::is_same_v<T, ErrorType>)
|
||||||
{
|
|
||||||
if (FFlag::LuauSyntheticErrors)
|
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(ty->persistent || a.synthetic);
|
LUAU_ASSERT(ty->persistent || a.synthetic);
|
||||||
|
|
||||||
|
@ -71,12 +68,6 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
||||||
clone.synthetic = a.synthetic;
|
clone.synthetic = a.synthetic;
|
||||||
return dest.addType(clone);
|
return dest.addType(clone);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(ty->persistent);
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<T, UnknownType>)
|
else if constexpr (std::is_same_v<T, UnknownType>)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(ty->persistent);
|
LUAU_ASSERT(ty->persistent);
|
||||||
|
|
|
@ -17,12 +17,10 @@
|
||||||
#include "Luau/TypePath.h"
|
#include "Luau/TypePath.h"
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
||||||
|
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -101,7 +99,7 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
||||||
result.insert(r);
|
result.insert(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
if (result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +118,7 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
||||||
result.insert(r);
|
result.insert(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
if (result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,7 +813,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||||
// <X>(X) -> () <: (T) -> ()
|
// <X>(X) -> () <: (T) -> ()
|
||||||
|
|
||||||
// Possible optimization: If headSize == 0 then we can just use subTp as-is.
|
// Possible optimization: If headSize == 0 then we can just use subTp as-is.
|
||||||
std::vector<TypeId> headSlice(begin(superHead), begin(superHead) + headSize);
|
std::vector<TypeId> headSlice = FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2
|
||||||
|
? std::vector<TypeId>(begin(superHead) + headSize, end(superHead))
|
||||||
|
: std::vector<TypeId>(begin(superHead), begin(superHead) + headSize);
|
||||||
TypePackId superTailPack = arena->addTypePack(std::move(headSlice), superTail);
|
TypePackId superTailPack = arena->addTypePack(std::move(headSlice), superTail);
|
||||||
|
|
||||||
if (TypePackId* other = env.getMappedPackBounds(*subTail))
|
if (TypePackId* other = env.getMappedPackBounds(*subTail))
|
||||||
|
@ -870,11 +870,16 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||||
// <X...>(X...) -> () <: (T) -> ()
|
// <X...>(X...) -> () <: (T) -> ()
|
||||||
|
|
||||||
// Possible optimization: If headSize == 0 then we can just use subTp as-is.
|
// Possible optimization: If headSize == 0 then we can just use subTp as-is.
|
||||||
std::vector<TypeId> headSlice(begin(subHead), begin(subHead) + headSize);
|
std::vector<TypeId> headSlice = FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2
|
||||||
|
? std::vector<TypeId>(begin(subHead) + headSize, end(subHead))
|
||||||
|
: std::vector<TypeId>(begin(subHead), begin(subHead) + headSize);
|
||||||
TypePackId subTailPack = arena->addTypePack(std::move(headSlice), subTail);
|
TypePackId subTailPack = arena->addTypePack(std::move(headSlice), subTail);
|
||||||
|
|
||||||
if (TypePackId* other = env.getMappedPackBounds(*superTail))
|
if (TypePackId* other = env.getMappedPackBounds(*superTail))
|
||||||
// TODO: TypePath can't express "slice of a pack + its tail".
|
// TODO: TypePath can't express "slice of a pack + its tail".
|
||||||
|
if (FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
results.push_back(isCovariantWith(env, subTailPack, *other, scope).withSuperComponent(TypePath::PackField::Tail));
|
||||||
|
else
|
||||||
results.push_back(isContravariantWith(env, subTailPack, *other, scope).withSuperComponent(TypePath::PackField::Tail));
|
results.push_back(isContravariantWith(env, subTailPack, *other, scope).withSuperComponent(TypePath::PackField::Tail));
|
||||||
else
|
else
|
||||||
env.mappedGenericPacks.try_insert(*superTail, subTailPack);
|
env.mappedGenericPacks.try_insert(*superTail, subTailPack);
|
||||||
|
|
|
@ -14,17 +14,11 @@
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
||||||
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
// A fast approximation of subTy <: superTy
|
|
||||||
static bool fastIsSubtype(TypeId subTy, TypeId superTy)
|
|
||||||
{
|
|
||||||
Relation r = relate(superTy, subTy);
|
|
||||||
return r == Relation::Coincident || r == Relation::Superset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isRecord(const AstExprTable::Item& item)
|
static bool isRecord(const AstExprTable::Item& item)
|
||||||
{
|
{
|
||||||
if (item.kind == AstExprTable::Item::Record)
|
if (item.kind == AstExprTable::Item::Record)
|
||||||
|
@ -35,80 +29,6 @@ static bool isRecord(const AstExprTable::Item& item)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, TypeId exprType, NotNull<BuiltinTypes> builtinTypes)
|
|
||||||
{
|
|
||||||
if (tables.empty())
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const TableType* exprTable = get<TableType>(follow(exprType));
|
|
||||||
if (!exprTable)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
size_t tableCount = 0;
|
|
||||||
std::optional<TypeId> firstTable;
|
|
||||||
|
|
||||||
for (TypeId ty : tables)
|
|
||||||
{
|
|
||||||
ty = follow(ty);
|
|
||||||
if (auto tt = get<TableType>(ty))
|
|
||||||
{
|
|
||||||
// If the expected table has a key whose type is a string or boolean
|
|
||||||
// singleton and the corresponding exprType property does not match,
|
|
||||||
// then skip this table.
|
|
||||||
|
|
||||||
if (!firstTable)
|
|
||||||
firstTable = ty;
|
|
||||||
++tableCount;
|
|
||||||
|
|
||||||
for (const auto& [name, expectedProp] : tt->props)
|
|
||||||
{
|
|
||||||
if (!expectedProp.readTy)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const TypeId expectedType = follow(*expectedProp.readTy);
|
|
||||||
|
|
||||||
auto st = get<SingletonType>(expectedType);
|
|
||||||
if (!st)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto it = exprTable->props.find(name);
|
|
||||||
if (it == exprTable->props.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto& [_name, exprProp] = *it;
|
|
||||||
|
|
||||||
if (!exprProp.readTy)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const TypeId propType = follow(*exprProp.readTy);
|
|
||||||
|
|
||||||
const FreeType* ft = get<FreeType>(propType);
|
|
||||||
|
|
||||||
if (ft && get<SingletonType>(ft->lowerBound))
|
|
||||||
{
|
|
||||||
if (fastIsSubtype(builtinTypes->booleanType, ft->upperBound) && fastIsSubtype(expectedType, builtinTypes->booleanType))
|
|
||||||
{
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fastIsSubtype(builtinTypes->stringType, ft->upperBound) && fastIsSubtype(expectedType, ft->lowerBound))
|
|
||||||
{
|
|
||||||
return ty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tableCount == 1)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(firstTable);
|
|
||||||
return firstTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId matchLiteralType(
|
TypeId matchLiteralType(
|
||||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
||||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||||
|
@ -257,7 +177,6 @@ TypeId matchLiteralType(
|
||||||
if (tt)
|
if (tt)
|
||||||
{
|
{
|
||||||
TypeId res = matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *tt, exprType, expr, toBlock);
|
TypeId res = matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *tt, exprType, expr, toBlock);
|
||||||
|
|
||||||
parts.push_back(res);
|
parts.push_back(res);
|
||||||
return arena->addType(UnionType{std::move(parts)});
|
return arena->addType(UnionType{std::move(parts)});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1092,7 +1091,7 @@ struct TypeStringifier
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
|
|
||||||
if (FFlag::LuauSyntheticErrors && tv.synthetic)
|
if (tv.synthetic)
|
||||||
{
|
{
|
||||||
state.emit("*error-type<");
|
state.emit("*error-type<");
|
||||||
stringify(*tv.synthetic);
|
stringify(*tv.synthetic);
|
||||||
|
@ -1278,7 +1277,7 @@ struct TypePackStringifier
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
|
|
||||||
if (FFlag::LuauSyntheticErrors && error.synthetic)
|
if (error.synthetic)
|
||||||
{
|
{
|
||||||
state.emit("*");
|
state.emit("*");
|
||||||
stringify(*error.synthetic);
|
stringify(*error.synthetic);
|
||||||
|
@ -1965,6 +1964,8 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
|
||||||
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>)
|
else if constexpr (std::is_same_v<T, TableCheckConstraint>)
|
||||||
return "table_check " + tos(c.expectedType) + " :> " + tos(c.exprType);
|
return "table_check " + tos(c.expectedType) + " :> " + tos(c.exprType);
|
||||||
|
else if constexpr (std::is_same_v<T, SimplifyConstraint>)
|
||||||
|
return "simplify " + tos(c.ty);
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
|
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||||
|
@ -308,8 +307,7 @@ public:
|
||||||
std::optional<AstArgumentName>* arg = &argNames.data[i++];
|
std::optional<AstArgumentName>* arg = &argNames.data[i++];
|
||||||
|
|
||||||
if (el)
|
if (el)
|
||||||
new (arg)
|
new (arg) std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), Location()));
|
||||||
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location));
|
|
||||||
else
|
else
|
||||||
new (arg) std::optional<AstArgumentName>();
|
new (arg) std::optional<AstArgumentName>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,12 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -710,14 +711,77 @@ void TypeChecker2::visit(AstStatReturn* ret)
|
||||||
{
|
{
|
||||||
Scope* scope = findInnermostScope(ret->location);
|
Scope* scope = findInnermostScope(ret->location);
|
||||||
TypePackId expectedRetType = scope->returnType;
|
TypePackId expectedRetType = scope->returnType;
|
||||||
|
if (FFlag::LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
{
|
||||||
|
if (ret->list.size == 0)
|
||||||
|
{
|
||||||
|
testIsSubtype(builtinTypes->emptyTypePack, expectedRetType, ret->location);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [head, _] = extendTypePack(module->internalTypes, builtinTypes, expectedRetType, ret->list.size);
|
||||||
|
bool isSubtype = true;
|
||||||
|
std::vector<TypeId> actualHead;
|
||||||
|
std::optional<TypePackId> actualTail;
|
||||||
|
for (size_t idx = 0; idx < ret->list.size - 1; idx++)
|
||||||
|
{
|
||||||
|
if (idx < head.size())
|
||||||
|
{
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(ret->list.data[idx], head[idx]);
|
||||||
|
actualHead.push_back(head[idx]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actualHead.push_back(lookupType(ret->list.data[idx]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This stanza is deconstructing what constraint generation does to
|
||||||
|
// return statements. If we have some statement like:
|
||||||
|
//
|
||||||
|
// return E0, E1, E2, ... , EN
|
||||||
|
//
|
||||||
|
// All expressions *except* the last will be types, and the last can
|
||||||
|
// potentially be a pack. However, if the last expression is a function
|
||||||
|
// call or varargs (`...`), then we _could_ have a pack in the final
|
||||||
|
// position. Additionally, if we have an argument overflow, then we can't
|
||||||
|
// do anything interesting with subtyping.
|
||||||
|
//
|
||||||
|
// _If_ the last argument is not a function call or varargs and we have
|
||||||
|
// at least an argument underflow, then we grab the last type out of
|
||||||
|
// the type pack head and use that to check the subtype of
|
||||||
|
auto lastExpr = ret->list.data[ret->list.size - 1];
|
||||||
|
if (head.size() < ret->list.size || lastExpr->is<AstExprCall>() || lastExpr->is<AstExprVarargs>())
|
||||||
|
{
|
||||||
|
actualTail = lookupPack(lastExpr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto lastType = head[ret->list.size - 1];
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(lastExpr, lastType);
|
||||||
|
actualHead.push_back(lastType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After all that, we still fire a pack subtype test to determine
|
||||||
|
// whether we have a well-formed return statement. We only fire
|
||||||
|
// this if all the previous subtype tests have succeeded, lest
|
||||||
|
// we double error.
|
||||||
|
if (isSubtype)
|
||||||
|
{
|
||||||
|
auto reconstructedRetType = module->internalTypes.addTypePack(TypePack{std::move(actualHead), std::move(actualTail)});
|
||||||
|
testIsSubtype(reconstructedRetType, expectedRetType, ret->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
TypeArena* arena = &module->internalTypes;
|
TypeArena* arena = &module->internalTypes;
|
||||||
TypePackId actualRetType = reconstructPack(ret->list, *arena);
|
TypePackId actualRetType = reconstructPack(ret->list, *arena);
|
||||||
|
|
||||||
testIsSubtype(actualRetType, expectedRetType, ret->location);
|
testIsSubtype(actualRetType, expectedRetType, ret->location);
|
||||||
|
}
|
||||||
|
|
||||||
for (AstExpr* expr : ret->list)
|
for (AstExpr* expr : ret->list)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatExpr* expr)
|
void TypeChecker2::visit(AstStatExpr* expr)
|
||||||
|
@ -745,7 +809,12 @@ void TypeChecker2::visit(AstStatLocal* local)
|
||||||
TypeId annotationType = lookupAnnotation(var->annotation);
|
TypeId annotationType = lookupAnnotation(var->annotation);
|
||||||
TypeId valueType = value ? lookupType(value) : nullptr;
|
TypeId valueType = value ? lookupType(value) : nullptr;
|
||||||
if (valueType)
|
if (valueType)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
testPotentialLiteralIsSubtype(value, annotationType);
|
||||||
|
else
|
||||||
testIsSubtype(valueType, annotationType, value->location);
|
testIsSubtype(valueType, annotationType, value->location);
|
||||||
|
}
|
||||||
|
|
||||||
visit(var->annotation);
|
visit(var->annotation);
|
||||||
}
|
}
|
||||||
|
@ -1154,6 +1223,17 @@ void TypeChecker2::visit(AstStatAssign* assign)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
{
|
||||||
|
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType
|
||||||
|
if (testPotentialLiteralIsSubtype(rhs, lhsType))
|
||||||
|
{
|
||||||
|
if (std::optional<TypeId> bindingType = getBindingType(lhs))
|
||||||
|
testPotentialLiteralIsSubtype(rhs, *bindingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
bool ok = testIsSubtype(rhsType, lhsType, rhs->location);
|
bool ok = testIsSubtype(rhsType, lhsType, rhs->location);
|
||||||
|
|
||||||
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType
|
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType
|
||||||
|
@ -1165,6 +1245,7 @@ void TypeChecker2::visit(AstStatAssign* assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatCompoundAssign* stat)
|
void TypeChecker2::visit(AstStatCompoundAssign* stat)
|
||||||
{
|
{
|
||||||
|
@ -1200,13 +1281,18 @@ void TypeChecker2::visit(const AstTypeList* typeList)
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatTypeAlias* stat)
|
void TypeChecker2::visit(AstStatTypeAlias* stat)
|
||||||
{
|
{
|
||||||
|
// We will not visit type aliases that do not have an associated scope,
|
||||||
|
// this means that (probably) this was a duplicate type alias or a
|
||||||
|
// type alias with an illegal name (like `typeof`).
|
||||||
|
if (FFlag::LuauSkipMalformedTypeAliases && !module->astScopes.contains(stat))
|
||||||
|
return;
|
||||||
|
|
||||||
visitGenerics(stat->generics, stat->genericPacks);
|
visitGenerics(stat->generics, stat->genericPacks);
|
||||||
visit(stat->type);
|
visit(stat->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatTypeFunction* stat)
|
void TypeChecker2::visit(AstStatTypeFunction* stat)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
visit(stat->body);
|
visit(stat->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2857,6 +2943,130 @@ void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location l
|
||||||
reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location);
|
reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool isRecord(const AstExprTable::Item& item)
|
||||||
|
{
|
||||||
|
return item.kind == AstExprTable::Item::Record || (item.kind == AstExprTable::Item::General && item.key->is<AstExprConstantString>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType)
|
||||||
|
{
|
||||||
|
auto exprType = follow(lookupType(expr));
|
||||||
|
expectedType = follow(expectedType);
|
||||||
|
|
||||||
|
auto exprTable = expr->as<AstExprTable>();
|
||||||
|
auto exprTableType = get<TableType>(exprType);
|
||||||
|
auto expectedTableType = get<TableType>(expectedType);
|
||||||
|
|
||||||
|
// If we don't have a table or the type of the expression isn't a
|
||||||
|
// table, then do a normal subtype test.
|
||||||
|
if (!exprTableType || !exprTable)
|
||||||
|
return testIsSubtype(exprType, expectedType, expr->location);
|
||||||
|
|
||||||
|
// At this point we *know* that the expression is a table and has a specific
|
||||||
|
// table type, but if there isn't an expected table type we should do something
|
||||||
|
// slightly different.
|
||||||
|
if (!expectedTableType)
|
||||||
|
{
|
||||||
|
if (auto utv = get<UnionType>(expectedType))
|
||||||
|
{
|
||||||
|
std::vector<TypeId> parts{begin(utv), end(utv)};
|
||||||
|
std::optional<TypeId> tt = extractMatchingTableType(parts, exprType, builtinTypes);
|
||||||
|
if (tt)
|
||||||
|
return testPotentialLiteralIsSubtype(expr, *tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return testIsSubtype(exprType, expectedType, expr->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<std::optional<std::string> > missingKeys{{}};
|
||||||
|
for (const auto& [name, prop] : expectedTableType->props)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!prop.isWriteOnly());
|
||||||
|
auto readTy = *prop.readTy;
|
||||||
|
if (!isOptional(readTy))
|
||||||
|
missingKeys.insert(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isArrayLike = false;
|
||||||
|
if (expectedTableType->indexer)
|
||||||
|
{
|
||||||
|
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||||
|
auto result = subtyping->isSubtype(expectedTableType->indexer->indexType, builtinTypes->numberType, scope);
|
||||||
|
isArrayLike = result.isSubtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSubtype = true;
|
||||||
|
|
||||||
|
for (const auto& item : exprTable->items)
|
||||||
|
{
|
||||||
|
if (isRecord(item))
|
||||||
|
{
|
||||||
|
const AstArray<char>& s = item.key->as<AstExprConstantString>()->value;
|
||||||
|
std::string keyStr{s.data, s.data + s.size};
|
||||||
|
|
||||||
|
missingKeys.erase(keyStr);
|
||||||
|
auto expectedIt = expectedTableType->props.find(keyStr);
|
||||||
|
if (expectedIt == expectedTableType->props.end())
|
||||||
|
{
|
||||||
|
if (expectedTableType->indexer)
|
||||||
|
{
|
||||||
|
module->astExpectedTypes[item.key] = expectedTableType->indexer->indexType;
|
||||||
|
module->astExpectedTypes[item.value] = expectedTableType->indexer->indexResultType;
|
||||||
|
auto inferredKeyType = module->internalTypes.addType(SingletonType{StringSingleton{keyStr}});
|
||||||
|
isSubtype &= testIsSubtype(inferredKeyType, expectedTableType->indexer->indexType, item.key->location);
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(item.value, expectedTableType->indexer->indexResultType);
|
||||||
|
}
|
||||||
|
// If there's not an indexer, then by width subtyping we can just do nothing :)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: What do we do for write only props?
|
||||||
|
LUAU_ASSERT(expectedIt->second.readTy);
|
||||||
|
// Some property is in the expected type: we can test against the specific type.
|
||||||
|
module->astExpectedTypes[item.value] = *expectedIt->second.readTy;
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (item.kind == AstExprTable::Item::List)
|
||||||
|
{
|
||||||
|
if (!isArrayLike)
|
||||||
|
{
|
||||||
|
isSubtype = false;
|
||||||
|
reportError(UnexpectedArrayLikeTableItem{}, item.value->location);
|
||||||
|
}
|
||||||
|
// if the indexer index type is not exactly `number`.
|
||||||
|
if (expectedTableType->indexer)
|
||||||
|
{
|
||||||
|
module->astExpectedTypes[item.value] = expectedTableType->indexer->indexResultType;
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(item.value, expectedTableType->indexer->indexResultType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (item.kind == AstExprTable::Item::General && expectedTableType->indexer)
|
||||||
|
{
|
||||||
|
module->astExpectedTypes[item.key] = expectedTableType->indexer->indexType;
|
||||||
|
module->astExpectedTypes[item.value] = expectedTableType->indexer->indexResultType;
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(item.key, expectedTableType->indexer->indexType);
|
||||||
|
isSubtype &= testPotentialLiteralIsSubtype(item.value, expectedTableType->indexer->indexResultType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missingKeys.empty())
|
||||||
|
{
|
||||||
|
std::vector<Name> temp;
|
||||||
|
temp.reserve(missingKeys.size());
|
||||||
|
for (const auto& key : missingKeys)
|
||||||
|
if (key)
|
||||||
|
temp.push_back(*key);
|
||||||
|
reportError(MissingProperties{expectedType, exprType, std::move(temp)}, expr->location);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSubtype;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location)
|
bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location)
|
||||||
{
|
{
|
||||||
NotNull<Scope> scope{findInnermostScope(location)};
|
NotNull<Scope> scope{findInnermostScope(location)};
|
||||||
|
|
|
@ -47,24 +47,14 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionImprovements)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexDeferPendingIndexee)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNoMoreInjectiveTypeFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -72,61 +62,6 @@ namespace Luau
|
||||||
|
|
||||||
using TypeOrTypePackIdSet = DenseHashSet<const void*>;
|
using TypeOrTypePackIdSet = DenseHashSet<const void*>;
|
||||||
|
|
||||||
struct InstanceCollector_DEPRECATED : TypeOnceVisitor
|
|
||||||
{
|
|
||||||
VecDeque<TypeId> tys;
|
|
||||||
VecDeque<TypePackId> tps;
|
|
||||||
TypeOrTypePackIdSet shouldGuess{nullptr};
|
|
||||||
std::vector<TypeId> cyclicInstance;
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
|
||||||
{
|
|
||||||
// TypeOnceVisitor performs a depth-first traversal in the absence of
|
|
||||||
// cycles. This means that by pushing to the front of the queue, we will
|
|
||||||
// try to reduce deeper instances first if we start with the first thing
|
|
||||||
// in the queue. Consider Add<Add<Add<number, number>, number>, number>:
|
|
||||||
// we want to reduce the innermost Add<number, number> instantiation
|
|
||||||
// first.
|
|
||||||
|
|
||||||
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFunctionDepth > DFInt::LuauTypeFamilyUseGuesserDepth)
|
|
||||||
shouldGuess.insert(ty);
|
|
||||||
|
|
||||||
tys.push_front(ty);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cycle(TypeId ty) override
|
|
||||||
{
|
|
||||||
/// Detected cyclic type pack
|
|
||||||
TypeId t = follow(ty);
|
|
||||||
if (get<TypeFunctionInstanceType>(t))
|
|
||||||
cyclicInstance.push_back(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const ExternType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
|
|
||||||
{
|
|
||||||
// TypeOnceVisitor performs a depth-first traversal in the absence of
|
|
||||||
// cycles. This means that by pushing to the front of the queue, we will
|
|
||||||
// try to reduce deeper instances first if we start with the first thing
|
|
||||||
// in the queue. Consider Add<Add<Add<number, number>, number>, number>:
|
|
||||||
// we want to reduce the innermost Add<number, number> instantiation
|
|
||||||
// first.
|
|
||||||
|
|
||||||
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFunctionDepth > DFInt::LuauTypeFamilyUseGuesserDepth)
|
|
||||||
shouldGuess.insert(tp);
|
|
||||||
|
|
||||||
tps.push_front(tp);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstanceCollector : TypeOnceVisitor
|
struct InstanceCollector : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> recordedTys{nullptr};
|
DenseHashSet<TypeId> recordedTys{nullptr};
|
||||||
|
@ -546,7 +481,7 @@ struct TypeFunctionReducer
|
||||||
|
|
||||||
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(subject))
|
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(subject))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2 && tfit->function->name == "user")
|
if (tfit->function->name == "user")
|
||||||
{
|
{
|
||||||
UnscopedGenericFinder finder;
|
UnscopedGenericFinder finder;
|
||||||
finder.traverse(subject);
|
finder.traverse(subject);
|
||||||
|
@ -673,8 +608,6 @@ static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
||||||
{
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
{
|
||||||
InstanceCollector collector;
|
InstanceCollector collector;
|
||||||
|
|
||||||
|
@ -700,37 +633,8 @@ FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location loc
|
||||||
force
|
force
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
InstanceCollector_DEPRECATED collector;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
collector.traverse(entrypoint);
|
|
||||||
}
|
|
||||||
catch (RecursionLimitException&)
|
|
||||||
{
|
|
||||||
return FunctionGraphReductionResult{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collector.tys.empty() && collector.tps.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return reduceFunctionsInternal(
|
|
||||||
std::move(collector.tys),
|
|
||||||
std::move(collector.tps),
|
|
||||||
std::move(collector.shouldGuess),
|
|
||||||
std::move(collector.cyclicInstance),
|
|
||||||
location,
|
|
||||||
ctx,
|
|
||||||
force
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
||||||
{
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
{
|
||||||
InstanceCollector collector;
|
InstanceCollector collector;
|
||||||
|
|
||||||
|
@ -756,33 +660,6 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location
|
||||||
force
|
force
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
InstanceCollector_DEPRECATED collector;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
collector.traverse(entrypoint);
|
|
||||||
}
|
|
||||||
catch (RecursionLimitException&)
|
|
||||||
{
|
|
||||||
return FunctionGraphReductionResult{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collector.tys.empty() && collector.tps.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return reduceFunctionsInternal(
|
|
||||||
std::move(collector.tys),
|
|
||||||
std::move(collector.tps),
|
|
||||||
std::move(collector.shouldGuess),
|
|
||||||
std::move(collector.cyclicInstance),
|
|
||||||
location,
|
|
||||||
ctx,
|
|
||||||
force
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPending(TypeId ty, ConstraintSolver* solver)
|
bool isPending(TypeId ty, ConstraintSolver* solver)
|
||||||
{
|
{
|
||||||
|
@ -934,11 +811,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
||||||
if (!ctx->typeFunctionRuntime->allowEvaluation || (FFlag::LuauTypeFunResultInAutocomplete && typeFunction->userFuncData.definition->hasErrors))
|
if (!ctx->typeFunctionRuntime->allowEvaluation || typeFunction->userFuncData.definition->hasErrors)
|
||||||
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
|
||||||
FindUserTypeFunctionBlockers check{ctx};
|
FindUserTypeFunctionBlockers check{ctx};
|
||||||
|
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
|
@ -946,24 +821,12 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
|
|
||||||
if (!check.blockingTypes.empty())
|
if (!check.blockingTypes.empty())
|
||||||
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto typeParam : typeParams)
|
|
||||||
{
|
|
||||||
TypeId ty = follow(typeParam);
|
|
||||||
|
|
||||||
// block if we need to
|
|
||||||
if (isPending(ty, ctx->solver))
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {ty}, {}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that whole type function environment is registered
|
// Ensure that whole type function environment is registered
|
||||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||||
{
|
{
|
||||||
// Cannot evaluate if a potential dependency couldn't be parsed
|
// Cannot evaluate if a potential dependency couldn't be parsed
|
||||||
if (FFlag::LuauTypeFunResultInAutocomplete && definition.first->hasErrors)
|
if (definition.first->hasErrors)
|
||||||
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
||||||
|
@ -1179,13 +1042,10 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
||||||
|
|
||||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, operandTy, "__len", Location{});
|
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, operandTy, "__len", Location{});
|
||||||
if (!mmType)
|
if (!mmType)
|
||||||
{
|
|
||||||
if (FFlag::LuauMetatablesHaveLength)
|
|
||||||
{
|
{
|
||||||
// If we have a metatable type with no __len, this means we still have a table with default length function
|
// If we have a metatable type with no __len, this means we still have a table with default length function
|
||||||
if (get<MetatableType>(normalizedOperand))
|
if (get<MetatableType>(normalizedOperand))
|
||||||
return {ctx->builtins->numberType, Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->numberType, Reduction::MaybeOk, {}, {}};
|
||||||
}
|
|
||||||
|
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
}
|
}
|
||||||
|
@ -1241,7 +1101,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
||||||
if (isPending(operandTy, ctx->solver))
|
if (isPending(operandTy, ctx->solver))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
operandTy = follow(operandTy);
|
operandTy = follow(operandTy);
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
||||||
|
@ -1322,7 +1182,7 @@ std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunc
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Do not evaluate type functions with parse errors inside
|
// Do not evaluate type functions with parse errors inside
|
||||||
if (FFlag::LuauTypeFunResultInAutocomplete && function->hasErrors)
|
if (function->hasErrors)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
prepareState();
|
prepareState();
|
||||||
|
@ -1918,12 +1778,12 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
|
||||||
emplaceType<BoundType>(asMutable(lhsTy), ctx->builtins->numberType);
|
emplaceType<BoundType>(asMutable(lhsTy), ctx->builtins->numberType);
|
||||||
else if (rhsFree && isNumber(lhsTy))
|
else if (rhsFree && isNumber(lhsTy))
|
||||||
emplaceType<BoundType>(asMutable(rhsTy), ctx->builtins->numberType);
|
emplaceType<BoundType>(asMutable(rhsTy), ctx->builtins->numberType);
|
||||||
else if (lhsFree && ctx->normalizer->isInhabited(rhsTy) != NormalizationResult::False)
|
else if (!FFlag::LuauNoMoreInjectiveTypeFunctions && lhsFree && ctx->normalizer->isInhabited(rhsTy) != NormalizationResult::False)
|
||||||
{
|
{
|
||||||
auto c1 = ctx->pushConstraint(EqualityConstraint{lhsTy, rhsTy});
|
auto c1 = ctx->pushConstraint(EqualityConstraint{lhsTy, rhsTy});
|
||||||
const_cast<Constraint*>(ctx->constraint)->dependencies.emplace_back(c1);
|
const_cast<Constraint*>(ctx->constraint)->dependencies.emplace_back(c1);
|
||||||
}
|
}
|
||||||
else if (rhsFree && ctx->normalizer->isInhabited(lhsTy) != NormalizationResult::False)
|
else if (!FFlag::LuauNoMoreInjectiveTypeFunctions && rhsFree && ctx->normalizer->isInhabited(lhsTy) != NormalizationResult::False)
|
||||||
{
|
{
|
||||||
auto c1 = ctx->pushConstraint(EqualityConstraint{rhsTy, lhsTy});
|
auto c1 = ctx->pushConstraint(EqualityConstraint{rhsTy, lhsTy});
|
||||||
const_cast<Constraint*>(ctx->constraint)->dependencies.emplace_back(c1);
|
const_cast<Constraint*>(ctx->constraint)->dependencies.emplace_back(c1);
|
||||||
|
@ -2325,8 +2185,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
if (!crt.found)
|
if (!crt.found)
|
||||||
return {target, {}};
|
return {target, {}};
|
||||||
|
|
||||||
if (FFlag::LuauSimplyRefineNotNil)
|
|
||||||
{
|
|
||||||
if (auto negation = get<NegationType>(discriminant))
|
if (auto negation = get<NegationType>(discriminant))
|
||||||
{
|
{
|
||||||
if (auto primitive = get<PrimitiveType>(follow(negation->ty)); primitive && primitive->type == PrimitiveType::NilType)
|
if (auto primitive = get<PrimitiveType>(follow(negation->ty)); primitive && primitive->type == PrimitiveType::NilType)
|
||||||
|
@ -2335,7 +2193,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
return {result.result, {}};
|
return {result.result, {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (FFlag::LuauOptimizeFalsyAndTruthyIntersect)
|
if (FFlag::LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
{
|
{
|
||||||
|
@ -2620,19 +2477,11 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauIntersectNotNil)
|
|
||||||
{
|
|
||||||
for (TypeId blockedType : result.blockedTypes)
|
for (TypeId blockedType : result.blockedTypes)
|
||||||
{
|
{
|
||||||
if (!get<GenericType>(blockedType))
|
if (!get<GenericType>(blockedType))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!result.blockedTypes.empty())
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
resultTy = result.result;
|
resultTy = result.result;
|
||||||
}
|
}
|
||||||
|
@ -2858,7 +2707,7 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
std::vector<TypeId> singletons;
|
std::vector<TypeId> singletons;
|
||||||
singletons.reserve(keys.size());
|
singletons.reserve(keys.size());
|
||||||
|
|
||||||
for (std::string key : keys)
|
for (const std::string& key : keys)
|
||||||
singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}}));
|
singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}}));
|
||||||
|
|
||||||
// If there's only one entry, we don't need a UnionType.
|
// If there's only one entry, we don't need a UnionType.
|
||||||
|
@ -2927,10 +2776,7 @@ bool searchPropsAndIndexer(
|
||||||
{
|
{
|
||||||
for (TypeId option : propUnionTy->options)
|
for (TypeId option : propUnionTy->options)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauIndexTypeFunctionImprovements)
|
|
||||||
result.insert(follow(option));
|
result.insert(follow(option));
|
||||||
else
|
|
||||||
result.insert(option);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // property is a singular type or intersection type -> we can simply append
|
else // property is a singular type or intersection type -> we can simply append
|
||||||
|
@ -2943,17 +2789,14 @@ bool searchPropsAndIndexer(
|
||||||
// index into tbl's indexer
|
// index into tbl's indexer
|
||||||
if (tblIndexer)
|
if (tblIndexer)
|
||||||
{
|
{
|
||||||
TypeId indexType = FFlag::LuauFixCyclicIndexInIndexer ? follow(tblIndexer->indexType) : tblIndexer->indexType;
|
TypeId indexType = follow(tblIndexer->indexType);
|
||||||
|
|
||||||
if (FFlag::LuauFixCyclicIndexInIndexer)
|
|
||||||
{
|
|
||||||
if (auto tfit = get<TypeFunctionInstanceType>(indexType))
|
if (auto tfit = get<TypeFunctionInstanceType>(indexType))
|
||||||
{
|
{
|
||||||
// if we have an index function here, it means we're in a cycle, so let's see if it's well-founded if we tie the knot
|
// if we have an index function here, it means we're in a cycle, so let's see if it's well-founded if we tie the knot
|
||||||
if (tfit->function.get() == &builtinTypeFunctions().indexFunc)
|
if (tfit->function.get() == &builtinTypeFunctions().indexFunc)
|
||||||
indexType = follow(tblIndexer->indexResultType);
|
indexType = follow(tblIndexer->indexResultType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isSubtype(ty, indexType, ctx->scope, ctx->builtins, ctx->simplifier, *ctx->ice))
|
if (isSubtype(ty, indexType, ctx->scope, ctx->builtins, ctx->simplifier, *ctx->ice))
|
||||||
{
|
{
|
||||||
|
@ -2964,10 +2807,7 @@ bool searchPropsAndIndexer(
|
||||||
{
|
{
|
||||||
for (TypeId option : idxResUnionTy->options)
|
for (TypeId option : idxResUnionTy->options)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauIndexTypeFunctionImprovements)
|
|
||||||
result.insert(follow(option));
|
result.insert(follow(option));
|
||||||
else
|
|
||||||
result.insert(option);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // indexResultType is a singular type or intersection type -> we can simply append
|
else // indexResultType is a singular type or intersection type -> we can simply append
|
||||||
|
@ -2980,46 +2820,6 @@ bool searchPropsAndIndexer(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handles recursion / metamethods of tables and extern types
|
|
||||||
`isRaw` parameter indicates whether or not we should follow __index metamethods
|
|
||||||
returns false if property of `ty` could not be found */
|
|
||||||
bool tblIndexInto_DEPRECATED(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
|
||||||
{
|
|
||||||
indexer = follow(indexer);
|
|
||||||
indexee = follow(indexee);
|
|
||||||
|
|
||||||
// we have a table type to try indexing
|
|
||||||
if (auto tableTy = get<TableType>(indexee))
|
|
||||||
{
|
|
||||||
return searchPropsAndIndexer(indexer, tableTy->props, tableTy->indexer, result, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have a metatable type to try indexing
|
|
||||||
if (auto metatableTy = get<MetatableType>(indexee))
|
|
||||||
{
|
|
||||||
if (auto tableTy = get<TableType>(metatableTy->table))
|
|
||||||
{
|
|
||||||
|
|
||||||
// try finding all properties within the current scope of the table
|
|
||||||
if (searchPropsAndIndexer(indexer, tableTy->props, tableTy->indexer, result, ctx))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the code reached here, it means we weren't able to find all properties -> look into __index metamethod
|
|
||||||
if (!isRaw)
|
|
||||||
{
|
|
||||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
|
||||||
// the necessary state to do that, even if we intend to just eat the errors.
|
|
||||||
ErrorVec dummy;
|
|
||||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, indexee, "__index", Location{});
|
|
||||||
if (mmType)
|
|
||||||
return tblIndexInto_DEPRECATED(indexer, *mmType, result, ctx, isRaw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tblIndexInto(
|
bool tblIndexInto(
|
||||||
TypeId indexer,
|
TypeId indexer,
|
||||||
TypeId indexee,
|
TypeId indexee,
|
||||||
|
@ -3036,8 +2836,6 @@ bool tblIndexInto(
|
||||||
return false;
|
return false;
|
||||||
seenSet.insert(indexee);
|
seenSet.insert(indexee);
|
||||||
|
|
||||||
if (FFlag::LuauIndexTypeFunctionFunctionMetamethods)
|
|
||||||
{
|
|
||||||
if (auto unionTy = get<UnionType>(indexee))
|
if (auto unionTy = get<UnionType>(indexee))
|
||||||
{
|
{
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
@ -3080,7 +2878,6 @@ bool tblIndexInto(
|
||||||
result.insert(follow(extracted.head.front()));
|
result.insert(follow(extracted.head.front()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// we have a table type to try indexing
|
// we have a table type to try indexing
|
||||||
if (auto tableTy = get<TableType>(indexee))
|
if (auto tableTy = get<TableType>(indexee))
|
||||||
|
@ -3115,17 +2912,10 @@ bool tblIndexInto(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
||||||
{
|
|
||||||
if (FFlag::LuauIndexTypeFunctionImprovements)
|
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> seenSet{{}};
|
DenseHashSet<TypeId> seenSet{{}};
|
||||||
return tblIndexInto(indexer, indexee, result, seenSet, ctx, isRaw);
|
return tblIndexInto(indexer, indexee, result, seenSet, ctx, isRaw);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return tblIndexInto_DEPRECATED(indexer, indexee, result, ctx, isRaw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Vocabulary note: indexee refers to the type that contains the properties,
|
/* Vocabulary note: indexee refers to the type that contains the properties,
|
||||||
indexer refers to the type that is used to access indexee
|
indexer refers to the type that is used to access indexee
|
||||||
|
@ -3139,7 +2929,7 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
{
|
{
|
||||||
TypeId indexeeTy = follow(typeParams.at(0));
|
TypeId indexeeTy = follow(typeParams.at(0));
|
||||||
|
|
||||||
if (FFlag::LuauIndexDeferPendingIndexee && isPending(indexeeTy, ctx->solver))
|
if (isPending(indexeeTy, ctx->solver))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {indexeeTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {indexeeTy}, {}};
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
|
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
|
||||||
|
@ -3148,12 +2938,9 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
if (!indexeeNormTy)
|
if (!indexeeNormTy)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauIndexAnyIsAny)
|
|
||||||
{
|
|
||||||
// if the indexee is `any`, then indexing also gives us `any`.
|
// if the indexee is `any`, then indexing also gives us `any`.
|
||||||
if (indexeeNormTy->shouldSuppressErrors())
|
if (indexeeNormTy->shouldSuppressErrors())
|
||||||
return {ctx->builtins->anyType, Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->anyType, Reduction::MaybeOk, {}, {}};
|
||||||
}
|
|
||||||
|
|
||||||
// if we don't have either just tables or just extern types, we've got nothing to index into
|
// if we don't have either just tables or just extern types, we've got nothing to index into
|
||||||
if (indexeeNormTy->hasTables() == indexeeNormTy->hasExternTypes())
|
if (indexeeNormTy->hasTables() == indexeeNormTy->hasExternTypes())
|
||||||
|
@ -3254,20 +3041,6 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FFlag::LuauIndexTypeFunctionImprovements)
|
|
||||||
{
|
|
||||||
// Call `follow()` on each element to resolve all Bound types before returning
|
|
||||||
std::transform(
|
|
||||||
properties.begin(),
|
|
||||||
properties.end(),
|
|
||||||
properties.begin(),
|
|
||||||
[](TypeId ty)
|
|
||||||
{
|
|
||||||
return follow(ty);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the type being reduced to is a single type, no need to union
|
// If the type being reduced to is a single type, no need to union
|
||||||
if (properties.size() == 1)
|
if (properties.size() == 1)
|
||||||
return {*properties.begin(), Reduction::MaybeOk, {}, {}};
|
return {*properties.begin(), Reduction::MaybeOk, {}, {}};
|
||||||
|
@ -3544,7 +3317,6 @@ TypeFunctionReductionResult<TypeId> weakoptionalTypeFunc(
|
||||||
return {targetTy, Reduction::MaybeOk, {}, {}};
|
return {targetTy, Reduction::MaybeOk, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
: userFunc{"user", userDefinedTypeFunction}
|
: userFunc{"user", userDefinedTypeFunction}
|
||||||
, notFunc{"not", notTypeFunction}
|
, notFunc{"not", notTypeFunction}
|
||||||
|
@ -3640,15 +3412,12 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
|
||||||
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunctionWithDefault(&rawgetFunc);
|
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunctionWithDefault(&rawgetFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauMetatableTypeFunctions)
|
|
||||||
{
|
|
||||||
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
|
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||||
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
|
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
|
||||||
else
|
else
|
||||||
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc);
|
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc);
|
||||||
scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc);
|
scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const BuiltinTypeFunctions& builtinTypeFunctions()
|
const BuiltinTypeFunctions& builtinTypeFunctions()
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1138,28 +1137,6 @@ static int getFunctionGenerics(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Luau: `self:parent() -> type`
|
|
||||||
// Returns the parent of a class type
|
|
||||||
static int getClassParent_DEPRECATED(lua_State* L)
|
|
||||||
{
|
|
||||||
int argumentCount = lua_gettop(L);
|
|
||||||
if (argumentCount != 1)
|
|
||||||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
|
||||||
|
|
||||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
|
||||||
auto tfct = get<TypeFunctionExternType>(self);
|
|
||||||
if (!tfct)
|
|
||||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
|
||||||
|
|
||||||
// If the parent does not exist, we should return nil
|
|
||||||
if (!tfct->parent_DEPRECATED)
|
|
||||||
lua_pushnil(L);
|
|
||||||
else
|
|
||||||
allocTypeUserData(L, (*tfct->parent_DEPRECATED)->type);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Luau: `self:readparent() -> type`
|
// Luau: `self:readparent() -> type`
|
||||||
// Returns the read type of the class' parent
|
// Returns the read type of the class' parent
|
||||||
static int getReadParent(lua_State* L)
|
static int getReadParent(lua_State* L)
|
||||||
|
@ -1628,7 +1605,8 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"components", getComponents},
|
{"components", getComponents},
|
||||||
|
|
||||||
// Extern type methods
|
// Extern type methods
|
||||||
{FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED},
|
{"readparent", getReadParent},
|
||||||
|
{"writeparent", getWriteParent},
|
||||||
|
|
||||||
// Function type methods (cont.)
|
// Function type methods (cont.)
|
||||||
{"setgenerics", setFunctionGenerics},
|
{"setgenerics", setFunctionGenerics},
|
||||||
|
@ -1638,9 +1616,6 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"name", getGenericName},
|
{"name", getGenericName},
|
||||||
{"ispack", getGenericIsPack},
|
{"ispack", getGenericIsPack},
|
||||||
|
|
||||||
// move this under extern type methods when removing FFlagLuauTypeFunReadWriteParents
|
|
||||||
{FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr},
|
|
||||||
|
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
// 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(LuauTypeFunReadWriteParents)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -211,7 +210,7 @@ private:
|
||||||
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the original
|
// 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
|
// class
|
||||||
target = typeFunctionRuntime->typeArena.allocate(
|
target = typeFunctionRuntime->typeArena.allocate(
|
||||||
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (auto g = get<GenericType>(ty))
|
else if (auto g = get<GenericType>(ty))
|
||||||
|
@ -436,17 +435,10 @@ private:
|
||||||
{
|
{
|
||||||
TypeFunctionTypeId parent = shallowSerialize(*c1->parent);
|
TypeFunctionTypeId parent = shallowSerialize(*c1->parent);
|
||||||
|
|
||||||
if (FFlag::LuauTypeFunReadWriteParents)
|
|
||||||
{
|
|
||||||
// we don't yet have read/write parents in the type inference engine.
|
// we don't yet have read/write parents in the type inference engine.
|
||||||
c2->readParent = parent;
|
c2->readParent = parent;
|
||||||
c2->writeParent = parent;
|
c2->writeParent = parent;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
c2->parent_DEPRECATED = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2)
|
void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2)
|
||||||
|
|
|
@ -35,7 +35,6 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1319,8 +1318,6 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
||||||
// and check them against the parameter types of the iterator function.
|
// and check them against the parameter types of the iterator function.
|
||||||
auto [types, tail] = flatten(callRetPack);
|
auto [types, tail] = flatten(callRetPack);
|
||||||
|
|
||||||
if (FFlag::LuauStatForInFix)
|
|
||||||
{
|
|
||||||
if (!types.empty())
|
if (!types.empty())
|
||||||
{
|
{
|
||||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
||||||
|
@ -1332,12 +1329,6 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
|
||||||
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Check if iterator function accepts 0 arguments
|
// Check if iterator function accepts 0 arguments
|
||||||
argPack = addTypePack(TypePack{});
|
argPack = addTypePack(TypePack{});
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypePackDetectCycles)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -149,7 +147,7 @@ TypePackIterator& TypePackIterator::operator++()
|
||||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||||
|
|
||||||
if (FFlag::LuauTypePackDetectCycles && tp)
|
if (tp)
|
||||||
{
|
{
|
||||||
// Step twice on each iteration to detect cycles
|
// Step twice on each iteration to detect cycles
|
||||||
tailCycleCheck = tp->tail ? log->follow(*tp->tail) : nullptr;
|
tailCycleCheck = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||||
|
|
|
@ -4,15 +4,17 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
|
#include "Luau/Simplify.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -305,7 +307,7 @@ TypePack extendTypePack(
|
||||||
TypePack newPack;
|
TypePack newPack;
|
||||||
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -434,6 +436,25 @@ TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty)
|
||||||
|
|
||||||
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauErrorSuppressionTypeFunctionArgs)
|
||||||
|
{
|
||||||
|
if (auto tfit = get<TypeFunctionInstanceType>(follow(ty)))
|
||||||
|
{
|
||||||
|
for (auto ty : tfit->typeArguments)
|
||||||
|
{
|
||||||
|
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
||||||
|
|
||||||
|
if (!normType)
|
||||||
|
return ErrorSuppression::NormalizationFailed;
|
||||||
|
|
||||||
|
if (normType->shouldSuppressErrors())
|
||||||
|
return ErrorSuppression::Suppress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorSuppression::DoNotSuppress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
||||||
|
|
||||||
if (!normType)
|
if (!normType)
|
||||||
|
@ -570,7 +591,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(tp);
|
LUAU_ASSERT(tp);
|
||||||
if (!FFlag::LuauNonReentrantGeneralization2)
|
if (!FFlag::LuauNonReentrantGeneralization3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (; scope; scope = scope->parent.get())
|
for (; scope; scope = scope->parent.get())
|
||||||
|
@ -587,4 +608,84 @@ void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
||||||
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypePacks` member.");
|
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypePacks` member.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fastIsSubtype(TypeId subTy, TypeId superTy)
|
||||||
|
{
|
||||||
|
Relation r = relate(superTy, subTy);
|
||||||
|
return r == Relation::Coincident || r == Relation::Superset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, TypeId exprType, NotNull<BuiltinTypes> builtinTypes)
|
||||||
|
{
|
||||||
|
if (tables.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const TableType* exprTable = get<TableType>(follow(exprType));
|
||||||
|
if (!exprTable)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
size_t tableCount = 0;
|
||||||
|
std::optional<TypeId> firstTable;
|
||||||
|
|
||||||
|
for (TypeId ty : tables)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
if (auto tt = get<TableType>(ty))
|
||||||
|
{
|
||||||
|
// If the expected table has a key whose type is a string or boolean
|
||||||
|
// singleton and the corresponding exprType property does not match,
|
||||||
|
// then skip this table.
|
||||||
|
|
||||||
|
if (!firstTable)
|
||||||
|
firstTable = ty;
|
||||||
|
++tableCount;
|
||||||
|
|
||||||
|
for (const auto& [name, expectedProp] : tt->props)
|
||||||
|
{
|
||||||
|
if (!expectedProp.readTy)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const TypeId expectedType = follow(*expectedProp.readTy);
|
||||||
|
|
||||||
|
auto st = get<SingletonType>(expectedType);
|
||||||
|
if (!st)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto it = exprTable->props.find(name);
|
||||||
|
if (it == exprTable->props.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto& [_name, exprProp] = *it;
|
||||||
|
|
||||||
|
if (!exprProp.readTy)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const TypeId propType = follow(*exprProp.readTy);
|
||||||
|
|
||||||
|
const FreeType* ft = get<FreeType>(propType);
|
||||||
|
|
||||||
|
if (ft && get<SingletonType>(ft->lowerBound))
|
||||||
|
{
|
||||||
|
if (fastIsSubtype(builtinTypes->booleanType, ft->upperBound) && fastIsSubtype(expectedType, builtinTypes->booleanType))
|
||||||
|
{
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fastIsSubtype(builtinTypes->stringType, ft->upperBound) && fastIsSubtype(expectedType, ft->lowerBound))
|
||||||
|
{
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tableCount == 1)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(firstTable);
|
||||||
|
return firstTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -329,7 +329,7 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
|
|
||||||
for (TypePackId genericPack : subFn->genericPacks)
|
for (TypePackId genericPack : subFn->genericPacks)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauGreedyGeneralization)
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
genericPack = follow(genericPack);
|
genericPack = follow(genericPack);
|
||||||
|
@ -454,6 +454,12 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
|
||||||
{
|
{
|
||||||
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
|
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
|
||||||
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
|
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
|
||||||
|
if (FFlag::LuauNonReentrantGeneralization3)
|
||||||
|
{
|
||||||
|
// FIXME: We can probably do something more efficient here.
|
||||||
|
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);
|
||||||
|
result &= unify(superTable->indexer->indexResultType, subTable->indexer->indexResultType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subTable->indexer && subTable->state == TableState::Unsealed && superTable->indexer)
|
if (!subTable->indexer && subTable->state == TableState::Unsealed && superTable->indexer)
|
||||||
|
|
|
@ -84,6 +84,6 @@ struct ParseExprResult
|
||||||
CstNodeMap cstNodeMap{nullptr};
|
CstNodeMap cstNodeMap{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr const char* kParseNameError = "%error-id%";
|
inline constexpr const char* kParseNameError = "%error-id%";
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -245,8 +245,6 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
||||||
// Remove with FFlagLuauStoreCSTData2
|
|
||||||
AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
|
||||||
|
|
||||||
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
|
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
|
||||||
AstType* parseFunctionTypeTail(
|
AstType* parseFunctionTypeTail(
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/RequireNavigator.h"
|
#include "Luau/RequireNavigator.h"
|
||||||
#include "Luau/RequirerUtils.h"
|
#include "Luau/VfsNavigator.h"
|
||||||
|
|
||||||
struct FileNavigationContext : Luau::Require::NavigationContext
|
struct FileNavigationContext : Luau::Require::NavigationContext
|
||||||
{
|
{
|
||||||
|
@ -13,23 +13,22 @@ struct FileNavigationContext : Luau::Require::NavigationContext
|
||||||
std::string getRequirerIdentifier() const override;
|
std::string getRequirerIdentifier() const override;
|
||||||
|
|
||||||
// Navigation interface
|
// Navigation interface
|
||||||
NavigateResult reset(const std::string& requirerChunkname) override;
|
NavigateResult reset(const std::string& identifier) override;
|
||||||
NavigateResult jumpToAlias(const std::string& path) override;
|
NavigateResult jumpToAlias(const std::string& path) override;
|
||||||
|
|
||||||
NavigateResult toParent() override;
|
NavigateResult toParent() override;
|
||||||
NavigateResult toChild(const std::string& component) override;
|
NavigateResult toChild(const std::string& component) override;
|
||||||
|
|
||||||
bool isConfigPresent() const override;
|
bool isConfigPresent() const override;
|
||||||
std::optional<std::string> getConfig() const override;
|
virtual ConfigBehavior getConfigBehavior() const override;
|
||||||
|
virtual std::optional<std::string> getAlias(const std::string& alias) const override;
|
||||||
|
virtual std::optional<std::string> getConfig() const override;
|
||||||
|
|
||||||
// Custom capabilities
|
// Custom capabilities
|
||||||
bool isModulePresent() const;
|
bool isModulePresent() const;
|
||||||
std::optional<std::string> getIdentifier() const;
|
std::optional<std::string> getIdentifier() const;
|
||||||
|
|
||||||
std::string path;
|
|
||||||
std::string suffix;
|
|
||||||
std::string requirerPath;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavigateResult storePathResult(PathResult result);
|
std::string requirerPath;
|
||||||
|
VfsNavigator vfs;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/Require.h"
|
#include "Luau/Require.h"
|
||||||
|
|
||||||
#include "Luau/Compiler.h"
|
#include "Luau/Compiler.h"
|
||||||
|
#include "Luau/VfsNavigator.h"
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
|
@ -29,7 +30,5 @@ struct ReplRequirer
|
||||||
BoolCheck codegenEnabled;
|
BoolCheck codegenEnabled;
|
||||||
Coverage coverageTrack;
|
Coverage coverageTrack;
|
||||||
|
|
||||||
std::string absPath;
|
VfsNavigator vfs;
|
||||||
std::string relPath;
|
|
||||||
std::string suffix;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
struct PathResult
|
|
||||||
{
|
|
||||||
enum class Status
|
|
||||||
{
|
|
||||||
SUCCESS,
|
|
||||||
AMBIGUOUS,
|
|
||||||
NOT_FOUND
|
|
||||||
};
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
std::string absPath;
|
|
||||||
std::string relPath;
|
|
||||||
std::string suffix;
|
|
||||||
};
|
|
||||||
|
|
||||||
PathResult getStdInResult();
|
|
||||||
|
|
||||||
PathResult getAbsolutePathResult(const std::string& path);
|
|
||||||
|
|
||||||
// If given an absolute path, this will implicitly call getAbsolutePathResult.
|
|
||||||
// Aliases prevent us from solely operating on relative paths, so we need to
|
|
||||||
// be able to fall back to operating on absolute paths if needed.
|
|
||||||
PathResult tryGetRelativePathResult(const std::string& path);
|
|
||||||
|
|
||||||
PathResult getParent(const std::string& absPath, const std::string& relPath);
|
|
||||||
PathResult getChild(const std::string& absPath, const std::string& relPath, const std::string& name);
|
|
||||||
|
|
||||||
bool isFilePresent(const std::string& path, const std::string& suffix);
|
|
||||||
std::optional<std::string> getFileContents(const std::string& path, const std::string& suffix);
|
|
35
CLI/include/Luau/VfsNavigator.h
Normal file
35
CLI/include/Luau/VfsNavigator.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum class NavigationStatus
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
Ambiguous,
|
||||||
|
NotFound
|
||||||
|
};
|
||||||
|
|
||||||
|
class VfsNavigator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NavigationStatus resetToStdIn();
|
||||||
|
NavigationStatus resetToPath(const std::string& path);
|
||||||
|
|
||||||
|
NavigationStatus toParent();
|
||||||
|
NavigationStatus toChild(const std::string& name);
|
||||||
|
|
||||||
|
std::string getFilePath() const;
|
||||||
|
std::string getAbsoluteFilePath() const;
|
||||||
|
std::string getLuaurcPath() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NavigationStatus updateRealPaths();
|
||||||
|
|
||||||
|
std::string realPath;
|
||||||
|
std::string absoluteRealPath;
|
||||||
|
std::string absolutePathPrefix;
|
||||||
|
|
||||||
|
std::string modulePath;
|
||||||
|
std::string absoluteModulePath;
|
||||||
|
};
|
|
@ -1,47 +1,25 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/AnalyzeRequirer.h"
|
#include "Luau/AnalyzeRequirer.h"
|
||||||
|
|
||||||
|
#include "Luau/FileUtils.h"
|
||||||
#include "Luau/RequireNavigator.h"
|
#include "Luau/RequireNavigator.h"
|
||||||
#include "Luau/RequirerUtils.h"
|
#include "Luau/VfsNavigator.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::storePathResult(PathResult result)
|
static Luau::Require::NavigationContext::NavigateResult convert(NavigationStatus status)
|
||||||
{
|
{
|
||||||
if (result.status == PathResult::Status::AMBIGUOUS)
|
if (status == NavigationStatus::Success)
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Ambiguous;
|
|
||||||
|
|
||||||
if (result.status == PathResult::Status::NOT_FOUND)
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::NotFound;
|
|
||||||
|
|
||||||
path = result.absPath;
|
|
||||||
suffix = result.suffix;
|
|
||||||
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Success;
|
return Luau::Require::NavigationContext::NavigateResult::Success;
|
||||||
|
else if (status == NavigationStatus::Ambiguous)
|
||||||
|
return Luau::Require::NavigationContext::NavigateResult::Ambiguous;
|
||||||
|
else
|
||||||
|
return Luau::Require::NavigationContext::NavigateResult::NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileNavigationContext::FileNavigationContext(std::string requirerPath)
|
FileNavigationContext::FileNavigationContext(std::string requirerPath)
|
||||||
|
: requirerPath(std::move(requirerPath))
|
||||||
{
|
{
|
||||||
std::string_view path = requirerPath;
|
|
||||||
if (path.size() >= 10 && path.substr(path.size() - 10) == "/init.luau")
|
|
||||||
{
|
|
||||||
path.remove_suffix(10);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 9 && path.substr(path.size() - 9) == "/init.lua")
|
|
||||||
{
|
|
||||||
path.remove_suffix(9);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 5 && path.substr(path.size() - 5) == ".luau")
|
|
||||||
{
|
|
||||||
path.remove_suffix(5);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 4 && path.substr(path.size() - 4) == ".lua")
|
|
||||||
{
|
|
||||||
path.remove_suffix(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->requirerPath = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileNavigationContext::getRequirerIdentifier() const
|
std::string FileNavigationContext::getRequirerIdentifier() const
|
||||||
|
@ -49,51 +27,58 @@ std::string FileNavigationContext::getRequirerIdentifier() const
|
||||||
return requirerPath;
|
return requirerPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::reset(const std::string& requirerChunkname)
|
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::reset(const std::string& identifier)
|
||||||
{
|
{
|
||||||
if (requirerChunkname == "-")
|
if (identifier == "-")
|
||||||
{
|
return convert(vfs.resetToStdIn());
|
||||||
return storePathResult(getStdInResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
return storePathResult(tryGetRelativePathResult(requirerChunkname));
|
return convert(vfs.resetToPath(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::jumpToAlias(const std::string& path)
|
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::jumpToAlias(const std::string& path)
|
||||||
{
|
{
|
||||||
Luau::Require::NavigationContext::NavigateResult result = storePathResult(getAbsolutePathResult(path));
|
if (!isAbsolutePath(path))
|
||||||
if (result != Luau::Require::NavigationContext::NavigateResult::Success)
|
return Luau::Require::NavigationContext::NavigateResult::NotFound;
|
||||||
return result;
|
|
||||||
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Success;
|
return convert(vfs.resetToPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toParent()
|
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toParent()
|
||||||
{
|
{
|
||||||
return storePathResult(getParent(path, path));
|
return convert(vfs.toParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toChild(const std::string& component)
|
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toChild(const std::string& component)
|
||||||
{
|
{
|
||||||
return storePathResult(getChild(path, path, component));
|
return convert(vfs.toChild(component));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileNavigationContext::isModulePresent() const
|
bool FileNavigationContext::isModulePresent() const
|
||||||
{
|
{
|
||||||
return isFilePresent(path, suffix);
|
return isFile(vfs.getAbsoluteFilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> FileNavigationContext::getIdentifier() const
|
std::optional<std::string> FileNavigationContext::getIdentifier() const
|
||||||
{
|
{
|
||||||
return path + suffix;
|
return vfs.getAbsoluteFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileNavigationContext::isConfigPresent() const
|
bool FileNavigationContext::isConfigPresent() const
|
||||||
{
|
{
|
||||||
return isFilePresent(path, "/.luaurc");
|
return isFile(vfs.getLuaurcPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
Luau::Require::NavigationContext::ConfigBehavior FileNavigationContext::getConfigBehavior() const
|
||||||
|
{
|
||||||
|
return Luau::Require::NavigationContext::ConfigBehavior::GetConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> FileNavigationContext::getAlias(const std::string& alias) const
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> FileNavigationContext::getConfig() const
|
std::optional<std::string> FileNavigationContext::getConfig() const
|
||||||
{
|
{
|
||||||
return getFileContents(path, "/.luaurc");
|
return readFile(vfs.getLuaurcPath());
|
||||||
}
|
}
|
||||||
|
|
|
@ -581,14 +581,7 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
|
||||||
// new thread needs to have the globals sandboxed
|
// new thread needs to have the globals sandboxed
|
||||||
luaL_sandboxthread(L);
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
// ignore file extension when storing module's chunkname
|
std::string chunkname = "@" + normalizePath(name);
|
||||||
std::string chunkname = "@";
|
|
||||||
std::string_view nameView = name;
|
|
||||||
if (size_t dotPos = nameView.find_last_of('.'); dotPos != std::string_view::npos)
|
|
||||||
{
|
|
||||||
nameView.remove_suffix(nameView.size() - dotPos);
|
|
||||||
}
|
|
||||||
chunkname += nameView;
|
|
||||||
|
|
||||||
std::string bytecode = Luau::compile(*source, copts());
|
std::string bytecode = Luau::compile(*source, copts());
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include "Luau/CodeGenOptions.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/FileUtils.h"
|
#include "Luau/FileUtils.h"
|
||||||
#include "Luau/Require.h"
|
#include "Luau/Require.h"
|
||||||
|
#include "Luau/VfsNavigator.h"
|
||||||
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
|
||||||
|
@ -32,19 +32,14 @@ static luarequire_WriteResult write(std::optional<std::string> contents, char* b
|
||||||
return luarequire_WriteResult::WRITE_SUCCESS;
|
return luarequire_WriteResult::WRITE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_NavigateResult storePathResult(ReplRequirer* req, PathResult result)
|
static luarequire_NavigateResult convert(NavigationStatus status)
|
||||||
{
|
{
|
||||||
if (result.status == PathResult::Status::AMBIGUOUS)
|
if (status == NavigationStatus::Success)
|
||||||
return NAVIGATE_AMBIGUOUS;
|
|
||||||
|
|
||||||
if (result.status == PathResult::Status::NOT_FOUND)
|
|
||||||
return NAVIGATE_NOT_FOUND;
|
|
||||||
|
|
||||||
req->absPath = result.absPath;
|
|
||||||
req->relPath = result.relPath;
|
|
||||||
req->suffix = result.suffix;
|
|
||||||
|
|
||||||
return NAVIGATE_SUCCESS;
|
return NAVIGATE_SUCCESS;
|
||||||
|
else if (status == NavigationStatus::Ambiguous)
|
||||||
|
return NAVIGATE_AMBIGUOUS;
|
||||||
|
else
|
||||||
|
return NAVIGATE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_require_allowed(lua_State* L, void* ctx, const char* requirer_chunkname)
|
static bool is_require_allowed(lua_State* L, void* ctx, const char* requirer_chunkname)
|
||||||
|
@ -59,13 +54,9 @@ static luarequire_NavigateResult reset(lua_State* L, void* ctx, const char* requ
|
||||||
|
|
||||||
std::string chunkname = requirer_chunkname;
|
std::string chunkname = requirer_chunkname;
|
||||||
if (chunkname == "=stdin")
|
if (chunkname == "=stdin")
|
||||||
{
|
return convert(req->vfs.resetToStdIn());
|
||||||
return storePathResult(req, getStdInResult());
|
|
||||||
}
|
|
||||||
else if (!chunkname.empty() && chunkname[0] == '@')
|
else if (!chunkname.empty() && chunkname[0] == '@')
|
||||||
{
|
return convert(req->vfs.resetToPath(chunkname.substr(1)));
|
||||||
return storePathResult(req, tryGetRelativePathResult(chunkname.substr(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NAVIGATE_NOT_FOUND;
|
return NAVIGATE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -74,62 +65,58 @@ static luarequire_NavigateResult jump_to_alias(lua_State* L, void* ctx, const ch
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
|
|
||||||
luarequire_NavigateResult result = storePathResult(req, getAbsolutePathResult(path));
|
if (!isAbsolutePath(path))
|
||||||
if (result != NAVIGATE_SUCCESS)
|
return NAVIGATE_NOT_FOUND;
|
||||||
return result;
|
|
||||||
|
|
||||||
// Jumping to an absolute path breaks the relative-require chain. The best
|
return convert(req->vfs.resetToPath(path));
|
||||||
// we can do is to store the absolute path itself.
|
|
||||||
req->relPath = req->absPath;
|
|
||||||
return NAVIGATE_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_NavigateResult to_parent(lua_State* L, void* ctx)
|
static luarequire_NavigateResult to_parent(lua_State* L, void* ctx)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return storePathResult(req, getParent(req->absPath, req->relPath));
|
return convert(req->vfs.toParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_NavigateResult to_child(lua_State* L, void* ctx, const char* name)
|
static luarequire_NavigateResult to_child(lua_State* L, void* ctx, const char* name)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return storePathResult(req, getChild(req->absPath, req->relPath, name));
|
return convert(req->vfs.toChild(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_module_present(lua_State* L, void* ctx)
|
static bool is_module_present(lua_State* L, void* ctx)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return isFilePresent(req->absPath, req->suffix);
|
return isFile(req->vfs.getFilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_WriteResult get_chunkname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
static luarequire_WriteResult get_chunkname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return write("@" + req->relPath, buffer, buffer_size, size_out);
|
return write("@" + req->vfs.getFilePath(), buffer, buffer_size, size_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_WriteResult get_loadname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
static luarequire_WriteResult get_loadname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return write(req->absPath + req->suffix, buffer, buffer_size, size_out);
|
return write(req->vfs.getAbsoluteFilePath(), buffer, buffer_size, size_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_WriteResult get_cache_key(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
static luarequire_WriteResult get_cache_key(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return write(req->absPath + req->suffix, buffer, buffer_size, size_out);
|
return write(req->vfs.getAbsoluteFilePath(), buffer, buffer_size, size_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_config_present(lua_State* L, void* ctx)
|
static bool is_config_present(lua_State* L, void* ctx)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return isFilePresent(req->absPath, "/.luaurc");
|
return isFile(req->vfs.getLuaurcPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
return write(getFileContents(req->absPath, "/.luaurc"), buffer, buffer_size, size_out);
|
return write(readFile(req->vfs.getLuaurcPath()), buffer, buffer_size, size_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname)
|
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname)
|
||||||
|
@ -145,13 +132,26 @@ static int load(lua_State* L, void* ctx, const char* path, const char* chunkname
|
||||||
// new thread needs to have the globals sandboxed
|
// new thread needs to have the globals sandboxed
|
||||||
luaL_sandboxthread(ML);
|
luaL_sandboxthread(ML);
|
||||||
|
|
||||||
std::optional<std::string> contents = readFile(loadname);
|
bool hadContents = false;
|
||||||
if (!contents)
|
int status = LUA_OK;
|
||||||
luaL_error(L, "could not read file '%s'", loadname);
|
|
||||||
|
|
||||||
|
// Handle C++ RAII objects in a scope which doesn't cause a Luau error
|
||||||
|
{
|
||||||
|
std::optional<std::string> contents = readFile(loadname);
|
||||||
|
hadContents = contents.has_value();
|
||||||
|
|
||||||
|
if (contents)
|
||||||
|
{
|
||||||
// now we can compile & run module on the new thread
|
// now we can compile & run module on the new thread
|
||||||
std::string bytecode = Luau::compile(*contents, req->copts());
|
std::string bytecode = Luau::compile(*contents, req->copts());
|
||||||
if (luau_load(ML, chunkname, bytecode.data(), bytecode.size(), 0) == 0)
|
status = luau_load(ML, chunkname, bytecode.data(), bytecode.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hadContents)
|
||||||
|
luaL_error(L, "could not read file '%s'", loadname);
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
{
|
{
|
||||||
if (req->codegenEnabled())
|
if (req->codegenEnabled())
|
||||||
{
|
{
|
||||||
|
@ -208,6 +208,7 @@ void requireConfigInit(luarequire_Configuration* config)
|
||||||
config->get_chunkname = get_chunkname;
|
config->get_chunkname = get_chunkname;
|
||||||
config->get_loadname = get_loadname;
|
config->get_loadname = get_loadname;
|
||||||
config->get_cache_key = get_cache_key;
|
config->get_cache_key = get_cache_key;
|
||||||
|
config->get_alias = nullptr;
|
||||||
config->get_config = get_config;
|
config->get_config = get_config;
|
||||||
config->load = load;
|
config->load = load;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
|
|
||||||
#include "Luau/FileUtils.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
static std::pair<PathResult::Status, std::string> getSuffixWithAmbiguityCheck(const std::string& path)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
std::string suffix;
|
|
||||||
|
|
||||||
for (const char* potentialSuffix : {".luau", ".lua"})
|
|
||||||
{
|
|
||||||
if (isFile(path + potentialSuffix))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
suffix = potentialSuffix;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isDirectory(path))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
for (const char* potentialSuffix : {"/init.luau", "/init.lua"})
|
|
||||||
{
|
|
||||||
if (isFile(path + potentialSuffix))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
suffix = potentialSuffix;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return {PathResult::Status::NOT_FOUND, ""};
|
|
||||||
|
|
||||||
return {PathResult::Status::SUCCESS, suffix};
|
|
||||||
}
|
|
||||||
|
|
||||||
static PathResult addSuffix(PathResult partialResult)
|
|
||||||
{
|
|
||||||
if (partialResult.status != PathResult::Status::SUCCESS)
|
|
||||||
return partialResult;
|
|
||||||
|
|
||||||
auto [status, suffix] = getSuffixWithAmbiguityCheck(partialResult.absPath);
|
|
||||||
if (status != PathResult::Status::SUCCESS)
|
|
||||||
return PathResult{status};
|
|
||||||
|
|
||||||
partialResult.suffix = std::move(suffix);
|
|
||||||
return partialResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getStdInResult()
|
|
||||||
{
|
|
||||||
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
|
||||||
if (!cwd)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
std::replace(cwd->begin(), cwd->end(), '\\', '/');
|
|
||||||
|
|
||||||
return PathResult{PathResult::Status::SUCCESS, *cwd + "/stdin", "./stdin", ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getAbsolutePathResult(const std::string& path)
|
|
||||||
{
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, path});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult tryGetRelativePathResult(const std::string& path)
|
|
||||||
{
|
|
||||||
if (isAbsolutePath(path))
|
|
||||||
return getAbsolutePathResult(path);
|
|
||||||
|
|
||||||
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
|
||||||
if (!cwd)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
std::optional<std::string> resolvedAbsPath = resolvePath(path, *cwd + "/stdin");
|
|
||||||
if (!resolvedAbsPath)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, std::move(*resolvedAbsPath), path});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getParent(const std::string& absPath, const std::string& relPath)
|
|
||||||
{
|
|
||||||
std::optional<std::string> parent = getParentPath(absPath);
|
|
||||||
if (!parent)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, *parent, normalizePath(relPath + "/..")});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getChild(const std::string& absPath, const std::string& relPath, const std::string& name)
|
|
||||||
{
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, joinPaths(absPath, name), joinPaths(relPath, name)});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFilePresent(const std::string& path, const std::string& suffix)
|
|
||||||
{
|
|
||||||
return isFile(path + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> getFileContents(const std::string& path, const std::string& suffix)
|
|
||||||
{
|
|
||||||
return readFile(path + suffix);
|
|
||||||
}
|
|
234
CLI/src/VfsNavigator.cpp
Normal file
234
CLI/src/VfsNavigator.cpp
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include "Luau/VfsNavigator.h"
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/FileUtils.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
const std::array<std::string_view, 2> kSuffixes = {".luau", ".lua"};
|
||||||
|
const std::array<std::string_view, 2> kInitSuffixes = {"/init.luau", "/init.lua"};
|
||||||
|
|
||||||
|
struct ResolvedRealPath
|
||||||
|
{
|
||||||
|
NavigationStatus status;
|
||||||
|
std::string realPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ResolvedRealPath getRealPath(std::string modulePath)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
std::string suffix;
|
||||||
|
|
||||||
|
size_t lastSlash = modulePath.find_last_of('/');
|
||||||
|
LUAU_ASSERT(lastSlash != std::string::npos);
|
||||||
|
std::string lastComponent = modulePath.substr(lastSlash + 1);
|
||||||
|
|
||||||
|
if (lastComponent != "init")
|
||||||
|
{
|
||||||
|
for (std::string_view potentialSuffix : kSuffixes)
|
||||||
|
{
|
||||||
|
if (isFile(modulePath + std::string(potentialSuffix)))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
return {NavigationStatus::Ambiguous};
|
||||||
|
|
||||||
|
suffix = potentialSuffix;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDirectory(modulePath))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
return {NavigationStatus::Ambiguous};
|
||||||
|
|
||||||
|
for (std::string_view potentialSuffix : kInitSuffixes)
|
||||||
|
{
|
||||||
|
if (isFile(modulePath + std::string(potentialSuffix)))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
return {NavigationStatus::Ambiguous};
|
||||||
|
|
||||||
|
suffix = potentialSuffix;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return {NavigationStatus::NotFound};
|
||||||
|
|
||||||
|
return {NavigationStatus::Success, modulePath + suffix};
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasSuffix(std::string_view str, std::string_view suffix)
|
||||||
|
{
|
||||||
|
return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getModulePath(std::string filePath)
|
||||||
|
{
|
||||||
|
for (char& c : filePath)
|
||||||
|
{
|
||||||
|
if (c == '\\')
|
||||||
|
c = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view pathView = filePath;
|
||||||
|
|
||||||
|
if (isAbsolutePath(pathView))
|
||||||
|
{
|
||||||
|
size_t firstSlash = pathView.find_first_of('/');
|
||||||
|
LUAU_ASSERT(firstSlash != std::string::npos);
|
||||||
|
pathView.remove_prefix(firstSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::string_view suffix : kInitSuffixes)
|
||||||
|
{
|
||||||
|
if (hasSuffix(pathView, suffix))
|
||||||
|
{
|
||||||
|
pathView.remove_suffix(suffix.size());
|
||||||
|
return std::string(pathView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (std::string_view suffix : kSuffixes)
|
||||||
|
{
|
||||||
|
if (hasSuffix(pathView, suffix))
|
||||||
|
{
|
||||||
|
pathView.remove_suffix(suffix.size());
|
||||||
|
return std::string(pathView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(pathView);
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationStatus VfsNavigator::updateRealPaths()
|
||||||
|
{
|
||||||
|
ResolvedRealPath result = getRealPath(modulePath);
|
||||||
|
ResolvedRealPath absoluteResult = getRealPath(absoluteModulePath);
|
||||||
|
if (result.status != NavigationStatus::Success || absoluteResult.status != NavigationStatus::Success)
|
||||||
|
return result.status;
|
||||||
|
|
||||||
|
realPath = isAbsolutePath(result.realPath) ? absolutePathPrefix + result.realPath : result.realPath;
|
||||||
|
absoluteRealPath = absolutePathPrefix + absoluteResult.realPath;
|
||||||
|
return NavigationStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationStatus VfsNavigator::resetToStdIn()
|
||||||
|
{
|
||||||
|
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
||||||
|
if (!cwd)
|
||||||
|
return NavigationStatus::NotFound;
|
||||||
|
|
||||||
|
realPath = "./stdin";
|
||||||
|
absoluteRealPath = normalizePath(*cwd + "/stdin");
|
||||||
|
modulePath = "./stdin";
|
||||||
|
absoluteModulePath = getModulePath(absoluteRealPath);
|
||||||
|
|
||||||
|
size_t firstSlash = absoluteRealPath.find_first_of('/');
|
||||||
|
LUAU_ASSERT(firstSlash != std::string::npos);
|
||||||
|
absolutePathPrefix = absoluteRealPath.substr(0, firstSlash);
|
||||||
|
|
||||||
|
return NavigationStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationStatus VfsNavigator::resetToPath(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string normalizedPath = normalizePath(path);
|
||||||
|
|
||||||
|
if (isAbsolutePath(normalizedPath))
|
||||||
|
{
|
||||||
|
modulePath = getModulePath(normalizedPath);
|
||||||
|
absoluteModulePath = modulePath;
|
||||||
|
|
||||||
|
size_t firstSlash = normalizedPath.find_first_of('/');
|
||||||
|
LUAU_ASSERT(firstSlash != std::string::npos);
|
||||||
|
absolutePathPrefix = normalizedPath.substr(0, firstSlash);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
||||||
|
if (!cwd)
|
||||||
|
return NavigationStatus::NotFound;
|
||||||
|
|
||||||
|
modulePath = getModulePath(normalizedPath);
|
||||||
|
std::string joinedPath = normalizePath(*cwd + "/" + normalizedPath);
|
||||||
|
absoluteModulePath = getModulePath(joinedPath);
|
||||||
|
|
||||||
|
size_t firstSlash = joinedPath.find_first_of('/');
|
||||||
|
LUAU_ASSERT(firstSlash != std::string::npos);
|
||||||
|
absolutePathPrefix = joinedPath.substr(0, firstSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateRealPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationStatus VfsNavigator::toParent()
|
||||||
|
{
|
||||||
|
if (absoluteModulePath == "/")
|
||||||
|
return NavigationStatus::NotFound;
|
||||||
|
|
||||||
|
size_t numSlashes = 0;
|
||||||
|
for (char c : absoluteModulePath)
|
||||||
|
{
|
||||||
|
if (c == '/')
|
||||||
|
numSlashes++;
|
||||||
|
}
|
||||||
|
LUAU_ASSERT(numSlashes > 0);
|
||||||
|
|
||||||
|
if (numSlashes == 1)
|
||||||
|
return NavigationStatus::NotFound;
|
||||||
|
|
||||||
|
modulePath = normalizePath(modulePath + "/..");
|
||||||
|
absoluteModulePath = normalizePath(absoluteModulePath + "/..");
|
||||||
|
|
||||||
|
return updateRealPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationStatus VfsNavigator::toChild(const std::string& name)
|
||||||
|
{
|
||||||
|
modulePath = normalizePath(modulePath + "/" + name);
|
||||||
|
absoluteModulePath = normalizePath(absoluteModulePath + "/" + name);
|
||||||
|
|
||||||
|
return updateRealPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VfsNavigator::getFilePath() const
|
||||||
|
{
|
||||||
|
return realPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VfsNavigator::getAbsoluteFilePath() const
|
||||||
|
{
|
||||||
|
return absoluteRealPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VfsNavigator::getLuaurcPath() const
|
||||||
|
{
|
||||||
|
std::string_view directory = realPath;
|
||||||
|
|
||||||
|
for (std::string_view suffix : kInitSuffixes)
|
||||||
|
{
|
||||||
|
if (hasSuffix(directory, suffix))
|
||||||
|
{
|
||||||
|
directory.remove_suffix(suffix.size());
|
||||||
|
return std::string(directory) + "/.luaurc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (std::string_view suffix : kSuffixes)
|
||||||
|
{
|
||||||
|
if (hasSuffix(directory, suffix))
|
||||||
|
{
|
||||||
|
directory.remove_suffix(suffix.size());
|
||||||
|
return std::string(directory) + "/.luaurc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(directory) + "/.luaurc";
|
||||||
|
}
|
|
@ -832,7 +832,7 @@ enum class IrOpKind : uint32_t
|
||||||
|
|
||||||
// VmExit uses a special value to indicate that pcpos update should be skipped
|
// VmExit uses a special value to indicate that pcpos update should be skipped
|
||||||
// This is only used during type checking at function entry
|
// This is only used during type checking at function entry
|
||||||
constexpr uint32_t kVmExitEntryGuardPc = (1u << 28) - 1;
|
inline constexpr uint32_t kVmExitEntryGuardPc = (1u << 28) - 1;
|
||||||
|
|
||||||
struct IrOp
|
struct IrOp
|
||||||
{
|
{
|
||||||
|
@ -900,7 +900,7 @@ struct IrInst
|
||||||
};
|
};
|
||||||
|
|
||||||
// When IrInst operands are used, current instruction index is often required to track lifetime
|
// When IrInst operands are used, current instruction index is often required to track lifetime
|
||||||
constexpr uint32_t kInvalidInstIdx = ~0u;
|
inline constexpr uint32_t kInvalidInstIdx = ~0u;
|
||||||
|
|
||||||
struct IrInstHash
|
struct IrInstHash
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,13 +72,13 @@ struct OperandX64
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr OperandX64 addr{SizeX64::none, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 addr{SizeX64::none, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 byte{SizeX64::byte, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 byte{SizeX64::byte, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 word{SizeX64::word, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 word{SizeX64::word, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 dword{SizeX64::dword, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 dword{SizeX64::dword, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 qword{SizeX64::qword, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 qword{SizeX64::qword, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 xmmword{SizeX64::xmmword, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 xmmword{SizeX64::xmmword, noreg, 1, noreg, 0};
|
||||||
constexpr OperandX64 ymmword{SizeX64::ymmword, noreg, 1, noreg, 0};
|
inline constexpr OperandX64 ymmword{SizeX64::ymmword, noreg, 1, noreg, 0};
|
||||||
|
|
||||||
constexpr OperandX64 operator*(RegisterX64 reg, uint8_t scale)
|
constexpr OperandX64 operator*(RegisterX64 reg, uint8_t scale)
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,174 +47,174 @@ constexpr RegisterA64 castReg(KindA64 kind, RegisterA64 reg)
|
||||||
return RegisterA64{kind, reg.index};
|
return RegisterA64{kind, reg.index};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr RegisterA64 noreg{KindA64::none, 0};
|
inline constexpr RegisterA64 noreg{KindA64::none, 0};
|
||||||
|
|
||||||
constexpr RegisterA64 w0{KindA64::w, 0};
|
inline constexpr RegisterA64 w0{KindA64::w, 0};
|
||||||
constexpr RegisterA64 w1{KindA64::w, 1};
|
inline constexpr RegisterA64 w1{KindA64::w, 1};
|
||||||
constexpr RegisterA64 w2{KindA64::w, 2};
|
inline constexpr RegisterA64 w2{KindA64::w, 2};
|
||||||
constexpr RegisterA64 w3{KindA64::w, 3};
|
inline constexpr RegisterA64 w3{KindA64::w, 3};
|
||||||
constexpr RegisterA64 w4{KindA64::w, 4};
|
inline constexpr RegisterA64 w4{KindA64::w, 4};
|
||||||
constexpr RegisterA64 w5{KindA64::w, 5};
|
inline constexpr RegisterA64 w5{KindA64::w, 5};
|
||||||
constexpr RegisterA64 w6{KindA64::w, 6};
|
inline constexpr RegisterA64 w6{KindA64::w, 6};
|
||||||
constexpr RegisterA64 w7{KindA64::w, 7};
|
inline constexpr RegisterA64 w7{KindA64::w, 7};
|
||||||
constexpr RegisterA64 w8{KindA64::w, 8};
|
inline constexpr RegisterA64 w8{KindA64::w, 8};
|
||||||
constexpr RegisterA64 w9{KindA64::w, 9};
|
inline constexpr RegisterA64 w9{KindA64::w, 9};
|
||||||
constexpr RegisterA64 w10{KindA64::w, 10};
|
inline constexpr RegisterA64 w10{KindA64::w, 10};
|
||||||
constexpr RegisterA64 w11{KindA64::w, 11};
|
inline constexpr RegisterA64 w11{KindA64::w, 11};
|
||||||
constexpr RegisterA64 w12{KindA64::w, 12};
|
inline constexpr RegisterA64 w12{KindA64::w, 12};
|
||||||
constexpr RegisterA64 w13{KindA64::w, 13};
|
inline constexpr RegisterA64 w13{KindA64::w, 13};
|
||||||
constexpr RegisterA64 w14{KindA64::w, 14};
|
inline constexpr RegisterA64 w14{KindA64::w, 14};
|
||||||
constexpr RegisterA64 w15{KindA64::w, 15};
|
inline constexpr RegisterA64 w15{KindA64::w, 15};
|
||||||
constexpr RegisterA64 w16{KindA64::w, 16};
|
inline constexpr RegisterA64 w16{KindA64::w, 16};
|
||||||
constexpr RegisterA64 w17{KindA64::w, 17};
|
inline constexpr RegisterA64 w17{KindA64::w, 17};
|
||||||
constexpr RegisterA64 w18{KindA64::w, 18};
|
inline constexpr RegisterA64 w18{KindA64::w, 18};
|
||||||
constexpr RegisterA64 w19{KindA64::w, 19};
|
inline constexpr RegisterA64 w19{KindA64::w, 19};
|
||||||
constexpr RegisterA64 w20{KindA64::w, 20};
|
inline constexpr RegisterA64 w20{KindA64::w, 20};
|
||||||
constexpr RegisterA64 w21{KindA64::w, 21};
|
inline constexpr RegisterA64 w21{KindA64::w, 21};
|
||||||
constexpr RegisterA64 w22{KindA64::w, 22};
|
inline constexpr RegisterA64 w22{KindA64::w, 22};
|
||||||
constexpr RegisterA64 w23{KindA64::w, 23};
|
inline constexpr RegisterA64 w23{KindA64::w, 23};
|
||||||
constexpr RegisterA64 w24{KindA64::w, 24};
|
inline constexpr RegisterA64 w24{KindA64::w, 24};
|
||||||
constexpr RegisterA64 w25{KindA64::w, 25};
|
inline constexpr RegisterA64 w25{KindA64::w, 25};
|
||||||
constexpr RegisterA64 w26{KindA64::w, 26};
|
inline constexpr RegisterA64 w26{KindA64::w, 26};
|
||||||
constexpr RegisterA64 w27{KindA64::w, 27};
|
inline constexpr RegisterA64 w27{KindA64::w, 27};
|
||||||
constexpr RegisterA64 w28{KindA64::w, 28};
|
inline constexpr RegisterA64 w28{KindA64::w, 28};
|
||||||
constexpr RegisterA64 w29{KindA64::w, 29};
|
inline constexpr RegisterA64 w29{KindA64::w, 29};
|
||||||
constexpr RegisterA64 w30{KindA64::w, 30};
|
inline constexpr RegisterA64 w30{KindA64::w, 30};
|
||||||
constexpr RegisterA64 wzr{KindA64::w, 31};
|
inline constexpr RegisterA64 wzr{KindA64::w, 31};
|
||||||
|
|
||||||
constexpr RegisterA64 x0{KindA64::x, 0};
|
inline constexpr RegisterA64 x0{KindA64::x, 0};
|
||||||
constexpr RegisterA64 x1{KindA64::x, 1};
|
inline constexpr RegisterA64 x1{KindA64::x, 1};
|
||||||
constexpr RegisterA64 x2{KindA64::x, 2};
|
inline constexpr RegisterA64 x2{KindA64::x, 2};
|
||||||
constexpr RegisterA64 x3{KindA64::x, 3};
|
inline constexpr RegisterA64 x3{KindA64::x, 3};
|
||||||
constexpr RegisterA64 x4{KindA64::x, 4};
|
inline constexpr RegisterA64 x4{KindA64::x, 4};
|
||||||
constexpr RegisterA64 x5{KindA64::x, 5};
|
inline constexpr RegisterA64 x5{KindA64::x, 5};
|
||||||
constexpr RegisterA64 x6{KindA64::x, 6};
|
inline constexpr RegisterA64 x6{KindA64::x, 6};
|
||||||
constexpr RegisterA64 x7{KindA64::x, 7};
|
inline constexpr RegisterA64 x7{KindA64::x, 7};
|
||||||
constexpr RegisterA64 x8{KindA64::x, 8};
|
inline constexpr RegisterA64 x8{KindA64::x, 8};
|
||||||
constexpr RegisterA64 x9{KindA64::x, 9};
|
inline constexpr RegisterA64 x9{KindA64::x, 9};
|
||||||
constexpr RegisterA64 x10{KindA64::x, 10};
|
inline constexpr RegisterA64 x10{KindA64::x, 10};
|
||||||
constexpr RegisterA64 x11{KindA64::x, 11};
|
inline constexpr RegisterA64 x11{KindA64::x, 11};
|
||||||
constexpr RegisterA64 x12{KindA64::x, 12};
|
inline constexpr RegisterA64 x12{KindA64::x, 12};
|
||||||
constexpr RegisterA64 x13{KindA64::x, 13};
|
inline constexpr RegisterA64 x13{KindA64::x, 13};
|
||||||
constexpr RegisterA64 x14{KindA64::x, 14};
|
inline constexpr RegisterA64 x14{KindA64::x, 14};
|
||||||
constexpr RegisterA64 x15{KindA64::x, 15};
|
inline constexpr RegisterA64 x15{KindA64::x, 15};
|
||||||
constexpr RegisterA64 x16{KindA64::x, 16};
|
inline constexpr RegisterA64 x16{KindA64::x, 16};
|
||||||
constexpr RegisterA64 x17{KindA64::x, 17};
|
inline constexpr RegisterA64 x17{KindA64::x, 17};
|
||||||
constexpr RegisterA64 x18{KindA64::x, 18};
|
inline constexpr RegisterA64 x18{KindA64::x, 18};
|
||||||
constexpr RegisterA64 x19{KindA64::x, 19};
|
inline constexpr RegisterA64 x19{KindA64::x, 19};
|
||||||
constexpr RegisterA64 x20{KindA64::x, 20};
|
inline constexpr RegisterA64 x20{KindA64::x, 20};
|
||||||
constexpr RegisterA64 x21{KindA64::x, 21};
|
inline constexpr RegisterA64 x21{KindA64::x, 21};
|
||||||
constexpr RegisterA64 x22{KindA64::x, 22};
|
inline constexpr RegisterA64 x22{KindA64::x, 22};
|
||||||
constexpr RegisterA64 x23{KindA64::x, 23};
|
inline constexpr RegisterA64 x23{KindA64::x, 23};
|
||||||
constexpr RegisterA64 x24{KindA64::x, 24};
|
inline constexpr RegisterA64 x24{KindA64::x, 24};
|
||||||
constexpr RegisterA64 x25{KindA64::x, 25};
|
inline constexpr RegisterA64 x25{KindA64::x, 25};
|
||||||
constexpr RegisterA64 x26{KindA64::x, 26};
|
inline constexpr RegisterA64 x26{KindA64::x, 26};
|
||||||
constexpr RegisterA64 x27{KindA64::x, 27};
|
inline constexpr RegisterA64 x27{KindA64::x, 27};
|
||||||
constexpr RegisterA64 x28{KindA64::x, 28};
|
inline constexpr RegisterA64 x28{KindA64::x, 28};
|
||||||
constexpr RegisterA64 x29{KindA64::x, 29};
|
inline constexpr RegisterA64 x29{KindA64::x, 29};
|
||||||
constexpr RegisterA64 x30{KindA64::x, 30};
|
inline constexpr RegisterA64 x30{KindA64::x, 30};
|
||||||
constexpr RegisterA64 xzr{KindA64::x, 31};
|
inline constexpr RegisterA64 xzr{KindA64::x, 31};
|
||||||
|
|
||||||
constexpr RegisterA64 sp{KindA64::none, 31};
|
inline constexpr RegisterA64 sp{KindA64::none, 31};
|
||||||
|
|
||||||
constexpr RegisterA64 s0{KindA64::s, 0};
|
inline constexpr RegisterA64 s0{KindA64::s, 0};
|
||||||
constexpr RegisterA64 s1{KindA64::s, 1};
|
inline constexpr RegisterA64 s1{KindA64::s, 1};
|
||||||
constexpr RegisterA64 s2{KindA64::s, 2};
|
inline constexpr RegisterA64 s2{KindA64::s, 2};
|
||||||
constexpr RegisterA64 s3{KindA64::s, 3};
|
inline constexpr RegisterA64 s3{KindA64::s, 3};
|
||||||
constexpr RegisterA64 s4{KindA64::s, 4};
|
inline constexpr RegisterA64 s4{KindA64::s, 4};
|
||||||
constexpr RegisterA64 s5{KindA64::s, 5};
|
inline constexpr RegisterA64 s5{KindA64::s, 5};
|
||||||
constexpr RegisterA64 s6{KindA64::s, 6};
|
inline constexpr RegisterA64 s6{KindA64::s, 6};
|
||||||
constexpr RegisterA64 s7{KindA64::s, 7};
|
inline constexpr RegisterA64 s7{KindA64::s, 7};
|
||||||
constexpr RegisterA64 s8{KindA64::s, 8};
|
inline constexpr RegisterA64 s8{KindA64::s, 8};
|
||||||
constexpr RegisterA64 s9{KindA64::s, 9};
|
inline constexpr RegisterA64 s9{KindA64::s, 9};
|
||||||
constexpr RegisterA64 s10{KindA64::s, 10};
|
inline constexpr RegisterA64 s10{KindA64::s, 10};
|
||||||
constexpr RegisterA64 s11{KindA64::s, 11};
|
inline constexpr RegisterA64 s11{KindA64::s, 11};
|
||||||
constexpr RegisterA64 s12{KindA64::s, 12};
|
inline constexpr RegisterA64 s12{KindA64::s, 12};
|
||||||
constexpr RegisterA64 s13{KindA64::s, 13};
|
inline constexpr RegisterA64 s13{KindA64::s, 13};
|
||||||
constexpr RegisterA64 s14{KindA64::s, 14};
|
inline constexpr RegisterA64 s14{KindA64::s, 14};
|
||||||
constexpr RegisterA64 s15{KindA64::s, 15};
|
inline constexpr RegisterA64 s15{KindA64::s, 15};
|
||||||
constexpr RegisterA64 s16{KindA64::s, 16};
|
inline constexpr RegisterA64 s16{KindA64::s, 16};
|
||||||
constexpr RegisterA64 s17{KindA64::s, 17};
|
inline constexpr RegisterA64 s17{KindA64::s, 17};
|
||||||
constexpr RegisterA64 s18{KindA64::s, 18};
|
inline constexpr RegisterA64 s18{KindA64::s, 18};
|
||||||
constexpr RegisterA64 s19{KindA64::s, 19};
|
inline constexpr RegisterA64 s19{KindA64::s, 19};
|
||||||
constexpr RegisterA64 s20{KindA64::s, 20};
|
inline constexpr RegisterA64 s20{KindA64::s, 20};
|
||||||
constexpr RegisterA64 s21{KindA64::s, 21};
|
inline constexpr RegisterA64 s21{KindA64::s, 21};
|
||||||
constexpr RegisterA64 s22{KindA64::s, 22};
|
inline constexpr RegisterA64 s22{KindA64::s, 22};
|
||||||
constexpr RegisterA64 s23{KindA64::s, 23};
|
inline constexpr RegisterA64 s23{KindA64::s, 23};
|
||||||
constexpr RegisterA64 s24{KindA64::s, 24};
|
inline constexpr RegisterA64 s24{KindA64::s, 24};
|
||||||
constexpr RegisterA64 s25{KindA64::s, 25};
|
inline constexpr RegisterA64 s25{KindA64::s, 25};
|
||||||
constexpr RegisterA64 s26{KindA64::s, 26};
|
inline constexpr RegisterA64 s26{KindA64::s, 26};
|
||||||
constexpr RegisterA64 s27{KindA64::s, 27};
|
inline constexpr RegisterA64 s27{KindA64::s, 27};
|
||||||
constexpr RegisterA64 s28{KindA64::s, 28};
|
inline constexpr RegisterA64 s28{KindA64::s, 28};
|
||||||
constexpr RegisterA64 s29{KindA64::s, 29};
|
inline constexpr RegisterA64 s29{KindA64::s, 29};
|
||||||
constexpr RegisterA64 s30{KindA64::s, 30};
|
inline constexpr RegisterA64 s30{KindA64::s, 30};
|
||||||
constexpr RegisterA64 s31{KindA64::s, 31};
|
inline constexpr RegisterA64 s31{KindA64::s, 31};
|
||||||
|
|
||||||
constexpr RegisterA64 d0{KindA64::d, 0};
|
inline constexpr RegisterA64 d0{KindA64::d, 0};
|
||||||
constexpr RegisterA64 d1{KindA64::d, 1};
|
inline constexpr RegisterA64 d1{KindA64::d, 1};
|
||||||
constexpr RegisterA64 d2{KindA64::d, 2};
|
inline constexpr RegisterA64 d2{KindA64::d, 2};
|
||||||
constexpr RegisterA64 d3{KindA64::d, 3};
|
inline constexpr RegisterA64 d3{KindA64::d, 3};
|
||||||
constexpr RegisterA64 d4{KindA64::d, 4};
|
inline constexpr RegisterA64 d4{KindA64::d, 4};
|
||||||
constexpr RegisterA64 d5{KindA64::d, 5};
|
inline constexpr RegisterA64 d5{KindA64::d, 5};
|
||||||
constexpr RegisterA64 d6{KindA64::d, 6};
|
inline constexpr RegisterA64 d6{KindA64::d, 6};
|
||||||
constexpr RegisterA64 d7{KindA64::d, 7};
|
inline constexpr RegisterA64 d7{KindA64::d, 7};
|
||||||
constexpr RegisterA64 d8{KindA64::d, 8};
|
inline constexpr RegisterA64 d8{KindA64::d, 8};
|
||||||
constexpr RegisterA64 d9{KindA64::d, 9};
|
inline constexpr RegisterA64 d9{KindA64::d, 9};
|
||||||
constexpr RegisterA64 d10{KindA64::d, 10};
|
inline constexpr RegisterA64 d10{KindA64::d, 10};
|
||||||
constexpr RegisterA64 d11{KindA64::d, 11};
|
inline constexpr RegisterA64 d11{KindA64::d, 11};
|
||||||
constexpr RegisterA64 d12{KindA64::d, 12};
|
inline constexpr RegisterA64 d12{KindA64::d, 12};
|
||||||
constexpr RegisterA64 d13{KindA64::d, 13};
|
inline constexpr RegisterA64 d13{KindA64::d, 13};
|
||||||
constexpr RegisterA64 d14{KindA64::d, 14};
|
inline constexpr RegisterA64 d14{KindA64::d, 14};
|
||||||
constexpr RegisterA64 d15{KindA64::d, 15};
|
inline constexpr RegisterA64 d15{KindA64::d, 15};
|
||||||
constexpr RegisterA64 d16{KindA64::d, 16};
|
inline constexpr RegisterA64 d16{KindA64::d, 16};
|
||||||
constexpr RegisterA64 d17{KindA64::d, 17};
|
inline constexpr RegisterA64 d17{KindA64::d, 17};
|
||||||
constexpr RegisterA64 d18{KindA64::d, 18};
|
inline constexpr RegisterA64 d18{KindA64::d, 18};
|
||||||
constexpr RegisterA64 d19{KindA64::d, 19};
|
inline constexpr RegisterA64 d19{KindA64::d, 19};
|
||||||
constexpr RegisterA64 d20{KindA64::d, 20};
|
inline constexpr RegisterA64 d20{KindA64::d, 20};
|
||||||
constexpr RegisterA64 d21{KindA64::d, 21};
|
inline constexpr RegisterA64 d21{KindA64::d, 21};
|
||||||
constexpr RegisterA64 d22{KindA64::d, 22};
|
inline constexpr RegisterA64 d22{KindA64::d, 22};
|
||||||
constexpr RegisterA64 d23{KindA64::d, 23};
|
inline constexpr RegisterA64 d23{KindA64::d, 23};
|
||||||
constexpr RegisterA64 d24{KindA64::d, 24};
|
inline constexpr RegisterA64 d24{KindA64::d, 24};
|
||||||
constexpr RegisterA64 d25{KindA64::d, 25};
|
inline constexpr RegisterA64 d25{KindA64::d, 25};
|
||||||
constexpr RegisterA64 d26{KindA64::d, 26};
|
inline constexpr RegisterA64 d26{KindA64::d, 26};
|
||||||
constexpr RegisterA64 d27{KindA64::d, 27};
|
inline constexpr RegisterA64 d27{KindA64::d, 27};
|
||||||
constexpr RegisterA64 d28{KindA64::d, 28};
|
inline constexpr RegisterA64 d28{KindA64::d, 28};
|
||||||
constexpr RegisterA64 d29{KindA64::d, 29};
|
inline constexpr RegisterA64 d29{KindA64::d, 29};
|
||||||
constexpr RegisterA64 d30{KindA64::d, 30};
|
inline constexpr RegisterA64 d30{KindA64::d, 30};
|
||||||
constexpr RegisterA64 d31{KindA64::d, 31};
|
inline constexpr RegisterA64 d31{KindA64::d, 31};
|
||||||
|
|
||||||
constexpr RegisterA64 q0{KindA64::q, 0};
|
inline constexpr RegisterA64 q0{KindA64::q, 0};
|
||||||
constexpr RegisterA64 q1{KindA64::q, 1};
|
inline constexpr RegisterA64 q1{KindA64::q, 1};
|
||||||
constexpr RegisterA64 q2{KindA64::q, 2};
|
inline constexpr RegisterA64 q2{KindA64::q, 2};
|
||||||
constexpr RegisterA64 q3{KindA64::q, 3};
|
inline constexpr RegisterA64 q3{KindA64::q, 3};
|
||||||
constexpr RegisterA64 q4{KindA64::q, 4};
|
inline constexpr RegisterA64 q4{KindA64::q, 4};
|
||||||
constexpr RegisterA64 q5{KindA64::q, 5};
|
inline constexpr RegisterA64 q5{KindA64::q, 5};
|
||||||
constexpr RegisterA64 q6{KindA64::q, 6};
|
inline constexpr RegisterA64 q6{KindA64::q, 6};
|
||||||
constexpr RegisterA64 q7{KindA64::q, 7};
|
inline constexpr RegisterA64 q7{KindA64::q, 7};
|
||||||
constexpr RegisterA64 q8{KindA64::q, 8};
|
inline constexpr RegisterA64 q8{KindA64::q, 8};
|
||||||
constexpr RegisterA64 q9{KindA64::q, 9};
|
inline constexpr RegisterA64 q9{KindA64::q, 9};
|
||||||
constexpr RegisterA64 q10{KindA64::q, 10};
|
inline constexpr RegisterA64 q10{KindA64::q, 10};
|
||||||
constexpr RegisterA64 q11{KindA64::q, 11};
|
inline constexpr RegisterA64 q11{KindA64::q, 11};
|
||||||
constexpr RegisterA64 q12{KindA64::q, 12};
|
inline constexpr RegisterA64 q12{KindA64::q, 12};
|
||||||
constexpr RegisterA64 q13{KindA64::q, 13};
|
inline constexpr RegisterA64 q13{KindA64::q, 13};
|
||||||
constexpr RegisterA64 q14{KindA64::q, 14};
|
inline constexpr RegisterA64 q14{KindA64::q, 14};
|
||||||
constexpr RegisterA64 q15{KindA64::q, 15};
|
inline constexpr RegisterA64 q15{KindA64::q, 15};
|
||||||
constexpr RegisterA64 q16{KindA64::q, 16};
|
inline constexpr RegisterA64 q16{KindA64::q, 16};
|
||||||
constexpr RegisterA64 q17{KindA64::q, 17};
|
inline constexpr RegisterA64 q17{KindA64::q, 17};
|
||||||
constexpr RegisterA64 q18{KindA64::q, 18};
|
inline constexpr RegisterA64 q18{KindA64::q, 18};
|
||||||
constexpr RegisterA64 q19{KindA64::q, 19};
|
inline constexpr RegisterA64 q19{KindA64::q, 19};
|
||||||
constexpr RegisterA64 q20{KindA64::q, 20};
|
inline constexpr RegisterA64 q20{KindA64::q, 20};
|
||||||
constexpr RegisterA64 q21{KindA64::q, 21};
|
inline constexpr RegisterA64 q21{KindA64::q, 21};
|
||||||
constexpr RegisterA64 q22{KindA64::q, 22};
|
inline constexpr RegisterA64 q22{KindA64::q, 22};
|
||||||
constexpr RegisterA64 q23{KindA64::q, 23};
|
inline constexpr RegisterA64 q23{KindA64::q, 23};
|
||||||
constexpr RegisterA64 q24{KindA64::q, 24};
|
inline constexpr RegisterA64 q24{KindA64::q, 24};
|
||||||
constexpr RegisterA64 q25{KindA64::q, 25};
|
inline constexpr RegisterA64 q25{KindA64::q, 25};
|
||||||
constexpr RegisterA64 q26{KindA64::q, 26};
|
inline constexpr RegisterA64 q26{KindA64::q, 26};
|
||||||
constexpr RegisterA64 q27{KindA64::q, 27};
|
inline constexpr RegisterA64 q27{KindA64::q, 27};
|
||||||
constexpr RegisterA64 q28{KindA64::q, 28};
|
inline constexpr RegisterA64 q28{KindA64::q, 28};
|
||||||
constexpr RegisterA64 q29{KindA64::q, 29};
|
inline constexpr RegisterA64 q29{KindA64::q, 29};
|
||||||
constexpr RegisterA64 q30{KindA64::q, 30};
|
inline constexpr RegisterA64 q30{KindA64::q, 30};
|
||||||
constexpr RegisterA64 q31{KindA64::q, 31};
|
inline constexpr RegisterA64 q31{KindA64::q, 31};
|
||||||
|
|
||||||
} // namespace A64
|
} // namespace A64
|
||||||
} // namespace CodeGen
|
} // namespace CodeGen
|
||||||
|
|
|
@ -39,93 +39,93 @@ struct RegisterX64
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr RegisterX64 noreg{SizeX64::none, 16};
|
inline constexpr RegisterX64 noreg{SizeX64::none, 16};
|
||||||
constexpr RegisterX64 rip{SizeX64::none, 0};
|
inline constexpr RegisterX64 rip{SizeX64::none, 0};
|
||||||
|
|
||||||
constexpr RegisterX64 al{SizeX64::byte, 0};
|
inline constexpr RegisterX64 al{SizeX64::byte, 0};
|
||||||
constexpr RegisterX64 cl{SizeX64::byte, 1};
|
inline constexpr RegisterX64 cl{SizeX64::byte, 1};
|
||||||
constexpr RegisterX64 dl{SizeX64::byte, 2};
|
inline constexpr RegisterX64 dl{SizeX64::byte, 2};
|
||||||
constexpr RegisterX64 bl{SizeX64::byte, 3};
|
inline constexpr RegisterX64 bl{SizeX64::byte, 3};
|
||||||
constexpr RegisterX64 spl{SizeX64::byte, 4};
|
inline constexpr RegisterX64 spl{SizeX64::byte, 4};
|
||||||
constexpr RegisterX64 bpl{SizeX64::byte, 5};
|
inline constexpr RegisterX64 bpl{SizeX64::byte, 5};
|
||||||
constexpr RegisterX64 sil{SizeX64::byte, 6};
|
inline constexpr RegisterX64 sil{SizeX64::byte, 6};
|
||||||
constexpr RegisterX64 dil{SizeX64::byte, 7};
|
inline constexpr RegisterX64 dil{SizeX64::byte, 7};
|
||||||
constexpr RegisterX64 r8b{SizeX64::byte, 8};
|
inline constexpr RegisterX64 r8b{SizeX64::byte, 8};
|
||||||
constexpr RegisterX64 r9b{SizeX64::byte, 9};
|
inline constexpr RegisterX64 r9b{SizeX64::byte, 9};
|
||||||
constexpr RegisterX64 r10b{SizeX64::byte, 10};
|
inline constexpr RegisterX64 r10b{SizeX64::byte, 10};
|
||||||
constexpr RegisterX64 r11b{SizeX64::byte, 11};
|
inline constexpr RegisterX64 r11b{SizeX64::byte, 11};
|
||||||
constexpr RegisterX64 r12b{SizeX64::byte, 12};
|
inline constexpr RegisterX64 r12b{SizeX64::byte, 12};
|
||||||
constexpr RegisterX64 r13b{SizeX64::byte, 13};
|
inline constexpr RegisterX64 r13b{SizeX64::byte, 13};
|
||||||
constexpr RegisterX64 r14b{SizeX64::byte, 14};
|
inline constexpr RegisterX64 r14b{SizeX64::byte, 14};
|
||||||
constexpr RegisterX64 r15b{SizeX64::byte, 15};
|
inline constexpr RegisterX64 r15b{SizeX64::byte, 15};
|
||||||
|
|
||||||
constexpr RegisterX64 eax{SizeX64::dword, 0};
|
inline constexpr RegisterX64 eax{SizeX64::dword, 0};
|
||||||
constexpr RegisterX64 ecx{SizeX64::dword, 1};
|
inline constexpr RegisterX64 ecx{SizeX64::dword, 1};
|
||||||
constexpr RegisterX64 edx{SizeX64::dword, 2};
|
inline constexpr RegisterX64 edx{SizeX64::dword, 2};
|
||||||
constexpr RegisterX64 ebx{SizeX64::dword, 3};
|
inline constexpr RegisterX64 ebx{SizeX64::dword, 3};
|
||||||
constexpr RegisterX64 esp{SizeX64::dword, 4};
|
inline constexpr RegisterX64 esp{SizeX64::dword, 4};
|
||||||
constexpr RegisterX64 ebp{SizeX64::dword, 5};
|
inline constexpr RegisterX64 ebp{SizeX64::dword, 5};
|
||||||
constexpr RegisterX64 esi{SizeX64::dword, 6};
|
inline constexpr RegisterX64 esi{SizeX64::dword, 6};
|
||||||
constexpr RegisterX64 edi{SizeX64::dword, 7};
|
inline constexpr RegisterX64 edi{SizeX64::dword, 7};
|
||||||
constexpr RegisterX64 r8d{SizeX64::dword, 8};
|
inline constexpr RegisterX64 r8d{SizeX64::dword, 8};
|
||||||
constexpr RegisterX64 r9d{SizeX64::dword, 9};
|
inline constexpr RegisterX64 r9d{SizeX64::dword, 9};
|
||||||
constexpr RegisterX64 r10d{SizeX64::dword, 10};
|
inline constexpr RegisterX64 r10d{SizeX64::dword, 10};
|
||||||
constexpr RegisterX64 r11d{SizeX64::dword, 11};
|
inline constexpr RegisterX64 r11d{SizeX64::dword, 11};
|
||||||
constexpr RegisterX64 r12d{SizeX64::dword, 12};
|
inline constexpr RegisterX64 r12d{SizeX64::dword, 12};
|
||||||
constexpr RegisterX64 r13d{SizeX64::dword, 13};
|
inline constexpr RegisterX64 r13d{SizeX64::dword, 13};
|
||||||
constexpr RegisterX64 r14d{SizeX64::dword, 14};
|
inline constexpr RegisterX64 r14d{SizeX64::dword, 14};
|
||||||
constexpr RegisterX64 r15d{SizeX64::dword, 15};
|
inline constexpr RegisterX64 r15d{SizeX64::dword, 15};
|
||||||
|
|
||||||
constexpr RegisterX64 rax{SizeX64::qword, 0};
|
inline constexpr RegisterX64 rax{SizeX64::qword, 0};
|
||||||
constexpr RegisterX64 rcx{SizeX64::qword, 1};
|
inline constexpr RegisterX64 rcx{SizeX64::qword, 1};
|
||||||
constexpr RegisterX64 rdx{SizeX64::qword, 2};
|
inline constexpr RegisterX64 rdx{SizeX64::qword, 2};
|
||||||
constexpr RegisterX64 rbx{SizeX64::qword, 3};
|
inline constexpr RegisterX64 rbx{SizeX64::qword, 3};
|
||||||
constexpr RegisterX64 rsp{SizeX64::qword, 4};
|
inline constexpr RegisterX64 rsp{SizeX64::qword, 4};
|
||||||
constexpr RegisterX64 rbp{SizeX64::qword, 5};
|
inline constexpr RegisterX64 rbp{SizeX64::qword, 5};
|
||||||
constexpr RegisterX64 rsi{SizeX64::qword, 6};
|
inline constexpr RegisterX64 rsi{SizeX64::qword, 6};
|
||||||
constexpr RegisterX64 rdi{SizeX64::qword, 7};
|
inline constexpr RegisterX64 rdi{SizeX64::qword, 7};
|
||||||
constexpr RegisterX64 r8{SizeX64::qword, 8};
|
inline constexpr RegisterX64 r8{SizeX64::qword, 8};
|
||||||
constexpr RegisterX64 r9{SizeX64::qword, 9};
|
inline constexpr RegisterX64 r9{SizeX64::qword, 9};
|
||||||
constexpr RegisterX64 r10{SizeX64::qword, 10};
|
inline constexpr RegisterX64 r10{SizeX64::qword, 10};
|
||||||
constexpr RegisterX64 r11{SizeX64::qword, 11};
|
inline constexpr RegisterX64 r11{SizeX64::qword, 11};
|
||||||
constexpr RegisterX64 r12{SizeX64::qword, 12};
|
inline constexpr RegisterX64 r12{SizeX64::qword, 12};
|
||||||
constexpr RegisterX64 r13{SizeX64::qword, 13};
|
inline constexpr RegisterX64 r13{SizeX64::qword, 13};
|
||||||
constexpr RegisterX64 r14{SizeX64::qword, 14};
|
inline constexpr RegisterX64 r14{SizeX64::qword, 14};
|
||||||
constexpr RegisterX64 r15{SizeX64::qword, 15};
|
inline constexpr RegisterX64 r15{SizeX64::qword, 15};
|
||||||
|
|
||||||
constexpr RegisterX64 xmm0{SizeX64::xmmword, 0};
|
inline constexpr RegisterX64 xmm0{SizeX64::xmmword, 0};
|
||||||
constexpr RegisterX64 xmm1{SizeX64::xmmword, 1};
|
inline constexpr RegisterX64 xmm1{SizeX64::xmmword, 1};
|
||||||
constexpr RegisterX64 xmm2{SizeX64::xmmword, 2};
|
inline constexpr RegisterX64 xmm2{SizeX64::xmmword, 2};
|
||||||
constexpr RegisterX64 xmm3{SizeX64::xmmword, 3};
|
inline constexpr RegisterX64 xmm3{SizeX64::xmmword, 3};
|
||||||
constexpr RegisterX64 xmm4{SizeX64::xmmword, 4};
|
inline constexpr RegisterX64 xmm4{SizeX64::xmmword, 4};
|
||||||
constexpr RegisterX64 xmm5{SizeX64::xmmword, 5};
|
inline constexpr RegisterX64 xmm5{SizeX64::xmmword, 5};
|
||||||
constexpr RegisterX64 xmm6{SizeX64::xmmword, 6};
|
inline constexpr RegisterX64 xmm6{SizeX64::xmmword, 6};
|
||||||
constexpr RegisterX64 xmm7{SizeX64::xmmword, 7};
|
inline constexpr RegisterX64 xmm7{SizeX64::xmmword, 7};
|
||||||
constexpr RegisterX64 xmm8{SizeX64::xmmword, 8};
|
inline constexpr RegisterX64 xmm8{SizeX64::xmmword, 8};
|
||||||
constexpr RegisterX64 xmm9{SizeX64::xmmword, 9};
|
inline constexpr RegisterX64 xmm9{SizeX64::xmmword, 9};
|
||||||
constexpr RegisterX64 xmm10{SizeX64::xmmword, 10};
|
inline constexpr RegisterX64 xmm10{SizeX64::xmmword, 10};
|
||||||
constexpr RegisterX64 xmm11{SizeX64::xmmword, 11};
|
inline constexpr RegisterX64 xmm11{SizeX64::xmmword, 11};
|
||||||
constexpr RegisterX64 xmm12{SizeX64::xmmword, 12};
|
inline constexpr RegisterX64 xmm12{SizeX64::xmmword, 12};
|
||||||
constexpr RegisterX64 xmm13{SizeX64::xmmword, 13};
|
inline constexpr RegisterX64 xmm13{SizeX64::xmmword, 13};
|
||||||
constexpr RegisterX64 xmm14{SizeX64::xmmword, 14};
|
inline constexpr RegisterX64 xmm14{SizeX64::xmmword, 14};
|
||||||
constexpr RegisterX64 xmm15{SizeX64::xmmword, 15};
|
inline constexpr RegisterX64 xmm15{SizeX64::xmmword, 15};
|
||||||
|
|
||||||
constexpr RegisterX64 ymm0{SizeX64::ymmword, 0};
|
inline constexpr RegisterX64 ymm0{SizeX64::ymmword, 0};
|
||||||
constexpr RegisterX64 ymm1{SizeX64::ymmword, 1};
|
inline constexpr RegisterX64 ymm1{SizeX64::ymmword, 1};
|
||||||
constexpr RegisterX64 ymm2{SizeX64::ymmword, 2};
|
inline constexpr RegisterX64 ymm2{SizeX64::ymmword, 2};
|
||||||
constexpr RegisterX64 ymm3{SizeX64::ymmword, 3};
|
inline constexpr RegisterX64 ymm3{SizeX64::ymmword, 3};
|
||||||
constexpr RegisterX64 ymm4{SizeX64::ymmword, 4};
|
inline constexpr RegisterX64 ymm4{SizeX64::ymmword, 4};
|
||||||
constexpr RegisterX64 ymm5{SizeX64::ymmword, 5};
|
inline constexpr RegisterX64 ymm5{SizeX64::ymmword, 5};
|
||||||
constexpr RegisterX64 ymm6{SizeX64::ymmword, 6};
|
inline constexpr RegisterX64 ymm6{SizeX64::ymmword, 6};
|
||||||
constexpr RegisterX64 ymm7{SizeX64::ymmword, 7};
|
inline constexpr RegisterX64 ymm7{SizeX64::ymmword, 7};
|
||||||
constexpr RegisterX64 ymm8{SizeX64::ymmword, 8};
|
inline constexpr RegisterX64 ymm8{SizeX64::ymmword, 8};
|
||||||
constexpr RegisterX64 ymm9{SizeX64::ymmword, 9};
|
inline constexpr RegisterX64 ymm9{SizeX64::ymmword, 9};
|
||||||
constexpr RegisterX64 ymm10{SizeX64::ymmword, 10};
|
inline constexpr RegisterX64 ymm10{SizeX64::ymmword, 10};
|
||||||
constexpr RegisterX64 ymm11{SizeX64::ymmword, 11};
|
inline constexpr RegisterX64 ymm11{SizeX64::ymmword, 11};
|
||||||
constexpr RegisterX64 ymm12{SizeX64::ymmword, 12};
|
inline constexpr RegisterX64 ymm12{SizeX64::ymmword, 12};
|
||||||
constexpr RegisterX64 ymm13{SizeX64::ymmword, 13};
|
inline constexpr RegisterX64 ymm13{SizeX64::ymmword, 13};
|
||||||
constexpr RegisterX64 ymm14{SizeX64::ymmword, 14};
|
inline constexpr RegisterX64 ymm14{SizeX64::ymmword, 14};
|
||||||
constexpr RegisterX64 ymm15{SizeX64::ymmword, 15};
|
inline constexpr RegisterX64 ymm15{SizeX64::ymmword, 15};
|
||||||
|
|
||||||
constexpr RegisterX64 byteReg(RegisterX64 reg)
|
constexpr RegisterX64 byteReg(RegisterX64 reg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
// This value is used in 'finishFunction' to mark the function that spans to the end of the whole code block
|
// This value is used in 'finishFunction' to mark the function that spans to the end of the whole code block
|
||||||
static uint32_t kFullBlockFunction = ~0u;
|
inline constexpr uint32_t kFullBlockFunction = ~0u;
|
||||||
|
|
||||||
class UnwindBuilder
|
class UnwindBuilder
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,26 +27,26 @@ namespace A64
|
||||||
|
|
||||||
// Data that is very common to access is placed in non-volatile registers:
|
// Data that is very common to access is placed in non-volatile registers:
|
||||||
// 1. Constant registers (only loaded during codegen entry)
|
// 1. Constant registers (only loaded during codegen entry)
|
||||||
constexpr RegisterA64 rState = x19; // lua_State* L
|
inline constexpr RegisterA64 rState = x19; // lua_State* L
|
||||||
constexpr RegisterA64 rNativeContext = x20; // NativeContext* context
|
inline constexpr RegisterA64 rNativeContext = x20; // NativeContext* context
|
||||||
constexpr RegisterA64 rGlobalState = x21; // global_State* L->global
|
inline constexpr RegisterA64 rGlobalState = x21; // global_State* L->global
|
||||||
|
|
||||||
// 2. Frame registers (reloaded when call frame changes; rBase is also reloaded after all calls that may reallocate stack)
|
// 2. Frame registers (reloaded when call frame changes; rBase is also reloaded after all calls that may reallocate stack)
|
||||||
constexpr RegisterA64 rConstants = x22; // TValue* k
|
inline constexpr RegisterA64 rConstants = x22; // TValue* k
|
||||||
constexpr RegisterA64 rClosure = x23; // Closure* cl
|
inline constexpr RegisterA64 rClosure = x23; // Closure* cl
|
||||||
constexpr RegisterA64 rCode = x24; // Instruction* code
|
inline constexpr RegisterA64 rCode = x24; // Instruction* code
|
||||||
constexpr RegisterA64 rBase = x25; // StkId base
|
inline constexpr RegisterA64 rBase = x25; // StkId base
|
||||||
|
|
||||||
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
|
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
|
||||||
// See CodeGenA64.cpp for layout
|
// See CodeGenA64.cpp for layout
|
||||||
constexpr unsigned kStashSlots = 9; // stashed non-volatile registers
|
inline constexpr unsigned kStashSlots = 9; // stashed non-volatile registers
|
||||||
constexpr unsigned kTempSlots = 1; // 8 bytes of temporary space, such luxury!
|
inline constexpr unsigned kTempSlots = 1; // 8 bytes of temporary space, such luxury!
|
||||||
constexpr unsigned kSpillSlots = 22; // slots for spilling temporary registers
|
inline constexpr unsigned kSpillSlots = 22; // slots for spilling temporary registers
|
||||||
|
|
||||||
constexpr unsigned kStackSize = (kStashSlots + kTempSlots + kSpillSlots) * 8;
|
inline constexpr unsigned kStackSize = (kStashSlots + kTempSlots + kSpillSlots) * 8;
|
||||||
|
|
||||||
constexpr AddressA64 sSpillArea = mem(sp, (kStashSlots + kTempSlots) * 8);
|
inline constexpr AddressA64 sSpillArea = mem(sp, (kStashSlots + kTempSlots) * 8);
|
||||||
constexpr AddressA64 sTemporary = mem(sp, kStashSlots * 8);
|
inline constexpr AddressA64 sTemporary = mem(sp, kStashSlots * 8);
|
||||||
|
|
||||||
inline void emitUpdateBase(AssemblyBuilderA64& build)
|
inline void emitUpdateBase(AssemblyBuilderA64& build)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,22 +33,22 @@ namespace X64
|
||||||
|
|
||||||
struct IrRegAllocX64;
|
struct IrRegAllocX64;
|
||||||
|
|
||||||
constexpr uint32_t kFunctionAlignment = 32;
|
inline constexpr uint32_t kFunctionAlignment = 32;
|
||||||
|
|
||||||
// Data that is very common to access is placed in non-volatile registers
|
// Data that is very common to access is placed in non-volatile registers
|
||||||
constexpr RegisterX64 rState = r15; // lua_State* L
|
inline constexpr RegisterX64 rState = r15; // lua_State* L
|
||||||
constexpr RegisterX64 rBase = r14; // StkId base
|
inline constexpr RegisterX64 rBase = r14; // StkId base
|
||||||
constexpr RegisterX64 rNativeContext = r13; // NativeContext* context
|
inline constexpr RegisterX64 rNativeContext = r13; // NativeContext* context
|
||||||
constexpr RegisterX64 rConstants = r12; // TValue* k
|
inline constexpr RegisterX64 rConstants = r12; // TValue* k
|
||||||
|
|
||||||
constexpr unsigned kExtraLocals = 3; // Number of 8 byte slots available for specialized local variables specified below
|
inline constexpr unsigned kExtraLocals = 3; // Number of 8 byte slots available for specialized local variables specified below
|
||||||
constexpr unsigned kSpillSlots = 13; // Number of 8 byte slots available for register allocator to spill data into
|
inline constexpr unsigned kSpillSlots = 13; // Number of 8 byte slots available for register allocator to spill data into
|
||||||
static_assert((kExtraLocals + kSpillSlots) * 8 % 16 == 0, "locals have to preserve 16 byte alignment");
|
static_assert((kExtraLocals + kSpillSlots) * 8 % 16 == 0, "locals have to preserve 16 byte alignment");
|
||||||
|
|
||||||
constexpr uint8_t kWindowsFirstNonVolXmmReg = 6;
|
inline constexpr uint8_t kWindowsFirstNonVolXmmReg = 6;
|
||||||
|
|
||||||
constexpr uint8_t kWindowsUsableXmmRegs = 10; // Some xmm regs are non-volatile, we have to balance how many we want to use/preserve
|
inline constexpr uint8_t kWindowsUsableXmmRegs = 10; // Some xmm regs are non-volatile, we have to balance how many we want to use/preserve
|
||||||
constexpr uint8_t kSystemVUsableXmmRegs = 16; // All xmm regs are volatile
|
inline constexpr uint8_t kSystemVUsableXmmRegs = 16; // All xmm regs are volatile
|
||||||
|
|
||||||
inline uint8_t getXmmRegisterCount(ABIX64 abi)
|
inline uint8_t getXmmRegisterCount(ABIX64 abi)
|
||||||
{
|
{
|
||||||
|
@ -57,11 +57,11 @@ inline uint8_t getXmmRegisterCount(ABIX64 abi)
|
||||||
|
|
||||||
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
|
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
|
||||||
// Stack is separated into sections for different data. See CodeGenX64.cpp for layout overview
|
// Stack is separated into sections for different data. See CodeGenX64.cpp for layout overview
|
||||||
constexpr unsigned kStackAlign = 8; // Bytes we need to align the stack for non-vol xmm register storage
|
inline constexpr unsigned kStackAlign = 8; // Bytes we need to align the stack for non-vol xmm register storage
|
||||||
constexpr unsigned kStackLocalStorage = 8 * kExtraLocals;
|
inline constexpr unsigned kStackLocalStorage = 8 * kExtraLocals;
|
||||||
constexpr unsigned kStackSpillStorage = 8 * kSpillSlots;
|
inline constexpr unsigned kStackSpillStorage = 8 * kSpillSlots;
|
||||||
constexpr unsigned kStackExtraArgumentStorage = 2 * 8; // Bytes for 5th and 6th function call arguments used under Windows ABI
|
inline constexpr unsigned kStackExtraArgumentStorage = 2 * 8; // Bytes for 5th and 6th function call arguments used under Windows ABI
|
||||||
constexpr unsigned kStackRegHomeStorage = 4 * 8; // Register 'home' locations that can be used by callees under Windows ABI
|
inline constexpr unsigned kStackRegHomeStorage = 4 * 8; // Register 'home' locations that can be used by callees under Windows ABI
|
||||||
|
|
||||||
inline unsigned getNonVolXmmStorageSize(ABIX64 abi, uint8_t xmmRegCount)
|
inline unsigned getNonVolXmmStorageSize(ABIX64 abi, uint8_t xmmRegCount)
|
||||||
{
|
{
|
||||||
|
@ -77,19 +77,19 @@ inline unsigned getNonVolXmmStorageSize(ABIX64 abi, uint8_t xmmRegCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Useful offsets to specific parts
|
// Useful offsets to specific parts
|
||||||
constexpr unsigned kStackOffsetToLocals = kStackExtraArgumentStorage + kStackRegHomeStorage;
|
inline constexpr unsigned kStackOffsetToLocals = kStackExtraArgumentStorage + kStackRegHomeStorage;
|
||||||
constexpr unsigned kStackOffsetToSpillSlots = kStackOffsetToLocals + kStackLocalStorage;
|
inline constexpr unsigned kStackOffsetToSpillSlots = kStackOffsetToLocals + kStackLocalStorage;
|
||||||
|
|
||||||
inline unsigned getFullStackSize(ABIX64 abi, uint8_t xmmRegCount)
|
inline unsigned getFullStackSize(ABIX64 abi, uint8_t xmmRegCount)
|
||||||
{
|
{
|
||||||
return kStackOffsetToSpillSlots + kStackSpillStorage + getNonVolXmmStorageSize(abi, xmmRegCount) + kStackAlign;
|
return kStackOffsetToSpillSlots + kStackSpillStorage + getNonVolXmmStorageSize(abi, xmmRegCount) + kStackAlign;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr OperandX64 sClosure = qword[rsp + kStackOffsetToLocals + 0]; // Closure* cl
|
inline constexpr OperandX64 sClosure = qword[rsp + kStackOffsetToLocals + 0]; // Closure* cl
|
||||||
constexpr OperandX64 sCode = qword[rsp + kStackOffsetToLocals + 8]; // Instruction* code
|
inline constexpr OperandX64 sCode = qword[rsp + kStackOffsetToLocals + 8]; // Instruction* code
|
||||||
constexpr OperandX64 sTemporarySlot = addr[rsp + kStackOffsetToLocals + 16];
|
inline constexpr OperandX64 sTemporarySlot = addr[rsp + kStackOffsetToLocals + 16];
|
||||||
|
|
||||||
constexpr OperandX64 sSpillArea = addr[rsp + kStackOffsetToSpillSlots];
|
inline constexpr OperandX64 sSpillArea = addr[rsp + kStackOffsetToSpillSlots];
|
||||||
|
|
||||||
inline OperandX64 luauReg(int ri)
|
inline OperandX64 luauReg(int ri)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,6 +29,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCompileFixTypeFunctionSkip)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -3933,6 +3934,11 @@ struct Compiler
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstStatTypeFunction* node) override
|
||||||
|
{
|
||||||
|
return !FFlag::LuauCompileFixTypeFunctionSkip;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UndefinedLocalVisitor : AstVisitor
|
struct UndefinedLocalVisitor : AstVisitor
|
||||||
|
|
|
@ -85,7 +85,7 @@ struct LintOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const char* kWarningNames[] = {
|
inline constexpr const char* kWarningNames[] = {
|
||||||
"Unknown",
|
"Unknown",
|
||||||
|
|
||||||
"UnknownGlobal",
|
"UnknownGlobal",
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -50,15 +50,15 @@ ISOCLINE_SOURCES=extern/isocline/src/isocline.c
|
||||||
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
|
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
|
||||||
ISOCLINE_TARGET=$(BUILD)/libisocline.a
|
ISOCLINE_TARGET=$(BUILD)/libisocline.a
|
||||||
|
|
||||||
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplRequirer.cpp CLI/src/RequirerUtils.cpp
|
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplRequirer.cpp CLI/src/VfsNavigator.cpp
|
||||||
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
|
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
|
||||||
TESTS_TARGET=$(BUILD)/luau-tests
|
TESTS_TARGET=$(BUILD)/luau-tests
|
||||||
|
|
||||||
REPL_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplEntry.cpp CLI/src/ReplRequirer.cpp CLI/src/RequirerUtils.cpp
|
REPL_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplEntry.cpp CLI/src/ReplRequirer.cpp CLI/src/VfsNavigator.cpp
|
||||||
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
|
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
|
||||||
REPL_CLI_TARGET=$(BUILD)/luau
|
REPL_CLI_TARGET=$(BUILD)/luau
|
||||||
|
|
||||||
ANALYZE_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Analyze.cpp CLI/src/AnalyzeRequirer.cpp CLI/src/RequirerUtils.cpp
|
ANALYZE_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Analyze.cpp CLI/src/AnalyzeRequirer.cpp CLI/src/VfsNavigator.cpp
|
||||||
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
|
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
|
||||||
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
|
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,18 @@ public:
|
||||||
virtual NavigateResult toParent() = 0;
|
virtual NavigateResult toParent() = 0;
|
||||||
virtual NavigateResult toChild(const std::string& component) = 0;
|
virtual NavigateResult toChild(const std::string& component) = 0;
|
||||||
|
|
||||||
|
enum class ConfigBehavior
|
||||||
|
{
|
||||||
|
GetAlias,
|
||||||
|
GetConfig
|
||||||
|
};
|
||||||
|
|
||||||
virtual bool isConfigPresent() const = 0;
|
virtual bool isConfigPresent() const = 0;
|
||||||
|
|
||||||
|
// The result of getConfigBehavior determines whether getAlias or getConfig
|
||||||
|
// is called when isConfigPresent returns true.
|
||||||
|
virtual ConfigBehavior getConfigBehavior() const = 0;
|
||||||
|
virtual std::optional<std::string> getAlias(const std::string& alias) const = 0;
|
||||||
virtual std::optional<std::string> getConfig() const = 0;
|
virtual std::optional<std::string> getConfig() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,7 +105,8 @@ private:
|
||||||
|
|
||||||
NavigationContext& navigationContext;
|
NavigationContext& navigationContext;
|
||||||
ErrorHandler& errorHandler;
|
ErrorHandler& errorHandler;
|
||||||
Luau::Config config;
|
|
||||||
|
std::optional<std::string> foundAliasValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -78,7 +78,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
||||||
if (Error error = navigateToAndPopulateConfig(alias))
|
if (Error error = navigateToAndPopulateConfig(alias))
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (!config.aliases.contains(alias))
|
if (!foundAliasValue)
|
||||||
{
|
{
|
||||||
if (alias != "self")
|
if (alias != "self")
|
||||||
return "@" + alias + " is not a valid alias";
|
return "@" + alias + " is not a valid alias";
|
||||||
|
@ -93,7 +93,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Error error = navigateToAlias(alias, config.aliases[alias].value))
|
if (Error error = navigateToAlias(alias, *foundAliasValue))
|
||||||
return error;
|
return error;
|
||||||
if (Error error = navigateThroughPath(path))
|
if (Error error = navigateThroughPath(path))
|
||||||
return error;
|
return error;
|
||||||
|
@ -169,12 +169,20 @@ Error Navigator::navigateToAlias(const std::string& alias, const std::string& va
|
||||||
|
|
||||||
Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
||||||
{
|
{
|
||||||
while (!config.aliases.contains(desiredAlias))
|
Luau::Config config;
|
||||||
|
|
||||||
|
while (!foundAliasValue)
|
||||||
{
|
{
|
||||||
if (navigationContext.toParent() != NavigationContext::NavigateResult::Success)
|
if (navigationContext.toParent() != NavigationContext::NavigateResult::Success)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (navigationContext.isConfigPresent())
|
if (navigationContext.isConfigPresent())
|
||||||
|
{
|
||||||
|
if (navigationContext.getConfigBehavior() == NavigationContext::ConfigBehavior::GetAlias)
|
||||||
|
{
|
||||||
|
foundAliasValue = navigationContext.getAlias(desiredAlias);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
std::optional<std::string> configContents = navigationContext.getConfig();
|
std::optional<std::string> configContents = navigationContext.getConfig();
|
||||||
if (!configContents)
|
if (!configContents)
|
||||||
|
@ -188,6 +196,10 @@ Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
||||||
|
|
||||||
if (Error error = Luau::parseConfig(*configContents, config, opts))
|
if (Error error = Luau::parseConfig(*configContents, config, opts))
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
if (config.aliases.contains(desiredAlias))
|
||||||
|
foundAliasValue = config.aliases[desiredAlias].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -101,8 +101,18 @@ struct luarequire_Configuration
|
||||||
// configuration file is present or NAVIGATE_FAILURE is returned (at root).
|
// configuration file is present or NAVIGATE_FAILURE is returned (at root).
|
||||||
bool (*is_config_present)(lua_State* L, void* ctx);
|
bool (*is_config_present)(lua_State* L, void* ctx);
|
||||||
|
|
||||||
|
// Parses the configuration file in the current context for the given alias
|
||||||
|
// and returns its value or WRITE_FAILURE if not found. This function is
|
||||||
|
// only called if is_config_present returns true. If this function pointer
|
||||||
|
// is set, get_config must not be set. Opting in to this function pointer
|
||||||
|
// disables parsing configuration files internally and can be used for finer
|
||||||
|
// control over the configuration file parsing process.
|
||||||
|
luarequire_WriteResult (*get_alias)(lua_State* L, void* ctx, const char* alias, char* buffer, size_t buffer_size, size_t* size_out);
|
||||||
|
|
||||||
// Provides the contents of the configuration file in the current context.
|
// Provides the contents of the configuration file in the current context.
|
||||||
// This function is only called if is_config_present returns true.
|
// This function is only called if is_config_present returns true. If this
|
||||||
|
// function pointer is set, get_alias must not be set. Opting in to this
|
||||||
|
// function pointer enables parsing configuration files internally.
|
||||||
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||||
|
|
||||||
// Executes the module and places the result on the stack. Returns the
|
// Executes the module and places the result on the stack. Returns the
|
||||||
|
|
|
@ -82,6 +82,18 @@ bool RuntimeNavigationContext::isConfigPresent() const
|
||||||
return config->is_config_present(L, ctx);
|
return config->is_config_present(L, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationContext::ConfigBehavior RuntimeNavigationContext::getConfigBehavior() const
|
||||||
|
{
|
||||||
|
if (config->get_alias)
|
||||||
|
return ConfigBehavior::GetAlias;
|
||||||
|
return ConfigBehavior::GetConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> RuntimeNavigationContext::getAlias(const std::string& alias) const
|
||||||
|
{
|
||||||
|
return getStringFromCWriterWithInput(config->get_alias, alias, initalIdentifierBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getConfig() const
|
std::optional<std::string> RuntimeNavigationContext::getConfig() const
|
||||||
{
|
{
|
||||||
return getStringFromCWriter(config->get_config, initalFileBufferSize);
|
return getStringFromCWriter(config->get_config, initalFileBufferSize);
|
||||||
|
@ -112,17 +124,45 @@ std::optional<std::string> RuntimeNavigationContext::getStringFromCWriter(
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> RuntimeNavigationContext::getStringFromCWriterWithInput(
|
||||||
|
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, const char* input, char* buffer, size_t buffer_size, size_t* size_out),
|
||||||
|
std::string input,
|
||||||
|
size_t initalBufferSize
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
buffer.resize(initalBufferSize);
|
||||||
|
|
||||||
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L, std::string requiredPath)
|
size_t size;
|
||||||
: L(L)
|
luarequire_WriteResult result = writer(L, ctx, input.c_str(), buffer.data(), buffer.size(), &size);
|
||||||
, errorPrefix("error requiring module \"" + std::move(requiredPath) + "\": ")
|
if (result == WRITE_BUFFER_TOO_SMALL)
|
||||||
|
{
|
||||||
|
buffer.resize(size);
|
||||||
|
result = writer(L, ctx, input.c_str(), buffer.data(), buffer.size(), &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == WRITE_SUCCESS)
|
||||||
|
{
|
||||||
|
buffer.resize(size);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeErrorHandler::RuntimeErrorHandler(std::string requiredPath)
|
||||||
|
: errorPrefix("error requiring module \"" + std::move(requiredPath) + "\": ")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeErrorHandler::reportError(std::string message)
|
void RuntimeErrorHandler::reportError(std::string message)
|
||||||
{
|
{
|
||||||
std::string fullError = errorPrefix + std::move(message);
|
errorMessage = errorPrefix + std::move(message);
|
||||||
luaL_errorL(L, "%s", fullError.c_str());
|
}
|
||||||
|
|
||||||
|
const std::string& RuntimeErrorHandler::getReportedError() const
|
||||||
|
{
|
||||||
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
NavigateResult toChild(const std::string& component) override;
|
NavigateResult toChild(const std::string& component) override;
|
||||||
|
|
||||||
bool isConfigPresent() const override;
|
bool isConfigPresent() const override;
|
||||||
|
NavigationContext::ConfigBehavior getConfigBehavior() const override;
|
||||||
|
std::optional<std::string> getAlias(const std::string& alias) const override;
|
||||||
std::optional<std::string> getConfig() const override;
|
std::optional<std::string> getConfig() const override;
|
||||||
|
|
||||||
// Custom capabilities
|
// Custom capabilities
|
||||||
|
@ -40,6 +42,11 @@ private:
|
||||||
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out),
|
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out),
|
||||||
size_t initalBufferSize
|
size_t initalBufferSize
|
||||||
) const;
|
) const;
|
||||||
|
std::optional<std::string> getStringFromCWriterWithInput(
|
||||||
|
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, const char* input, char* buffer, size_t buffer_size, size_t* size_out),
|
||||||
|
std::string input,
|
||||||
|
size_t initalBufferSize
|
||||||
|
) const;
|
||||||
|
|
||||||
luarequire_Configuration* config;
|
luarequire_Configuration* config;
|
||||||
lua_State* L;
|
lua_State* L;
|
||||||
|
@ -47,15 +54,18 @@ private:
|
||||||
std::string requirerChunkname;
|
std::string requirerChunkname;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Non-throwing error reporter
|
||||||
class RuntimeErrorHandler : public ErrorHandler
|
class RuntimeErrorHandler : public ErrorHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RuntimeErrorHandler(lua_State* L, std::string requiredPath);
|
RuntimeErrorHandler(std::string requiredPath);
|
||||||
void reportError(std::string message) override;
|
void reportError(std::string message) override;
|
||||||
|
|
||||||
|
const std::string& getReportedError() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
lua_State* L;
|
|
||||||
std::string errorPrefix;
|
std::string errorPrefix;
|
||||||
|
std::string errorMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -29,8 +29,10 @@ static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_cache_key");
|
luaL_error(L, "require configuration is missing required function pointer: get_cache_key");
|
||||||
if (!config.is_config_present)
|
if (!config.is_config_present)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: is_config_present");
|
luaL_error(L, "require configuration is missing required function pointer: is_config_present");
|
||||||
if (!config.get_config)
|
if (config.get_alias && config.get_config)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_config");
|
luaL_error(L, "require configuration cannot define both get_alias and get_config");
|
||||||
|
if (!config.get_alias && !config.get_config)
|
||||||
|
luaL_error(L, "require configuration is missing required function pointer: either get_alias or get_config (not both)");
|
||||||
if (!config.load)
|
if (!config.load)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: load");
|
luaL_error(L, "require configuration is missing required function pointer: load");
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,16 @@ static const char* requiredCacheTableKey = "_MODULES";
|
||||||
|
|
||||||
struct ResolvedRequire
|
struct ResolvedRequire
|
||||||
{
|
{
|
||||||
|
static ResolvedRequire fromErrorHandler(const RuntimeErrorHandler& errorHandler)
|
||||||
|
{
|
||||||
|
return {ResolvedRequire::Status::ErrorReported, "", "", "", errorHandler.getReportedError()};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResolvedRequire fromErrorMessage(const char* message)
|
||||||
|
{
|
||||||
|
return {ResolvedRequire::Status::ErrorReported, "", "", "", message};
|
||||||
|
}
|
||||||
|
|
||||||
enum class Status
|
enum class Status
|
||||||
{
|
{
|
||||||
Cached,
|
Cached,
|
||||||
|
@ -32,6 +42,7 @@ struct ResolvedRequire
|
||||||
std::string chunkname;
|
std::string chunkname;
|
||||||
std::string loadname;
|
std::string loadname;
|
||||||
std::string cacheKey;
|
std::string cacheKey;
|
||||||
|
std::string error;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool isCached(lua_State* L, const std::string& key)
|
static bool isCached(lua_State* L, const std::string& key)
|
||||||
|
@ -47,30 +58,24 @@ static bool isCached(lua_State* L, const std::string& key)
|
||||||
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, const char* requirerChunkname, std::string path)
|
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, const char* requirerChunkname, std::string path)
|
||||||
{
|
{
|
||||||
if (!lrc->is_require_allowed(L, ctx, requirerChunkname))
|
if (!lrc->is_require_allowed(L, ctx, requirerChunkname))
|
||||||
luaL_error(L, "require is not supported in this context");
|
return ResolvedRequire::fromErrorMessage("require is not supported in this context");
|
||||||
|
|
||||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
||||||
RuntimeErrorHandler errorHandler{L, path}; // Errors reported directly to lua_State.
|
RuntimeErrorHandler errorHandler{path};
|
||||||
|
|
||||||
Navigator navigator(navigationContext, errorHandler);
|
Navigator navigator(navigationContext, errorHandler);
|
||||||
|
|
||||||
// Updates navigationContext while navigating through the given path.
|
// Updates navigationContext while navigating through the given path.
|
||||||
Navigator::Status status = navigator.navigate(std::move(path));
|
Navigator::Status status = navigator.navigate(std::move(path));
|
||||||
if (status == Navigator::Status::ErrorReported)
|
if (status == Navigator::Status::ErrorReported)
|
||||||
return {ResolvedRequire::Status::ErrorReported};
|
return ResolvedRequire::fromErrorHandler(errorHandler);
|
||||||
|
|
||||||
if (!navigationContext.isModulePresent())
|
if (!navigationContext.isModulePresent())
|
||||||
{
|
return ResolvedRequire::fromErrorMessage("no module present at resolved path");
|
||||||
errorHandler.reportError("no module present at resolved path");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cacheKey = navigationContext.getCacheKey();
|
std::optional<std::string> cacheKey = navigationContext.getCacheKey();
|
||||||
if (!cacheKey)
|
if (!cacheKey)
|
||||||
{
|
return ResolvedRequire::fromErrorMessage("could not get cache key for module");
|
||||||
errorHandler.reportError("could not get cache key for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCached(L, *cacheKey))
|
if (isCached(L, *cacheKey))
|
||||||
{
|
{
|
||||||
|
@ -84,17 +89,11 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
||||||
|
|
||||||
std::optional<std::string> chunkname = navigationContext.getChunkname();
|
std::optional<std::string> chunkname = navigationContext.getChunkname();
|
||||||
if (!chunkname)
|
if (!chunkname)
|
||||||
{
|
return ResolvedRequire::fromErrorMessage("could not get chunkname for module");
|
||||||
errorHandler.reportError("could not get chunkname for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> loadname = navigationContext.getLoadname();
|
std::optional<std::string> loadname = navigationContext.getLoadname();
|
||||||
if (!loadname)
|
if (!loadname)
|
||||||
{
|
return ResolvedRequire::fromErrorMessage("could not get loadname for module");
|
||||||
errorHandler.reportError("could not get loadname for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResolvedRequire{
|
return ResolvedRequire{
|
||||||
ResolvedRequire::Status::ModuleRead,
|
ResolvedRequire::Status::ModuleRead,
|
||||||
|
@ -118,12 +117,14 @@ static int checkRegisteredModules(lua_State* L, const char* path)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int kRequireStackValues = 4;
|
||||||
|
|
||||||
int lua_requirecont(lua_State* L, int status)
|
int lua_requirecont(lua_State* L, int status)
|
||||||
{
|
{
|
||||||
// Number of stack arguments present before this continuation is called.
|
// Number of stack arguments present before this continuation is called.
|
||||||
const int numStackArgs = lua_tointeger(L, 1);
|
LUAU_ASSERT(lua_gettop(L) >= kRequireStackValues);
|
||||||
const int numResults = lua_gettop(L) - numStackArgs;
|
const int numResults = lua_gettop(L) - kRequireStackValues;
|
||||||
const char* cacheKey = luaL_checkstring(L, numStackArgs);
|
const char* cacheKey = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
if (numResults > 1)
|
if (numResults > 1)
|
||||||
luaL_error(L, "module must return a single value");
|
luaL_error(L, "module must return a single value");
|
||||||
|
@ -152,10 +153,8 @@ int lua_requirecont(lua_State* L, int status)
|
||||||
|
|
||||||
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
{
|
{
|
||||||
int stackTop = lua_gettop(L);
|
// Discard extra arguments, we only use path
|
||||||
|
lua_settop(L, 1);
|
||||||
// If modifying the state of the stack, please update numStackArgs in the
|
|
||||||
// lua_requirecont continuation function.
|
|
||||||
|
|
||||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||||
if (!lrc)
|
if (!lrc)
|
||||||
|
@ -169,22 +168,42 @@ int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
if (checkRegisteredModules(L, path) == 1)
|
if (checkRegisteredModules(L, path) == 1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
// ResolvedRequire will be destroyed and any string will be pinned to Luau stack, so that luaL_error doesn't need destructors
|
||||||
|
bool resolveError = false;
|
||||||
|
|
||||||
|
{
|
||||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
||||||
|
|
||||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// (1) path, ..., cacheKey
|
if (resolvedRequire.status == ResolvedRequire::Status::ErrorReported)
|
||||||
|
{
|
||||||
|
lua_pushstring(L, resolvedRequire.error.c_str());
|
||||||
|
resolveError = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// (1) path, ..., cacheKey, chunkname, loadname
|
||||||
lua_pushstring(L, resolvedRequire.cacheKey.c_str());
|
lua_pushstring(L, resolvedRequire.cacheKey.c_str());
|
||||||
|
lua_pushstring(L, resolvedRequire.chunkname.c_str());
|
||||||
|
lua_pushstring(L, resolvedRequire.loadname.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert number of arguments before the continuation to check the results.
|
if (resolveError)
|
||||||
int numArgsBeforeLoad = stackTop + 2;
|
lua_error(L); // Error already on top of the stack
|
||||||
lua_pushinteger(L, numArgsBeforeLoad);
|
|
||||||
lua_insert(L, 1);
|
|
||||||
|
|
||||||
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.loadname.c_str());
|
int stackValues = lua_gettop(L);
|
||||||
|
LUAU_ASSERT(stackValues == kRequireStackValues);
|
||||||
|
|
||||||
|
const char* chunkname = lua_tostring(L, -2);
|
||||||
|
const char* loadname = lua_tostring(L, -1);
|
||||||
|
|
||||||
|
int numResults = lrc->load(L, ctx, path, chunkname, loadname);
|
||||||
if (numResults == -1)
|
if (numResults == -1)
|
||||||
{
|
{
|
||||||
if (lua_gettop(L) != numArgsBeforeLoad)
|
if (lua_gettop(L) != stackValues)
|
||||||
luaL_error(L, "stack cannot be modified when require yields");
|
luaL_error(L, "stack cannot be modified when require yields");
|
||||||
|
|
||||||
return lua_yield(L, 0);
|
return lua_yield(L, 0);
|
||||||
|
|
|
@ -398,9 +398,11 @@ target_sources(isocline PRIVATE
|
||||||
target_sources(Luau.CLI.lib PRIVATE
|
target_sources(Luau.CLI.lib PRIVATE
|
||||||
CLI/include/Luau/FileUtils.h
|
CLI/include/Luau/FileUtils.h
|
||||||
CLI/include/Luau/Flags.h
|
CLI/include/Luau/Flags.h
|
||||||
|
CLI/include/Luau/VfsNavigator.h
|
||||||
|
|
||||||
CLI/src/FileUtils.cpp
|
CLI/src/FileUtils.cpp
|
||||||
CLI/src/Flags.cpp
|
CLI/src/Flags.cpp
|
||||||
|
CLI/src/VfsNavigator.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TARGET Luau.Repl.CLI)
|
if(TARGET Luau.Repl.CLI)
|
||||||
|
@ -409,14 +411,12 @@ if(TARGET Luau.Repl.CLI)
|
||||||
CLI/include/Luau/Coverage.h
|
CLI/include/Luau/Coverage.h
|
||||||
CLI/include/Luau/Profiler.h
|
CLI/include/Luau/Profiler.h
|
||||||
CLI/include/Luau/ReplRequirer.h
|
CLI/include/Luau/ReplRequirer.h
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Coverage.cpp
|
CLI/src/Coverage.cpp
|
||||||
CLI/src/Profiler.cpp
|
CLI/src/Profiler.cpp
|
||||||
CLI/src/Repl.cpp
|
CLI/src/Repl.cpp
|
||||||
CLI/src/ReplEntry.cpp
|
CLI/src/ReplEntry.cpp
|
||||||
CLI/src/ReplRequirer.cpp
|
CLI/src/ReplRequirer.cpp
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -424,11 +424,9 @@ if(TARGET Luau.Analyze.CLI)
|
||||||
# Luau.Analyze.CLI Sources
|
# Luau.Analyze.CLI Sources
|
||||||
target_sources(Luau.Analyze.CLI PRIVATE
|
target_sources(Luau.Analyze.CLI PRIVATE
|
||||||
CLI/include/Luau/AnalyzeRequirer.h
|
CLI/include/Luau/AnalyzeRequirer.h
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Analyze.cpp
|
CLI/src/Analyze.cpp
|
||||||
CLI/src/AnalyzeRequirer.cpp
|
CLI/src/AnalyzeRequirer.cpp
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -563,13 +561,11 @@ if(TARGET Luau.CLI.Test)
|
||||||
CLI/include/Luau/Coverage.h
|
CLI/include/Luau/Coverage.h
|
||||||
CLI/include/Luau/Profiler.h
|
CLI/include/Luau/Profiler.h
|
||||||
CLI/include/Luau/ReplRequirer.h
|
CLI/include/Luau/ReplRequirer.h
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Coverage.cpp
|
CLI/src/Coverage.cpp
|
||||||
CLI/src/Profiler.cpp
|
CLI/src/Profiler.cpp
|
||||||
CLI/src/Repl.cpp
|
CLI/src/Repl.cpp
|
||||||
CLI/src/ReplRequirer.cpp
|
CLI/src/ReplRequirer.cpp
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
|
|
||||||
tests/RegisterCallbacks.h
|
tests/RegisterCallbacks.h
|
||||||
tests/RegisterCallbacks.cpp
|
tests/RegisterCallbacks.cpp
|
||||||
|
|
|
@ -329,8 +329,6 @@ LUA_API lua_Destructor lua_getuserdatadtor(lua_State* L, int tag);
|
||||||
LUA_API void lua_setuserdatametatable(lua_State* L, int tag);
|
LUA_API void lua_setuserdatametatable(lua_State* L, int tag);
|
||||||
LUA_API void lua_getuserdatametatable(lua_State* L, int tag);
|
LUA_API void lua_getuserdatametatable(lua_State* L, int tag);
|
||||||
|
|
||||||
LUA_API void lua_setuserdatametatable_DEPRECATED(lua_State* L, int tag, int idx); // Deprecated for incorrect behavior with 'idx != -1'
|
|
||||||
|
|
||||||
LUA_API void lua_setlightuserdataname(lua_State* L, int tag, const char* name);
|
LUA_API void lua_setlightuserdataname(lua_State* L, int tag, const char* name);
|
||||||
LUA_API const char* lua_getlightuserdataname(lua_State* L, int tag);
|
LUA_API const char* lua_getlightuserdataname(lua_State* L, int tag);
|
||||||
|
|
||||||
|
|
|
@ -128,21 +128,6 @@
|
||||||
|
|
||||||
// }==================================================================
|
// }==================================================================
|
||||||
|
|
||||||
/*
|
|
||||||
@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
|
|
||||||
** CHANGE it if your system requires alignments larger than double. (For
|
|
||||||
** instance, if your system supports long doubles and they must be
|
|
||||||
** aligned in 16-byte boundaries, then you should add long double in the
|
|
||||||
** union.) Probably you do not need to change this.
|
|
||||||
*/
|
|
||||||
#define LUAI_USER_ALIGNMENT_T \
|
|
||||||
union \
|
|
||||||
{ \
|
|
||||||
double u; \
|
|
||||||
void* s; \
|
|
||||||
long l; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef LUA_VECTOR_SIZE
|
#ifndef LUA_VECTOR_SIZE
|
||||||
#define LUA_VECTOR_SIZE 3 // must be 3 or 4
|
#define LUA_VECTOR_SIZE 3 // must be 3 or 4
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1479,16 +1479,6 @@ void lua_setuserdatametatable(lua_State* L, int tag)
|
||||||
L->top--;
|
L->top--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lua_setuserdatametatable_DEPRECATED(lua_State* L, int tag, int idx)
|
|
||||||
{
|
|
||||||
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
|
||||||
api_check(L, !L->global->udatamt[tag]); // reassignment not supported
|
|
||||||
StkId o = index2addr(L, idx);
|
|
||||||
api_check(L, ttistable(o));
|
|
||||||
L->global->udatamt[tag] = hvalue(o);
|
|
||||||
L->top--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lua_getuserdatametatable(lua_State* L, int tag)
|
void lua_getuserdatametatable(lua_State* L, int tag)
|
||||||
{
|
{
|
||||||
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
|
|
||||||
|
|
||||||
// internal assertions for in-house debugging
|
// internal assertions for in-house debugging
|
||||||
#define check_exp(c, e) (LUAU_ASSERT(c), (e))
|
#define check_exp(c, e) (LUAU_ASSERT(c), (e))
|
||||||
#define api_check(l, e) LUAU_ASSERT(e)
|
#define api_check(l, e) LUAU_ASSERT(e)
|
||||||
|
|
|
@ -12,11 +12,16 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCurrentLineBounds)
|
||||||
|
|
||||||
static const char* getfuncname(Closure* f);
|
static const char* getfuncname(Closure* f);
|
||||||
|
|
||||||
static int currentpc(lua_State* L, CallInfo* ci)
|
static int currentpc(lua_State* L, CallInfo* ci)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauCurrentLineBounds)
|
||||||
return pcRel(ci->savedpc, ci_func(ci)->l.p);
|
return pcRel(ci->savedpc, ci_func(ci)->l.p);
|
||||||
|
else
|
||||||
|
return pcRel_DEPRECATED(ci->savedpc, ci_func(ci)->l.p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int currentline(lua_State* L, CallInfo* ci)
|
static int currentline(lua_State* L, CallInfo* ci)
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
|
|
||||||
#define pcRel(pc, p) ((pc) ? cast_to(int, (pc) - (p)->code) - 1 : 0)
|
#define pcRel(pc, p) ((pc) && (pc) != (p)->code ? cast_to(int, (pc) - (p)->code) - 1 : 0)
|
||||||
|
// TODO: remove with FFlagLuauCurrentLineBounds
|
||||||
|
#define pcRel_DEPRECATED(pc, p) ((pc) ? cast_to(int, (pc) - (p)->code) - 1 : 0)
|
||||||
|
|
||||||
#define luaG_typeerror(L, o, opname) luaG_typeerrorL(L, o, opname)
|
#define luaG_typeerror(L, o, opname) luaG_typeerrorL(L, o, opname)
|
||||||
#define luaG_forerror(L, o, what) luaG_forerrorL(L, o, what)
|
#define luaG_forerror(L, o, what) luaG_forerrorL(L, o, what)
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||||
|
|
||||||
static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!isdead(g, t));
|
LUAU_ASSERT(!isdead(g, t));
|
||||||
|
@ -462,7 +464,7 @@ static void dumpthread(FILE* f, lua_State* th)
|
||||||
else if (isLua(ci))
|
else if (isLua(ci))
|
||||||
{
|
{
|
||||||
Proto* p = ci_func(ci)->l.p;
|
Proto* p = ci_func(ci)->l.p;
|
||||||
int pc = pcRel(ci->savedpc, p);
|
int pc = FFlag::LuauCurrentLineBounds ? pcRel(ci->savedpc, p) : pcRel_DEPRECATED(ci->savedpc, p);
|
||||||
const LocVar* var = luaF_findlocal(p, int(v - ci->base), pc);
|
const LocVar* var = luaF_findlocal(p, int(v - ci->base), pc);
|
||||||
|
|
||||||
if (var && var->varname)
|
if (var && var->varname)
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The sizes of Luau objects aren't crucial for code correctness, but they are crucial for memory efficiency
|
* The sizes of most Luau objects aren't crucial for code correctness, but they are crucial for memory efficiency
|
||||||
* To prevent some of them accidentally growing and us losing memory without realizing it, we're going to lock
|
* To prevent some of them accidentally growing and us losing memory without realizing it, we're going to lock
|
||||||
* the sizes of all critical structures down.
|
* the sizes of all critical structures down.
|
||||||
*/
|
*/
|
||||||
|
@ -120,10 +120,12 @@ static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
|
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
|
||||||
static_assert(offsetof(Udata, data) == ABISWITCH(16, 16, 12), "size mismatch for userdata header");
|
|
||||||
static_assert(sizeof(LuaTable) == ABISWITCH(48, 32, 32), "size mismatch for table header");
|
static_assert(sizeof(LuaTable) == ABISWITCH(48, 32, 32), "size mismatch for table header");
|
||||||
static_assert(offsetof(Buffer, data) == ABISWITCH(8, 8, 8), "size mismatch for buffer header");
|
static_assert(offsetof(Buffer, data) == ABISWITCH(8, 8, 8), "size mismatch for buffer header");
|
||||||
|
|
||||||
|
// The userdata is designed to provide 16 byte alignment for 16 byte and larger userdata sizes
|
||||||
|
static_assert(offsetof(Udata, data) == 16, "data must be at precise offset provide proper alignment");
|
||||||
|
|
||||||
const size_t kSizeClasses = LUA_SIZECLASSES;
|
const size_t kSizeClasses = LUA_SIZECLASSES;
|
||||||
|
|
||||||
// Controls the number of entries in SizeClassConfig and define the maximum possible paged allocation size
|
// Controls the number of entries in SizeClassConfig and define the maximum possible paged allocation size
|
||||||
|
@ -221,14 +223,15 @@ struct lua_Page
|
||||||
int freeNext; // next free block offset in this page, in bytes; when negative, freeList is used instead
|
int freeNext; // next free block offset in this page, in bytes; when negative, freeList is used instead
|
||||||
int busyBlocks; // number of blocks allocated out of this page
|
int busyBlocks; // number of blocks allocated out of this page
|
||||||
|
|
||||||
union
|
// provide additional padding based on current object size to provide 16 byte alignment of data
|
||||||
{
|
// later static_assert checks that this requirement is held
|
||||||
|
char padding[sizeof(void*) == 8 ? 8 : 12];
|
||||||
|
|
||||||
char data[1];
|
char data[1];
|
||||||
double align1;
|
|
||||||
void* align2;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(offsetof(lua_Page, data) % 16 == 0, "data must be 16 byte aligned to provide properly aligned allocation of userdata objects");
|
||||||
|
|
||||||
l_noret luaM_toobig(lua_State* L)
|
l_noret luaM_toobig(lua_State* L)
|
||||||
{
|
{
|
||||||
luaG_runerror(L, "memory allocation error: block too big");
|
luaG_runerror(L, "memory allocation error: block too big");
|
||||||
|
|
|
@ -265,11 +265,9 @@ typedef struct Udata
|
||||||
|
|
||||||
struct LuaTable* metatable;
|
struct LuaTable* metatable;
|
||||||
|
|
||||||
union
|
// userdata is allocated right after the header
|
||||||
{
|
// while the alignment is only 8 here, for sizes starting at 16 bytes, 16 byte alignment is provided
|
||||||
char data[1]; // userdata is allocated right after the header
|
alignas(8) char data[1];
|
||||||
L_Umaxalign dummy; // ensures maximum alignment for data
|
|
||||||
};
|
|
||||||
} Udata;
|
} Udata;
|
||||||
|
|
||||||
typedef struct LuauBuffer
|
typedef struct LuauBuffer
|
||||||
|
@ -278,11 +276,7 @@ typedef struct LuauBuffer
|
||||||
|
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
|
||||||
union
|
alignas(8) char data[1];
|
||||||
{
|
|
||||||
char data[1]; // buffer is allocated right after the header
|
|
||||||
L_Umaxalign dummy; // ensures maximum alignment for data
|
|
||||||
};
|
|
||||||
} Buffer;
|
} Buffer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -366,7 +366,7 @@ static TValue* arrayornewkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
int k;
|
int k;
|
||||||
double n = nvalue(key);
|
double n = nvalue(key);
|
||||||
luai_num2int(k, n);
|
luai_num2int(k, n);
|
||||||
if (luai_numeq(cast_num(k), n) && cast_to(unsigned int, k - 1) < cast_to(unsigned int, t->sizearray))
|
if (luai_numeq(cast_num(k), n) && unsigned(k) - 1 < unsigned(t->sizearray))
|
||||||
return &t->array[k - 1];
|
return &t->array[k - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,7 +604,7 @@ static TValue* newkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
const TValue* luaH_getnum(LuaTable* t, int key)
|
const TValue* luaH_getnum(LuaTable* t, int key)
|
||||||
{
|
{
|
||||||
// (1 <= key && key <= t->sizearray)
|
// (1 <= key && key <= t->sizearray)
|
||||||
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
if (unsigned(key) - 1 < unsigned(t->sizearray))
|
||||||
return &t->array[key - 1];
|
return &t->array[key - 1];
|
||||||
else if (t->node != dummynode)
|
else if (t->node != dummynode)
|
||||||
{
|
{
|
||||||
|
@ -701,7 +701,7 @@ TValue* luaH_newkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
TValue* luaH_setnum(lua_State* L, LuaTable* t, int key)
|
TValue* luaH_setnum(lua_State* L, LuaTable* t, int key)
|
||||||
{
|
{
|
||||||
// (1 <= key && key <= t->sizearray)
|
// (1 <= key && key <= t->sizearray)
|
||||||
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
if (unsigned(key) - 1 < unsigned(t->sizearray))
|
||||||
return &t->array[key - 1];
|
return &t->array[key - 1];
|
||||||
// hash fallback
|
// hash fallback
|
||||||
const TValue* p = luaH_getnum(t, key);
|
const TValue* p = luaH_getnum(t, key);
|
||||||
|
|
|
@ -95,10 +95,8 @@ static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
|
||||||
|
|
||||||
int n = e - f + 1; // number of elements to move
|
int n = e - f + 1; // number of elements to move
|
||||||
|
|
||||||
if (cast_to(unsigned int, f - 1) < cast_to(unsigned int, src->sizearray) &&
|
if (unsigned(f) - 1 < unsigned(src->sizearray) && unsigned(t) - 1 < unsigned(dst->sizearray) &&
|
||||||
cast_to(unsigned int, t - 1) < cast_to(unsigned int, dst->sizearray) &&
|
unsigned(f) - 1 + unsigned(n) <= unsigned(src->sizearray) && unsigned(t) - 1 + unsigned(n) <= unsigned(dst->sizearray))
|
||||||
cast_to(unsigned int, f - 1 + n) <= cast_to(unsigned int, src->sizearray) &&
|
|
||||||
cast_to(unsigned int, t - 1 + n) <= cast_to(unsigned int, dst->sizearray))
|
|
||||||
{
|
{
|
||||||
TValue* srcarray = src->array;
|
TValue* srcarray = src->array;
|
||||||
TValue* dstarray = dst->array;
|
TValue* dstarray = dst->array;
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
// special tag value is used for newproxy-created user data (all other user data objects are host-exposed)
|
// special tag value is used for newproxy-created user data (all other user data objects are host-exposed)
|
||||||
#define UTAG_PROXY (LUA_UTAG_LIMIT + 1)
|
#define UTAG_PROXY (LUA_UTAG_LIMIT + 1)
|
||||||
|
|
||||||
#define sizeudata(len) (offsetof(Udata, data) + len)
|
// userdata larger than 16 bytes will be extended to guarantee 16 byte alignment of subsequent blocks
|
||||||
|
#define sizeudata(len) (offsetof(Udata, data) + (len > 16 ? ((len + 15) & ~15) : len))
|
||||||
|
|
||||||
LUAI_FUNC Udata* luaU_newudata(lua_State* L, size_t s, int tag);
|
LUAI_FUNC Udata* luaU_newudata(lua_State* L, size_t s, int tag);
|
||||||
LUAI_FUNC void luaU_freeudata(lua_State* L, Udata* u, struct lua_Page* page);
|
LUAI_FUNC void luaU_freeudata(lua_State* L, Udata* u, struct lua_Page* page);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||||
|
|
||||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#if __has_warning("-Wc99-designator")
|
#if __has_warning("-Wc99-designator")
|
||||||
|
@ -147,6 +149,32 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
||||||
L->base = L->ci->base;
|
L->base = L->ci->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauCurrentLineBounds)
|
||||||
|
{
|
||||||
|
Closure* cl = clvalue(L->ci->func);
|
||||||
|
|
||||||
|
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
||||||
|
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
||||||
|
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
||||||
|
const Instruction* oldsavedpc = L->ci->savedpc;
|
||||||
|
|
||||||
|
if (L->ci->savedpc && L->ci->savedpc != cl->l.p->code + cl->l.p->sizecode)
|
||||||
|
L->ci->savedpc++;
|
||||||
|
|
||||||
|
luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size
|
||||||
|
L->ci->top = L->top + LUA_MINSTACK;
|
||||||
|
LUAU_ASSERT(L->ci->top <= L->stack_last);
|
||||||
|
|
||||||
|
lua_Debug ar;
|
||||||
|
ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p));
|
||||||
|
ar.userdata = userdata;
|
||||||
|
|
||||||
|
hook(L, &ar);
|
||||||
|
|
||||||
|
L->ci->savedpc = oldsavedpc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
||||||
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
||||||
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
||||||
|
@ -167,6 +195,7 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
||||||
|
|
||||||
if (L->ci->savedpc)
|
if (L->ci->savedpc)
|
||||||
L->ci->savedpc--;
|
L->ci->savedpc--;
|
||||||
|
}
|
||||||
|
|
||||||
L->ci->top = restorestack(L, ci_top);
|
L->ci->top = restorestack(L, ci_top);
|
||||||
L->top = restorestack(L, top);
|
L->top = restorestack(L, top);
|
||||||
|
@ -646,7 +675,7 @@ reentry:
|
||||||
int index = int(indexd);
|
int index = int(indexd);
|
||||||
|
|
||||||
// index has to be an exact integer and in-bounds for the array portion
|
// index has to be an exact integer and in-bounds for the array portion
|
||||||
if (LUAU_LIKELY(unsigned(index - 1) < unsigned(h->sizearray) && !h->metatable && double(index) == indexd))
|
if (LUAU_LIKELY(unsigned(index) - 1 < unsigned(h->sizearray) && !h->metatable && double(index) == indexd))
|
||||||
{
|
{
|
||||||
setobj2s(L, ra, &h->array[unsigned(index - 1)]);
|
setobj2s(L, ra, &h->array[unsigned(index - 1)]);
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
@ -676,7 +705,7 @@ reentry:
|
||||||
int index = int(indexd);
|
int index = int(indexd);
|
||||||
|
|
||||||
// index has to be an exact integer and in-bounds for the array portion
|
// index has to be an exact integer and in-bounds for the array portion
|
||||||
if (LUAU_LIKELY(unsigned(index - 1) < unsigned(h->sizearray) && !h->metatable && !h->readonly && double(index) == indexd))
|
if (LUAU_LIKELY(unsigned(index) - 1 < unsigned(h->sizearray) && !h->metatable && !h->readonly && double(index) == indexd))
|
||||||
{
|
{
|
||||||
setobj2t(L, &h->array[unsigned(index - 1)], ra);
|
setobj2t(L, &h->array[unsigned(index - 1)], ra);
|
||||||
luaC_barriert(L, h, ra);
|
luaC_barriert(L, h, ra);
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens
|
LUAU_FASTFLAGVARIABLE(LuauLoadNoOomThrow)
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct TempBuffer
|
struct TempBuffer
|
||||||
{
|
{
|
||||||
|
@ -21,11 +22,20 @@ struct TempBuffer
|
||||||
T* data;
|
T* data;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
||||||
|
TempBuffer()
|
||||||
|
: L(NULL)
|
||||||
|
, data(NULL)
|
||||||
|
, count(0)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauLoadNoOomThrow);
|
||||||
|
}
|
||||||
|
|
||||||
TempBuffer(lua_State* L, size_t count)
|
TempBuffer(lua_State* L, size_t count)
|
||||||
: L(L)
|
: L(L)
|
||||||
, data(luaM_newarray(L, count, T, 0))
|
, data(luaM_newarray(L, count, T, 0))
|
||||||
, count(count)
|
, count(count)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauLoadNoOomThrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
TempBuffer(const TempBuffer&) = delete;
|
TempBuffer(const TempBuffer&) = delete;
|
||||||
|
@ -36,9 +46,18 @@ struct TempBuffer
|
||||||
|
|
||||||
~TempBuffer() noexcept
|
~TempBuffer() noexcept
|
||||||
{
|
{
|
||||||
|
if (data)
|
||||||
luaM_freearray(L, data, count, T, 0);
|
luaM_freearray(L, data, count, T, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void allocate(lua_State* L, size_t count)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(this->L == nullptr);
|
||||||
|
this->L = L;
|
||||||
|
this->data = luaM_newarray(L, count, T, 0);
|
||||||
|
this->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
T& operator[](size_t index)
|
T& operator[](size_t index)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(index < count);
|
LUAU_ASSERT(index < count);
|
||||||
|
@ -242,7 +261,360 @@ static void remapUserdataTypes(char* data, size_t size, uint8_t* userdataRemappi
|
||||||
LUAU_ASSERT(offset == size);
|
LUAU_ASSERT(offset == size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
static int loadsafe(
|
||||||
|
lua_State* L,
|
||||||
|
TempBuffer<TString*>& strings,
|
||||||
|
TempBuffer<Proto*>& protos,
|
||||||
|
const char* chunkname,
|
||||||
|
const char* data,
|
||||||
|
size_t size,
|
||||||
|
int env
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
uint8_t version = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
|
||||||
|
// 0 means the rest of the bytecode is the error message
|
||||||
|
if (version == 0)
|
||||||
|
{
|
||||||
|
char chunkbuf[LUA_IDSIZE];
|
||||||
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||||
|
lua_pushfstring(L, "%s%.*s", chunkid, int(size - offset), data + offset);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
||||||
|
{
|
||||||
|
char chunkbuf[LUA_IDSIZE];
|
||||||
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||||
|
lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t typesversion = 0;
|
||||||
|
|
||||||
|
if (version >= 4)
|
||||||
|
{
|
||||||
|
typesversion = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
if (typesversion < LBC_TYPE_VERSION_MIN || typesversion > LBC_TYPE_VERSION_MAX)
|
||||||
|
{
|
||||||
|
char chunkbuf[LUA_IDSIZE];
|
||||||
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||||
|
lua_pushfstring(
|
||||||
|
L, "%s: bytecode type version mismatch (expected [%d..%d], got %d)", chunkid, LBC_TYPE_VERSION_MIN, LBC_TYPE_VERSION_MAX, typesversion
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// env is 0 for current environment and a stack index otherwise
|
||||||
|
LuaTable* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
||||||
|
|
||||||
|
TString* source = luaS_new(L, chunkname);
|
||||||
|
|
||||||
|
// string table
|
||||||
|
unsigned int stringCount = readVarInt(data, size, offset);
|
||||||
|
strings.allocate(L, stringCount);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < stringCount; ++i)
|
||||||
|
{
|
||||||
|
unsigned int length = readVarInt(data, size, offset);
|
||||||
|
|
||||||
|
strings[i] = luaS_newlstr(L, data + offset, length);
|
||||||
|
offset += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// userdata type remapping table
|
||||||
|
// for unknown userdata types, the entry will remap to common 'userdata' type
|
||||||
|
const uint32_t userdataTypeLimit = LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE;
|
||||||
|
uint8_t userdataRemapping[userdataTypeLimit];
|
||||||
|
|
||||||
|
if (typesversion == 3)
|
||||||
|
{
|
||||||
|
memset(userdataRemapping, LBC_TYPE_USERDATA, userdataTypeLimit);
|
||||||
|
|
||||||
|
uint8_t index = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
while (index != 0)
|
||||||
|
{
|
||||||
|
TString* name = readString(strings, data, size, offset);
|
||||||
|
|
||||||
|
if (uint32_t(index - 1) < userdataTypeLimit)
|
||||||
|
{
|
||||||
|
if (auto cb = L->global->ecb.gettypemapping)
|
||||||
|
userdataRemapping[index - 1] = cb(L, getstr(name), name->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = read<uint8_t>(data, size, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// proto table
|
||||||
|
unsigned int protoCount = readVarInt(data, size, offset);
|
||||||
|
protos.allocate(L, protoCount);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < protoCount; ++i)
|
||||||
|
{
|
||||||
|
Proto* p = luaF_newproto(L);
|
||||||
|
p->source = source;
|
||||||
|
p->bytecodeid = int(i);
|
||||||
|
|
||||||
|
p->maxstacksize = read<uint8_t>(data, size, offset);
|
||||||
|
p->numparams = read<uint8_t>(data, size, offset);
|
||||||
|
p->nups = read<uint8_t>(data, size, offset);
|
||||||
|
p->is_vararg = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
if (version >= 4)
|
||||||
|
{
|
||||||
|
p->flags = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
if (typesversion == 1)
|
||||||
|
{
|
||||||
|
uint32_t typesize = readVarInt(data, size, offset);
|
||||||
|
|
||||||
|
if (typesize)
|
||||||
|
{
|
||||||
|
uint8_t* types = (uint8_t*)data + offset;
|
||||||
|
|
||||||
|
LUAU_ASSERT(typesize == unsigned(2 + p->numparams));
|
||||||
|
LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION);
|
||||||
|
LUAU_ASSERT(types[1] == p->numparams);
|
||||||
|
|
||||||
|
// transform v1 into v2 format
|
||||||
|
int headersize = typesize > 127 ? 4 : 3;
|
||||||
|
|
||||||
|
p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat);
|
||||||
|
p->sizetypeinfo = headersize + typesize;
|
||||||
|
|
||||||
|
if (headersize == 4)
|
||||||
|
{
|
||||||
|
p->typeinfo[0] = (typesize & 127) | (1 << 7);
|
||||||
|
p->typeinfo[1] = typesize >> 7;
|
||||||
|
p->typeinfo[2] = 0;
|
||||||
|
p->typeinfo[3] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p->typeinfo[0] = uint8_t(typesize);
|
||||||
|
p->typeinfo[1] = 0;
|
||||||
|
p->typeinfo[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(p->typeinfo + headersize, types, typesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += typesize;
|
||||||
|
}
|
||||||
|
else if (typesversion == 2 || typesversion == 3)
|
||||||
|
{
|
||||||
|
uint32_t typesize = readVarInt(data, size, offset);
|
||||||
|
|
||||||
|
if (typesize)
|
||||||
|
{
|
||||||
|
uint8_t* types = (uint8_t*)data + offset;
|
||||||
|
|
||||||
|
p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat);
|
||||||
|
p->sizetypeinfo = typesize;
|
||||||
|
memcpy(p->typeinfo, types, typesize);
|
||||||
|
offset += typesize;
|
||||||
|
|
||||||
|
if (typesversion == 3)
|
||||||
|
{
|
||||||
|
remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int sizecode = readVarInt(data, size, offset);
|
||||||
|
p->code = luaM_newarray(L, sizecode, Instruction, p->memcat);
|
||||||
|
p->sizecode = sizecode;
|
||||||
|
|
||||||
|
for (int j = 0; j < p->sizecode; ++j)
|
||||||
|
p->code[j] = read<uint32_t>(data, size, offset);
|
||||||
|
|
||||||
|
p->codeentry = p->code;
|
||||||
|
|
||||||
|
const int sizek = readVarInt(data, size, offset);
|
||||||
|
p->k = luaM_newarray(L, sizek, TValue, p->memcat);
|
||||||
|
p->sizek = sizek;
|
||||||
|
|
||||||
|
// Initialize the constants to nil to ensure they have a valid state
|
||||||
|
// in the event that some operation in the following loop fails with
|
||||||
|
// an exception.
|
||||||
|
for (int j = 0; j < p->sizek; ++j)
|
||||||
|
{
|
||||||
|
setnilvalue(&p->k[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < p->sizek; ++j)
|
||||||
|
{
|
||||||
|
switch (read<uint8_t>(data, size, offset))
|
||||||
|
{
|
||||||
|
case LBC_CONSTANT_NIL:
|
||||||
|
// All constants have already been pre-initialized to nil
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LBC_CONSTANT_BOOLEAN:
|
||||||
|
{
|
||||||
|
uint8_t v = read<uint8_t>(data, size, offset);
|
||||||
|
setbvalue(&p->k[j], v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_NUMBER:
|
||||||
|
{
|
||||||
|
double v = read<double>(data, size, offset);
|
||||||
|
setnvalue(&p->k[j], v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_VECTOR:
|
||||||
|
{
|
||||||
|
float x = read<float>(data, size, offset);
|
||||||
|
float y = read<float>(data, size, offset);
|
||||||
|
float z = read<float>(data, size, offset);
|
||||||
|
float w = read<float>(data, size, offset);
|
||||||
|
(void)w;
|
||||||
|
setvvalue(&p->k[j], x, y, z, w);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_STRING:
|
||||||
|
{
|
||||||
|
TString* v = readString(strings, data, size, offset);
|
||||||
|
setsvalue(L, &p->k[j], v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_IMPORT:
|
||||||
|
{
|
||||||
|
uint32_t iid = read<uint32_t>(data, size, offset);
|
||||||
|
resolveImportSafe(L, envt, p->k, iid);
|
||||||
|
setobj(L, &p->k[j], L->top - 1);
|
||||||
|
L->top--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_TABLE:
|
||||||
|
{
|
||||||
|
int keys = readVarInt(data, size, offset);
|
||||||
|
LuaTable* h = luaH_new(L, 0, keys);
|
||||||
|
for (int i = 0; i < keys; ++i)
|
||||||
|
{
|
||||||
|
int key = readVarInt(data, size, offset);
|
||||||
|
TValue* val = luaH_set(L, h, &p->k[key]);
|
||||||
|
setnvalue(val, 0.0);
|
||||||
|
}
|
||||||
|
sethvalue(L, &p->k[j], h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LBC_CONSTANT_CLOSURE:
|
||||||
|
{
|
||||||
|
uint32_t fid = readVarInt(data, size, offset);
|
||||||
|
Closure* cl = luaF_newLclosure(L, protos[fid]->nups, envt, protos[fid]);
|
||||||
|
cl->preload = (cl->nupvalues > 0);
|
||||||
|
setclvalue(L, &p->k[j], cl);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
LUAU_ASSERT(!"Unexpected constant kind");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int sizep = readVarInt(data, size, offset);
|
||||||
|
p->p = luaM_newarray(L, sizep, Proto*, p->memcat);
|
||||||
|
p->sizep = sizep;
|
||||||
|
|
||||||
|
for (int j = 0; j < p->sizep; ++j)
|
||||||
|
{
|
||||||
|
uint32_t fid = readVarInt(data, size, offset);
|
||||||
|
p->p[j] = protos[fid];
|
||||||
|
}
|
||||||
|
|
||||||
|
p->linedefined = readVarInt(data, size, offset);
|
||||||
|
p->debugname = readString(strings, data, size, offset);
|
||||||
|
|
||||||
|
uint8_t lineinfo = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
if (lineinfo)
|
||||||
|
{
|
||||||
|
p->linegaplog2 = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
int intervals = ((p->sizecode - 1) >> p->linegaplog2) + 1;
|
||||||
|
int absoffset = (p->sizecode + 3) & ~3;
|
||||||
|
|
||||||
|
const int sizelineinfo = absoffset + intervals * sizeof(int);
|
||||||
|
p->lineinfo = luaM_newarray(L, sizelineinfo, uint8_t, p->memcat);
|
||||||
|
p->sizelineinfo = sizelineinfo;
|
||||||
|
|
||||||
|
p->abslineinfo = (int*)(p->lineinfo + absoffset);
|
||||||
|
|
||||||
|
uint8_t lastoffset = 0;
|
||||||
|
for (int j = 0; j < p->sizecode; ++j)
|
||||||
|
{
|
||||||
|
lastoffset += read<uint8_t>(data, size, offset);
|
||||||
|
p->lineinfo[j] = lastoffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastline = 0;
|
||||||
|
for (int j = 0; j < intervals; ++j)
|
||||||
|
{
|
||||||
|
lastline += read<int32_t>(data, size, offset);
|
||||||
|
p->abslineinfo[j] = lastline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t debuginfo = read<uint8_t>(data, size, offset);
|
||||||
|
|
||||||
|
if (debuginfo)
|
||||||
|
{
|
||||||
|
const int sizelocvars = readVarInt(data, size, offset);
|
||||||
|
p->locvars = luaM_newarray(L, sizelocvars, LocVar, p->memcat);
|
||||||
|
p->sizelocvars = sizelocvars;
|
||||||
|
|
||||||
|
for (int j = 0; j < p->sizelocvars; ++j)
|
||||||
|
{
|
||||||
|
p->locvars[j].varname = readString(strings, data, size, offset);
|
||||||
|
p->locvars[j].startpc = readVarInt(data, size, offset);
|
||||||
|
p->locvars[j].endpc = readVarInt(data, size, offset);
|
||||||
|
p->locvars[j].reg = read<uint8_t>(data, size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int sizeupvalues = readVarInt(data, size, offset);
|
||||||
|
LUAU_ASSERT(sizeupvalues == p->nups);
|
||||||
|
|
||||||
|
p->upvalues = luaM_newarray(L, sizeupvalues, TString*, p->memcat);
|
||||||
|
p->sizeupvalues = sizeupvalues;
|
||||||
|
|
||||||
|
for (int j = 0; j < p->sizeupvalues; ++j)
|
||||||
|
{
|
||||||
|
p->upvalues[j] = readString(strings, data, size, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protos[i] = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "main" proto is pushed to Lua stack
|
||||||
|
uint32_t mainid = readVarInt(data, size, offset);
|
||||||
|
Proto* main = protos[mainid];
|
||||||
|
|
||||||
|
luaC_threadbarrier(L);
|
||||||
|
|
||||||
|
Closure* cl = luaF_newLclosure(L, 0, envt, main);
|
||||||
|
setclvalue(L, L->top, cl);
|
||||||
|
incr_top(L);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int luau_load_DEPRECATED(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
||||||
{
|
{
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
|
||||||
|
@ -592,3 +964,54 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauLoadNoOomThrow)
|
||||||
|
return luau_load_DEPRECATED(L, chunkname, data, size, env);
|
||||||
|
|
||||||
|
// we will allocate a fair amount of memory so check GC before we do
|
||||||
|
luaC_checkGC(L);
|
||||||
|
|
||||||
|
// pause GC for the duration of deserialization - some objects we're creating aren't rooted
|
||||||
|
const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX};
|
||||||
|
|
||||||
|
struct LoadContext
|
||||||
|
{
|
||||||
|
TempBuffer<TString*> strings;
|
||||||
|
TempBuffer<Proto*> protos;
|
||||||
|
const char* chunkname;
|
||||||
|
const char* data;
|
||||||
|
size_t size;
|
||||||
|
int env;
|
||||||
|
|
||||||
|
int result;
|
||||||
|
|
||||||
|
static void run(lua_State* L, void* ud)
|
||||||
|
{
|
||||||
|
LoadContext* ctx = (LoadContext*)ud;
|
||||||
|
|
||||||
|
ctx->result = loadsafe(L, ctx->strings, ctx->protos, ctx->chunkname, ctx->data, ctx->size, ctx->env);
|
||||||
|
}
|
||||||
|
} ctx = {
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
chunkname,
|
||||||
|
data,
|
||||||
|
size,
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
|
||||||
|
int status = luaD_rawrunprotected(L, &LoadContext::run, &ctx);
|
||||||
|
|
||||||
|
// load can either succeed or get an OOM error, any other errors should be handled internally
|
||||||
|
LUAU_ASSERT(status == LUA_OK || status == LUA_ERRMEM);
|
||||||
|
|
||||||
|
if (status == LUA_ERRMEM)
|
||||||
|
{
|
||||||
|
lua_pushstring(L, LUA_MEMERRMSG); // out-of-memory error message doesn't require an allocation
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.result;
|
||||||
|
}
|
||||||
|
|
|
@ -85,8 +85,8 @@ set(LUAU_PB_SOURCES ${LUAU_PB_DIR}/luau.pb.cc ${LUAU_PB_DIR}/luau.pb.h)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${LUAU_PB_SOURCES}
|
OUTPUT ${LUAU_PB_SOURCES}
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${LUAU_PB_DIR}
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${LUAU_PB_DIR}
|
||||||
COMMAND $<TARGET_FILE:protobuf::protoc> ${CMAKE_CURRENT_SOURCE_DIR}/luau.proto --proto_path=${CMAKE_CURRENT_SOURCE_DIR} --cpp_out=${LUAU_PB_DIR}
|
COMMAND ${protobuf_PROTOC} ${CMAKE_CURRENT_SOURCE_DIR}/luau.proto --proto_path=${CMAKE_CURRENT_SOURCE_DIR} --cpp_out=${LUAU_PB_DIR}
|
||||||
DEPENDS protobuf::protoc ${CMAKE_CURRENT_SOURCE_DIR}/luau.proto
|
DEPENDS ${protobuf_PROTOC} ${CMAKE_CURRENT_SOURCE_DIR}/luau.proto
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(Luau.Fuzz.Proto)
|
add_executable(Luau.Fuzz.Proto)
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
struct JsonEncoderFixture
|
struct JsonEncoderFixture
|
||||||
|
@ -460,9 +459,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr")
|
||||||
AstStat* expr = expectParseStatement("@checked function a(b) return c end");
|
AstStat* expr = expectParseStatement("@checked function a(b) return c end");
|
||||||
|
|
||||||
std::string_view expected =
|
std::string_view expected =
|
||||||
FFlag::LuauFixFunctionWithAttributesStartLocation
|
R"({"type":"AstStatFunction","location":"0,0 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,0 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})";
|
||||||
? R"({"type":"AstStatFunction","location":"0,0 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,0 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})"
|
|
||||||
: R"({"type":"AstStatFunction","location":"0,9 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,9 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})";
|
|
||||||
|
|
||||||
CHECK(toJson(expr) == expected);
|
CHECK(toJson(expr) == expected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,7 @@
|
||||||
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -3121,6 +3118,20 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons")
|
||||||
CHECK_EQ(ac.context, AutocompleteContext::String);
|
CHECK_EQ(ac.context, AutocompleteContext::String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key_iso")
|
||||||
|
{
|
||||||
|
|
||||||
|
check(R"(
|
||||||
|
type Direction = "up" | "down"
|
||||||
|
local b: {[Direction]: boolean} = {["@2"] = true}
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('2');
|
||||||
|
|
||||||
|
CHECK(ac.entryMap.count("up"));
|
||||||
|
CHECK(ac.entryMap.count("down"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key")
|
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key")
|
||||||
{
|
{
|
||||||
check(R"(
|
check(R"(
|
||||||
|
@ -4431,7 +4442,6 @@ local x = 1 + result.
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAutocompleteUnionCopyPreviousSeen, true};
|
|
||||||
check(R"(
|
check(R"(
|
||||||
local table1: {ChildClass} = {}
|
local table1: {ChildClass} = {}
|
||||||
local table2 = {}
|
local table2 = {}
|
||||||
|
@ -4443,17 +4453,24 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
// RIDE-11517: This should *really* be the members of `ChildClass`, but
|
|
||||||
// would previously stack overflow.
|
if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization3)
|
||||||
|
{
|
||||||
|
// This `if` statement is because `LuauNonReentrantGeneralization3`
|
||||||
|
// sets some flags
|
||||||
|
CHECK(ac.entryMap.count("BaseMethod") > 0);
|
||||||
|
CHECK(ac.entryMap.count("Method") > 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, we don't infer anything for `value`, which is _fine_.
|
||||||
CHECK(ac.entryMap.empty());
|
CHECK(ac.entryMap.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_has_types_definitions")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_has_types_definitions")
|
||||||
{
|
{
|
||||||
// Needs new global initialization in the Fixture, but can't place the flag inside the base Fixture
|
|
||||||
if (!FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
|
@ -4468,10 +4485,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_private_scope")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_private_scope")
|
||||||
{
|
{
|
||||||
// Needs new global initialization in the Fixture, but can't place the flag inside the base Fixture
|
|
||||||
if (!FFlag::LuauUserTypeFunTypecheck)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
// Global scope polution by the embedder has no effect
|
// Global scope polution by the embedder has no effect
|
||||||
|
@ -4504,7 +4517,6 @@ this@2
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_eval_in_autocomplete")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_eval_in_autocomplete")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag luauTypeFunResultInAutocomplete{FFlag::LuauTypeFunResultInAutocomplete, true};
|
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
type function foo(x)
|
type function foo(x)
|
||||||
|
|
|
@ -23,6 +23,7 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
|
LUAU_FASTFLAG(LuauCompileFixTypeFunctionSkip)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -2971,6 +2972,33 @@ TEST_CASE("TypeFunction")
|
||||||
CHECK_NOTHROW(Luau::compileOrThrow(bcb, "type function a() return types.any end", options, parseOptions));
|
CHECK_NOTHROW(Luau::compileOrThrow(bcb, "type function a() return types.any end", options, parseOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("NoTypeFunctionsInBytecode")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauCompileFixTypeFunctionSkip{FFlag::LuauCompileFixTypeFunctionSkip, true};
|
||||||
|
|
||||||
|
Luau::BytecodeBuilder bcb;
|
||||||
|
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code);
|
||||||
|
Luau::compileOrThrow(bcb, R"(
|
||||||
|
type function a() return types.any end
|
||||||
|
function b() return 2 end
|
||||||
|
return b()
|
||||||
|
)");
|
||||||
|
|
||||||
|
CHECK_EQ("\n" + bcb.dumpEverything(), R"(
|
||||||
|
Function 0 (b):
|
||||||
|
LOADN R0 2
|
||||||
|
RETURN R0 1
|
||||||
|
|
||||||
|
Function 1 (??):
|
||||||
|
DUPCLOSURE R0 K0 ['b']
|
||||||
|
SETGLOBAL R0 K1 ['b']
|
||||||
|
GETGLOBAL R0 K1 ['b']
|
||||||
|
CALL R0 0 -1
|
||||||
|
RETURN R0 -1
|
||||||
|
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("DebugLineInfo")
|
TEST_CASE("DebugLineInfo")
|
||||||
{
|
{
|
||||||
Luau::BytecodeBuilder bcb;
|
Luau::BytecodeBuilder bcb;
|
||||||
|
|
|
@ -31,10 +31,15 @@ extern int optimizationLevel;
|
||||||
void luaC_fullgc(lua_State* L);
|
void luaC_fullgc(lua_State* L);
|
||||||
void luaC_validate(lua_State* L);
|
void luaC_validate(lua_State* L);
|
||||||
|
|
||||||
|
// internal functions, declared in lvm.h - not exposed via lua.h
|
||||||
|
void luau_callhook(lua_State* L, lua_Hook hook, void* userdata);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||||
|
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||||
|
LUAU_FASTFLAG(LuauLoadNoOomThrow)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
{
|
{
|
||||||
|
@ -1424,6 +1429,119 @@ TEST_CASE("Debugger")
|
||||||
CHECK(stephits > 100); // note; this will depend on number of instructions which can vary, so we just make sure the callback gets hit often
|
CHECK(stephits > 100); // note; this will depend on number of instructions which can vary, so we just make sure the callback gets hit often
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("InterruptInspection")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true};
|
||||||
|
|
||||||
|
static bool skipbreak = false;
|
||||||
|
|
||||||
|
runConformance(
|
||||||
|
"basic.luau",
|
||||||
|
[](lua_State* L)
|
||||||
|
{
|
||||||
|
lua_Callbacks* cb = lua_callbacks(L);
|
||||||
|
|
||||||
|
cb->interrupt = [](lua_State* L, int gc)
|
||||||
|
{
|
||||||
|
if (gc >= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!lua_isyieldable(L))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!skipbreak)
|
||||||
|
lua_break(L);
|
||||||
|
|
||||||
|
skipbreak = !skipbreak;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[](lua_State* L)
|
||||||
|
{
|
||||||
|
// Debug info can be retrieved from every location
|
||||||
|
lua_Debug ar = {};
|
||||||
|
CHECK(lua_getinfo(L, 0, "nsl", &ar));
|
||||||
|
|
||||||
|
// Simulating a hook being called from the original break location
|
||||||
|
luau_callhook(
|
||||||
|
L,
|
||||||
|
[](lua_State* L, lua_Debug* ar)
|
||||||
|
{
|
||||||
|
CHECK(lua_getinfo(L, 0, "nsl", ar));
|
||||||
|
},
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
/* skipCodegen */ true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("InterruptErrorInspection")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true};
|
||||||
|
|
||||||
|
// for easy access in no-capture lambda
|
||||||
|
static int target = 0;
|
||||||
|
static int step = 0;
|
||||||
|
|
||||||
|
std::string source = R"(
|
||||||
|
function fib(n)
|
||||||
|
return n < 2 and 1 or fib(n - 1) + fib(n - 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
fib(5)
|
||||||
|
)";
|
||||||
|
|
||||||
|
for (target = 0; target < 20; target++)
|
||||||
|
{
|
||||||
|
step = 0;
|
||||||
|
|
||||||
|
StateRef globalState(luaL_newstate(), lua_close);
|
||||||
|
lua_State* L = globalState.get();
|
||||||
|
|
||||||
|
luaL_openlibs(L);
|
||||||
|
luaL_sandbox(L);
|
||||||
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
|
size_t bytecodeSize = 0;
|
||||||
|
char* bytecode = luau_compile(source.data(), source.size(), nullptr, &bytecodeSize);
|
||||||
|
int result = luau_load(L, "=InterruptErrorInspection", bytecode, bytecodeSize, 0);
|
||||||
|
free(bytecode);
|
||||||
|
|
||||||
|
REQUIRE(result == LUA_OK);
|
||||||
|
|
||||||
|
lua_Callbacks* cb = lua_callbacks(L);
|
||||||
|
|
||||||
|
cb->interrupt = [](lua_State* L, int gc)
|
||||||
|
{
|
||||||
|
if (gc >= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (step == target)
|
||||||
|
luaL_error(L, "test");
|
||||||
|
|
||||||
|
step++;
|
||||||
|
};
|
||||||
|
|
||||||
|
lua_resume(L, nullptr, 0);
|
||||||
|
|
||||||
|
// Debug info can be retrieved from every location
|
||||||
|
lua_Debug ar = {};
|
||||||
|
CHECK(lua_getinfo(L, 0, "nsl", &ar));
|
||||||
|
|
||||||
|
// Simulating a hook being called from the original break location
|
||||||
|
luau_callhook(
|
||||||
|
L,
|
||||||
|
[](lua_State* L, lua_Debug* ar)
|
||||||
|
{
|
||||||
|
CHECK(lua_getinfo(L, 0, "nsl", ar));
|
||||||
|
},
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("NDebugGetUpValue")
|
TEST_CASE("NDebugGetUpValue")
|
||||||
{
|
{
|
||||||
lua_CompileOptions copts = defaultOptions();
|
lua_CompileOptions copts = defaultOptions();
|
||||||
|
@ -2495,6 +2613,46 @@ TEST_CASE("UserdataApi")
|
||||||
CHECK(dtorhits == 42);
|
CHECK(dtorhits == 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// provide alignment of 16 for userdata objects with size of 16 and up as long as the Luau allocation functions supports it
|
||||||
|
TEST_CASE("UserdataAlignment")
|
||||||
|
{
|
||||||
|
const auto testAllocate = [](void* ud, void* ptr, size_t osize, size_t nsize) -> void*
|
||||||
|
{
|
||||||
|
if (nsize == 0)
|
||||||
|
{
|
||||||
|
::operator delete(ptr, std::align_val_t(16));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else if (osize == 0)
|
||||||
|
{
|
||||||
|
return ::operator new(nsize, std::align_val_t(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize is unreachable in this test and is omitted
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
StateRef globalState(lua_newstate(testAllocate, nullptr), lua_close);
|
||||||
|
lua_State* L = globalState.get();
|
||||||
|
|
||||||
|
for (int size = 16; size <= 4096; size += 4)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
void* data = lua_newuserdata(L, size);
|
||||||
|
LUAU_ASSERT(uintptr_t(data) % 16 == 0);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
void* data = lua_newuserdatadtor(L, size, [](void*) {});
|
||||||
|
LUAU_ASSERT(uintptr_t(data) % 16 == 0);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("LightuserdataApi")
|
TEST_CASE("LightuserdataApi")
|
||||||
{
|
{
|
||||||
StateRef globalState(luaL_newstate(), lua_close);
|
StateRef globalState(luaL_newstate(), lua_close);
|
||||||
|
@ -3016,6 +3174,8 @@ TEST_CASE("HugeFunction")
|
||||||
|
|
||||||
TEST_CASE("HugeFunctionLoadFailure")
|
TEST_CASE("HugeFunctionLoadFailure")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag luauLoadNoOomThrow{FFlag::LuauLoadNoOomThrow, true};
|
||||||
|
|
||||||
// This test case verifies that if an out-of-memory error occurs inside of
|
// This test case verifies that if an out-of-memory error occurs inside of
|
||||||
// luau_load, we are not left with any GC objects in inconsistent states
|
// luau_load, we are not left with any GC objects in inconsistent states
|
||||||
// that would cause issues during garbage collection.
|
// that would cause issues during garbage collection.
|
||||||
|
@ -3066,15 +3226,11 @@ TEST_CASE("HugeFunctionLoadFailure")
|
||||||
luaL_sandbox(L);
|
luaL_sandbox(L);
|
||||||
luaL_sandboxthread(L);
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
try
|
int status = luau_load(L, "=HugeFunction", bytecode, bytecodeSize, 0);
|
||||||
{
|
REQUIRE(status == 1);
|
||||||
luau_load(L, "=HugeFunction", bytecode, bytecodeSize, 0);
|
|
||||||
REQUIRE(false); // The luau_load should fail with an exception
|
const char* error = lua_tostring(L, -1);
|
||||||
}
|
CHECK(strcmp(error, "not enough memory") == 0);
|
||||||
catch (const std::exception& ex)
|
|
||||||
{
|
|
||||||
REQUIRE(strcmp(ex.what(), "lua_exception: not enough memory") == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
luaC_fullgc(L);
|
luaC_fullgc(L);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||||
|
|
||||||
struct DataFlowGraphFixture
|
struct DataFlowGraphFixture
|
||||||
{
|
{
|
||||||
|
@ -46,6 +47,17 @@ struct DataFlowGraphFixture
|
||||||
REQUIRE(node);
|
REQUIRE(node);
|
||||||
return graph->getDef(node);
|
return graph->getDef(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkOperands(const Phi* phi, std::vector<DefId> operands)
|
||||||
|
{
|
||||||
|
Set<const Def*> operandSet{nullptr};
|
||||||
|
for (auto o : operands)
|
||||||
|
operandSet.insert(o.get());
|
||||||
|
CHECK(phi->operands.size() == operandSet.size());
|
||||||
|
for (auto o : phi->operands)
|
||||||
|
CHECK(operandSet.contains(o.get()));
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("DataFlowGraphBuilder");
|
TEST_SUITE_BEGIN("DataFlowGraphBuilder");
|
||||||
|
@ -119,6 +131,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_while")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_while")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local x
|
local x
|
||||||
|
|
||||||
|
@ -133,8 +147,9 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_while")
|
||||||
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
||||||
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
||||||
|
|
||||||
CHECK(x0 == x1);
|
auto phi = get<Phi>(x2);
|
||||||
CHECK(x1 == x2);
|
REQUIRE(phi);
|
||||||
|
checkOperands(phi, {x0, x1});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_while")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_while")
|
||||||
|
@ -157,6 +172,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_while")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_repeat")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_repeat")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local x
|
local x
|
||||||
|
|
||||||
|
@ -171,7 +188,7 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_repeat")
|
||||||
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
||||||
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
||||||
|
|
||||||
CHECK(x0 == x1);
|
CHECK(x0 != x1);
|
||||||
CHECK(x1 == x2);
|
CHECK(x1 == x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +212,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_repeat")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local x
|
local x
|
||||||
|
|
||||||
|
@ -209,8 +228,9 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for")
|
||||||
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
||||||
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
||||||
|
|
||||||
CHECK(x0 == x1);
|
auto phi = get<Phi>(x2);
|
||||||
CHECK(x1 == x2);
|
REQUIRE(phi);
|
||||||
|
checkOperands(phi, {x0, x1});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for")
|
||||||
|
@ -233,6 +253,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for_in")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for_in")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local x
|
local x
|
||||||
|
|
||||||
|
@ -247,8 +269,9 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for_in")
|
||||||
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
|
||||||
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
|
||||||
|
|
||||||
CHECK(x0 == x1);
|
auto phi = get<Phi>(x2);
|
||||||
CHECK(x1 == x2);
|
REQUIRE(phi);
|
||||||
|
checkOperands(phi, {x0, x1});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for_in")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for_in")
|
||||||
|
@ -271,6 +294,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for_in")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_preexisting_property_not_owned_by_while")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_preexisting_property_not_owned_by_while")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
t.x = 5
|
t.x = 5
|
||||||
|
@ -286,8 +311,9 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_preexisting_property_not_owned_b
|
||||||
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = true
|
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = true
|
||||||
DefId x3 = getDef<AstExprIndexName, 3>(); // local y = t.x
|
DefId x3 = getDef<AstExprIndexName, 3>(); // local y = t.x
|
||||||
|
|
||||||
CHECK(x1 == x2);
|
auto phi = get<Phi>(x3);
|
||||||
CHECK(x2 == x3);
|
REQUIRE(phi);
|
||||||
|
checkOperands(phi, {x1, x2});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_non_preexisting_property_not_owned_by_while")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_non_preexisting_property_not_owned_by_while")
|
||||||
|
|
|
@ -33,7 +33,6 @@ LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
||||||
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
|
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
|
||||||
LUAU_FASTFLAG(LuauBetterScopeSelection)
|
LUAU_FASTFLAG(LuauBetterScopeSelection)
|
||||||
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||||
|
@ -3065,8 +3064,6 @@ z = a.P.E
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "user_defined_type_function_local")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "user_defined_type_function_local")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauUserTypeFunTypecheck{FFlag::LuauUserTypeFunTypecheck, true};
|
|
||||||
|
|
||||||
const std::string source = R"(--!strict
|
const std::string source = R"(--!strict
|
||||||
type function foo(x: type): type
|
type function foo(x: type): type
|
||||||
if x.tag == "singleton" then
|
if x.tag == "singleton" then
|
||||||
|
|
|
@ -17,6 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -876,6 +877,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "discard_type_graphs")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded")
|
TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
||||||
|
|
||||||
Frontend fe{&fileResolver, &configResolver, {false}};
|
Frontend fe{&fileResolver, &configResolver, {false}};
|
||||||
|
|
||||||
fileResolver.source["Module/A"] = R"(
|
fileResolver.source["Module/A"] = R"(
|
||||||
|
@ -892,13 +895,9 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f
|
||||||
// It could segfault, or you could see weird type names like the empty string or <VALUELESS BY EXCEPTION>
|
// It could segfault, or you could see weird type names like the empty string or <VALUELESS BY EXCEPTION>
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
REQUIRE_EQ(
|
CHECK_EQ(
|
||||||
"Type\n\t"
|
"Table type '{ count: string }' not compatible with type '{ Count: number }' because the former is missing field 'Count'",
|
||||||
"'{ count: string }'"
|
toString(result.errors[0]));
|
||||||
"\ncould not be converted into\n\t"
|
|
||||||
"'{ Count: number }'",
|
|
||||||
toString(result.errors[0])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
REQUIRE_EQ(
|
REQUIRE_EQ(
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})")
|
TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true};
|
||||||
|
|
||||||
TableType tt;
|
TableType tt;
|
||||||
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
||||||
|
@ -260,7 +260,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true};
|
||||||
|
|
||||||
auto [aTy, aFree] = freshType();
|
auto [aTy, aFree] = freshType();
|
||||||
auto [bTy, bFree] = freshType();
|
auto [bTy, bFree] = freshType();
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2);
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3);
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("InferPolarity");
|
TEST_SUITE_BEGIN("InferPolarity");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
|
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true};
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LintRedundantNativeAttribute);
|
LUAU_FASTFLAG(LintRedundantNativeAttribute);
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2);
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3);
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1942,7 +1942,7 @@ print(foo:bar(2.0))
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
|
||||||
{
|
{
|
||||||
// FIXME: For now this flag causes a stack overflow on Windows.
|
// FIXME: For now this flag causes a stack overflow on Windows.
|
||||||
ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization2, false};
|
ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization3, false};
|
||||||
|
|
||||||
LintResult result = lint(R"(
|
LintResult result = lint(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
|
||||||
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
@ -362,8 +360,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors_2")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauNonStrictVisitorImprovements{FFlag::LuauNonStrictVisitorImprovements, true};
|
|
||||||
|
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
local t = {function(x)
|
local t = {function(x)
|
||||||
abs(x)
|
abs(x)
|
||||||
|
@ -510,8 +506,6 @@ foo.bar("hi")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "exprgroup_is_checked")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "exprgroup_is_checked")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonStrictVisitorImprovements, true};
|
|
||||||
|
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
local foo = (abs("foo"))
|
local foo = (abs("foo"))
|
||||||
)");
|
)");
|
||||||
|
@ -527,8 +521,6 @@ TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "exprgroup_is_checked")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "binop_is_checked")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "binop_is_checked")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonStrictVisitorImprovements, true};
|
|
||||||
|
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
local foo = 4 + abs("foo")
|
local foo = 4 + abs("foo")
|
||||||
)");
|
)");
|
||||||
|
@ -653,8 +645,6 @@ 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[] = {{FFlag::LuauNonStrictVisitorImprovements, true}, {FFlag::LuauNewNonStrictWarnOnUnknownGlobals, true}};
|
|
||||||
|
|
||||||
CheckResult result = check(Mode::Nonstrict, R"(
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
foo = 5
|
foo = 5
|
||||||
local wrong1 = foob
|
local wrong1 = foob
|
||||||
|
|
|
@ -15,10 +15,14 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauNormalizationCatchMetatableCycles)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
|
||||||
LUAU_FASTFLAG(LuauSubtypingEnableReasoningLimit)
|
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
LUAU_FASTFLAG(LuauTypePackDetectCycles)
|
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||||
|
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1068,19 +1072,6 @@ TEST_CASE_FIXTURE(NormalizeFixture, "free_type_and_not_truthy")
|
||||||
CHECK("'a & (false?)" == toString(result));
|
CHECK("'a & (false?)" == toString(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NormalizeFixture, "normalize_recursive_metatable")
|
|
||||||
{
|
|
||||||
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauNormalizationCatchMetatableCycles, true}};
|
|
||||||
|
|
||||||
TypeId root = arena.addType(BlockedType{});
|
|
||||||
TypeId emptyTable = arena.addType(TableType(TableState::Sealed, {}));
|
|
||||||
TypeId metatable = arena.addType(MetatableType{emptyTable, root});
|
|
||||||
emplaceType<BoundType>(asMutable(root), metatable);
|
|
||||||
auto normalized = normalizer.normalize(root);
|
|
||||||
REQUIRE(normalized);
|
|
||||||
CHECK_EQ("t1 where t1 = { @metatable t1, { } }", toString(normalizer.typeFromNormal(*normalized)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "normalizer_should_be_able_to_detect_cyclic_tables_and_not_stack_overflow")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "normalizer_should_be_able_to_detect_cyclic_tables_and_not_stack_overflow")
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauSolverV2)
|
if (!FFlag::LuauSolverV2)
|
||||||
|
@ -1186,12 +1177,20 @@ end
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_limit_function_intersection_complexity")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_limit_function_intersection_complexity")
|
||||||
{
|
{
|
||||||
ScopedFastInt luauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 80};
|
ScopedFastInt luauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 80};
|
||||||
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
|
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
|
||||||
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
|
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
|
||||||
|
|
||||||
|
ScopedFastFlag _[] = {
|
||||||
|
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
||||||
|
{FFlag::DebugLuauGreedyGeneralization, true},
|
||||||
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||||
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function _(_).readu32(l0)
|
function _(_).readu32(l0)
|
||||||
return ({[_(_(_))]=_,[_(if _ then _)]=_,n0=_,})[_],nil
|
return ({[_(_(_))]=_,[_(if _ then _)]=_,n0=_,})[_],nil
|
||||||
|
@ -1202,13 +1201,21 @@ _(_)[_(n32)] %= _(_(_))
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(defined(_WIN32) && !(defined(_M_X64) || defined(_M_ARM64)))
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_propagate_normalization_failures")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_propagate_normalization_failures")
|
||||||
{
|
{
|
||||||
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
|
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
|
||||||
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
|
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
|
||||||
ScopedFastFlag luauSubtypingEnableReasoningLimit{FFlag::LuauSubtypingEnableReasoningLimit, true};
|
|
||||||
ScopedFastFlag luauTurnOffNonreentrantGeneralization{FFlag::LuauNonReentrantGeneralization2, false};
|
ScopedFastFlag _[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
|
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
||||||
|
{FFlag::LuauSimplifyOutOfLine, true},
|
||||||
|
{FFlag::LuauNonReentrantGeneralization3, false},
|
||||||
|
{FFlag::DebugLuauGreedyGeneralization, true},
|
||||||
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function _(_,"").readu32(l0)
|
function _(_,"").readu32(l0)
|
||||||
|
@ -1223,7 +1230,7 @@ _().readu32 %= _(_(_(_),_))
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_flatten_type_pack_cycle")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_flatten_type_pack_cycle")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTypePackDetectCycles, true}};
|
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}};
|
||||||
|
|
||||||
// Note: if this stops throwing an exception, it means we fixed cycle construction and can replace with a regular check
|
// Note: if this stops throwing an exception, it means we fixed cycle construction and can replace with a regular check
|
||||||
CHECK_THROWS_AS(
|
CHECK_THROWS_AS(
|
||||||
|
@ -1243,15 +1250,19 @@ do end
|
||||||
#if 0
|
#if 0
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle")
|
||||||
{
|
{
|
||||||
// FIXME? This test code happens not to ICE with eager generalization
|
ScopedFastFlag sff[] = {
|
||||||
// enabled. This could either be because the problem is fixed, or because
|
{FFlag::LuauSolverV2, true},
|
||||||
// another bug is obscuring the problem.
|
{FFlag::LuauRefineWaitForBlockedTypesInTarget, true},
|
||||||
if (FFlag::DebugLuauGreedyGeneralization)
|
{FFlag::LuauSimplifyOutOfLine, true},
|
||||||
return;
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
|
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
||||||
|
{FFlag::DebugLuauGreedyGeneralization, true}
|
||||||
|
};
|
||||||
|
ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0};
|
||||||
|
|
||||||
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTypePackDetectCycles, true}};
|
// FIXME CLI-153131: This is constructing a cyclic type pack
|
||||||
|
|
||||||
// Note: if this stops throwing an exception, it means we fixed cycle construction and can replace with a regular check
|
|
||||||
CHECK_THROWS_AS(
|
CHECK_THROWS_AS(
|
||||||
check(R"(
|
check(R"(
|
||||||
function _(_).n0(l32,...)
|
function _(_).n0(l32,...)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue