mirror of
https://github.com/luau-lang/luau.git
synced 2025-04-21 20:23:47 +01:00
Merge branch 'upstream' into merge
This commit is contained in:
commit
2f6fb98a8e
82 changed files with 2133 additions and 1148 deletions
|
@ -455,6 +455,13 @@ struct UserDefinedTypeFunctionError
|
||||||
bool operator==(const UserDefinedTypeFunctionError& rhs) const;
|
bool operator==(const UserDefinedTypeFunctionError& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReservedIdentifier
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
bool operator==(const ReservedIdentifier& rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
using TypeErrorData = Variant<
|
using TypeErrorData = Variant<
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
UnknownSymbol,
|
UnknownSymbol,
|
||||||
|
@ -504,7 +511,8 @@ using TypeErrorData = Variant<
|
||||||
UnexpectedTypeInSubtyping,
|
UnexpectedTypeInSubtyping,
|
||||||
UnexpectedTypePackInSubtyping,
|
UnexpectedTypePackInSubtyping,
|
||||||
ExplicitFunctionAnnotationRecommended,
|
ExplicitFunctionAnnotationRecommended,
|
||||||
UserDefinedTypeFunctionError>;
|
UserDefinedTypeFunctionError,
|
||||||
|
ReservedIdentifier>;
|
||||||
|
|
||||||
struct TypeErrorSummary
|
struct TypeErrorSummary
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct SourceCode
|
||||||
None,
|
None,
|
||||||
Module,
|
Module,
|
||||||
Script,
|
Script,
|
||||||
Local
|
Local_DEPRECATED
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string source;
|
std::string source;
|
||||||
|
|
|
@ -16,8 +16,24 @@ struct GeneralizationParams
|
||||||
Polarity polarity = Polarity::None;
|
Polarity polarity = Polarity::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename TID>
|
||||||
|
struct GeneralizationResult
|
||||||
|
{
|
||||||
|
std::optional<TID> result;
|
||||||
|
|
||||||
|
// True if the provided type was replaced with a generic.
|
||||||
|
bool wasReplacedByGeneric = false;
|
||||||
|
|
||||||
|
bool resourceLimitsExceeded = false;
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return bool(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Replace a single free type by its bounds according to the polarity provided.
|
// Replace a single free type by its bounds according to the polarity provided.
|
||||||
std::optional<TypeId> generalizeType(
|
GeneralizationResult<TypeId> generalizeType(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
|
@ -26,7 +42,7 @@ std::optional<TypeId> generalizeType(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Generalize one type pack
|
// Generalize one type pack
|
||||||
std::optional<TypePackId> generalizeTypePack(
|
GeneralizationResult<TypePackId> generalizeTypePack(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
|
@ -36,11 +52,31 @@ std::optional<TypePackId> generalizeTypePack(
|
||||||
|
|
||||||
void sealTable(NotNull<Scope> scope, TypeId ty);
|
void sealTable(NotNull<Scope> scope, TypeId ty);
|
||||||
|
|
||||||
|
/** Attempt to generalize a type.
|
||||||
|
*
|
||||||
|
* If generalizationTarget is set, then only that type will be replaced by its
|
||||||
|
* bounds. The way this is intended to be used is that ty is some function that
|
||||||
|
* is not fully generalized, and generalizationTarget is a type within its
|
||||||
|
* signature. There should be no further constraints that could affect the
|
||||||
|
* bounds of generalizationTarget.
|
||||||
|
*
|
||||||
|
* Returns nullopt if generalization failed due to resources limits.
|
||||||
|
*/
|
||||||
std::optional<TypeId> generalize(
|
std::optional<TypeId> generalize(
|
||||||
|
NotNull<TypeArena> arena,
|
||||||
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<Scope> scope,
|
||||||
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
|
TypeId ty,
|
||||||
|
std::optional<TypeId> generalizationTarget = {}
|
||||||
|
);
|
||||||
|
|
||||||
|
void pruneUnnecessaryGenerics(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
TypeId ty
|
TypeId ty
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
} // namespace Luau
|
||||||
|
|
|
@ -24,6 +24,9 @@ SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<
|
||||||
|
|
||||||
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
|
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
|
||||||
|
|
||||||
|
SimplifyResult simplifyIntersectWithTruthy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||||
|
SimplifyResult simplifyIntersectWithFalsy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||||
|
|
||||||
enum class Relation
|
enum class Relation
|
||||||
{
|
{
|
||||||
Disjoint, // No A is a B or vice versa
|
Disjoint, // No A is a B or vice versa
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct TarjanNode
|
||||||
struct Tarjan
|
struct Tarjan
|
||||||
{
|
{
|
||||||
Tarjan();
|
Tarjan();
|
||||||
|
virtual ~Tarjan() = default;
|
||||||
|
|
||||||
// Vertices (types and type packs) are indexed, using pre-order traversal.
|
// Vertices (types and type packs) are indexed, using pre-order traversal.
|
||||||
DenseHashMap<TypeId, int> typeToIndex{nullptr};
|
DenseHashMap<TypeId, int> typeToIndex{nullptr};
|
||||||
|
@ -121,7 +122,7 @@ struct Tarjan
|
||||||
void visitChildren(TypePackId tp, int index);
|
void visitChildren(TypePackId tp, int index);
|
||||||
|
|
||||||
void visitChild(TypeId ty);
|
void visitChild(TypeId ty);
|
||||||
void visitChild(TypePackId ty);
|
void visitChild(TypePackId tp);
|
||||||
|
|
||||||
template<typename Ty>
|
template<typename Ty>
|
||||||
void visitChild(std::optional<Ty> ty)
|
void visitChild(std::optional<Ty> ty)
|
||||||
|
@ -132,7 +133,7 @@ struct Tarjan
|
||||||
|
|
||||||
// Visit the root vertex.
|
// Visit the root vertex.
|
||||||
TarjanResult visitRoot(TypeId ty);
|
TarjanResult visitRoot(TypeId ty);
|
||||||
TarjanResult visitRoot(TypePackId ty);
|
TarjanResult visitRoot(TypePackId tp);
|
||||||
|
|
||||||
// Used to reuse the object for a new operation
|
// Used to reuse the object for a new operation
|
||||||
void clearTarjan(const TxnLog* log);
|
void clearTarjan(const TxnLog* log);
|
||||||
|
@ -150,26 +151,12 @@ struct Tarjan
|
||||||
void visitSCC(int index);
|
void visitSCC(int index);
|
||||||
|
|
||||||
// Each subclass can decide to ignore some nodes.
|
// Each subclass can decide to ignore some nodes.
|
||||||
virtual bool ignoreChildren(TypeId ty)
|
virtual bool ignoreChildren(TypeId ty);
|
||||||
{
|
virtual bool ignoreChildren(TypePackId ty);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool ignoreChildren(TypePackId ty)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
||||||
virtual bool ignoreChildrenVisit(TypeId ty)
|
virtual bool ignoreChildrenVisit(TypeId ty);
|
||||||
{
|
virtual bool ignoreChildrenVisit(TypePackId ty);
|
||||||
return ignoreChildren(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool ignoreChildrenVisit(TypePackId ty)
|
|
||||||
{
|
|
||||||
return ignoreChildren(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subclasses should say which vertices are dirty,
|
// Subclasses should say which vertices are dirty,
|
||||||
// and what to do with dirty vertices.
|
// and what to do with dirty vertices.
|
||||||
|
@ -184,6 +171,7 @@ struct Tarjan
|
||||||
struct Substitution : Tarjan
|
struct Substitution : Tarjan
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
explicit Substitution(TypeArena* arena);
|
||||||
Substitution(const TxnLog* log_, TypeArena* arena);
|
Substitution(const TxnLog* log_, TypeArena* arena);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -232,28 +220,23 @@ public:
|
||||||
virtual TypeId clean(TypeId ty) = 0;
|
virtual TypeId clean(TypeId ty) = 0;
|
||||||
virtual TypePackId clean(TypePackId tp) = 0;
|
virtual TypePackId clean(TypePackId tp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
// Helper functions to create new types (used by subclasses)
|
// Helper functions to create new types (used by subclasses)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
TypeId addType(const T& tv)
|
TypeId addType(T tv)
|
||||||
{
|
{
|
||||||
return arena->addType(tv);
|
return arena->addType(std::move(tv));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
TypePackId addTypePack(const T& tp)
|
TypePackId addTypePack(T tp)
|
||||||
{
|
{
|
||||||
return arena->addTypePack(TypePackVar{tp});
|
return arena->addTypePack(TypePackVar{std::move(tp)});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Ty>
|
template<typename Ty>
|
||||||
std::optional<Ty> replace(std::optional<Ty> ty)
|
std::optional<Ty> replace(std::optional<Ty> ty);
|
||||||
{
|
|
||||||
if (ty)
|
|
||||||
return replace(*ty);
|
|
||||||
else
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -155,6 +155,9 @@ struct TypeFunction
|
||||||
|
|
||||||
/// The reducer function for the type function.
|
/// The reducer function for the type function.
|
||||||
ReducerFunction<TypeId> reducer;
|
ReducerFunction<TypeId> reducer;
|
||||||
|
|
||||||
|
/// If true, this type function can reduce even if it is parameterized on a generic.
|
||||||
|
bool canReduceGenerics = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a type function that may be applied to map a series of types and
|
/// Represents a type function that may be applied to map a series of types and
|
||||||
|
@ -167,6 +170,9 @@ struct TypePackFunction
|
||||||
|
|
||||||
/// The reducer function for the type pack function.
|
/// The reducer function for the type pack function.
|
||||||
ReducerFunction<TypePackId> reducer;
|
ReducerFunction<TypePackId> reducer;
|
||||||
|
|
||||||
|
/// If true, this type function can reduce even if it is parameterized on a generic.
|
||||||
|
bool canReduceGenerics = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FunctionGraphReductionResult
|
struct FunctionGraphReductionResult
|
||||||
|
|
|
@ -130,6 +130,7 @@ struct TypeChecker
|
||||||
const PredicateVec& predicates = {}
|
const PredicateVec& predicates = {}
|
||||||
);
|
);
|
||||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
|
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||||
|
WithPredicate<TypeId> checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
|
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
|
||||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
|
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
|
||||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
|
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||||
|
|
|
@ -31,6 +31,7 @@ LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -83,6 +84,8 @@ static ParenthesesRecommendation getParenRecommendationForIntersect(const Inters
|
||||||
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
|
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
|
||||||
for (Luau::TypeId partId : intersect->parts)
|
for (Luau::TypeId partId : intersect->parts)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauAutocompleteMissingFollows)
|
||||||
|
partId = follow(partId);
|
||||||
if (auto partFunc = Luau::get<FunctionType>(partId))
|
if (auto partFunc = Luau::get<FunctionType>(partId))
|
||||||
{
|
{
|
||||||
rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes));
|
rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes));
|
||||||
|
@ -1623,6 +1626,8 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
|
||||||
{
|
{
|
||||||
for (TypeId part : intersect->parts)
|
for (TypeId part : intersect->parts)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauAutocompleteMissingFollows)
|
||||||
|
part = follow(part);
|
||||||
if (auto candidateFunctionType = Luau::get<FunctionType>(part))
|
if (auto candidateFunctionType = Luau::get<FunctionType>(part))
|
||||||
{
|
{
|
||||||
if (std::optional<AutocompleteEntryMap> ret = performCallback(candidateFunctionType))
|
if (std::optional<AutocompleteEntryMap> ret = performCallback(candidateFunctionType))
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
||||||
|
@ -314,8 +314,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::LuauNonReentrantGeneralization
|
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization2
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
globalScope = globals.globalScope.get();
|
globalScope = globals.globalScope.get();
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -1614,7 +1614,7 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
|
||||||
if (resultTy && !get<BlockedType>(resultTy))
|
if (resultTy && !get<BlockedType>(resultTy))
|
||||||
{
|
{
|
||||||
// If there's an existing result type but it's _not_ blocked, then
|
// If there's an existing result type but it's _not_ blocked, then
|
||||||
// we aren't type stating this builtin and should fall back to
|
// we aren't type stating this builtin and should fall back to
|
||||||
// regular inference.
|
// regular inference.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,19 +33,16 @@
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGlobalSelfAssignmentCycle)
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauExtraFollows)
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||||
|
|
||||||
|
@ -53,6 +50,9 @@ LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||||
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -230,7 +230,7 @@ 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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
DEPRECATED_interiorTypes.emplace_back();
|
||||||
|
@ -263,12 +263,12 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
result,
|
result,
|
||||||
moduleFnTy,
|
moduleFnTy,
|
||||||
(FFlag::LuauNonReentrantGeneralization || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
: std::move(DEPRECATED_interiorTypes.back())
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -287,7 +287,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -319,13 +319,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -355,7 +355,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -377,7 +377,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.back().typePacks.push_back(result);
|
interiorFreeTypes.back().typePacks.push_back(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1120,122 +1120,62 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
|
||||||
TypePackId rvaluePack = checkPack(scope, statLocal->values, expectedTypes).tp;
|
TypePackId rvaluePack = checkPack(scope, statLocal->values, expectedTypes).tp;
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
if (FFlag::LuauInferLocalTypesInMultipleAssignments)
|
std::vector<TypeId> deferredTypes;
|
||||||
|
auto [head, tail] = flatten(rvaluePack);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
||||||
{
|
{
|
||||||
std::vector<TypeId> deferredTypes;
|
LUAU_ASSERT(get<BlockedType>(assignees[i]));
|
||||||
auto [head, tail] = flatten(rvaluePack);
|
TypeIds* localDomain = localTypes.find(assignees[i]);
|
||||||
|
LUAU_ASSERT(localDomain);
|
||||||
|
|
||||||
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
if (statLocal->vars.data[i]->annotation)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(get<BlockedType>(assignees[i]));
|
localDomain->insert(annotatedTypes[i]);
|
||||||
TypeIds* localDomain = localTypes.find(assignees[i]);
|
|
||||||
LUAU_ASSERT(localDomain);
|
|
||||||
|
|
||||||
if (statLocal->vars.data[i]->annotation)
|
|
||||||
{
|
|
||||||
localDomain->insert(annotatedTypes[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (i < head.size())
|
|
||||||
{
|
|
||||||
localDomain->insert(head[i]);
|
|
||||||
}
|
|
||||||
else if (tail)
|
|
||||||
{
|
|
||||||
deferredTypes.push_back(arena->addType(BlockedType{}));
|
|
||||||
localDomain->insert(deferredTypes.back());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
localDomain->insert(builtinTypes->nilType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasAnnotation)
|
|
||||||
{
|
|
||||||
TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
|
|
||||||
addConstraint(scope, statLocal->location, PackSubtypeConstraint{rvaluePack, annotatedPack});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deferredTypes.empty())
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(tail);
|
|
||||||
NotNull<Constraint> uc = addConstraint(scope, statLocal->location, UnpackConstraint{deferredTypes, *tail});
|
|
||||||
|
|
||||||
forEachConstraint(
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
this,
|
|
||||||
[&uc](const ConstraintPtr& runBefore)
|
|
||||||
{
|
|
||||||
uc->dependencies.emplace_back(runBefore.get());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
for (TypeId t : deferredTypes)
|
|
||||||
getMutable<BlockedType>(t)->setOwner(uc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hasAnnotation)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(get<BlockedType>(assignees[i]));
|
|
||||||
TypeIds* localDomain = localTypes.find(assignees[i]);
|
|
||||||
LUAU_ASSERT(localDomain);
|
|
||||||
localDomain->insert(annotatedTypes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
|
|
||||||
addConstraint(scope, statLocal->location, PackSubtypeConstraint{rvaluePack, annotatedPack});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<TypeId> valueTypes;
|
if (i < head.size())
|
||||||
valueTypes.reserve(statLocal->vars.size);
|
|
||||||
|
|
||||||
auto [head, tail] = flatten(rvaluePack);
|
|
||||||
|
|
||||||
if (head.size() >= statLocal->vars.size)
|
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
localDomain->insert(head[i]);
|
||||||
valueTypes.push_back(head[i]);
|
}
|
||||||
|
else if (tail)
|
||||||
|
{
|
||||||
|
deferredTypes.push_back(arena->addType(BlockedType{}));
|
||||||
|
localDomain->insert(deferredTypes.back());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
localDomain->insert(builtinTypes->nilType);
|
||||||
valueTypes.push_back(arena->addType(BlockedType{}));
|
|
||||||
|
|
||||||
auto uc = addConstraint(scope, statLocal->location, UnpackConstraint{valueTypes, rvaluePack});
|
|
||||||
|
|
||||||
forEachConstraint(
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
this,
|
|
||||||
[&uc](const ConstraintPtr& runBefore)
|
|
||||||
{
|
|
||||||
uc->dependencies.push_back(NotNull{runBefore.get()});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
for (TypeId t : valueTypes)
|
|
||||||
getMutable<BlockedType>(t)->setOwner(uc);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < statLocal->vars.size; ++i)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(get<BlockedType>(assignees[i]));
|
|
||||||
TypeIds* localDomain = localTypes.find(assignees[i]);
|
|
||||||
LUAU_ASSERT(localDomain);
|
|
||||||
localDomain->insert(valueTypes[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasAnnotation)
|
||||||
|
{
|
||||||
|
TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
|
||||||
|
addConstraint(scope, statLocal->location, PackSubtypeConstraint{rvaluePack, annotatedPack});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deferredTypes.empty())
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(tail);
|
||||||
|
NotNull<Constraint> uc = addConstraint(scope, statLocal->location, UnpackConstraint{deferredTypes, *tail});
|
||||||
|
|
||||||
|
forEachConstraint(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
this,
|
||||||
|
[&uc](const ConstraintPtr& runBefore)
|
||||||
|
{
|
||||||
|
uc->dependencies.emplace_back(runBefore.get());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
for (TypeId t : deferredTypes)
|
||||||
|
getMutable<BlockedType>(t)->setOwner(uc);
|
||||||
|
}
|
||||||
|
|
||||||
if (statLocal->vars.size == 1 && statLocal->values.size == 1 && firstValueType && scope.get() == rootScope && !hasAnnotation)
|
if (statLocal->vars.size == 1 && statLocal->values.size == 1 && firstValueType && scope.get() == rootScope && !hasAnnotation)
|
||||||
{
|
{
|
||||||
AstLocal* var = statLocal->vars.data[0];
|
AstLocal* var = statLocal->vars.data[0];
|
||||||
|
@ -1747,7 +1687,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeAlias*
|
||||||
|
|
||||||
if (alias->name == "typeof")
|
if (alias->name == "typeof")
|
||||||
{
|
{
|
||||||
reportError(alias->location, GenericError{"Type aliases cannot be named typeof"});
|
reportError(alias->location, ReservedIdentifier{"typeof"});
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,6 +1748,14 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
if (!FFlag::LuauUserTypeFunTypecheck)
|
if (!FFlag::LuauUserTypeFunTypecheck)
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
|
|
||||||
|
if (FFlag::LuauNoTypeFunctionsNamedTypeOf)
|
||||||
|
{
|
||||||
|
if (function->name == "typeof")
|
||||||
|
{
|
||||||
|
reportError(function->location, ReservedIdentifier{"typeof"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
||||||
LUAU_ASSERT(scopePtr);
|
LUAU_ASSERT(scopePtr);
|
||||||
|
|
||||||
|
@ -1817,7 +1765,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
|
@ -1835,7 +1783,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -1844,7 +1792,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -2285,7 +2233,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
{
|
{
|
||||||
std::vector<TypeId> unpackedTypes;
|
std::vector<TypeId> unpackedTypes;
|
||||||
if (args.size() > 0)
|
if (args.size() > 0)
|
||||||
target = FFlag::LuauExtraFollows ? follow(args[0]) : args[0];
|
target = follow(args[0]);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
target = arena->addType(BlockedType{});
|
target = arena->addType(BlockedType{});
|
||||||
|
@ -2506,7 +2454,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2534,7 +2482,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
return Inference{singletonType};
|
return Inference{singletonType};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
TypeId freeTy = nullptr;
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2696,7 +2644,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
|
@ -2710,12 +2658,12 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
generalizedTy,
|
generalizedTy,
|
||||||
sig.signature,
|
sig.signature,
|
||||||
(FFlag::LuauNonReentrantGeneralization || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
: std::move(DEPRECATED_interiorTypes.back())
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -3142,12 +3090,9 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* glob
|
||||||
DefId def = dfg->getDef(global);
|
DefId def = dfg->getDef(global);
|
||||||
rootScope->lvalueTypes[def] = rhsType;
|
rootScope->lvalueTypes[def] = rhsType;
|
||||||
|
|
||||||
if (FFlag::LuauGlobalSelfAssignmentCycle)
|
// Ignore possible self-assignment, it doesn't create a new constraint
|
||||||
{
|
if (annotatedTy == follow(rhsType))
|
||||||
// Ignore possible self-assignment, it doesn't create a new constraint
|
return;
|
||||||
if (annotatedTy == follow(rhsType))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sketchy: We're specifically looking for BlockedTypes that were
|
// Sketchy: We're specifically looking for BlockedTypes that were
|
||||||
// initially created by ConstraintGenerator::prepopulateGlobalScope.
|
// initially created by ConstraintGenerator::prepopulateGlobalScope.
|
||||||
|
@ -3210,7 +3155,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
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);
|
||||||
|
@ -3373,7 +3318,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
|
|
||||||
if (fn->self)
|
if (fn->self)
|
||||||
{
|
{
|
||||||
TypeId selfType = freshType(signatureScope);
|
TypeId selfType = freshType(signatureScope, Polarity::Negative);
|
||||||
argTypes.push_back(selfType);
|
argTypes.push_back(selfType);
|
||||||
argNames.emplace_back(FunctionArgument{fn->self->name.value, fn->self->location});
|
argNames.emplace_back(FunctionArgument{fn->self->name.value, fn->self->location});
|
||||||
signatureScope->bindings[fn->self] = Binding{selfType, fn->self->location};
|
signatureScope->bindings[fn->self] = Binding{selfType, fn->self->location};
|
||||||
|
@ -4043,14 +3988,14 @@ TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location locati
|
||||||
return resultType;
|
return resultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentTypeCheckGlobalPrepopulator : AstVisitor
|
struct FragmentTypeCheckGlobalPrepopulator_DEPRECATED : AstVisitor
|
||||||
{
|
{
|
||||||
const NotNull<Scope> globalScope;
|
const NotNull<Scope> globalScope;
|
||||||
const NotNull<Scope> currentScope;
|
const NotNull<Scope> currentScope;
|
||||||
const NotNull<const DataFlowGraph> dfg;
|
const NotNull<const DataFlowGraph> dfg;
|
||||||
const NotNull<TypeArena> arena;
|
const NotNull<TypeArena> arena;
|
||||||
|
|
||||||
FragmentTypeCheckGlobalPrepopulator(
|
FragmentTypeCheckGlobalPrepopulator_DEPRECATED(
|
||||||
NotNull<Scope> globalScope,
|
NotNull<Scope> globalScope,
|
||||||
NotNull<Scope> currentScope,
|
NotNull<Scope> currentScope,
|
||||||
NotNull<const DataFlowGraph> dfg,
|
NotNull<const DataFlowGraph> dfg,
|
||||||
|
@ -4167,12 +4112,16 @@ struct GlobalPrepopulator : AstVisitor
|
||||||
|
|
||||||
void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program)
|
void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program)
|
||||||
{
|
{
|
||||||
FragmentTypeCheckGlobalPrepopulator gp{NotNull{globalScope.get()}, NotNull{resumeScope.get()}, dfg, arena};
|
if (!FFlag::LuauGlobalVariableModuleIsolation)
|
||||||
|
{
|
||||||
|
FragmentTypeCheckGlobalPrepopulator_DEPRECATED gp{NotNull{globalScope.get()}, NotNull{resumeScope.get()}, dfg, arena};
|
||||||
|
|
||||||
if (prepareModuleScope)
|
if (prepareModuleScope)
|
||||||
prepareModuleScope(module->name, resumeScope);
|
prepareModuleScope(module->name, resumeScope);
|
||||||
|
|
||||||
|
program->visit(&gp);
|
||||||
|
}
|
||||||
|
|
||||||
program->visit(&gp);
|
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
if (FFlag::LuauUserTypeFunTypecheck)
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,7 +39,7 @@ LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
@ -673,73 +673,20 @@ void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
if (!freeTy)
|
if (!freeTy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NotNull<Scope> tyScope{freeTy->scope};
|
TypeId* functionType = scopeToFunction->find(freeTy->scope);
|
||||||
|
if (!functionType)
|
||||||
|
return;
|
||||||
|
|
||||||
// TODO: If freeTy occurs within the enclosing function's type, we need to
|
std::optional<TypeId> resultTy = generalize(arena, builtinTypes, NotNull{freeTy->scope}, generalizedTypes, *functionType, ty);
|
||||||
// check to see whether this type should instead be generic.
|
|
||||||
|
|
||||||
TypeId newBound = follow(freeTy->upperBound);
|
if (FFlag::DebugLuauLogSolver)
|
||||||
|
|
||||||
TypeId* functionTyPtr = nullptr;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
functionTyPtr = scopeToFunction->find(tyScope);
|
printf(
|
||||||
if (functionTyPtr || !tyScope->parent)
|
"Eagerly generalized %s (now %s)\n\tin function %s\n",
|
||||||
break;
|
saveme.c_str(),
|
||||||
else if (tyScope->parent)
|
toString(ty, opts).c_str(),
|
||||||
tyScope = NotNull{tyScope->parent.get()};
|
toString(resultTy.value_or(*functionType), opts).c_str()
|
||||||
else
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ty == newBound)
|
|
||||||
ty = builtinTypes->unknownType;
|
|
||||||
|
|
||||||
if (!functionTyPtr)
|
|
||||||
{
|
|
||||||
asMutable(ty)->reassign(Type{BoundType{follow(freeTy->upperBound)}});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const TypeId functionTy = follow(*functionTyPtr);
|
|
||||||
FunctionType* const function = getMutable<FunctionType>(functionTy);
|
|
||||||
LUAU_ASSERT(function);
|
|
||||||
|
|
||||||
TypeSearcher ts{ty};
|
|
||||||
ts.traverse(functionTy);
|
|
||||||
|
|
||||||
const TypeId upperBound = follow(freeTy->upperBound);
|
|
||||||
const TypeId lowerBound = follow(freeTy->lowerBound);
|
|
||||||
|
|
||||||
switch (ts.result)
|
|
||||||
{
|
|
||||||
case Polarity::None:
|
|
||||||
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Polarity::Negative:
|
|
||||||
case Polarity::Mixed:
|
|
||||||
if (get<UnknownType>(upperBound) && ts.count > 1)
|
|
||||||
{
|
|
||||||
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
|
||||||
function->generics.emplace_back(ty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Polarity::Positive:
|
|
||||||
if (get<UnknownType>(lowerBound) && ts.count > 1)
|
|
||||||
{
|
|
||||||
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
|
||||||
function->generics.emplace_back(ty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asMutable(ty)->reassign(Type{BoundType{lowerBound}});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +702,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
trackInteriorFreeType(constraint->scope, ty);
|
trackInteriorFreeType(constraint->scope, ty);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -890,6 +837,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
|
|
||||||
if (generalizedTy)
|
if (generalizedTy)
|
||||||
{
|
{
|
||||||
|
pruneUnnecessaryGenerics(arena, builtinTypes, constraint->scope, generalizedTypes, *generalizedTy);
|
||||||
if (get<BlockedType>(generalizedType))
|
if (get<BlockedType>(generalizedType))
|
||||||
bind(constraint, generalizedType, *generalizedTy);
|
bind(constraint, generalizedType, *generalizedTy);
|
||||||
else
|
else
|
||||||
|
@ -918,7 +866,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
if (auto freeTy = get<FreeType>(ty))
|
if (auto freeTy = get<FreeType>(ty))
|
||||||
|
@ -928,7 +876,9 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
params.useCount = 1;
|
params.useCount = 1;
|
||||||
params.polarity = freeTy->polarity;
|
params.polarity = freeTy->polarity;
|
||||||
|
|
||||||
generalizeType(arena, builtinTypes, constraint->scope, ty, params);
|
GeneralizationResult<TypeId> res = generalizeType(arena, builtinTypes, constraint->scope, ty, params);
|
||||||
|
if (res.resourceLimitsExceeded)
|
||||||
|
reportError(CodeTooComplex{}, constraint->scope->location); // FIXME: We don't have a very good location for this.
|
||||||
}
|
}
|
||||||
else if (get<TableType>(ty))
|
else if (get<TableType>(ty))
|
||||||
sealTable(constraint->scope, ty);
|
sealTable(constraint->scope, ty);
|
||||||
|
@ -938,7 +888,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
if (constraint->scope->interiorFreeTypePacks)
|
if (constraint->scope->interiorFreeTypePacks)
|
||||||
{
|
{
|
||||||
|
@ -1544,7 +1494,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
for (TypeId freeTy : u2.newFreshTypes)
|
for (TypeId freeTy : u2.newFreshTypes)
|
||||||
trackInteriorFreeType(constraint->scope, freeTy);
|
trackInteriorFreeType(constraint->scope, freeTy);
|
||||||
|
@ -1944,7 +1894,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
trackInteriorFreeType(constraint->scope, resultType);
|
trackInteriorFreeType(constraint->scope, resultType);
|
||||||
|
|
||||||
tt->indexer = TableIndexer{indexType, resultType};
|
tt->indexer = TableIndexer{indexType, resultType};
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||||
LUAU_FASTFLAG(LuauNonStrictFuncDefErrorFix)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
static std::string wrongNumberOfArgsString(
|
static std::string wrongNumberOfArgsString(
|
||||||
size_t expectedCount,
|
size_t expectedCount,
|
||||||
|
@ -70,7 +70,7 @@ namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
// this list of binary operator type functions is used for better stringification of type functions errors
|
// this list of binary operator type functions is used for better stringification of type functions errors
|
||||||
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
static const std::unordered_map<std::string, const char*> DEPRECATED_kBinaryOps{
|
||||||
{"add", "+"},
|
{"add", "+"},
|
||||||
{"sub", "-"},
|
{"sub", "-"},
|
||||||
{"mul", "*"},
|
{"mul", "*"},
|
||||||
|
@ -86,12 +86,27 @@ static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||||
{"eq", "== or ~="}
|
{"eq", "== or ~="}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||||
|
{"add", "+"},
|
||||||
|
{"sub", "-"},
|
||||||
|
{"mul", "*"},
|
||||||
|
{"div", "/"},
|
||||||
|
{"idiv", "//"},
|
||||||
|
{"pow", "^"},
|
||||||
|
{"mod", "%"},
|
||||||
|
{"concat", ".."},
|
||||||
|
{"lt", "< or >="},
|
||||||
|
{"le", "<= or >"},
|
||||||
|
{"eq", "== or ~="}
|
||||||
|
};
|
||||||
|
|
||||||
// this list of unary operator type functions is used for better stringification of type functions errors
|
// this list of unary operator type functions is used for better stringification of type functions errors
|
||||||
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};
|
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};
|
||||||
|
|
||||||
// this list of type functions will receive a special error indicating that the user should file a bug on the GitHub repository
|
// this list of type functions will receive a special error indicating that the user should file a bug on the GitHub repository
|
||||||
// putting a type function in this list indicates that it is expected to _always_ reduce
|
// putting a type function in this list indicates that it is expected to _always_ reduce
|
||||||
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
static const std::unordered_set<std::string> DEPRECATED_kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
||||||
|
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect", "and", "or"};
|
||||||
|
|
||||||
struct ErrorConverter
|
struct ErrorConverter
|
||||||
{
|
{
|
||||||
|
@ -643,7 +658,8 @@ struct ErrorConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
// binary operators
|
// binary operators
|
||||||
if (auto binaryString = kBinaryOps.find(tfit->function->name); binaryString != kBinaryOps.end())
|
const auto binaryOps = FFlag::DebugLuauGreedyGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps;
|
||||||
|
if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end())
|
||||||
{
|
{
|
||||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||||
|
|
||||||
|
@ -697,10 +713,10 @@ struct ErrorConverter
|
||||||
"'";
|
"'";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kUnreachableTypeFunctions.count(tfit->function->name))
|
if ((FFlag::DebugLuauGreedyGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
|
||||||
{
|
{
|
||||||
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
||||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
|
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
|
||||||
|
@ -756,7 +772,7 @@ struct ErrorConverter
|
||||||
|
|
||||||
std::string operator()(const NonStrictFunctionDefinitionError& e) const
|
std::string operator()(const NonStrictFunctionDefinitionError& e) const
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictFuncDefErrorFix && e.functionName.empty())
|
if (e.functionName.empty())
|
||||||
{
|
{
|
||||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' is used in a way that will run time error";
|
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' is used in a way that will run time error";
|
||||||
}
|
}
|
||||||
|
@ -803,6 +819,11 @@ struct ErrorConverter
|
||||||
return e.message;
|
return e.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string operator()(const ReservedIdentifier& e) const
|
||||||
|
{
|
||||||
|
return e.name + " cannot be used as an identifier for a type function or alias";
|
||||||
|
}
|
||||||
|
|
||||||
std::string operator()(const CannotAssignToNever& e) const
|
std::string operator()(const CannotAssignToNever& e) const
|
||||||
{
|
{
|
||||||
std::string result = "Cannot assign a value of type " + toString(e.rhsType) + " to a field of type never";
|
std::string result = "Cannot assign a value of type " + toString(e.rhsType) + " to a field of type never";
|
||||||
|
@ -1190,6 +1211,11 @@ bool UserDefinedTypeFunctionError::operator==(const UserDefinedTypeFunctionError
|
||||||
return message == rhs.message;
|
return message == rhs.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReservedIdentifier::operator==(const ReservedIdentifier& rhs) const
|
||||||
|
{
|
||||||
|
return name == rhs.name;
|
||||||
|
}
|
||||||
|
|
||||||
bool CannotAssignToNever::operator==(const CannotAssignToNever& rhs) const
|
bool CannotAssignToNever::operator==(const CannotAssignToNever& rhs) const
|
||||||
{
|
{
|
||||||
if (cause.size() != rhs.cause.size())
|
if (cause.size() != rhs.cause.size())
|
||||||
|
@ -1409,6 +1435,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, ReservedIdentifier>)
|
||||||
|
{
|
||||||
|
}
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,13 @@ LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneReturnTypePack)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteDemandBasedCloning)
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -569,12 +569,32 @@ struct UsageFinder : public AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstExprGlobal* global) override
|
||||||
|
{
|
||||||
|
if (FFlag::LuauGlobalVariableModuleIsolation)
|
||||||
|
globalDefsToPrePopulate.emplace_back(global->name, dfg->getDef(global));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AstStatFunction* function) override
|
||||||
|
{
|
||||||
|
if (FFlag::LuauGlobalVariableModuleIsolation)
|
||||||
|
{
|
||||||
|
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
||||||
|
globalFunctionsReferenced.emplace_back(g->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
NotNull<DataFlowGraph> dfg;
|
NotNull<DataFlowGraph> dfg;
|
||||||
DenseHashSet<Name> declaredAliases{""};
|
DenseHashSet<Name> declaredAliases{""};
|
||||||
std::vector<std::pair<const Def*, AstLocal*>> localBindingsReferenced;
|
std::vector<std::pair<const Def*, AstLocal*>> localBindingsReferenced;
|
||||||
DenseHashSet<const Def*> mentionedDefs{nullptr};
|
DenseHashSet<const Def*> mentionedDefs{nullptr};
|
||||||
std::vector<Name> referencedBindings{""};
|
std::vector<Name> referencedBindings{""};
|
||||||
std::vector<std::pair<Name, Name>> referencedImportedBindings{{"", ""}};
|
std::vector<std::pair<Name, Name>> referencedImportedBindings{{"", ""}};
|
||||||
|
std::vector<std::pair<AstName, const Def*>> globalDefsToPrePopulate;
|
||||||
|
std::vector<AstName> globalFunctionsReferenced;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Runs the `UsageFinder` traversal on the fragment and grabs all of the types that are
|
// Runs the `UsageFinder` traversal on the fragment and grabs all of the types that are
|
||||||
|
@ -648,7 +668,45 @@ void cloneTypesFromFragment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally - clone the returnType on the staleScope. This helps avoid potential leaks of free types.
|
if (FFlag::LuauGlobalVariableModuleIsolation)
|
||||||
|
{
|
||||||
|
// Fourth - prepopulate the global function types
|
||||||
|
for (const auto& name : f.globalFunctionsReferenced)
|
||||||
|
{
|
||||||
|
if (auto ty = staleModule->getModuleScope()->lookup(name))
|
||||||
|
{
|
||||||
|
destScope->bindings[name] = Binding{Luau::cloneIncremental(*ty, *destArena, cloneState, destScope)};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId bt = destArena->addType(BlockedType{});
|
||||||
|
destScope->bindings[name] = Binding{bt};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fifth - prepopulate the globals here
|
||||||
|
for (const auto& [name, def] : f.globalDefsToPrePopulate)
|
||||||
|
{
|
||||||
|
if (auto ty = staleModule->getModuleScope()->lookup(name))
|
||||||
|
{
|
||||||
|
destScope->lvalueTypes[def] = Luau::cloneIncremental(*ty, *destArena, cloneState, destScope);
|
||||||
|
}
|
||||||
|
else if (auto ty = destScope->lookup(name))
|
||||||
|
{
|
||||||
|
// This branch is a little strange - we are looking up a symbol in the destScope
|
||||||
|
// This scope has no parent pointer, and only cloned types are written to it, so this is a
|
||||||
|
// safe operation to do without cloning.
|
||||||
|
// The reason we do this, is the usage finder will traverse the global functions referenced first
|
||||||
|
// If there is no name associated with this function at the global scope, it must appear first in the fragment and we must
|
||||||
|
// create a blocked type for it. We write this blocked type directly into the `destScope` bindings
|
||||||
|
// Then when we go to traverse the `AstExprGlobal` associated with this function, we need to ensure that we map the def -> blockedType
|
||||||
|
// in `lvalueTypes`, which was previously written into `destScope`
|
||||||
|
destScope->lvalueTypes[def] = *ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, clone the returnType on the staleScope. This helps avoid potential leaks of free types.
|
||||||
if (staleScope->returnType)
|
if (staleScope->returnType)
|
||||||
destScope->returnType = Luau::cloneIncremental(staleScope->returnType, *destArena, cloneState, destScope);
|
destScope->returnType = Luau::cloneIncremental(staleScope->returnType, *destArena, cloneState, destScope);
|
||||||
}
|
}
|
||||||
|
@ -820,7 +878,7 @@ void cloneAndSquashScopes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauCloneReturnTypePack && destScope->returnType)
|
if (destScope->returnType)
|
||||||
destScope->returnType = Luau::cloneIncremental(destScope->returnType, *destArena, cloneState, destScope);
|
destScope->returnType = Luau::cloneIncremental(destScope->returnType, *destArena, cloneState, destScope);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1452,7 +1510,7 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
SimplifierPtr simplifier = newSimplifier(NotNull{&incrementalModule->internalTypes}, frontend.builtinTypes);
|
SimplifierPtr simplifier = newSimplifier(NotNull{&incrementalModule->internalTypes}, frontend.builtinTypes);
|
||||||
|
|
||||||
FrontendModuleResolver& resolver = getModuleResolver(frontend, opts);
|
FrontendModuleResolver& resolver = getModuleResolver(frontend, opts);
|
||||||
|
std::shared_ptr<Scope> freshChildOfNearestScope = std::make_shared<Scope>(nullptr);
|
||||||
/// Contraint Generator
|
/// Contraint Generator
|
||||||
ConstraintGenerator cg{
|
ConstraintGenerator cg{
|
||||||
incrementalModule,
|
incrementalModule,
|
||||||
|
@ -1462,7 +1520,7 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
NotNull{&resolver},
|
NotNull{&resolver},
|
||||||
frontend.builtinTypes,
|
frontend.builtinTypes,
|
||||||
iceHandler,
|
iceHandler,
|
||||||
stale->getModuleScope(),
|
FFlag::LuauGlobalVariableModuleIsolation ? freshChildOfNearestScope : stale->getModuleScope(),
|
||||||
frontend.globals.globalTypeFunctionScope,
|
frontend.globals.globalTypeFunctionScope,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -1471,7 +1529,6 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
};
|
};
|
||||||
|
|
||||||
CloneState cloneState{frontend.builtinTypes};
|
CloneState cloneState{frontend.builtinTypes};
|
||||||
std::shared_ptr<Scope> freshChildOfNearestScope = std::make_shared<Scope>(nullptr);
|
|
||||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||||
freshChildOfNearestScope->interiorFreeTypes.emplace();
|
freshChildOfNearestScope->interiorFreeTypes.emplace();
|
||||||
freshChildOfNearestScope->interiorFreeTypePacks.emplace();
|
freshChildOfNearestScope->interiorFreeTypePacks.emplace();
|
||||||
|
|
|
@ -46,7 +46,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1003,11 +1002,8 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||||
freeze(module->interfaceTypes);
|
freeze(module->interfaceTypes);
|
||||||
|
|
||||||
module->internalTypes.clear();
|
module->internalTypes.clear();
|
||||||
if (FFlag::LuauSelectivelyRetainDFGArena)
|
module->defArena.allocator.clear();
|
||||||
{
|
module->keyArena.allocator.clear();
|
||||||
module->defArena.allocator.clear();
|
|
||||||
module->keyArena.allocator.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
module->astTypes.clear();
|
module->astTypes.clear();
|
||||||
module->astTypePacks.clear();
|
module->astTypePacks.clear();
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
|
#include "Luau/Substitution.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -468,7 +470,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
if (!subsumes(scope, ft.scope))
|
if (!subsumes(scope, ft.scope))
|
||||||
return true;
|
return true;
|
||||||
|
@ -519,7 +521,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
unsealedTables.insert(ty);
|
unsealedTables.insert(ty);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -558,7 +560,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if (tt.indexer)
|
if (tt.indexer)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
||||||
//
|
//
|
||||||
|
@ -616,7 +618,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
if (!subsumes(scope, ftp.scope))
|
if (!subsumes(scope, ftp.scope))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
||||||
++params.useCount;
|
++params.useCount;
|
||||||
|
@ -1092,6 +1094,118 @@ struct TypeCacher : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RemoveType : Substitution // NOLINT
|
||||||
|
{
|
||||||
|
NotNull<BuiltinTypes> builtinTypes;
|
||||||
|
TypeId needle;
|
||||||
|
|
||||||
|
RemoveType(NotNull<BuiltinTypes> builtinTypes, TypeArena* arena, TypeId needle)
|
||||||
|
: Substitution(arena)
|
||||||
|
, builtinTypes(builtinTypes)
|
||||||
|
, needle(needle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ignoreChildren(TypeId ty) override
|
||||||
|
{
|
||||||
|
if (get<UnionType>(ty) || get<IntersectionType>(ty))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDirty(TypeId ty) override
|
||||||
|
{
|
||||||
|
// A union or intersection is dirty if it contains the needle or if it has any duplicate members.
|
||||||
|
if (auto ut = get<UnionType>(ty))
|
||||||
|
{
|
||||||
|
DenseHashSet<TypeId> distinctParts{nullptr};
|
||||||
|
size_t count = 0;
|
||||||
|
for (TypeId part : ut)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
if (part == needle)
|
||||||
|
return true;
|
||||||
|
distinctParts.insert(follow(part));
|
||||||
|
}
|
||||||
|
return distinctParts.size() != count;
|
||||||
|
}
|
||||||
|
else if (auto it = get<IntersectionType>(ty))
|
||||||
|
{
|
||||||
|
DenseHashSet<TypeId> distinctParts{nullptr};
|
||||||
|
size_t count = 0;
|
||||||
|
for (TypeId part : it)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
if (part == needle)
|
||||||
|
return true;
|
||||||
|
distinctParts.insert(follow(part));
|
||||||
|
}
|
||||||
|
return distinctParts.size() != count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDirty(TypePackId tp) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeId clean(TypeId ty) override
|
||||||
|
{
|
||||||
|
if (auto ut = get<UnionType>(ty))
|
||||||
|
{
|
||||||
|
OrderedSet<TypeId> newParts;
|
||||||
|
|
||||||
|
for (TypeId ty : ut)
|
||||||
|
{
|
||||||
|
if (ty != needle)
|
||||||
|
newParts.insert(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParts.empty())
|
||||||
|
return builtinTypes->neverType;
|
||||||
|
else if (newParts.size() == 1)
|
||||||
|
{
|
||||||
|
TypeId onlyType = *newParts.begin();
|
||||||
|
LUAU_ASSERT(onlyType != needle);
|
||||||
|
return onlyType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return arena->addType(UnionType{newParts.takeVector()});
|
||||||
|
}
|
||||||
|
else if (auto it = get<IntersectionType>(ty))
|
||||||
|
{
|
||||||
|
OrderedSet<TypeId> newParts;
|
||||||
|
|
||||||
|
for (TypeId ty : it)
|
||||||
|
{
|
||||||
|
if (ty != needle)
|
||||||
|
newParts.insert(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParts.empty())
|
||||||
|
return builtinTypes->unknownType;
|
||||||
|
else if (newParts.size() == 1)
|
||||||
|
{
|
||||||
|
TypeId onlyType = *newParts.begin();
|
||||||
|
LUAU_ASSERT(onlyType != needle);
|
||||||
|
return onlyType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return arena->addType(IntersectionType{newParts.takeVector()});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePackId clean(TypePackId tp) override
|
||||||
|
{
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove occurrences of `needle` within `haystack`. This is used to cull cyclic bounds from free types.
|
* Remove occurrences of `needle` within `haystack`. This is used to cull cyclic bounds from free types.
|
||||||
*
|
*
|
||||||
|
@ -1099,84 +1213,14 @@ struct TypeCacher : TypeOnceVisitor
|
||||||
* @param needle The type to be removed.
|
* @param needle The type to be removed.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static TypeId removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, DenseHashSet<TypeId>& seen, TypeId haystack, TypeId needle)
|
static std::optional<
|
||||||
|
TypeId> removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, TypeId haystack, TypeId needle)
|
||||||
{
|
{
|
||||||
haystack = follow(haystack);
|
RemoveType rt{builtinTypes, arena, needle};
|
||||||
|
return rt.substitute(haystack);
|
||||||
if (seen.find(haystack))
|
|
||||||
return haystack;
|
|
||||||
seen.insert(haystack);
|
|
||||||
|
|
||||||
if (const UnionType* ut = get<UnionType>(haystack))
|
|
||||||
{
|
|
||||||
OrderedSet<TypeId> newOptions;
|
|
||||||
|
|
||||||
for (TypeId option : ut)
|
|
||||||
{
|
|
||||||
if (option == needle)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (get<NeverType>(option))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LUAU_ASSERT(!get<UnionType>(option));
|
|
||||||
|
|
||||||
if (get<IntersectionType>(option))
|
|
||||||
newOptions.insert(removeType(arena, builtinTypes, seen, option, needle));
|
|
||||||
else
|
|
||||||
newOptions.insert(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newOptions.empty())
|
|
||||||
return builtinTypes->neverType;
|
|
||||||
else if (newOptions.size() == 1)
|
|
||||||
{
|
|
||||||
TypeId onlyType = *newOptions.begin();
|
|
||||||
LUAU_ASSERT(onlyType != haystack);
|
|
||||||
return onlyType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return arena->addType(UnionType{newOptions.takeVector()});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const IntersectionType* it = get<IntersectionType>(haystack))
|
|
||||||
{
|
|
||||||
OrderedSet<TypeId> newParts;
|
|
||||||
|
|
||||||
for (TypeId part : it)
|
|
||||||
{
|
|
||||||
part = follow(part);
|
|
||||||
|
|
||||||
if (part == needle)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (get<UnknownType>(part))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LUAU_ASSERT(!get<IntersectionType>(follow(part)));
|
|
||||||
|
|
||||||
if (get<UnionType>(part))
|
|
||||||
newParts.insert(removeType(arena, builtinTypes, seen, part, needle));
|
|
||||||
else
|
|
||||||
newParts.insert(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newParts.empty())
|
|
||||||
return builtinTypes->unknownType;
|
|
||||||
else if (newParts.size() == 1)
|
|
||||||
{
|
|
||||||
TypeId onlyType = *newParts.begin();
|
|
||||||
LUAU_ASSERT(onlyType != needle);
|
|
||||||
return onlyType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return arena->addType(IntersectionType{newParts.takeVector()});
|
|
||||||
}
|
|
||||||
|
|
||||||
return haystack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> generalizeType(
|
GeneralizationResult<TypeId> generalizeType(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
|
@ -1189,7 +1233,7 @@ std::optional<TypeId> generalizeType(
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
LUAU_ASSERT(ft);
|
LUAU_ASSERT(ft);
|
||||||
|
|
||||||
LUAU_ASSERT(isPositive(params.polarity) || isNegative(params.polarity));
|
LUAU_ASSERT(isKnown(params.polarity));
|
||||||
|
|
||||||
const bool hasLowerBound = !get<NeverType>(follow(ft->lowerBound));
|
const bool hasLowerBound = !get<NeverType>(follow(ft->lowerBound));
|
||||||
const bool hasUpperBound = !get<UnknownType>(follow(ft->upperBound));
|
const bool hasUpperBound = !get<UnknownType>(follow(ft->upperBound));
|
||||||
|
@ -1198,12 +1242,12 @@ std::optional<TypeId> generalizeType(
|
||||||
|
|
||||||
if (!hasLowerBound && !hasUpperBound)
|
if (!hasLowerBound && !hasUpperBound)
|
||||||
{
|
{
|
||||||
if ((params.polarity != Polarity::Mixed && params.useCount == 1) || !isWithinFunction)
|
if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1)))
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
||||||
return freeTy;
|
return {freeTy, /*wasReplacedByGeneric*/ true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// It is possible that this free type has other free types in its upper
|
// It is possible that this free type has other free types in its upper
|
||||||
|
@ -1219,20 +1263,24 @@ std::optional<TypeId> generalizeType(
|
||||||
lowerFree->upperBound = builtinTypes->unknownType;
|
lowerFree->upperBound = builtinTypes->unknownType;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> replaceSeen{nullptr};
|
std::optional<TypeId> removed = removeType(arena, builtinTypes, lb, freeTy);
|
||||||
lb = removeType(arena, builtinTypes, replaceSeen, lb, freeTy);
|
if (removed)
|
||||||
|
lb = *removed;
|
||||||
|
else
|
||||||
|
return {std::nullopt, false, /*resourceLimitsExceeded*/ true};
|
||||||
|
|
||||||
ft->lowerBound = lb;
|
ft->lowerBound = lb;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (follow(lb) != freeTy)
|
if (follow(lb) != freeTy)
|
||||||
emplaceType<BoundType>(asMutable(freeTy), lb);
|
emplaceType<BoundType>(asMutable(freeTy), lb);
|
||||||
else if (!isWithinFunction || params.useCount == 1)
|
else if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && params.useCount == 1))
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if the lower bound is the type in question (eg 'a <: 'a), we don't actually have a lower bound.
|
// if the lower bound is the type in question (eg 'a <: 'a), we don't actually have a lower bound.
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
||||||
return freeTy;
|
return {freeTy, /*wasReplacedByGeneric*/ true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1243,8 +1291,11 @@ std::optional<TypeId> generalizeType(
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the free type appears within its own upper bound, cull that cycle.
|
// If the free type appears within its own upper bound, cull that cycle.
|
||||||
DenseHashSet<TypeId> replaceSeen{nullptr};
|
std::optional<TypeId> removed = removeType(arena, builtinTypes, ub, freeTy);
|
||||||
ub = removeType(arena, builtinTypes, replaceSeen, ub, freeTy);
|
if (removed)
|
||||||
|
ub = *removed;
|
||||||
|
else
|
||||||
|
return {std::nullopt, false, /*resourceLimitsExceeded*/ true};
|
||||||
ft->upperBound = ub;
|
ft->upperBound = ub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,14 +1307,14 @@ std::optional<TypeId> generalizeType(
|
||||||
{
|
{
|
||||||
// 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.
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
||||||
return freeTy;
|
return {freeTy, /*wasReplacedByGeneric*/ true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return {freeTy, /*wasReplacedByGeneric*/ false};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypePackId> generalizeTypePack(
|
GeneralizationResult<TypePackId> generalizeTypePack(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
|
@ -1274,24 +1325,24 @@ std::optional<TypePackId> generalizeTypePack(
|
||||||
tp = follow(tp);
|
tp = follow(tp);
|
||||||
|
|
||||||
if (tp->owningArena != arena)
|
if (tp->owningArena != arena)
|
||||||
return std::nullopt;
|
return {tp, /*wasReplacedByGeneric*/ false};
|
||||||
|
|
||||||
const FreeTypePack* ftp = get<FreeTypePack>(tp);
|
const FreeTypePack* ftp = get<FreeTypePack>(tp);
|
||||||
if (!ftp)
|
if (!ftp)
|
||||||
return std::nullopt;
|
return {tp, /*wasReplacedByGeneric*/ false};
|
||||||
|
|
||||||
if (!subsumes(scope, ftp->scope))
|
if (!subsumes(scope, ftp->scope))
|
||||||
return std::nullopt;
|
return {tp, /*wasReplacedByGeneric*/ false};
|
||||||
|
|
||||||
if (1 == params.useCount)
|
if (1 == params.useCount)
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(tp), builtinTypes->unknownTypePack);
|
emplaceTypePack<BoundTypePack>(asMutable(tp), builtinTypes->unknownTypePack);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emplaceTypePack<GenericTypePack>(asMutable(tp), scope, params.polarity);
|
emplaceTypePack<GenericTypePack>(asMutable(tp), scope, params.polarity);
|
||||||
return tp;
|
return {tp, /*wasReplacedByGeneric*/ true};
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return {tp, /*wasReplacedByGeneric*/ false};
|
||||||
}
|
}
|
||||||
|
|
||||||
void sealTable(NotNull<Scope> scope, TypeId ty)
|
void sealTable(NotNull<Scope> scope, TypeId ty)
|
||||||
|
@ -1312,7 +1363,8 @@ std::optional<TypeId> generalize(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
TypeId ty
|
TypeId ty,
|
||||||
|
std::optional<TypeId> generalizationTarget
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -1323,7 +1375,7 @@ std::optional<TypeId> generalize(
|
||||||
FreeTypeSearcher fts{scope, cachedTypes};
|
FreeTypeSearcher fts{scope, cachedTypes};
|
||||||
fts.traverse(ty);
|
fts.traverse(ty);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
||||||
auto pushGeneric = [&](TypeId t)
|
auto pushGeneric = [&](TypeId t)
|
||||||
|
@ -1340,8 +1392,15 @@ std::optional<TypeId> generalize(
|
||||||
|
|
||||||
for (const auto& [freeTy, params] : fts.types)
|
for (const auto& [freeTy, params] : fts.types)
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> genericTy = generalizeType(arena, builtinTypes, scope, freeTy, params))
|
if (!generalizationTarget || freeTy == *generalizationTarget)
|
||||||
pushGeneric(*genericTy);
|
{
|
||||||
|
GeneralizationResult<TypeId> res = generalizeType(arena, builtinTypes, scope, freeTy, params);
|
||||||
|
if (res.resourceLimitsExceeded)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (res && res.wasReplacedByGeneric)
|
||||||
|
pushGeneric(*res.result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TypeId unsealedTableTy : fts.unsealedTables)
|
for (TypeId unsealedTableTy : fts.unsealedTables)
|
||||||
|
@ -1350,10 +1409,16 @@ std::optional<TypeId> generalize(
|
||||||
for (const auto& [freePackId, params] : fts.typePacks)
|
for (const auto& [freePackId, params] : fts.typePacks)
|
||||||
{
|
{
|
||||||
TypePackId freePack = follow(freePackId);
|
TypePackId freePack = follow(freePackId);
|
||||||
std::optional<TypePackId> generalizedTp = generalizeTypePack(arena, builtinTypes, scope, freePack, params);
|
if (!generalizationTarget)
|
||||||
|
{
|
||||||
|
GeneralizationResult<TypePackId> generalizedTp = generalizeTypePack(arena, builtinTypes, scope, freePack, params);
|
||||||
|
|
||||||
if (generalizedTp)
|
if (generalizedTp.resourceLimitsExceeded)
|
||||||
pushGenericPack(freePack);
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (generalizedTp && generalizedTp.wasReplacedByGeneric)
|
||||||
|
pushGenericPack(freePack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeCacher cacher{cachedTypes};
|
TypeCacher cacher{cachedTypes};
|
||||||
|
@ -1397,4 +1462,121 @@ std::optional<TypeId> generalize(
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GenericCounter : TypeVisitor
|
||||||
|
{
|
||||||
|
NotNull<DenseHashSet<TypeId>> cachedTypes;
|
||||||
|
DenseHashMap<TypeId, size_t> generics{nullptr};
|
||||||
|
DenseHashMap<TypePackId, size_t> genericPacks{nullptr};
|
||||||
|
|
||||||
|
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
||||||
|
: cachedTypes(cachedTypes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const GenericType&) override
|
||||||
|
{
|
||||||
|
size_t* count = generics.find(ty);
|
||||||
|
if (count)
|
||||||
|
++*count;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp, const GenericTypePack&) override
|
||||||
|
{
|
||||||
|
size_t* count = genericPacks.find(tp);
|
||||||
|
if (count)
|
||||||
|
++*count;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void pruneUnnecessaryGenerics(
|
||||||
|
NotNull<TypeArena> arena,
|
||||||
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<Scope> scope,
|
||||||
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
|
TypeId ty
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ty = follow(ty);
|
||||||
|
|
||||||
|
if (ty->owningArena != arena || ty->persistent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
||||||
|
|
||||||
|
if (!functionTy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Types (and packs) to be removed from the generics list
|
||||||
|
DenseHashSet<TypeId> clipTypes{nullptr};
|
||||||
|
DenseHashSet<TypePackId> clipTypePacks{nullptr};
|
||||||
|
|
||||||
|
GenericCounter counter{cachedTypes};
|
||||||
|
for (TypeId generic : functionTy->generics)
|
||||||
|
{
|
||||||
|
auto g = get<GenericType>(generic);
|
||||||
|
LUAU_ASSERT(g);
|
||||||
|
if (!g)
|
||||||
|
clipTypes.insert(generic);
|
||||||
|
else if (!g->explicitName)
|
||||||
|
counter.generics[generic] = 0;
|
||||||
|
}
|
||||||
|
for (TypePackId genericPack : functionTy->genericPacks)
|
||||||
|
{
|
||||||
|
auto g = get<GenericTypePack>(genericPack);
|
||||||
|
if (!g)
|
||||||
|
clipTypePacks.insert(genericPack);
|
||||||
|
else if (!g->explicitName)
|
||||||
|
counter.genericPacks[genericPack] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.traverse(ty);
|
||||||
|
|
||||||
|
for (const auto& [generic, count] : counter.generics)
|
||||||
|
{
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
|
||||||
|
clipTypes.insert(generic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::remove_if(
|
||||||
|
functionTy->generics.begin(),
|
||||||
|
functionTy->generics.end(),
|
||||||
|
[&](TypeId ty)
|
||||||
|
{
|
||||||
|
return clipTypes.contains(ty);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
functionTy->generics.erase(it, functionTy->generics.end());
|
||||||
|
|
||||||
|
for (const auto& [genericPack, count] : counter.genericPacks)
|
||||||
|
{
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
||||||
|
clipTypePacks.insert(genericPack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it2 = std::remove_if(
|
||||||
|
functionTy->genericPacks.begin(),
|
||||||
|
functionTy->genericPacks.end(),
|
||||||
|
[&](TypePackId tp)
|
||||||
|
{
|
||||||
|
return clipTypePacks.contains(tp);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
functionTy->genericPacks.erase(it2, functionTy->genericPacks.end());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
|
|
||||||
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::LuauNonReentrantGeneralization)
|
if (!FFlag::LuauNonReentrantGeneralization2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InferPolarity infer{arena, scope};
|
InferPolarity infer{arena, scope};
|
||||||
|
|
|
@ -229,6 +229,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
||||||
stream << "UnexpectedTypePackInSubtyping { tp = '" + toString(err.tp) + "' }";
|
stream << "UnexpectedTypePackInSubtyping { tp = '" + toString(err.tp) + "' }";
|
||||||
else if constexpr (std::is_same_v<T, UserDefinedTypeFunctionError>)
|
else if constexpr (std::is_same_v<T, UserDefinedTypeFunctionError>)
|
||||||
stream << "UserDefinedTypeFunctionError { " << err.message << " }";
|
stream << "UserDefinedTypeFunctionError { " << err.message << " }";
|
||||||
|
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||||
|
stream << "ReservedIdentifier { " << err.name << " }";
|
||||||
else if constexpr (std::is_same_v<T, CannotAssignToNever>)
|
else if constexpr (std::is_same_v<T, CannotAssignToNever>)
|
||||||
{
|
{
|
||||||
stream << "CannotAssignToNever { rvalueType = '" << toString(err.rhsType) << "', reason = '" << err.reason << "', cause = { ";
|
stream << "CannotAssignToNever { rvalueType = '" << toString(err.rhsType) << "', reason = '" << err.reason << "', cause = { ";
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictFuncDefErrorFix)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -335,7 +337,15 @@ struct NonStrictTypeChecker
|
||||||
// local x ; B generates the context of B without x
|
// local x ; B generates the context of B without x
|
||||||
visit(local);
|
visit(local);
|
||||||
for (auto local : local->vars)
|
for (auto local : local->vars)
|
||||||
|
{
|
||||||
ctx.remove(dfg->getDef(local));
|
ctx.remove(dfg->getDef(local));
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
if (local->annotation)
|
||||||
|
visit(local->annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ctx = NonStrictContext::disjunction(builtinTypes, arena, visit(stat), ctx);
|
ctx = NonStrictContext::disjunction(builtinTypes, arena, visit(stat), ctx);
|
||||||
|
@ -420,6 +430,10 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatFor* forStatement)
|
NonStrictContext visit(AstStatFor* forStatement)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
if (forStatement->var->annotation)
|
||||||
|
visit(forStatement->var->annotation);
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
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?
|
||||||
|
@ -439,6 +453,15 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatForIn* forInStatement)
|
NonStrictContext visit(AstStatForIn* forInStatement)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
for (auto var : forInStatement->vars)
|
||||||
|
{
|
||||||
|
if (var->annotation)
|
||||||
|
visit(var->annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
{
|
{
|
||||||
for (AstExpr* rhs : forInStatement->values)
|
for (AstExpr* rhs : forInStatement->values)
|
||||||
|
@ -487,6 +510,12 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
|
||||||
|
visit(typeAlias->type);
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,16 +526,38 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
visitGenerics(declFn->generics, declFn->genericPacks);
|
||||||
|
visit(declFn->params);
|
||||||
|
visit(declFn->retTypes);
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
visit(declGlobal->type);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareClass* declClass)
|
NonStrictContext visit(AstStatDeclareClass* declClass)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
if (declClass->indexer)
|
||||||
|
{
|
||||||
|
visit(declClass->indexer->indexType);
|
||||||
|
visit(declClass->indexer->resultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto prop : declClass->props)
|
||||||
|
visit(prop.ty);
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,18 +817,29 @@ struct NonStrictTypeChecker
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> ty = willRunTimeErrorFunctionDefinition(local, remainder))
|
if (std::optional<TypeId> ty = willRunTimeErrorFunctionDefinition(local, remainder))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonStrictFuncDefErrorFix)
|
const char* debugname = exprFn->debugname.value;
|
||||||
{
|
reportError(NonStrictFunctionDefinitionError{debugname ? debugname : "", local->name.value, *ty}, local->location);
|
||||||
const char* debugname = exprFn->debugname.value;
|
|
||||||
reportError(NonStrictFunctionDefinitionError{debugname ? debugname : "", local->name.value, *ty}, local->location);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reportError(NonStrictFunctionDefinitionError{exprFn->debugname.value, local->name.value, *ty}, local->location);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
remainder.remove(dfg->getDef(local));
|
remainder.remove(dfg->getDef(local));
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
if (local->annotation)
|
||||||
|
visit(local->annotation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
{
|
||||||
|
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||||
|
|
||||||
|
if (exprFn->returnAnnotation)
|
||||||
|
visit(*exprFn->returnAnnotation);
|
||||||
|
|
||||||
|
if (exprFn->varargAnnotation)
|
||||||
|
visit(exprFn->varargAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
return remainder;
|
return remainder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,6 +880,9 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
visit(typeAssertion->annotation);
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
return visit(typeAssertion->expr, ValueContext::RValue);
|
return visit(typeAssertion->expr, ValueContext::RValue);
|
||||||
else
|
else
|
||||||
|
@ -854,6 +919,323 @@ struct NonStrictTypeChecker
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void visit(AstType* ty)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
||||||
|
|
||||||
|
if (auto t = ty->as<AstTypeReference>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeTable>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeFunction>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeTypeof>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeUnion>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeIntersection>())
|
||||||
|
return visit(t);
|
||||||
|
else if (auto t = ty->as<AstTypeGroup>())
|
||||||
|
return visit(t->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeReference* ty)
|
||||||
|
{
|
||||||
|
// No further validation is necessary in this case. The main logic for
|
||||||
|
// _luau_print is contained in lookupAnnotation.
|
||||||
|
if (FFlag::DebugLuauMagicTypes && ty->name == "_luau_print")
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const AstTypeOrPack& param : ty->parameters)
|
||||||
|
{
|
||||||
|
if (param.type)
|
||||||
|
visit(param.type);
|
||||||
|
else
|
||||||
|
visit(param.typePack);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope* scope = findInnermostScope(ty->location);
|
||||||
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
|
std::optional<TypeFun> alias = ty->prefix ? scope->lookupImportedType(ty->prefix->value, ty->name.value) : scope->lookupType(ty->name.value);
|
||||||
|
|
||||||
|
if (alias.has_value())
|
||||||
|
{
|
||||||
|
size_t typesRequired = alias->typeParams.size();
|
||||||
|
size_t packsRequired = alias->typePackParams.size();
|
||||||
|
|
||||||
|
bool hasDefaultTypes = std::any_of(
|
||||||
|
alias->typeParams.begin(),
|
||||||
|
alias->typeParams.end(),
|
||||||
|
[](auto&& el)
|
||||||
|
{
|
||||||
|
return el.defaultValue.has_value();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
bool hasDefaultPacks = std::any_of(
|
||||||
|
alias->typePackParams.begin(),
|
||||||
|
alias->typePackParams.end(),
|
||||||
|
[](auto&& el)
|
||||||
|
{
|
||||||
|
return el.defaultValue.has_value();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!ty->hasParameterList)
|
||||||
|
{
|
||||||
|
if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks))
|
||||||
|
reportError(GenericError{"Type parameter list is required"}, ty->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t typesProvided = 0;
|
||||||
|
size_t extraTypes = 0;
|
||||||
|
size_t packsProvided = 0;
|
||||||
|
|
||||||
|
for (const AstTypeOrPack& p : ty->parameters)
|
||||||
|
{
|
||||||
|
if (p.type)
|
||||||
|
{
|
||||||
|
if (packsProvided != 0)
|
||||||
|
{
|
||||||
|
reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typesProvided < typesRequired)
|
||||||
|
typesProvided += 1;
|
||||||
|
else
|
||||||
|
extraTypes += 1;
|
||||||
|
}
|
||||||
|
else if (p.typePack)
|
||||||
|
{
|
||||||
|
std::optional<TypePackId> tp = lookupPackAnnotation(p.typePack);
|
||||||
|
if (!tp.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (typesProvided < typesRequired && size(*tp) == 1 && finite(*tp) && first(*tp))
|
||||||
|
typesProvided += 1;
|
||||||
|
else
|
||||||
|
packsProvided += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraTypes != 0 && packsProvided == 0)
|
||||||
|
{
|
||||||
|
// Extra types are only collected into a pack if a pack is expected
|
||||||
|
if (packsRequired != 0)
|
||||||
|
packsProvided += 1;
|
||||||
|
else
|
||||||
|
typesProvided += extraTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = typesProvided; i < typesRequired; ++i)
|
||||||
|
{
|
||||||
|
if (alias->typeParams[i].defaultValue)
|
||||||
|
typesProvided += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = packsProvided; i < packsRequired; ++i)
|
||||||
|
{
|
||||||
|
if (alias->typePackParams[i].defaultValue)
|
||||||
|
packsProvided += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
|
||||||
|
packsProvided += 1;
|
||||||
|
|
||||||
|
|
||||||
|
if (typesProvided != typesRequired || packsProvided != packsRequired)
|
||||||
|
{
|
||||||
|
reportError(
|
||||||
|
IncorrectGenericParameterCount{
|
||||||
|
/* name */ ty->name.value,
|
||||||
|
/* typeFun */ *alias,
|
||||||
|
/* actualParameters */ typesProvided,
|
||||||
|
/* actualPackParameters */ packsProvided,
|
||||||
|
},
|
||||||
|
ty->location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (scope->lookupPack(ty->name.value))
|
||||||
|
{
|
||||||
|
reportError(
|
||||||
|
SwappedGenericTypeParameter{
|
||||||
|
ty->name.value,
|
||||||
|
SwappedGenericTypeParameter::Kind::Type,
|
||||||
|
},
|
||||||
|
ty->location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string symbol = "";
|
||||||
|
if (ty->prefix)
|
||||||
|
{
|
||||||
|
symbol += (*(ty->prefix)).value;
|
||||||
|
symbol += ".";
|
||||||
|
}
|
||||||
|
symbol += ty->name.value;
|
||||||
|
|
||||||
|
reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeTable* table)
|
||||||
|
{
|
||||||
|
if (table->indexer)
|
||||||
|
{
|
||||||
|
visit(table->indexer->indexType);
|
||||||
|
visit(table->indexer->resultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto prop : table->props)
|
||||||
|
visit(prop.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeFunction* function)
|
||||||
|
{
|
||||||
|
visit(function->argTypes);
|
||||||
|
visit(function->returnTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeTypeof* typeOf)
|
||||||
|
{
|
||||||
|
visit(typeOf->expr, ValueContext::RValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeUnion* unionType)
|
||||||
|
{
|
||||||
|
for (auto typ : unionType->types)
|
||||||
|
visit(typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeIntersection* intersectionType)
|
||||||
|
{
|
||||||
|
for (auto typ : intersectionType->types)
|
||||||
|
visit(typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypeList& list)
|
||||||
|
{
|
||||||
|
for (auto typ : list.types)
|
||||||
|
visit(typ);
|
||||||
|
if (list.tailType)
|
||||||
|
visit(list.tailType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypePack* pack)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
||||||
|
|
||||||
|
if (auto p = pack->as<AstTypePackExplicit>())
|
||||||
|
return visit(p);
|
||||||
|
else if (auto p = pack->as<AstTypePackVariadic>())
|
||||||
|
return visit(p);
|
||||||
|
else if (auto p = pack->as<AstTypePackGeneric>())
|
||||||
|
return visit(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypePackExplicit* tp)
|
||||||
|
{
|
||||||
|
for (AstType* type : tp->typeList.types)
|
||||||
|
visit(type);
|
||||||
|
|
||||||
|
if (tp->typeList.tailType)
|
||||||
|
visit(tp->typeList.tailType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypePackVariadic* tp)
|
||||||
|
{
|
||||||
|
visit(tp->variadicType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(AstTypePackGeneric* tp)
|
||||||
|
{
|
||||||
|
Scope* scope = findInnermostScope(tp->location);
|
||||||
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
|
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||||
|
if (!alias.has_value())
|
||||||
|
{
|
||||||
|
if (scope->lookupType(tp->genericName.value))
|
||||||
|
{
|
||||||
|
reportError(
|
||||||
|
SwappedGenericTypeParameter{
|
||||||
|
tp->genericName.value,
|
||||||
|
SwappedGenericTypeParameter::Kind::Pack,
|
||||||
|
},
|
||||||
|
tp->location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks)
|
||||||
|
{
|
||||||
|
DenseHashSet<AstName> seen{AstName{}};
|
||||||
|
|
||||||
|
for (const auto* g : generics)
|
||||||
|
{
|
||||||
|
if (seen.contains(g->name))
|
||||||
|
reportError(DuplicateGenericParameter{g->name.value}, g->location);
|
||||||
|
else
|
||||||
|
seen.insert(g->name);
|
||||||
|
|
||||||
|
if (g->defaultValue)
|
||||||
|
visit(g->defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto* g : genericPacks)
|
||||||
|
{
|
||||||
|
if (seen.contains(g->name))
|
||||||
|
reportError(DuplicateGenericParameter{g->name.value}, g->location);
|
||||||
|
else
|
||||||
|
seen.insert(g->name);
|
||||||
|
|
||||||
|
if (g->defaultValue)
|
||||||
|
visit(g->defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope* findInnermostScope(Location location) const
|
||||||
|
{
|
||||||
|
Scope* bestScope = module->getModuleScope().get();
|
||||||
|
|
||||||
|
bool didNarrow;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
didNarrow = false;
|
||||||
|
for (auto scope : bestScope->children)
|
||||||
|
{
|
||||||
|
if (scope->location.encloses(location))
|
||||||
|
{
|
||||||
|
bestScope = scope.get();
|
||||||
|
didNarrow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (didNarrow && bestScope->children.size() > 0);
|
||||||
|
|
||||||
|
return bestScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation) const
|
||||||
|
{
|
||||||
|
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
||||||
|
if (tp != nullptr)
|
||||||
|
return {follow(*tp)};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void reportError(TypeErrorData data, const Location& location)
|
void reportError(TypeErrorData data, const Location& location)
|
||||||
{
|
{
|
||||||
module->errors.emplace_back(location, module->name, std::move(data));
|
module->errors.emplace_back(location, module->name, std::move(data));
|
||||||
|
|
|
@ -17,15 +17,11 @@
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeNegatedErrorToAnError)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeIntersectErrorToAnError)
|
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
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(LuauFixInfiniteRecursionInNormalization)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizedBufferIsNotUnknown)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeLimitFunctionSet)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationCatchMetatableCycles)
|
LUAU_FASTFLAGVARIABLE(LuauNormalizationCatchMetatableCycles)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -308,9 +304,7 @@ bool NormalizedType::isUnknown() const
|
||||||
|
|
||||||
// Otherwise, we can still be unknown!
|
// Otherwise, we can still be unknown!
|
||||||
bool hasAllPrimitives = isPrim(booleans, PrimitiveType::Boolean) && isPrim(nils, PrimitiveType::NilType) && isNumber(numbers) &&
|
bool hasAllPrimitives = isPrim(booleans, PrimitiveType::Boolean) && isPrim(nils, PrimitiveType::NilType) && isNumber(numbers) &&
|
||||||
strings.isString() &&
|
strings.isString() && isThread(threads) && isBuffer(buffers);
|
||||||
(FFlag::LuauNormalizedBufferIsNotUnknown ? isThread(threads) && isBuffer(buffers)
|
|
||||||
: isPrim(threads, PrimitiveType::Thread) && isThread(threads));
|
|
||||||
|
|
||||||
// Check is class
|
// Check is class
|
||||||
bool isTopClass = false;
|
bool isTopClass = false;
|
||||||
|
@ -1691,12 +1685,9 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNormalizeLimitFunctionSet)
|
// Limit based on worst-case expansion of the function unions
|
||||||
{
|
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeUnionLimit))
|
||||||
// Limit based on worst-case expansion of the function unions
|
return NormalizationResult::HitLimits;
|
||||||
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeUnionLimit))
|
|
||||||
return NormalizationResult::HitLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
here.booleans = unionOfBools(here.booleans, there.booleans);
|
here.booleans = unionOfBools(here.booleans, there.booleans);
|
||||||
unionClasses(here.classes, there.classes);
|
unionClasses(here.classes, there.classes);
|
||||||
|
@ -3087,11 +3078,8 @@ NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const Nor
|
||||||
if (here.tables.size() * there.tables.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
if (here.tables.size() * there.tables.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
||||||
return NormalizationResult::HitLimits;
|
return NormalizationResult::HitLimits;
|
||||||
|
|
||||||
if (FFlag::LuauNormalizeLimitFunctionSet)
|
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
||||||
{
|
return NormalizationResult::HitLimits;
|
||||||
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
|
||||||
return NormalizationResult::HitLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
||||||
|
|
||||||
|
@ -3228,7 +3216,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
{
|
{
|
||||||
TypeId errors = here.errors;
|
TypeId errors = here.errors;
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
here.errors = FFlag::LuauNormalizeIntersectErrorToAnError && get<ErrorType>(errors) ? errors : there;
|
here.errors = get<ErrorType>(errors) ? errors : there;
|
||||||
}
|
}
|
||||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
||||||
{
|
{
|
||||||
|
@ -3325,12 +3313,12 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauNormalizeNegatedErrorToAnError && get<ErrorType>(t))
|
else if (get<ErrorType>(t))
|
||||||
{
|
{
|
||||||
// ~error is still an error, so intersecting with the negation is the same as intersecting with a type
|
// ~error is still an error, so intersecting with the negation is the same as intersecting with a type
|
||||||
TypeId errors = here.errors;
|
TypeId errors = here.errors;
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
here.errors = FFlag::LuauNormalizeIntersectErrorToAnError && get<ErrorType>(errors) ? errors : t;
|
here.errors = get<ErrorType>(errors) ? errors : t;
|
||||||
}
|
}
|
||||||
else if (auto nt = get<NegationType>(t))
|
else if (auto nt = get<NegationType>(t))
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -254,15 +256,32 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any of the unsatisfied arguments are not supertypes of
|
// If any of the unsatisfied arguments are not supertypes of
|
||||||
// nil, then this overload does not match.
|
// nil or are `unknown`, then this overload does not match.
|
||||||
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
|
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
{
|
{
|
||||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
if (get<UnknownType>(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
{
|
||||||
|
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||||
|
for (auto arg : fn->argTypes)
|
||||||
|
if (get<UnknownType>(follow(arg)))
|
||||||
|
minParams += 1;
|
||||||
|
|
||||||
return {Analysis::ArityMismatch, {error}};
|
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||||
|
|
||||||
|
return {Analysis::ArityMismatch, {error}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||||
|
{
|
||||||
|
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||||
|
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||||
|
|
||||||
|
return {Analysis::ArityMismatch, {error}};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
|
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows);
|
LUAU_FASTFLAGVARIABLE(LuauSimplificationRecheckAssumption)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -47,6 +48,8 @@ struct TypeSimplifier
|
||||||
// Attempt to intersect the two types. Does not recurse. Does not handle
|
// Attempt to intersect the two types. Does not recurse. Does not handle
|
||||||
// unions, intersections, or negations.
|
// unions, intersections, or negations.
|
||||||
std::optional<TypeId> basicIntersect(TypeId left, TypeId right);
|
std::optional<TypeId> basicIntersect(TypeId left, TypeId right);
|
||||||
|
std::optional<TypeId> basicIntersectWithTruthy(TypeId target) const;
|
||||||
|
std::optional<TypeId> basicIntersectWithFalsy(TypeId target) const;
|
||||||
|
|
||||||
TypeId intersect(TypeId left, TypeId right);
|
TypeId intersect(TypeId left, TypeId right);
|
||||||
TypeId union_(TypeId left, TypeId right);
|
TypeId union_(TypeId left, TypeId right);
|
||||||
|
@ -707,7 +710,9 @@ TypeId TypeSimplifier::intersectUnionWithType(TypeId left, TypeId right)
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
std::set<TypeId> newParts;
|
std::set<TypeId> newParts;
|
||||||
|
|
||||||
if (leftUnion->options.size() > (size_t)DFInt::LuauSimplificationComplexityLimit)
|
size_t maxSize = DFInt::LuauSimplificationComplexityLimit;
|
||||||
|
|
||||||
|
if (leftUnion->options.size() > maxSize)
|
||||||
return arena->addType(IntersectionType{{left, right}});
|
return arena->addType(IntersectionType{{left, right}});
|
||||||
|
|
||||||
for (TypeId part : leftUnion)
|
for (TypeId part : leftUnion)
|
||||||
|
@ -722,6 +727,13 @@ TypeId TypeSimplifier::intersectUnionWithType(TypeId left, TypeId right)
|
||||||
}
|
}
|
||||||
|
|
||||||
newParts.insert(simplified);
|
newParts.insert(simplified);
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplificationRecheckAssumption)
|
||||||
|
{
|
||||||
|
// Initial combination size check could not predict nested union iteration
|
||||||
|
if (newParts.size() > maxSize)
|
||||||
|
return arena->addType(IntersectionType{{left, right}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!changed)
|
if (!changed)
|
||||||
|
@ -762,6 +774,13 @@ TypeId TypeSimplifier::intersectUnions(TypeId left, TypeId right)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
newParts.insert(simplified);
|
newParts.insert(simplified);
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplificationRecheckAssumption)
|
||||||
|
{
|
||||||
|
// Initial combination size check could not predict nested union iteration
|
||||||
|
if (newParts.size() > maxSize)
|
||||||
|
return arena->addType(IntersectionType{{left, right}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,6 +859,78 @@ TypeId TypeSimplifier::intersectNegatedUnion(TypeId left, TypeId right)
|
||||||
return intersectFromParts(std::move(newParts));
|
return intersectFromParts(std::move(newParts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) const
|
||||||
|
{
|
||||||
|
target = follow(target);
|
||||||
|
|
||||||
|
if (is<UnknownType>(target))
|
||||||
|
return builtinTypes->truthyType;
|
||||||
|
|
||||||
|
if (is<AnyType>(target))
|
||||||
|
// any = *error-type* | unknown, so truthy & any = *error-type* | truthy
|
||||||
|
return arena->addType(UnionType{{builtinTypes->truthyType, builtinTypes->errorType}});
|
||||||
|
|
||||||
|
if (is<NeverType, ErrorType>(target))
|
||||||
|
return target;
|
||||||
|
|
||||||
|
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
||||||
|
return target;
|
||||||
|
|
||||||
|
if (auto pt = get<PrimitiveType>(target))
|
||||||
|
{
|
||||||
|
switch (pt->type)
|
||||||
|
{
|
||||||
|
case PrimitiveType::NilType:
|
||||||
|
return builtinTypes->neverType;
|
||||||
|
case PrimitiveType::Boolean:
|
||||||
|
return builtinTypes->trueType;
|
||||||
|
default:
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto st = get<SingletonType>(target))
|
||||||
|
return st->variant == BooleanSingleton{false} ? builtinTypes->neverType : target;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) const
|
||||||
|
{
|
||||||
|
target = follow(target);
|
||||||
|
|
||||||
|
if (is<NeverType, ErrorType>(target))
|
||||||
|
return target;
|
||||||
|
|
||||||
|
if (is<AnyType>(target))
|
||||||
|
// any = *error-type* | unknown, so falsy & any = *error-type* | falsy
|
||||||
|
return arena->addType(UnionType{{builtinTypes->falsyType, builtinTypes->errorType}});
|
||||||
|
|
||||||
|
if (is<UnknownType>(target))
|
||||||
|
return builtinTypes->falsyType;
|
||||||
|
|
||||||
|
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
||||||
|
return builtinTypes->neverType;
|
||||||
|
|
||||||
|
if (auto pt = get<PrimitiveType>(target))
|
||||||
|
{
|
||||||
|
switch (pt->type)
|
||||||
|
{
|
||||||
|
case PrimitiveType::NilType:
|
||||||
|
return builtinTypes->nilType;
|
||||||
|
case PrimitiveType::Boolean:
|
||||||
|
return builtinTypes->falseType;
|
||||||
|
default:
|
||||||
|
return builtinTypes->neverType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto st = get<SingletonType>(target))
|
||||||
|
return st->variant == BooleanSingleton{false} ? builtinTypes->falseType : builtinTypes->neverType;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
||||||
{
|
{
|
||||||
const NegationType* leftNegation = get<NegationType>(left);
|
const NegationType* leftNegation = get<NegationType>(left);
|
||||||
|
@ -1066,11 +1157,8 @@ TypeId TypeSimplifier::intersectIntersectionWithType(TypeId left, TypeId right)
|
||||||
|
|
||||||
std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauFlagBasicIntersectFollows)
|
left = follow(left);
|
||||||
{
|
right = follow(right);
|
||||||
left = follow(left);
|
|
||||||
right = follow(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get<AnyType>(left) && get<ErrorType>(right))
|
if (get<AnyType>(left) && get<ErrorType>(right))
|
||||||
return right;
|
return right;
|
||||||
|
@ -1179,6 +1267,25 @@ std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
{
|
||||||
|
if (isTruthyType(left))
|
||||||
|
if (auto res = basicIntersectWithTruthy(right))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (isTruthyType(right))
|
||||||
|
if (auto res = basicIntersectWithTruthy(left))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (isFalsyType(left))
|
||||||
|
if (auto res = basicIntersectWithFalsy(right))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (isFalsyType(right))
|
||||||
|
if (auto res = basicIntersectWithFalsy(left))
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
Relation relation = relate(left, right);
|
Relation relation = relate(left, right);
|
||||||
if (left == right || Relation::Coincident == relation)
|
if (left == right || Relation::Coincident == relation)
|
||||||
return left;
|
return left;
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
#include "Luau/Substitution.h"
|
#include "Luau/Substitution.h"
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Clone.h"
|
|
||||||
#include "Luau/TxnLog.h"
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
@ -18,9 +16,9 @@ LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool alwaysClone)
|
static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
||||||
{
|
{
|
||||||
auto go = [ty, &dest, alwaysClone](auto&& a)
|
auto go = [ty, &dest](auto&& a)
|
||||||
{
|
{
|
||||||
using T = std::decay_t<decltype(a)>;
|
using T = std::decay_t<decltype(a)>;
|
||||||
|
|
||||||
|
@ -140,13 +138,8 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, ClassType>)
|
else if constexpr (std::is_same_v<T, ClassType>)
|
||||||
{
|
{
|
||||||
if (alwaysClone)
|
ClassType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
||||||
{
|
return dest.addType(std::move(clone));
|
||||||
ClassType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
|
||||||
return dest.addType(std::move(clone));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return ty;
|
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, NegationType>)
|
else if constexpr (std::is_same_v<T, NegationType>)
|
||||||
return dest.addType(NegationType{a.ty});
|
return dest.addType(NegationType{a.ty});
|
||||||
|
@ -547,6 +540,27 @@ void Tarjan::visitSCC(int index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Tarjan::ignoreChildren(TypeId ty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tarjan::ignoreChildren(TypePackId ty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
||||||
|
bool Tarjan::ignoreChildrenVisit(TypeId ty)
|
||||||
|
{
|
||||||
|
return ignoreChildren(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tarjan::ignoreChildrenVisit(TypePackId ty)
|
||||||
|
{
|
||||||
|
return ignoreChildren(ty);
|
||||||
|
}
|
||||||
|
|
||||||
TarjanResult Tarjan::findDirty(TypeId ty)
|
TarjanResult Tarjan::findDirty(TypeId ty)
|
||||||
{
|
{
|
||||||
return visitRoot(ty);
|
return visitRoot(ty);
|
||||||
|
@ -557,6 +571,11 @@ TarjanResult Tarjan::findDirty(TypePackId tp)
|
||||||
return visitRoot(tp);
|
return visitRoot(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Substitution::Substitution(TypeArena* arena)
|
||||||
|
: Substitution(TxnLog::empty(), arena)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Substitution::Substitution(const TxnLog* log_, TypeArena* arena)
|
Substitution::Substitution(const TxnLog* log_, TypeArena* arena)
|
||||||
: arena(arena)
|
: arena(arena)
|
||||||
{
|
{
|
||||||
|
@ -657,7 +676,7 @@ void Substitution::resetState(const TxnLog* log, TypeArena* arena)
|
||||||
|
|
||||||
TypeId Substitution::clone(TypeId ty)
|
TypeId Substitution::clone(TypeId ty)
|
||||||
{
|
{
|
||||||
return shallowClone(ty, *arena, log, /* alwaysClone */ true);
|
return shallowClone(ty, *arena, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId Substitution::clone(TypePackId tp)
|
TypePackId Substitution::clone(TypePackId tp)
|
||||||
|
@ -873,4 +892,13 @@ void Substitution::replaceChildren(TypePackId tp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Ty>
|
||||||
|
std::optional<Ty> Substitution::replace(std::optional<Ty> ty)
|
||||||
|
{
|
||||||
|
if (ty)
|
||||||
|
return replace(*ty);
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingStopAtNormFail)
|
|
||||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
||||||
|
|
||||||
|
@ -424,7 +423,7 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope
|
||||||
|
|
||||||
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && result.normalizationTooComplex)
|
if (result.normalizationTooComplex)
|
||||||
{
|
{
|
||||||
if (result.isCacheable)
|
if (result.isCacheable)
|
||||||
resultCache[{subTy, superTy}] = result;
|
resultCache[{subTy, superTy}] = result;
|
||||||
|
@ -610,7 +609,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
{
|
{
|
||||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && semantic.normalizationTooComplex)
|
if (semantic.normalizationTooComplex)
|
||||||
{
|
{
|
||||||
result = semantic;
|
result = semantic;
|
||||||
}
|
}
|
||||||
|
@ -630,7 +629,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
{
|
{
|
||||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && semantic.normalizationTooComplex)
|
if (semantic.normalizationTooComplex)
|
||||||
{
|
{
|
||||||
result = semantic;
|
result = semantic;
|
||||||
}
|
}
|
||||||
|
@ -1110,7 +1109,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
{
|
{
|
||||||
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && next.normalizationTooComplex)
|
if (next.normalizationTooComplex)
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||||
|
|
||||||
if (next.isSubtype)
|
if (next.isSubtype)
|
||||||
|
@ -1134,7 +1133,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Unio
|
||||||
{
|
{
|
||||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Union}));
|
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Union}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
if (subtypings.back().normalizationTooComplex)
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1150,7 +1149,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
{
|
{
|
||||||
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
if (subtypings.back().normalizationTooComplex)
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1166,7 +1165,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Inte
|
||||||
{
|
{
|
||||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
if (subtypings.back().normalizationTooComplex)
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1812,7 +1811,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||||
{
|
{
|
||||||
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && results.back().normalizationTooComplex)
|
if (results.back().normalizationTooComplex)
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceUpcast)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalFailsafe)
|
LUAU_FASTFLAGVARIABLE(LuauBidirectionalFailsafe)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
||||||
|
@ -142,13 +141,8 @@ TypeId matchLiteralType(
|
||||||
|
|
||||||
if (!isLiteral(expr))
|
if (!isLiteral(expr))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauBidirectionalInferenceUpcast)
|
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
||||||
{
|
return result.isSubtype ? expectedType : exprType;
|
||||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
|
||||||
return result.isSubtype ? expectedType : exprType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return exprType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedType = follow(expectedType);
|
expectedType = follow(expectedType);
|
||||||
|
@ -239,7 +233,7 @@ TypeId matchLiteralType(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (FFlag::LuauBidirectionalInferenceUpcast && expr->is<AstExprFunction>())
|
if (expr->is<AstExprFunction>())
|
||||||
{
|
{
|
||||||
// TODO: Push argument / return types into the lambda. For now, just do
|
// TODO: Push argument / return types into the lambda. For now, just do
|
||||||
// the non-literal thing: check for a subtype and upcast if valid.
|
// the non-literal thing: check for a subtype and upcast if valid.
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
||||||
|
@ -910,6 +911,9 @@ struct TypeStringifier
|
||||||
bool hasNonNilDisjunct = false;
|
bool hasNonNilDisjunct = false;
|
||||||
|
|
||||||
std::vector<std::string> results = {};
|
std::vector<std::string> results = {};
|
||||||
|
size_t resultsLength = 0;
|
||||||
|
bool lengthLimitHit = false;
|
||||||
|
|
||||||
for (auto el : &uv)
|
for (auto el : &uv)
|
||||||
{
|
{
|
||||||
el = follow(el);
|
el = follow(el);
|
||||||
|
@ -936,14 +940,34 @@ struct TypeStringifier
|
||||||
if (needParens)
|
if (needParens)
|
||||||
state.emit(")");
|
state.emit(")");
|
||||||
|
|
||||||
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
|
resultsLength += state.result.name.length();
|
||||||
|
|
||||||
results.push_back(std::move(state.result.name));
|
results.push_back(std::move(state.result.name));
|
||||||
|
|
||||||
state.result.name = std::move(saved);
|
state.result.name = std::move(saved);
|
||||||
|
|
||||||
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
|
{
|
||||||
|
lengthLimitHit = state.opts.maxTypeLength > 0 && resultsLength > state.opts.maxTypeLength;
|
||||||
|
|
||||||
|
if (lengthLimitHit)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.unsee(&uv);
|
state.unsee(&uv);
|
||||||
|
|
||||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
std::sort(results.begin(), results.end());
|
{
|
||||||
|
if (!lengthLimitHit && !FFlag::DebugLuauToStringNoLexicalSort)
|
||||||
|
std::sort(results.begin(), results.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||||
|
std::sort(results.begin(), results.end());
|
||||||
|
}
|
||||||
|
|
||||||
if (optional && results.size() > 1)
|
if (optional && results.size() > 1)
|
||||||
state.emit("(");
|
state.emit("(");
|
||||||
|
@ -987,6 +1011,9 @@ struct TypeStringifier
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> results = {};
|
std::vector<std::string> results = {};
|
||||||
|
size_t resultsLength = 0;
|
||||||
|
bool lengthLimitHit = false;
|
||||||
|
|
||||||
for (auto el : uv.parts)
|
for (auto el : uv.parts)
|
||||||
{
|
{
|
||||||
el = follow(el);
|
el = follow(el);
|
||||||
|
@ -1003,14 +1030,34 @@ struct TypeStringifier
|
||||||
if (needParens)
|
if (needParens)
|
||||||
state.emit(")");
|
state.emit(")");
|
||||||
|
|
||||||
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
|
resultsLength += state.result.name.length();
|
||||||
|
|
||||||
results.push_back(std::move(state.result.name));
|
results.push_back(std::move(state.result.name));
|
||||||
|
|
||||||
state.result.name = std::move(saved);
|
state.result.name = std::move(saved);
|
||||||
|
|
||||||
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
|
{
|
||||||
|
lengthLimitHit = state.opts.maxTypeLength > 0 && resultsLength > state.opts.maxTypeLength;
|
||||||
|
|
||||||
|
if (lengthLimitHit)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.unsee(&uv);
|
state.unsee(&uv);
|
||||||
|
|
||||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
if (FFlag::LuauStringPartLengthLimit)
|
||||||
std::sort(results.begin(), results.end());
|
{
|
||||||
|
if (!lengthLimitHit && !FFlag::DebugLuauToStringNoLexicalSort)
|
||||||
|
std::sort(results.begin(), results.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||||
|
std::sort(results.begin(), results.end());
|
||||||
|
}
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
bool shouldPlaceOnNewlines = results.size() > state.opts.compositeTypesSingleLineLimit || isOverloadedFunction(ty);
|
bool shouldPlaceOnNewlines = results.size() > state.opts.compositeTypesSingleLineLimit || isOverloadedFunction(ty);
|
||||||
|
|
|
@ -2646,6 +2646,7 @@ struct Printer
|
||||||
{
|
{
|
||||||
advance(item.indexerOpenPosition);
|
advance(item.indexerOpenPosition);
|
||||||
writer.symbol("[");
|
writer.symbol("[");
|
||||||
|
advance(item.stringPosition);
|
||||||
writer.sourceString(
|
writer.sourceString(
|
||||||
std::string_view(item.stringInfo->sourceString.data, item.stringInfo->sourceString.size),
|
std::string_view(item.stringInfo->sourceString.data, item.stringInfo->sourceString.size),
|
||||||
item.stringInfo->quoteStyle,
|
item.stringInfo->quoteStyle,
|
||||||
|
|
|
@ -48,7 +48,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
||||||
|
@ -65,6 +66,8 @@ LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexDeferPendingIndexee)
|
LUAU_FASTFLAGVARIABLE(LuauIndexDeferPendingIndexee)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
||||||
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -309,9 +312,22 @@ struct TypeFunctionReducer
|
||||||
|
|
||||||
enum class SkipTestResult
|
enum class SkipTestResult
|
||||||
{
|
{
|
||||||
|
/// If a type function is cyclic, it cannot be reduced, but maybe we can
|
||||||
|
/// make a guess and offer a suggested annotation to the user.
|
||||||
CyclicTypeFunction,
|
CyclicTypeFunction,
|
||||||
|
|
||||||
|
/// Indicase that we will not be able to reduce this type function this
|
||||||
|
/// time. Constraint resolution may cause this type function to become
|
||||||
|
/// reducible later.
|
||||||
Irreducible,
|
Irreducible,
|
||||||
|
|
||||||
|
/// Some type functions can operate on generic parameters
|
||||||
|
Generic,
|
||||||
|
|
||||||
|
/// We might be able to reduce this type function, but not yet.
|
||||||
Defer,
|
Defer,
|
||||||
|
|
||||||
|
/// We can attempt to reduce this type function right now.
|
||||||
Okay,
|
Okay,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -334,7 +350,10 @@ struct TypeFunctionReducer
|
||||||
}
|
}
|
||||||
else if (is<GenericType>(ty))
|
else if (is<GenericType>(ty))
|
||||||
{
|
{
|
||||||
return SkipTestResult::Irreducible;
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
return SkipTestResult::Generic;
|
||||||
|
else
|
||||||
|
return SkipTestResult::Irreducible;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SkipTestResult::Okay;
|
return SkipTestResult::Okay;
|
||||||
|
@ -353,7 +372,10 @@ struct TypeFunctionReducer
|
||||||
}
|
}
|
||||||
else if (is<GenericTypePack>(ty))
|
else if (is<GenericTypePack>(ty))
|
||||||
{
|
{
|
||||||
return SkipTestResult::Irreducible;
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
return SkipTestResult::Generic;
|
||||||
|
else
|
||||||
|
return SkipTestResult::Irreducible;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SkipTestResult::Okay;
|
return SkipTestResult::Okay;
|
||||||
|
@ -435,7 +457,7 @@ struct TypeFunctionReducer
|
||||||
{
|
{
|
||||||
SkipTestResult skip = testForSkippability(p);
|
SkipTestResult skip = testForSkippability(p);
|
||||||
|
|
||||||
if (skip == SkipTestResult::Irreducible)
|
if (skip == SkipTestResult::Irreducible || (skip == SkipTestResult::Generic && !tfit->function->canReduceGenerics))
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauLogTypeFamilies)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||||
|
@ -461,7 +483,7 @@ struct TypeFunctionReducer
|
||||||
{
|
{
|
||||||
SkipTestResult skip = testForSkippability(p);
|
SkipTestResult skip = testForSkippability(p);
|
||||||
|
|
||||||
if (skip == SkipTestResult::Irreducible)
|
if (skip == SkipTestResult::Irreducible || (skip == SkipTestResult::Generic && !tfit->function->canReduceGenerics))
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauLogTypeFamilies)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
printf("%s is irreducible due to a dependency on %s\n", toString(subject, {true}).c_str(), toString(p, {true}).c_str());
|
||||||
|
@ -1221,7 +1243,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
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);
|
||||||
|
@ -2163,6 +2185,44 @@ struct ContainsRefinableType : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool isApproximateFalsy(TypeId ty)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
bool seenNil = false;
|
||||||
|
bool seenFalse = false;
|
||||||
|
if (auto ut = get<UnionType>(ty))
|
||||||
|
{
|
||||||
|
for (auto option : ut)
|
||||||
|
{
|
||||||
|
if (auto pt = get<PrimitiveType>(option); pt && pt->type == PrimitiveType::NilType)
|
||||||
|
seenNil = true;
|
||||||
|
else if (auto st = get<SingletonType>(option); st && st->variant == BooleanSingleton{false})
|
||||||
|
seenFalse = true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seenFalse && seenNil;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isApproximateTruthy(TypeId ty)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
if (auto nt = get<NegationType>(ty))
|
||||||
|
return isApproximateFalsy(nt->ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSimpleDiscriminant(TypeId ty)
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
return isApproximateTruthy(ty) || isApproximateFalsy(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
TypeId instance,
|
TypeId instance,
|
||||||
const std::vector<TypeId>& typeParams,
|
const std::vector<TypeId>& typeParams,
|
||||||
|
@ -2257,16 +2317,37 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
if (FFlag::LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
|
||||||
if (get<TableType>(target))
|
|
||||||
{
|
{
|
||||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
||||||
if (!result.blockedTypes.empty())
|
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
||||||
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
// We also fire for simple discriminants such as false? and ~(false?): the falsy and truthy types respectively
|
||||||
|
// NOTE: It would be nice to be able to do a simple intersection for something like:
|
||||||
return {result.result, {}};
|
//
|
||||||
|
// { a: A, b: B, ... } & { x: X }
|
||||||
|
//
|
||||||
|
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
||||||
|
{
|
||||||
|
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||||
|
if (!result.blockedTypes.empty())
|
||||||
|
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||||
|
return {result.result, {}};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
||||||
|
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
||||||
|
if (get<TableType>(target))
|
||||||
|
{
|
||||||
|
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||||
|
if (!result.blockedTypes.empty())
|
||||||
|
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||||
|
|
||||||
|
return {result.result, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// In the general case, we'll still use normalization though.
|
// In the general case, we'll still use normalization though.
|
||||||
TypeId intersection = ctx->arena->addType(IntersectionType{{target, discriminant}});
|
TypeId intersection = ctx->arena->addType(IntersectionType{{target, discriminant}});
|
||||||
|
@ -2485,6 +2566,8 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
|
|
||||||
// fold over the types with `simplifyIntersection`
|
// fold over the types with `simplifyIntersection`
|
||||||
TypeId resultTy = ctx->builtins->unknownType;
|
TypeId resultTy = ctx->builtins->unknownType;
|
||||||
|
// collect types which caused intersection to return never
|
||||||
|
DenseHashSet<TypeId> unintersectableTypes{nullptr};
|
||||||
for (auto ty : types)
|
for (auto ty : types)
|
||||||
{
|
{
|
||||||
// skip any `*no-refine*` types.
|
// skip any `*no-refine*` types.
|
||||||
|
@ -2493,6 +2576,17 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
|
|
||||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
||||||
|
|
||||||
|
if (FFlag::LuauNarrowIntersectionNevers)
|
||||||
|
{
|
||||||
|
// If simplifying the intersection returned never, note the type we tried to intersect it with, and continue trying to intersect with the
|
||||||
|
// rest
|
||||||
|
if (get<NeverType>(result.result))
|
||||||
|
{
|
||||||
|
unintersectableTypes.insert(follow(ty));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (FFlag::LuauIntersectNotNil)
|
if (FFlag::LuauIntersectNotNil)
|
||||||
{
|
{
|
||||||
for (TypeId blockedType : result.blockedTypes)
|
for (TypeId blockedType : result.blockedTypes)
|
||||||
|
@ -2510,6 +2604,24 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
resultTy = result.result;
|
resultTy = result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauNarrowIntersectionNevers)
|
||||||
|
{
|
||||||
|
if (!unintersectableTypes.empty())
|
||||||
|
{
|
||||||
|
unintersectableTypes.insert(resultTy);
|
||||||
|
if (unintersectableTypes.size() > 1)
|
||||||
|
{
|
||||||
|
TypeId intersection =
|
||||||
|
ctx->arena->addType(IntersectionType{std::vector<TypeId>(unintersectableTypes.begin(), unintersectableTypes.end())});
|
||||||
|
return {intersection, Reduction::MaybeOk, {}, {}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {*unintersectableTypes.begin(), Reduction::MaybeOk, {}, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if the intersection simplifies to `never`, this gives us bad autocomplete.
|
// if the intersection simplifies to `never`, this gives us bad autocomplete.
|
||||||
// we'll just produce the intersection plainly instead, but this might be revisitable
|
// we'll just produce the intersection plainly instead, but this might be revisitable
|
||||||
// if we ever give `never` some kind of "explanation" trail.
|
// if we ever give `never` some kind of "explanation" trail.
|
||||||
|
@ -3413,8 +3525,8 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
, powFunc{"pow", powTypeFunction}
|
, powFunc{"pow", powTypeFunction}
|
||||||
, modFunc{"mod", modTypeFunction}
|
, modFunc{"mod", modTypeFunction}
|
||||||
, concatFunc{"concat", concatTypeFunction}
|
, concatFunc{"concat", concatTypeFunction}
|
||||||
, andFunc{"and", andTypeFunction}
|
, andFunc{"and", andTypeFunction, /*canReduceGenerics*/ true}
|
||||||
, orFunc{"or", orTypeFunction}
|
, orFunc{"or", orTypeFunction, /*canReduceGenerics*/ true}
|
||||||
, ltFunc{"lt", ltTypeFunction}
|
, ltFunc{"lt", ltTypeFunction}
|
||||||
, leFunc{"le", leTypeFunction}
|
, leFunc{"le", leTypeFunction}
|
||||||
, eqFunc{"eq", eqTypeFunction}
|
, eqFunc{"eq", eqTypeFunction}
|
||||||
|
|
|
@ -36,6 +36,8 @@ LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauLimitIterationWhenCheckingArgumentCounts)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1924,7 +1926,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||||
else if (auto a = expr.as<AstExprUnary>())
|
else if (auto a = expr.as<AstExprUnary>())
|
||||||
result = checkExpr(scope, *a);
|
result = checkExpr(scope, *a);
|
||||||
else if (auto a = expr.as<AstExprBinary>())
|
else if (auto a = expr.as<AstExprBinary>())
|
||||||
result = checkExpr(scope, *a, expectedType);
|
result = FFlag::LuauReduceCheckBinaryExprStackPressure ? checkExpr(scope, *a, expectedType) : checkExpr_DEPRECATED(scope, *a, expectedType);
|
||||||
else if (auto a = expr.as<AstExprTypeAssertion>())
|
else if (auto a = expr.as<AstExprTypeAssertion>())
|
||||||
result = checkExpr(scope, *a);
|
result = checkExpr(scope, *a);
|
||||||
else if (auto a = expr.as<AstExprError>())
|
else if (auto a = expr.as<AstExprError>())
|
||||||
|
@ -3186,20 +3188,82 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||||
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||||
}
|
}
|
||||||
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
||||||
|
{
|
||||||
|
// Defer the stack allocation of lhs, predicate etc until this lambda is called.
|
||||||
|
auto checkExprOr = [&]() -> WithPredicate<TypeId>
|
||||||
|
{
|
||||||
|
// For these, passing expectedType is worse than simply forcing them, because their implementation
|
||||||
|
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
|
||||||
|
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
|
||||||
|
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
|
||||||
|
|
||||||
|
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
||||||
|
return {booleanType, {std::move(*predicate)}};
|
||||||
|
|
||||||
|
PredicateVec predicates;
|
||||||
|
|
||||||
|
if (auto lvalue = tryGetLValue(*expr.left))
|
||||||
|
predicates.emplace_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
|
||||||
|
|
||||||
|
if (auto lvalue = tryGetLValue(*expr.right))
|
||||||
|
predicates.emplace_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
|
||||||
|
|
||||||
|
if (!predicates.empty() && expr.op == AstExprBinary::CompareNe)
|
||||||
|
predicates = {NotPredicate{std::move(predicates)}};
|
||||||
|
|
||||||
|
return {checkBinaryOperation(scope, expr, lhs.type, rhs.type), std::move(predicates)};
|
||||||
|
};
|
||||||
|
return checkExprOr();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Expected types are not useful for other binary operators.
|
||||||
|
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left);
|
||||||
|
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right);
|
||||||
|
|
||||||
|
// Intentionally discarding predicates with other operators.
|
||||||
|
return WithPredicate{checkBinaryOperation(scope, expr, lhs.type, rhs.type, lhs.predicates)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WithPredicate<TypeId> TypeChecker::checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType)
|
||||||
|
{
|
||||||
|
if (expr.op == AstExprBinary::And)
|
||||||
|
{
|
||||||
|
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
|
||||||
|
|
||||||
|
ScopePtr innerScope = childScope(scope, expr.location);
|
||||||
|
resolve(lhsPredicates, innerScope, true);
|
||||||
|
|
||||||
|
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
|
||||||
|
|
||||||
|
return {checkBinaryOperation(scope, expr, lhsTy, rhsTy), {AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||||
|
}
|
||||||
|
else if (expr.op == AstExprBinary::Or)
|
||||||
|
{
|
||||||
|
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
|
||||||
|
|
||||||
|
ScopePtr innerScope = childScope(scope, expr.location);
|
||||||
|
resolve(lhsPredicates, innerScope, false);
|
||||||
|
|
||||||
|
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
|
||||||
|
|
||||||
|
// Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation.
|
||||||
|
TypeId result = checkBinaryOperation(scope, expr, lhsTy, rhsTy, lhsPredicates);
|
||||||
|
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||||
|
}
|
||||||
|
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
||||||
{
|
{
|
||||||
// For these, passing expectedType is worse than simply forcing them, because their implementation
|
// For these, passing expectedType is worse than simply forcing them, because their implementation
|
||||||
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
|
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
|
||||||
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
|
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
|
||||||
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
|
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
|
||||||
|
|
||||||
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
||||||
return {booleanType, {std::move(*predicate)}};
|
return {booleanType, {std::move(*predicate)}};
|
||||||
|
|
||||||
PredicateVec predicates;
|
PredicateVec predicates;
|
||||||
|
|
||||||
if (auto lvalue = tryGetLValue(*expr.left))
|
if (auto lvalue = tryGetLValue(*expr.left))
|
||||||
predicates.push_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
|
predicates.push_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
|
||||||
|
|
||||||
if (auto lvalue = tryGetLValue(*expr.right))
|
if (auto lvalue = tryGetLValue(*expr.right))
|
||||||
predicates.push_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
|
predicates.push_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
|
||||||
|
|
||||||
|
@ -4050,6 +4114,23 @@ void TypeChecker::checkArgumentList(
|
||||||
|
|
||||||
size_t paramIndex = 0;
|
size_t paramIndex = 0;
|
||||||
|
|
||||||
|
int loopCount = 0;
|
||||||
|
auto exceedsLoopCount = [&]()
|
||||||
|
{
|
||||||
|
if (FFlag::LuauLimitIterationWhenCheckingArgumentCounts)
|
||||||
|
{
|
||||||
|
++loopCount;
|
||||||
|
if (loopCount > FInt::LuauTypeInferTypePackLoopLimit)
|
||||||
|
{
|
||||||
|
state.reportError(TypeError{state.location, CodeTooComplex{}});
|
||||||
|
reportErrorCodeTooComplex(state.location);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
auto reportCountMismatchError = [&state, &argLocations, paramPack, argPack, &funName]()
|
auto reportCountMismatchError = [&state, &argLocations, paramPack, argPack, &funName]()
|
||||||
{
|
{
|
||||||
// For this case, we want the error span to cover every errant extra parameter
|
// For this case, we want the error span to cover every errant extra parameter
|
||||||
|
@ -4124,12 +4205,17 @@ void TypeChecker::checkArgumentList(
|
||||||
}
|
}
|
||||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||||
{
|
{
|
||||||
|
loopCount = 0;
|
||||||
|
|
||||||
// Function is variadic and requires that all subsequent parameters
|
// Function is variadic and requires that all subsequent parameters
|
||||||
// be compatible with a type.
|
// be compatible with a type.
|
||||||
while (paramIter != endIter)
|
while (paramIter != endIter)
|
||||||
{
|
{
|
||||||
state.tryUnify(vtp->ty, *paramIter);
|
state.tryUnify(vtp->ty, *paramIter);
|
||||||
++paramIter;
|
++paramIter;
|
||||||
|
|
||||||
|
if (exceedsLoopCount())
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -4138,10 +4224,16 @@ void TypeChecker::checkArgumentList(
|
||||||
{
|
{
|
||||||
std::vector<TypeId> rest;
|
std::vector<TypeId> rest;
|
||||||
rest.reserve(std::distance(paramIter, endIter));
|
rest.reserve(std::distance(paramIter, endIter));
|
||||||
|
|
||||||
|
loopCount = 0;
|
||||||
|
|
||||||
while (paramIter != endIter)
|
while (paramIter != endIter)
|
||||||
{
|
{
|
||||||
rest.push_back(*paramIter);
|
rest.push_back(*paramIter);
|
||||||
++paramIter;
|
++paramIter;
|
||||||
|
|
||||||
|
if (exceedsLoopCount())
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||||
|
@ -4185,12 +4277,17 @@ void TypeChecker::checkArgumentList(
|
||||||
// too many parameters passed
|
// too many parameters passed
|
||||||
if (!paramIter.tail())
|
if (!paramIter.tail())
|
||||||
{
|
{
|
||||||
|
loopCount = 0;
|
||||||
|
|
||||||
while (argIter != endIter)
|
while (argIter != endIter)
|
||||||
{
|
{
|
||||||
// The use of unify here is deliberate. We don't want this unification
|
// The use of unify here is deliberate. We don't want this unification
|
||||||
// to be undoable.
|
// to be undoable.
|
||||||
unify(errorRecoveryType(scope), *argIter, scope, state.location);
|
unify(errorRecoveryType(scope), *argIter, scope, state.location);
|
||||||
++argIter;
|
++argIter;
|
||||||
|
|
||||||
|
if (exceedsLoopCount())
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
reportCountMismatchError();
|
reportCountMismatchError();
|
||||||
return;
|
return;
|
||||||
|
@ -4204,6 +4301,8 @@ void TypeChecker::checkArgumentList(
|
||||||
}
|
}
|
||||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||||
{
|
{
|
||||||
|
loopCount = 0;
|
||||||
|
|
||||||
// Function is variadic and requires that all subsequent parameters
|
// Function is variadic and requires that all subsequent parameters
|
||||||
// be compatible with a type.
|
// be compatible with a type.
|
||||||
size_t argIndex = paramIndex;
|
size_t argIndex = paramIndex;
|
||||||
|
@ -4219,12 +4318,17 @@ void TypeChecker::checkArgumentList(
|
||||||
|
|
||||||
++argIter;
|
++argIter;
|
||||||
++argIndex;
|
++argIndex;
|
||||||
|
|
||||||
|
if (exceedsLoopCount())
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||||
{
|
{
|
||||||
|
loopCount = 0;
|
||||||
|
|
||||||
// Create a type pack out of the remaining argument types
|
// Create a type pack out of the remaining argument types
|
||||||
// and unify it with the tail.
|
// and unify it with the tail.
|
||||||
std::vector<TypeId> rest;
|
std::vector<TypeId> rest;
|
||||||
|
@ -4233,7 +4337,10 @@ void TypeChecker::checkArgumentList(
|
||||||
{
|
{
|
||||||
rest.push_back(*argIter);
|
rest.push_back(*argIter);
|
||||||
++argIter;
|
++argIter;
|
||||||
}
|
|
||||||
|
if (exceedsLoopCount())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||||
state.tryUnify(varPack, tail);
|
state.tryUnify(varPack, tail);
|
||||||
|
|
|
@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -308,7 +308,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::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -577,7 +577,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::LuauNonReentrantGeneralization)
|
if (!FFlag::LuauNonReentrantGeneralization2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (; scope; scope = scope->parent.get())
|
for (; scope; scope = scope->parent.get())
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifyMetatableWithAny)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauExtraFollows)
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -238,9 +236,9 @@ bool Unifier2::unify(TypeId subTy, TypeId superTy)
|
||||||
auto superMetatable = get<MetatableType>(superTy);
|
auto superMetatable = get<MetatableType>(superTy);
|
||||||
if (subMetatable && superMetatable)
|
if (subMetatable && superMetatable)
|
||||||
return unify(subMetatable, superMetatable);
|
return unify(subMetatable, superMetatable);
|
||||||
else if (FFlag::LuauUnifyMetatableWithAny && subMetatable && superAny)
|
else if (subMetatable && superAny)
|
||||||
return unify(subMetatable, superAny);
|
return unify(subMetatable, superAny);
|
||||||
else if (FFlag::LuauUnifyMetatableWithAny && subAny && superMetatable)
|
else if (subAny && superMetatable)
|
||||||
return unify(subAny, superMetatable);
|
return unify(subAny, superMetatable);
|
||||||
else if (subMetatable) // if we only have one metatable, unify with the inner table
|
else if (subMetatable) // if we only have one metatable, unify with the inner table
|
||||||
return unify(subMetatable->table, superTy);
|
return unify(subMetatable->table, superTy);
|
||||||
|
@ -284,7 +282,7 @@ bool Unifier2::unifyFreeWithType(TypeId subTy, TypeId superTy)
|
||||||
if (superArgTail)
|
if (superArgTail)
|
||||||
return doDefault();
|
return doDefault();
|
||||||
|
|
||||||
const IntersectionType* upperBoundIntersection = get<IntersectionType>(FFlag::LuauExtraFollows ? upperBound : subFree->upperBound);
|
const IntersectionType* upperBoundIntersection = get<IntersectionType>(upperBound);
|
||||||
if (!upperBoundIntersection)
|
if (!upperBoundIntersection)
|
||||||
return doDefault();
|
return doDefault();
|
||||||
|
|
||||||
|
@ -321,18 +319,18 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
|
|
||||||
if (shouldInstantiate)
|
if (shouldInstantiate)
|
||||||
{
|
{
|
||||||
for (auto generic : subFn->generics)
|
for (TypeId generic : subFn->generics)
|
||||||
{
|
{
|
||||||
const GenericType* gen = get<GenericType>(generic);
|
const GenericType* gen = get<GenericType>(generic);
|
||||||
LUAU_ASSERT(gen);
|
LUAU_ASSERT(gen);
|
||||||
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto genericPack : subFn->genericPacks)
|
for (TypePackId genericPack : subFn->genericPacks)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
const GenericTypePack* gen = get<GenericTypePack>(genericPack);
|
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||||
LUAU_ASSERT(gen);
|
LUAU_ASSERT(gen);
|
||||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
||||||
}
|
}
|
||||||
|
@ -651,211 +649,6 @@ bool Unifier2::unify(TypePackId subTp, TypePackId superTp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FreeTypeSearcher : TypeVisitor
|
|
||||||
{
|
|
||||||
NotNull<Scope> scope;
|
|
||||||
|
|
||||||
explicit FreeTypeSearcher(NotNull<Scope> scope)
|
|
||||||
: TypeVisitor(/*skipBoundTypes*/ true)
|
|
||||||
, scope(scope)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Polarity polarity = Polarity::Positive;
|
|
||||||
|
|
||||||
void flip()
|
|
||||||
{
|
|
||||||
switch (polarity)
|
|
||||||
{
|
|
||||||
case Polarity::Positive:
|
|
||||||
polarity = Polarity::Negative;
|
|
||||||
break;
|
|
||||||
case Polarity::Negative:
|
|
||||||
polarity = Polarity::Positive;
|
|
||||||
break;
|
|
||||||
case Polarity::Mixed:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DenseHashSet<const void*> seenPositive{nullptr};
|
|
||||||
DenseHashSet<const void*> seenNegative{nullptr};
|
|
||||||
|
|
||||||
bool seenWithCurrentPolarity(const void* ty)
|
|
||||||
{
|
|
||||||
switch (polarity)
|
|
||||||
{
|
|
||||||
case Polarity::Positive:
|
|
||||||
{
|
|
||||||
if (seenPositive.contains(ty))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
seenPositive.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case Polarity::Negative:
|
|
||||||
{
|
|
||||||
if (seenNegative.contains(ty))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
seenNegative.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case Polarity::Mixed:
|
|
||||||
{
|
|
||||||
if (seenPositive.contains(ty) && seenNegative.contains(ty))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
seenPositive.insert(ty);
|
|
||||||
seenNegative.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The keys in these maps are either TypeIds or TypePackIds. It's safe to
|
|
||||||
// mix them because we only use these pointers as unique keys. We never
|
|
||||||
// indirect them.
|
|
||||||
DenseHashMap<const void*, size_t> negativeTypes{0};
|
|
||||||
DenseHashMap<const void*, size_t> positiveTypes{0};
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
|
||||||
{
|
|
||||||
if (seenWithCurrentPolarity(ty))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
LUAU_ASSERT(ty);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
|
||||||
{
|
|
||||||
if (seenWithCurrentPolarity(ty))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!subsumes(scope, ft.scope))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
switch (polarity)
|
|
||||||
{
|
|
||||||
case Polarity::Positive:
|
|
||||||
positiveTypes[ty]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Negative:
|
|
||||||
negativeTypes[ty]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Mixed:
|
|
||||||
positiveTypes[ty]++;
|
|
||||||
negativeTypes[ty]++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TableType& tt) override
|
|
||||||
{
|
|
||||||
if (seenWithCurrentPolarity(ty))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
|
||||||
{
|
|
||||||
switch (polarity)
|
|
||||||
{
|
|
||||||
case Polarity::Positive:
|
|
||||||
positiveTypes[ty]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Negative:
|
|
||||||
negativeTypes[ty]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Mixed:
|
|
||||||
positiveTypes[ty]++;
|
|
||||||
negativeTypes[ty]++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& [_name, prop] : tt.props)
|
|
||||||
{
|
|
||||||
if (prop.isReadOnly())
|
|
||||||
traverse(*prop.readTy);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(prop.isShared());
|
|
||||||
|
|
||||||
Polarity p = polarity;
|
|
||||||
polarity = Polarity::Mixed;
|
|
||||||
traverse(prop.type());
|
|
||||||
polarity = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tt.indexer)
|
|
||||||
{
|
|
||||||
traverse(tt.indexer->indexType);
|
|
||||||
traverse(tt.indexer->indexResultType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const FunctionType& ft) override
|
|
||||||
{
|
|
||||||
if (seenWithCurrentPolarity(ty))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
flip();
|
|
||||||
traverse(ft.argTypes);
|
|
||||||
flip();
|
|
||||||
|
|
||||||
traverse(ft.retTypes);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId, const ClassType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp, const FreeTypePack& ftp) override
|
|
||||||
{
|
|
||||||
if (seenWithCurrentPolarity(tp))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!subsumes(scope, ftp.scope))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
switch (polarity)
|
|
||||||
{
|
|
||||||
case Polarity::Positive:
|
|
||||||
positiveTypes[tp]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Negative:
|
|
||||||
negativeTypes[tp]++;
|
|
||||||
break;
|
|
||||||
case Polarity::Mixed:
|
|
||||||
positiveTypes[tp]++;
|
|
||||||
negativeTypes[tp]++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeId Unifier2::mkUnion(TypeId left, TypeId right)
|
TypeId Unifier2::mkUnion(TypeId left, TypeId right)
|
||||||
{
|
{
|
||||||
left = follow(left);
|
left = follow(left);
|
||||||
|
|
|
@ -87,8 +87,8 @@ struct AstLocal
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct AstArray
|
struct AstArray
|
||||||
{
|
{
|
||||||
T* data;
|
T* data = nullptr;
|
||||||
size_t size;
|
size_t size = 0;
|
||||||
|
|
||||||
const T* begin() const
|
const T* begin() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -388,6 +388,7 @@ public:
|
||||||
std::optional<Position> separatorPosition;
|
std::optional<Position> separatorPosition;
|
||||||
|
|
||||||
CstExprConstantString* stringInfo = nullptr; // only if Kind == StringProperty
|
CstExprConstantString* stringInfo = nullptr; // only if Kind == StringProperty
|
||||||
|
Position stringPosition{0, 0}; // only if Kind == StringProperty
|
||||||
};
|
};
|
||||||
|
|
||||||
CstTypeTable(AstArray<Item> items, bool isArray);
|
CstTypeTable(AstArray<Item> items, bool isArray);
|
||||||
|
|
|
@ -18,8 +18,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
// flag so that we don't break production games by reverting syntax changes.
|
// flag so that we don't break production games by reverting syntax changes.
|
||||||
// See docs/SyntaxChanges.md for an explanation.
|
// See docs/SyntaxChanges.md for an explanation.
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
||||||
|
@ -1670,8 +1668,7 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
||||||
//
|
//
|
||||||
// function (t: { a: number }) end
|
// function (t: { a: number }) end
|
||||||
//
|
//
|
||||||
if (FFlag::LuauErrorRecoveryForTableTypes)
|
matchRecoveryStopOnToken[')']++;
|
||||||
matchRecoveryStopOnToken[')']++;
|
|
||||||
|
|
||||||
TempVector<Binding> args(scratchBinding);
|
TempVector<Binding> args(scratchBinding);
|
||||||
|
|
||||||
|
@ -1690,8 +1687,7 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
||||||
|
|
||||||
expectMatchAndConsume(')', matchParen, true);
|
expectMatchAndConsume(')', matchParen, true);
|
||||||
|
|
||||||
if (FFlag::LuauErrorRecoveryForTableTypes)
|
matchRecoveryStopOnToken[')']--;
|
||||||
matchRecoveryStopOnToken[')']--;
|
|
||||||
|
|
||||||
std::optional<AstTypeList> typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
std::optional<AstTypeList> typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
||||||
|
|
||||||
|
@ -2173,6 +2169,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
||||||
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
||||||
std::tie(style, blockDepth) = extractStringDetails();
|
std::tie(style, blockDepth) = extractStringDetails();
|
||||||
|
|
||||||
|
Position stringPosition = lexer.current().location.begin;
|
||||||
AstArray<char> sourceString;
|
AstArray<char> sourceString;
|
||||||
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
|
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
|
||||||
|
|
||||||
|
@ -2197,7 +2194,8 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
||||||
colonPosition,
|
colonPosition,
|
||||||
tableSeparator(),
|
tableSeparator(),
|
||||||
lexer.current().location.begin,
|
lexer.current().location.begin,
|
||||||
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth)
|
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth),
|
||||||
|
stringPosition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2288,6 +2286,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
||||||
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
||||||
std::tie(style, blockDepth) = extractStringDetails();
|
std::tie(style, blockDepth) = extractStringDetails();
|
||||||
|
|
||||||
|
Position stringPosition = lexer.current().location.begin;
|
||||||
AstArray<char> sourceString;
|
AstArray<char> sourceString;
|
||||||
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
|
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
|
||||||
|
|
||||||
|
@ -2312,7 +2311,8 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
||||||
colonPosition,
|
colonPosition,
|
||||||
tableSeparator(),
|
tableSeparator(),
|
||||||
lexer.current().location.begin,
|
lexer.current().location.begin,
|
||||||
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth)
|
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth),
|
||||||
|
stringPosition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2408,7 +2408,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
||||||
|
|
||||||
Location end = lexer.current().location;
|
Location end = lexer.current().location;
|
||||||
|
|
||||||
if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ FFlag::LuauErrorRecoveryForTableTypes))
|
if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ true))
|
||||||
end = lexer.previousLocation();
|
end = lexer.previousLocation();
|
||||||
|
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData2)
|
||||||
|
@ -4088,78 +4088,66 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams(Position* openingPosition, TempV
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '(')
|
else if (lexer.current().type == '(')
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAllowComplexTypesInGenericParams)
|
Location begin = lexer.current().location;
|
||||||
|
AstType* type = nullptr;
|
||||||
|
AstTypePack* typePack = nullptr;
|
||||||
|
Lexeme::Type c = lexer.current().type;
|
||||||
|
|
||||||
|
if (c != '|' && c != '&')
|
||||||
{
|
{
|
||||||
Location begin = lexer.current().location;
|
auto typeOrTypePack = parseSimpleType(/* allowPack */ true, /* inDeclarationContext */ false);
|
||||||
AstType* type = nullptr;
|
type = typeOrTypePack.type;
|
||||||
AstTypePack* typePack = nullptr;
|
typePack = typeOrTypePack.typePack;
|
||||||
Lexeme::Type c = lexer.current().type;
|
}
|
||||||
|
|
||||||
if (c != '|' && c != '&')
|
// Consider the following type:
|
||||||
|
//
|
||||||
|
// X<(T)>
|
||||||
|
//
|
||||||
|
// Is this a type pack or a parenthesized type? The
|
||||||
|
// assumption will be a type pack, as that's what allows one
|
||||||
|
// to express either a singular type pack or a potential
|
||||||
|
// complex type.
|
||||||
|
|
||||||
|
if (typePack)
|
||||||
|
{
|
||||||
|
auto explicitTypePack = typePack->as<AstTypePackExplicit>();
|
||||||
|
if (explicitTypePack && explicitTypePack->typeList.tailType == nullptr && explicitTypePack->typeList.types.size == 1 &&
|
||||||
|
isTypeFollow(lexer.current().type))
|
||||||
{
|
{
|
||||||
auto typeOrTypePack = parseSimpleType(/* allowPack */ true, /* inDeclarationContext */ false);
|
// If we parsed an explicit type pack with a single
|
||||||
type = typeOrTypePack.type;
|
// type in it (something of the form `(T)`), and
|
||||||
typePack = typeOrTypePack.typePack;
|
// the next lexeme is one that follows a type
|
||||||
}
|
// (&, |, ?), then assume that this was actually a
|
||||||
|
// parenthesized type.
|
||||||
// Consider the following type:
|
if (FFlag::LuauAstTypeGroup3)
|
||||||
//
|
|
||||||
// X<(T)>
|
|
||||||
//
|
|
||||||
// Is this a type pack or a parenthesized type? The
|
|
||||||
// assumption will be a type pack, as that's what allows one
|
|
||||||
// to express either a singular type pack or a potential
|
|
||||||
// complex type.
|
|
||||||
|
|
||||||
if (typePack)
|
|
||||||
{
|
|
||||||
auto explicitTypePack = typePack->as<AstTypePackExplicit>();
|
|
||||||
if (explicitTypePack && explicitTypePack->typeList.tailType == nullptr && explicitTypePack->typeList.types.size == 1 &&
|
|
||||||
isTypeFollow(lexer.current().type))
|
|
||||||
{
|
{
|
||||||
// If we parsed an explicit type pack with a single
|
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
||||||
// type in it (something of the form `(T)`), and
|
parameters.push_back(
|
||||||
// the next lexeme is one that follows a type
|
{parseTypeSuffix(allocator.alloc<AstTypeGroup>(parenthesizedType->location, parenthesizedType), begin), {}}
|
||||||
// (&, |, ?), then assume that this was actually a
|
);
|
||||||
// parenthesized type.
|
|
||||||
if (FFlag::LuauAstTypeGroup3)
|
|
||||||
{
|
|
||||||
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
|
||||||
parameters.push_back(
|
|
||||||
{parseTypeSuffix(allocator.alloc<AstTypeGroup>(parenthesizedType->location, parenthesizedType), begin), {}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
parameters.push_back({parseTypeSuffix(explicitTypePack->typeList.types.data[0], begin), {}});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
parameters.push_back({parseTypeSuffix(explicitTypePack->typeList.types.data[0], begin), {}});
|
||||||
// Otherwise, it's a type pack.
|
|
||||||
parameters.push_back({{}, typePack});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// There's two cases in which `typePack` will be null:
|
// Otherwise, it's a type pack.
|
||||||
// - We try to parse a simple type or a type pack, and
|
parameters.push_back({{}, typePack});
|
||||||
// we get a simple type: there's no ambiguity and
|
|
||||||
// we attempt to parse a complex type.
|
|
||||||
// - The next lexeme was a `|` or `&` indicating a
|
|
||||||
// union or intersection type with a leading
|
|
||||||
// separator. We just fall right into
|
|
||||||
// `parseTypeSuffix`, which allows its first
|
|
||||||
// argument to be `nullptr`
|
|
||||||
parameters.push_back({parseTypeSuffix(type, begin), {}});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto [type, typePack] = parseSimpleTypeOrPack();
|
// There's two cases in which `typePack` will be null:
|
||||||
|
// - We try to parse a simple type or a type pack, and
|
||||||
if (typePack)
|
// we get a simple type: there's no ambiguity and
|
||||||
parameters.push_back({{}, typePack});
|
// we attempt to parse a complex type.
|
||||||
else
|
// - The next lexeme was a `|` or `&` indicating a
|
||||||
parameters.push_back({type, {}});
|
// union or intersection type with a leading
|
||||||
|
// separator. We just fall right into
|
||||||
|
// `parseTypeSuffix`, which allows its first
|
||||||
|
// argument to be `nullptr`
|
||||||
|
parameters.push_back({parseTypeSuffix(type, begin), {}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '>' && parameters.empty())
|
else if (lexer.current().type == '>' && parameters.empty())
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -15,80 +13,11 @@ namespace CodeGen
|
||||||
struct IrBuilder;
|
struct IrBuilder;
|
||||||
enum class HostMetamethod;
|
enum class HostMetamethod;
|
||||||
|
|
||||||
inline bool isJumpD(LuauOpcode op)
|
int getOpLength(LuauOpcode op);
|
||||||
{
|
bool isJumpD(LuauOpcode op);
|
||||||
switch (op)
|
bool isSkipC(LuauOpcode op);
|
||||||
{
|
bool isFastCall(LuauOpcode op);
|
||||||
case LOP_JUMP:
|
int getJumpTarget(uint32_t insn, uint32_t pc);
|
||||||
case LOP_JUMPIF:
|
|
||||||
case LOP_JUMPIFNOT:
|
|
||||||
case LOP_JUMPIFEQ:
|
|
||||||
case LOP_JUMPIFLE:
|
|
||||||
case LOP_JUMPIFLT:
|
|
||||||
case LOP_JUMPIFNOTEQ:
|
|
||||||
case LOP_JUMPIFNOTLE:
|
|
||||||
case LOP_JUMPIFNOTLT:
|
|
||||||
case LOP_FORNPREP:
|
|
||||||
case LOP_FORNLOOP:
|
|
||||||
case LOP_FORGPREP:
|
|
||||||
case LOP_FORGLOOP:
|
|
||||||
case LOP_FORGPREP_INEXT:
|
|
||||||
case LOP_FORGPREP_NEXT:
|
|
||||||
case LOP_JUMPBACK:
|
|
||||||
case LOP_JUMPXEQKNIL:
|
|
||||||
case LOP_JUMPXEQKB:
|
|
||||||
case LOP_JUMPXEQKN:
|
|
||||||
case LOP_JUMPXEQKS:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isSkipC(LuauOpcode op)
|
|
||||||
{
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case LOP_LOADB:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isFastCall(LuauOpcode op)
|
|
||||||
{
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case LOP_FASTCALL:
|
|
||||||
case LOP_FASTCALL1:
|
|
||||||
case LOP_FASTCALL2:
|
|
||||||
case LOP_FASTCALL2K:
|
|
||||||
case LOP_FASTCALL3:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getJumpTarget(uint32_t insn, uint32_t pc)
|
|
||||||
{
|
|
||||||
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
|
||||||
|
|
||||||
if (isJumpD(op))
|
|
||||||
return int(pc + LUAU_INSN_D(insn) + 1);
|
|
||||||
else if (isFastCall(op))
|
|
||||||
return int(pc + LUAU_INSN_C(insn) + 2);
|
|
||||||
else if (isSkipC(op) && LUAU_INSN_C(insn))
|
|
||||||
return int(pc + LUAU_INSN_C(insn) + 1);
|
|
||||||
else if (op == LOP_JUMPX)
|
|
||||||
return int(pc + LUAU_INSN_E(insn) + 1);
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isBlockTerminator(IrCmd cmd)
|
inline bool isBlockTerminator(IrCmd cmd)
|
||||||
{
|
{
|
||||||
|
@ -180,9 +109,6 @@ inline bool hasResult(IrCmd cmd)
|
||||||
case IrCmd::MUL_VEC:
|
case IrCmd::MUL_VEC:
|
||||||
case IrCmd::DIV_VEC:
|
case IrCmd::DIV_VEC:
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
if (cmd == IrCmd::DOT_VEC)
|
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
LUAU_FALLTHROUGH;
|
|
||||||
case IrCmd::UNM_VEC:
|
case IrCmd::UNM_VEC:
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -590,7 +588,6 @@ void AssemblyBuilderA64::fabs(RegisterA64 dst, RegisterA64 src)
|
||||||
|
|
||||||
void AssemblyBuilderA64::faddp(RegisterA64 dst, RegisterA64 src)
|
void AssemblyBuilderA64::faddp(RegisterA64 dst, RegisterA64 src)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
|
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
|
||||||
CODEGEN_ASSERT(dst.kind == src.kind);
|
CODEGEN_ASSERT(dst.kind == src.kind);
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -955,7 +953,6 @@ void AssemblyBuilderX64::vpinsrd(RegisterX64 dst, RegisterX64 src1, OperandX64 s
|
||||||
|
|
||||||
void AssemblyBuilderX64::vdpps(OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t mask)
|
void AssemblyBuilderX64::vdpps(OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t mask)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
placeAvx("vdpps", dst, src1, src2, mask, 0x40, false, AVX_0F3A, AVX_66);
|
placeAvx("vdpps", dst, src1, src2, mask, 0x40, false, AVX_0F3A, AVX_66);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BytecodeAnalysis.h"
|
#include "Luau/BytecodeAnalysis.h"
|
||||||
|
|
||||||
#include "Luau/BytecodeUtils.h"
|
|
||||||
#include "Luau/CodeGenOptions.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
@ -639,7 +638,7 @@ void buildBytecodeBlocks(IrFunction& function, const std::vector<uint8_t>& jumpT
|
||||||
bcBlocks.push_back(BytecodeBlock{nexti, -1});
|
bcBlocks.push_back(BytecodeBlock{nexti, -1});
|
||||||
}
|
}
|
||||||
// Returns just terminate the block
|
// Returns just terminate the block
|
||||||
else if (op == LOP_RETURN)
|
else if (int(op) == LOP_RETURN)
|
||||||
{
|
{
|
||||||
bcBlocks.back().finishpc = i;
|
bcBlocks.back().finishpc = i;
|
||||||
}
|
}
|
||||||
|
@ -702,7 +701,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
|
|
||||||
BytecodeTypes& bcType = function.bcTypes[i];
|
BytecodeTypes& bcType = function.bcTypes[i];
|
||||||
|
|
||||||
switch (op)
|
switch (int(op))
|
||||||
{
|
{
|
||||||
case LOP_NOP:
|
case LOP_NOP:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// 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/BytecodeSummary.h"
|
#include "Luau/BytecodeSummary.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
|
||||||
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
#include "CodeGenLower.h"
|
#include "CodeGenLower.h"
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
@ -42,7 +44,7 @@ FunctionBytecodeSummary FunctionBytecodeSummary::fromProto(Proto* proto, unsigne
|
||||||
Instruction insn = proto->code[i];
|
Instruction insn = proto->code[i];
|
||||||
uint8_t op = LUAU_INSN_OP(insn);
|
uint8_t op = LUAU_INSN_OP(insn);
|
||||||
summary.incCount(0, op);
|
summary.incCount(0, op);
|
||||||
i += Luau::getOpLength(LuauOpcode(op));
|
i += getOpLength(LuauOpcode(op));
|
||||||
}
|
}
|
||||||
|
|
||||||
return summary;
|
return summary;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
|
||||||
|
|
||||||
// __register_frame and __deregister_frame are defined in libgcc or libc++
|
// __register_frame and __deregister_frame are defined in libgcc or libc++
|
||||||
// (depending on how it's built). We want to declare them as weak symbols
|
// (depending on how it's built). We want to declare them as weak symbols
|
||||||
|
@ -81,7 +81,7 @@ static int findDynamicUnwindSections(uintptr_t addr, unw_dynamic_unwind_sections
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
|
||||||
static void visitFdeEntries(char* pos, void (*cb)(const void*))
|
static void visitFdeEntries(char* pos, void (*cb)(const void*))
|
||||||
{
|
{
|
||||||
// When using glibc++ unwinder, we need to call __register_frame/__deregister_frame on the entire .eh_frame data
|
// When using glibc++ unwinder, we need to call __register_frame/__deregister_frame on the entire .eh_frame data
|
||||||
|
@ -132,7 +132,7 @@ void* createBlockUnwindInfo(void* context, uint8_t* block, size_t blockSize, siz
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
|
||||||
if (!&__register_frame)
|
if (!&__register_frame)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ void destroyBlockUnwindInfo(void* context, void* unwindData)
|
||||||
CODEGEN_ASSERT(!"Failed to deallocate function table");
|
CODEGEN_ASSERT(!"Failed to deallocate function table");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
|
||||||
if (!&__deregister_frame)
|
if (!&__deregister_frame)
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(!"Cannot deregister unwind information");
|
CODEGEN_ASSERT(!"Cannot deregister unwind information");
|
||||||
|
@ -184,7 +184,7 @@ bool isUnwindSupported()
|
||||||
size_t verLength = sizeof(ver);
|
size_t verLength = sizeof(ver);
|
||||||
// libunwind on macOS 12 and earlier (which maps to osrelease 21) assumes JIT frames use pointer authentication without a way to override that
|
// libunwind on macOS 12 and earlier (which maps to osrelease 21) assumes JIT frames use pointer authentication without a way to override that
|
||||||
return sysctlbyname("kern.osrelease", ver, &verLength, NULL, 0) == 0 && atoi(ver) >= 22;
|
return sysctlbyname("kern.osrelease", ver, &verLength, NULL, 0) == 0 && atoi(ver) >= 22;
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
#elif (defined(__linux__) || defined(__APPLE__)) && (defined(CODEGEN_TARGET_X64) || defined(CODEGEN_TARGET_A64))
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -284,7 +284,6 @@ bool initHeaderFunctions(BaseCodeGenContext& codeGenContext)
|
||||||
codeStart
|
codeStart
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(!"Failed to create entry function");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// 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/BytecodeAnalysis.h"
|
#include "Luau/BytecodeAnalysis.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
|
||||||
#include "Luau/BytecodeSummary.h"
|
#include "Luau/BytecodeSummary.h"
|
||||||
#include "Luau/IrDump.h"
|
#include "Luau/IrDump.h"
|
||||||
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
#include "CodeGenLower.h"
|
#include "CodeGenLower.h"
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ unsigned getInstructionCount(const Instruction* insns, const unsigned size)
|
||||||
for (unsigned i = 0; i < size;)
|
for (unsigned i = 0; i < size;)
|
||||||
{
|
{
|
||||||
++count;
|
++count;
|
||||||
i += Luau::getOpLength(LuauOpcode(LUAU_INSN_OP(insns[i])));
|
i += getOpLength(LuauOpcode(LUAU_INSN_OP(insns[i])));
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,6 @@ bool initHeaderFunctions(BaseCodeGenContext& codeGenContext)
|
||||||
codeStart
|
codeStart
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(!"Failed to create entry function");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/Bytecode.h"
|
#include "Luau/Bytecode.h"
|
||||||
#include "Luau/BytecodeAnalysis.h"
|
#include "Luau/BytecodeAnalysis.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
|
@ -177,7 +176,7 @@ void IrBuilder::buildFunctionIr(Proto* proto)
|
||||||
|
|
||||||
// Numeric for loops require additional processing to maintain loop stack
|
// Numeric for loops require additional processing to maintain loop stack
|
||||||
// Notably, this must be performed even when the block is dead so that we maintain the pairing FORNPREP-FORNLOOP
|
// Notably, this must be performed even when the block is dead so that we maintain the pairing FORNPREP-FORNLOOP
|
||||||
if (op == LOP_FORNPREP)
|
if (int(op) == LOP_FORNPREP)
|
||||||
beforeInstForNPrep(*this, pc, i);
|
beforeInstForNPrep(*this, pc, i);
|
||||||
|
|
||||||
// We skip dead bytecode instructions when they appear after block was already terminated
|
// We skip dead bytecode instructions when they appear after block was already terminated
|
||||||
|
@ -199,7 +198,7 @@ void IrBuilder::buildFunctionIr(Proto* proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See above for FORNPREP..FORNLOOP processing
|
// See above for FORNPREP..FORNLOOP processing
|
||||||
if (op == LOP_FORNLOOP)
|
if (int(op) == LOP_FORNLOOP)
|
||||||
afterInstForNLoop(*this, pc);
|
afterInstForNLoop(*this, pc);
|
||||||
|
|
||||||
i = nexti;
|
i = nexti;
|
||||||
|
@ -255,7 +254,7 @@ void IrBuilder::rebuildBytecodeBasicBlocks(Proto* proto)
|
||||||
|
|
||||||
void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
|
void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (int(op))
|
||||||
{
|
{
|
||||||
case LOP_NOP:
|
case LOP_NOP:
|
||||||
break;
|
break;
|
||||||
|
@ -478,7 +477,7 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
|
||||||
int ra = LUAU_INSN_A(*pc);
|
int ra = LUAU_INSN_A(*pc);
|
||||||
|
|
||||||
IrOp loopRepeat = blockAtInst(i + 1 + LUAU_INSN_D(*pc));
|
IrOp loopRepeat = blockAtInst(i + 1 + LUAU_INSN_D(*pc));
|
||||||
IrOp loopExit = blockAtInst(i + getOpLength(LOP_FORGLOOP));
|
IrOp loopExit = blockAtInst(i + getOpLength(LuauOpcode(LOP_FORGLOOP)));
|
||||||
IrOp fallback = block(IrBlockKind::Fallback);
|
IrOp fallback = block(IrBlockKind::Fallback);
|
||||||
|
|
||||||
inst(IrCmd::INTERRUPT, constUint(i));
|
inst(IrCmd::INTERRUPT, constUint(i));
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -182,7 +180,6 @@ const char* getCmdName(IrCmd cmd)
|
||||||
case IrCmd::UNM_VEC:
|
case IrCmd::UNM_VEC:
|
||||||
return "UNM_VEC";
|
return "UNM_VEC";
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
return "DOT_VEC";
|
return "DOT_VEC";
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
return "NOT_ANY";
|
return "NOT_ANY";
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -753,8 +751,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
}
|
}
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
inst.regA64 = regs.allocReg(KindA64::d, index);
|
inst.regA64 = regs.allocReg(KindA64::d, index);
|
||||||
|
|
||||||
RegisterA64 temp = regs.allocTemp(KindA64::q);
|
RegisterA64 temp = regs.allocTemp(KindA64::q);
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -706,8 +704,6 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
}
|
}
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b});
|
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b});
|
||||||
|
|
||||||
ScopedRegX64 tmp1{regs};
|
ScopedRegX64 tmp1{regs};
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
static const int kMinMaxUnrolledParams = 5;
|
static const int kMinMaxUnrolledParams = 5;
|
||||||
static const int kBit32BinaryOpUnrolledParams = 5;
|
static const int kBit32BinaryOpUnrolledParams = 5;
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -939,26 +937,9 @@ static BuiltinImplResult translateBuiltinVectorMagnitude(
|
||||||
|
|
||||||
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
||||||
|
|
||||||
IrOp sum;
|
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
||||||
|
|
||||||
if (FFlag::LuauVectorLibNativeDot)
|
IrOp sum = build.inst(IrCmd::DOT_VEC, a, a);
|
||||||
{
|
|
||||||
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
|
||||||
|
|
||||||
sum = build.inst(IrCmd::DOT_VEC, a, a);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
|
|
||||||
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
|
|
||||||
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
|
|
||||||
|
|
||||||
IrOp x2 = build.inst(IrCmd::MUL_NUM, x, x);
|
|
||||||
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
||||||
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
|
|
||||||
|
|
||||||
sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
|
|
||||||
}
|
|
||||||
|
|
||||||
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
||||||
|
|
||||||
|
@ -986,43 +967,18 @@ static BuiltinImplResult translateBuiltinVectorNormalize(
|
||||||
|
|
||||||
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
||||||
|
|
||||||
if (FFlag::LuauVectorLibNativeDot)
|
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
||||||
{
|
IrOp sum = build.inst(IrCmd::DOT_VEC, a, a);
|
||||||
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
|
||||||
IrOp sum = build.inst(IrCmd::DOT_VEC, a, a);
|
|
||||||
|
|
||||||
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
||||||
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
|
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
|
||||||
IrOp invvec = build.inst(IrCmd::NUM_TO_VEC, inv);
|
IrOp invvec = build.inst(IrCmd::NUM_TO_VEC, inv);
|
||||||
|
|
||||||
IrOp result = build.inst(IrCmd::MUL_VEC, a, invvec);
|
IrOp result = build.inst(IrCmd::MUL_VEC, a, invvec);
|
||||||
|
|
||||||
result = build.inst(IrCmd::TAG_VECTOR, result);
|
result = build.inst(IrCmd::TAG_VECTOR, result);
|
||||||
|
|
||||||
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), result);
|
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), result);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
|
|
||||||
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
|
|
||||||
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
|
|
||||||
|
|
||||||
IrOp x2 = build.inst(IrCmd::MUL_NUM, x, x);
|
|
||||||
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
||||||
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
|
|
||||||
|
|
||||||
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
|
|
||||||
|
|
||||||
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
|
||||||
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
|
|
||||||
|
|
||||||
IrOp xr = build.inst(IrCmd::MUL_NUM, x, inv);
|
|
||||||
IrOp yr = build.inst(IrCmd::MUL_NUM, y, inv);
|
|
||||||
IrOp zr = build.inst(IrCmd::MUL_NUM, z, inv);
|
|
||||||
|
|
||||||
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
|
|
||||||
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {BuiltinImplType::Full, 1};
|
return {BuiltinImplType::Full, 1};
|
||||||
}
|
}
|
||||||
|
@ -1074,31 +1030,10 @@ static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams
|
||||||
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
|
||||||
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
|
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
|
||||||
|
|
||||||
IrOp sum;
|
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
||||||
|
IrOp b = build.inst(IrCmd::LOAD_TVALUE, args, build.constInt(0));
|
||||||
|
|
||||||
if (FFlag::LuauVectorLibNativeDot)
|
IrOp sum = build.inst(IrCmd::DOT_VEC, a, b);
|
||||||
{
|
|
||||||
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
|
|
||||||
IrOp b = build.inst(IrCmd::LOAD_TVALUE, args, build.constInt(0));
|
|
||||||
|
|
||||||
sum = build.inst(IrCmd::DOT_VEC, a, b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
|
|
||||||
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
|
|
||||||
IrOp xx = build.inst(IrCmd::MUL_NUM, x1, x2);
|
|
||||||
|
|
||||||
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
|
|
||||||
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
|
|
||||||
IrOp yy = build.inst(IrCmd::MUL_NUM, y1, y2);
|
|
||||||
|
|
||||||
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
|
|
||||||
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
|
|
||||||
IrOp zz = build.inst(IrCmd::MUL_NUM, z1, z2);
|
|
||||||
|
|
||||||
sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, xx, yy), zz);
|
|
||||||
}
|
|
||||||
|
|
||||||
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), sum);
|
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), sum);
|
||||||
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
|
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "IrTranslation.h"
|
#include "IrTranslation.h"
|
||||||
|
|
||||||
#include "Luau/Bytecode.h"
|
#include "Luau/Bytecode.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
|
||||||
#include "Luau/CodeGenOptions.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/IrBuilder.h"
|
#include "Luau/IrBuilder.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
@ -1502,7 +1501,7 @@ bool translateInstNamecall(IrBuilder& build, const Instruction* pc, int pcpos)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrOp next = build.blockAtInst(pcpos + getOpLength(LOP_NAMECALL));
|
IrOp next = build.blockAtInst(pcpos + getOpLength(LuauOpcode(LOP_NAMECALL)));
|
||||||
IrOp fallback = build.block(IrBlockKind::Fallback);
|
IrOp fallback = build.block(IrBlockKind::Fallback);
|
||||||
IrOp firstFastPathSuccess = build.block(IrBlockKind::Internal);
|
IrOp firstFastPathSuccess = build.block(IrBlockKind::Internal);
|
||||||
IrOp secondFastPath = build.block(IrBlockKind::Internal);
|
IrOp secondFastPath = build.block(IrBlockKind::Internal);
|
||||||
|
|
|
@ -16,13 +16,120 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int getOpLength(LuauOpcode op)
|
||||||
|
{
|
||||||
|
switch (int(op))
|
||||||
|
{
|
||||||
|
case LOP_GETGLOBAL:
|
||||||
|
case LOP_SETGLOBAL:
|
||||||
|
case LOP_GETIMPORT:
|
||||||
|
case LOP_GETTABLEKS:
|
||||||
|
case LOP_SETTABLEKS:
|
||||||
|
case LOP_NAMECALL:
|
||||||
|
case LOP_JUMPIFEQ:
|
||||||
|
case LOP_JUMPIFLE:
|
||||||
|
case LOP_JUMPIFLT:
|
||||||
|
case LOP_JUMPIFNOTEQ:
|
||||||
|
case LOP_JUMPIFNOTLE:
|
||||||
|
case LOP_JUMPIFNOTLT:
|
||||||
|
case LOP_NEWTABLE:
|
||||||
|
case LOP_SETLIST:
|
||||||
|
case LOP_FORGLOOP:
|
||||||
|
case LOP_LOADKX:
|
||||||
|
case LOP_FASTCALL2:
|
||||||
|
case LOP_FASTCALL2K:
|
||||||
|
case LOP_FASTCALL3:
|
||||||
|
case LOP_JUMPXEQKNIL:
|
||||||
|
case LOP_JUMPXEQKB:
|
||||||
|
case LOP_JUMPXEQKN:
|
||||||
|
case LOP_JUMPXEQKS:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isJumpD(LuauOpcode op)
|
||||||
|
{
|
||||||
|
switch (int(op))
|
||||||
|
{
|
||||||
|
case LOP_JUMP:
|
||||||
|
case LOP_JUMPIF:
|
||||||
|
case LOP_JUMPIFNOT:
|
||||||
|
case LOP_JUMPIFEQ:
|
||||||
|
case LOP_JUMPIFLE:
|
||||||
|
case LOP_JUMPIFLT:
|
||||||
|
case LOP_JUMPIFNOTEQ:
|
||||||
|
case LOP_JUMPIFNOTLE:
|
||||||
|
case LOP_JUMPIFNOTLT:
|
||||||
|
case LOP_FORNPREP:
|
||||||
|
case LOP_FORNLOOP:
|
||||||
|
case LOP_FORGPREP:
|
||||||
|
case LOP_FORGLOOP:
|
||||||
|
case LOP_FORGPREP_INEXT:
|
||||||
|
case LOP_FORGPREP_NEXT:
|
||||||
|
case LOP_JUMPBACK:
|
||||||
|
case LOP_JUMPXEQKNIL:
|
||||||
|
case LOP_JUMPXEQKB:
|
||||||
|
case LOP_JUMPXEQKN:
|
||||||
|
case LOP_JUMPXEQKS:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSkipC(LuauOpcode op)
|
||||||
|
{
|
||||||
|
switch (int(op))
|
||||||
|
{
|
||||||
|
case LOP_LOADB:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFastCall(LuauOpcode op)
|
||||||
|
{
|
||||||
|
switch (int(op))
|
||||||
|
{
|
||||||
|
case LOP_FASTCALL:
|
||||||
|
case LOP_FASTCALL1:
|
||||||
|
case LOP_FASTCALL2:
|
||||||
|
case LOP_FASTCALL2K:
|
||||||
|
case LOP_FASTCALL3:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getJumpTarget(uint32_t insn, uint32_t pc)
|
||||||
|
{
|
||||||
|
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
||||||
|
|
||||||
|
if (isJumpD(op))
|
||||||
|
return int(pc + LUAU_INSN_D(insn) + 1);
|
||||||
|
else if (isFastCall(op))
|
||||||
|
return int(pc + LUAU_INSN_C(insn) + 2);
|
||||||
|
else if (isSkipC(op) && LUAU_INSN_C(insn))
|
||||||
|
return int(pc + LUAU_INSN_C(insn) + 1);
|
||||||
|
else if (int(op) == LOP_JUMPX)
|
||||||
|
return int(pc + LUAU_INSN_E(insn) + 1);
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
IrValueKind getCmdValueKind(IrCmd cmd)
|
IrValueKind getCmdValueKind(IrCmd cmd)
|
||||||
{
|
{
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
|
@ -83,7 +190,6 @@ IrValueKind getCmdValueKind(IrCmd cmd)
|
||||||
case IrCmd::UNM_VEC:
|
case IrCmd::UNM_VEC:
|
||||||
return IrValueKind::Tvalue;
|
return IrValueKind::Tvalue;
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
return IrValueKind::Double;
|
return IrValueKind::Double;
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
|
|
|
@ -21,7 +21,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1475,9 +1474,6 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
case IrCmd::MUL_VEC:
|
case IrCmd::MUL_VEC:
|
||||||
case IrCmd::DIV_VEC:
|
case IrCmd::DIV_VEC:
|
||||||
case IrCmd::DOT_VEC:
|
case IrCmd::DOT_VEC:
|
||||||
if (inst.cmd == IrCmd::DOT_VEC)
|
|
||||||
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
if (IrInst* a = function.asInstOp(inst.a); a && a->cmd == IrCmd::TAG_VECTOR)
|
if (IrInst* a = function.asInstOp(inst.a); a && a->cmd == IrCmd::TAG_VECTOR)
|
||||||
replace(function, inst.a, a->a);
|
replace(function, inst.a, a->a);
|
||||||
|
|
||||||
|
|
|
@ -113,5 +113,9 @@ struct luarequire_Configuration
|
||||||
// Populates function pointers in the given luarequire_Configuration.
|
// Populates function pointers in the given luarequire_Configuration.
|
||||||
typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);
|
typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);
|
||||||
|
|
||||||
// Initializes the require library with the given configuration and context.
|
// Initializes and pushes the require closure onto the stack without
|
||||||
|
// registration.
|
||||||
|
LUALIB_API int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||||
|
|
||||||
|
// Initializes the require library and registers it globally.
|
||||||
LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||||
|
|
|
@ -35,7 +35,7 @@ static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: load");
|
luaL_error(L, "require configuration is missing required function pointer: load");
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||||
{
|
{
|
||||||
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
||||||
if (!config)
|
if (!config)
|
||||||
|
@ -48,5 +48,11 @@ void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, vo
|
||||||
|
|
||||||
// "require" captures config and ctx as upvalues
|
// "require" captures config and ctx as upvalues
|
||||||
lua_pushcclosure(L, Luau::Require::lua_require, "require", 2);
|
lua_pushcclosure(L, Luau::Require::lua_require, "require", 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||||
|
{
|
||||||
|
lua_pushrequire(L, config_init, ctx);
|
||||||
lua_setglobal(L, "require");
|
lua_setglobal(L, "require");
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,14 +72,12 @@ enum lua_Type
|
||||||
LUA_TNIL = 0, // must be 0 due to lua_isnoneornil
|
LUA_TNIL = 0, // must be 0 due to lua_isnoneornil
|
||||||
LUA_TBOOLEAN = 1, // must be 1 due to l_isfalse
|
LUA_TBOOLEAN = 1, // must be 1 due to l_isfalse
|
||||||
|
|
||||||
|
|
||||||
LUA_TLIGHTUSERDATA,
|
LUA_TLIGHTUSERDATA,
|
||||||
LUA_TNUMBER,
|
LUA_TNUMBER,
|
||||||
LUA_TVECTOR,
|
LUA_TVECTOR,
|
||||||
|
|
||||||
LUA_TSTRING, // all types above this must be value types, all types below this must be GC types - see iscollectable
|
LUA_TSTRING, // all types above this must be value types, all types below this must be GC types - see iscollectable
|
||||||
|
|
||||||
|
|
||||||
LUA_TTABLE,
|
LUA_TTABLE,
|
||||||
LUA_TFUNCTION,
|
LUA_TFUNCTION,
|
||||||
LUA_TUSERDATA,
|
LUA_TUSERDATA,
|
||||||
|
|
|
@ -356,6 +356,7 @@ static void resume(lua_State* L, void* ud)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// resume from previous yield or break
|
// resume from previous yield or break
|
||||||
|
LUAU_ASSERT(firstArg >= L->base);
|
||||||
LUAU_ASSERT(L->status == LUA_YIELD || L->status == LUA_BREAK);
|
LUAU_ASSERT(L->status == LUA_YIELD || L->status == LUA_BREAK);
|
||||||
L->status = 0;
|
L->status = 0;
|
||||||
|
|
||||||
|
@ -469,6 +470,8 @@ static void resume_finish(lua_State* L, int status)
|
||||||
|
|
||||||
int lua_resume(lua_State* L, lua_State* from, int nargs)
|
int lua_resume(lua_State* L, lua_State* from, int nargs)
|
||||||
{
|
{
|
||||||
|
api_check(L, L->top - L->base >= nargs);
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
||||||
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
|
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
|
||||||
|
@ -498,6 +501,8 @@ int lua_resume(lua_State* L, lua_State* from, int nargs)
|
||||||
|
|
||||||
int lua_resumeerror(lua_State* L, lua_State* from)
|
int lua_resumeerror(lua_State* L, lua_State* from)
|
||||||
{
|
{
|
||||||
|
api_check(L, L->top - L->base >= 1);
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
||||||
return resume_error(L, "cannot resume non-suspended coroutine", 1);
|
return resume_error(L, "cannot resume non-suspended coroutine", 1);
|
||||||
|
|
|
@ -272,7 +272,7 @@ typedef struct Udata
|
||||||
};
|
};
|
||||||
} Udata;
|
} Udata;
|
||||||
|
|
||||||
typedef struct Buffer
|
typedef struct LuauBuffer
|
||||||
{
|
{
|
||||||
CommonHeader;
|
CommonHeader;
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ union GCObject
|
||||||
struct Proto p;
|
struct Proto p;
|
||||||
struct UpVal uv;
|
struct UpVal uv;
|
||||||
struct lua_State th; // thread
|
struct lua_State th; // thread
|
||||||
struct Buffer buf;
|
struct LuauBuffer buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
// macros to convert a GCObject into a specific value
|
// macros to convert a GCObject into a specific value
|
||||||
|
|
|
@ -16,14 +16,12 @@ const char* const luaT_typenames[] = {
|
||||||
"nil",
|
"nil",
|
||||||
"boolean",
|
"boolean",
|
||||||
|
|
||||||
|
|
||||||
"userdata",
|
"userdata",
|
||||||
"number",
|
"number",
|
||||||
"vector",
|
"vector",
|
||||||
|
|
||||||
"string",
|
"string",
|
||||||
|
|
||||||
|
|
||||||
"table",
|
"table",
|
||||||
"function",
|
"function",
|
||||||
"userdata",
|
"userdata",
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
using namespace Luau::CodeGen;
|
using namespace Luau::CodeGen;
|
||||||
using namespace Luau::CodeGen::A64;
|
using namespace Luau::CodeGen::A64;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
|
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
|
||||||
{
|
{
|
||||||
std::string result = "{";
|
std::string result = "{";
|
||||||
|
@ -389,8 +387,6 @@ TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPBasic")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPMath")
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPMath")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true};
|
|
||||||
|
|
||||||
SINGLE_COMPARE(fabs(d1, d2), 0x1E60C041);
|
SINGLE_COMPARE(fabs(d1, d2), 0x1E60C041);
|
||||||
SINGLE_COMPARE(fadd(d1, d2, d3), 0x1E632841);
|
SINGLE_COMPARE(fadd(d1, d2, d3), 0x1E632841);
|
||||||
SINGLE_COMPARE(fadd(s29, s29, s28), 0x1E3C2BBD);
|
SINGLE_COMPARE(fadd(s29, s29, s28), 0x1E3C2BBD);
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
using namespace Luau::CodeGen;
|
using namespace Luau::CodeGen;
|
||||||
using namespace Luau::CodeGen::X64;
|
using namespace Luau::CodeGen::X64;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
|
||||||
|
|
||||||
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
|
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
|
||||||
{
|
{
|
||||||
std::string result = "{";
|
std::string result = "{";
|
||||||
|
@ -571,8 +569,6 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXConversionInstructionForms")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXTernaryInstructionForms")
|
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXTernaryInstructionForms")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true};
|
|
||||||
|
|
||||||
SINGLE_COMPARE(vroundsd(xmm7, xmm12, xmm3, RoundingModeX64::RoundToNegativeInfinity), 0xc4, 0xe3, 0x19, 0x0b, 0xfb, 0x09);
|
SINGLE_COMPARE(vroundsd(xmm7, xmm12, xmm3, RoundingModeX64::RoundToNegativeInfinity), 0xc4, 0xe3, 0x19, 0x0b, 0xfb, 0x09);
|
||||||
SINGLE_COMPARE(
|
SINGLE_COMPARE(
|
||||||
vroundsd(xmm8, xmm13, xmmword[r13 + rdx], RoundingModeX64::RoundToPositiveInfinity), 0xc4, 0x43, 0x11, 0x0b, 0x44, 0x15, 0x00, 0x0a
|
vroundsd(xmm8, xmm13, xmmword[r13 + rdx], RoundingModeX64::RoundToPositiveInfinity), 0xc4, 0x43, 0x11, 0x0b, 0x44, 0x15, 0x00, 0x0a
|
||||||
|
|
|
@ -34,7 +34,6 @@ void luaC_validate(lua_State* L);
|
||||||
LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
|
@ -881,8 +880,6 @@ TEST_CASE("Vector")
|
||||||
|
|
||||||
TEST_CASE("VectorLibrary")
|
TEST_CASE("VectorLibrary")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
|
||||||
|
|
||||||
lua_CompileOptions copts = defaultOptions();
|
lua_CompileOptions copts = defaultOptions();
|
||||||
|
|
||||||
SUBCASE("O0")
|
SUBCASE("O0")
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
@ -40,12 +39,12 @@ LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
||||||
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
|
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
|
||||||
LUAU_FASTFLAG(LuauCloneReturnTypePack)
|
|
||||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
|
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAG(LuauBetterScopeSelection)
|
LUAU_FASTFLAG(LuauBetterScopeSelection)
|
||||||
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||||
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
||||||
{
|
{
|
||||||
|
@ -83,12 +82,12 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
||||||
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
|
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
|
||||||
ScopedFastFlag luauCloneTypeAliasBindings{FFlag::LuauCloneTypeAliasBindings, true};
|
ScopedFastFlag luauCloneTypeAliasBindings{FFlag::LuauCloneTypeAliasBindings, true};
|
||||||
ScopedFastFlag luauDoNotClonePersistentBindings{FFlag::LuauDoNotClonePersistentBindings, true};
|
ScopedFastFlag luauDoNotClonePersistentBindings{FFlag::LuauDoNotClonePersistentBindings, true};
|
||||||
ScopedFastFlag luauCloneReturnTypePack{FFlag::LuauCloneReturnTypePack, true};
|
|
||||||
ScopedFastFlag luauIncrementalAutocompleteDemandBasedCloning{FFlag::LuauIncrementalAutocompleteDemandBasedCloning, true};
|
ScopedFastFlag luauIncrementalAutocompleteDemandBasedCloning{FFlag::LuauIncrementalAutocompleteDemandBasedCloning, true};
|
||||||
ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true};
|
ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true};
|
||||||
ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true};
|
ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true};
|
||||||
ScopedFastFlag luauAutocompleteUsesModuleForTypeCompatibility{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
|
ScopedFastFlag luauAutocompleteUsesModuleForTypeCompatibility{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
|
||||||
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
|
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
|
||||||
|
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
|
||||||
|
|
||||||
FragmentAutocompleteFixtureImpl()
|
FragmentAutocompleteFixtureImpl()
|
||||||
: BaseType(true)
|
: BaseType(true)
|
||||||
|
@ -3598,6 +3597,30 @@ end
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "NotNull_assertion_caused_by_leaking_free_type_from_stale_module")
|
||||||
|
{
|
||||||
|
const std::string source = R"(
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
Players.PlayerAdded:Connect(function(Player)
|
||||||
|
for_,v in script.PlayerValue:GetChildren()do
|
||||||
|
v
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
)";
|
||||||
|
|
||||||
|
const std::string dest = R"(
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
Players.PlayerAdded:Connect(function(Player)
|
||||||
|
for_,v in script.PlayerValue:GetChildren()do
|
||||||
|
v:L
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
)";
|
||||||
|
autocompleteFragmentInBothSolvers(source, dest, Position{5, 11}, [](auto& result) {});
|
||||||
|
}
|
||||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -16,8 +16,8 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauSelectivelyRetainDFGArena)
|
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -1015,8 +1015,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments")
|
||||||
LUAU_REQUIRE_NO_ERRORS(resultA);
|
LUAU_REQUIRE_NO_ERRORS(resultA);
|
||||||
|
|
||||||
CheckResult resultB = frontend.check("B");
|
CheckResult resultB = frontend.check("B");
|
||||||
// In the new non-strict mode, we do not currently support error reporting for unknown symbols in type positions.
|
if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes)
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(resultB);
|
LUAU_REQUIRE_NO_ERRORS(resultB);
|
||||||
else
|
else
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, resultB);
|
LUAU_REQUIRE_ERROR_COUNT(1, resultB);
|
||||||
|
@ -1570,7 +1569,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_correct_ast_root")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FrontendFixture, "dfg_data_cleared_on_retain_type_graphs_unset")
|
TEST_CASE_FIXTURE(FrontendFixture, "dfg_data_cleared_on_retain_type_graphs_unset")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauSelectivelyRetainDFGArena, true}};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
fileResolver.source["game/A"] = R"(
|
fileResolver.source["game/A"] = R"(
|
||||||
local a = 1
|
local a = 1
|
||||||
local b = 2
|
local b = 2
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
@ -227,7 +227,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::LuauNonReentrantGeneralization, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, true};
|
||||||
|
|
||||||
TableType tt;
|
TableType tt;
|
||||||
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
||||||
|
@ -261,7 +261,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, 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(LuauNonReentrantGeneralization);
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2);
|
||||||
|
|
||||||
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::LuauNonReentrantGeneralization, true};
|
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization2, true};
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAG(LuauNonStrictFuncDefErrorFix)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
||||||
LUAU_FASTFLAG(LuauNormalizedBufferIsNotUnknown)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -363,7 +362,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors_2")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauNonStrictFuncDefErrorFix{FFlag::LuauNonStrictFuncDefErrorFix, true};
|
|
||||||
ScopedFastFlag luauNonStrictVisitorImprovements{FFlag::LuauNonStrictVisitorImprovements, true};
|
ScopedFastFlag luauNonStrictVisitorImprovements{FFlag::LuauNonStrictVisitorImprovements, true};
|
||||||
|
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
|
@ -668,10 +666,38 @@ TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict")
|
||||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes, true};
|
||||||
|
|
||||||
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
|
--!nonstrict
|
||||||
|
local foo: Foo = 1
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
|
||||||
|
CHECK_EQ(err->name, "Foo");
|
||||||
|
CHECK_EQ(err->context, UnknownSymbol::Context::Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes, true};
|
||||||
|
|
||||||
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
|
--!nonstrict
|
||||||
|
local foo = 1 :: Foo
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
|
||||||
|
CHECK_EQ(err->name, "Foo");
|
||||||
|
CHECK_EQ(err->context, UnknownSymbol::Context::Type);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_not_unknown")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_not_unknown")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauNormalizedBufferIsNotUnknown{FFlag::LuauNormalizedBufferIsNotUnknown, true};
|
|
||||||
|
|
||||||
CheckResult result = check(Mode::Nonstrict, R"(
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
local function wrap(b: buffer, i: number, v: number)
|
local function wrap(b: buffer, i: number, v: number)
|
||||||
buffer.writeu32(b, i * 4, v)
|
buffer.writeu32(b, i * 4, v)
|
||||||
|
|
|
@ -10,14 +10,10 @@
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNormalizeNegatedErrorToAnError)
|
|
||||||
LUAU_FASTFLAG(LuauNormalizeIntersectErrorToAnError)
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
||||||
LUAU_FASTFLAG(LuauNormalizeLimitFunctionSet)
|
|
||||||
LUAU_FASTFLAG(LuauSubtypingStopAtNormFail)
|
|
||||||
LUAU_FASTFLAG(LuauNormalizationCatchMetatableCycles)
|
LUAU_FASTFLAG(LuauNormalizationCatchMetatableCycles)
|
||||||
LUAU_FASTFLAG(LuauSubtypingEnableReasoningLimit)
|
LUAU_FASTFLAG(LuauSubtypingEnableReasoningLimit)
|
||||||
LUAU_FASTFLAG(LuauTypePackDetectCycles)
|
LUAU_FASTFLAG(LuauTypePackDetectCycles)
|
||||||
|
@ -605,8 +601,6 @@ TEST_CASE_FIXTURE(NormalizeFixture, "intersect_truthy_expressed_as_intersection"
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_error")
|
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauNormalizeIntersectErrorToAnError{FFlag::LuauNormalizeIntersectErrorToAnError, true};
|
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> norm = toNormalizedType(R"(string & AAA)", 1);
|
std::shared_ptr<const NormalizedType> norm = toNormalizedType(R"(string & AAA)", 1);
|
||||||
REQUIRE(norm);
|
REQUIRE(norm);
|
||||||
CHECK("*error-type*" == toString(normalizer.typeFromNormal(*norm)));
|
CHECK("*error-type*" == toString(normalizer.typeFromNormal(*norm)));
|
||||||
|
@ -614,9 +608,6 @@ TEST_CASE_FIXTURE(NormalizeFixture, "intersect_error")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_not_error")
|
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_not_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauNormalizeIntersectErrorToAnError{FFlag::LuauNormalizeIntersectErrorToAnError, true};
|
|
||||||
ScopedFastFlag luauNormalizeNegatedErrorToAnError{FFlag::LuauNormalizeNegatedErrorToAnError, true};
|
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> norm = toNormalizedType(R"(string & Not<)", 1);
|
std::shared_ptr<const NormalizedType> norm = toNormalizedType(R"(string & Not<)", 1);
|
||||||
REQUIRE(norm);
|
REQUIRE(norm);
|
||||||
CHECK("*error-type*" == toString(normalizer.typeFromNormal(*norm)));
|
CHECK("*error-type*" == toString(normalizer.typeFromNormal(*norm)));
|
||||||
|
@ -1199,8 +1190,6 @@ 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 luauNormalizeLimitFunctionSet{FFlag::LuauNormalizeLimitFunctionSet, true};
|
|
||||||
ScopedFastFlag luauSubtypingStopAtNormFail{FFlag::LuauSubtypingStopAtNormFail, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function _(_).readu32(l0)
|
function _(_).readu32(l0)
|
||||||
|
@ -1217,8 +1206,6 @@ 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 luauNormalizeLimitFunctionSet{FFlag::LuauNormalizeLimitFunctionSet, true};
|
|
||||||
ScopedFastFlag luauSubtypingStopAtNormFail{FFlag::LuauSubtypingStopAtNormFail, true};
|
|
||||||
ScopedFastFlag luauSubtypingEnableReasoningLimit{FFlag::LuauSubtypingEnableReasoningLimit, true};
|
ScopedFastFlag luauSubtypingEnableReasoningLimit{FFlag::LuauSubtypingEnableReasoningLimit, true};
|
||||||
ScopedFastFlag luauTurnOffNonreentrantGeneralization{FFlag::LuauNonReentrantGeneralization, false};
|
ScopedFastFlag luauTurnOffNonreentrantGeneralization{FFlag::LuauNonReentrantGeneralization, false};
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,13 @@ LUAU_FASTINT(LuauRecursionLimit)
|
||||||
LUAU_FASTINT(LuauTypeLengthLimit)
|
LUAU_FASTINT(LuauTypeLengthLimit)
|
||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
|
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
|
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||||
LUAU_FASTFLAG(LuauParseStringIndexer)
|
LUAU_FASTFLAG(LuauParseStringIndexer)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||||
|
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||||
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
|
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
|
||||||
|
|
||||||
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
||||||
|
@ -2579,6 +2578,28 @@ TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes")
|
||||||
CHECK_EQ(anonymousFunction->location, Location({9, 18}, {10, 11}));
|
CHECK_EQ(anonymousFunction->location, Location({9, 18}, {10, 11}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||||
|
|
||||||
|
ParseOptions parseOptions;
|
||||||
|
parseOptions.storeCstData = true;
|
||||||
|
|
||||||
|
ParseResult result = parseEx(R"(
|
||||||
|
for value in tbl do
|
||||||
|
end
|
||||||
|
)", parseOptions);
|
||||||
|
REQUIRE(result.root);
|
||||||
|
REQUIRE_EQ(1, result.root->body.size);
|
||||||
|
|
||||||
|
auto forLoop = result.root->body.data[0]->as<AstStatForIn>();
|
||||||
|
auto baseCstNode = result.cstNodeMap.find(forLoop);
|
||||||
|
REQUIRE(baseCstNode);
|
||||||
|
|
||||||
|
auto cstNode = (*baseCstNode)->as<CstStatForIn>();
|
||||||
|
CHECK_EQ(cstNode->varsCommaPositions.size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
||||||
|
@ -3828,7 +3849,6 @@ TEST_CASE_FIXTURE(Fixture, "mixed_leading_intersection_and_union_not_allowed")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAllowComplexTypesInGenericParams, true};
|
|
||||||
const auto root = parse(R"(
|
const auto root = parse(R"(
|
||||||
type X<T> = T
|
type X<T> = T
|
||||||
local x: X<(() -> ())?>
|
local x: X<(() -> ())?>
|
||||||
|
@ -3865,7 +3885,6 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAllowComplexTypesInGenericParams, true};
|
|
||||||
const auto root = parse(R"(
|
const auto root = parse(R"(
|
||||||
type X<T> = T
|
type X<T> = T
|
||||||
local x: X<
|
local x: X<
|
||||||
|
@ -3902,7 +3921,6 @@ TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
|
TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauErrorRecoveryForTableTypes, true};
|
|
||||||
ParseOptions opts;
|
ParseOptions opts;
|
||||||
opts.allowDeclarationSyntax = true;
|
opts.allowDeclarationSyntax = true;
|
||||||
const auto result = tryParse(
|
const auto result = tryParse(
|
||||||
|
|
|
@ -544,7 +544,9 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireFromLuauBinary")
|
||||||
char executable[] = "luau";
|
char executable[] = "luau";
|
||||||
std::vector<std::string> paths = {
|
std::vector<std::string> paths = {
|
||||||
getLuauDirectory(PathType::Relative) + "/tests/require/without_config/dependency.luau",
|
getLuauDirectory(PathType::Relative) + "/tests/require/without_config/dependency.luau",
|
||||||
getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/dependency.luau"
|
getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/dependency.luau",
|
||||||
|
getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module.luau",
|
||||||
|
getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/module.luau",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const std::string& path : paths)
|
for (const std::string& path : paths)
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNormalizedBufferIsNotUnknown)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -967,14 +966,7 @@ TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->booleanType)
|
||||||
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->numberType));
|
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->numberType));
|
||||||
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->stringType));
|
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->stringType));
|
||||||
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->threadType));
|
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->threadType));
|
||||||
|
TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->bufferType));
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "unknown <!: ~buffer")
|
|
||||||
{
|
|
||||||
// TODO: replace with TEST_IS_NOT_SUBTYPE on flag removal
|
|
||||||
ScopedFastFlag luauNormalizedBufferIsNotUnknown{FFlag::LuauNormalizedBufferIsNotUnknown, true};
|
|
||||||
|
|
||||||
CHECK_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->bufferType));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "Root <: class")
|
TEST_CASE_FIXTURE(SubtypeFixture, "Root <: class")
|
||||||
{
|
{
|
||||||
|
|
|
@ -191,6 +191,13 @@ TEST_CASE("for_in_loop_spaces_around_tokens")
|
||||||
CHECK_EQ(five, transpile(five).code);
|
CHECK_EQ(five, transpile(five).code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("for_in_single_variable")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||||
|
const std::string one = R"( for key in pairs(x) do end )";
|
||||||
|
CHECK_EQ(one, transpile(one).code);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("while_loop")
|
TEST_CASE("while_loop")
|
||||||
{
|
{
|
||||||
const std::string code = R"( while f(x)do print() end )";
|
const std::string code = R"( while f(x)do print() end )";
|
||||||
|
@ -1997,6 +2004,18 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style")
|
||||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||||
|
std::string code = R"(
|
||||||
|
type Foo = {
|
||||||
|
[ "$$typeof1"]: string,
|
||||||
|
['$$typeof2' ]: string,
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("transpile_types_preserve_parentheses_style")
|
TEST_CASE("transpile_types_preserve_parentheses_style")
|
||||||
{
|
{
|
||||||
ScopedFastFlag flags[] = {
|
ScopedFastFlag flags[] = {
|
||||||
|
|
|
@ -13,6 +13,8 @@ LUAU_FASTFLAG(LuauTypeFunReadWriteParents)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||||
|
LUAU_FASTFLAG(LuauNoTypeFunctionsNamedTypeOf)
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
|
@ -462,7 +464,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
|
||||||
-- this should never be returned
|
-- this should never be returned
|
||||||
return types.number
|
return types.number
|
||||||
end
|
end
|
||||||
|
|
||||||
-- forcing an error here to check the exact type of the negation
|
-- forcing an error here to check the exact type of the negation
|
||||||
local function ok(idx: getnegation<>): never return idx end
|
local function ok(idx: getnegation<>): never return idx end
|
||||||
)");
|
)");
|
||||||
|
@ -1113,7 +1115,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global")
|
||||||
|
|
||||||
return arg -- this should not be reached
|
return arg -- this should not be reached
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ok(idx: illegal<number>): nil return idx end
|
local function ok(idx: illegal<number>): nil return idx end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
@ -2204,4 +2206,21 @@ local x: wrap<{a: number}> = { a = 2 }
|
||||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "{ a: number }?");
|
CHECK(toString(requireType("x"), ToStringOptions{true}) == "{ a: number }?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "typeof_is_not_a_valid_type_function_name")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag udtf{FFlag::LuauUserTypeFunTypecheck, true};
|
||||||
|
ScopedFastFlag noTypeOfTypeFunctions{FFlag::LuauNoTypeFunctionsNamedTypeOf, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function typeof(t)
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
|
CHECK("typeof cannot be used as an identifier for a type function or alias" == toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -12,9 +12,9 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauFixInfiniteRecursionInNormalization)
|
LUAU_FASTFLAG(LuauFixInfiniteRecursionInNormalization)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceUpcast)
|
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeAliases");
|
TEST_SUITE_BEGIN("TypeAliases");
|
||||||
|
|
||||||
|
@ -256,10 +256,7 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
|
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag _{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true};
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -1103,7 +1100,14 @@ TEST_CASE_FIXTURE(Fixture, "typeof_is_not_a_valid_alias_name")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK("Type aliases cannot be named typeof" == toString(result.errors[0]));
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
CHECK("typeof cannot be used as an identifier for a type function or alias" == toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK("Type aliases cannot be named typeof" == toString(result.errors[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "fuzzer_bug_doesnt_crash")
|
TEST_CASE_FIXTURE(Fixture, "fuzzer_bug_doesnt_crash")
|
||||||
|
@ -1184,14 +1188,19 @@ TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
CheckResult result = check(R"(
|
||||||
--!nonstrict
|
--!nonstrict
|
||||||
type Map<T, V> = {[ K]: V}
|
type Map<T, V> = {[K]: V}
|
||||||
function foo:bar(): Config<any, any> end
|
function foo:bar(): Config<any, any> end
|
||||||
type Config<TSource, TContext> = Map<TSource, TContext> & { fields: FieldConfigMap<any, any>}
|
type Config<TSource, TContext> = Map<TSource, TContext> & { fields: FieldConfigMap<any, any>}
|
||||||
export type FieldConfig<TSource, TContext, TArgs> = {[ string]: any}
|
export type FieldConfig<TSource, TContext, TArgs> = {[string]: any}
|
||||||
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
|
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
|
||||||
)"));
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
|
LUAU_CHECK_ERROR_COUNT(2, result);
|
||||||
|
else
|
||||||
|
LUAU_CHECK_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "gh1632_no_infinite_recursion_in_normalization")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "gh1632_no_infinite_recursion_in_normalization")
|
||||||
|
|
|
@ -12,6 +12,7 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
|
||||||
|
@ -719,7 +720,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash")
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2 && FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
|
CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[0]));
|
||||||
|
CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[1]));
|
||||||
|
}
|
||||||
|
else if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
// Counterintuitively, the parameter l0 is unconstrained and therefore it is valid to pass nil.
|
// Counterintuitively, the parameter l0 is unconstrained and therefore it is valid to pass nil.
|
||||||
// The new solver therefore considers that parameter to be optional.
|
// The new solver therefore considers that parameter to be optional.
|
||||||
|
|
|
@ -25,6 +25,9 @@ LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauReduceUnionFollowUnionType)
|
LUAU_FASTFLAG(LuauReduceUnionFollowUnionType)
|
||||||
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -2017,6 +2020,11 @@ TEST_CASE_FIXTURE(Fixture, "free_is_not_bound_to_unknown")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_call_site")
|
TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_call_site")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauHasPropProperBlock, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}
|
||||||
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
|
|
||||||
|
@ -2034,13 +2042,20 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_
|
||||||
local f = t.f
|
local f = t.f
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
|
||||||
|
|
||||||
CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
|
CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
CHECK_EQ("({ read p: { read q: unknown } }) -> ~(false?)?", toString(requireType("g")));
|
{
|
||||||
|
// FIXME CLI-143852: Depends on interleaving generalization and type function reduction.
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
|
CHECK_EQ("({ read p: unknown }) -> (*error-type* | ~(false?))?", toString(requireType("g")));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("({+ p: {+ q: nil +} +}) -> nil", toString(requireType("g")));
|
CHECK_EQ("({+ p: {+ q: nil +} +}) -> nil", toString(requireType("g")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "dont_mutate_the_underlying_head_of_typepack_when_calling_with_self")
|
TEST_CASE_FIXTURE(Fixture, "dont_mutate_the_underlying_head_of_typepack_when_calling_with_self")
|
||||||
|
@ -2093,7 +2108,10 @@ u.b().foo()
|
||||||
CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function expects 1 to 3 arguments, but none are specified");
|
CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function expects 1 to 3 arguments, but none are specified");
|
||||||
CHECK_EQ(toString(result.errors[3]), "Argument count mismatch. Function expects 2 to 4 arguments, but none are specified");
|
CHECK_EQ(toString(result.errors[3]), "Argument count mismatch. Function expects 2 to 4 arguments, but none are specified");
|
||||||
CHECK_EQ(toString(result.errors[4]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
CHECK_EQ(toString(result.errors[4]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||||
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified");
|
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 3 arguments, but only 1 is specified");
|
||||||
|
else
|
||||||
|
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified");
|
||||||
CHECK_EQ(toString(result.errors[6]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
CHECK_EQ(toString(result.errors[6]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||||
CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||||
CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||||
|
@ -2257,7 +2275,7 @@ end
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization")
|
TEST_CASE_FIXTURE(Fixture, "dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastInt sfi{FInt::LuauTarjanChildLimit, 1};
|
ScopedFastInt sfi{FInt::LuauTarjanChildLimit, 1};
|
||||||
|
@ -2268,9 +2286,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceede
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR(result, UnificationTooComplex);
|
||||||
|
|
||||||
CHECK_MESSAGE(get<UnificationTooComplex>(result.errors[0]), "Expected UnificationTooComplex but got: " << toString(result.errors[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We had a bug under DCR where instantiated type packs had a nullptr scope.
|
/* We had a bug under DCR where instantiated type packs had a nullptr scope.
|
||||||
|
@ -2986,6 +3002,8 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_ast_stat_fun")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function foo(player)
|
function foo(player)
|
||||||
local success,result = player:thing()
|
local success,result = player:thing()
|
||||||
|
@ -3013,7 +3031,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
||||||
auto tm2 = get<TypePackMismatch>(result.errors[1]);
|
auto tm2 = get<TypePackMismatch>(result.errors[1]);
|
||||||
REQUIRE(tm2);
|
REQUIRE(tm2);
|
||||||
CHECK(toString(tm2->wantedTp) == "string");
|
CHECK(toString(tm2->wantedTp) == "string");
|
||||||
CHECK(toString(tm2->givenTp) == "(buffer | class | function | number | string | table | thread | true) & unknown");
|
CHECK(toString(tm2->givenTp) == "~(false?)");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -144,8 +144,6 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
||||||
local function id<a>(x:a):a return x end
|
local function id<a>(x:a):a return x end
|
||||||
|
@ -260,10 +258,8 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver")
|
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T = { id: <a>(a) -> a }
|
type T = { id: <a>(a) -> a }
|
||||||
local x: T = { id = function<a>(x:a):a return x end }
|
local x: T = { id = function<a>(x:a):a return x end }
|
||||||
|
@ -273,19 +269,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver")
|
|
||||||
{
|
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
|
||||||
type T = { read id: <a>(a) -> a }
|
|
||||||
local x: T = { id = function<a>(x:a):a return x end }
|
|
||||||
local y: string = x.id("hi")
|
|
||||||
local z: number = x.id(37)
|
|
||||||
)");
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
||||||
|
@ -311,8 +294,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||||
{
|
{
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type T = { id: <a>(a) -> a }
|
type T = { id: <a>(a) -> a }
|
||||||
type Factory = { build: () -> T }
|
type Factory = { build: () -> T }
|
||||||
|
@ -330,6 +311,7 @@ TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||||
local y: string = x.id("hi")
|
local y: string = x.id("hi")
|
||||||
local z: number = x.id(37)
|
local z: number = x.id(37)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +491,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
|
||||||
local x: T = t.m(37)
|
local x: T = t.m(37)
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1370,29 +1353,27 @@ TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
||||||
{
|
{
|
||||||
// CLI-114507: temporarily changed to have a cast for `object` to silence false positive error
|
|
||||||
|
|
||||||
// https://github.com/luau-lang/luau/issues/484
|
// https://github.com/luau-lang/luau/issues/484
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type MyObject = {
|
type MyObject = {
|
||||||
getReturnValue: <V>(cb: () -> V) -> V
|
getReturnValue: <V>(cb: () -> V) -> V
|
||||||
}
|
}
|
||||||
local object: MyObject = {
|
local object: MyObject = {
|
||||||
getReturnValue = function<U>(cb: () -> U): U
|
getReturnValue = function<U>(cb: () -> U): U
|
||||||
return cb()
|
return cb()
|
||||||
end,
|
end,
|
||||||
} :: MyObject
|
}
|
||||||
|
|
||||||
type ComplexObject<T> = {
|
type ComplexObject<T> = {
|
||||||
id: T,
|
id: T,
|
||||||
nested: MyObject
|
nested: MyObject
|
||||||
}
|
}
|
||||||
|
|
||||||
local complex: ComplexObject<string> = {
|
local complex: ComplexObject<string> = {
|
||||||
id = "Foo",
|
id = "Foo",
|
||||||
nested = object,
|
nested = object,
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
|
@ -11,6 +11,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(LuauNarrowIntersectionNevers)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("IntersectionTypes");
|
TEST_SUITE_BEGIN("IntersectionTypes");
|
||||||
|
|
||||||
|
@ -1779,4 +1780,25 @@ TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_more_realistic_intersections")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "narrow_intersection_nevers")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag narrowIntersections{FFlag::LuauNarrowIntersectionNevers, true};
|
||||||
|
|
||||||
|
loadDefinition(R"(
|
||||||
|
declare class Player
|
||||||
|
Character: unknown
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local function foo(player: Player?)
|
||||||
|
if player and player.Character then
|
||||||
|
print(player.Character)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
|
||||||
|
CHECK_EQ("Player & { Character: ~(false?) }", toString(requireTypeAtPosition({3, 23})));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -231,7 +231,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export")
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
|
||||||
{
|
{
|
||||||
fileResolver.source["Modules/A"] = "";
|
fileResolver.source["Modules/A"] = "";
|
||||||
fileResolver.sourceTypes["Modules/A"] = SourceCode::Local;
|
fileResolver.sourceTypes["Modules/A"] = SourceCode::Script;
|
||||||
|
|
||||||
fileResolver.source["Modules/B"] = R"(
|
fileResolver.source["Modules/B"] = R"(
|
||||||
local M = require(script.Parent.A)
|
local M = require(script.Parent.A)
|
||||||
|
|
|
@ -12,15 +12,13 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferOOP");
|
TEST_SUITE_BEGIN("TypeInferOOP");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon")
|
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon")
|
||||||
{
|
{
|
||||||
// CLI-116571 method calls are missing arity checking?
|
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local someTable = {}
|
local someTable = {}
|
||||||
|
|
||||||
|
@ -30,15 +28,15 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi
|
||||||
someTable.Function1() -- Argument count mismatch
|
someTable.Function1() -- Argument count mismatch
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
REQUIRE(get<CountMismatch>(result.errors[0]));
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
REQUIRE(get<CountMismatch>(result.errors[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2")
|
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2")
|
||||||
{
|
{
|
||||||
// CLI-116571 method calls are missing arity checking?
|
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local someTable = {}
|
local someTable = {}
|
||||||
|
|
||||||
|
@ -48,8 +46,11 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_
|
||||||
someTable.Function2() -- Argument count mismatch
|
someTable.Function2() -- Argument count mismatch
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
REQUIRE(get<CountMismatch>(result.errors[0]));
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
REQUIRE(get<CountMismatch>(result.errors[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_another_overload_works")
|
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_another_overload_works")
|
||||||
|
|
|
@ -2567,4 +2567,29 @@ TEST_CASE_FIXTURE(Fixture, "oss_1687_equality_shouldnt_leak_nil")
|
||||||
)"));
|
)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauWeakNilRefinementType, true}
|
||||||
|
};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
type Part = {
|
||||||
|
HasTag: (Part, string) -> boolean,
|
||||||
|
Name: string,
|
||||||
|
}
|
||||||
|
local myList = {} :: {Part}
|
||||||
|
local nextPart = (table.remove(myList)) :: Part
|
||||||
|
|
||||||
|
if nextPart:HasTag("foo") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print(nextPart.Name)
|
||||||
|
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -24,14 +24,14 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAG(LuauFollowTableFreeze)
|
LUAU_FASTFLAG(LuauFollowTableFreeze)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceUpcast)
|
|
||||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalFailsafe)
|
LUAU_FASTFLAG(LuauBidirectionalFailsafe)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
||||||
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
|
|
||||||
|
@ -702,7 +702,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization2)
|
||||||
CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
|
CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
|
||||||
else if (FFlag::LuauSolverV2)
|
else if (FFlag::LuauSolverV2)
|
||||||
CHECK("({unknown}) -> ()" == toString(requireType("swap")));
|
CHECK("({unknown}) -> ()" == toString(requireType("swap")));
|
||||||
|
@ -4678,7 +4678,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties")
|
||||||
if (!FFlag::LuauSolverV2)
|
if (!FFlag::LuauSolverV2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedFastFlag sff[] = {{FFlag::LuauNonReentrantGeneralization, true}};
|
ScopedFastFlag sff[] = {{FFlag::LuauNonReentrantGeneralization2, true}};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function oc(player, speaker)
|
function oc(player, speaker)
|
||||||
|
@ -4717,6 +4717,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tables_can_have_both_metatables_and_indexers
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function foo(x, y)
|
function foo(x, y)
|
||||||
if x then
|
if x then
|
||||||
|
@ -4727,9 +4729,17 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
CHECK("<a>({a}, a) -> a" == toString(requireType("foo")));
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(get<NotATable>(result.errors[0]));
|
||||||
|
CHECK_EQ("(unknown, *error-type*) -> *error-type*", toString(requireType("foo")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
CHECK("<a>({a}, a) -> a" == toString(requireType("foo")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_string")
|
TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_string")
|
||||||
|
@ -5395,10 +5405,6 @@ TEST_CASE_FIXTURE(Fixture, "inference_in_constructor")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "returning_optional_in_table")
|
TEST_CASE_FIXTURE(Fixture, "returning_optional_in_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
local Numbers = { zero = 0 }
|
local Numbers = { zero = 0 }
|
||||||
local function FuncA(): { Value: number? }
|
local function FuncA(): { Value: number? }
|
||||||
|
@ -5430,10 +5436,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
local t: { (() -> ())? } = {
|
local t: { (() -> ())? } = {
|
||||||
|
@ -5456,10 +5459,6 @@ TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "oss_1596_expression_in_table")
|
TEST_CASE_FIXTURE(Fixture, "oss_1596_expression_in_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
type foo = {abc: number?}
|
type foo = {abc: number?}
|
||||||
local x: foo = {abc = 100}
|
local x: foo = {abc = 100}
|
||||||
|
@ -5492,7 +5491,6 @@ TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5524,7 +5522,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauBidirectionalInferenceUpcast, true},
|
|
||||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5559,8 +5556,8 @@ TEST_CASE_FIXTURE(Fixture, "deeply_nested_classish_inference")
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauSearchForRefineableType, true},
|
{FFlag::LuauSearchForRefineableType, true},
|
||||||
{FFlag::DebugLuauAssertOnForcedConstraint, true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: This probably should be revisited after CLI-143852: we end up
|
// NOTE: This probably should be revisited after CLI-143852: we end up
|
||||||
// cyclic types with *tons* of overlap.
|
// cyclic types with *tons* of overlap.
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
@ -5686,5 +5683,26 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local dict: {[string]: boolean} = {
|
||||||
|
code1 = true,
|
||||||
|
code2 = 123,
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
// ideally, we'd actually want this to give you `boolean` and `number` mismatch on `123`, but this is okay.
|
||||||
|
CHECK_EQ(toString(tm->wantedType, {true}), "{ [string]: boolean }");
|
||||||
|
CHECK_EQ(toString(tm->givenType, {true}), "{ [string]: boolean | number }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -20,21 +20,24 @@ LUAU_FASTFLAG(LuauFixLocationSpanTableIndexExpr)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauGlobalSelfAssignmentCycle)
|
|
||||||
LUAU_FASTINT(LuauNormalizeCacheLimit)
|
LUAU_FASTINT(LuauNormalizeCacheLimit)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
|
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAG(LuauInferLocalTypesInMultipleAssignments)
|
|
||||||
LUAU_FASTFLAG(LuauUnifyMetatableWithAny)
|
|
||||||
LUAU_FASTFLAG(LuauExtraFollows)
|
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
|
LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
|
||||||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAG(LuauCacheInferencePerAstExpr)
|
LUAU_FASTFLAG(LuauCacheInferencePerAstExpr)
|
||||||
|
LUAU_FASTFLAG(LuauLimitIterationWhenCheckingArgumentCounts)
|
||||||
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked)
|
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
|
LUAU_FASTFLAG(LuauStringPartLengthLimit)
|
||||||
|
LUAU_FASTFLAG(LuauSimplificationRecheckAssumption)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1822,7 +1825,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "infer_types_of_globals")
|
||||||
TEST_CASE_FIXTURE(Fixture, "multiple_assignment")
|
TEST_CASE_FIXTURE(Fixture, "multiple_assignment")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag sff_InferLocalTypesInMultipleAssignments{FFlag::LuauInferLocalTypesInMultipleAssignments, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function requireString(arg: string) end
|
local function requireString(arg: string) end
|
||||||
|
@ -1842,8 +1844,6 @@ TEST_CASE_FIXTURE(Fixture, "multiple_assignment")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "fuzz_global_self_assignment")
|
TEST_CASE_FIXTURE(Fixture, "fuzz_global_self_assignment")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauGlobalSelfAssignmentCycle{FFlag::LuauGlobalSelfAssignmentCycle, true};
|
|
||||||
|
|
||||||
// Shouldn't assert or crash
|
// Shouldn't assert or crash
|
||||||
check(R"(
|
check(R"(
|
||||||
_ = _
|
_ = _
|
||||||
|
@ -1853,8 +1853,6 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_global_self_assignment")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_works_with_any")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_works_with_any")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauUnifyMetatableWithAny, true};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
return {
|
return {
|
||||||
new = function(name: string)
|
new = function(name: string)
|
||||||
|
@ -1872,8 +1870,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_works_with_any")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_ret")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_ret")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauUnifyMetatableWithAny, true};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local function spooky(x: any)
|
local function spooky(x: any)
|
||||||
return getmetatable(x)
|
return getmetatable(x)
|
||||||
|
@ -1885,10 +1881,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_ret")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_param")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_param")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauUnifyMetatableWithAny, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = check(R"(
|
auto result = check(R"(
|
||||||
local function check(x): any
|
local function check(x): any
|
||||||
|
@ -1896,15 +1889,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_infer_any_param")
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// CLI-144695: We're leaking the `MT` generic here, this happens regardless
|
// CLI-144695: We're leaking the `MT` generic here.
|
||||||
// of if `LuauUnifyMetatableWithAny` is set.
|
|
||||||
CHECK_EQ("({ @metatable MT, {+ +} }) -> any", toString(requireType("check")));
|
CHECK_EQ("({ @metatable MT, {+ +} }) -> any", toString(requireType("check")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_pack_check_missing_follow")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_pack_check_missing_follow")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauExtraFollows{FFlag::LuauExtraFollows, true};
|
|
||||||
|
|
||||||
// Shouldn't assert or crash
|
// Shouldn't assert or crash
|
||||||
check(R"(
|
check(R"(
|
||||||
_ = n255
|
_ = n255
|
||||||
|
@ -1917,8 +1907,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "fuzzer_unify_with_free_missing_follow")
|
TEST_CASE_FIXTURE(Fixture, "fuzzer_unify_with_free_missing_follow")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauExtraFollows{FFlag::LuauExtraFollows, true};
|
|
||||||
|
|
||||||
// Shouldn't assert or crash
|
// Shouldn't assert or crash
|
||||||
check(R"(
|
check(R"(
|
||||||
for _ in ... do
|
for _ in ... do
|
||||||
|
@ -2035,5 +2023,114 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving"
|
||||||
LUAU_REQUIRE_NO_ERROR(results, ConstraintSolvingIncompleteError);
|
LUAU_REQUIRE_NO_ERROR(results, ConstraintSolvingIncompleteError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "konnichiwa" * doctest::timeout(0.25))
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, false},
|
||||||
|
{FFlag::LuauInstantiateInSubtyping, true},
|
||||||
|
{FFlag::LuauLimitIterationWhenCheckingArgumentCounts, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopedFastInt sfi{FInt::LuauTypeInferTypePackLoopLimit, 100};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(pcall(table.unpack({pcall})))");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR(result, CodeTooComplex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauTrackInteriorFreeTypesOnScope, true},
|
||||||
|
{FFlag::LuauHasPropProperBlock, true},
|
||||||
|
{FFlag::LuauNonReentrantGeneralization2, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = check(R"(
|
||||||
|
local _ = {_ = _}, l0
|
||||||
|
_ += _
|
||||||
|
while _ do
|
||||||
|
while _[_] do
|
||||||
|
if _.n0 then
|
||||||
|
_ = _
|
||||||
|
else
|
||||||
|
_ = _
|
||||||
|
return _
|
||||||
|
end
|
||||||
|
do
|
||||||
|
while _ do
|
||||||
|
_, _ = nil
|
||||||
|
end
|
||||||
|
return function()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while _[_] do
|
||||||
|
_ = _._VERSION, ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local _
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
|
LUAU_REQUIRE_NO_ERROR(result, ConstraintSolvingIncompleteError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauTrackInteriorFreeTypesOnScope, true},
|
||||||
|
{FFlag::LuauNonReentrantGeneralization2, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local _ = {n0 = _.n0}, -_, _
|
||||||
|
_ += _.n0
|
||||||
|
_ /= _[_]
|
||||||
|
while _.n110 do
|
||||||
|
while _._ do
|
||||||
|
while _ do
|
||||||
|
while _ do
|
||||||
|
_ = _
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while _[_] do
|
||||||
|
function _()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while ... do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
|
LUAU_REQUIRE_NO_ERROR(result, ConstraintSolvingIncompleteError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauTrackInteriorFreeTypesOnScope, true},
|
||||||
|
{FFlag::LuauHasPropProperBlock, true},
|
||||||
|
{FFlag::LuauNonReentrantGeneralization2, true},
|
||||||
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
|
{FFlag::LuauStringPartLengthLimit, true},
|
||||||
|
{FFlag::LuauSimplificationRecheckAssumption, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERRORS(check(R"(
|
||||||
|
_ = {[_[`{_ + ...}`]]=_,_,[{_=nil,[_._G]=false,}]={[_[_[_]][_][_ / ...]]=_,[...]=false,_,},[_[_][_][_]]=l255,},""
|
||||||
|
local _
|
||||||
|
)"));
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERRORS(check(R"(
|
||||||
|
_ = {[(_G)]=_,[_[_[_]][_[_]][nil][_]]={_G=_,},_[_[_]][_][_],n0={[_]=_,_G=_,},248,}
|
||||||
|
local _
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -10,6 +10,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UnionTypes");
|
TEST_SUITE_BEGIN("UnionTypes");
|
||||||
|
@ -983,9 +984,14 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ(
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
"(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
CHECK_EQ(
|
||||||
);
|
"<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||||
|
);
|
||||||
|
else
|
||||||
|
CHECK_EQ(
|
||||||
|
"(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types_2")
|
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types_2")
|
||||||
|
|
Loading…
Add table
Reference in a new issue