mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
Sync to upstream/release/688 (#1968)
This commit is contained in:
parent
31725a6521
commit
0afee00064
69 changed files with 1481 additions and 599 deletions
|
@ -1,11 +1,12 @@
|
||||||
// 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
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
|
#include "Luau/TypeIds.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
#include "Luau/Ast.h"
|
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
@ -513,6 +514,18 @@ struct RecursiveRestraintViolation
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Error during subtyping when the inferred bounds of a generic type are incompatible
|
||||||
|
struct GenericBoundsMismatch
|
||||||
|
{
|
||||||
|
std::string_view genericName;
|
||||||
|
std::vector<TypeId> lowerBounds;
|
||||||
|
std::vector<TypeId> upperBounds;
|
||||||
|
|
||||||
|
GenericBoundsMismatch(std::string_view genericName, TypeIds lowerBoundSet, TypeIds upperBoundSet);
|
||||||
|
|
||||||
|
bool operator==(const GenericBoundsMismatch& rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
using TypeErrorData = Variant<
|
using TypeErrorData = Variant<
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
UnknownSymbol,
|
UnknownSymbol,
|
||||||
|
@ -569,7 +582,8 @@ using TypeErrorData = Variant<
|
||||||
GenericTypeCountMismatch,
|
GenericTypeCountMismatch,
|
||||||
GenericTypePackCountMismatch,
|
GenericTypePackCountMismatch,
|
||||||
MultipleNonviableOverloads,
|
MultipleNonviableOverloads,
|
||||||
RecursiveRestraintViolation>;
|
RecursiveRestraintViolation,
|
||||||
|
GenericBoundsMismatch>;
|
||||||
|
|
||||||
struct TypeErrorSummary
|
struct TypeErrorSummary
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "Luau/Unifiable.h"
|
#include "Luau/Unifiable.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -93,7 +95,7 @@ struct GenericTypeFinder : TypeOnceVisitor
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
GenericTypeFinder()
|
GenericTypeFinder()
|
||||||
: TypeOnceVisitor("GenericTypeFinder")
|
: TypeOnceVisitor("GenericTypeFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,9 @@ struct SubtypingResult
|
||||||
// those assumptions are recorded here.
|
// those assumptions are recorded here.
|
||||||
std::vector<SubtypeConstraint> assumedConstraints;
|
std::vector<SubtypeConstraint> assumedConstraints;
|
||||||
|
|
||||||
|
/// If any generic bounds were invalid, report them here
|
||||||
|
std::vector<GenericBoundsMismatch> genericBoundsMismatches;
|
||||||
|
|
||||||
SubtypingResult& andAlso(const SubtypingResult& other);
|
SubtypingResult& andAlso(const SubtypingResult& other);
|
||||||
SubtypingResult& orElse(const SubtypingResult& other);
|
SubtypingResult& orElse(const SubtypingResult& other);
|
||||||
SubtypingResult& withBothComponent(TypePath::Component component);
|
SubtypingResult& withBothComponent(TypePath::Component component);
|
||||||
|
@ -358,7 +361,15 @@ private:
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
SubtypingResult& original);
|
SubtypingResult& original);
|
||||||
|
|
||||||
SubtypingResult checkGenericBounds(const SubtypingEnvironment::GenericBounds& bounds, SubtypingEnvironment& env, NotNull<Scope> scope);
|
SubtypingResult checkGenericBounds(
|
||||||
|
const SubtypingEnvironment::GenericBounds& bounds,
|
||||||
|
SubtypingEnvironment& env,
|
||||||
|
NotNull<Scope> scope,
|
||||||
|
std::string_view genericName
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Clip with LuauSubtypingReportGenericBoundMismatches
|
||||||
|
SubtypingResult checkGenericBounds_DEPRECATED(const SubtypingEnvironment::GenericBounds& bounds, SubtypingEnvironment& env, NotNull<Scope> scope);
|
||||||
|
|
||||||
static void maybeUpdateBounds(
|
static void maybeUpdateBounds(
|
||||||
TypeId here,
|
TypeId here,
|
||||||
|
|
|
@ -112,7 +112,6 @@ struct TxnLog
|
||||||
// If both logs talk about the same type, pack, or table, the rhs takes
|
// If both logs talk about the same type, pack, or table, the rhs takes
|
||||||
// priority.
|
// priority.
|
||||||
void concat(TxnLog rhs);
|
void concat(TxnLog rhs);
|
||||||
void concatAsIntersections(TxnLog rhs, NotNull<TypeArena> arena);
|
|
||||||
void concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena);
|
void concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena);
|
||||||
|
|
||||||
// Commits the TxnLog, rebinding all type pointers to their pending states.
|
// Commits the TxnLog, rebinding all type pointers to their pending states.
|
||||||
|
@ -269,8 +268,6 @@ struct TxnLog
|
||||||
return Luau::get_if<T>(&ty->ty) != nullptr;
|
return Luau::get_if<T>(&ty->ty) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<TypeId>, std::vector<TypePackId>> getChanges() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// unique_ptr is used to give us stable pointers across insertions into the
|
// unique_ptr is used to give us stable pointers across insertions into the
|
||||||
// map. Otherwise, it would be really easy to accidentally invalidate the
|
// map. Otherwise, it would be really easy to accidentally invalidate the
|
||||||
|
@ -291,12 +288,6 @@ private:
|
||||||
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);
|
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// There is one spot in the code where TxnLog has to reconcile collisions
|
|
||||||
// between parallel logs. In that codepath, we have to work out which of two
|
|
||||||
// FreeTypes subsumes the other. If useScopes is false, the TypeLevel is
|
|
||||||
// used. Else we use the embedded Scope*.
|
|
||||||
bool useScopes = false;
|
|
||||||
|
|
||||||
// It is sometimes the case under DCR that we speculatively rebind
|
// It is sometimes the case under DCR that we speculatively rebind
|
||||||
// GenericTypes to other types as though they were free. We mark logs that
|
// GenericTypes to other types as though they were free. We mark logs that
|
||||||
// contain these kinds of substitutions as radioactive so that we know that
|
// contain these kinds of substitutions as radioactive so that we know that
|
||||||
|
|
|
@ -80,9 +80,6 @@ struct Unifier
|
||||||
bool checkInhabited = true; // Normalize types to check if they are inhabited
|
bool checkInhabited = true; // Normalize types to check if they are inhabited
|
||||||
CountMismatch::Context ctx = CountMismatch::Arg;
|
CountMismatch::Context ctx = CountMismatch::Arg;
|
||||||
|
|
||||||
// If true, generics act as free types when unifying.
|
|
||||||
bool hideousFixMeGenericsAreActuallyFree = false;
|
|
||||||
|
|
||||||
UnifierSharedState& sharedState;
|
UnifierSharedState& sharedState;
|
||||||
|
|
||||||
// When the Unifier is forced to unify two blocked types (or packs), they
|
// When the Unifier is forced to unify two blocked types (or packs), they
|
||||||
|
|
|
@ -556,7 +556,7 @@ struct GenericTypeVisitor
|
||||||
*/
|
*/
|
||||||
struct TypeVisitor : GenericTypeVisitor<std::unordered_set<void*>>
|
struct TypeVisitor : GenericTypeVisitor<std::unordered_set<void*>>
|
||||||
{
|
{
|
||||||
explicit TypeVisitor(const std::string visitorName, bool skipBoundTypes = false)
|
explicit TypeVisitor(const std::string visitorName, bool skipBoundTypes)
|
||||||
: GenericTypeVisitor{visitorName, {}, skipBoundTypes}
|
: GenericTypeVisitor{visitorName, {}, skipBoundTypes}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -565,7 +565,7 @@ struct TypeVisitor : GenericTypeVisitor<std::unordered_set<void*>>
|
||||||
/// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it.
|
/// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it.
|
||||||
struct TypeOnceVisitor : GenericTypeVisitor<DenseHashSet<void*>>
|
struct TypeOnceVisitor : GenericTypeVisitor<DenseHashSet<void*>>
|
||||||
{
|
{
|
||||||
explicit TypeOnceVisitor(const std::string visitorName, bool skipBoundTypes = false)
|
explicit TypeOnceVisitor(const std::string visitorName, bool skipBoundTypes)
|
||||||
: GenericTypeVisitor{visitorName, DenseHashSet<void*>{nullptr}, skipBoundTypes}
|
: GenericTypeVisitor{visitorName, DenseHashSet<void*>{nullptr}, skipBoundTypes}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||||
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
|
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauIncludeBreakContinueStatements)
|
||||||
|
|
||||||
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"};
|
||||||
|
@ -1210,6 +1211,28 @@ static bool isBindingLegalAtCurrentPosition(const Symbol& symbol, const Binding&
|
||||||
return binding.location == Location() || !binding.location.containsClosed(pos);
|
return binding.location == Location() || !binding.location.containsClosed(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isValidBreakContinueContext(const std::vector<AstNode*>& ancestry, Position position)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauIncludeBreakContinueStatements);
|
||||||
|
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->is<AstStatFunction>() || (*it)->is<AstStatLocalFunction>() || (*it)->is<AstExprFunction>() || (*it)->is<AstStatTypeFunction>() ||
|
||||||
|
(*it)->is<AstTypeFunction>())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (auto statWhile = (*it)->as<AstStatWhile>(); statWhile && statWhile->body->location.contains(position))
|
||||||
|
return true;
|
||||||
|
else if (auto statFor = (*it)->as<AstStatFor>(); statFor && statFor->body->location.contains(position))
|
||||||
|
return true;
|
||||||
|
else if (auto statForIn = (*it)->as<AstStatForIn>(); statForIn && statForIn->body->location.contains(position))
|
||||||
|
return true;
|
||||||
|
else if (auto statRepeat = (*it)->as<AstStatRepeat>(); statRepeat && statRepeat->body->location.contains(position))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static AutocompleteEntryMap autocompleteStatement(
|
static AutocompleteEntryMap autocompleteStatement(
|
||||||
const Module& module,
|
const Module& module,
|
||||||
const std::vector<AstNode*>& ancestry,
|
const std::vector<AstNode*>& ancestry,
|
||||||
|
@ -1254,8 +1277,20 @@ static AutocompleteEntryMap autocompleteStatement(
|
||||||
scope = scope->parent;
|
scope = scope->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauIncludeBreakContinueStatements)
|
||||||
|
{
|
||||||
|
bool shouldIncludeBreakAndContinue = isValidBreakContinueContext(ancestry, position);
|
||||||
|
for (const auto& kw : kStatementStartingKeywords)
|
||||||
|
{
|
||||||
|
if ((kw != "break" && kw != "continue") || shouldIncludeBreakAndContinue)
|
||||||
|
result.emplace(kw, AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (const auto& kw : kStatementStartingKeywords)
|
for (const auto& kw : kStatementStartingKeywords)
|
||||||
result.emplace(kw, AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
result.emplace(kw, AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
|
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
|
|
||||||
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
|
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
|
|
||||||
|
@ -408,35 +407,17 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frontend.getLuauSolverMode() == SolverMode::New)
|
if (frontend.getLuauSolverMode() == SolverMode::New)
|
||||||
{
|
|
||||||
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
|
||||||
|
|
||||||
if (FFlag::LuauUpdateSetMetatableTypeSignature)
|
|
||||||
{
|
{
|
||||||
// setmetatable<T: {}, MT>(T, MT) -> setmetatable<T, MT>
|
// setmetatable<T: {}, MT>(T, MT) -> setmetatable<T, MT>
|
||||||
TypeId setmtReturn = arena.addType(TypeFunctionInstanceType{builtinTypeFunctions().setmetatableFunc, {genericT, genericMT}});
|
TypeId setmtReturn = arena.addType(TypeFunctionInstanceType{builtinTypeFunctions().setmetatableFunc, {genericT, genericMT}});
|
||||||
addGlobalBinding(
|
addGlobalBinding(
|
||||||
globals, "setmetatable", makeFunction(arena, std::nullopt, {genericT, genericMT}, {}, {genericT, genericMT}, {setmtReturn}), "@luau"
|
globals,
|
||||||
|
"setmetatable",
|
||||||
|
makeFunction(arena, std::nullopt, {genericT, genericMT}, {}, {genericT, genericMT}, {setmtReturn}),
|
||||||
|
"@luau"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
// clang-format off
|
|
||||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
|
||||||
addGlobalBinding(globals, "setmetatable",
|
|
||||||
arena.addType(
|
|
||||||
FunctionType{
|
|
||||||
{genericT, genericMT},
|
|
||||||
{},
|
|
||||||
arena.addTypePack(TypePack{{genericT, genericMT}}),
|
|
||||||
arena.addTypePack(TypePack{{tMetaMT}})
|
|
||||||
}
|
|
||||||
), "@luau"
|
|
||||||
);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
||||||
|
|
|
@ -27,6 +27,7 @@ LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways)
|
LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefineDistributesOverUnions)
|
LUAU_FASTFLAGVARIABLE(LuauRefineDistributesOverUnions)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1063,7 +1064,7 @@ struct FindRefinementBlockers : TypeOnceVisitor
|
||||||
DenseHashSet<TypeId> found{nullptr};
|
DenseHashSet<TypeId> found{nullptr};
|
||||||
|
|
||||||
FindRefinementBlockers()
|
FindRefinementBlockers()
|
||||||
: TypeOnceVisitor("FindRefinementBlockers")
|
: TypeOnceVisitor("FindRefinementBlockers", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
|
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
bool traverseIntoTypeFunctions = true;
|
bool traverseIntoTypeFunctions = true;
|
||||||
|
|
||||||
explicit ReferenceCountInitializer(NotNull<TypeIds> result)
|
explicit ReferenceCountInitializer(NotNull<TypeIds> result)
|
||||||
: TypeOnceVisitor("ReferenceCountInitializer")
|
: TypeOnceVisitor("ReferenceCountInitializer", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, result(result)
|
, result(result)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -178,13 +178,11 @@ TypeIds Constraint::getMaybeMutatedFreeTypes() const
|
||||||
rci.traverse(tcc->exprType);
|
rci.traverse(tcc->exprType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauPushFunctionTypesInFunctionStatement)
|
// NOTE: this should probably be in an if-else chain with the above.
|
||||||
{
|
|
||||||
if (auto pftc = get<PushFunctionTypeConstraint>(*this))
|
if (auto pftc = get<PushFunctionTypeConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(pftc->functionType);
|
rci.traverse(pftc->functionType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,17 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
|
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
|
||||||
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType2)
|
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex)
|
LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunNoScopeMapRef)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAGVARIABLE(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAGVARIABLE(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
||||||
LUAU_FASTFLAG(LuauParametrizedAttributeSyntax)
|
LUAU_FASTFLAG(LuauParametrizedAttributeSyntax)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
LUAU_FASTFLAG(DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -137,7 +139,7 @@ struct HasFreeType : TypeOnceVisitor
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
HasFreeType()
|
HasFreeType()
|
||||||
: TypeOnceVisitor("TypeOnceVisitor")
|
: TypeOnceVisitor("TypeOnceVisitor", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,7 +645,7 @@ struct FindSimplificationBlockers : TypeOnceVisitor
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
FindSimplificationBlockers()
|
FindSimplificationBlockers()
|
||||||
: TypeOnceVisitor("FindSimplificationBlockers")
|
: TypeOnceVisitor("FindSimplificationBlockers", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1565,8 +1567,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
sig.bodyScope->rvalueRefinements[def] = sig.signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
{
|
|
||||||
if (auto indexName = function->name->as<AstExprIndexName>())
|
if (auto indexName = function->name->as<AstExprIndexName>())
|
||||||
{
|
{
|
||||||
auto beginProp = checkpoint(this);
|
auto beginProp = checkpoint(this);
|
||||||
|
@ -1608,11 +1608,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
{
|
{
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
|
||||||
}
|
|
||||||
|
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
|
@ -1906,6 +1901,92 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
reportError(function->location, ReservedIdentifier{"typeof"});
|
reportError(function->location, ReservedIdentifier{"typeof"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauTypeFunNoScopeMapRef)
|
||||||
|
{
|
||||||
|
auto scopeIt = astTypeFunctionEnvironmentScopes.find(function);
|
||||||
|
LUAU_ASSERT(scopeIt);
|
||||||
|
|
||||||
|
ScopePtr environmentScope = *scopeIt;
|
||||||
|
|
||||||
|
Checkpoint startCheckpoint = checkpoint(this);
|
||||||
|
FunctionSignature sig = checkFunctionSignature(environmentScope, function->body, /* expectedType */ std::nullopt);
|
||||||
|
|
||||||
|
// Place this function as a child of the non-type function scope
|
||||||
|
if (FFlag::LuauEmplaceNotPushBack)
|
||||||
|
scope->children.emplace_back(sig.signatureScope.get());
|
||||||
|
else
|
||||||
|
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackFreeInteriorTypePacks)
|
||||||
|
interiorFreeTypes.emplace_back();
|
||||||
|
else
|
||||||
|
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
|
checkFunctionBody(sig.bodyScope, function->body);
|
||||||
|
Checkpoint endCheckpoint = checkpoint(this);
|
||||||
|
|
||||||
|
TypeId generalizedTy = arena->addType(BlockedType{});
|
||||||
|
NotNull<Constraint> gc = addConstraint(
|
||||||
|
sig.signatureScope,
|
||||||
|
function->location,
|
||||||
|
GeneralizationConstraint{
|
||||||
|
generalizedTy,
|
||||||
|
sig.signature,
|
||||||
|
std::vector<TypeId>{},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackFreeInteriorTypePacks)
|
||||||
|
{
|
||||||
|
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
|
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
|
if (FFlag::LuauTrackFreeInteriorTypePacks)
|
||||||
|
interiorFreeTypes.pop_back();
|
||||||
|
else
|
||||||
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
|
||||||
|
Constraint* previous = nullptr;
|
||||||
|
forEachConstraint(
|
||||||
|
startCheckpoint,
|
||||||
|
endCheckpoint,
|
||||||
|
this,
|
||||||
|
[gc, &previous](const ConstraintPtr& constraint)
|
||||||
|
{
|
||||||
|
gc->dependencies.emplace_back(constraint.get());
|
||||||
|
|
||||||
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
||||||
|
{
|
||||||
|
if (previous)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauEmplaceNotPushBack)
|
||||||
|
constraint->dependencies.emplace_back(previous);
|
||||||
|
else
|
||||||
|
constraint->dependencies.push_back(NotNull{previous});
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = constraint.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
std::optional<TypeId> existingFunctionTy = environmentScope->lookup(function->name);
|
||||||
|
|
||||||
|
if (!existingFunctionTy)
|
||||||
|
ice->ice("checkAliases did not populate type function name", function->nameLocation);
|
||||||
|
|
||||||
|
TypeId unpackedTy = follow(*existingFunctionTy);
|
||||||
|
|
||||||
|
if (auto bt = get<BlockedType>(unpackedTy); bt && nullptr == bt->getOwner())
|
||||||
|
emplaceType<BoundType>(asMutable(unpackedTy), generalizedTy);
|
||||||
|
|
||||||
|
return ControlFlow::None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
auto scopePtr = astTypeFunctionEnvironmentScopes.find(function);
|
||||||
LUAU_ASSERT(scopePtr);
|
LUAU_ASSERT(scopePtr);
|
||||||
|
|
||||||
|
@ -1986,6 +2067,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
|
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
|
||||||
{
|
{
|
||||||
|
@ -2690,6 +2772,15 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||||
|
|
||||||
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton)
|
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
{
|
||||||
|
if (string->quoteStyle == AstExprConstantString::QuotedSingle || forceSingleton)
|
||||||
|
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
||||||
|
|
||||||
|
return Inference{builtinTypes->stringType};
|
||||||
|
}
|
||||||
|
|
||||||
if (forceSingleton)
|
if (forceSingleton)
|
||||||
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}}})};
|
||||||
|
|
||||||
|
@ -2725,6 +2816,16 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
||||||
|
|
||||||
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton)
|
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton)
|
||||||
{
|
{
|
||||||
|
if (FFlag::DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
{
|
||||||
|
const TypeId singletonType = boolExpr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
||||||
|
if (forceSingleton)
|
||||||
|
return Inference{singletonType};
|
||||||
|
|
||||||
|
return Inference { builtinTypes->booleanType };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const TypeId singletonType = boolExpr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
const TypeId singletonType = boolExpr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
||||||
if (forceSingleton)
|
if (forceSingleton)
|
||||||
return Inference{singletonType};
|
return Inference{singletonType};
|
||||||
|
@ -4321,65 +4422,6 @@ TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location locati
|
||||||
return resultType;
|
return resultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentTypeCheckGlobalPrepopulator_DEPRECATED : AstVisitor
|
|
||||||
{
|
|
||||||
const NotNull<Scope> globalScope;
|
|
||||||
const NotNull<Scope> currentScope;
|
|
||||||
const NotNull<const DataFlowGraph> dfg;
|
|
||||||
const NotNull<TypeArena> arena;
|
|
||||||
|
|
||||||
FragmentTypeCheckGlobalPrepopulator_DEPRECATED(
|
|
||||||
NotNull<Scope> globalScope,
|
|
||||||
NotNull<Scope> currentScope,
|
|
||||||
NotNull<const DataFlowGraph> dfg,
|
|
||||||
NotNull<TypeArena> arena
|
|
||||||
)
|
|
||||||
: globalScope(globalScope)
|
|
||||||
, currentScope(currentScope)
|
|
||||||
, dfg(dfg)
|
|
||||||
, arena(arena)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(AstExprGlobal* global) override
|
|
||||||
{
|
|
||||||
if (auto ty = globalScope->lookup(global->name))
|
|
||||||
{
|
|
||||||
DefId def = dfg->getDef(global);
|
|
||||||
// We only want to write into the current scope the type of the global
|
|
||||||
currentScope->lvalueTypes[def] = *ty;
|
|
||||||
}
|
|
||||||
else if (auto ty = currentScope->lookup(global->name))
|
|
||||||
{
|
|
||||||
// We are trying to create a binding for a brand new function, so we actually do have to write it into the scope.
|
|
||||||
DefId def = dfg->getDef(global);
|
|
||||||
// We only want to write into the current scope the type of the global
|
|
||||||
currentScope->lvalueTypes[def] = *ty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(AstStatFunction* function) override
|
|
||||||
{
|
|
||||||
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
|
||||||
{
|
|
||||||
if (auto ty = globalScope->lookup(g->name))
|
|
||||||
{
|
|
||||||
currentScope->bindings[g->name] = Binding{*ty};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Hasn't existed since a previous typecheck
|
|
||||||
TypeId bt = arena->addType(BlockedType{});
|
|
||||||
currentScope->bindings[g->name] = Binding{bt};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlobalPrepopulator : AstVisitor
|
struct GlobalPrepopulator : AstVisitor
|
||||||
{
|
{
|
||||||
const NotNull<Scope> globalScope;
|
const NotNull<Scope> globalScope;
|
||||||
|
|
|
@ -36,8 +36,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
|
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
|
||||||
LUAU_FASTFLAG(LuauLimitUnification)
|
LUAU_FASTFLAG(LuauLimitUnification)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2)
|
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2)
|
||||||
|
@ -49,6 +47,8 @@ LUAU_FASTFLAGVARIABLE(LuauExtendSealedTableUpperBounds)
|
||||||
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
||||||
LUAU_FASTFLAG(LuauParametrizedAttributeSyntax)
|
LUAU_FASTFLAG(LuauParametrizedAttributeSyntax)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNameConstraintRestrictRecursiveTypes)
|
LUAU_FASTFLAGVARIABLE(LuauNameConstraintRestrictRecursiveTypes)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
LUAU_FASTFLAG(DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -346,7 +346,7 @@ struct InfiniteTypeFinder : TypeOnceVisitor
|
||||||
bool foundInfiniteType = false;
|
bool foundInfiniteType = false;
|
||||||
|
|
||||||
explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature, NotNull<Scope> scope)
|
explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature, NotNull<Scope> scope)
|
||||||
: TypeOnceVisitor("InfiniteTypeFinder")
|
: TypeOnceVisitor("InfiniteTypeFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, solver(solver)
|
, solver(solver)
|
||||||
, signature(signature)
|
, signature(signature)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
|
@ -683,7 +683,7 @@ struct TypeSearcher : TypeVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
|
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
|
||||||
: TypeVisitor("TypeSearcher")
|
: TypeVisitor("TypeSearcher", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, needle(needle)
|
, needle(needle)
|
||||||
, current(initialPolarity)
|
, current(initialPolarity)
|
||||||
{
|
{
|
||||||
|
@ -1664,7 +1664,7 @@ struct ContainsGenerics_DEPRECATED : public TypeOnceVisitor
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
ContainsGenerics_DEPRECATED()
|
ContainsGenerics_DEPRECATED()
|
||||||
: TypeOnceVisitor("ContainsGenerics_DEPRECATED")
|
: TypeOnceVisitor("ContainsGenerics_DEPRECATED", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1748,7 +1748,7 @@ struct ContainsGenerics : public TypeOnceVisitor
|
||||||
NotNull<DenseHashSet<const void*>> generics;
|
NotNull<DenseHashSet<const void*>> generics;
|
||||||
|
|
||||||
explicit ContainsGenerics(NotNull<DenseHashSet<const void*>> generics)
|
explicit ContainsGenerics(NotNull<DenseHashSet<const void*>> generics)
|
||||||
: TypeOnceVisitor("ContainsGenerics")
|
: TypeOnceVisitor("ContainsGenerics", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, generics{generics}
|
, generics{generics}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1887,7 +1887,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
|
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
|
||||||
expr->is<AstExprConstantNil>() || (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is<AstExprTable>()))
|
expr->is<AstExprConstantNil>() || expr->is<AstExprTable>())
|
||||||
{
|
{
|
||||||
if (ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks}))
|
if (ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks}))
|
||||||
{
|
{
|
||||||
|
@ -1901,20 +1901,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
}
|
}
|
||||||
u2.unify(actualArgTy, expectedArgTy);
|
u2.unify(actualArgTy, expectedArgTy);
|
||||||
}
|
}
|
||||||
else if (!FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is<AstExprTable>() &&
|
|
||||||
!ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks}))
|
|
||||||
{
|
|
||||||
Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
|
|
||||||
std::vector<TypeId> toBlock;
|
|
||||||
(void)matchLiteralType(
|
|
||||||
c.astTypes, c.astExpectedTypes, builtinTypes, arena, NotNull{&u2}, NotNull{&sp}, expectedArgTy, actualArgTy, expr, toBlock
|
|
||||||
);
|
|
||||||
LUAU_ASSERT(toBlock.empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
{
|
|
||||||
// Consider:
|
// Consider:
|
||||||
//
|
//
|
||||||
// local Direction = { Left = 1, Right = 2 }
|
// local Direction = { Left = 1, Right = 2 }
|
||||||
|
@ -1932,7 +1920,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
NotNull<Constraint> addition = pushConstraint(constraint->scope, constraint->location, std::move(c));
|
NotNull<Constraint> addition = pushConstraint(constraint->scope, constraint->location, std::move(c));
|
||||||
inheritBlocks(constraint, addition);
|
inheritBlocks(constraint, addition);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1962,6 +1949,7 @@ bool ConstraintSolver::tryDispatch(const TableCheckConstraint& c, NotNull<const
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
|
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::DebugLuauStringSingletonBasedOnQuotes);
|
||||||
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
|
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
|
||||||
if (expectedType && (isBlocked(*expectedType) || get<PendingExpansionType>(*expectedType)))
|
if (expectedType && (isBlocked(*expectedType) || get<PendingExpansionType>(*expectedType)))
|
||||||
return block(*expectedType, constraint);
|
return block(*expectedType, constraint);
|
||||||
|
@ -2213,7 +2201,7 @@ struct BlockedTypeFinder : TypeOnceVisitor
|
||||||
std::optional<TypeId> blocked;
|
std::optional<TypeId> blocked;
|
||||||
|
|
||||||
BlockedTypeFinder()
|
BlockedTypeFinder()
|
||||||
: TypeOnceVisitor("ContainsGenerics_DEPRECATED")
|
: TypeOnceVisitor("ContainsGenerics_DEPRECATED", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2444,7 +2432,6 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
// was blocked on missing a member. In the above, we may
|
// was blocked on missing a member. In the above, we may
|
||||||
// try to solve for `hasProp T "bar"`, block, then never
|
// try to solve for `hasProp T "bar"`, block, then never
|
||||||
// wake up without forcing a constraint.
|
// wake up without forcing a constraint.
|
||||||
if (FFlag::LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
unblock(lhsType, constraint->location);
|
unblock(lhsType, constraint->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3568,7 +3555,7 @@ struct Blocker : TypeOnceVisitor
|
||||||
bool blocked = false;
|
bool blocked = false;
|
||||||
|
|
||||||
explicit Blocker(NotNull<ConstraintSolver> solver, NotNull<const Constraint> constraint)
|
explicit Blocker(NotNull<ConstraintSolver> solver, NotNull<const Constraint> constraint)
|
||||||
: TypeOnceVisitor("Blocker")
|
: TypeOnceVisitor("Blocker", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, solver(solver)
|
, solver(solver)
|
||||||
, constraint(constraint)
|
, constraint(constraint)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerVectorLerp)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -268,6 +270,37 @@ declare extern type vector with
|
||||||
z: number
|
z: number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
declare vector: {
|
||||||
|
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||||
|
magnitude: @checked (vec: vector) -> number,
|
||||||
|
normalize: @checked (vec: vector) -> vector,
|
||||||
|
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||||
|
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||||
|
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||||
|
floor: @checked (vec: vector) -> vector,
|
||||||
|
ceil: @checked (vec: vector) -> vector,
|
||||||
|
abs: @checked (vec: vector) -> vector,
|
||||||
|
sign: @checked (vec: vector) -> vector,
|
||||||
|
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||||
|
max: @checked (vector, ...vector) -> vector,
|
||||||
|
min: @checked (vector, ...vector) -> vector,
|
||||||
|
lerp: @checked (vec1: vector, vec2: vector, t: number) -> number,
|
||||||
|
|
||||||
|
zero: vector,
|
||||||
|
one: vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const char* const kBuiltinDefinitionVectorSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
-- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties
|
||||||
|
declare extern type vector with
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
z: number
|
||||||
|
end
|
||||||
|
|
||||||
declare vector: {
|
declare vector: {
|
||||||
create: @checked (x: number, y: number, z: number?) -> vector,
|
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||||
magnitude: @checked (vec: vector) -> number,
|
magnitude: @checked (vec: vector) -> number,
|
||||||
|
@ -301,7 +334,14 @@ std::string getBuiltinDefinitionSource()
|
||||||
result += kBuiltinDefinitionDebugSrc;
|
result += kBuiltinDefinitionDebugSrc;
|
||||||
result += kBuiltinDefinitionUtf8Src;
|
result += kBuiltinDefinitionUtf8Src;
|
||||||
result += kBuiltinDefinitionBufferSrc;
|
result += kBuiltinDefinitionBufferSrc;
|
||||||
|
if (FFlag::LuauTypeCheckerVectorLerp)
|
||||||
|
{
|
||||||
result += kBuiltinDefinitionVectorSrc;
|
result += kBuiltinDefinitionVectorSrc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += kBuiltinDefinitionVectorSrc_DEPRECATED;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -894,6 +894,27 @@ struct ErrorConverter
|
||||||
{
|
{
|
||||||
return "Recursive type being used with different parameters.";
|
return "Recursive type being used with different parameters.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string operator()(const GenericBoundsMismatch& e) const
|
||||||
|
{
|
||||||
|
std::string lowerBounds;
|
||||||
|
for (size_t i = 0; i < e.lowerBounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
lowerBounds += ", ";
|
||||||
|
lowerBounds += Luau::toString(e.lowerBounds[i]);
|
||||||
|
}
|
||||||
|
std::string upperBounds;
|
||||||
|
for (size_t i = 0; i < e.upperBounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
upperBounds += ", ";
|
||||||
|
upperBounds += Luau::toString(e.upperBounds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "The generic type parameter " + std::string{e.genericName} + "was found to have invalid bounds. Its lower bounds were [" +
|
||||||
|
lowerBounds + "], and its upper bounds were [" + upperBounds + "].";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InvalidNameChecker
|
struct InvalidNameChecker
|
||||||
|
@ -1297,6 +1318,18 @@ bool MultipleNonviableOverloads::operator==(const MultipleNonviableOverloads& rh
|
||||||
return attemptedArgCount == rhs.attemptedArgCount;
|
return attemptedArgCount == rhs.attemptedArgCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GenericBoundsMismatch::GenericBoundsMismatch(const std::string_view genericName, TypeIds lowerBoundSet, TypeIds upperBoundSet)
|
||||||
|
: genericName(genericName)
|
||||||
|
, lowerBounds(lowerBoundSet.take())
|
||||||
|
, upperBounds(upperBoundSet.take())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericBoundsMismatch::operator==(const GenericBoundsMismatch& rhs) const
|
||||||
|
{
|
||||||
|
return genericName == rhs.genericName && lowerBounds == rhs.lowerBounds && upperBounds == rhs.upperBounds;
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString(const TypeError& error)
|
std::string toString(const TypeError& error)
|
||||||
{
|
{
|
||||||
return toString(error, TypeErrorToStringOptions{});
|
return toString(error, TypeErrorToStringOptions{});
|
||||||
|
@ -1523,6 +1556,13 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
||||||
else if constexpr (std::is_same_v<T, RecursiveRestraintViolation>)
|
else if constexpr (std::is_same_v<T, RecursiveRestraintViolation>)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, GenericBoundsMismatch>)
|
||||||
|
{
|
||||||
|
for (auto& lowerBound : e.lowerBounds)
|
||||||
|
lowerBound = clone(lowerBound);
|
||||||
|
for (auto& upperBound : e.upperBounds)
|
||||||
|
upperBound = clone(upperBound);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauAlwaysShowConstraintSolvingIncomplete)
|
LUAU_FASTFLAGVARIABLE(DebugLuauAlwaysShowConstraintSolvingIncomplete)
|
||||||
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1369,7 +1370,7 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
|
||||||
struct InternalTypeFinder : TypeOnceVisitor
|
struct InternalTypeFinder : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
InternalTypeFinder()
|
InternalTypeFinder()
|
||||||
: TypeOnceVisitor("InternalTypeFinder")
|
: TypeOnceVisitor("InternalTypeFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4)
|
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceSetTypeStackPressure)
|
LUAU_FASTFLAGVARIABLE(LuauReduceSetTypeStackPressure)
|
||||||
LUAU_FASTINTVARIABLE(LuauGenericCounterMaxDepth, 15)
|
LUAU_FASTINTVARIABLE(LuauGenericCounterMaxDepth, 15)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1406,7 +1407,7 @@ struct GenericCounter : TypeVisitor
|
||||||
bool hitLimits = false;
|
bool hitLimits = false;
|
||||||
|
|
||||||
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
||||||
: TypeVisitor("GenericCounter")
|
: TypeVisitor("GenericCounter", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, cachedTypes(cachedTypes)
|
, cachedTypes(cachedTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -21,7 +22,7 @@ struct InferPolarity : TypeVisitor
|
||||||
Polarity polarity = Polarity::Positive;
|
Polarity polarity = Polarity::Positive;
|
||||||
|
|
||||||
explicit InferPolarity(NotNull<TypeArena> arena, NotNull<Scope> scope)
|
explicit InferPolarity(NotNull<TypeArena> arena, NotNull<Scope> scope)
|
||||||
: TypeVisitor("InferPolarity")
|
: TypeVisitor("InferPolarity", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
{
|
{
|
||||||
|
|
|
@ -269,6 +269,24 @@ static void errorToString(std::ostream& stream, const T& err)
|
||||||
stream << "MultipleNonviableOverloads { attemptedArgCount = " << err.attemptedArgCount << " }";
|
stream << "MultipleNonviableOverloads { attemptedArgCount = " << err.attemptedArgCount << " }";
|
||||||
else if constexpr (std::is_same_v<T, RecursiveRestraintViolation>)
|
else if constexpr (std::is_same_v<T, RecursiveRestraintViolation>)
|
||||||
stream << "RecursiveRestraintViolation";
|
stream << "RecursiveRestraintViolation";
|
||||||
|
else if constexpr (std::is_same_v<T, GenericBoundsMismatch>)
|
||||||
|
{
|
||||||
|
stream << "GenericBoundsMismatch { genericName = " << std::string{err.genericName} << ", lowerBounds = [";
|
||||||
|
for (size_t i = 0; i < err.lowerBounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
stream << ", ";
|
||||||
|
stream << toString(err.lowerBounds[i]);
|
||||||
|
}
|
||||||
|
stream << "], upperBounds = [";
|
||||||
|
for (size_t i = 0; i < err.upperBounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
stream << ", ";
|
||||||
|
stream << toString(err.upperBounds[i]);
|
||||||
|
}
|
||||||
|
stream << "] }";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
LUAU_FASTFLAG(LuauLimitUnification)
|
LUAU_FASTFLAG(LuauLimitUnification)
|
||||||
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
|
LUAU_FASTFLAG(LuauVariadicAnyPackShouldBeErrorSuppressing)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -487,6 +489,13 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||||
argLocation = argExprs->at(argExprs->size() - 1)->location;
|
argLocation = argExprs->at(argExprs->size() - 1)->location;
|
||||||
|
|
||||||
// TODO extract location from the SubtypingResult path and argExprs
|
// TODO extract location from the SubtypingResult path and argExprs
|
||||||
|
if (FFlag::LuauVariadicAnyPackShouldBeErrorSuppressing)
|
||||||
|
{
|
||||||
|
auto errorSuppression = shouldSuppressErrors(normalizer, *failedSubPack).orElse(shouldSuppressErrors(normalizer, *failedSuperPack));
|
||||||
|
if (errorSuppression == ErrorSuppression::Suppress)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (reason.variance)
|
switch (reason.variance)
|
||||||
{
|
{
|
||||||
case SubtypingVariance::Covariant:
|
case SubtypingVariance::Covariant:
|
||||||
|
@ -505,6 +514,12 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
{
|
||||||
|
for (GenericBoundsMismatch& mismatch : sr.genericBoundsMismatches)
|
||||||
|
errors.emplace_back(fnExpr->location, std::move(mismatch));
|
||||||
|
}
|
||||||
|
|
||||||
return {Analysis::OverloadIsNonviable, std::move(errors)};
|
return {Analysis::OverloadIsNonviable, std::move(errors)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ struct Quantifier final : TypeOnceVisitor
|
||||||
bool seenMutableType = false;
|
bool seenMutableType = false;
|
||||||
|
|
||||||
explicit Quantifier(TypeLevel level)
|
explicit Quantifier(TypeLevel level)
|
||||||
: TypeOnceVisitor("Quantifier")
|
: TypeOnceVisitor("Quantifier", /* skipBoundTypes */ false)
|
||||||
, level(level)
|
, level(level)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ LUAU_FASTFLAGVARIABLE(LuauMissingFollowMappedGenericPacks)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingNegationsChecksNormalizationComplexity)
|
LUAU_FASTFLAGVARIABLE(LuauSubtypingNegationsChecksNormalizationComplexity)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAGVARIABLE(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -174,6 +175,8 @@ SubtypingResult& SubtypingResult::andAlso(const SubtypingResult& other)
|
||||||
normalizationTooComplex |= other.normalizationTooComplex;
|
normalizationTooComplex |= other.normalizationTooComplex;
|
||||||
isCacheable &= other.isCacheable;
|
isCacheable &= other.isCacheable;
|
||||||
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
|
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
genericBoundsMismatches.insert(genericBoundsMismatches.end(), other.genericBoundsMismatches.begin(), other.genericBoundsMismatches.end());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -196,6 +199,8 @@ SubtypingResult& SubtypingResult::orElse(const SubtypingResult& other)
|
||||||
normalizationTooComplex |= other.normalizationTooComplex;
|
normalizationTooComplex |= other.normalizationTooComplex;
|
||||||
isCacheable &= other.isCacheable;
|
isCacheable &= other.isCacheable;
|
||||||
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
|
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
genericBoundsMismatches.insert(genericBoundsMismatches.end(), other.genericBoundsMismatches.begin(), other.genericBoundsMismatches.end());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -710,8 +715,15 @@ SubtypingResult Subtyping::isSubtype(TypePackId subTp, TypePackId superTp, NotNu
|
||||||
{
|
{
|
||||||
// Bounds should have exactly one entry
|
// Bounds should have exactly one entry
|
||||||
LUAU_ASSERT(bounds->size() == 1);
|
LUAU_ASSERT(bounds->size() == 1);
|
||||||
if (!bounds->empty())
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
result.andAlso(checkGenericBounds(bounds->back(), env, scope));
|
{
|
||||||
|
if (bounds->empty())
|
||||||
|
continue;
|
||||||
|
if (const GenericType* gen = get<GenericType>(bg))
|
||||||
|
result.andAlso(checkGenericBounds(bounds->back(), env, scope, gen->name));
|
||||||
|
}
|
||||||
|
else if (!bounds->empty())
|
||||||
|
result.andAlso(checkGenericBounds_DEPRECATED(bounds->back(), env, scope));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2034,12 +2046,15 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
for (TypeId g : subFunction->generics)
|
for (TypeId g : subFunction->generics)
|
||||||
{
|
{
|
||||||
g = follow(g);
|
g = follow(g);
|
||||||
if (get<GenericType>(g))
|
if (const GenericType* gen = get<GenericType>(g))
|
||||||
{
|
{
|
||||||
auto bounds = env.mappedGenerics.find(g);
|
auto bounds = env.mappedGenerics.find(g);
|
||||||
LUAU_ASSERT(bounds && !bounds->empty());
|
LUAU_ASSERT(bounds && !bounds->empty());
|
||||||
// Check the bounds are valid
|
// Check the bounds are valid
|
||||||
result.andAlso(checkGenericBounds(bounds->back(), env, scope));
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
result.andAlso(checkGenericBounds(bounds->back(), env, scope, gen->name));
|
||||||
|
else
|
||||||
|
result.andAlso(checkGenericBounds_DEPRECATED(bounds->back(), env, scope));
|
||||||
|
|
||||||
bounds->pop_back();
|
bounds->pop_back();
|
||||||
}
|
}
|
||||||
|
@ -2524,9 +2539,16 @@ SubtypingResult Subtyping::trySemanticSubtyping(SubtypingEnvironment& env,
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtypingResult Subtyping::checkGenericBounds(const SubtypingEnvironment::GenericBounds& bounds, SubtypingEnvironment& env, NotNull<Scope> scope)
|
|
||||||
|
SubtypingResult Subtyping::checkGenericBounds(
|
||||||
|
const SubtypingEnvironment::GenericBounds& bounds,
|
||||||
|
SubtypingEnvironment& env,
|
||||||
|
NotNull<Scope> scope,
|
||||||
|
std::string_view genericName
|
||||||
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSubtypingGenericsDoesntUseVariance);
|
LUAU_ASSERT(FFlag::LuauSubtypingGenericsDoesntUseVariance);
|
||||||
|
LUAU_ASSERT(FFlag::LuauSubtypingReportGenericBoundMismatches);
|
||||||
|
|
||||||
SubtypingResult result{true};
|
SubtypingResult result{true};
|
||||||
|
|
||||||
|
@ -2607,6 +2629,101 @@ SubtypingResult Subtyping::checkGenericBounds(const SubtypingEnvironment::Generi
|
||||||
SubtypingResult boundsResult = isCovariantWith(boundsEnv, lowerBound, upperBound, scope);
|
SubtypingResult boundsResult = isCovariantWith(boundsEnv, lowerBound, upperBound, scope);
|
||||||
boundsResult.reasoning.clear();
|
boundsResult.reasoning.clear();
|
||||||
|
|
||||||
|
if (res == NormalizationResult::False || !boundsResult.isSubtype)
|
||||||
|
result.genericBoundsMismatches.emplace_back(genericName, bounds.lowerBound, bounds.upperBound);
|
||||||
|
|
||||||
|
result.andAlso(boundsResult);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtypingResult Subtyping::checkGenericBounds_DEPRECATED(
|
||||||
|
const SubtypingEnvironment::GenericBounds& bounds,
|
||||||
|
SubtypingEnvironment& env,
|
||||||
|
NotNull<Scope> scope
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauSubtypingGenericsDoesntUseVariance);
|
||||||
|
LUAU_ASSERT(!FFlag::LuauSubtypingReportGenericBoundMismatches);
|
||||||
|
|
||||||
|
SubtypingResult result{true};
|
||||||
|
|
||||||
|
const auto& [lb, ub] = bounds;
|
||||||
|
|
||||||
|
TypeIds lbTypes;
|
||||||
|
for (TypeId t : lb)
|
||||||
|
{
|
||||||
|
t = follow(t);
|
||||||
|
if (const auto mappedBounds = env.mappedGenerics.find(t))
|
||||||
|
{
|
||||||
|
if (mappedBounds->empty()) // If the generic is no longer in scope, we don't have any info about it
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto& [lowerBound, upperBound] = mappedBounds->back();
|
||||||
|
// We're populating the lower bounds, so we prioritize the upper bounds of a mapped generic
|
||||||
|
if (!upperBound.empty())
|
||||||
|
lbTypes.insert(upperBound.begin(), upperBound.end());
|
||||||
|
else if (!lowerBound.empty())
|
||||||
|
lbTypes.insert(lowerBound.begin(), lowerBound.end());
|
||||||
|
else
|
||||||
|
lbTypes.insert(builtinTypes->unknownType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lbTypes.insert(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeIds ubTypes;
|
||||||
|
for (TypeId t : ub)
|
||||||
|
{
|
||||||
|
t = follow(t);
|
||||||
|
if (const auto mappedBounds = env.mappedGenerics.find(t))
|
||||||
|
{
|
||||||
|
if (mappedBounds->empty()) // If the generic is no longer in scope, we don't have any info about it
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto& [lowerBound, upperBound] = mappedBounds->back();
|
||||||
|
// We're populating the upper bounds, so we prioritize the lower bounds of a mapped generic
|
||||||
|
if (!lowerBound.empty())
|
||||||
|
ubTypes.insert(lowerBound.begin(), lowerBound.end());
|
||||||
|
else if (!upperBound.empty())
|
||||||
|
ubTypes.insert(upperBound.begin(), upperBound.end());
|
||||||
|
else
|
||||||
|
ubTypes.insert(builtinTypes->unknownType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ubTypes.insert(t);
|
||||||
|
}
|
||||||
|
TypeId lowerBound = makeAggregateType<UnionType>(lbTypes.take(), builtinTypes->neverType);
|
||||||
|
TypeId upperBound = makeAggregateType<IntersectionType>(ubTypes.take(), builtinTypes->unknownType);
|
||||||
|
|
||||||
|
std::shared_ptr<const NormalizedType> nt = normalizer->normalize(upperBound);
|
||||||
|
// we say that the result is true if normalization failed because complex types are likely to be inhabited.
|
||||||
|
NormalizationResult res = nt ? normalizer->isInhabited(nt.get()) : NormalizationResult::True;
|
||||||
|
|
||||||
|
if (!nt || res == NormalizationResult::HitLimits)
|
||||||
|
result.normalizationTooComplex = true;
|
||||||
|
else if (res == NormalizationResult::False)
|
||||||
|
{
|
||||||
|
/* If the normalized upper bound we're mapping to a generic is
|
||||||
|
* uninhabited, then we must consider the subtyping relation not to
|
||||||
|
* hold.
|
||||||
|
*
|
||||||
|
* This happens eg in <T>() -> (T, T) <: () -> (string, number)
|
||||||
|
*
|
||||||
|
* T appears in covariant position and would have to be both string
|
||||||
|
* and number at once.
|
||||||
|
*
|
||||||
|
* No actual value is both a string and a number, so the test fails.
|
||||||
|
*
|
||||||
|
* TODO: We'll need to add explanitory context here.
|
||||||
|
*/
|
||||||
|
result.isSubtype = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtypingEnvironment boundsEnv;
|
||||||
|
boundsEnv.parent = &env;
|
||||||
|
SubtypingResult boundsResult = isCovariantWith(boundsEnv, lowerBound, upperBound, scope);
|
||||||
|
boundsResult.reasoning.clear();
|
||||||
result.andAlso(boundsResult);
|
result.andAlso(boundsResult);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -22,6 +22,7 @@ LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification)
|
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
||||||
|
@ -51,7 +52,7 @@ namespace
|
||||||
struct FindCyclicTypes final : TypeVisitor
|
struct FindCyclicTypes final : TypeVisitor
|
||||||
{
|
{
|
||||||
FindCyclicTypes()
|
FindCyclicTypes()
|
||||||
: TypeVisitor("FindCyclicTypes")
|
: TypeVisitor("FindCyclicTypes", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauOccursCheckInCommit)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -84,29 +86,6 @@ void TxnLog::concat(TxnLog rhs)
|
||||||
radioactive |= rhs.radioactive;
|
radioactive |= rhs.radioactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxnLog::concatAsIntersections(TxnLog rhs, NotNull<TypeArena> arena)
|
|
||||||
{
|
|
||||||
for (auto& [ty, rightRep] : rhs.typeVarChanges)
|
|
||||||
{
|
|
||||||
if (rightRep->dead)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (auto leftRep = typeVarChanges.find(ty); leftRep && !(*leftRep)->dead)
|
|
||||||
{
|
|
||||||
TypeId leftTy = arena->addType((*leftRep)->pending.clone());
|
|
||||||
TypeId rightTy = arena->addType(rightRep->pending.clone());
|
|
||||||
typeVarChanges[ty]->pending.ty = IntersectionType{{leftTy, rightTy}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
typeVarChanges[ty] = std::move(rightRep);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& [tp, rep] : rhs.typePackChanges)
|
|
||||||
typePackChanges[tp] = std::move(rep);
|
|
||||||
|
|
||||||
radioactive |= rhs.radioactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TxnLog::concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena)
|
void TxnLog::concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -154,7 +133,7 @@ void TxnLog::concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena)
|
||||||
// leftTy has been bound to rightTy, but rightTy has also been bound
|
// leftTy has been bound to rightTy, but rightTy has also been bound
|
||||||
// to leftTy. We find the one that belongs to the more deeply nested
|
// to leftTy. We find the one that belongs to the more deeply nested
|
||||||
// scope and remove it from the log.
|
// scope and remove it from the log.
|
||||||
const bool discardLeft = useScopes ? subsumes(lf->scope, rf->scope) : lf->level.subsumes(rf->level);
|
const bool discardLeft = lf->level.subsumes(rf->level);
|
||||||
|
|
||||||
if (discardLeft)
|
if (discardLeft)
|
||||||
(*leftRep)->dead = true;
|
(*leftRep)->dead = true;
|
||||||
|
@ -188,6 +167,57 @@ void TxnLog::concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena)
|
||||||
radioactive |= rhs.radioactive;
|
radioactive |= rhs.radioactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Like follow(), but only takes a single step.
|
||||||
|
//
|
||||||
|
// This is potentailly performance sensitive, so we use nullptr rather than an
|
||||||
|
// optional<TypeId> for the return type here.
|
||||||
|
static TypeId followOnce(TxnLog& log, TypeId ty)
|
||||||
|
{
|
||||||
|
if (auto bound = log.get<BoundType>(ty))
|
||||||
|
return bound->boundTo;
|
||||||
|
if (auto tt = log.get<TableType>(ty))
|
||||||
|
return tt->boundTo.value_or(nullptr);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must take extra care not to replace a type with a BoundType to itself. We
|
||||||
|
// check each BoundType along the chain
|
||||||
|
//
|
||||||
|
// This function returns true if any of the bound types pointed at by 'needle'
|
||||||
|
// point at 'haystack'.
|
||||||
|
static bool occurs(TxnLog& log, TypeId needle, TypeId haystack)
|
||||||
|
{
|
||||||
|
TypeId tortoise = needle;
|
||||||
|
TypeId hare = needle;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (tortoise == haystack)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
TypeId g = followOnce(log, tortoise);
|
||||||
|
if (!g)
|
||||||
|
return false;
|
||||||
|
tortoise = g;
|
||||||
|
|
||||||
|
// Cycle detection: The hare steps twice for each step that the tortoise takes.
|
||||||
|
// If ever the two meet, it can only be because the track is cyclic.
|
||||||
|
// When we hit the end of the chain, hare becomes nullptr.
|
||||||
|
if (hare)
|
||||||
|
{
|
||||||
|
hare = followOnce(log, hare);
|
||||||
|
if (hare)
|
||||||
|
{
|
||||||
|
hare = followOnce(log, hare);
|
||||||
|
|
||||||
|
if (hare == tortoise)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TxnLog::commit()
|
void TxnLog::commit()
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!radioactive);
|
LUAU_ASSERT(!radioactive);
|
||||||
|
@ -195,7 +225,17 @@ void TxnLog::commit()
|
||||||
for (auto& [ty, rep] : typeVarChanges)
|
for (auto& [ty, rep] : typeVarChanges)
|
||||||
{
|
{
|
||||||
if (!rep->dead)
|
if (!rep->dead)
|
||||||
asMutable(ty)->reassign(rep.get()->pending);
|
{
|
||||||
|
const TypeId unfollowed = &rep.get()->pending;
|
||||||
|
|
||||||
|
if (FFlag::LuauOccursCheckInCommit)
|
||||||
|
{
|
||||||
|
if (!occurs(*this, unfollowed, ty))
|
||||||
|
asMutable(ty)->reassign(*unfollowed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
asMutable(ty)->reassign(*unfollowed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [tp, rep] : typePackChanges)
|
for (auto& [tp, rep] : typePackChanges)
|
||||||
|
@ -474,16 +514,4 @@ TypePackId TxnLog::follow(TypePackId tp) const
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<TypeId>, std::vector<TypePackId>> TxnLog::getChanges() const
|
|
||||||
{
|
|
||||||
std::pair<std::vector<TypeId>, std::vector<TypePackId>> result;
|
|
||||||
|
|
||||||
for (const auto& [typeId, _newState] : typeVarChanges)
|
|
||||||
result.first.push_back(typeId);
|
|
||||||
for (const auto& [typePackId, _newState] : typePackChanges)
|
|
||||||
result.second.push_back(typePackId);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSuppressErrorsForMultipleNonviableOverloads)
|
LUAU_FASTFLAGVARIABLE(LuauSuppressErrorsForMultipleNonviableOverloads)
|
||||||
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
||||||
|
@ -40,6 +39,8 @@ LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauNameConstraintRestrictRecursiveTypes)
|
LUAU_FASTFLAG(LuauNameConstraintRestrictRecursiveTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIceLess)
|
LUAU_FASTFLAGVARIABLE(LuauIceLess)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAllowMixedTables)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -162,7 +163,7 @@ struct TypeFunctionFinder : TypeOnceVisitor
|
||||||
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
||||||
|
|
||||||
TypeFunctionFinder()
|
TypeFunctionFinder()
|
||||||
: TypeOnceVisitor("TypeFunctionFinder")
|
: TypeOnceVisitor("TypeFunctionFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor
|
||||||
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
||||||
|
|
||||||
explicit InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
|
explicit InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
|
||||||
: TypeOnceVisitor("InternalTypeFunctionFinder")
|
: TypeOnceVisitor("InternalTypeFunctionFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
TypeFunctionFinder f;
|
TypeFunctionFinder f;
|
||||||
for (TypeId fn : declStack)
|
for (TypeId fn : declStack)
|
||||||
|
@ -1545,8 +1546,6 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
||||||
argExprs.push_back(indexExpr->expr);
|
argExprs.push_back(indexExpr->expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
{
|
|
||||||
// FIXME: Similar to bidirectional inference prior, this does not support
|
// FIXME: Similar to bidirectional inference prior, this does not support
|
||||||
// overloaded functions nor generic types (yet).
|
// overloaded functions nor generic types (yet).
|
||||||
if (auto fty = get<FunctionType>(fnTy); fty && fty->generics.empty() && fty->genericPacks.empty() && call->args.size > 0)
|
if (auto fty = get<FunctionType>(fnTy); fty && fty->generics.empty() && fty->genericPacks.empty() && call->args.size > 0)
|
||||||
|
@ -1619,31 +1618,6 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
||||||
args.head.push_back(builtinTypes->anyType);
|
args.head.push_back(builtinTypes->anyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < call->args.size; ++i)
|
|
||||||
{
|
|
||||||
AstExpr* arg = call->args.data[i];
|
|
||||||
argExprs.push_back(arg);
|
|
||||||
TypeId* argTy = module->astTypes.find(arg);
|
|
||||||
if (argTy)
|
|
||||||
args.head.push_back(*argTy);
|
|
||||||
else if (i == call->args.size - 1)
|
|
||||||
{
|
|
||||||
if (auto argTail = module->astTypePacks.find(arg))
|
|
||||||
{
|
|
||||||
auto [head, tail] = flatten(*argTail);
|
|
||||||
args.head.insert(args.head.end(), head.begin(), head.end());
|
|
||||||
args.tail = tail;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
args.tail = builtinTypes->anyTypePack;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
args.head.push_back(builtinTypes->anyType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId argsTp = module->internalTypes.addTypePack(args);
|
TypePackId argsTp = module->internalTypes.addTypePack(args);
|
||||||
if (auto ftv = get<FunctionType>(follow(*originalCallTy)))
|
if (auto ftv = get<FunctionType>(follow(*originalCallTy)))
|
||||||
|
@ -3157,9 +3131,18 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
|
||||||
if (expectedTableType->indexer)
|
if (expectedTableType->indexer)
|
||||||
{
|
{
|
||||||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||||
auto result = subtyping->isSubtype(expectedTableType->indexer->indexType, builtinTypes->numberType, scope);
|
|
||||||
|
if (FFlag::LuauAllowMixedTables)
|
||||||
|
{
|
||||||
|
auto result = subtyping->isSubtype(/* subTy */ builtinTypes->numberType, /* superTy */ expectedTableType->indexer->indexType, scope);
|
||||||
|
isArrayLike = result.isSubtype || isErrorSuppressing(expr->location, expectedTableType->indexer->indexType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = subtyping->isSubtype(/* subTy */ expectedTableType->indexer->indexType, /* superTy */ builtinTypes->numberType, scope);
|
||||||
isArrayLike = result.isSubtype;
|
isArrayLike = result.isSubtype;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isSubtype = true;
|
bool isSubtype = true;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -53,7 +54,7 @@ struct InstanceCollector : TypeOnceVisitor
|
||||||
|
|
||||||
|
|
||||||
InstanceCollector()
|
InstanceCollector()
|
||||||
: TypeOnceVisitor("InstanceCollector")
|
: TypeOnceVisitor("InstanceCollector", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ struct UnscopedGenericFinder : TypeOnceVisitor
|
||||||
bool foundUnscoped = false;
|
bool foundUnscoped = false;
|
||||||
|
|
||||||
UnscopedGenericFinder()
|
UnscopedGenericFinder()
|
||||||
: TypeOnceVisitor("UnscopedGenericFinder")
|
: TypeOnceVisitor("UnscopedGenericFinder", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
struct InstanceCollector2 : TypeOnceVisitor
|
struct InstanceCollector2 : TypeOnceVisitor
|
||||||
|
@ -25,7 +27,7 @@ struct InstanceCollector2 : TypeOnceVisitor
|
||||||
DenseHashSet<TypeId> instanceArguments{nullptr};
|
DenseHashSet<TypeId> instanceArguments{nullptr};
|
||||||
|
|
||||||
InstanceCollector2()
|
InstanceCollector2()
|
||||||
: TypeOnceVisitor("InstanceCollector2")
|
: TypeOnceVisitor("InstanceCollector2", FFlag::LuauExplicitSkipBoundTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
#include "Luau/TypePack.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTidyTypeUtils)
|
LUAU_FASTFLAGVARIABLE(LuauTidyTypeUtils)
|
||||||
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauVariadicAnyPackShouldBeErrorSuppressing)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -502,6 +504,17 @@ ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
||||||
|
|
||||||
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypePackId tp)
|
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypePackId tp)
|
||||||
{
|
{
|
||||||
|
// Flatten t where t = ...any will produce a type pack [ {}, t]
|
||||||
|
// which trivially fails the tail check below, which is why we need to special case here
|
||||||
|
if (FFlag::LuauVariadicAnyPackShouldBeErrorSuppressing)
|
||||||
|
{
|
||||||
|
if (auto tpId = get<VariadicTypePack>(follow(tp)))
|
||||||
|
{
|
||||||
|
if (get<AnyType>(follow(tpId->ty)))
|
||||||
|
return ErrorSuppression::Suppress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto [tys, tail] = flatten(tp);
|
auto [tys, tail] = flatten(tp);
|
||||||
|
|
||||||
// check the head, one type at a time
|
// check the head, one type at a time
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct PromoteTypeLevels final : TypeOnceVisitor
|
||||||
TypeLevel minLevel;
|
TypeLevel minLevel;
|
||||||
|
|
||||||
PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel)
|
PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel)
|
||||||
: TypeOnceVisitor("PromoteTypeLevels")
|
: TypeOnceVisitor("PromoteTypeLevels", /* skipBoundTypes */ false)
|
||||||
, log(log)
|
, log(log)
|
||||||
, typeArena(typeArena)
|
, typeArena(typeArena)
|
||||||
, minLevel(minLevel)
|
, minLevel(minLevel)
|
||||||
|
@ -146,7 +146,7 @@ void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLev
|
||||||
struct SkipCacheForType final : TypeOnceVisitor
|
struct SkipCacheForType final : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
SkipCacheForType(const DenseHashMap<TypeId, bool>& skipCacheForType, const TypeArena* typeArena)
|
SkipCacheForType(const DenseHashMap<TypeId, bool>& skipCacheForType, const TypeArena* typeArena)
|
||||||
: TypeOnceVisitor("SkipCacheForType")
|
: TypeOnceVisitor("SkipCacheForType", /* skipBoundTypes */ false)
|
||||||
, skipCacheForType(skipCacheForType)
|
, skipCacheForType(skipCacheForType)
|
||||||
, typeArena(typeArena)
|
, typeArena(typeArena)
|
||||||
{
|
{
|
||||||
|
@ -508,49 +508,6 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideousFixMeGenericsAreActuallyFree)
|
|
||||||
{
|
|
||||||
auto superGeneric = log.getMutable<GenericType>(superTy);
|
|
||||||
auto subGeneric = log.getMutable<GenericType>(subTy);
|
|
||||||
|
|
||||||
if (superGeneric && subGeneric && subsumes(superGeneric, subGeneric))
|
|
||||||
{
|
|
||||||
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
|
||||||
log.replace(subTy, BoundType(superTy));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (superGeneric && subGeneric)
|
|
||||||
{
|
|
||||||
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
|
||||||
log.replace(superTy, BoundType(subTy));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (superGeneric)
|
|
||||||
{
|
|
||||||
if (!occursCheck(superTy, subTy, /* reversed = */ true))
|
|
||||||
{
|
|
||||||
Widen widen{types, builtinTypes};
|
|
||||||
log.replace(superTy, BoundType(widen(subTy)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (subGeneric)
|
|
||||||
{
|
|
||||||
// Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
|
|
||||||
// But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors.
|
|
||||||
if (log.get<UnknownType>(superTy))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!occursCheck(subTy, superTy, /* reversed = */ false))
|
|
||||||
log.replace(subTy, BoundType(superTy));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log.get<AnyType>(superTy))
|
if (log.get<AnyType>(superTy))
|
||||||
return tryUnifyWithAny(subTy, builtinTypes->anyType);
|
return tryUnifyWithAny(subTy, builtinTypes->anyType);
|
||||||
|
|
||||||
|
@ -1510,21 +1467,6 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
|
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (hideousFixMeGenericsAreActuallyFree && log.getMutable<GenericTypePack>(superTp))
|
|
||||||
{
|
|
||||||
if (!occursCheck(superTp, subTp, /* reversed = */ true))
|
|
||||||
{
|
|
||||||
Widen widen{types, builtinTypes};
|
|
||||||
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (hideousFixMeGenericsAreActuallyFree && log.getMutable<GenericTypePack>(subTp))
|
|
||||||
{
|
|
||||||
if (!occursCheck(subTp, superTp, /* reversed = */ false))
|
|
||||||
{
|
|
||||||
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (log.getMutable<ErrorTypePack>(superTp))
|
else if (log.getMutable<ErrorTypePack>(superTp))
|
||||||
tryUnifyWithAny(subTp, superTp);
|
tryUnifyWithAny(subTp, superTp);
|
||||||
else if (log.getMutable<ErrorTypePack>(subTp))
|
else if (log.getMutable<ErrorTypePack>(subTp))
|
||||||
|
@ -2557,12 +2499,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever
|
||||||
tryUnify_(vtp->ty, variadicTy);
|
tryUnify_(vtp->ty, variadicTy);
|
||||||
}
|
}
|
||||||
else if (get<GenericTypePack>(tail))
|
else if (get<GenericTypePack>(tail))
|
||||||
{
|
|
||||||
if (!hideousFixMeGenericsAreActuallyFree)
|
|
||||||
reportError(location, GenericError{"Cannot unify variadic and generic packs"});
|
reportError(location, GenericError{"Cannot unify variadic and generic packs"});
|
||||||
else
|
|
||||||
log.replace(tail, BoundTypePack{superTp});
|
|
||||||
}
|
|
||||||
else if (get<ErrorTypePack>(tail))
|
else if (get<ErrorTypePack>(tail))
|
||||||
{
|
{
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
|
@ -2755,13 +2692,13 @@ bool Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId hays
|
||||||
if (log.getMutable<ErrorType>(needle))
|
if (log.getMutable<ErrorType>(needle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!log.getMutable<FreeType>(needle) && !(hideousFixMeGenericsAreActuallyFree && log.is<GenericType>(needle)))
|
if (!log.getMutable<FreeType>(needle))
|
||||||
ice("Expected needle to be free");
|
ice("Expected needle to be free");
|
||||||
|
|
||||||
if (needle == haystack)
|
if (needle == haystack)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (log.getMutable<FreeType>(haystack) || (hideousFixMeGenericsAreActuallyFree && log.is<GenericType>(haystack)))
|
if (log.getMutable<FreeType>(haystack))
|
||||||
return false;
|
return false;
|
||||||
else if (auto a = log.getMutable<UnionType>(haystack))
|
else if (auto a = log.getMutable<UnionType>(haystack))
|
||||||
{
|
{
|
||||||
|
@ -2805,7 +2742,7 @@ bool Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
|
||||||
if (log.getMutable<ErrorTypePack>(needle))
|
if (log.getMutable<ErrorTypePack>(needle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!log.getMutable<FreeTypePack>(needle) && !(hideousFixMeGenericsAreActuallyFree && log.is<GenericTypePack>(needle)))
|
if (!log.getMutable<FreeTypePack>(needle))
|
||||||
ice("Expected needle pack to be free");
|
ice("Expected needle pack to be free");
|
||||||
|
|
||||||
RecursionLimiter _ra("Unifier::occursCheck", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
|
RecursionLimiter _ra("Unifier::occursCheck", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
|
||||||
|
|
|
@ -339,9 +339,28 @@ public:
|
||||||
|
|
||||||
enum QuoteStyle
|
enum QuoteStyle
|
||||||
{
|
{
|
||||||
|
// A string created using double quotes or an interpolated string,
|
||||||
|
// as in:
|
||||||
|
//
|
||||||
|
// "foo", `My name is {protagonist}! / And I'm {antagonist}!`
|
||||||
|
//
|
||||||
QuotedSimple,
|
QuotedSimple,
|
||||||
|
// A string created using single quotes, as in:
|
||||||
|
//
|
||||||
|
// 'bar'
|
||||||
|
//
|
||||||
|
QuotedSingle,
|
||||||
|
// A string created using `[[ ... ]]` as in:
|
||||||
|
//
|
||||||
|
// [[ Gee, this sure is a long string.
|
||||||
|
// it even has a new line in it! ]]
|
||||||
|
//
|
||||||
QuotedRaw,
|
QuotedRaw,
|
||||||
Unquoted
|
// A "string" in the context of a table literal, as in:
|
||||||
|
//
|
||||||
|
// { foo = 42 } -- `foo` here is a "constant string"
|
||||||
|
//
|
||||||
|
Unquoted,
|
||||||
};
|
};
|
||||||
|
|
||||||
AstExprConstantString(const Location& location, const AstArray<char>& value, QuoteStyle quoteStyle);
|
AstExprConstantString(const Location& location, const AstArray<char>& value, QuoteStyle quoteStyle);
|
||||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseIncompleteInterpStringsWithLocation)
|
LUAU_FASTFLAGVARIABLE(LuauParseIncompleteInterpStringsWithLocation)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParametrizedAttributeSyntax)
|
LUAU_FASTFLAGVARIABLE(LuauParametrizedAttributeSyntax)
|
||||||
|
LUAU_FASTFLAGVARIABLE(DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
|
||||||
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
||||||
bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false;
|
bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false;
|
||||||
|
@ -3847,6 +3848,27 @@ AstExpr* Parser::parseString()
|
||||||
Location location = lexer.current().location;
|
Location location = lexer.current().location;
|
||||||
|
|
||||||
AstExprConstantString::QuoteStyle style;
|
AstExprConstantString::QuoteStyle style;
|
||||||
|
if (FFlag::DebugLuauStringSingletonBasedOnQuotes)
|
||||||
|
{
|
||||||
|
switch (lexer.current().type)
|
||||||
|
{
|
||||||
|
case Lexeme::InterpStringSimple:
|
||||||
|
style = AstExprConstantString::QuotedSimple;
|
||||||
|
break;
|
||||||
|
case Lexeme::RawString:
|
||||||
|
style = AstExprConstantString::QuotedRaw;
|
||||||
|
break;
|
||||||
|
case Lexeme::QuotedString:
|
||||||
|
style = lexer.current().getQuoteStyle() == Lexeme::QuoteStyle::Single
|
||||||
|
? AstExprConstantString::QuotedSingle
|
||||||
|
: AstExprConstantString::QuotedSimple;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LUAU_ASSERT(false && "Invalid string type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
switch (lexer.current().type)
|
switch (lexer.current().type)
|
||||||
{
|
{
|
||||||
case Lexeme::QuotedString:
|
case Lexeme::QuotedString:
|
||||||
|
@ -3859,6 +3881,7 @@ AstExpr* Parser::parseString()
|
||||||
default:
|
default:
|
||||||
LUAU_ASSERT(false && "Invalid string type");
|
LUAU_ASSERT(false && "Invalid string type");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CstExprConstantString::QuoteStyle fullStyle;
|
CstExprConstantString::QuoteStyle fullStyle;
|
||||||
unsigned int blockDepth;
|
unsigned int blockDepth;
|
||||||
|
|
|
@ -52,6 +52,35 @@ enum class ConditionA64
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns a condition that for 'a op b' will result in 'b op a'
|
||||||
|
// Only a subset of conditions is allowed
|
||||||
|
inline ConditionA64 getInverseCondition(ConditionA64 cond)
|
||||||
|
{
|
||||||
|
switch (cond)
|
||||||
|
{
|
||||||
|
case ConditionA64::Equal:
|
||||||
|
return ConditionA64::Equal;
|
||||||
|
case ConditionA64::NotEqual:
|
||||||
|
return ConditionA64::NotEqual;
|
||||||
|
case ConditionA64::UnsignedGreater:
|
||||||
|
return ConditionA64::CarryClear;
|
||||||
|
case ConditionA64::UnsignedLessEqual:
|
||||||
|
return ConditionA64::CarrySet;
|
||||||
|
case ConditionA64::GreaterEqual:
|
||||||
|
return ConditionA64::LessEqual;
|
||||||
|
case ConditionA64::Less:
|
||||||
|
return ConditionA64::Greater;
|
||||||
|
case ConditionA64::Greater:
|
||||||
|
return ConditionA64::Less;
|
||||||
|
case ConditionA64::LessEqual:
|
||||||
|
return ConditionA64::GreaterEqual;
|
||||||
|
default:
|
||||||
|
CODEGEN_ASSERT(!"invalid ConditionA64 value for getInverseCondition");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConditionA64::Count;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace A64
|
} // namespace A64
|
||||||
} // namespace CodeGen
|
} // namespace CodeGen
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -45,7 +45,8 @@ enum class ConditionX64 : uint8_t
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
inline ConditionX64 getReverseCondition(ConditionX64 cond)
|
// Returns a condition that for 'a op b' will result in '!(a op b)'
|
||||||
|
inline ConditionX64 getNegatedCondition(ConditionX64 cond)
|
||||||
{
|
{
|
||||||
switch (cond)
|
switch (cond)
|
||||||
{
|
{
|
||||||
|
@ -108,5 +109,54 @@ inline ConditionX64 getReverseCondition(ConditionX64 cond)
|
||||||
return ConditionX64::Count;
|
return ConditionX64::Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a condition that for 'a op b' will result in 'b op a'
|
||||||
|
// Only a subset of conditions is allowed
|
||||||
|
inline ConditionX64 getInverseCondition(ConditionX64 cond)
|
||||||
|
{
|
||||||
|
switch (cond)
|
||||||
|
{
|
||||||
|
case ConditionX64::Below:
|
||||||
|
return ConditionX64::Above;
|
||||||
|
case ConditionX64::BelowEqual:
|
||||||
|
return ConditionX64::AboveEqual;
|
||||||
|
case ConditionX64::Above:
|
||||||
|
return ConditionX64::Above;
|
||||||
|
case ConditionX64::AboveEqual:
|
||||||
|
return ConditionX64::BelowEqual;
|
||||||
|
case ConditionX64::Equal:
|
||||||
|
return ConditionX64::Equal;
|
||||||
|
case ConditionX64::Less:
|
||||||
|
return ConditionX64::Greater;
|
||||||
|
case ConditionX64::LessEqual:
|
||||||
|
return ConditionX64::GreaterEqual;
|
||||||
|
case ConditionX64::Greater:
|
||||||
|
return ConditionX64::Less;
|
||||||
|
case ConditionX64::GreaterEqual:
|
||||||
|
return ConditionX64::LessEqual;
|
||||||
|
case ConditionX64::NotBelow:
|
||||||
|
return ConditionX64::NotAbove;
|
||||||
|
case ConditionX64::NotBelowEqual:
|
||||||
|
return ConditionX64::NotAboveEqual;
|
||||||
|
case ConditionX64::NotAbove:
|
||||||
|
return ConditionX64::NotBelow;
|
||||||
|
case ConditionX64::NotAboveEqual:
|
||||||
|
return ConditionX64::NotBelowEqual;
|
||||||
|
case ConditionX64::NotEqual:
|
||||||
|
return ConditionX64::NotEqual;
|
||||||
|
case ConditionX64::NotLess:
|
||||||
|
return ConditionX64::NotGreater;
|
||||||
|
case ConditionX64::NotLessEqual:
|
||||||
|
return ConditionX64::NotGreaterEqual;
|
||||||
|
case ConditionX64::NotGreater:
|
||||||
|
return ConditionX64::NotLess;
|
||||||
|
case ConditionX64::NotGreaterEqual:
|
||||||
|
return ConditionX64::NotLessEqual;
|
||||||
|
default:
|
||||||
|
CODEGEN_ASSERT(!"invalid ConditionX64 value for getInverseCondition");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConditionX64::Count;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CodeGen
|
} // namespace CodeGen
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -217,6 +217,11 @@ enum class IrCmd : uint8_t
|
||||||
// C: condition
|
// C: condition
|
||||||
CMP_ANY,
|
CMP_ANY,
|
||||||
|
|
||||||
|
// Perform a comparison of two integer numbers. Result is an integer register containing 0 or 1
|
||||||
|
// A, B: int
|
||||||
|
// C: condition
|
||||||
|
CMP_INT,
|
||||||
|
|
||||||
// Unconditional jump
|
// Unconditional jump
|
||||||
// A: block/vmexit/undef
|
// A: block/vmexit/undef
|
||||||
JUMP,
|
JUMP,
|
||||||
|
|
|
@ -112,6 +112,7 @@ inline bool hasResult(IrCmd cmd)
|
||||||
case IrCmd::UNM_VEC:
|
case IrCmd::UNM_VEC:
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
case IrCmd::TABLE_LEN:
|
case IrCmd::TABLE_LEN:
|
||||||
case IrCmd::TABLE_SETNUM:
|
case IrCmd::TABLE_SETNUM:
|
||||||
case IrCmd::STRING_LEN:
|
case IrCmd::STRING_LEN:
|
||||||
|
|
|
@ -548,6 +548,12 @@ static void applyBuiltinCall(LuauBuiltinFunction bfid, BytecodeTypes& types)
|
||||||
types.b = LBC_TYPE_VECTOR;
|
types.b = LBC_TYPE_VECTOR;
|
||||||
types.c = LBC_TYPE_VECTOR; // We can mark optional arguments
|
types.c = LBC_TYPE_VECTOR; // We can mark optional arguments
|
||||||
break;
|
break;
|
||||||
|
case LBF_VECTOR_LERP:
|
||||||
|
types.result = LBC_TYPE_VECTOR;
|
||||||
|
types.a = LBC_TYPE_VECTOR;
|
||||||
|
types.b = LBC_TYPE_VECTOR;
|
||||||
|
types.c = LBC_TYPE_NUMBER;
|
||||||
|
break;
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
types.result = LBC_TYPE_NUMBER;
|
types.result = LBC_TYPE_NUMBER;
|
||||||
types.a = LBC_TYPE_NUMBER;
|
types.a = LBC_TYPE_NUMBER;
|
||||||
|
|
|
@ -185,6 +185,8 @@ const char* getCmdName(IrCmd cmd)
|
||||||
return "NOT_ANY";
|
return "NOT_ANY";
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
return "CMP_ANY";
|
return "CMP_ANY";
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
|
return "CMP_INT";
|
||||||
case IrCmd::JUMP:
|
case IrCmd::JUMP:
|
||||||
return "JUMP";
|
return "JUMP";
|
||||||
case IrCmd::JUMP_IF_TRUTHY:
|
case IrCmd::JUMP_IF_TRUTHY:
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -798,6 +800,30 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(FFlag::LuauCodeGenDirectBtest);
|
||||||
|
|
||||||
|
inst.regA64 = regs.allocReuse(KindA64::w, index, {inst.a, inst.b});
|
||||||
|
|
||||||
|
IrCondition cond = conditionOp(inst.c);
|
||||||
|
|
||||||
|
if (inst.a.kind == IrOpKind::Constant)
|
||||||
|
{
|
||||||
|
build.cmp(regOp(inst.b), intOp(inst.a));
|
||||||
|
build.cset(inst.regA64, getInverseCondition(getConditionInt(cond)));
|
||||||
|
}
|
||||||
|
else if (inst.a.kind == IrOpKind::Inst)
|
||||||
|
{
|
||||||
|
build.cmp(regOp(inst.a), intOp(inst.b));
|
||||||
|
build.cset(inst.regA64, getConditionInt(cond));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(!"Unsupported instruction form");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
{
|
{
|
||||||
IrCondition cond = conditionOp(inst.c);
|
IrCondition cond = conditionOp(inst.c);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -759,6 +761,34 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
build.setLabel(exit);
|
build.setLabel(exit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(FFlag::LuauCodeGenDirectBtest);
|
||||||
|
|
||||||
|
// Cannot reuse operand registers as a target because we have to modify it before the comparison
|
||||||
|
inst.regX64 = regs.allocReg(SizeX64::dword, index);
|
||||||
|
|
||||||
|
// We are going to operate on byte register, those do not clear high bits on write
|
||||||
|
build.xor_(inst.regX64, inst.regX64);
|
||||||
|
|
||||||
|
IrCondition cond = conditionOp(inst.c);
|
||||||
|
|
||||||
|
if (inst.a.kind == IrOpKind::Constant)
|
||||||
|
{
|
||||||
|
build.cmp(regOp(inst.b), intOp(inst.a));
|
||||||
|
build.setcc(getInverseCondition(getConditionInt(cond)), byteReg(inst.regX64));
|
||||||
|
}
|
||||||
|
else if (inst.a.kind == IrOpKind::Inst)
|
||||||
|
{
|
||||||
|
build.cmp(regOp(inst.a), intOp(inst.b));
|
||||||
|
build.setcc(getConditionInt(cond), byteReg(inst.regX64));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(!"Unsupported instruction form");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
{
|
{
|
||||||
IrCondition cond = conditionOp(inst.c);
|
IrCondition cond = conditionOp(inst.c);
|
||||||
|
@ -2246,7 +2276,7 @@ void IrLoweringX64::jumpOrAbortOnUndef(ConditionX64 cond, IrOp target, const IrB
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
build.jcc(getReverseCondition(cond), label);
|
build.jcc(getNegatedCondition(cond), label);
|
||||||
build.ud2();
|
build.ud2();
|
||||||
build.setLabel(label);
|
build.setLabel(label);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
// TODO: when nresults is less than our actual result count, we can skip computing/writing unused results
|
// TODO: when nresults is less than our actual result count, we can skip computing/writing unused results
|
||||||
|
|
||||||
static const int kMinMaxUnrolledParams = 5;
|
static const int kMinMaxUnrolledParams = 5;
|
||||||
|
@ -410,6 +412,14 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (btest)
|
if (btest)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauCodeGenDirectBtest)
|
||||||
|
{
|
||||||
|
IrOp value = build.inst(IrCmd::CMP_INT, res, build.constInt(0), build.cond(IrCondition::NotEqual));
|
||||||
|
build.inst(IrCmd::STORE_INT, build.vmReg(ra), value);
|
||||||
|
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TBOOLEAN));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
IrOp falsey = build.block(IrBlockKind::Internal);
|
IrOp falsey = build.block(IrBlockKind::Internal);
|
||||||
IrOp truthy = build.block(IrBlockKind::Internal);
|
IrOp truthy = build.block(IrBlockKind::Internal);
|
||||||
|
@ -427,6 +437,7 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp(
|
||||||
build.beginBlock(exit);
|
build.beginBlock(exit);
|
||||||
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TBOOLEAN));
|
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TBOOLEAN));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IrOp value = build.inst(IrCmd::UINT_TO_NUM, res);
|
IrOp value = build.inst(IrCmd::UINT_TO_NUM, res);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -193,6 +195,7 @@ IrValueKind getCmdValueKind(IrCmd cmd)
|
||||||
return IrValueKind::Double;
|
return IrValueKind::Double;
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
return IrValueKind::Int;
|
return IrValueKind::Int;
|
||||||
case IrCmd::JUMP:
|
case IrCmd::JUMP:
|
||||||
case IrCmd::JUMP_IF_TRUTHY:
|
case IrCmd::JUMP_IF_TRUTHY:
|
||||||
|
@ -790,6 +793,17 @@ void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint3
|
||||||
substitute(function, inst, build.constInt(function.intOp(inst.b) == 1 ? 0 : 1));
|
substitute(function, inst, build.constInt(function.intOp(inst.b) == 1 ? 0 : 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
|
CODEGEN_ASSERT(FFlag::LuauCodeGenDirectBtest);
|
||||||
|
|
||||||
|
if (inst.a.kind == IrOpKind::Constant && inst.b.kind == IrOpKind::Constant)
|
||||||
|
{
|
||||||
|
if (compare(function.intOp(inst.a), function.intOp(inst.b), conditionOp(inst.c)))
|
||||||
|
substitute(function, inst, build.constInt(1));
|
||||||
|
else
|
||||||
|
substitute(function, inst, build.constInt(0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case IrCmd::JUMP_EQ_TAG:
|
case IrCmd::JUMP_EQ_TAG:
|
||||||
if (inst.a.kind == IrOpKind::Constant && inst.b.kind == IrOpKind::Constant)
|
if (inst.a.kind == IrOpKind::Constant && inst.b.kind == IrOpKind::Constant)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ 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(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -607,6 +608,7 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
|
||||||
case LBF_VECTOR_CLAMP:
|
case LBF_VECTOR_CLAMP:
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
|
case LBF_VECTOR_LERP:
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
break;
|
break;
|
||||||
case LBF_TABLE_INSERT:
|
case LBF_TABLE_INSERT:
|
||||||
|
@ -1326,6 +1328,9 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
case IrCmd::NOT_ANY:
|
case IrCmd::NOT_ANY:
|
||||||
state.substituteOrRecord(inst, index);
|
state.substituteOrRecord(inst, index);
|
||||||
break;
|
break;
|
||||||
|
case IrCmd::CMP_INT:
|
||||||
|
CODEGEN_ASSERT(FFlag::LuauCodeGenDirectBtest);
|
||||||
|
break;
|
||||||
case IrCmd::CMP_ANY:
|
case IrCmd::CMP_ANY:
|
||||||
state.invalidateUserCall();
|
state.invalidateUserCall();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -618,6 +618,8 @@ enum LuauBuiltinFunction
|
||||||
|
|
||||||
// math.lerp
|
// math.lerp
|
||||||
LBF_MATH_LERP,
|
LBF_MATH_LERP,
|
||||||
|
|
||||||
|
LBF_VECTOR_LERP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capture type, used in LOP_CAPTURE
|
// Capture type, used in LOP_CAPTURE
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCompileVectorLerp)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -251,6 +253,8 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
||||||
return LBF_VECTOR_MIN;
|
return LBF_VECTOR_MIN;
|
||||||
if (builtin.method == "max")
|
if (builtin.method == "max")
|
||||||
return LBF_VECTOR_MAX;
|
return LBF_VECTOR_MAX;
|
||||||
|
if (FFlag::LuauCompileVectorLerp && builtin.method == "lerp")
|
||||||
|
return LBF_VECTOR_LERP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.vectorCtor)
|
if (options.vectorCtor)
|
||||||
|
@ -552,6 +556,8 @@ BuiltinInfo getBuiltinInfo(int bfid)
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
return {-1, 1}; // variadic
|
return {-1, 1}; // variadic
|
||||||
|
case LBF_VECTOR_LERP:
|
||||||
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
||||||
|
|
||||||
case LBF_MATH_LERP:
|
case LBF_MATH_LERP:
|
||||||
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
||||||
|
|
|
@ -771,6 +771,7 @@ struct TypeMapVisitor : AstVisitor
|
||||||
case LBF_VECTOR_CLAMP:
|
case LBF_VECTOR_CLAMP:
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
|
case LBF_VECTOR_LERP:
|
||||||
recordResolvedType(node, &builtinTypes.vectorType);
|
recordResolvedType(node, &builtinTypes.vectorType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauVectorLerp)
|
||||||
|
|
||||||
// luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM
|
// luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM
|
||||||
// The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack.
|
// The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack.
|
||||||
// If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path
|
// If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path
|
||||||
|
@ -1701,6 +1703,26 @@ static int luauF_vectormax(lua_State* L, StkId res, TValue* arg0, int nresults,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int luauF_vectorlerp(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauVectorLerp && nparams >= 3 && nresults <= 1 && ttisvector(arg0) && ttisvector(args) && ttisnumber(args + 1))
|
||||||
|
{
|
||||||
|
const float* a = vvalue(arg0);
|
||||||
|
const float* b = vvalue(args);
|
||||||
|
const float t = static_cast<float>(nvalue(args + 1));
|
||||||
|
|
||||||
|
#if LUA_VECTOR_SIZE == 4
|
||||||
|
setvvalue(res, luai_lerpf(a[0], b[0], t), luai_lerpf(a[1], b[1], t), luai_lerpf(a[2], b[2], t), luai_lerpf(a[3], b[3], t));
|
||||||
|
#else
|
||||||
|
setvvalue(res, luai_lerpf(a[0], b[0], t), luai_lerpf(a[1], b[1], t), luai_lerpf(a[2], b[2], t), 0.0f);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int luauF_lerp(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_lerp(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
|
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
|
||||||
|
@ -1915,6 +1937,8 @@ const luau_FastFunction luauF_table[256] = {
|
||||||
|
|
||||||
luauF_lerp,
|
luauF_lerp,
|
||||||
|
|
||||||
|
luauF_vectorlerp,
|
||||||
|
|
||||||
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
|
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
|
||||||
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
|
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
|
||||||
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
|
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
|
||||||
|
|
|
@ -58,6 +58,11 @@ inline double luai_numidiv(double a, double b)
|
||||||
}
|
}
|
||||||
LUAU_FASTMATH_END
|
LUAU_FASTMATH_END
|
||||||
|
|
||||||
|
inline float luai_lerpf(float a, float b, float t)
|
||||||
|
{
|
||||||
|
return (t == 1.0) ? b : a + (b - a) * t;
|
||||||
|
}
|
||||||
|
|
||||||
#define luai_num2int(i, d) ((i) = (int)(d))
|
#define luai_num2int(i, d) ((i) = (int)(d))
|
||||||
|
|
||||||
// On MSVC in 32-bit, double to unsigned cast compiles into a call to __dtoui3, so we invoke x87->int64 conversion path manually
|
// On MSVC in 32-bit, double to unsigned cast compiles into a call to __dtoui3, so we invoke x87->int64 conversion path manually
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauVectorLerp)
|
||||||
|
|
||||||
static int vector_create(lua_State* L)
|
static int vector_create(lua_State* L)
|
||||||
{
|
{
|
||||||
// checking argument count to avoid accepting 'nil' as a valid value
|
// checking argument count to avoid accepting 'nil' as a valid value
|
||||||
|
@ -283,6 +285,21 @@ static int vector_index(lua_State* L)
|
||||||
luaL_error(L, "attempt to index vector with '%s'", name);
|
luaL_error(L, "attempt to index vector with '%s'", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vector_lerp(lua_State* L)
|
||||||
|
{
|
||||||
|
const float* a = luaL_checkvector(L, 1);
|
||||||
|
const float* b = luaL_checkvector(L, 2);
|
||||||
|
const float t = static_cast<float>(luaL_checknumber(L, 3));
|
||||||
|
|
||||||
|
#if LUA_VECTOR_SIZE == 4
|
||||||
|
lua_pushvector(L, luai_lerpf(a[0], b[0], t), luai_lerpf(a[1], b[1], t), luai_lerpf(a[2], b[2], t), luai_lerpf(a[3], b[3], t));
|
||||||
|
#else
|
||||||
|
lua_pushvector(L, luai_lerpf(a[0], b[0], t), luai_lerpf(a[1], b[1], t), luai_lerpf(a[2], b[2], t));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg vectorlib[] = {
|
static const luaL_Reg vectorlib[] = {
|
||||||
{"create", vector_create},
|
{"create", vector_create},
|
||||||
{"magnitude", vector_magnitude},
|
{"magnitude", vector_magnitude},
|
||||||
|
@ -326,6 +343,13 @@ int luaopen_vector(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_register(L, LUA_VECLIBNAME, vectorlib);
|
luaL_register(L, LUA_VECLIBNAME, vectorlib);
|
||||||
|
|
||||||
|
if (FFlag::LuauVectorLerp)
|
||||||
|
{
|
||||||
|
// To unflag put {"lerp", vector_lerp} in the `vectorlib` table
|
||||||
|
lua_pushcfunction(L, vector_lerp, "lerp");
|
||||||
|
lua_setfield(L, -2, "lerp");
|
||||||
|
}
|
||||||
|
|
||||||
#if LUA_VECTOR_SIZE == 4
|
#if LUA_VECTOR_SIZE == 4
|
||||||
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
|
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
lua_setfield(L, -2, "zero");
|
lua_setfield(L, -2, "zero");
|
||||||
|
|
|
@ -21,7 +21,7 @@ LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
|
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
LUAU_FASTFLAG(LuauIncludeBreakContinueStatements)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -4685,10 +4685,7 @@ TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_via_bidirectional_self")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_via_bidirectional_self")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
type IAccount = {
|
type IAccount = {
|
||||||
|
@ -4722,4 +4719,163 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_via_bidirectional_self")
|
||||||
CHECK_EQ(ac.entryMap.count("balance"), 1);
|
CHECK_EQ(ac.entryMap.count("balance"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_include_break_continue_in_loop")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(for x in y do
|
||||||
|
@1
|
||||||
|
if true then
|
||||||
|
@2
|
||||||
|
end
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK(ac.entryMap.count("break") > 0);
|
||||||
|
CHECK(ac.entryMap.count("continue") > 0);
|
||||||
|
|
||||||
|
ac = autocomplete('2');
|
||||||
|
|
||||||
|
CHECK(ac.entryMap.count("break") > 0);
|
||||||
|
CHECK(ac.entryMap.count("continue") > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_outside_loop")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(@1if true then
|
||||||
|
@2
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
|
||||||
|
ac = autocomplete('2');
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_function_boundary")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(for i = 1, 10 do
|
||||||
|
local function helper()
|
||||||
|
@1
|
||||||
|
end
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_in_param")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(while @1 do
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_incomplete_while")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check("while @1");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_incomplete_for")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check("for @1 in @2 do");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
ac = autocomplete('1');
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
|
||||||
|
ac = autocomplete('2');
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_expr_func")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(while true do
|
||||||
|
local _ = function ()
|
||||||
|
@1
|
||||||
|
end
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_include_break_continue_in_repeat")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(repeat
|
||||||
|
@1
|
||||||
|
until foo())");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK(ac.entryMap.count("break") > 0);
|
||||||
|
CHECK(ac.entryMap.count("continue") > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_include_break_continue_in_nests")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(while ((function ()
|
||||||
|
while true do
|
||||||
|
@1
|
||||||
|
end
|
||||||
|
end)()) do
|
||||||
|
end)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
CHECK(ac.entryMap.count("break") > 0);
|
||||||
|
CHECK(ac.entryMap.count("continue") > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_exclude_break_continue_in_incomplete_loop")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncludeBreakContinueStatements, true};
|
||||||
|
|
||||||
|
check(R"(while foo() do
|
||||||
|
@1)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
// We'd like to include break/continue here but the incomplete loop ends immediately.
|
||||||
|
CHECK_EQ(ac.entryMap.count("break"), 0);
|
||||||
|
CHECK_EQ(ac.entryMap.count("continue"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -36,9 +36,13 @@ void luau_callhook(lua_State* L, lua_Hook hook, void* userdata);
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauHeapDumpStringSizeOverhead)
|
LUAU_FASTFLAG(LuauHeapDumpStringSizeOverhead)
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenDirectBtest)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauErrorYield)
|
LUAU_DYNAMIC_FASTFLAG(LuauErrorYield)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck)
|
LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck)
|
||||||
|
LUAU_FASTFLAG(LuauVectorLerp)
|
||||||
|
LUAU_FASTFLAG(LuauCompileVectorLerp)
|
||||||
|
LUAU_FASTFLAG(LuauTypeCheckerVectorLerp)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
{
|
{
|
||||||
|
@ -1130,6 +1134,8 @@ TEST_CASE("Vector")
|
||||||
|
|
||||||
TEST_CASE("VectorLibrary")
|
TEST_CASE("VectorLibrary")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _[]{{FFlag::LuauCompileVectorLerp, true}, {FFlag::LuauTypeCheckerVectorLerp, true}, {FFlag::LuauVectorLerp, true}};
|
||||||
|
|
||||||
lua_CompileOptions copts = defaultOptions();
|
lua_CompileOptions copts = defaultOptions();
|
||||||
|
|
||||||
SUBCASE("O0")
|
SUBCASE("O0")
|
||||||
|
@ -3112,6 +3118,8 @@ TEST_CASE("SafeEnv")
|
||||||
|
|
||||||
TEST_CASE("Native")
|
TEST_CASE("Native")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag luauCodeGenDirectBtest{FFlag::LuauCodeGenDirectBtest, true};
|
||||||
|
|
||||||
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
||||||
if (!codegen || !luau_codegen_supported())
|
if (!codegen || !luau_codegen_supported())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUpdateSetMetatableTypeSignature)
|
|
||||||
LUAU_FASTFLAG(LuauTidyTypeUtils)
|
LUAU_FASTFLAG(LuauTidyTypeUtils)
|
||||||
LUAU_FASTFLAG(DebugLuauAlwaysShowConstraintSolvingIncomplete);
|
LUAU_FASTFLAG(DebugLuauAlwaysShowConstraintSolvingIncomplete);
|
||||||
|
|
||||||
|
@ -153,7 +152,6 @@ struct Fixture
|
||||||
// Most often those are changes related to builtin type definitions.
|
// Most often those are changes related to builtin type definitions.
|
||||||
// In that case, flag can be forced to 'true' using the example below:
|
// In that case, flag can be forced to 'true' using the example below:
|
||||||
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
||||||
ScopedFastFlag sff_LuauUpdateSetMetatableTypeSignature{FFlag::LuauUpdateSetMetatableTypeSignature, true};
|
|
||||||
|
|
||||||
ScopedFastFlag sff_TypeUtilTidy{FFlag::LuauTidyTypeUtils, true};
|
ScopedFastFlag sff_TypeUtilTidy{FFlag::LuauTidyTypeUtils, true};
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
||||||
LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule)
|
LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule)
|
||||||
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
|
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
|
||||||
LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment)
|
LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation)
|
LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation)
|
||||||
LUAU_FASTFLAG(LuauForInProvidesRecommendations)
|
LUAU_FASTFLAG(LuauForInProvidesRecommendations)
|
||||||
|
|
||||||
|
@ -3948,8 +3947,6 @@ require(script.A).
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_types_provide_rich_autocomplete")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_types_provide_rich_autocomplete")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
|
|
||||||
|
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
type Service = {
|
type Service = {
|
||||||
Start: (self: Service) -> (),
|
Start: (self: Service) -> (),
|
||||||
|
@ -3990,7 +3987,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_with_fancy_metatable_setting_new_solver")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_with_fancy_metatable_setting_new_solver")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
|
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
type IAccount = {
|
type IAccount = {
|
||||||
__index: IAccount,
|
__index: IAccount,
|
||||||
|
@ -4060,8 +4056,6 @@ TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_with_fancy_metatabl
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_with_colon_good_recommendations")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "self_with_colon_good_recommendations")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
|
|
||||||
|
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
type Service = {
|
type Service = {
|
||||||
Start: (self: Service) -> (),
|
Start: (self: Service) -> (),
|
||||||
|
|
|
@ -19,6 +19,9 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("Generalization");
|
TEST_SUITE_BEGIN("Generalization");
|
||||||
|
|
||||||
|
@ -404,16 +407,27 @@ TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2")
|
TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true}, {FFlag::LuauSubtypingReportGenericBoundMismatches, true}, {FFlag::LuauSubtypingGenericsDoesntUseVariance, true}
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: CLI-156389: this is clearly wrong, but also predates this PR.
|
CheckResult result = check(R"(
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
|
||||||
local func: <T>(T, (T) -> ()) -> () = nil :: any
|
local func: <T>(T, (T) -> ()) -> () = nil :: any
|
||||||
local foobar: (number) -> () = nil :: any
|
local foobar: (number) -> () = nil :: any
|
||||||
func({}, function(obj)
|
func({}, function(obj)
|
||||||
foobar(obj)
|
foobar(obj)
|
||||||
end)
|
end)
|
||||||
)"));
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
const GenericBoundsMismatch* gbm = get<GenericBoundsMismatch>(result.errors[0]);
|
||||||
|
REQUIRE_MESSAGE(gbm, "Expected GenericBoundsMismatch but got: " << toString(result.errors[0]));
|
||||||
|
CHECK_EQ(gbm->genericName, "T");
|
||||||
|
CHECK_EQ(gbm->lowerBounds.size(), 1);
|
||||||
|
CHECK_EQ(toString(gbm->lowerBounds[0]), "{ }");
|
||||||
|
CHECK_EQ(gbm->upperBounds.size(), 1);
|
||||||
|
CHECK_EQ(toString(gbm->upperBounds[0]), "number");
|
||||||
|
CHECK_EQ(result.errors[0].location, Location{Position{3, 0}, Position{3, 4}});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808")
|
TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808")
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauCodeGenSimplifyImport2)
|
LUAU_FASTFLAG(LuauCodeGenSimplifyImport2)
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenDirectBtest)
|
||||||
|
|
||||||
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant)
|
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant)
|
||||||
{
|
{
|
||||||
|
@ -2205,4 +2206,35 @@ bb_bytecode_0:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Bit32BtestDirect")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauCodeGenDirectBtest{FFlag::LuauCodeGenDirectBtest, true};
|
||||||
|
|
||||||
|
CHECK_EQ(
|
||||||
|
"\n" + getCodegenAssembly(R"(
|
||||||
|
local function foo(a: number)
|
||||||
|
return bit32.btest(a, 0x1f)
|
||||||
|
end
|
||||||
|
)"),
|
||||||
|
R"(
|
||||||
|
; function foo($arg0) line 2
|
||||||
|
bb_0:
|
||||||
|
CHECK_TAG R0, tnumber, exit(entry)
|
||||||
|
JUMP bb_2
|
||||||
|
bb_2:
|
||||||
|
JUMP bb_bytecode_1
|
||||||
|
bb_bytecode_1:
|
||||||
|
CHECK_SAFE_ENV exit(2)
|
||||||
|
%7 = LOAD_DOUBLE R0
|
||||||
|
%8 = NUM_TO_UINT %7
|
||||||
|
%10 = BITAND_UINT %8, 31i
|
||||||
|
%11 = CMP_INT %10, 0i, not_eq
|
||||||
|
STORE_INT R1, %11
|
||||||
|
STORE_TAG R1, tboolean
|
||||||
|
INTERRUPT 7u
|
||||||
|
RETURN R1, 1i
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -24,7 +24,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauIceLess)
|
LUAU_FASTFLAG(LuauIceLess)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauSimplifyAnyAndUnion)
|
LUAU_FASTFLAG(LuauSimplifyAnyAndUnion)
|
||||||
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
|
||||||
LUAU_FASTFLAG(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
|
LUAU_FASTFLAG(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
|
||||||
|
@ -302,7 +301,6 @@ TEST_CASE_FIXTURE(LimitFixture, "Signal_exerpt" * doctest::timeout(0.5))
|
||||||
{FFlag::LuauEagerGeneralization4, true},
|
{FFlag::LuauEagerGeneralization4, true},
|
||||||
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
||||||
{FFlag::LuauResetConditionalContextProperly, true},
|
{FFlag::LuauResetConditionalContextProperly, true},
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
|
|
||||||
// And this flag is the one that fixes it.
|
// And this flag is the one that fixes it.
|
||||||
{FFlag::LuauSimplifyAnyAndUnion, true},
|
{FFlag::LuauSimplifyAnyAndUnion, true},
|
||||||
|
@ -387,7 +385,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "limit_number_of_dynamically_created_constrai
|
||||||
{FFlag::LuauEagerGeneralization4, true},
|
{FFlag::LuauEagerGeneralization4, true},
|
||||||
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
||||||
{FFlag::LuauResetConditionalContextProperly, true},
|
{FFlag::LuauResetConditionalContextProperly, true},
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
{FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints, true},
|
{FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
|
LUAU_FASTFLAG(LuauVariadicAnyPackShouldBeErrorSuppressing)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1762,4 +1763,19 @@ TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes")
|
||||||
REQUIRE(1 == result.assumedConstraints.size());
|
REQUIRE(1 == result.assumedConstraints.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "variadic_any_pack_should_suppress_errors_during_overload_resolution")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauVariadicAnyPackShouldBeErrorSuppressing, true};
|
||||||
|
auto res = check(R"(
|
||||||
|
type ActionCallback = (string) -> ...any
|
||||||
|
|
||||||
|
function bindAction(callback: ActionCallback)
|
||||||
|
local _ = function(...)
|
||||||
|
callback(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(res);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -15,6 +15,8 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
|
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
||||||
|
LUAU_FASTFLAG(LuauExplicitSkipBoundTypes)
|
||||||
|
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ToString");
|
TEST_SUITE_BEGIN("ToString");
|
||||||
|
|
||||||
|
@ -693,6 +695,11 @@ TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_union"
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_intersection")
|
TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_intersection")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauExplicitSkipBoundTypes, true},
|
||||||
|
{FFlag::LuauReduceSetTypeStackPressure, true},
|
||||||
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f() return f end
|
function f() return f end
|
||||||
local a: ((number) -> ()) & typeof(f)
|
local a: ((number) -> ()) & typeof(f)
|
||||||
|
@ -700,9 +707,6 @@ TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_inters
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
CHECK("(() -> t1) & ((number) -> ()) where t1 = () -> t1" == toString(requireType("a")));
|
|
||||||
else
|
|
||||||
CHECK_EQ("((number) -> ()) & t1 where t1 = () -> t1", toString(requireType("a")));
|
CHECK_EQ("((number) -> ()) & t1 where t1 = () -> t1", toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunNoScopeMapRef)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
|
@ -2451,4 +2452,35 @@ end
|
||||||
CHECK(toString(result.errors[0]) == R"(Redefinition of type 't0', previously defined at line 2)");
|
CHECK(toString(result.errors[0]) == R"(Redefinition of type 't0', previously defined at line 2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_fuzz_environment_scope_crash")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauTypeFunNoScopeMapRef{FFlag::LuauTypeFunNoScopeMapRef, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local _, running = ...
|
||||||
|
type function t255() end
|
||||||
|
if _ then
|
||||||
|
type function t1() end
|
||||||
|
type function t6(l0,...) end
|
||||||
|
type function t255<A...>() end
|
||||||
|
export type function t0<A>() end
|
||||||
|
else
|
||||||
|
type function t1(...) end
|
||||||
|
type function t66<A...>(...) end
|
||||||
|
type function t255() end
|
||||||
|
if running then
|
||||||
|
export type function t255() end
|
||||||
|
type function t0(l0) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
type function t0(l0,...) end
|
||||||
|
export type function t66(...)
|
||||||
|
export type function t255() end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -13,7 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauNoScopeShallNotSubsumeAll)
|
LUAU_FASTFLAG(LuauNoScopeShallNotSubsumeAll)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
|
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
@ -1713,10 +1712,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_strin
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
|
TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local function accept(t: { write foo: number })
|
local function accept(t: { write foo: number })
|
||||||
|
|
|
@ -15,7 +15,6 @@ using namespace Luau;
|
||||||
using std::nullopt;
|
using std::nullopt;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
LUAU_FASTFLAG(LuauScopeMethodsAreSolverAgnostic)
|
LUAU_FASTFLAG(LuauScopeMethodsAreSolverAgnostic)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferExternTypes");
|
TEST_SUITE_BEGIN("TypeInferExternTypes");
|
||||||
|
@ -443,8 +442,6 @@ b.X = 2 -- real Vector2.X is also read-only
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ExternTypeFixture, "detailed_class_unification_error")
|
TEST_CASE_FIXTURE(ExternTypeFixture, "detailed_class_unification_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function foo(v)
|
local function foo(v)
|
||||||
return v.X :: number + string.len(v.Y)
|
return v.X :: number + string.len(v.Y)
|
||||||
|
|
|
@ -27,11 +27,11 @@ LUAU_FASTFLAG(LuauCollapseShouldNotCrash)
|
||||||
LUAU_FASTFLAG(LuauFormatUseLastPosition)
|
LUAU_FASTFLAG(LuauFormatUseLastPosition)
|
||||||
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
||||||
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
|
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
LUAU_FASTFLAG(LuauUnifyShortcircuitSomeIntersectionsAndUnions)
|
LUAU_FASTFLAG(LuauUnifyShortcircuitSomeIntersectionsAndUnions)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -1449,6 +1449,13 @@ local a = {{x=4}, {x=7}, {x=1}}
|
||||||
table.sort(a, function(x, y) return x.x < y.x end)
|
table.sort(a, function(x, y) return x.x < y.x end)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
{
|
||||||
|
// FIXME CLI-161355
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(get<GenericBoundsMismatch>(result.errors[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3184,10 +3191,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_pack_variadic")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_annotated_explicit_self")
|
TEST_CASE_FIXTURE(Fixture, "table_annotated_explicit_self")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult results = check(R"(
|
CheckResult results = check(R"(
|
||||||
type MyObject = {
|
type MyObject = {
|
||||||
|
@ -3210,11 +3214,7 @@ TEST_CASE_FIXTURE(Fixture, "table_annotated_explicit_self")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "oss_1871")
|
TEST_CASE_FIXTURE(Fixture, "oss_1871")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
export type Test = {
|
export type Test = {
|
||||||
|
@ -3233,10 +3233,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1871")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "io_manager_oop_ish")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "io_manager_oop_ish")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
type IIOManager = {
|
type IIOManager = {
|
||||||
|
@ -3268,10 +3265,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "io_manager_oop_ish")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_function_statement")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_function_statement")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
type Object = {
|
type Object = {
|
||||||
|
|
|
@ -13,11 +13,11 @@ LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauIntersectNotNil)
|
LUAU_FASTFLAG(LuauIntersectNotNil)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauContainsAnyGenericFollowBeforeChecking)
|
LUAU_FASTFLAG(LuauContainsAnyGenericFollowBeforeChecking)
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function2")
|
||||||
CHECK_EQ(getBuiltins()->numberType, requireType("y"));
|
CHECK_EQ(getBuiltins()->numberType, requireType("y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "unions_and_generics")
|
TEST_CASE_FIXTURE(Fixture, "unions_and_generics")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type foo = <T>(T | {T}) -> T
|
type foo = <T>(T | {T}) -> T
|
||||||
|
@ -1220,12 +1220,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_table_method")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions")
|
TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions")
|
||||||
{
|
{
|
||||||
// Prior to `LuauPushFunctionTypesInFunctionStatement`, we _always_ forced
|
ScopedFastFlag sff{FFlag::DebugLuauAssertOnForcedConstraint, true};
|
||||||
// a constraint when solving this block.
|
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{FFlag::DebugLuauAssertOnForcedConstraint, true},
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local T = {}
|
local T = {}
|
||||||
|
@ -1490,6 +1485,9 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded"
|
||||||
g12({x=1}, {x=2}, function(x, y) return {x=x.x + y.x} end)
|
g12({x=1}, {x=2}, function(x, y) return {x=x.x + y.x} end)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(2, result); // FIXME CLI-161355
|
||||||
|
else
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1892,4 +1890,42 @@ end
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "ensure_that_invalid_generic_instantiations_error")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _[] = {{FFlag::LuauSubtypingGenericsDoesntUseVariance, true}, {FFlag::LuauSubtypingReportGenericBoundMismatches, true}};
|
||||||
|
CheckResult res = check(R"(
|
||||||
|
local func: <T>(T, (T) -> ()) -> () = nil :: any
|
||||||
|
local foobar: (number) -> () = nil :: any
|
||||||
|
func({}, foobar)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, res);
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
CHECK(get<GenericBoundsMismatch>(res.errors[0]));
|
||||||
|
else
|
||||||
|
CHECK(get<TypeMismatch>(res.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "ensure_that_invalid_generic_instantiations_error_1")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _[] = {{FFlag::LuauSubtypingGenericsDoesntUseVariance, true}, {FFlag::LuauSubtypingReportGenericBoundMismatches, true}};
|
||||||
|
CheckResult res = check(R"(
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
function insert<T>(arr: {T}, value: T)
|
||||||
|
return arr
|
||||||
|
end
|
||||||
|
|
||||||
|
local a: {number} = {}
|
||||||
|
|
||||||
|
local b = insert(a, "five")
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, res);
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
CHECK(get<GenericBoundsMismatch>(res.errors[0]));
|
||||||
|
else
|
||||||
|
CHECK(get<TypeMismatch>(res.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -11,7 +11,6 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("IntersectionTypes");
|
TEST_SUITE_BEGIN("IntersectionTypes");
|
||||||
|
|
||||||
|
@ -334,8 +333,6 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type X = { x: (number) -> number }
|
type X = { x: (number) -> number }
|
||||||
type Y = { y: (string) -> string }
|
type Y = { y: (string) -> string }
|
||||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauUnifyShortcircuitSomeIntersectionsAndUnions)
|
LUAU_FASTFLAG(LuauUnifyShortcircuitSomeIntersectionsAndUnions)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
|
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
|
||||||
|
LUAU_FASTFLAG(LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -2350,6 +2351,13 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "mutate_prop_of_some_refined_symb
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauSubtypingReportGenericBoundMismatches)
|
||||||
|
{
|
||||||
|
// FIXME CLI-161355
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(get<GenericBoundsMismatch>(result.errors[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,16 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
|
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
|
||||||
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
|
||||||
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
||||||
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
|
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(LuauNoScopeShallNotSubsumeAll)
|
LUAU_FASTFLAG(LuauNoScopeShallNotSubsumeAll)
|
||||||
LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize)
|
LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize)
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
LUAU_FASTFLAG(LuauExtendSealedTableUpperBounds)
|
LUAU_FASTFLAG(LuauExtendSealedTableUpperBounds)
|
||||||
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
LUAU_FASTFLAG(LuauSubtypingGenericsDoesntUseVariance)
|
||||||
|
LUAU_FASTFLAG(LuauAllowMixedTables)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
|
|
||||||
|
@ -491,8 +490,6 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_1")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
function foo(o)
|
function foo(o)
|
||||||
|
@ -3879,10 +3876,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_call_is_unsound")
|
TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_call_is_unsound")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[]{
|
ScopedFastFlag sff{FFlag::LuauInstantiateInSubtyping, true};
|
||||||
{FFlag::LuauInstantiateInSubtyping, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -4436,8 +4430,6 @@ TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression")
|
TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function one(tbl: {x: any}) end
|
function one(tbl: {x: any}) end
|
||||||
function two(tbl: {x: string}) one(tbl) end -- ok, string <: any and any <: string
|
function two(tbl: {x: string}) one(tbl) end -- ok, string <: any and any <: string
|
||||||
|
@ -5835,10 +5827,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1651")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call")
|
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local function take(_: { foo: string? }) end
|
local function take(_: { foo: string? }) end
|
||||||
|
@ -5849,10 +5838,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_incorrect")
|
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_incorrect")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult results = check(R"(
|
CheckResult results = check(R"(
|
||||||
local function take(_: { foo: string?, bing: number }) end
|
local function take(_: { foo: string?, bing: number }) end
|
||||||
|
@ -5870,10 +5856,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_incorrect")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_singleton")
|
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_singleton")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult results = check(R"(
|
CheckResult results = check(R"(
|
||||||
local function take(_: { foo: "foo" }) end
|
local function take(_: { foo: "foo" }) end
|
||||||
|
@ -5888,7 +5871,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
|
||||||
{FFlag::LuauEagerGeneralization4, true},
|
{FFlag::LuauEagerGeneralization4, true},
|
||||||
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
{FFlag::LuauTrackFreeInteriorTypePacks, true},
|
||||||
{FFlag::LuauResetConditionalContextProperly, true}
|
{FFlag::LuauResetConditionalContextProperly, true}
|
||||||
|
@ -6047,4 +6029,30 @@ TEST_CASE_FIXTURE(Fixture, "free_types_with_sealed_table_upper_bounds_can_still_
|
||||||
CHECK("({ read nope: () -> (...unknown) } & { x: number }) -> ()" == toString(requireType("foo")));
|
CHECK("({ read nope: () -> (...unknown) } & { x: number }) -> ()" == toString(requireType("foo")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "mixed_tables_are_ok_when_explicit")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauAllowMixedTables, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local foo: { [number | string]: unknown } = {
|
||||||
|
Key = "sorry",
|
||||||
|
"A",
|
||||||
|
"B",
|
||||||
|
}
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "mixed_tables_are_ok_for_any_key")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauAllowMixedTables, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local foo: { [any]: unknown } = {
|
||||||
|
Key = "sorry",
|
||||||
|
"A",
|
||||||
|
"B",
|
||||||
|
}
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -28,11 +28,11 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||||
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
|
||||||
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
|
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
|
||||||
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
|
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
|
||||||
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
|
||||||
LUAU_FASTFLAG(LuauMissingFollowMappedGenericPacks)
|
LUAU_FASTFLAG(LuauMissingFollowMappedGenericPacks)
|
||||||
|
LUAU_FASTFLAG(LuauOccursCheckInCommit)
|
||||||
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
|
||||||
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
LUAU_FASTFLAG(LuauResetConditionalContextProperly)
|
||||||
|
|
||||||
|
@ -2513,36 +2513,6 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_two_errors")
|
||||||
CHECK_EQ("number", toString(err2->givenType));
|
CHECK_EQ("number", toString(err2->givenType));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "simplify_constraint_can_force")
|
|
||||||
{
|
|
||||||
ScopedFastFlag sff[] = {
|
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauForceSimplifyConstraint2, true},
|
|
||||||
// NOTE: Feel free to clip this test when this flag is clipped.
|
|
||||||
{FFlag::LuauPushFunctionTypesInFunctionStatement, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
|
||||||
--!strict
|
|
||||||
|
|
||||||
local foo = nil
|
|
||||||
|
|
||||||
bar(function()
|
|
||||||
if foo then
|
|
||||||
foo.baz()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
foo = {}
|
|
||||||
|
|
||||||
foo.a = {
|
|
||||||
foo.b
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERROR(result, ConstraintSolvingIncompleteError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "standalone_constraint_solving_incomplete_is_hidden")
|
TEST_CASE_FIXTURE(Fixture, "standalone_constraint_solving_incomplete_is_hidden")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
|
@ -2623,4 +2593,50 @@ do end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "txnlog_checks_for_occurrence_before_self_binding_a_type")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff[] = {
|
||||||
|
{FFlag::LuauSolverV2, false},
|
||||||
|
{FFlag::LuauOccursCheckInCommit, true}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local any = nil :: any
|
||||||
|
|
||||||
|
function f1(x)
|
||||||
|
x:m()
|
||||||
|
local _ = x.A.p.a
|
||||||
|
end
|
||||||
|
|
||||||
|
function f2(x)
|
||||||
|
local _ = x.d
|
||||||
|
end
|
||||||
|
|
||||||
|
function f3(x)
|
||||||
|
local a = ""
|
||||||
|
a = x.d.p
|
||||||
|
local _ = undef[x.a]
|
||||||
|
end
|
||||||
|
|
||||||
|
function f4(x)
|
||||||
|
f2(x)
|
||||||
|
if undef and x and x:m() then
|
||||||
|
any(x)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
f3(x)
|
||||||
|
for _, v in any.x do
|
||||||
|
local a = x[v].p
|
||||||
|
end
|
||||||
|
a.b = x
|
||||||
|
if x.q ~= nil then
|
||||||
|
f1(x) -- things go bad here
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return f4
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -366,7 +366,7 @@ struct VisitCountTracker final : TypeOnceVisitor
|
||||||
std::unordered_map<TypePackId, unsigned> tpVisits;
|
std::unordered_map<TypePackId, unsigned> tpVisits;
|
||||||
|
|
||||||
VisitCountTracker()
|
VisitCountTracker()
|
||||||
: TypeOnceVisitor("VisitCountTracker")
|
: TypeOnceVisitor("VisitCountTracker", /* skipBoundTypes */ true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,6 +237,17 @@ end
|
||||||
|
|
||||||
assert(pcall(fuzzfail23) == false)
|
assert(pcall(fuzzfail23) == false)
|
||||||
|
|
||||||
|
local function fuzzfail24(...)
|
||||||
|
local _ = l4
|
||||||
|
repeat
|
||||||
|
local function l0()
|
||||||
|
end
|
||||||
|
until (...)[_][_]:n16((_),_,l4,bit32.btest(0,_,_,_) / bit32.btest(0,_,_,_)(_,_,_,0,((_)),_))
|
||||||
|
do end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(pcall(fuzzfail24) == false)
|
||||||
|
|
||||||
local function arraySizeInv1()
|
local function arraySizeInv1()
|
||||||
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,16 @@ assert(vector.clamp(vector.create(1, 1, 1), vector.create(0, 1, 2), vector.creat
|
||||||
assert(vector.clamp(vector.create(1, 1, 1), vector.create(-1, -1, -1), vector.create(0, 1, 2)) == vector.create(0, 1, 1))
|
assert(vector.clamp(vector.create(1, 1, 1), vector.create(-1, -1, -1), vector.create(0, 1, 2)) == vector.create(0, 1, 1))
|
||||||
assert(select("#", vector.clamp(vector.zero, vector.zero, vector.one)) == 1)
|
assert(select("#", vector.clamp(vector.zero, vector.zero, vector.one)) == 1)
|
||||||
|
|
||||||
|
-- lerp
|
||||||
|
assert(vector.lerp(vector.zero, vector.one, 0) == vector.zero)
|
||||||
|
assert(vector.lerp(vector.zero, vector.one, 1) == vector.one)
|
||||||
|
assert(vector.lerp(vector.zero, vector.one, 0.5) == vector.create(0.5, 0.5, 0.5))
|
||||||
|
assert(vector.lerp(vector.one, vector.zero, 0.5) == vector.create(0.5, 0.5, 0.5))
|
||||||
|
assert(vector.lerp(vector.one, vector.zero, 0.5) == vector.create(0.5, 0.5, 0.5))
|
||||||
|
assert(vector.lerp(vector.create(1, 2, 3), vector.create(3, 2, 1), 0.5) == vector.create(2, 2, 2))
|
||||||
|
assert(vector.lerp(vector.create(-1, -1, -3), vector.zero, 0.5) == vector.create(-0.5, -0.5, -1.5))
|
||||||
|
assert(ecall(function() return vector.lerp(vector.zero, vector.one, vector.one) end) == "invalid argument #3 to 'lerp' (number expected, got vector)")
|
||||||
|
|
||||||
-- validate component access
|
-- validate component access
|
||||||
assert(vector.create(1, 2, 3).x == 1)
|
assert(vector.create(1, 2, 3).x == 1)
|
||||||
assert(vector.create(1, 2, 3).X == 1)
|
assert(vector.create(1, 2, 3).X == 1)
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -426,6 +428,11 @@ int main(int argc, char** argv)
|
||||||
doctest::String filter;
|
doctest::String filter;
|
||||||
if (doctest::parseOption(argc, argv, "--run_test", &filter) && filter[0] == '=')
|
if (doctest::parseOption(argc, argv, "--run_test", &filter) && filter[0] == '=')
|
||||||
{
|
{
|
||||||
|
if (doctest::parseOption(argc, argv, "--run_tests_in_file"))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: Cannot pass both --run_test and --run_tests_in_file\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
const char* f = filter.c_str() + 1;
|
const char* f = filter.c_str() + 1;
|
||||||
const char* s = strchr(f, '/');
|
const char* s = strchr(f, '/');
|
||||||
|
|
||||||
|
@ -440,6 +447,15 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doctest::String filter_path;
|
||||||
|
if (doctest::parseOption(argc, argv, "--run_tests_in_file", &filter_path) && filter_path[0] == '=')
|
||||||
|
{
|
||||||
|
filter_path = filter_path.substr(1, filter_path.size() - 1);
|
||||||
|
std::ifstream filter_stream(filter_path.c_str());
|
||||||
|
std::string case_list((std::istreambuf_iterator<char>(filter_stream)), std::istreambuf_iterator<char>());
|
||||||
|
context.addFilter("test-case", case_list.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// These callbacks register unit tests that need runtime support to be
|
// These callbacks register unit tests that need runtime support to be
|
||||||
// correctly set up. Running them here means that all command line flags
|
// correctly set up. Running them here means that all command line flags
|
||||||
// have been parsed, fast flags have been set, and we've potentially already
|
// have been parsed, fast flags have been set, and we've potentially already
|
||||||
|
|
Loading…
Add table
Reference in a new issue