Sync to upstream/release/686 (#1948)

## General
This week has been spent mostly on fixing bugs in incremental
autocomplete as well as making the new Type Solver more stable.

- Fixes a bug where registered "require" aliases were case-sensitive
instead of case-insensitive.
### New Type Solver
- Adjust literal sub typing logic to account for unreduced type
functions
- Implement a number of subtyping stack utilization improvements
- Emit a single error if an internal type escapes a module's interface
- Checked function errors in the New Non Strict warn about incorrect
argument use with one-indexed positions, e.g. `argument #1 was used
incorrectly` instead of `argument #0 was used incorrectly`.
- Improvements to type function reduction that let us progress further
while reducing
- Augment the generalization system to not emit duplicate constraints.
- Fix a bug where we didn't seal tables in modules that failed to
complete typechecking.

### Fragment Autocomplete
- Provide richer autocomplete suggestions inside of for loops
- Provide richer autocomplete suggestions inside of interpolated string
expressions
- Improve the quality of error messages when typing out interpolated
strings.

### Compiler
- Fixes REX encoding of extended byte registers for the x86 assembly
code generation.
- Fixes for table shape constant data encoding

---
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
Vighnesh-V 2025-08-08 10:18:16 -07:00 committed by GitHub
parent f3f3bf8f72
commit 8863bfc950
Signed by: DevComp
GPG key ID: B5690EEEBB952194
88 changed files with 2100 additions and 3208 deletions

View file

@ -337,9 +337,6 @@ struct Constraint
std::vector<NotNull<Constraint>> dependencies;
// Clip with LuauUseOrderedTypeSetsInConstraints
DenseHashSet<TypeId> getMaybeMutatedFreeTypes_DEPRECATED() const;
TypeIds getMaybeMutatedFreeTypes() const;
};

View file

@ -13,6 +13,7 @@
#include "Luau/Normalize.h"
#include "Luau/OrderedSet.h"
#include "Luau/Substitution.h"
#include "Luau/SubtypingVariance.h"
#include "Luau/ToString.h"
#include "Luau/Type.h"
#include "Luau/TypeCheckLimits.h"
@ -41,6 +42,20 @@ struct HashBlockedConstraintId
size_t operator()(const BlockedConstraintId& bci) const;
};
struct SubtypeConstraintRecord
{
TypeId subTy = nullptr;
TypeId superTy = nullptr;
SubtypingVariance variance = SubtypingVariance::Invalid;
bool operator==(const SubtypeConstraintRecord& other) const;
};
struct HashSubtypeConstraintRecord
{
size_t operator()(const SubtypeConstraintRecord& c) const;
};
struct ModuleResolver;
struct InstantiationSignature
@ -127,16 +142,14 @@ struct ConstraintSolver
// A mapping from free types to the number of unresolved constraints that mention them.
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
// Clip with LuauUseOrderedTypeSetsInConstraints
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes_DEPRECATED;
std::unordered_map<TypeId, DenseHashSet<const Constraint*>> mutatedFreeTypeToConstraint_DEPRECATED;
std::unordered_map<NotNull<const Constraint>, TypeIds> maybeMutatedFreeTypes;
std::unordered_map<TypeId, OrderedSet<const Constraint*>> mutatedFreeTypeToConstraint;
// Irreducible/uninhabited type functions or type pack functions.
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
DenseHashMap<SubtypeConstraintRecord, Constraint*, HashSubtypeConstraintRecord> seenConstraints{{}};
// The set of types that will definitely be unchanged by generalization.
DenseHashSet<TypeId> generalizedTypes_{nullptr};
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};

View file

@ -93,9 +93,6 @@ struct DfgScope
std::optional<DefId> lookup(DefId def, const std::string& key) const;
void inherit(const DfgScope* childScope);
bool canUpdateDefinition(Symbol symbol) const;
bool canUpdateDefinition(DefId def, const std::string& key) const;
};
struct DataFlowResult
@ -133,7 +130,6 @@ private:
/// A stack of scopes used by the visitor to see where we are.
ScopeStack scopeStack;
NotNull<DfgScope> currentScope();
DfgScope* currentScope_DEPRECATED();
struct FunctionCapture
{

View file

@ -75,7 +75,6 @@ struct FragmentAutocompleteResult
{
ModulePtr incrementalModule;
Scope* freshScope;
TypeArena arenaForAutocomplete_DEPRECATED;
AutocompleteResult acResults;
};

View file

@ -170,6 +170,8 @@ struct Frontend
double timeParse = 0;
double timeCheck = 0;
double timeLint = 0;
size_t dynamicConstraintsCreated = 0;
};
Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {});
@ -243,6 +245,7 @@ private:
std::optional<ScopePtr> environmentScope,
bool forAutocomplete,
bool recordJsonLog,
Frontend::Stats& stats,
TypeCheckLimits typeCheckLimits
);
@ -333,6 +336,7 @@ ModulePtr check(
FrontendOptions options,
TypeCheckLimits limits,
bool recordJsonLog,
Frontend::Stats& stats,
std::function<void(const ModuleName&, std::string)> writeJsonLog
);

View file

@ -4,6 +4,7 @@
#include "Luau/DenseHash.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Set.h"
#include "Luau/SubtypingVariance.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h"
@ -32,17 +33,6 @@ struct TableIndexer;
struct TypeArena;
struct TypeCheckLimits;
enum class SubtypingVariance
{
// Used for an empty key. Should never appear in actual code.
Invalid,
Covariant,
// This is used to identify cases where we have a covariant + a
// contravariant reason and we need to merge them.
Contravariant,
Invariant,
};
struct SubtypingReasoning
{
// The path, relative to the _root subtype_, where subtyping failed.
@ -327,6 +317,12 @@ private:
[[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp);
SubtypingResult trySemanticSubtyping(SubtypingEnvironment& env,
TypeId subTy,
TypeId superTy,
NotNull<Scope> scope,
SubtypingResult& original);
};
} // namespace Luau

View file

@ -0,0 +1,19 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
namespace Luau
{
enum class SubtypingVariance
{
// Used for an empty key. Should never appear in actual code.
Invalid,
Covariant,
// This is used to identify cases where we have a covariant + a
// contravariant reason and we need to merge them.
Contravariant,
Invariant,
};
}

View file

@ -130,7 +130,6 @@ struct TypeChecker
const PredicateVec& predicates = {}
);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);

View file

@ -12,7 +12,6 @@
#include <algorithm>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
namespace Luau
{
@ -526,7 +525,7 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
return std::nullopt;
TypeId followed;
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
{
if (indexIt->second.readTy)
followed = follow(*indexIt->second.readTy);

View file

@ -21,11 +21,9 @@
#include <utility>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
static const std::unordered_set<std::string> kStatementStartingKeywords =
@ -79,7 +77,6 @@ static ParenthesesRecommendation getParenRecommendationForIntersect(const Inters
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
for (Luau::TypeId partId : intersect->parts)
{
if (FFlag::LuauAutocompleteMissingFollows)
partId = follow(partId);
if (auto partFunc = Luau::get<FunctionType>(partId))
{
@ -370,7 +367,7 @@ static void autocompleteProps(
if (indexIt != mtable->props.end())
{
TypeId followed;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
followed = follow(*indexIt->second.readTy);
else
followed = follow(indexIt->second.type_DEPRECATED());
@ -1636,7 +1633,6 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
{
for (TypeId part : intersect->parts)
{
if (FFlag::LuauAutocompleteMissingFollows)
part = follow(part);
if (auto candidateFunctionType = Luau::get<FunctionType>(part))
{

View file

@ -33,9 +33,7 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements)
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
LUAU_FASTFLAGVARIABLE(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
@ -339,14 +337,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
auto it = stringMetatableTable->props.find("__index");
LUAU_ASSERT(it != stringMetatableTable->props.end());
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
addGlobalBinding(globals, "string", *it->second.readTy, "@luau");
addGlobalBinding(globals, "string", *it->second.writeTy, "@luau");
}
else
addGlobalBinding(globals, "string", it->second.type_DEPRECATED(), "@luau");
// Setup 'vector' metatable
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
@ -504,21 +496,11 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
ttv->props["foreach"].deprecated = true;
ttv->props["foreachi"].deprecated = true;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared<MagicPack>());
if (FFlag::LuauTableCloneClonesType3)
attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared<MagicClone>());
attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared<MagicFreeze>());
}
else
{
attachMagicFunction(ttv->props["pack"].type_DEPRECATED(), std::make_shared<MagicPack>());
if (FFlag::LuauTableCloneClonesType3)
attachMagicFunction(ttv->props["clone"].type_DEPRECATED(), std::make_shared<MagicClone>());
attachMagicFunction(ttv->props["freeze"].type_DEPRECATED(), std::make_shared<MagicFreeze>());
}
}
TypeId requireTy = getGlobalBinding(globals, "require");
attachTag(requireTy, kRequireTagName);
@ -665,8 +647,6 @@ bool MagicFormat::infer(const MagicFunctionCallContext& context)
{
TypeArena* arena = context.solver->arena;
if (FFlag::LuauStringFormatImprovements)
{
auto iter = begin(context.arguments);
// we'll suppress any errors for `string.format` if the format string is error suppressing.
@ -719,54 +699,10 @@ bool MagicFormat::infer(const MagicFunctionCallContext& context)
asMutable(context.result)->ty.emplace<BoundTypePack>(resultPack);
return true;
}
else
{
AstExprConstantString* fmt = nullptr;
if (auto index = context.callSite->func->as<AstExprIndexName>(); index && context.callSite->self)
{
if (auto group = index->expr->as<AstExprGroup>())
fmt = group->expr->as<AstExprConstantString>();
else
fmt = index->expr->as<AstExprConstantString>();
}
if (!context.callSite->self && context.callSite->args.size > 0)
fmt = context.callSite->args.data[0]->as<AstExprConstantString>();
if (!fmt)
return false;
std::vector<TypeId> expected = parseFormatString(context.solver->builtinTypes, fmt->value.data, fmt->value.size);
const auto& [params, tail] = flatten(context.arguments);
size_t paramOffset = 1;
// unify the prefix one argument at a time - needed if any of the involved types are free
for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i)
{
context.solver->unify(context.constraint, params[i + paramOffset], expected[i]);
}
// if we know the argument count or if we have too many arguments for sure, we can issue an error
size_t numActualParams = params.size();
size_t numExpectedParams = expected.size() + 1; // + 1 for the format string
if (numExpectedParams != numActualParams && (!tail || numExpectedParams < numActualParams))
context.solver->reportError(TypeError{context.callSite->location, CountMismatch{numExpectedParams, std::nullopt, numActualParams}});
// This is invoked at solve time, so we just need to provide a type for the result of :/.format
TypePackId resultPack = arena->addTypePack({context.solver->builtinTypes->stringType});
asMutable(context.result)->ty.emplace<BoundTypePack>(resultPack);
return true;
}
}
bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
{
if (FFlag::LuauStringFormatImprovements)
{
auto iter = begin(context.arguments);
if (iter == end(context.arguments))
@ -848,73 +784,6 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
}
return true;
}
else
{
AstExprConstantString* fmt = nullptr;
if (auto index = context.callSite->func->as<AstExprIndexName>(); index && context.callSite->self)
{
if (auto group = index->expr->as<AstExprGroup>())
fmt = group->expr->as<AstExprConstantString>();
else
fmt = index->expr->as<AstExprConstantString>();
}
if (!context.callSite->self && context.callSite->args.size > 0)
fmt = context.callSite->args.data[0]->as<AstExprConstantString>();
if (!fmt)
{
context.typechecker->reportError(
CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location
);
return true;
}
// CLI-150726: The block below effectively constructs a type pack and then type checks it by going parameter-by-parameter.
// This does _not_ handle cases like:
//
// local foo : () -> (...string) = (nil :: any)
// print(string.format("%s %d %s", foo()))
//
// ... which should be disallowed.
std::vector<TypeId> expected = parseFormatString(context.builtinTypes, fmt->value.data, fmt->value.size);
const auto& [params, tail] = flatten(context.arguments);
size_t paramOffset = 1;
// Compare the expressions passed with the types the function expects to determine whether this function was called with : or .
bool calledWithSelf = expected.size() == context.callSite->args.size;
// unify the prefix one argument at a time
for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i)
{
TypeId actualTy = params[i + paramOffset];
TypeId expectedTy = expected[i];
Location location =
context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location;
// use subtyping instead here
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
if (!result.isSubtype)
{
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
{
case ErrorSuppression::Suppress:
break;
case ErrorSuppression::NormalizationFailed:
break;
case ErrorSuppression::DoNotSuppress:
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
if (!reasonings.suppressed)
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
}
}
}
return true;
}
}
static std::vector<TypeId> parsePatternString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)

View file

@ -17,16 +17,14 @@
#include "Luau/UserDefinedTypeFunction.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauNotAllBinaryTypeFunsHaveDefaults)
LUAU_FASTFLAG(LuauEmptyStringInKeyOf)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
LUAU_FASTFLAG(LuauOccursCheckForRefinement)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAGVARIABLE(LuauDoNotBlockOnStuckTypeFunctions)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways)
@ -682,6 +680,16 @@ TypeFunctionReductionResult<TypeId> concatTypeFunction(
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}};
}
namespace
{
bool isBlockedOrUnsolvedType(TypeId ty)
{
if (auto tfit = get<TypeFunctionInstanceType>(ty); tfit && tfit->state == TypeFunctionInstanceState::Unsolved)
return true;
return is<BlockedType, PendingExpansionType>(ty);
}
} // namespace
TypeFunctionReductionResult<TypeId> andTypeFunction(
TypeId instance,
const std::vector<TypeId>& typeParams,
@ -747,12 +755,22 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
// check to see if both operand types are resolved enough, and wait to reduce if not
if (FFlag::LuauEagerGeneralization4)
{
if (FFlag::LuauDoNotBlockOnStuckTypeFunctions)
{
if (isBlockedOrUnsolvedType(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (isBlockedOrUnsolvedType(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
}
else
{
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
}
}
else
{
if (isPending(lhsTy, ctx->solver))
@ -794,12 +812,22 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
if (FFlag::LuauEagerGeneralization4)
{
if (FFlag::LuauDoNotBlockOnStuckTypeFunctions)
{
if (isBlockedOrUnsolvedType(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (isBlockedOrUnsolvedType(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
}
else
{
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
}
}
else
{
if (isPending(lhsTy, ctx->solver))
@ -1302,8 +1330,18 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
return {targetTy, {}};
}
const bool targetIsPending = FFlag::LuauEagerGeneralization4 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
: isPending(targetTy, ctx->solver);
bool targetIsPending = false;
if (FFlag::LuauEagerGeneralization4)
{
targetIsPending = FFlag::LuauDoNotBlockOnStuckTypeFunctions
? isBlockedOrUnsolvedType(targetTy)
: is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy);
}
else
{
targetIsPending = isPending(targetTy, ctx->solver);
}
// check to see if both operand types are resolved enough, and wait to reduce if not
if (targetIsPending)
@ -2133,20 +2171,15 @@ bool searchPropsAndIndexer(
if (tblProps.find(stringSingleton->value) != tblProps.end())
{
TypeId propTy;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& prop = tblProps.at(stringSingleton->value);
TypeId propTy;
if (prop.readTy)
propTy = follow(*prop.readTy);
else if (prop.writeTy)
propTy = follow(*prop.writeTy);
else // found the property, but there was no type associated with it
return false;
}
else
propTy = follow(tblProps.at(stringSingleton->value).type_DEPRECATED());
// property is a union type -> we need to extend our reduction type
if (auto propUnionTy = get<UnionType>(propTy))
@ -2804,21 +2837,10 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
scope->exportedTypeBindings[keyofFunc.name] = mkUnaryTypeFunction(&keyofFunc);
scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc);
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
{
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc);
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc);
}
else
{
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunctionWithDefault(&indexFunc);
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunctionWithDefault(&rawgetFunc);
}
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
else
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc);
scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc);
}

View file

@ -13,7 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2)
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticClone)
namespace Luau
{
@ -202,8 +201,6 @@ public:
private:
Property shallowClone(const Property& p)
{
if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{
std::optional<TypeId> cloneReadTy;
if (auto ty = p.readTy)
@ -222,19 +219,6 @@ private:
cloned.typeLocation = p.typeLocation;
return cloned;
}
else
{
return Property{
shallowClone(p.type_DEPRECATED()),
p.deprecated,
p.deprecatedSuggestion,
p.location,
p.tags,
p.documentationSymbol,
p.typeLocation,
};
}
}
void cloneChildren(TypeId ty)
{

View file

@ -18,61 +18,6 @@ Constraint::Constraint(NotNull<Scope> scope, const Location& location, Constrain
{
}
struct ReferenceCountInitializer_DEPRECATED : TypeOnceVisitor
{
DenseHashSet<TypeId>* result;
bool traverseIntoTypeFunctions = true;
explicit ReferenceCountInitializer_DEPRECATED(DenseHashSet<TypeId>* result)
: TypeOnceVisitor("ReferenceCountInitializer_DEPRECATED")
, result(result)
{
}
bool visit(TypeId ty, const FreeType&) override
{
result->insert(ty);
return false;
}
bool visit(TypeId ty, const BlockedType&) override
{
result->insert(ty);
return false;
}
bool visit(TypeId ty, const PendingExpansionType&) override
{
result->insert(ty);
return false;
}
bool visit(TypeId ty, const TableType& tt) override
{
if (FFlag::LuauEagerGeneralization4)
{
if (tt.state == TableState::Unsealed || tt.state == TableState::Free)
result->insert(ty);
}
return true;
}
bool visit(TypeId ty, const ExternType&) override
{
// ExternTypes never contain free types.
return false;
}
bool visit(TypeId, const TypeFunctionInstanceType& tfit) override
{
if (FFlag::LuauForceSimplifyConstraint2)
return tfit.function->canReduceGenerics;
else
return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions;
}
};
struct ReferenceCountInitializer : TypeOnceVisitor
{
NotNull<TypeIds> result;
@ -140,110 +85,6 @@ bool isReferenceCountedType(const TypeId typ)
return get<FreeType>(typ) || get<BlockedType>(typ) || get<PendingExpansionType>(typ);
}
DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes_DEPRECATED() const
{
// For the purpose of this function and reference counting in general, we are only considering
// mutations that affect the _bounds_ of the free type, and not something that may bind the free
// type itself to a new type. As such, `ReduceConstraint` and `GeneralizationConstraint` have no
// contribution to the output set here.
DenseHashSet<TypeId> types{{}};
ReferenceCountInitializer_DEPRECATED rci{&types};
if (auto ec = get<EqualityConstraint>(*this))
{
rci.traverse(ec->resultType);
// `EqualityConstraints` should not mutate `assignmentType`.
}
else if (auto sc = get<SubtypeConstraint>(*this))
{
rci.traverse(sc->subType);
rci.traverse(sc->superType);
}
else if (auto psc = get<PackSubtypeConstraint>(*this))
{
rci.traverse(psc->subPack);
rci.traverse(psc->superPack);
}
else if (auto itc = get<IterableConstraint>(*this))
{
for (TypeId ty : itc->variables)
rci.traverse(ty);
// `IterableConstraints` should not mutate `iterator`.
}
else if (auto nc = get<NameConstraint>(*this))
{
rci.traverse(nc->namedType);
}
else if (auto taec = get<TypeAliasExpansionConstraint>(*this))
{
rci.traverse(taec->target);
}
else if (auto fchc = get<FunctionCheckConstraint>(*this))
{
rci.traverse(fchc->argsPack);
}
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization4)
{
rci.traverseIntoTypeFunctions = false;
rci.traverse(fcc->fn);
rci.traverse(fcc->argsPack);
rci.traverseIntoTypeFunctions = true;
}
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
{
rci.traverse(ptc->freeType);
}
else if (auto hpc = get<HasPropConstraint>(*this))
{
rci.traverse(hpc->resultType);
if (FFlag::LuauEagerGeneralization4)
rci.traverse(hpc->subjectType);
}
else if (auto hic = get<HasIndexerConstraint>(*this))
{
if (FFlag::LuauEagerGeneralization4)
rci.traverse(hic->subjectType);
rci.traverse(hic->resultType);
// `HasIndexerConstraint` should not mutate `indexType`.
}
else if (auto apc = get<AssignPropConstraint>(*this))
{
rci.traverse(apc->lhsType);
rci.traverse(apc->rhsType);
}
else if (auto aic = get<AssignIndexConstraint>(*this))
{
rci.traverse(aic->lhsType);
rci.traverse(aic->indexType);
rci.traverse(aic->rhsType);
}
else if (auto uc = get<UnpackConstraint>(*this))
{
for (TypeId ty : uc->resultPack)
rci.traverse(ty);
// `UnpackConstraint` should not mutate `sourcePack`.
}
else if (auto rpc = get<ReducePackConstraint>(*this))
{
rci.traverse(rpc->tp);
}
else if (auto tcc = get<TableCheckConstraint>(*this))
{
rci.traverse(tcc->exprType);
}
if (FFlag::LuauPushFunctionTypesInFunctionStatement)
{
if (auto pftc = get<PushFunctionTypeConstraint>(*this))
{
rci.traverse(pftc->functionType);
}
}
return types;
}
TypeIds Constraint::getMaybeMutatedFreeTypes() const
{
// For the purpose of this function and reference counting in general, we are only considering

View file

@ -36,21 +36,16 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType)
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType)
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType2)
LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAGVARIABLE(LuauTrackFreeInteriorTypePacks)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
namespace Luau
{
@ -233,6 +228,11 @@ ConstraintGenerator::ConstraintGenerator(
, logger(logger)
{
LUAU_ASSERT(module);
if (FFlag::LuauEagerGeneralization4)
{
LUAU_ASSERT(FFlag::LuauTrackFreeInteriorTypePacks);
}
}
ConstraintSet ConstraintGenerator::run(AstStatBlock* block)
@ -261,7 +261,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
rootScope->location = block->location;
module->astScopes[block] = NotNull{scope.get()};
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back();
else
DEPRECATED_interiorTypes.emplace_back();
@ -297,7 +297,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
}
);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
{
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -316,7 +316,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
}
);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.pop_back();
else
DEPRECATED_interiorTypes.pop_back();
@ -354,13 +354,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
// Pre
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back();
else
DEPRECATED_interiorTypes.emplace_back();
visitBlockWithoutChildScope(resumeScope, block);
// Post
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.pop_back();
else
DEPRECATED_interiorTypes.pop_back();
@ -390,9 +390,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
{
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
{
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
const TypeId ft = FFlag::LuauEagerGeneralization4
? Luau::freshType(arena, builtinTypes, scope.get(), polarity)
: Luau::freshType(arena, builtinTypes, scope.get());
interiorFreeTypes.back().types.push_back(ft);
if (FFlag::LuauEagerGeneralization4)
@ -412,7 +415,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
{
FreeTypePack f{scope.get(), polarity};
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.back().typePacks.push_back(result);
return result;
}
@ -1036,6 +1039,11 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat)
{
RecursionLimiter limiter{"ConstraintGenerator", &recursionCount, FInt::LuauCheckRecursionLimit};
if (FFlag::LuauTrackFreeInteriorTypePacks)
LUAU_ASSERT(DEPRECATED_interiorTypes.empty());
else
LUAU_ASSERT(interiorFreeTypes.empty());
if (auto s = stat->as<AstStatBlock>())
return visit(scope, s);
else if (auto i = stat->as<AstStatIf>())
@ -1741,16 +1749,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAss
{
TypeId resultTy = checkAstExprBinary(scope, assign->location, assign->op, assign->var, assign->value, std::nullopt).ty;
module->astCompoundAssignResultTypes[assign] = resultTy;
if (!FFlag::LuauSkipLvalueForCompoundAssignment)
{
TypeId lhsType = check(scope, assign->var).ty;
visitLValue(scope, assign->var, lhsType);
follow(lhsType);
follow(resultTy);
}
// NOTE: We do not update lvalues for compound assignments. This is
// intentional.
return ControlFlow::None;
}
@ -1870,7 +1870,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
// Place this function as a child of the non-type function scope
scope->children.push_back(NotNull{sig.signatureScope.get()});
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back();
else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -1888,7 +1888,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
}
);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
{
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -1897,7 +1897,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.pop_back();
else
DEPRECATED_interiorTypes.pop_back();
@ -1993,7 +1993,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
}
);
if (FFlag::LuauLimitDynamicConstraintSolving)
if (FFlag::LuauLimitDynamicConstraintSolving3)
{
// If we don't emplace an error type here, then later we'll be
// exposing a blocked type in this file's type interface. This
@ -2074,8 +2074,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
{
Luau::Property& prop = props[propName];
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (auto readTy = prop.readTy)
{
// We special-case this logic to keep the intersection flat; otherwise we
@ -2130,36 +2128,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
}
}
}
else
{
TypeId currentTy = prop.type_DEPRECATED();
// We special-case this logic to keep the intersection flat; otherwise we
// would create a ton of nested intersection types.
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
{
std::vector<TypeId> options = itv->parts;
options.push_back(propTy);
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
prop.readTy = newItv;
prop.writeTy = newItv;
}
else if (get<FunctionType>(currentTy))
{
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
prop.readTy = intersection;
prop.writeTy = intersection;
}
else
{
reportError(
declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}
);
}
}
}
}
return ControlFlow::None;
@ -2653,7 +2621,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
//
// The intent is (probably) not for this to be an array-like table with a massive
// union for the value, but instead a `{ string }`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0)
if (largeTableDepth > 0)
return Inference{builtinTypes->stringType};
TypeId freeTy = nullptr;
@ -2694,7 +2662,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
//
// The intent is (probably) not for this to be a table where each element
// is potentially `true` or `false` as a singleton, but just `boolean`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0)
if (largeTableDepth > 0)
return Inference{builtinTypes->booleanType};
TypeId freeTy = nullptr;
@ -2866,7 +2834,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
Checkpoint startCheckpoint = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back();
else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -2884,7 +2852,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
}
);
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
{
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -3114,7 +3082,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifEls
applyRefinements(elseScope, ifElse->falseExpr->location, refinementArena.negation(refinement));
TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty;
if (FFlag::LuauInferActualIfElseExprType)
if (FFlag::LuauInferActualIfElseExprType2)
return Inference{makeUnion(scope, ifElse->location, thenType, elseType)};
else
return Inference{expectedType ? *expectedType : makeUnion(scope, ifElse->location, thenType, elseType)};
@ -3406,11 +3374,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
ttv->definitionLocation = expr->location;
ttv->scope = scope.get();
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 &&
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth++;
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.back().types.push_back(ty);
else
DEPRECATED_interiorTypes.back().push_back(ty);
@ -3503,23 +3470,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
}
}
if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck2)
{
addConstraint(
scope,
expr->location,
TableCheckConstraint{
*expectedType,
ty,
expr,
NotNull{&module->astTypes},
NotNull{&module->astExpectedTypes},
}
);
}
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 &&
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth--;
return Inference{ty};
@ -3775,7 +3726,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
else
return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments);
}
else if (FFlag::LuauLimitDynamicConstraintSolving && ref->name == "_luau_blocked_type")
else if (FFlag::LuauLimitDynamicConstraintSolving3 && ref->name == "_luau_blocked_type")
{
return arena->addType(BlockedType{});
}
@ -3871,11 +3822,6 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
p.readTy = propTy;
break;
case AstTableAccess::Write:
if (!FFlag::LuauEnableWriteOnlyProperties)
{
reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"});
p.readTy = propTy;
}
p.writeTy = propTy;
break;
default:
@ -4401,17 +4347,6 @@ struct GlobalPrepopulator : AstVisitor
void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program)
{
if (!FFlag::LuauGlobalVariableModuleIsolation)
{
FragmentTypeCheckGlobalPrepopulator_DEPRECATED gp{NotNull{globalScope.get()}, NotNull{resumeScope.get()}, dfg, arena};
if (prepareModuleScope)
prepareModuleScope(module->name, resumeScope);
program->visit(&gp);
}
// Handle type function globals as well, without preparing a module scope since they have a separate environment
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
program->visit(&tfgp);

View file

@ -35,22 +35,40 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint)
LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAGVARIABLE(LuauUseOrderedTypeSetsInConstraints)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2)
LUAU_FASTFLAGVARIABLE(LuauCollapseShouldNotCrash)
LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking)
LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving3)
LUAU_FASTFLAGVARIABLE(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
namespace Luau
{
static void hashCombine(size_t& seed, size_t hash)
{
// Golden Ratio constant used for better hash scattering
// See https://softwareengineering.stackexchange.com/a/402543
seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
bool SubtypeConstraintRecord::operator==(const SubtypeConstraintRecord& other) const
{
return (subTy == other.subTy) && (superTy == other.superTy) && (variance == other.variance);
}
size_t HashSubtypeConstraintRecord::operator()(const SubtypeConstraintRecord& c) const
{
size_t result = 0;
hashCombine(result, intptr_t(c.subTy));
hashCombine(result, intptr_t(c.superTy));
hashCombine(result, intptr_t(c.variance));
return result;
}
static void dump(ConstraintSolver* cs, ToStringOptions& opts);
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
@ -426,8 +444,6 @@ void ConstraintSolver::run()
// Free types that have no constraints at all can be generalized right away.
if (FFlag::LuauEagerGeneralization4)
{
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
for (TypeId ty : constraintSet.freeTypes)
{
@ -435,16 +451,6 @@ void ConstraintSolver::run()
generalizeOneType(ty);
}
}
else
{
for (TypeId ty : constraintSet.freeTypes)
{
if (auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(ty);
it == mutatedFreeTypeToConstraint_DEPRECATED.end() || it->second.empty())
generalizeOneType(ty);
}
}
}
constraintSet.freeTypes.clear();
@ -469,7 +475,7 @@ void ConstraintSolver::run()
// If we were _given_ a limit, and the current limit has hit zero, ]
// then early exit from constraint solving.
if (FFlag::LuauLimitDynamicConstraintSolving && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0)
if (FFlag::LuauLimitDynamicConstraintSolving3 && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0)
break;
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
@ -492,8 +498,6 @@ void ConstraintSolver::run()
unblock(c);
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
if (const auto maybeMutated = maybeMutatedFreeTypes.find(c); maybeMutated != maybeMutatedFreeTypes.end())
{
DenseHashSet<TypeId> seen{nullptr};
@ -527,44 +531,6 @@ void ConstraintSolver::run()
generalizeOneType(ty);
}
}
}
else
{
const auto maybeMutated = maybeMutatedFreeTypes_DEPRECATED.find(c);
if (maybeMutated != maybeMutatedFreeTypes_DEPRECATED.end())
{
DenseHashSet<TypeId> seen{nullptr};
for (auto ty : maybeMutated->second)
{
// There is a high chance that this type has been rebound
// across blocked types, rebound free types, pending
// expansion types, etc, so we need to follow it.
ty = follow(ty);
if (FFlag::LuauEagerGeneralization4)
{
if (seen.contains(ty))
continue;
seen.insert(ty);
}
size_t& refCount = unresolvedConstraints[ty];
if (refCount > 0)
refCount -= 1;
// We have two constraints that are designed to wait for the
// refCount on a free type to be equal to 1: the
// PrimitiveTypeConstraint and ReduceConstraint. We
// therefore wake any constraint waiting for a free type's
// refcount to be 1 or 0.
if (refCount <= 1)
unblock(ty, Location{});
if (FFlag::LuauEagerGeneralization4 && refCount == 0)
generalizeOneType(ty);
}
}
}
if (logger)
@ -731,8 +697,6 @@ struct TypeSearcher : TypeVisitor
void ConstraintSolver::initFreeTypeTracking()
{
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
for (auto c : this->constraints)
{
unsolvedConstraints.emplace_back(c);
@ -756,34 +720,6 @@ void ConstraintSolver::initFreeTypeTracking()
block(dep, c);
}
}
}
else
{
for (NotNull<Constraint> c : this->constraints)
{
unsolvedConstraints.emplace_back(c);
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes_DEPRECATED();
for (auto ty : maybeMutatedTypesPerConstraint)
{
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
refCount += 1;
if (FFlag::LuauEagerGeneralization4)
{
auto [it, fresh] = mutatedFreeTypeToConstraint_DEPRECATED.try_emplace(ty, nullptr);
it->second.insert(c.get());
}
}
maybeMutatedFreeTypes_DEPRECATED.emplace(c, maybeMutatedTypesPerConstraint);
for (NotNull<const Constraint> dep : c->dependencies)
{
block(dep, c);
}
}
}
}
void ConstraintSolver::generalizeOneType(TypeId ty)
@ -1838,9 +1774,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
DenseHashMap<TypeId, TypeId> replacements{nullptr};
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
if (FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck)
{
DenseHashSet<const void*> genericTypesAndPacks{nullptr};
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
@ -1966,98 +1899,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
inheritBlocks(constraint, addition);
}
}
}
else
{
ContainsGenerics_DEPRECATED containsGenerics;
for (auto generic : ftv->generics)
{
replacements[generic] = builtinTypes->unknownType;
containsGenerics.generics.insert(generic);
}
for (auto genericPack : ftv->genericPacks)
{
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
containsGenerics.generics.insert(genericPack);
}
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
// If this is a self call, the types will have more elements than the AST call.
// We don't attempt to perform bidirectional inference on the self type.
const size_t typeOffset = c.callSite->self ? 1 : 0;
for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i)
{
const TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]);
const TypeId actualArgTy = follow(argPackHead[i + typeOffset]);
AstExpr* expr = unwrapGroup(c.callSite->args.data[i]);
(*c.astExpectedTypes)[expr] = expectedArgTy;
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
// Generic types are skipped over entirely, for now.
if (containsGenerics.hasGeneric(expectedArgTy))
{
if (!lambdaTy || !lambdaTy->argTypes)
continue;
const TypePack* argTp = get<TypePack>(follow(lambdaTy->argTypes));
if (!argTp || !argTp->tail)
continue;
if (const VariadicTypePack* argTpTail = get<VariadicTypePack>(follow(argTp->tail));
argTpTail && argTpTail->hidden && argTpTail->ty == builtinTypes->anyType)
{
// Strip variadic any
const TypePackId anyLessArgTp = arena->addTypePack(TypePack{argTp->head});
const TypeId newFuncTypeId = arena->addType(FunctionType{anyLessArgTp, lambdaTy->retTypes});
FunctionType* newFunc = getMutable<FunctionType>(newFuncTypeId);
newFunc->argNames = lambdaTy->argNames;
(*c.astTypes)[expr] = newFuncTypeId;
}
continue;
}
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
if (expectedLambdaTy && lambdaTy && lambdaExpr)
{
const std::vector<TypeId> expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first;
const std::vector<TypeId> lambdaArgTys = flatten(lambdaTy->argTypes).first;
for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j)
{
if (!lambdaExpr->args.data[j]->annotation && get<FreeType>(follow(lambdaArgTys[j])))
{
shiftReferences(lambdaArgTys[j], expectedLambdaArgTys[j]);
bind(constraint, lambdaArgTys[j], expectedLambdaArgTys[j]);
}
}
}
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
expr->is<AstExprConstantNil>())
{
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
u2.unify(actualArgTy, expectedArgTy);
}
else if (expr->is<AstExprTable>())
{
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
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());
}
}
}
return true;
}
@ -2620,23 +2461,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
};
if (auto lhsFree = getMutable<FreeType>(lhsType))
{
if (FFlag::LuauMissingFollowInAssignIndexConstraint)
{
if (auto lhsTable = getMutable<TableType>(follow(lhsFree->upperBound)))
{
if (auto res = tableStuff(lhsTable))
return *res;
}
}
else
{
if (auto lhsTable = getMutable<TableType>(lhsFree->upperBound))
{
if (auto res = tableStuff(lhsTable))
return *res;
}
}
TypeId newUpperBound =
arena->addType(TableType{/*props*/ {}, TableIndexer{indexType, rhsType}, TypeLevel{}, constraint->scope, TableState::Free});
@ -3400,17 +3230,12 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
return {{}, result.propType};
// TODO: __index can be an overloaded function.
//
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// if the property is write-only, then surely we cannot read from it.
if (indexProp->second.isWriteOnly())
return {{}, builtinTypes->errorType};
}
TypeId indexType =
FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED());
TypeId indexType = follow(*indexProp->second.readTy);
if (auto ft = get<FunctionType>(indexType))
{
@ -3448,17 +3273,12 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
if (indexProp == metatable->props.end())
return {{}, std::nullopt};
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// if the property is write-only, then surely we cannot read from it.
if (indexProp->second.isWriteOnly())
return {{}, builtinTypes->errorType};
return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen);
}
else
return lookupTableProp(constraint, indexProp->second.type_DEPRECATED(), propName, context, inConditional, suppressSimplification, seen);
}
else if (auto ft = get<FreeType>(subjectType))
{
const TypeId upperBound = follow(ft->upperBound);
@ -3826,7 +3646,7 @@ bool ConstraintSolver::isBlocked(TypeId ty) const
if (auto tfit = get<TypeFunctionInstanceType>(ty))
{
if (FFlag::LuauStuckTypeFunctionsStillDispatch && tfit->state != TypeFunctionInstanceState::Unsolved)
if (FFlag::LuauEagerGeneralization4 && tfit->state != TypeFunctionInstanceState::Unsolved)
return false;
return uninhabitedTypeFunctions.contains(ty) == false;
}
@ -3852,12 +3672,31 @@ bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint) const
NotNull<Constraint> ConstraintSolver::pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv)
{
std::optional<SubtypeConstraintRecord> scr;
if (FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints)
{
if (auto sc = cv.get_if<SubtypeConstraint>())
scr.emplace(SubtypeConstraintRecord{sc->subType, sc->superType, SubtypingVariance::Covariant});
else if (auto ec = cv.get_if<EqualityConstraint>())
scr.emplace(SubtypeConstraintRecord{ec->assignmentType, ec->resultType, SubtypingVariance::Invariant});
}
if (scr)
{
if (auto f = seenConstraints.find(*scr))
return NotNull{*f};
}
std::unique_ptr<Constraint> c = std::make_unique<Constraint>(scope, location, std::move(cv));
NotNull<Constraint> borrow = NotNull(c.get());
if (scr)
seenConstraints[*scr] = borrow;
solverConstraints.push_back(std::move(c));
unsolvedConstraints.emplace_back(borrow);
if (FFlag::LuauLimitDynamicConstraintSolving)
if (FFlag::LuauLimitDynamicConstraintSolving3)
{
if (solverConstraintLimit > 0)
{
@ -3962,9 +3801,6 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
if (FFlag::LuauEagerGeneralization4)
{
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
if (auto it = mutatedFreeTypeToConstraint.find(source); it != mutatedFreeTypeToConstraint.end())
{
const OrderedSet<const Constraint*>& constraintsAffectedBySource = it->second;
@ -3980,27 +3816,6 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
}
}
}
else
{
auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(source);
if (it != mutatedFreeTypeToConstraint_DEPRECATED.end())
{
const DenseHashSet<const Constraint*>& constraintsAffectedBySource = it->second;
auto [it2, fresh2] = mutatedFreeTypeToConstraint_DEPRECATED.try_emplace(target, DenseHashSet<const Constraint*>{nullptr});
DenseHashSet<const Constraint*>& constraintsAffectedByTarget = it2->second;
// auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet<const Constraint*>{nullptr});
for (const Constraint* constraint : constraintsAffectedBySource)
{
constraintsAffectedByTarget.insert(constraint);
auto [it3, fresh3] = maybeMutatedFreeTypes_DEPRECATED.try_emplace(NotNull{constraint}, DenseHashSet<TypeId>{nullptr});
it3->second.insert(target);
}
}
}
}
}
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type)

View file

@ -13,7 +13,6 @@
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauDfgForwardNilFromAndOr)
@ -154,18 +153,6 @@ void DfgScope::inherit(const DfgScope* childScope)
}
}
bool DfgScope::canUpdateDefinition(Symbol symbol) const
{
// NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops
return true;
}
bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
{
// NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops
return true;
}
DataFlowGraphBuilder::DataFlowGraphBuilder(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena)
: graph{defArena, keyArena}
, defArena{defArena}
@ -185,14 +172,7 @@ DataFlowGraph DataFlowGraphBuilder::build(
DataFlowGraphBuilder builder(defArena, keyArena);
builder.handle = handle;
DfgScope* moduleScope;
// We're not explicitly calling makeChildScope here because that function relies on currentScope
// which guarantees that the scope being returned is NotNull
// This means that while the scope stack is empty, we'll have to manually initialize the global scope
if (FFlag::LuauDfgScopeStackNotNull)
moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get();
else
moduleScope = builder.makeChildScope();
DfgScope* moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get();
PushScope ps{builder.scopeStack, moduleScope};
builder.visitBlockWithoutChildScope(block);
builder.resolveCaptures();
@ -230,19 +210,9 @@ NotNull<DfgScope> DataFlowGraphBuilder::currentScope()
return NotNull{scopeStack.back()};
}
DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED()
{
if (scopeStack.empty())
return nullptr; // nullptr is the root DFG scope.
return scopeStack.back();
}
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
{
if (FFlag::LuauDfgScopeStackNotNull)
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
else
return scopes.emplace_back(new DfgScope{currentScope_DEPRECATED(), scopeType}).get();
}
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
@ -319,7 +289,7 @@ void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const
DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
{
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
// true if any of the considered scopes are a loop.
bool outsideLoopScope = false;
@ -352,7 +322,7 @@ DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location location)
{
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
for (DfgScope* current = scope; current; current = current->parent)
{
if (auto props = current->props.find(def))
@ -398,10 +368,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
cf = visitBlockWithoutChildScope(b);
}
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(child);
else
currentScope_DEPRECATED()->inherit(child);
return cf;
}
@ -486,7 +453,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
elsecf = visit(i->elsebody);
}
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
// If the control flow from the `if` or `else` block is non-linear,
// then we should assume that the _other_ branch is the one taken.
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
@ -615,10 +582,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
}
}
graph.localDefs[local] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[local] = def;
else
currentScope_DEPRECATED()->bindings[local] = def;
captures[local].allVersions.push_back(def);
}
@ -675,10 +639,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
DefId def = defArena->freshCell(local, local->location);
graph.localDefs[local] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[local] = def;
else
currentScope_DEPRECATED()->bindings[local] = def;
captures[local].allVersions.push_back(def);
}
@ -757,10 +718,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
{
DefId def = defArena->freshCell(l->name, l->location);
graph.localDefs[l->name] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[l->name] = def;
else
currentScope_DEPRECATED()->bindings[l->name] = def;
captures[l->name].allVersions.push_back(def);
visitExpr(l->func);
@ -793,10 +751,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
{
DefId def = defArena->freshCell(d->name, d->nameLocation);
graph.declaredDefs[d] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[d->name] = def;
else
currentScope_DEPRECATED()->bindings[d->name] = def;
captures[d->name].allVersions.push_back(def);
visitType(d->type);
@ -808,10 +763,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
{
DefId def = defArena->freshCell(d->name, d->nameLocation);
graph.declaredDefs[d] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[d->name] = def;
else
currentScope_DEPRECATED()->bindings[d->name] = def;
captures[d->name].allVersions.push_back(def);
DfgScope* unreachable = makeChildScope();
@ -1058,10 +1010,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
{
DefId tableCell = defArena->freshCell(Symbol{}, t->location);
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->props[tableCell] = {};
else
currentScope_DEPRECATED()->props[tableCell] = {};
for (AstExprTable::Item item : t->items)
{
DataFlowResult result = visitExpr(item.value);
@ -1070,10 +1019,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
visitExpr(item.key);
if (auto string = item.key->as<AstExprConstantString>())
{
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->props[tableCell][string->value.data] = result.def;
else
currentScope_DEPRECATED()->props[tableCell][string->value.data] = result.def;
}
}
}
@ -1168,10 +1114,10 @@ void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
{
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
// In order to avoid alias tracking, we need to clip the reference to the parent def.
if (scope->canUpdateDefinition(l->local) && !l->upvalue)
if (!l->upvalue)
{
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
scope->bindings[l->local] = updated;
@ -1184,33 +1130,22 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, DefId incomingDef)
{
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
// In order to avoid alias tracking, we need to clip the reference to the parent def.
if (scope->canUpdateDefinition(g->name))
{
DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef));
scope->bindings[g->name] = updated;
captures[g->name].allVersions.push_back(updated);
return updated;
}
else
return visitExpr(static_cast<AstExpr*>(g)).def;
}
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef)
{
DefId parentDef = visitExpr(i->expr).def;
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
if (scope->canUpdateDefinition(parentDef, i->index.value))
{
DfgScope* scope = currentScope();
DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef));
scope->props[parentDef][i->index.value] = updated;
return updated;
}
else
return visitExpr(static_cast<AstExpr*>(i)).def;
}
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
@ -1218,18 +1153,13 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
DefId parentDef = visitExpr(i->expr).def;
visitExpr(i->index);
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
DfgScope* scope = currentScope();
if (auto string = i->index->as<AstExprConstantString>())
{
if (scope->canUpdateDefinition(parentDef, string->value.data))
{
DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef));
scope->props[parentDef][string->value.data] = updated;
return updated;
}
else
return visitExpr(static_cast<AstExpr*>(i)).def;
}
else
return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true);
}

View file

@ -1,8 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h"
LUAU_FASTFLAG(LuauTypeFunOptional)
namespace Luau
{
@ -364,29 +362,6 @@ export type type = {
static constexpr const char* kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
declare types: {
unknown: type,
never: type,
any: type,
boolean: type,
number: type,
string: type,
thread: type,
buffer: type,
singleton: @checked (arg: string | boolean | nil) -> type,
generic: @checked (name: string, ispack: boolean?) -> type,
negationof: @checked (arg: type) -> type,
unionof: @checked (...type) -> type,
intersectionof: @checked (...type) -> type,
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
copy: @checked (arg: type) -> type,
}
)BUILTIN_SRC";
static constexpr const char* kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
declare types: {
unknown: type,
never: type,
@ -415,9 +390,6 @@ std::string getTypeFunctionDefinitionSource()
std::string result = kBuiltinDefinitionTypeMethodSrc;
if (FFlag::LuauTypeFunOptional)
result += kBuiltinDefinitionTypesLibWithOptionalSrc;
else
result += kBuiltinDefinitionTypesLibSrc;
return result;

View file

@ -12,7 +12,6 @@
#include "Luau/Type.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeFunction.h"
#include "Luau/VisitType.h"
#include <fstream>
#include <iomanip>
@ -25,7 +24,6 @@
LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplification)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplificationToDot)
LUAU_FASTFLAGVARIABLE(DebugLuauExtraEqSatSanityChecks)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
namespace Luau::EqSatSimplification
{
@ -2371,14 +2369,9 @@ void Simplifier::intersectTableProperty(Id id)
}
Id newTableProp =
FFlag::LuauRemoveTypeCallsForReadWriteProps
? egraph.add(Intersection{
egraph.add(Intersection{
toId(egraph, builtinTypes, mappingIdToClass, stringCache, *it->second.readTy),
toId(egraph, builtinTypes, mappingIdToClass, stringCache, *table1Ty->props.begin()->second.readTy)
})
: egraph.add(Intersection{
toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type_DEPRECATED()),
toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type_DEPRECATED())
});
newIntersectionParts.push_back(egraph.add(TTable{jId, {stringCache.add(it->first)}, {newTableProp}}));
@ -2451,10 +2444,7 @@ void Simplifier::unneededTableModification(Id id)
StringId propName = tbl->propNames[i];
const Id propType = tbl->propTypes()[i];
Id importedProp =
FFlag::LuauRemoveTypeCallsForReadWriteProps
? toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy)
: toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type_DEPRECATED());
Id importedProp = toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy);
if (find(importedProp) != find(propType))
{

View file

@ -20,9 +20,8 @@
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictReportsOneIndexedErrors)
static std::string wrongNumberOfArgsString(
size_t expectedCount,
@ -427,7 +426,7 @@ struct ErrorConverter
}
else
{
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
return it->second.readTy;
else
return it->second.type_DEPRECATED();
@ -462,11 +461,8 @@ struct ErrorConverter
return err;
}
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
{
if (auto primitiveTy = get<PrimitiveType>(follow(e.ty)); primitiveTy && primitiveTy->type == PrimitiveType::Function)
return "The type " + toString(e.ty) + " is not precise enough for us to determine the appropriate result type of this call.";
}
return "Cannot call a value of type " + toString(e.ty);
}
@ -785,6 +781,10 @@ struct ErrorConverter
std::string operator()(const CheckedFunctionCallError& e) const
{
// TODO: What happens if checkedFunctionName cannot be found??
if (FFlag::LuauNewNonStrictReportsOneIndexedErrors)
return "Function '" + e.checkedFunctionName + "' expects '" + toString(e.expected) + "' at argument #" +
std::to_string(e.argumentIndex + 1) + ", but got '" + Luau::toString(e.passed) + "'";
else
return "Function '" + e.checkedFunctionName + "' expects '" + toString(e.expected) + "' at argument #" + std::to_string(e.argumentIndex) +
", but got '" + Luau::toString(e.passed) + "'";
}

View file

@ -31,17 +31,11 @@ LUAU_FASTINT(LuauTypeInferIterationLimit);
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauFragmentRequiresCanBeResolvedToAModule)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauPopulateSelfTypesInFragment)
LUAU_FASTFLAGVARIABLE(LuauForInProvidesRecommendations)
namespace Luau
{
@ -162,22 +156,52 @@ Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPo
if (auto forStat = nearestStatement->as<AstStatFor>())
{
if (FFlag::LuauForInProvidesRecommendations)
{
if (forStat->step && forStat->step->location.containsClosed(cursorPosition))
return {forStat->step->location.begin, cursorPosition};
if (forStat->to && forStat->to->location.containsClosed(cursorPosition))
return {forStat->to->location.begin, cursorPosition};
if (forStat->from && forStat->from->location.containsClosed(cursorPosition))
return {forStat->from->location.begin, cursorPosition};
}
if (!forStat->hasDo)
return nonEmpty;
else
{
if (FFlag::LuauForInProvidesRecommendations)
{
auto completeableExtents = Location{forStat->location.begin, forStat->doLocation.begin};
if (completeableExtents.containsClosed(cursorPosition))
return nonEmpty;
}
return empty;
}
}
if (auto forIn = nearestStatement->as<AstStatForIn>())
{
// If we don't have a do statement
if (!forIn->hasDo)
return nonEmpty;
else
{
if (FFlag::LuauForInProvidesRecommendations)
{
auto completeableExtents = Location{forIn->location.begin, forIn->doLocation.begin};
if (completeableExtents.containsClosed(cursorPosition))
{
if (!forIn->hasIn)
return nonEmpty;
else
return Location{forIn->inLocation.begin, cursorPosition};
}
}
return empty;
}
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
{
}
if (auto ifS = getNearestIfToCursor(nearestStatement, cursorPosition))
{
auto conditionExtents = Location{ifS->condition->location.begin, ifS->condition->location.end};
@ -208,29 +232,6 @@ Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPo
return empty;
}
}
}
else
{
if (auto ifS = nearestStatement->as<AstStatIf>())
{
auto conditionExtents = Location{ifS->location.begin, ifS->condition->location.end};
if (conditionExtents.containsClosed(cursorPosition))
return nonEmpty;
else if (ifS->thenbody->location.containsClosed(cursorPosition))
return empty;
else if (auto elseS = ifS->elsebody)
{
if (auto elseIf = ifS->elsebody->as<AstStatIf>())
{
if (elseIf->thenbody->hasEnd)
return empty;
else
return {elseS->location.begin, cursorPosition};
}
return empty;
}
}
}
return nonEmpty;
}
@ -381,8 +382,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* st
// the freshest ast can sometimes be null if the parse was bad.
if (lastGoodParse == nullptr)
return {};
FragmentRegion region = FFlag::LuauBlockDiffFragmentSelection ? getFragmentRegionWithBlockDiff(stale, lastGoodParse, cursorPos)
: getFragmentRegion(lastGoodParse, cursorPos);
FragmentRegion region = getFragmentRegionWithBlockDiff(stale, lastGoodParse, cursorPos);
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(stale, cursorPos);
LUAU_ASSERT(ancestry.size() >= 1);
// We should only pick up locals that are before the region
@ -524,19 +524,11 @@ std::optional<FragmentParseResult> parseFragment(
if (p.root == nullptr)
return std::nullopt;
std::vector<AstNode*> fabricatedAncestry;
// Moves cannot be on the rhs of a ? : statement, so the assignment is placed in an if else stmt
// Make sure to trim this comment after you remove FFlag::LuauFragmentAutocompleteIfRecommendations
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
fabricatedAncestry = findAncestryAtPositionForAutocomplete(mostRecentParse, cursorPos);
else
fabricatedAncestry = std::move(result.ancestry);
std::vector<AstNode*> fabricatedAncestry = findAncestryAtPositionForAutocomplete(mostRecentParse, cursorPos);
std::vector<AstNode*> fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorPos);
// Computes the accurate ancestry and then replaces the nodes that correspond to the fragment ancestry
// Needed because we look up types by pointer identity
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
{
LUAU_ASSERT(!fabricatedAncestry.empty());
auto back = fabricatedAncestry.size() - 1;
for (auto it = fragmentAncestry.rbegin(); it != fragmentAncestry.rend(); ++it)
@ -545,9 +537,6 @@ std::optional<FragmentParseResult> parseFragment(
fabricatedAncestry[back] = *it;
back--;
}
}
else
fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end());
if (nearestStatement == nullptr)
nearestStatement = p.root;
@ -629,7 +618,6 @@ struct UsageFinder : public AstVisitor
bool visit(AstExprGlobal* global) override
{
if (FFlag::LuauGlobalVariableModuleIsolation)
globalDefsToPrePopulate.emplace_back(global->name, dfg->getDef(global));
if (FFlag::LuauFragmentAutocompleteTracksRValueRefinements)
{
@ -640,12 +628,9 @@ struct UsageFinder : public AstVisitor
}
bool visit(AstStatFunction* function) override
{
if (FFlag::LuauGlobalVariableModuleIsolation)
{
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
globalFunctionsReferenced.emplace_back(g->name);
}
return true;
}
@ -702,13 +687,6 @@ void cloneTypesFromFragment(
destScope->lvalueTypes[d] = Luau::cloneIncremental(pair->second.typeId, *destArena, cloneState, destScope);
destScope->bindings[pair->first] = Luau::cloneIncremental(pair->second, *destArena, cloneState, destScope);
}
else if (FFlag::LuauBetterScopeSelection && !FFlag::LuauBlockDiffFragmentSelection)
{
destScope->lvalueTypes[d] = builtins->unknownType;
Binding b;
b.typeId = builtins->unknownType;
destScope->bindings[Symbol(loc)] = b;
}
}
if (FFlag::LuauFragmentAutocompleteTracksRValueRefinements)
@ -724,7 +702,7 @@ void cloneTypesFromFragment(
}
}
}
else if (FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver && !staleModule->checkedInNewSolver)
else if (!staleModule->checkedInNewSolver)
{
for (const auto& [d, loc] : f.localBindingsReferenced)
{
@ -761,8 +739,6 @@ void cloneTypesFromFragment(
}
}
if (FFlag::LuauGlobalVariableModuleIsolation)
{
// Fourth - prepopulate the global function types
for (const auto& name : f.globalFunctionsReferenced)
{
@ -797,7 +773,6 @@ void cloneTypesFromFragment(
destScope->lvalueTypes[def] = *ty;
}
}
}
// Finally, clone the returnType on the staleScope. This helps avoid potential leaks of free types.
if (staleScope->returnType)
@ -952,11 +927,6 @@ static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const
if (endPos.line == lineCount && endPos.column == colCount)
{
endOffset = docOffset;
if (!FFlag::LuauFragmentAutocompleteIfRecommendations)
{
while (endOffset < src.size() && src[endOffset] != '\n')
endOffset++;
}
foundEnd = true;
}
@ -1006,8 +976,6 @@ ScopePtr findClosestScope_DEPRECATED(const ModulePtr& module, const AstStat* nea
ScopePtr findClosestScope(const ModulePtr& module, const Position& scopePos)
{
LUAU_ASSERT(module->hasModuleScope());
if (FFlag::LuauBlockDiffFragmentSelection)
{
ScopePtr closest = module->getModuleScope();
// find the scope the nearest statement belonged to.
for (const auto& [loc, sc] : module->scopes)
@ -1020,18 +988,6 @@ ScopePtr findClosestScope(const ModulePtr& module, const Position& scopePos)
closest = sc;
}
return closest;
}
else
{
ScopePtr closest = module->getModuleScope();
// find the scope the nearest statement belonged to.
for (const auto& [loc, sc] : module->scopes)
{
if (sc->location.contains(scopePos) && closest->location.begin < sc->location.begin)
closest = sc;
}
return closest;
}
}
std::optional<FragmentParseResult> parseFragment_DEPRECATED(
@ -1228,7 +1184,7 @@ FragmentTypeCheckResult typecheckFragment_(
NotNull{&resolver},
frontend.builtinTypes,
iceHandler,
FFlag::LuauGlobalVariableModuleIsolation ? freshChildOfNearestScope : stale->getModuleScope(),
freshChildOfNearestScope,
frontend.globals.globalTypeFunctionScope,
nullptr,
nullptr,
@ -1299,8 +1255,6 @@ FragmentTypeCheckResult typecheckFragment_(
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd);
if (FFlag::LuauExpectedTypeVisitor)
{
ExpectedTypeVisitor etv{
NotNull{&incrementalModule->astTypes},
NotNull{&incrementalModule->astExpectedTypes},
@ -1310,7 +1264,6 @@ FragmentTypeCheckResult typecheckFragment_(
NotNull{freshChildOfNearestScope.get()}
};
root->visit(&etv);
}
// In frontend we would forbid internal types
// because this is just for autocomplete, we don't actually care
@ -1386,7 +1339,7 @@ FragmentTypeCheckResult typecheckFragment__DEPRECATED(
NotNull{&resolver},
frontend.builtinTypes,
iceHandler,
FFlag::LuauGlobalVariableModuleIsolation ? freshChildOfNearestScope : stale->getModuleScope(),
freshChildOfNearestScope,
frontend.globals.globalTypeFunctionScope,
nullptr,
nullptr,
@ -1457,8 +1410,6 @@ FragmentTypeCheckResult typecheckFragment__DEPRECATED(
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd);
if (FFlag::LuauExpectedTypeVisitor)
{
ExpectedTypeVisitor etv{
NotNull{&incrementalModule->astTypes},
NotNull{&incrementalModule->astExpectedTypes},
@ -1468,7 +1419,6 @@ FragmentTypeCheckResult typecheckFragment__DEPRECATED(
NotNull{freshChildOfNearestScope.get()}
};
root->visit(&etv);
}
// In frontend we would forbid internal types
// because this is just for autocomplete, we don't actually care
@ -1507,8 +1457,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
}
std::optional<FragmentParseResult> tryParse;
tryParse = FFlag::LuauBetterScopeSelection ? parseFragment(module->root, recentParse, module->names.get(), src, cursorPos, fragmentEndPosition)
: parseFragment_DEPRECATED(module->root, module->names.get(), src, cursorPos, fragmentEndPosition);
tryParse = parseFragment(module->root, recentParse, module->names.get(), src, cursorPos, fragmentEndPosition);
if (!tryParse)
@ -1520,8 +1469,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
FrontendOptions frontendOptions = opts.value_or(frontend.options);
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
: findClosestScope_DEPRECATED(module, parseResult.nearestStatement);
const ScopePtr& closestScope = findClosestScope(module, parseResult.scopePos);
FragmentTypeCheckResult result =
FFlag::LuauFragmentRequiresCanBeResolvedToAModule
? typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter)
@ -1590,13 +1538,11 @@ FragmentAutocompleteResult fragmentAutocomplete(
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();
if (FFlag::DebugLogFragmentsFromAutocomplete)
logLuau("Fragment Autocomplete Source Script", src);
TypeArena arenaForAutocomplete_DEPRECATED;
if (FFlag::LuauFragmentAcMemoryLeak)
unfreeze(tcResult.incrementalModule->internalTypes);
auto result = Luau::autocomplete_(
tcResult.incrementalModule,
frontend.builtinTypes,
FFlag::LuauFragmentAcMemoryLeak ? &tcResult.incrementalModule->internalTypes : &arenaForAutocomplete_DEPRECATED,
&tcResult.incrementalModule->internalTypes,
tcResult.ancestry,
globalScope,
tcResult.freshScope,
@ -1604,10 +1550,9 @@ FragmentAutocompleteResult fragmentAutocomplete(
frontend.fileResolver,
std::move(callback)
);
if (FFlag::LuauFragmentAcMemoryLeak)
freeze(tcResult.incrementalModule->internalTypes);
reportWaypoint(reporter, FragmentAutocompleteWaypoint::AutocompleteEnd);
return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(arenaForAutocomplete_DEPRECATED), std::move(result)};
return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(result)};
}
} // namespace Luau

View file

@ -44,12 +44,11 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor)
LUAU_FASTFLAGVARIABLE(LuauTrackTypeAllocations)
LUAU_FASTFLAGVARIABLE(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAGVARIABLE(DebugLuauAlwaysShowConstraintSolvingIncomplete)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
namespace Luau
{
@ -211,7 +210,8 @@ LoadDefinitionFileResult Frontend::loadDefinitionFile(
if (parseResult.errors.size() > 0)
return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), nullptr};
ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, {});
Frontend::Stats dummyStats;
ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, dummyStats, {});
if (checkedModule->errors.size() > 0)
return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), std::move(checkedModule)};
@ -988,6 +988,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
environmentScope,
/*forAutocomplete*/ true,
/*recordJsonLog*/ false,
item.stats,
std::move(typeCheckLimits)
);
@ -1018,7 +1019,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
}
ModulePtr module =
check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, std::move(typeCheckLimits));
check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, item.stats, std::move(typeCheckLimits));
double duration = getTimestamp() - timestamp;
@ -1171,6 +1172,8 @@ void Frontend::recordItemResult(const BuildQueueItem& item)
stats.strSingletonsMinted += item.stats.strSingletonsMinted;
stats.uniqueStrSingletonsMinted += item.stats.uniqueStrSingletonsMinted;
}
stats.dynamicConstraintsCreated += item.stats.dynamicConstraintsCreated;
}
void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state, size_t itemPos)
@ -1333,41 +1336,6 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
}
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
const std::vector<RequireCycle>& requireCycles,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> iceHandler,
NotNull<ModuleResolver> moduleResolver,
NotNull<FileResolver> fileResolver,
const ScopePtr& parentScope,
const ScopePtr& typeFunctionScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
FrontendOptions options,
TypeCheckLimits limits,
std::function<void(const ModuleName&, std::string)> writeJsonLog
)
{
const bool recordJsonLog = FFlag::DebugLuauLogSolverToJson;
return check(
sourceModule,
mode,
requireCycles,
builtinTypes,
iceHandler,
moduleResolver,
fileResolver,
parentScope,
typeFunctionScope,
std::move(prepareModuleScope),
std::move(options),
std::move(limits),
recordJsonLog,
std::move(writeJsonLog)
);
}
struct InternalTypeFinder : TypeOnceVisitor
{
InternalTypeFinder()
@ -1431,6 +1399,7 @@ ModulePtr check(
FrontendOptions options,
TypeCheckLimits limits,
bool recordJsonLog,
Frontend::Stats& stats,
std::function<void(const ModuleName&, std::string)> writeJsonLog
)
{
@ -1555,6 +1524,8 @@ ModulePtr check(
result->cancelled = true;
}
stats.dynamicConstraintsCreated += cs->solverConstraints.size();
if (recordJsonLog)
{
std::string output = logger->compileOutput();
@ -1641,8 +1612,6 @@ ModulePtr check(
result->errors.clear();
}
if (FFlag::LuauExpectedTypeVisitor)
{
ExpectedTypeVisitor etv{
NotNull{&result->astTypes},
NotNull{&result->astExpectedTypes},
@ -1652,11 +1621,10 @@ ModulePtr check(
NotNull{parentScope.get()}
};
sourceModule.root->visit(&etv);
}
// NOTE: This used to be done prior to cloning the public interface, but
// we now replace "internal" types with `*error-type*`.
if (FFlag::LuauLimitDynamicConstraintSolving)
if (FFlag::LuauLimitDynamicConstraintSolving3)
{
if (FFlag::DebugLuauForbidInternalTypes)
{
@ -1696,7 +1664,7 @@ ModulePtr check(
else
result->clonePublicInterface_DEPRECATED(builtinTypes, *iceHandler);
if (!FFlag::LuauLimitDynamicConstraintSolving)
if (!FFlag::LuauLimitDynamicConstraintSolving3)
{
if (FFlag::DebugLuauForbidInternalTypes)
{
@ -1751,6 +1719,7 @@ ModulePtr Frontend::check(
std::optional<ScopePtr> environmentScope,
bool forAutocomplete,
bool recordJsonLog,
Frontend::Stats& stats,
TypeCheckLimits typeCheckLimits
)
{
@ -1778,6 +1747,7 @@ ModulePtr Frontend::check(
options,
std::move(typeCheckLimits),
recordJsonLog,
stats,
writeJsonLog
);
}

View file

@ -15,11 +15,7 @@
#include "Luau/TypePack.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules)
namespace Luau
{
@ -484,8 +480,6 @@ struct FreeTypeSearcher : TypeVisitor
}
for (const auto& [_name, prop] : tt.props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.isReadOnly())
{
@ -502,10 +496,7 @@ struct FreeTypeSearcher : TypeVisitor
{
Polarity p = polarity;
polarity = Polarity::Mixed;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
traverse(*prop.readTy);
else
traverse(prop.type_DEPRECATED());
polarity = p;
}
else
@ -519,24 +510,6 @@ struct FreeTypeSearcher : TypeVisitor
polarity = p;
}
}
else
{
if (prop.isReadOnly())
traverse(*prop.readTy);
else
{
LUAU_ASSERT(prop.isShared());
Polarity p = polarity;
polarity = Polarity::Mixed;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
traverse(*prop.readTy);
else
traverse(prop.type_DEPRECATED());
polarity = p;
}
}
}
if (tt.indexer)
{
@ -1446,8 +1419,6 @@ struct GenericCounter : TypeVisitor
const Polarity previous = polarity;
for (const auto& [_name, prop] : tt.props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.isReadOnly())
{
@ -1464,10 +1435,7 @@ struct GenericCounter : TypeVisitor
{
Polarity p = polarity;
polarity = Polarity::Mixed;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
traverse(*prop.readTy);
else
traverse(prop.type_DEPRECATED());
polarity = p;
}
else
@ -1481,24 +1449,6 @@ struct GenericCounter : TypeVisitor
polarity = p;
}
}
else
{
if (prop.isReadOnly())
traverse(*prop.readTy);
else
{
LUAU_ASSERT(prop.isShared());
Polarity p = polarity;
polarity = Polarity::Mixed;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
traverse(*prop.readTy);
else
traverse(prop.type_DEPRECATED());
polarity = p;
}
}
}
if (tt.indexer)
{
@ -1596,7 +1546,7 @@ void pruneUnnecessaryGenerics(
{
if (state.count == 1 && state.polarity != Polarity::Mixed)
{
if (FFlag::LuauGeneralizationCannotMutateAcrossModules && arena.get() != generic->owningArena)
if (arena.get() != generic->owningArena)
continue;
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
}

View file

@ -6,7 +6,6 @@
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties)
namespace Luau
@ -51,8 +50,6 @@ struct InferPolarity : TypeVisitor
return false;
const Polarity p = polarity;
if (FFlag::LuauInferPolarityOfReadWriteProperties || FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
for (const auto& [name, prop] : tt.props)
{
if (prop.isShared())
@ -74,30 +71,6 @@ struct InferPolarity : TypeVisitor
traverse(*prop.writeTy);
}
}
}
else
{
for (const auto& [name, prop] : tt.props)
{
if (prop.isShared())
{
polarity = Polarity::Mixed;
traverse(prop.type_DEPRECATED());
}
else if (prop.isReadOnly())
{
polarity = p;
traverse(*prop.readTy);
}
else if (prop.isWriteOnly())
{
polarity = invert(p);
traverse(*prop.writeTy);
}
else
LUAU_ASSERT(!"Unreachable");
}
}
if (tt.indexer)
{

View file

@ -16,7 +16,7 @@
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
namespace Luau
{
@ -100,6 +100,7 @@ struct ClonePublicInterface : Substitution
// NOTE: This can be made non-optional after
// LuauUseWorkspacePropToChooseSolver is clipped.
std::optional<SolverMode> solverMode{std::nullopt};
bool internalTypeEscaped = false;
ClonePublicInterface(const TxnLog* log, NotNull<BuiltinTypes> builtinTypes, Module* module)
: Substitution(log, &module->interfaceTypes)
@ -177,21 +178,20 @@ struct ClonePublicInterface : Substitution
{
ttv->level = TypeLevel{0, 0};
if (isNewSolver())
{
ttv->scope = nullptr;
if (FFlag::LuauLimitDynamicConstraintSolving3)
ttv->state = TableState::Sealed;
}
}
if (isNewSolver())
{
if (FFlag::LuauLimitDynamicConstraintSolving)
if (FFlag::LuauLimitDynamicConstraintSolving3)
{
if (is<FreeType, BlockedType, PendingExpansionType>(ty))
{
module->errors.emplace_back(
Location{}, // Not amazing but the best we can do.
module->name,
InternalError{"An internal type is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
internalTypeEscaped = true;
result = builtinTypes->errorType;
}
else if (auto genericty = getMutable<GenericType>(result))
@ -225,16 +225,11 @@ struct ClonePublicInterface : Substitution
{
if (isNewSolver())
{
if (FFlag::LuauLimitDynamicConstraintSolving)
if (FFlag::LuauLimitDynamicConstraintSolving3)
{
if (is<FreeTypePack, BlockedTypePack>(tp))
{
module->errors.emplace_back(
Location{},
module->name,
InternalError{"An internal type pack is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
internalTypeEscaped = true;
return builtinTypes->errorTypePack;
}
@ -369,6 +364,16 @@ void Module::clonePublicInterface_DEPRECATED(NotNull<BuiltinTypes> builtinTypes,
*tf = clonePublicInterface.cloneTypeFun(*tf);
}
if (FFlag::LuauLimitDynamicConstraintSolving3 && clonePublicInterface.internalTypeEscaped)
{
errors.emplace_back(
Location{}, // Not amazing but the best we can do.
name,
InternalError{"An internal type is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
}
// Copy external stuff over to Module itself
this->returnType = moduleScope->returnType;
this->exportedTypeBindings = moduleScope->exportedTypeBindings;
@ -410,6 +415,16 @@ void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalEr
*tf = clonePublicInterface.cloneTypeFun(*tf);
}
if (FFlag::LuauLimitDynamicConstraintSolving3 && clonePublicInterface.internalTypeEscaped)
{
errors.emplace_back(
Location{}, // Not amazing but the best we can do.
name,
InternalError{"An internal type is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
}
// Copy external stuff over to Module itself
this->returnType = moduleScope->returnType;
this->exportedTypeBindings = moduleScope->exportedTypeBindings;

View file

@ -22,7 +22,6 @@
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictNoErrorsPassingNever)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressesDynamicRequireErrors)
@ -1095,8 +1094,6 @@ struct NonStrictTypeChecker
Scope* scope = findInnermostScope(tp->location);
LUAU_ASSERT(scope);
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
{
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
return;
@ -1111,28 +1108,6 @@ struct NonStrictTypeChecker
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
}
else
{
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
if (!alias.has_value())
{
if (scope->lookupType(tp->genericName.value))
{
reportError(
SwappedGenericTypeParameter{
tp->genericName.value,
SwappedGenericTypeParameter::Kind::Pack,
},
tp->location
);
}
}
else
{
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
}
}
}
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks)
{

View file

@ -21,7 +21,6 @@ LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauNormalizationIntersectTablesPreservesExternTypes)
LUAU_FASTFLAGVARIABLE(LuauNormalizationReorderFreeTypeIntersect)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
@ -3061,7 +3060,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
}
else if (get<TableType>(there) || get<MetatableType>(there))
{
if (useNewLuauSolver() && FFlag::LuauNormalizationIntersectTablesPreservesExternTypes)
if (useNewLuauSolver())
{
NormalizedExternType externTypes = std::move(here.externTypes);
TypeIds tables = std::move(here.tables);

View file

@ -10,7 +10,6 @@
#include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h"
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
namespace Luau
@ -298,8 +297,6 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
// If any of the unsatisfied arguments are not supertypes of
// nil or are `unknown`, then this overload does not match.
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
{
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
{
if (get<UnknownType>(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
{
@ -313,17 +310,6 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
return {Analysis::ArityMismatch, {std::move(error)}};
}
}
else
{
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
{
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
return {Analysis::ArityMismatch, {std::move(error)}};
}
}
}
return {Analysis::Ok, {}};
}

View file

@ -18,8 +18,6 @@
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauRelateTablesAreNeverDisjoint)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAGVARIABLE(LuauMissingSeenSetRelate)
@ -307,11 +305,7 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen)
if (!leftProp.isShared() || !rightProp.isShared())
return Relation::Intersects;
Relation r;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
r = relate(*leftProp.readTy, *rightProp.readTy, seen);
else
r = relate(leftProp.type_DEPRECATED(), rightProp.type_DEPRECATED(), seen);
Relation r = relate(*leftProp.readTy, *rightProp.readTy, seen);
if (r == Relation::Coincident && 1 != leftTable->props.size())
{
// eg {tag: "cat", prop: string} & {tag: "cat"}
@ -394,12 +388,9 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
if (isTypeVariable(left) || isTypeVariable(right))
return Relation::Intersects;
if (FFlag::LuauSimplificationTableExternType)
{
// if either type is a type function, we cannot know if they'll be related.
if (get<TypeFunctionInstanceType>(left) || get<TypeFunctionInstanceType>(right))
return Relation::Intersects;
}
if (get<ErrorType>(left))
{
@ -588,8 +579,6 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
return Relation::Intersects;
}
if (FFlag::LuauSimplificationTableExternType)
{
if (auto re = get<ExternType>(right))
{
Relation overall = Relation::Coincident;
@ -604,13 +593,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
LUAU_ASSERT(prop.readTy && propInExternType->second.readTy);
propRel = relate(*prop.readTy, *propInExternType->second.readTy, seen);
}
else if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
else
{
LUAU_ASSERT(prop.readTy && propInExternType->second.readTy);
propRel = relate(*prop.readTy, *propInExternType->second.readTy);
}
else
propRel = relate(prop.type_DEPRECATED(), propInExternType->second.type_DEPRECATED());
if (propRel == Relation::Disjoint)
return Relation::Disjoint;
@ -624,7 +611,6 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
return overall;
}
}
// TODO metatables
@ -1319,11 +1305,7 @@ std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
auto it = rt->props.find(propName);
if (it != rt->props.end() && leftProp.isShared() && it->second.isShared())
{
Relation r;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
r = relate(*leftProp.readTy, *it->second.readTy);
else
r = relate(leftProp.type_DEPRECATED(), it->second.type_DEPRECATED());
Relation r = relate(*leftProp.readTy, *it->second.readTy);
switch (r)
{

View file

@ -10,8 +10,6 @@
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauSolverAgnosticClone)
namespace Luau
{
@ -190,15 +188,10 @@ void Tarjan::visitChildren(TypeId ty, int index)
{
LUAU_ASSERT(!ttv->boundTo);
for (const auto& [name, prop] : ttv->props)
{
if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{
visitChild(prop.readTy);
visitChild(prop.writeTy);
}
else
visitChild(prop.type_DEPRECATED());
}
if (ttv->indexer)
{
@ -247,7 +240,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
{
for (const auto& [name, prop] : etv->props)
{
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
{
visitChild(prop.readTy);
visitChild(prop.writeTy);
@ -782,17 +775,12 @@ void Substitution::replaceChildren(TypeId ty)
{
LUAU_ASSERT(!ttv->boundTo);
for (auto& [name, prop] : ttv->props)
{
if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{
if (prop.readTy)
prop.readTy = replace(prop.readTy);
if (prop.writeTy)
prop.writeTy = replace(prop.writeTy);
}
else
prop.setType(replace(prop.type_DEPRECATED()));
}
if (ttv->indexer)
{
@ -841,7 +829,7 @@ void Substitution::replaceChildren(TypeId ty)
{
for (auto& [name, prop] : etv->props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
if (prop.readTy)
prop.readTy = replace(prop.readTy);

View file

@ -21,9 +21,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAGVARIABLE(LuauMissingFollowMappedGenericPacks)
LUAU_FASTFLAGVARIABLE(LuauSubtypingNegationsChecksNormalizationComplexity)
namespace Luau
{
@ -606,11 +606,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
RecursionCounter rc(&counters.recursionCount);
if (counters.recursionLimit > 0 && counters.recursionLimit < counters.recursionCount)
{
SubtypingResult result;
result.normalizationTooComplex = true;
return result;
}
return SubtypingResult{false, true};
subTy = follow(subTy);
superTy = follow(superTy);
@ -672,10 +668,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
* For now, we do the conservative thing and refuse to cache anything
* that touches a cycle.
*/
SubtypingResult res;
res.isSubtype = true;
res.isCacheable = false;
return res;
return SubtypingResult{true, false, false};
}
SeenSetPopper ssp{&seenTypes, typePair};
@ -696,19 +689,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
{
result = isCovariantWith(env, subTy, superUnion, scope);
if (!result.isSubtype && !result.normalizationTooComplex)
{
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
if (semantic.normalizationTooComplex)
{
result = semantic;
}
else if (semantic.isSubtype)
{
semantic.reasoning.clear();
result = semantic;
}
}
result = trySemanticSubtyping(env, subTy, superTy, scope, result);
}
else if (auto superIntersection = get<IntersectionType>(superTy))
result = isCovariantWith(env, subTy, superIntersection, scope);
@ -716,21 +697,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
{
result = isCovariantWith(env, subIntersection, superTy, scope);
if (!result.isSubtype && !result.normalizationTooComplex)
{
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
if (semantic.normalizationTooComplex)
{
result = semantic;
}
else if (semantic.isSubtype)
{
// Clear the semantic reasoning, as any reasonings within
// potentially contain invalid paths.
semantic.reasoning.clear();
result = semantic;
}
}
result = trySemanticSubtyping(env, subTy, superTy, scope, result);
}
else if (get<AnyType>(superTy))
result = {true};
@ -817,6 +784,10 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
{
result = isCovariantWith(env, subNegation, superTy, scope);
if (!result.isSubtype && !result.normalizationTooComplex)
{
if (FFlag::LuauSubtypingNegationsChecksNormalizationComplexity)
result = trySemanticSubtyping(env, subTy, superTy, scope, result);
else
{
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
if (semantic.isSubtype)
@ -826,10 +797,15 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
}
}
}
}
else if (auto superNegation = get<NegationType>(superTy))
{
result = isCovariantWith(env, subTy, superNegation, scope);
if (!result.isSubtype && !result.normalizationTooComplex)
{
if (FFlag::LuauSubtypingNegationsChecksNormalizationComplexity)
result = trySemanticSubtyping(env, subTy, superTy, scope, result);
else
{
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
if (semantic.isSubtype)
@ -839,6 +815,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
}
}
}
}
else if (auto subTypeFunctionInstance = get<TypeFunctionInstanceType>(subTy))
{
if (auto substSubTy = env.applyMappedGenerics(builtinTypes, arena, subTy))
@ -901,12 +878,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
{
std::pair<TypePackId, TypePackId> typePair = {subTp, superTp};
if (!seenPacks.insert(typePair))
{
SubtypingResult res;
res.isSubtype = true;
res.isCacheable = false;
return res;
}
return SubtypingResult{true, false, false};
popper.emplace(&seenPacks, std::move(typePair));
}
@ -1658,21 +1630,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType, scope).isSubtype)
{
if (superProp.isShared())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope)
.withSubComponent(TypePath::TypeField::IndexResult)
.withSuperComponent(TypePath::Property::read(name)));
}
else
{
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type_DEPRECATED(), scope)
.withSubComponent(TypePath::TypeField::IndexResult)
.withSuperComponent(TypePath::Property::read(name)));
}
}
else
{
if (superProp.readTy)
results.push_back(isCovariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope)
@ -1845,9 +1808,6 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim
if (auto mttv = get<TableType>(follow(metatable)))
{
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// the `string` metatable should not have any write-only types.
LUAU_ASSERT(*it->second.readTy);
@ -1856,13 +1816,6 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
}
else
{
if (auto stringTable = get<TableType>(it->second.type_DEPRECATED()))
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
}
}
}
}
}
@ -1890,8 +1843,6 @@ SubtypingResult Subtyping::isCovariantWith(
if (auto mttv = get<TableType>(follow(metatable)))
{
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// the `string` metatable should not have any write-only types.
LUAU_ASSERT(*it->second.readTy);
@ -1900,13 +1851,6 @@ SubtypingResult Subtyping::isCovariantWith(
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
}
else
{
if (auto stringTable = get<TableType>(it->second.type_DEPRECATED()))
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
}
}
}
}
}
@ -1938,14 +1882,7 @@ SubtypingResult Subtyping::isCovariantWith(
SubtypingResult res{true};
if (superProp.isShared() && subProp.isShared())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name)));
else
res.andAlso(
isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name))
);
}
else
{
if (superProp.readTy.has_value() && subProp.readTy.has_value())
@ -2252,4 +2189,25 @@ std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const T
return {builtinTypes->neverType, errors};
}
SubtypingResult Subtyping::trySemanticSubtyping(SubtypingEnvironment& env,
TypeId subTy,
TypeId superTy,
NotNull<Scope> scope,
SubtypingResult& original)
{
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
if (semantic.normalizationTooComplex)
{
return semantic;
}
else if (semantic.isSubtype)
{
semantic.reasoning.clear();
return semantic;
}
return original;
}
} // namespace Luau

View file

@ -11,7 +11,6 @@
#include <unordered_set>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
namespace Luau
{
@ -189,8 +188,6 @@ void StateDot::visitChildren(TypeId ty, int index)
return visitChild(*t.boundTo, index, "boundTo");
for (const auto& [name, prop] : t.props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (prop.isShared())
visitChild(*prop.readTy, index, name.c_str());
@ -209,9 +206,6 @@ void StateDot::visitChildren(TypeId ty, int index)
}
}
}
else
visitChild(prop.type_DEPRECATED(), index, name.c_str());
}
if (t.indexer)
{
visitChild(t.indexer->indexType, index, "[index]");
@ -329,8 +323,6 @@ void StateDot::visitChildren(TypeId ty, int index)
finishNode();
for (const auto& [name, prop] : t.props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (prop.isShared())
visitChild(*prop.readTy, index, name.c_str());
@ -349,9 +341,6 @@ void StateDot::visitChildren(TypeId ty, int index)
}
}
}
else
visitChild(prop.type_DEPRECATED(), index, name.c_str());
}
if (t.parent)
visitChild(*t.parent, index, "[parent]");

View file

@ -21,7 +21,6 @@
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification)
/*
@ -42,7 +41,6 @@ LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification)
*/
LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0)
LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort)
LUAU_FASTFLAGVARIABLE(LuauFixEmptyTypePackStringification)
namespace Luau
{
@ -420,10 +418,7 @@ struct TypeStringifier
if (prop.isShared())
{
emitKey(name);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
stringify(*prop.readTy);
else
stringify(prop.type_DEPRECATED());
return;
}
@ -493,7 +488,6 @@ struct TypeStringifier
bool wrap = !singleTp && get<TypePack>(follow(tp));
if (FFlag::LuauFixEmptyTypePackStringification)
wrap &= !isEmpty(tp);
if (wrap)
@ -742,7 +736,7 @@ struct TypeStringifier
state.emit("(");
if (FFlag::LuauFixEmptyTypePackStringification && isEmpty(ftv.argTypes))
if (isEmpty(ftv.argTypes))
{
// if we've got an empty argument pack, we're done.
}
@ -753,7 +747,7 @@ struct TypeStringifier
state.emit(") -> ");
bool plural = FFlag::LuauFixEmptyTypePackStringification ? !isEmpty(ftv.retTypes) : true;
bool plural = !isEmpty(ftv.retTypes);
auto retBegin = begin(ftv.retTypes);
auto retEnd = end(ftv.retTypes);
@ -1270,15 +1264,12 @@ struct TypePackStringifier
return;
}
if (FFlag::LuauFixEmptyTypePackStringification)
{
if (tp.head.empty() && (!tp.tail || isEmpty(*tp.tail)))
{
state.emit("()");
state.unsee(&tp);
return;
}
}
bool first = true;
@ -1827,8 +1818,6 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
state.emit("): ");
if (FFlag::LuauFixEmptyTypePackStringification)
{
size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize > 1);
@ -1840,21 +1829,7 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
if (wrap)
state.emit(")");
}
else
{
size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1);
if (wrap)
state.emit("(");
tvs.stringify(ftv.retTypes);
if (wrap)
state.emit(")");
}
return result.name;
}

View file

@ -30,7 +30,6 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticVisitType)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticSetType)
@ -714,9 +713,6 @@ Property Property::create(std::optional<TypeId> read, std::optional<TypeId> writ
TypeId Property::type_DEPRECATED() const
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && !FFlag::LuauUseWorkspacePropToChooseSolver)
LUAU_ASSERT(!FFlag::LuauSolverV2);
LUAU_ASSERT(readTy);
return *readTy;
}
@ -841,7 +837,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs)
if (l->first != r->first)
return false;
if (FFlag::LuauSolverV2 && (FFlag::LuauSubtypingCheckFunctionGenericCounts || FFlag::LuauRemoveTypeCallsForReadWriteProps))
if (FFlag::LuauSolverV2)
{
if (l->second.readTy && r->second.readTy)
{
@ -1090,7 +1086,7 @@ void persist(TypeId ty)
for (const auto& [_name, prop] : ttv->props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
if (prop.readTy)
queue.push_back(*prop.readTy);
@ -1112,7 +1108,7 @@ void persist(TypeId ty)
{
for (const auto& [_name, prop] : etv->props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
if (prop.readTy)
queue.push_back(*prop.readTy);

View file

@ -2,7 +2,6 @@
#include "Luau/TypeAttach.h"
#include "Luau/Ast.h"
#include "Luau/Error.h"
#include "Luau/Module.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
@ -14,8 +13,6 @@
#include <string>
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
{
char* result = (char*)allocator.allocate(contents.size() + 1);
@ -197,8 +194,6 @@ public:
char* name = allocateString(*allocator, propName);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (prop.isShared())
{
props.data[idx].name = AstName(name);
@ -228,14 +223,6 @@ public:
}
}
}
else
{
props.data[idx].name = AstName(name);
props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty);
props.data[idx].location = Location();
idx++;
}
}
AstTableIndexer* indexer = nullptr;
if (ttv.indexer)
@ -272,8 +259,6 @@ public:
{
char* name = allocateString(*allocator, propName);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (prop.isShared())
{
props.data[idx].name = AstName(name);
@ -303,14 +288,6 @@ public:
}
}
}
else
{
props.data[idx].name = AstName{name};
props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty);
props.data[idx].location = Location();
idx++;
}
}
AstTableIndexer* indexer = nullptr;
if (etv.indexer)

View file

@ -30,16 +30,13 @@
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAGVARIABLE(LuauSuppressErrorsForMultipleNonviableOverloads)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauInferActualIfElseExprType)
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauIceLess)
@ -722,8 +719,6 @@ void TypeChecker2::visit(AstStatReturn* ret)
{
Scope* scope = findInnermostScope(ret->location);
TypePackId expectedRetType = scope->returnType;
if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
{
if (ret->list.size == 0)
{
testIsSubtype(builtinTypes->emptyTypePack, expectedRetType, ret->location);
@ -738,6 +733,9 @@ void TypeChecker2::visit(AstStatReturn* ret)
{
if (idx < head.size())
{
if (FFlag::LuauInferActualIfElseExprType2)
isSubtype &= testLiteralOrAstTypeIsSubtype(ret->list.data[idx], head[idx]);
else
isSubtype &= testPotentialLiteralIsSubtype(ret->list.data[idx], head[idx]);
actualHead.push_back(head[idx]);
}
@ -769,6 +767,9 @@ void TypeChecker2::visit(AstStatReturn* ret)
else
{
auto lastType = head[ret->list.size - 1];
if (FFlag::LuauInferActualIfElseExprType2)
isSubtype &= testLiteralOrAstTypeIsSubtype(lastExpr, lastType);
else
isSubtype &= testPotentialLiteralIsSubtype(lastExpr, lastType);
actualHead.push_back(lastType);
}
@ -782,13 +783,6 @@ void TypeChecker2::visit(AstStatReturn* ret)
auto reconstructedRetType = module->internalTypes.addTypePack(TypePack{std::move(actualHead), std::move(actualTail)});
testIsSubtype(reconstructedRetType, expectedRetType, ret->location);
}
}
else
{
TypeArena* arena = &module->internalTypes;
TypePackId actualRetType = reconstructPack(ret->list, *arena);
testIsSubtype(actualRetType, expectedRetType, ret->location);
}
for (AstExpr* expr : ret->list)
visit(expr, ValueContext::RValue);
@ -819,12 +813,7 @@ void TypeChecker2::visit(AstStatLocal* local)
TypeId annotationType = lookupAnnotation(var->annotation);
TypeId valueType = value ? lookupType(value) : nullptr;
if (valueType)
{
if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
testPotentialLiteralIsSubtype(value, annotationType);
else
testIsSubtype(valueType, annotationType, value->location);
}
visit(var->annotation);
}
@ -1233,8 +1222,6 @@ void TypeChecker2::visit(AstStatAssign* assign)
continue;
}
if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
{
// FIXME CLI-142462: Due to the fact that we do not type state
// tables properly, table types "time travel." We can take
// advantage of this for the specific code pattern of:
@ -1245,23 +1232,11 @@ void TypeChecker2::visit(AstStatAssign* assign)
//
if (testLiteralOrAstTypeIsSubtype(rhs, lhsType))
{
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType
if (std::optional<TypeId> bindingType = getBindingType(lhs))
testLiteralOrAstTypeIsSubtype(rhs, *bindingType);
}
}
else
{
bool ok = testIsSubtype(rhsType, lhsType, rhs->location);
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType
if (ok)
{
std::optional<TypeId> bindingType = getBindingType(lhs);
if (bindingType)
testIsSubtype(rhsType, *bindingType, rhs->location);
}
}
}
}
void TypeChecker2::visit(AstStatCompoundAssign* stat)
@ -2067,7 +2042,7 @@ void TypeChecker2::visit(AstExprFunction* fn)
// If the function type has a function annotation, we need to see if we can suggest an annotation
if (normalizedFnTy)
{
if (FFlag::LuauStuckTypeFunctionsStillDispatch)
if (FFlag::LuauEagerGeneralization4)
suggestAnnotations(fn, normalizedFnTy->functions.parts.front());
else
{
@ -2905,8 +2880,6 @@ void TypeChecker2::visit(AstTypePackGeneric* tp)
Scope* scope = findInnermostScope(tp->location);
LUAU_ASSERT(scope);
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
{
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
return;
@ -2920,28 +2893,6 @@ void TypeChecker2::visit(AstTypePackGeneric* tp)
);
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
}
else
{
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
if (!alias.has_value())
{
if (scope->lookupType(tp->genericName.value))
{
reportError(
SwappedGenericTypeParameter{
tp->genericName.value,
SwappedGenericTypeParameter::Kind::Pack,
},
tp->location
);
}
else
{
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
}
}
}
}
template<typename TID>
@ -3110,7 +3061,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
auto exprType = follow(lookupType(expr));
expectedType = follow(expectedType);
if (FFlag::LuauInferActualIfElseExprType)
if (FFlag::LuauInferActualIfElseExprType2)
{
if (auto group = expr->as<AstExprGroup>())
{
@ -3161,8 +3112,6 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
Set<std::optional<std::string>> missingKeys{{}};
for (const auto& [name, prop] : expectedTableType->props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.readTy)
{
@ -3170,15 +3119,6 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
missingKeys.insert(name);
}
}
else
{
LUAU_ASSERT(!prop.isWriteOnly());
auto readTy = *prop.readTy;
if (!isOptional(readTy))
missingKeys.insert(name);
}
}
bool isArrayLike = false;
if (expectedTableType->indexer)
@ -3212,8 +3152,6 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
// If there's not an indexer, then by width subtyping we can just do nothing :)
}
else
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
// If the type has a read type, then we have an expected type for it, otherwise, we actually don't
// care what's assigned to it because the only allowed behavior is writing to that property.
@ -3224,15 +3162,6 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy);
}
}
else
{
// TODO: What do we do for write only props?
LUAU_ASSERT(expectedIt->second.readTy);
// Some property is in the expected type: we can test against the specific type.
module->astExpectedTypes[item.value] = *expectedIt->second.readTy;
isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy);
}
}
}
else if (item.kind == AstExprTable::Item::List)
{

View file

@ -33,14 +33,10 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement)
LUAU_FASTFLAGVARIABLE(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAGVARIABLE(LuauEmptyStringInKeyOf)
LUAU_FASTFLAGVARIABLE(LuauAvoidExcessiveTypeCopying)
@ -315,7 +311,7 @@ struct TypeFunctionReducer
if (auto tfit = get<TypeFunctionInstanceType>(t))
{
if (FFlag::LuauStuckTypeFunctionsStillDispatch)
if (FFlag::LuauEagerGeneralization4)
{
if (tfit->state == TypeFunctionInstanceState::Stuck)
return SkipTestResult::Stuck;
@ -438,7 +434,7 @@ struct TypeFunctionReducer
if (FFlag::DebugLuauLogTypeFamilies)
printf("%s is uninhabited\n", toString(subject, {true}).c_str());
if (FFlag::LuauStuckTypeFunctionsStillDispatch)
if (FFlag::LuauEagerGeneralization4)
{
if (getState(subject) == TypeFunctionInstanceState::Unsolved)
{
@ -500,7 +496,7 @@ struct TypeFunctionReducer
if (skip == SkipTestResult::Stuck)
{
// SkipTestResult::Stuck cannot happen when this flag is unset.
LUAU_ASSERT(FFlag::LuauStuckTypeFunctionsStillDispatch);
LUAU_ASSERT(FFlag::LuauEagerGeneralization4);
if (FFlag::DebugLuauLogTypeFamilies)
printf("%s is stuck!\n", toString(subject, {true}).c_str());
@ -779,7 +775,7 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location
bool isPending(TypeId ty, ConstraintSolver* solver)
{
if (FFlag::LuauStuckTypeFunctionsStillDispatch)
if (FFlag::LuauEagerGeneralization4)
{
if (auto tfit = get<TypeFunctionInstanceType>(ty); tfit && tfit->state == TypeFunctionInstanceState::Unsolved)
return true;

View file

@ -21,7 +21,6 @@
#include <vector>
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
namespace Luau
{
@ -455,8 +454,6 @@ static int getSingletonValue(lua_State* L)
// Otherwise, makes a union of the two things.
static int createOptional(lua_State* L)
{
LUAU_ASSERT(FFlag::LuauTypeFunOptional);
int argumentCount = lua_gettop(L);
if (argumentCount != 1)
luaL_error(L, "types.optional: expected 1 argument, but got %d", argumentCount);
@ -1663,12 +1660,12 @@ void registerTypesLibrary(lua_State* L)
{"negationof", createNegation},
{"unionof", createUnion},
{"intersectionof", createIntersection},
{"optional", createOptional},
{"newtable", createTable},
{"newfunction", createFunction},
{"copy", deepCopy},
{"generic", createGeneric},
{(FFlag::LuauTypeFunOptional) ? "optional" : nullptr, (FFlag::LuauTypeFunOptional) ? createOptional : nullptr},
{nullptr, nullptr}
};

View file

@ -20,8 +20,6 @@
// currently, controls serialization, deserialization, and `type.copy`
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
LUAU_FASTFLAGVARIABLE(LuauTypeFunctionSerializeFollowMetatable)
namespace Luau
{
@ -390,7 +388,7 @@ private:
void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2)
{
// Serialize main part of the metatable immediately
if (auto tableTy = get<TableType>(FFlag::LuauTypeFunctionSerializeFollowMetatable ? follow(m1->table) : m1->table))
if (auto tableTy = get<TableType>(follow(m1->table)))
serializeChildren(tableTy, m2);
m2->metatable = shallowSerialize(m1->metatable);

View file

@ -34,8 +34,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
namespace Luau
{
@ -1912,7 +1910,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
else if (auto a = expr.as<AstExprUnary>())
result = checkExpr(scope, *a);
else if (auto a = expr.as<AstExprBinary>())
result = FFlag::LuauReduceCheckBinaryExprStackPressure ? checkExpr(scope, *a, expectedType) : checkExpr_DEPRECATED(scope, *a, expectedType);
result = checkExpr(scope, *a, expectedType);
else if (auto a = expr.as<AstExprTypeAssertion>())
result = checkExpr(scope, *a);
else if (auto a = expr.as<AstExprError>())
@ -3212,63 +3210,6 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
}
}
WithPredicate<TypeId> TypeChecker::checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType)
{
if (expr.op == AstExprBinary::And)
{
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
ScopePtr innerScope = childScope(scope, expr.location);
resolve(lhsPredicates, innerScope, true);
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
return {checkBinaryOperation(scope, expr, lhsTy, rhsTy), {AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
}
else if (expr.op == AstExprBinary::Or)
{
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
ScopePtr innerScope = childScope(scope, expr.location);
resolve(lhsPredicates, innerScope, false);
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
// Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation.
TypeId result = checkBinaryOperation(scope, expr, lhsTy, rhsTy, lhsPredicates);
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
}
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
{
// For these, passing expectedType is worse than simply forcing them, because their implementation
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
if (auto predicate = tryGetTypeGuardPredicate(expr))
return {booleanType, {std::move(*predicate)}};
PredicateVec predicates;
if (auto lvalue = tryGetLValue(*expr.left))
predicates.push_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
if (auto lvalue = tryGetLValue(*expr.right))
predicates.push_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
if (!predicates.empty() && expr.op == AstExprBinary::CompareNe)
predicates = {NotPredicate{std::move(predicates)}};
return {checkBinaryOperation(scope, expr, lhs.type, rhs.type), std::move(predicates)};
}
else
{
// Expected types are not useful for other binary operators.
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left);
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right);
// Intentionally discarding predicates with other operators.
return WithPredicate{checkBinaryOperation(scope, expr, lhs.type, rhs.type, lhs.predicates)};
}
}
WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr)
{
TypeId annotationType = resolveType(scope, *expr.annotation);

View file

@ -13,8 +13,6 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
namespace Luau
{
@ -77,7 +75,7 @@ std::optional<Property> findTableProperty(NotNull<BuiltinTypes> builtinTypes, Er
const auto& fit = itt->props.find(name);
if (fit != itt->props.end())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
if (fit->second.readTy)
return fit->second.readTy;
@ -136,7 +134,7 @@ std::optional<TypeId> findMetatableEntry(
auto it = mtt->props.find(entry);
if (it != mtt->props.end())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
if (it->second.readTy)
return it->second.readTy;
@ -209,7 +207,7 @@ std::optional<TypeId> findTablePropertyRespectingMeta(
const auto& fit = itt->props.find(name);
if (fit != itt->props.end())
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2)
{
switch (context)
{
@ -469,8 +467,6 @@ TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty)
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
{
if (FFlag::LuauErrorSuppressionTypeFunctionArgs)
{
if (auto tfit = get<TypeFunctionInstanceType>(follow(ty)))
{
for (auto ty : tfit->typeArguments)
@ -486,7 +482,6 @@ ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
return ErrorSuppression::DoNotSuppress;
}
}
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);

View file

@ -18,10 +18,7 @@
#include <optional>
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
namespace Luau
@ -52,8 +49,6 @@ static bool areCompatible(TypeId left, TypeId right)
// the right table is free (and therefore potentially has an indexer or
// a compatible property)
if (FFlag::LuauRemoveTypeCallsForReadWriteProps || FFlag::LuauRefineTablesWithReadType)
{
if (rightTable->state == TableState::Free || rightTable->indexer.has_value())
return true;
@ -65,19 +60,6 @@ static bool areCompatible(TypeId left, TypeId right)
// FIXME: Could this create an issue for write only / divergent properties?
return false;
}
else
{
LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared());
const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type_DEPRECATED());
if (isOptional(leftType) || get<FreeType>(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value())
return true;
return false;
}
};
for (const auto& [name, leftProp] : leftTable->props)
@ -106,7 +88,7 @@ static bool areCompatible(TypeId left, TypeId right)
// returns `true` if `ty` is irressolvable and should be added to `incompleteSubtypes`.
static bool isIrresolvable(TypeId ty)
{
if (FFlag::LuauStuckTypeFunctionsStillDispatch)
if (FFlag::LuauEagerGeneralization4)
{
if (auto tfit = get<TypeFunctionInstanceType>(ty); tfit && tfit->state != TypeFunctionInstanceState::Unsolved)
return false;
@ -446,29 +428,12 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
{
const Property& superProp = superPropOpt->second;
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (subProp.readTy && superProp.readTy)
result &= unify(*subProp.readTy, *superProp.readTy);
if (subProp.writeTy && superProp.writeTy)
result &= unify(*superProp.writeTy, *subProp.writeTy);
}
else
{
if (subProp.isReadOnly() && superProp.isReadOnly())
result &= unify(*subProp.readTy, *superPropOpt->second.readTy);
else if (subProp.isReadOnly())
result &= unify(*subProp.readTy, superProp.type_DEPRECATED());
else if (superProp.isReadOnly())
result &= unify(subProp.type_DEPRECATED(), *superProp.readTy);
else
{
result &= unify(subProp.type_DEPRECATED(), superProp.type_DEPRECATED());
result &= unify(superProp.type_DEPRECATED(), subProp.type_DEPRECATED());
}
}
}
}
auto subTypeParamsIter = subTable->instantiatedTypeParams.begin();

View file

@ -192,6 +192,14 @@ public:
return offset;
}
enum class BraceType
{
InterpolatedString,
Normal
};
std::optional<Lexer::BraceType> peekBraceStackTop();
private:
char peekch() const;
char peekch(unsigned int lookahead) const;
@ -243,12 +251,6 @@ private:
bool skipComments;
bool readNames;
enum class BraceType
{
InterpolatedString,
Normal
};
std::vector<BraceType> braceStack;
};

View file

@ -130,7 +130,6 @@ private:
// function funcname funcbody
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
std::pair<bool, AstAttr::Type> validateAttribute_DEPRECATED(const char* attributeName, const TempVector<AstAttr*>& attributes);
std::optional<AstAttr::Type> validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes);
// attribute ::= '@' NAME

View file

@ -1009,6 +1009,14 @@ Lexeme Lexer::readNext()
}
}
std::optional<Lexer::BraceType> Lexer::peekBraceStackTop()
{
if (braceStack.empty())
return std::nullopt;
else
return {braceStack.back()};
}
LUAU_NOINLINE Lexeme Lexer::readUtf8Error()
{
Position start = position();

View file

@ -18,9 +18,8 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
// flag so that we don't break production games by reverting syntax changes.
// See docs/SyntaxChanges.md for an explanation.
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit)
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
LUAU_FASTFLAGVARIABLE(LuauParseIncompleteInterpStringsWithLocation)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false;
@ -769,53 +768,8 @@ AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
return node;
}
std::pair<bool, AstAttr::Type> Parser::validateAttribute_DEPRECATED(const char* attributeName, const TempVector<AstAttr*>& attributes)
{
LUAU_ASSERT(!FFlag::LuauParseAttributeFixUninit);
AstAttr::Type type;
// check if the attribute name is valid
bool found = false;
for (int i = 0; kAttributeEntries[i].name; ++i)
{
found = !strcmp(attributeName, kAttributeEntries[i].name);
if (found)
{
type = kAttributeEntries[i].type;
break;
}
}
if (!found)
{
if (strlen(attributeName) == 1)
report(lexer.current().location, "Attribute name is missing");
else
report(lexer.current().location, "Invalid attribute '%s'", attributeName);
}
else
{
// check that attribute is not duplicated
for (const AstAttr* attr : attributes)
{
if (attr->type == type)
{
report(lexer.current().location, "Cannot duplicate attribute '%s'", attributeName);
}
}
}
return {found, type};
}
std::optional<AstAttr::Type> Parser::validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes)
{
LUAU_ASSERT(FFlag::LuauParseAttributeFixUninit);
// check if the attribute name is valid
std::optional<AstAttr::Type> type;
@ -855,8 +809,6 @@ void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
Location loc = lexer.current().location;
if (FFlag::LuauParseAttributeFixUninit)
{
const char* name = lexer.current().name;
std::optional<AstAttr::Type> type = validateAttribute(name, attributes);
@ -864,17 +816,6 @@ void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
if (type)
attributes.push_back(allocator.alloc<AstAttr>(loc, *type));
}
else
{
const char* name = lexer.current().name;
const auto [found, type] = validateAttribute_DEPRECATED(name, attributes);
nextLexeme();
if (found)
attributes.push_back(allocator.alloc<AstAttr>(loc, type));
}
}
// attributes ::= {attribute}
@ -1308,8 +1249,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
);
}
if (FFlag::LuauParseStringIndexer)
{
// There are two possibilities: Either it's a property or a function.
if (lexer.current().type == Lexeme::ReservedFunction)
{
@ -1374,74 +1313,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
});
}
}
else
{
// There are two possibilities: Either it's a property or a function.
if (lexer.current().type == Lexeme::ReservedFunction)
{
props.push_back(parseDeclaredExternTypeMethod(attributes));
}
else if (lexer.current().type == '[' &&
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
{
const Lexeme begin = lexer.current();
nextLexeme(); // [
const Location nameBegin = lexer.current().location;
std::optional<AstArray<char>> chars = parseCharArray();
const Location nameEnd = lexer.previousLocation();
expectMatchAndConsume(']', begin);
expectAndConsume(':', "property type annotation");
AstType* type = parseType();
// since AstName contains a char*, it can't contain null
bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr);
if (chars && !containsNull)
{
props.push_back(AstDeclaredExternTypeProperty{
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
});
}
else
{
report(begin.location, "String literal contains malformed escape sequence or \\0");
}
}
else if (lexer.current().type == '[')
{
if (indexer)
{
// maybe we don't need to parse the entire badIndexer...
// however, we either have { or [ to lint, not the entire table type or the bad indexer.
AstTableIndexer* badIndexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, lexer.current()).node;
// we lose all additional indexer expressions from the AST after error recovery here
report(badIndexer->location, "Cannot have more than one indexer on an extern type");
}
else
{
indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, lexer.current()).node;
}
}
else
{
Location propStart = lexer.current().location;
std::optional<Name> propName = parseNameOpt("property name");
if (!propName)
break;
expectAndConsume(':', "property type annotation");
AstType* propType = parseType();
props.push_back(AstDeclaredExternTypeProperty{
propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())
});
}
}
}
Location classEnd = lexer.current().location;
nextLexeme(); // skip past `end`
@ -1982,12 +1853,6 @@ std::pair<CstExprConstantString::QuoteStyle, unsigned int> Parser::extractString
// TableIndexer ::= `[' Type `]' `:' Type
Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin)
{
if (!FFlag::LuauParseStringIndexer)
{
begin = lexer.current();
nextLexeme(); // [
}
AstType* index = parseType();
Position indexerClosePosition = lexer.current().location.begin;
@ -2046,8 +1911,6 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
}
}
if (FFlag::LuauParseStringIndexer)
{
if (lexer.current().type == '[')
{
const Lexeme begin = lexer.current();
@ -2153,113 +2016,6 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
lexer.current().location.begin
});
}
}
else
{
if (lexer.current().type == '[' && (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
{
const Lexeme begin = lexer.current();
nextLexeme(); // [
CstExprConstantString::QuoteStyle style;
unsigned int blockDepth = 0;
if (options.storeCstData)
std::tie(style, blockDepth) = extractStringDetails();
Position stringPosition = lexer.current().location.begin;
AstArray<char> sourceString;
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
Position indexerClosePosition = lexer.current().location.begin;
expectMatchAndConsume(']', begin);
Position colonPosition = lexer.current().location.begin;
expectAndConsume(':', "table field");
AstType* type = parseType();
// since AstName contains a char*, it can't contain null
bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr);
if (chars && !containsNull)
{
props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation});
if (options.storeCstData)
cstItems.push_back(CstTypeTable::Item{
CstTypeTable::Item::Kind::StringProperty,
begin.location.begin,
indexerClosePosition,
colonPosition,
tableSeparator(),
lexer.current().location.begin,
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth),
stringPosition
});
}
else
report(begin.location, "String literal contains malformed escape sequence or \\0");
}
else if (lexer.current().type == '[')
{
if (indexer)
{
// maybe we don't need to parse the entire badIndexer...
// however, we either have { or [ to lint, not the entire table type or the bad indexer.
AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, lexer.current()).node;
// we lose all additional indexer expressions from the AST after error recovery here
report(badIndexer->location, "Cannot have more than one table indexer");
}
else
{
// the last param in the parseTableIndexer is ignored
auto tableIndexerResult = parseTableIndexer(access, accessLocation, lexer.current());
indexer = tableIndexerResult.node;
if (options.storeCstData)
cstItems.push_back(CstTypeTable::Item{
CstTypeTable::Item::Kind::Indexer,
tableIndexerResult.indexerOpenPosition,
tableIndexerResult.indexerClosePosition,
tableIndexerResult.colonPosition,
tableSeparator(),
lexer.current().location.begin,
});
}
}
else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':'))
{
AstType* type = parseType();
// array-like table type: {T} desugars into {[number]: T}
isArray = true;
AstType* index = allocator.alloc<AstTypeReference>(type->location, std::nullopt, nameNumber, std::nullopt, type->location);
indexer = allocator.alloc<AstTableIndexer>(AstTableIndexer{index, type, type->location, access, accessLocation});
break;
}
else
{
std::optional<Name> name = parseNameOpt("table field");
if (!name)
break;
Position colonPosition = lexer.current().location.begin;
expectAndConsume(':', "table field");
AstType* type = parseType(inDeclarationContext);
props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation});
if (options.storeCstData)
cstItems.push_back(CstTypeTable::Item{
CstTypeTable::Item::Kind::Property,
Position{0, 0},
Position{0, 0},
colonPosition,
tableSeparator(),
lexer.current().location.begin
});
}
}
if (lexer.current().type == ',' || lexer.current().type == ';')
{
@ -3988,7 +3744,34 @@ AstExpr* Parser::parseInterpString()
return reportExprError(endLocation, {}, "Double braces are not permitted within interpolated strings; did you mean '\\{'?");
case Lexeme::BrokenString:
nextLexeme();
if (!FFlag::LuauParseIncompleteInterpStringsWithLocation)
return reportExprError(endLocation, {}, "Malformed interpolated string; did you forget to add a '}'?");
LUAU_FALLTHROUGH;
case Lexeme::Eof:
{
if (FFlag::LuauParseIncompleteInterpStringsWithLocation)
{
AstArray<AstArray<char>> stringsArray = copy(strings);
AstArray<AstExpr*> exprs = copy(expressions);
AstExprInterpString* node =
allocator.alloc<AstExprInterpString>(Location{startLocation, lexer.previousLocation()}, stringsArray, exprs);
if (options.storeCstData)
cstNodeMap[node] = allocator.alloc<CstExprInterpString>(copy(sourceStrings), copy(stringPositions));
if (auto top = lexer.peekBraceStackTop())
{
// We are in a broken interpolated string, the top of the stack is non empty, we are missing '}'
if (*top == Lexer::BraceType::InterpolatedString)
report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '}'?");
}
else
{
// We are in a broken interpolated string, the top of the stack is empty, we are missing '`'.
report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '`'?");
}
return node;
}
LUAU_FALLTHROUGH;
}
default:
return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str());
}

View file

@ -6,6 +6,8 @@
#include <stdarg.h>
#include <stdio.h>
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauCodeGenFixRexw, false)
namespace Luau
{
namespace CodeGen
@ -37,7 +39,10 @@ static_assert(sizeof(cmovTextForCondition) / sizeof(cmovTextForCondition[0]) ==
#define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc))
#define REX_W_BIT(value) (value ? 0x8 : 0x0)
#define REX_W(reg) REX_W_BIT((reg).size == SizeX64::qword || ((reg).size == SizeX64::byte && (reg).index >= 4))
// TODO: remove with DFFlagLuauCodeGenFixRexw
#define REX_W_DEPRECATED(reg) REX_W_BIT((reg).size == SizeX64::qword || ((reg).size == SizeX64::byte && (reg).index >= 4))
#define REX_W(reg) REX_W_BIT((reg).size == SizeX64::qword)
#define REX_FORCE(reg) (((reg).size == SizeX64::byte && (reg).index >= 4) ? 0x40 : 0x00)
#define REX_R(reg) (((reg).index & 0x8) >> 1)
#define REX_X(reg) (((reg).index & 0x8) >> 2)
#define REX_B(reg) (((reg).index & 0x8) >> 3)
@ -1390,18 +1395,30 @@ void AssemblyBuilderX64::
void AssemblyBuilderX64::placeRex(RegisterX64 op)
{
uint8_t code = REX_W(op) | REX_B(op);
if (DFFlag::LuauCodeGenFixRexw)
{
uint8_t code = REX_W(op) | REX_B(op) | REX_FORCE(op);
if (code != 0)
place(code | 0x40);
}
else
{
uint8_t code = REX_W_DEPRECATED(op) | REX_B(op);
if (code != 0)
place(code | 0x40);
}
}
void AssemblyBuilderX64::placeRex(OperandX64 op)
{
if (DFFlag::LuauCodeGenFixRexw)
{
uint8_t code = 0;
if (op.cat == CategoryX64::reg)
code = REX_W(op.base) | REX_B(op.base);
code = REX_W(op.base) | REX_B(op.base) | REX_FORCE(op.base);
else if (op.cat == CategoryX64::mem)
code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base);
else
@ -1409,6 +1426,21 @@ void AssemblyBuilderX64::placeRex(OperandX64 op)
if (code != 0)
place(code | 0x40);
}
else
{
uint8_t code = 0;
if (op.cat == CategoryX64::reg)
code = REX_W_DEPRECATED(op.base) | REX_B(op.base);
else if (op.cat == CategoryX64::mem)
code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base);
else
CODEGEN_ASSERT(!"No encoding for left operand of this category");
if (code != 0)
place(code | 0x40);
}
}
void AssemblyBuilderX64::placeRexNoW(OperandX64 op)
@ -1428,7 +1460,21 @@ void AssemblyBuilderX64::placeRexNoW(OperandX64 op)
void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs)
{
uint8_t code = REX_W(lhs);
if (DFFlag::LuauCodeGenFixRexw)
{
uint8_t code = REX_W(lhs) | REX_FORCE(lhs);
if (rhs.cat == CategoryX64::imm)
code |= REX_B(lhs);
else
code |= REX_R(lhs) | REX_X(rhs.index) | REX_B(rhs.base) | REX_FORCE(lhs) | REX_FORCE(rhs.base);
if (code != 0)
place(code | 0x40);
}
else
{
uint8_t code = REX_W_DEPRECATED(lhs);
if (rhs.cat == CategoryX64::imm)
code |= REX_B(lhs);
@ -1437,6 +1483,7 @@ void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs)
if (code != 0)
place(code | 0x40);
}
}
void AssemblyBuilderX64::placeVex(OperandX64 dst, OperandX64 src1, OperandX64 src2, bool setW, uint8_t mode, uint8_t prefix)

View file

@ -28,6 +28,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
LUAU_FASTFLAGVARIABLE(LuauCompileCli162537)
namespace Luau
{
@ -1928,6 +1929,10 @@ struct Compiler
CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile");
LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength);
if (FFlag::LuauCompileCli162537)
shape.keys[shape.length++] = cid;
else
shape.keys[shape.length++] = int16_t(cid);
}

View file

@ -106,7 +106,15 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
static int checkRegisteredModules(lua_State* L, const char* path)
{
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
lua_getfield(L, -1, path);
std::string pathLower = std::string(path);
for (char& c : pathLower)
{
if (c >= 'A' && c <= 'Z')
c -= ('A' - 'a');
}
lua_getfield(L, -1, pathLower.c_str());
if (lua_isnil(L, -1))
{
lua_pop(L, 2);
@ -243,6 +251,16 @@ int registerModuleImpl(lua_State* L)
if (pathView.empty() || pathView[0] != '@')
luaL_argerrorL(L, 1, "path must begin with '@'");
// Make path lowercase to ensure case-insensitive matching.
std::string pathLower = std::string(path, len);
for (char& c : pathLower)
{
if (c >= 'A' && c <= 'Z')
c -= ('A' - 'a');
}
lua_pushstring(L, pathLower.c_str());
lua_replace(L, 1);
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
// (1) path, (2) result, (3) cache table

View file

@ -220,6 +220,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/Simplify.h
Analysis/include/Luau/Substitution.h
Analysis/include/Luau/Subtyping.h
Analysis/include/Luau/SubtypingVariance.h
Analysis/include/Luau/Symbol.h
Analysis/include/Luau/TableLiteralInference.h
Analysis/include/Luau/ToDot.h

View file

@ -7,6 +7,8 @@
#include <string.h>
LUAU_DYNAMIC_FASTFLAG(LuauCodeGenFixRexw)
using namespace Luau::CodeGen;
using namespace Luau::CodeGen::X64;
@ -60,6 +62,8 @@ TEST_SUITE_BEGIN("x64Assembly");
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseBinaryInstructionForms")
{
ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true};
// reg, reg
SINGLE_COMPARE(add(rax, rcx), 0x48, 0x03, 0xc1);
SINGLE_COMPARE(add(rsp, r12), 0x49, 0x03, 0xe4);
@ -71,8 +75,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseBinaryInstructionForms")
SINGLE_COMPARE(add(rax, 0x80), 0x48, 0x81, 0xc0, 0x80, 0x00, 0x00, 0x00);
SINGLE_COMPARE(add(r10, 0x7fffffff), 0x49, 0x81, 0xc2, 0xff, 0xff, 0xff, 0x7f);
SINGLE_COMPARE(add(al, 3), 0x80, 0xc0, 0x03);
SINGLE_COMPARE(add(sil, 3), 0x48, 0x80, 0xc6, 0x03);
SINGLE_COMPARE(add(r11b, 3), 0x49, 0x80, 0xc3, 0x03);
SINGLE_COMPARE(add(sil, 3), 0x40, 0x80, 0xc6, 0x03);
SINGLE_COMPARE(add(r11b, 3), 0x41, 0x80, 0xc3, 0x03);
// reg, [reg]
SINGLE_COMPARE(add(rax, qword[rax]), 0x48, 0x03, 0x00);
@ -193,12 +197,14 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseUnaryInstructionForms")
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMov")
{
ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true};
SINGLE_COMPARE(mov(rcx, 1), 0x48, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
SINGLE_COMPARE(mov64(rcx, 0x1234567812345678ll), 0x48, 0xb9, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
SINGLE_COMPARE(mov(ecx, 2), 0xb9, 0x02, 0x00, 0x00, 0x00);
SINGLE_COMPARE(mov(cl, 2), 0xb1, 0x02);
SINGLE_COMPARE(mov(sil, 2), 0x48, 0xb6, 0x02);
SINGLE_COMPARE(mov(r9b, 2), 0x49, 0xb1, 0x02);
SINGLE_COMPARE(mov(sil, 2), 0x40, 0xb6, 0x02);
SINGLE_COMPARE(mov(r9b, 2), 0x41, 0xb1, 0x02);
SINGLE_COMPARE(mov(rcx, qword[rdi]), 0x48, 0x8b, 0x0f);
SINGLE_COMPARE(mov(dword[rax], 0xabcd), 0xc7, 0x00, 0xcd, 0xab, 0x00, 0x00);
SINGLE_COMPARE(mov(r13, 1), 0x49, 0xbd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
@ -209,8 +215,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMov")
SINGLE_COMPARE(mov(qword[rdx], r9), 0x4c, 0x89, 0x0a);
SINGLE_COMPARE(mov(byte[rsi], 0x3), 0xc6, 0x06, 0x03);
SINGLE_COMPARE(mov(byte[rsi], al), 0x88, 0x06);
SINGLE_COMPARE(mov(byte[rsi], dil), 0x48, 0x88, 0x3e);
SINGLE_COMPARE(mov(byte[rsi], r10b), 0x4c, 0x88, 0x16);
SINGLE_COMPARE(mov(byte[rsi], dil), 0x40, 0x88, 0x3e);
SINGLE_COMPARE(mov(byte[rsi], r10b), 0x44, 0x88, 0x16);
SINGLE_COMPARE(mov(wordReg(ebx), 0x3a3d), 0x66, 0xbb, 0x3d, 0x3a);
SINGLE_COMPARE(mov(word[rsi], 0x3a3d), 0x66, 0xc7, 0x06, 0x3d, 0x3a);
SINGLE_COMPARE(mov(word[rsi], wordReg(eax)), 0x66, 0x89, 0x06);
@ -232,20 +238,28 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMovExtended")
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfTest")
{
ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true};
SINGLE_COMPARE(test(al, 8), 0xf6, 0xc0, 0x08);
SINGLE_COMPARE(test(eax, 8), 0xf7, 0xc0, 0x08, 0x00, 0x00, 0x00);
SINGLE_COMPARE(test(rax, 8), 0x48, 0xf7, 0xc0, 0x08, 0x00, 0x00, 0x00);
SINGLE_COMPARE(test(rcx, 0xabab), 0x48, 0xf7, 0xc1, 0xab, 0xab, 0x00, 0x00);
SINGLE_COMPARE(test(rcx, rax), 0x48, 0x85, 0xc8);
SINGLE_COMPARE(test(rax, qword[rcx]), 0x48, 0x85, 0x01);
SINGLE_COMPARE(test(al, cl), 0x84, 0xc1);
SINGLE_COMPARE(test(al, sil), 0x40, 0x84, 0xc6);
SINGLE_COMPARE(test(cl, r12b), 0x41, 0x84, 0xcc);
SINGLE_COMPARE(test(sil, dil), 0x40, 0x84, 0xf7);
}
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfShift")
{
ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true};
SINGLE_COMPARE(shl(al, 1), 0xd0, 0xe0);
SINGLE_COMPARE(shl(al, cl), 0xd2, 0xe0);
SINGLE_COMPARE(shl(sil, cl), 0x48, 0xd2, 0xe6);
SINGLE_COMPARE(shl(r10b, cl), 0x49, 0xd2, 0xe2);
SINGLE_COMPARE(shl(sil, cl), 0x40, 0xd2, 0xe6);
SINGLE_COMPARE(shl(r10b, cl), 0x41, 0xd2, 0xe2);
SINGLE_COMPARE(shr(al, 4), 0xc0, 0xe8, 0x04);
SINGLE_COMPARE(shr(eax, 1), 0xd1, 0xe8);
SINGLE_COMPARE(sal(eax, cl), 0xd3, 0xe0);
@ -267,8 +281,10 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfLea")
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfSetcc")
{
ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true};
SINGLE_COMPARE(setcc(ConditionX64::NotEqual, bl), 0x0f, 0x95, 0xc3);
SINGLE_COMPARE(setcc(ConditionX64::NotEqual, dil), 0x48, 0x0f, 0x95, 0xc7);
SINGLE_COMPARE(setcc(ConditionX64::NotEqual, dil), 0x40, 0x0f, 0x95, 0xc7);
SINGLE_COMPARE(setcc(ConditionX64::BelowEqual, byte[rcx]), 0x0f, 0x96, 0x01);
}

View file

@ -20,7 +20,6 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
@ -4560,8 +4559,6 @@ end
TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment")
{
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"(
local function foobar(tbl: { tag: "left" | "right" })
tbl.tag = "@1"
@ -4575,8 +4572,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment")
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table")
{
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"(
type Entry = { field: number, prop: string }
local x : {Entry} = {}
@ -4603,8 +4598,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table")
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion")
{
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"(
type Entry = { field: number, prop: string }
return ( { f@1, p@2 } :: Entry )
@ -4621,7 +4614,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr")
ScopedFastFlag sffs[] = {
// Somewhat surprisingly, the old solver didn't cover this case.
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys3, true},
};
@ -4648,7 +4640,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys3, true},
};
@ -4679,10 +4670,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho
TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
check(R"(
local function take(_: { choice: "left" | "right" }) end

View file

@ -37,10 +37,9 @@ void luau_callhook(lua_State* L, lua_Hook hook, void* userdata);
LUAU_FASTFLAG(LuauHeapDumpStringSizeOverhead)
LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_DYNAMIC_FASTFLAG(LuauErrorYield)
LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck)
LUAU_FASTFLAG(LuauCompileCli162537)
static lua_CompileOptions defaultOptions()
{
@ -1210,16 +1209,11 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
lua_newtable(L);
for (const auto& [name, prop] : t->props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (prop.readTy)
populateRTTI(L, *prop.readTy);
else if (prop.writeTy)
populateRTTI(L, *prop.writeTy);
}
else
populateRTTI(L, prop.type_DEPRECATED());
lua_setfield(L, -2, name.c_str());
}
@ -3333,6 +3327,60 @@ TEST_CASE("HugeFunctionLoadFailure")
REQUIRE_EQ(largeAllocationToFail, expectedTotalLargeAllocations);
}
TEST_CASE("HugeConstantTable")
{
ScopedFastFlag luauCompileCli162537{FFlag::LuauCompileCli162537, true};
std::string source = "function foo(...)\n";
source += " local args = ...\n";
source += " local t = args and {\n";
for (int i = 0; i < 400; i++)
{
for (int k = 0; k < 100; k++)
{
source += "call(";
source += std::to_string(i * 100 + k);
source += ".125), ";
}
source += "\n ";
}
source += " }\n";
source += " return { a = 1, b = 2, c = 3 }\n";
source += "end\n";
source += "return foo().a + foo().b\n";
StateRef globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();
if (codegen && luau_codegen_supported())
luau_codegen_create(L);
luaL_openlibs(L);
luaL_sandbox(L);
luaL_sandboxthread(L);
size_t bytecodeSize = 0;
char* bytecode = luau_compile(source.data(), source.size(), nullptr, &bytecodeSize);
int result = luau_load(L, "=HugeConstantTable", bytecode, bytecodeSize, 0);
free(bytecode);
REQUIRE(result == 0);
if (codegen && luau_codegen_supported())
{
Luau::CodeGen::CompilationOptions nativeOptions{Luau::CodeGen::CodeGen_ColdFunctions};
Luau::CodeGen::compile(L, -1, nativeOptions);
}
int status = lua_resume(L, nullptr, 0);
REQUIRE(status == 0);
CHECK(lua_tonumber(L, -1) == 3);
}
TEST_CASE("IrInstructionLimit")
{

View file

@ -285,6 +285,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars
if (FFlag::LuauSolverV2)
{
Mode mode = sourceModule->mode ? *sourceModule->mode : Mode::Strict;
Frontend::Stats stats;
ModulePtr module = Luau::check(
*sourceModule,
mode,
@ -299,6 +300,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars
getFrontend().options,
{},
false,
stats,
{}
);

View file

@ -29,7 +29,6 @@
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
LUAU_FASTFLAG(LuauTypeFunOptional)
LUAU_FASTFLAG(LuauUpdateSetMetatableTypeSignature)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
@ -203,7 +202,6 @@ struct BuiltinsFixture : Fixture
explicit BuiltinsFixture(bool prepareAutocomplete = false);
// For the purpose of our tests, we're always the latest version of type functions.
ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true};
Frontend& getFrontend() override;
};

View file

@ -27,17 +27,13 @@ using namespace Luau;
LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
LUAU_FASTFLAG(LuauBetterScopeSelection)
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations)
LUAU_FASTFLAG(LuauPopulateRefinedTypesInFragmentFromOldSolver)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation)
LUAU_FASTFLAG(LuauForInProvidesRecommendations)
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
{
@ -67,14 +63,9 @@ struct FragmentAutocompleteFixtureImpl : BaseType
{
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true};
ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true};
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
ScopedFastFlag luauPopulateRefinedTypesInFragmentFromOldSolver{FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver, true};
ScopedFastFlag sffLuauFragmentAutocompleteTracksRValueRefinement{FFlag::LuauFragmentAutocompleteTracksRValueRefinements, true};
ScopedFastFlag sffLuauPopulateSelfTypesInFragment{FFlag::LuauPopulateSelfTypesInFragment, true};
ScopedFastFlag luauParseIncompleteInterpStringsWithLocation{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
FragmentAutocompleteFixtureImpl()
: BaseType(true)
@ -703,6 +694,7 @@ end
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "partial_for_numeric_in_condition")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
auto region = getAutocompleteRegion(
R"(
for c = 1,3
@ -710,7 +702,7 @@ for c = 1,3
Position{1, 11}
);
CHECK_EQ(Location{{1, 0}, {1, 11}}, region.fragmentLocation);
CHECK_EQ(Location{{1, 10}, {1, 11}}, region.fragmentLocation);
REQUIRE(region.parentBlock);
CHECK(region.nearestStatement->as<AstStatFor>());
}
@ -4110,6 +4102,198 @@ end
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "string_interpolation_format_provides_autocomplete_results")
{
const std::string source = R"(
type Foo = {x : number, x1 : string, x2 : boolean}
local e: Foo = {x = 1, x1 = "1", x2 = true}
local s =
)";
const std::string dest = R"(
type Foo = {x : number, x1 : string, x2 : boolean}
local e : Foo = {x = 1, x1 = "1", x2 = true}
local s = `{e. }`
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 14},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("x1"));
CHECK(result.result->acResults.entryMap.count("x2"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "string_interpolation_format_provides_results_inside_of_function_call")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
const std::string source = R"(
type T = {x : number, y : number, z : number}
local e = {x = 1, y = 2, z = 3}
print(`{e.x}`)
)";
const std::string dest = R"(
type T = {x : number, y : number, z : number}
local e = {x = 1, y = 2, z = 3}
print(`{e.x} {e.}`)
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 16},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
CHECK(result.result->acResults.entryMap.count("z"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_in_should_rec")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
const std::string source = R"(
type T = { x : {[number] : number}, y: number}
local x : T = ({} :: T)
for _,n in pairs(x.) do
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 19},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_no_do")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
const std::string source = R"(
type T = { x : {[number] : number}, y: number, z: number}
local x : T = ({} :: T)
for i =
end
)";
const std::string dest = R"(
type T = { x : {[number] : number}, y: number, z : number}
local x : T = ({} :: T)
for i = x.
end
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 10},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
CHECK(result.result->acResults.entryMap.count("z"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_step")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
const std::string source = R"(
type T = { x : {[number] : number}, y: number, z: number}
local x : T = ({} :: T)
for i = x.y, 100 do
end
)";
const std::string dest = R"(
type T = { x : {[number] : number}, y: number, z : number}
local x : T = ({} :: T)
for i = x.y, 100, x. do
end
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 20},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
CHECK(result.result->acResults.entryMap.count("z"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_max_delete")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
const std::string source = R"(
type T = { x : {[number] : number}, y: number, z: number}
local x : T = ({} :: T)
for i = x.y, x.z do
end
)";
const std::string dest = R"(
type T = { x : {[number] : number}, y: number, z : number}
local x : T = ({} :: T)
for i = x.y, x. do
end
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 15},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
CHECK(result.result->acResults.entryMap.count("z"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_max_add")
{
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
const std::string source = R"(
type T = { x : {[number] : number}, y: number, z: number}
local x : T = ({} :: T)
for i = x.y do
end
)";
const std::string dest = R"(
type T = { x : {[number] : number}, y: number, z : number}
local x : T = ({} :: T)
for i = x.y, x. do
end
)";
autocompleteFragmentInBothSolvers(
source,
dest,
Position{3, 15},
[](auto& result)
{
CHECK(!result.result->acResults.entryMap.empty());
CHECK(result.result->acResults.entryMap.count("x"));
CHECK(result.result->acResults.entryMap.count("y"));
CHECK(result.result->acResults.entryMap.count("z"));
}
);
}
// NOLINTEND(bugprone-unchecked-optional-access)
TEST_SUITE_END();

View file

@ -16,7 +16,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
namespace
{
@ -877,8 +876,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "discard_type_graphs")
TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
Frontend fe{&fileResolver, &configResolver, {false}};
fileResolver.source["Module/A"] = R"(

View file

@ -16,8 +16,8 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
TEST_SUITE_BEGIN("Generalization");
@ -227,7 +227,10 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a")
TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
TableType tt;
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
@ -261,7 +264,10 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?")
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
auto [aTy, aFree] = freshType();
auto [bTy, bFree] = freshType();
@ -376,10 +382,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type")
TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local func: <T>(T, (T) -> ()) -> () = nil :: any
@ -398,10 +401,7 @@ TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback")
TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
// FIXME: CLI-156389: this is clearly wrong, but also predates this PR.
LUAU_REQUIRE_NO_ERRORS(check(R"(
@ -415,7 +415,6 @@ TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2")
TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808")
{
ScopedFastFlag _{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true};
// All we care about here is that this has no errors, and we correctly
// infer that the `false` literal should be typed as `false`.
LUAU_REQUIRE_NO_ERRORS(check(R"(
@ -428,9 +427,9 @@ TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808")
TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional_inference")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
fileResolver.source["Module/ListFns"] = R"(

View file

@ -10,12 +10,16 @@ using namespace Luau;
LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("InferPolarity");
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
TypeArena arena;
ScopePtr globalScope = std::make_shared<Scope>(getBuiltins()->anyTypePack);
@ -53,6 +57,7 @@ TEST_CASE_FIXTURE(Fixture, "<a, b>({ read x: a, write x: b }) -> ()")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauInferPolarityOfReadWriteProperties, true},
};

View file

@ -14,7 +14,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTINT(LuauTypeCloneIterationLimit)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
TEST_SUITE_BEGIN("ModuleTests");
@ -138,8 +137,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table")
CHECK_EQ(std::optional<std::string>{"Cyclic"}, ttv->syntheticName);
TypeId methodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ttv->props["get"].readTy : ttv->props["get"].type_DEPRECATED();
TypeId methodType = *ttv->props["get"].readTy;
REQUIRE(methodType != nullptr);
const FunctionType* ftv = get<FunctionType>(methodType);
@ -172,7 +170,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2")
TableType* ctt = getMutable<TableType>(cloneTy);
REQUIRE(ctt);
TypeId clonedMethodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ctt->props["get"].readTy : ctt->props["get"].type_DEPRECATED();
TypeId clonedMethodType = *ctt->props["get"].readTy;
REQUIRE(clonedMethodType);
const FunctionType* cmf = get<FunctionType>(clonedMethodType);
@ -201,8 +199,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena")
TableType* exportsTable = getMutable<TableType>(*exports);
REQUIRE(exportsTable != nullptr);
TypeId signType =
FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED();
TypeId signType = *exportsTable->props["sign"].readTy;
REQUIRE(signType != nullptr);
CHECK(!isInArena(signType, module->interfaceTypes));
@ -356,18 +353,10 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit")
for (int i = 0; i < nesting; i++)
{
TableType* ttv = getMutable<TableType>(nested);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
ttv->props["a"].readTy = src.addType(TableType{});
ttv->props["a"].writeTy = ttv->props["a"].readTy;
nested = *ttv->props["a"].readTy;
}
else
{
ttv->props["a"].setType(src.addType(TableType{}));
nested = ttv->props["a"].type_DEPRECATED();
}
}
TypeArena dest;
CloneState cloneState{getBuiltins()};
@ -457,10 +446,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports")
TypeId typeB = modBiter->second.type;
TableType* tableB = getMutable<TableType>(typeB);
REQUIRE(tableB);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
CHECK(typeA == tableB->props["q"].readTy);
else
CHECK(typeA == tableB->props["q"].type_DEPRECATED());
}
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values")
@ -494,13 +480,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values")
TableType* tableB = getMutable<TableType>(*typeB);
REQUIRE_MESSAGE(tableB, "Expected a table, but got " << toString(*typeB));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
CHECK(tableA->props["a"].readTy == tableB->props["b"].readTy);
CHECK(tableA->props["a"].writeTy == tableB->props["b"].writeTy);
}
else
CHECK(tableA->props["a"].type_DEPRECATED() == tableB->props["b"].type_DEPRECATED());
}
TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table")

View file

@ -15,10 +15,10 @@
#include "doctest.h"
#include <iostream>
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
LUAU_FASTFLAG(LuauNewNonStrictSuppressesDynamicRequireErrors)
LUAU_FASTFLAG(LuauNewNonStrictReportsOneIndexedErrors)
using namespace Luau;
@ -125,6 +125,7 @@ declare os : {
}
@checked declare function require(target : any) : any
@checked declare function getAllTheArgsWrong(one: string, two: number, three: boolean) : any
)BUILTIN_SRC";
};
@ -629,8 +630,6 @@ optionalArgsAtTheEnd1("a", nil, 3)
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict")
{
ScopedFastFlag sff{FFlag::LuauNewNonStrictFixGenericTypePacks, true};
CheckResult result = checkNonStrict(R"(
--!nonstrict
local test: <T...>(T...) -> () -- TypeError: Unknown type 'T'
@ -862,4 +861,22 @@ require("@self/NonExistent")
CHECK(req2 != nullptr);
}
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "new_non_strict_stringifies_checked_function_errors_as_one_indexed")
{
ScopedFastFlag sff = {FFlag::LuauNewNonStrictReportsOneIndexedErrors, true};
CheckResult result = checkNonStrict(R"(
getAllTheArgsWrong(3, true, "what")
)");
LUAU_REQUIRE_ERROR_COUNT(3, result);
const CheckedFunctionCallError* err1 = get<CheckedFunctionCallError>(result.errors[0]);
const CheckedFunctionCallError* err2 = get<CheckedFunctionCallError>(result.errors[1]);
const CheckedFunctionCallError* err3 = get<CheckedFunctionCallError>(result.errors[2]);
CHECK(err1 != nullptr);
CHECK(err2 != nullptr);
CHECK(err3 != nullptr);
CHECK_EQ("Function 'getAllTheArgsWrong' expects 'string' at argument #1, but got 'number'", toString(result.errors[0]));
CHECK_EQ("Function 'getAllTheArgsWrong' expects 'number' at argument #2, but got 'boolean'", toString(result.errors[1]));
CHECK_EQ("Function 'getAllTheArgsWrong' expects 'boolean' at argument #3, but got 'string'", toString(result.errors[2]));
}
TEST_SUITE_END();

View file

@ -17,8 +17,8 @@ LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTINT(LuauTypeLengthLimit)
LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauParseStringIndexer)
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
extern bool luau_telemetry_parsed_return_type_variadic_with_type_suffix;
@ -951,6 +951,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid")
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
{
ScopedFastFlag sff{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto columnOfEndBraceError = [this](const char* code)
{
try
@ -969,9 +970,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
}
};
// This makes sure that the error is coming from the brace itself
// This makes sure that the error is coming from the closing brace itself
CHECK_EQ(columnOfEndBraceError("_ = `{a`"), 7);
CHECK_EQ(columnOfEndBraceError("_ = `{abcdefg`"), 13);
CHECK_EQ(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{abcdefg`"));
CHECK_NE(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{a`"));
}
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table")
@ -4194,8 +4196,136 @@ TEST_CASE_FIXTURE(Fixture, "parsing_type_suffix_for_return_type_with_variadic")
TEST_CASE_FIXTURE(Fixture, "parsing_string_union_indexers")
{
ScopedFastFlag _{FFlag::LuauParseStringIndexer, true};
parse(R"(type foo = { ["bar" | "baz"]: number })");
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_at_eof")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a)");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 17));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17}));
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_at_eof")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a})");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 18));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?");
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_at_eof")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a`)");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 18));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_broken_string")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a
)");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 17));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17}));
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_broken_string")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a}
)");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 18));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?");
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
}
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_broken_string")
{
ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto parseResult = tryParse(R"(print(`{e.x} {e.a`
)");
const auto first = parseResult.root->body.data[0];
auto expr = first->as<AstStatExpr>();
CHECK(expr != nullptr);
auto call = expr->expr->as<AstExprCall>();
CHECK(call != nullptr);
auto interpString = call->args.data[0]->as<AstExprInterpString>();
CHECK(interpString != nullptr);
CHECK(interpString->expressions.size == 2);
CHECK(interpString->location.begin == Position(0, 6));
CHECK(interpString->location.end == Position(0, 18));
CHECK_EQ(parseResult.errors.size(), 2);
auto err = parseResult.errors[0];
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
}
TEST_SUITE_END();

View file

@ -568,6 +568,20 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule")
assertOutputContainsAll({"true"});
}
TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModuleCaseInsensitive")
{
lua_pushcfunction(L, luarequire_registermodule, nullptr);
lua_pushstring(L, "@test/helloworld");
lua_newtable(L);
lua_pushstring(L, "hello");
lua_pushstring(L, "world");
lua_settable(L, -3);
lua_call(L, 2, 0);
runCode(L, "return require('@TeSt/heLLoWoRld').hello == 'world'");
assertOutputContainsAll({"true"});
}
TEST_CASE_FIXTURE(ReplWithPathFixture, "ProxyRequire")
{
luarequire_pushproxyrequire(L, requireConfigInit, createCliRequireContext(L));

View file

@ -25,7 +25,9 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauIceLess)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauSimplifyAnyAndUnion)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
LUAU_FASTFLAG(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
struct LimitFixture : BuiltinsFixture
{
@ -292,7 +294,9 @@ TEST_CASE_FIXTURE(LimitFixture, "Signal_exerpt" * doctest::timeout(0.5))
// These flags are required to surface the problem.
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
// And this flag is the one that fixes it.
{FFlag::LuauSimplifyAnyAndUnion, true},
@ -341,7 +345,7 @@ TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauLimitDynamicConstraintSolving, true},
{FFlag::LuauLimitDynamicConstraintSolving3, true},
};
constexpr const char* src = R"(
@ -369,4 +373,65 @@ TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints")
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "limit_number_of_dynamically_created_constraints_2")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauLimitDynamicConstraintSolving3, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
{FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints, true},
};
ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 50};
CheckResult result = check(R"(
local T = {}
export type T = typeof(setmetatable(
{},
{} :: typeof(T)
))
function T.One(): T
return nil :: any
end
function T.Two(self: T) end
function T.Three(self: T, x)
self.Prop[x] = true
end
function T.Four(self: T, x)
print("", x)
end
function T.Five(self: T) end
function T.Six(self: T) end
function T.Seven(self: T) end
function T.Eight(self: T) end
function T.Nine(self: T) end
function T.Ten(self: T) end
function T.Eleven(self: T) end
function T.Twelve(self: T) end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
LUAU_REQUIRE_ERROR(result, UnknownProperty);
// A sanity check to ensure that this statistic is being recorded at all.
CHECK(frontend->stats.dynamicConstraintsCreated > 10);
CHECK(frontend->stats.dynamicConstraintsCreated < 40);
}
TEST_SUITE_END();

View file

@ -17,6 +17,7 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
using namespace Luau;
@ -1654,7 +1655,10 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
TypeId argTy = arena.freshType(getBuiltins(), moduleScope.get());
FreeType* freeArg = getMutable<FreeType>(argTy);

View file

@ -14,8 +14,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
TEST_SUITE_BEGIN("ToString");
@ -507,8 +505,6 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax")
TEST_CASE_FIXTURE(Fixture, "the_empty_type_pack_should_be_parenthesized")
{
ScopedFastFlag sff{FFlag::LuauFixEmptyTypePackStringification, true};
TypePackVar emptyTypePack{TypePack{}};
CHECK_EQ(toString(&emptyTypePack), "()");
@ -889,8 +885,6 @@ TEST_CASE_FIXTURE(Fixture, "tostring_unsee_ttv_if_array")
TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
--!strict
function f1(t: {a : number, b: string, c: {d: string}}) : {a : number, b : string, c : { d : number}}

View file

@ -16,10 +16,11 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauEmptyStringInKeyOf)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauDoNotBlockOnStuckTypeFunctions)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
struct TypeFunctionFixture : Fixture
{
@ -351,8 +352,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number }
type KeysOfMyObject = keyof<MyObject>
@ -374,8 +373,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
local metatable = { __index = {w = 1} }
local obj = setmetatable({x = 1, y = 2, z = 3}, metatable)
@ -433,8 +430,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontabl
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
if (!FFlag::LuauSolverV2)
return;
@ -466,8 +461,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number }
type MyOtherObject = { w: number, y: number, z: number }
@ -504,8 +497,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number }
type KeysOfMyObject = rawkeyof<MyObject>
@ -527,8 +518,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
local metatable = { __index = {w = 1} }
local obj = setmetatable({x = 1, y = 2, z = 3}, metatable)
@ -570,8 +559,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_unio
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number }
type MyOtherObject = { w: number, y: number, z: number }
@ -608,8 +595,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "keyof_type_function_works_on_extern_types"
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type KeysOfMyObject = keyof<BaseClass>
@ -744,7 +729,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag sff[] = {{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauStuckTypeFunctionsStillDispatch, true}};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
local EnumVariants = {
@ -1008,8 +996,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean}
type IdxAType = index<MyObject, "a">
@ -1182,17 +1168,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_var_indexer")
type errType1 = index<MyObject, key>
)");
if (FFlag::LuauErrorSuppressionTypeFunctionArgs)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Unknown type 'key'");
}
else
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Second argument to index<MyObject, _> is not a valid index type");
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexer")
@ -1309,8 +1286,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean}
type RawAType = rawget<MyObject, "a">
@ -1354,17 +1329,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_errors_w_var_indexer")
)");
if (FFlag::LuauErrorSuppressionTypeFunctionArgs)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Unknown type 'key'");
}
else
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Second argument to rawget<MyObject, _> is not a valid index type");
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexer")
@ -1691,8 +1657,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "error_suppression_should_work_on_type_functi
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag errorSuppressionTypeFunctionArgs{FFlag::LuauErrorSuppressionTypeFunctionArgs, true};
CheckResult result = check(R"(
local Colours = {
Red = 1,
@ -1720,7 +1684,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fully_dispatch_type_function_that_is_paramet
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(
@ -1747,7 +1711,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "undefined_add_application")
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
@ -1806,8 +1770,9 @@ struct TFFixture
TypeCheckLimits limits;
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
const ScopedFastFlag sff[1] = {
const ScopedFastFlag sff[2] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
BuiltinTypeFunctions builtinTypeFunctions;
@ -1870,7 +1835,10 @@ TEST_CASE_FIXTURE(TFFixture, "a_type_function_parameterized_on_generics_is_solve
TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_solved_tf_is_solved")
{
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
TypeId a = arena->addType(GenericType{"A"});
TypeId b = arena->addType(GenericType{"B"});
@ -1889,7 +1857,10 @@ TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_solved_tf_is_solved")
TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_stuck_tf_is_stuck")
{
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
TypeId innerAddTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.addFunc, {builtinTypes_.bufferType, builtinTypes_.booleanType}});
@ -1903,4 +1874,23 @@ TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_stuck_tf_is_stuck")
CHECK(tfit->state == TypeFunctionInstanceState::Stuck);
}
TEST_CASE_FIXTURE(Fixture, "generic_type_functions_should_not_get_stuck_or")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauForceSimplifyConstraint2, true},
{FFlag::LuauDoNotBlockOnStuckTypeFunctions, true},
};
CheckResult result = check(R"(
local function init(data)
return not data or data == ''
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<ExplicitFunctionAnnotationRecommended>(result.errors[0]));
}
TEST_SUITE_END();

View file

@ -10,9 +10,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauTypeFunctionSerializeFollowMetatable)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
@ -352,7 +350,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function serialize_union(arg)
@ -372,7 +369,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function numberhuh()
@ -391,7 +387,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function foobar()
@ -413,8 +408,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getunion()
local ty = types.unionof(types.string, types.number, types.boolean)
@ -442,7 +435,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function serialize_intersection(arg)
@ -464,8 +456,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getintersection()
local tbl1 = types.newtable(nil, nil, nil)
@ -499,7 +489,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getnegation()
@ -548,7 +537,6 @@ local function notok(idx: fail<number>): never return idx end
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function serialize_table(arg)
@ -568,7 +556,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function gettable()
@ -607,7 +594,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getmetatable()
@ -657,8 +643,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getfunction()
local ty = types.newfunction(nil, nil) -- () -> ()
@ -718,7 +702,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_serialization_works2")
TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getclass(arg)
@ -740,7 +723,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works")
TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getclass(arg)
@ -767,7 +749,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function checkmut()
@ -799,7 +780,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function getcopy()
@ -985,7 +965,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function foo()
@ -1006,7 +985,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function first(arg)
@ -1059,7 +1037,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_3")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_unordered")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function bar()
@ -1125,8 +1102,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function optionify(tbl)
if not tbl:is("table") then
@ -1179,8 +1154,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function foo(tbl)
local count = 0
@ -1244,8 +1217,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type function stripindexer(tbl)
if not tbl:is("table") then
@ -1331,10 +1302,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_eq_field")
TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
type function test(x)
@ -2403,7 +2371,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "type_alias_reduction_errors")
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
@ -2456,8 +2424,6 @@ end
TEST_CASE_FIXTURE(Fixture, "udtf_metatable_serialization_follows")
{
ScopedFastFlag luauTypeFunctionSerializeFollowMetatable{FFlag::LuauTypeFunctionSerializeFollowMetatable, true};
LUAU_REQUIRE_ERRORS(check(R"(
_ = setmetatable(_(),_),(_) or _ == _ or f
while _() do

View file

@ -10,7 +10,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("TypeAliases");
@ -205,10 +204,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases")
TEST_CASE_FIXTURE(Fixture, "generic_aliases")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
type T<a> = { v: a }
@ -224,10 +220,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases")
TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
type T<a> = { v: a }

View file

@ -9,7 +9,6 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
using namespace Luau;
@ -445,11 +444,9 @@ TEST_CASE_FIXTURE(Fixture, "self_referential_type_alias")
std::optional<Property> incr = get(oTable->props, "incr");
REQUIRE(incr);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
REQUIRE(incr->readTy);
const FunctionType* incrFunc =
FFlag::LuauRemoveTypeCallsForReadWriteProps ? get<FunctionType>(*incr->readTy) : get<FunctionType>(incr->type_DEPRECATED());
const FunctionType* incrFunc = get<FunctionType>(*incr->readTy);
REQUIRE(incrFunc);
std::optional<TypeId> firstArg = first(incrFunc->argTypes);
@ -608,14 +605,8 @@ TEST_CASE_FIXTURE(Fixture, "interface_types_belong_to_interface_arena")
TableType* exportsTable = getMutable<TableType>(*exportsType);
REQUIRE(exportsTable != nullptr);
TypeId n;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(exportsTable->props["n"].readTy);
n = *exportsTable->props["n"].readTy;
}
else
n = exportsTable->props["n"].type_DEPRECATED();
TypeId n = *exportsTable->props["n"].readTy;
REQUIRE(n != nullptr);
CHECK(isInArena(n, mod.interfaceTypes));
@ -670,24 +661,12 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti
TableType* exportsTable = getMutable<TableType>(*exportsType);
REQUIRE(exportsTable != nullptr);
TypeId aType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(exportsTable->props["a"].readTy);
aType = *exportsTable->props["a"].readTy;
}
else
aType = exportsTable->props["a"].type_DEPRECATED();
TypeId aType = *exportsTable->props["a"].readTy;
REQUIRE(aType);
TypeId bType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(exportsTable->props["b"].readTy);
bType = *exportsTable->props["b"].readTy;
}
else
bType = exportsTable->props["b"].type_DEPRECATED();
TypeId bType = *exportsTable->props["b"].readTy;
REQUIRE(bType);
CHECK(isInArena(recordType, mod.interfaceTypes));

View file

@ -14,7 +14,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
TEST_SUITE_BEGIN("TypeInferAnyError");
@ -248,13 +247,8 @@ TEST_CASE_FIXTURE(Fixture, "assign_prop_to_table_by_calling_any_yields_any")
REQUIRE(ttv);
REQUIRE(ttv->props.count("prop"));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(ttv->props["prop"].readTy);
CHECK_EQ("any", toString(*ttv->props["prop"].readTy));
}
else
REQUIRE_EQ("any", toString(ttv->props["prop"].type_DEPRECATED()));
}
TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself")

View file

@ -12,11 +12,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableCloneClonesType3)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauStringFormatImprovements)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
@ -713,19 +709,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash")
end
)");
if (FFlag::LuauSolverV2 && FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
if (FFlag::LuauSolverV2)
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[0]));
CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[1]));
}
else if (FFlag::LuauSolverV2)
{
// Counterintuitively, the parameter l0 is unconstrained and therefore it is valid to pass nil.
// The new solver therefore considers that parameter to be optional.
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("Argument count mismatch. Function expects at least 1 argument, but none are specified" == toString(result.errors[0]));
}
else
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
@ -1317,14 +1306,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change")
REQUIRE(mathTy);
TableType* ttv = getMutable<TableType>(mathTy);
REQUIRE(ttv);
const FunctionType* ftv;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(ttv->props["frexp"].readTy);
ftv = get<FunctionType>(*ttv->props["frexp"].readTy);
}
else
ftv = get<FunctionType>(ttv->props["frexp"].type_DEPRECATED());
const FunctionType* ftv = get<FunctionType>(*ttv->props["frexp"].readTy);
REQUIRE(ftv);
auto original = ftv->level;
@ -1679,7 +1664,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any")
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any_2")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"(
local fmt = "Hello, %s!" :: any
@ -1695,7 +1679,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any_2")
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"(
local fmt: "Hello, %s!" = "Hello, %s!"
@ -1713,7 +1696,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types
TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_string_is_dynamic")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"(
local fmt: string = "Hello, %s!"
@ -1733,7 +1715,6 @@ TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEnableWriteOnlyProperties, true},
{FFlag::LuauWriteOnlyPropertyMangling, true},
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
};

View file

@ -11,7 +11,6 @@ using namespace Luau;
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
TEST_SUITE_BEGIN("DefinitionTests");
@ -171,18 +170,19 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function")
REQUIRE(!result.success);
CHECK_EQ(result.parseResult.errors.size(), 0);
REQUIRE(bool(result.module));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
REQUIRE_EQ(result.module->errors.size(), 2);
else
REQUIRE_EQ(result.module->errors.size(), 1);
GenericError* ge = get<GenericError>(result.module->errors[0]);
REQUIRE(ge);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
CHECK_EQ("Cannot overload read type of non-function class member 'X'", ge->message);
else
CHECK_EQ("Cannot overload non-function class member 'X'", ge->message);
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
if (FFlag::LuauSolverV2)
{
GenericError* ge2 = get<GenericError>(result.module->errors[1]);
REQUIRE(ge2);
@ -380,14 +380,8 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re
const auto& method = cls->props["myMethod"];
CHECK_EQ(method.documentationSymbol, "@test/globaltype/MyClass.myMethod");
FunctionType* function;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(method.readTy);
function = getMutable<FunctionType>(*method.readTy);
}
else
function = getMutable<FunctionType>(method.type_DEPRECATED());
FunctionType* function = getMutable<FunctionType>(*method.readTy);
REQUIRE(function);
REQUIRE(function->definition.has_value());

View file

@ -24,16 +24,12 @@ LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauCollapseShouldNotCrash)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauFormatUseLastPosition)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TypeInferFunctions");
@ -82,8 +78,6 @@ TEST_CASE_FIXTURE(Fixture, "tc_function")
TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
function myFunction(): number
local a = 0
@ -195,15 +189,9 @@ TEST_CASE_FIXTURE(Fixture, "generalize_table_property")
const TableType* tt = get<TableType>(follow(t));
REQUIRE(tt);
TypeId fooTy;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
const Property& foo = tt->props.at("foo");
REQUIRE(foo.readTy);
fooTy = *foo.readTy;
}
else
fooTy = tt->props.at("foo").type_DEPRECATED();
TypeId fooTy = *foo.readTy;
CHECK("<a>(a) -> a" == toString(fooTy));
}
@ -249,15 +237,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified")
REQUIRE(ttv->props.count("f"));
TypeId k;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
const Property& f = ttv->props["f"];
REQUIRE(f.readTy);
k = *f.readTy;
}
else
k = ttv->props["f"].type_DEPRECATED();
TypeId k = *f.readTy;
REQUIRE(k);
}
@ -1510,8 +1492,6 @@ local a: TableWithFunc = { x = 3, y = 4, f = function(a, b) return a + b end }
TEST_CASE_FIXTURE(Fixture, "infer_return_value_type")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
local function f(): {string|number}
return {1, "b", 3}
@ -1697,8 +1677,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite")
TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite_2")
{
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
CheckResult result = check(R"(
local t: { f: ((x: number) -> number)? } = {}
@ -1713,7 +1691,7 @@ t.f = function(x)
end
)");
if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2)
if (FFlag::LuauEagerGeneralization4)
{
LUAU_CHECK_ERROR_COUNT(2, result);
LUAU_CHECK_ERROR(result, WhereClauseNeeded); // x2
@ -1783,8 +1761,6 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th
TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_unsealed_overwrite")
{
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
CheckResult result = check(R"(
local t = { f = nil :: ((x: number) -> number)? }
@ -2061,10 +2037,7 @@ u.b().foo()
CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function expects 1 to 3 arguments, but none are specified");
CHECK_EQ(toString(result.errors[3]), "Argument count mismatch. Function expects 2 to 4 arguments, but none are specified");
CHECK_EQ(toString(result.errors[4]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 3 arguments, but only 1 is specified");
else
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified");
CHECK_EQ(toString(result.errors[6]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
@ -2454,8 +2427,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic")
TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
function num()
return 5
@ -2478,8 +2449,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str")
TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
local function num_or_str()
if math.random() > 0.5 then
@ -2623,7 +2592,11 @@ end
TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
// CLI-114134: This test:
// a) Has a kind of weird result (suggesting `number | false` is not great);
@ -2632,12 +2605,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type")
// clearly `number`. Hopefully the egraph will be able to unfold this.
CheckResult result = check(R"(
function fib(n)
function fib(n)
return n < 2 and 1 or fib(n-1) + fib(n-2)
end
)");
end
)");
LUAU_REQUIRE_ERRORS(result);
LUAU_REQUIRE_ERROR_COUNT(1, result);
auto err = get<ExplicitFunctionAnnotationRecommended>(result.errors.back());
LUAU_ASSERT(err);
CHECK("false | number" == toString(err->recommendedReturn));
@ -2647,13 +2620,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type")
{
if (!FFlag::LuauSolverV2)
return;
CheckResult result = check(R"(
function fib(n, u)
return (n or u) and (n < u and n + fib(n,u))
end
)");
LUAU_REQUIRE_ERRORS(result);
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(
function fib(n, u)
return (n or u) and (n < u and n + fib(n,u))
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
auto err = get<ExplicitFunctionAnnotationRecommended>(result.errors.back());
LUAU_ASSERT(err);
CHECK("number" == toString(err->recommendedReturn));
@ -2932,10 +2911,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_ast_stat_fun")
TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSimplifyOutOfLine2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSimplifyOutOfLine2, true};
CheckResult result = check(R"(
function foo(player)
@ -2954,17 +2930,23 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
{
// The new solver should ideally be able to do better here, but this is no worse than the old solver.
if (FFlag::LuauEagerGeneralization4)
if (FFlag::LuauTrackFreeInteriorTypePacks)
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
auto tm1 = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm1);
CHECK(toString(tm1->wantedType) == "string");
CHECK(toString(tm1->givenType) == "boolean");
auto tm2 = get<TypeMismatch>(result.errors[1]);
REQUIRE(tm2);
CHECK(toString(tm2->wantedType) == "string");
if (FFlag::LuauEagerGeneralization4)
CHECK(toString(tm2->givenType) == "unknown & ~(false?)");
else
CHECK(toString(tm2->givenType) == "~(false?)");
}
else
{

View file

@ -10,15 +10,13 @@
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauIntersectNotNil)
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauContainsAnyGenericFollowBeforeChecking)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
using namespace Luau;
@ -908,8 +906,6 @@ end
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
--!strict
-- At one point this produced a UAF
@ -1213,15 +1209,9 @@ TEST_CASE_FIXTURE(Fixture, "generic_table_method")
REQUIRE(tTable != nullptr);
REQUIRE(tTable->props.count("bar"));
TypeId barType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& bar = tTable->props["bar"];
REQUIRE(bar.readTy);
barType = *bar.readTy;
}
else
barType = tTable->props["bar"].type_DEPRECATED();
TypeId barType = *bar.readTy;
REQUIRE(barType != nullptr);
const FunctionType* ftv = get<FunctionType>(follow(barType));
@ -1263,15 +1253,8 @@ TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions")
std::optional<Property> fooProp = get(t->props, "foo");
REQUIRE(bool(fooProp));
const FunctionType* foo;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy);
foo = get<FunctionType>(follow(*fooProp->readTy));
}
else
foo = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
const FunctionType* foo = get<FunctionType>(follow(*fooProp->readTy));
REQUIRE(bool(foo));
std::optional<TypeId> ret_ = first(foo->retTypes);
@ -1318,14 +1301,8 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_cyclic_generic_function")
std::optional<Property> methodProp = get(argTable->props, "method");
REQUIRE(bool(methodProp));
const FunctionType* methodFunction;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(methodProp->readTy);
methodFunction = get<FunctionType>(follow(*methodProp->readTy));
}
else
methodFunction = get<FunctionType>(follow(methodProp->type_DEPRECATED()));
const FunctionType* methodFunction = get<FunctionType>(follow(*methodProp->readTy));
REQUIRE(methodFunction != nullptr);
std::optional<TypeId> methodArg = first(methodFunction->argTypes);
@ -1525,12 +1502,12 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded"
// Important FIXME CLI-158432: This test exposes some problems with overload
// selection and generic type substitution when
// FFlag::LuauStuckTypeFunctionsStillDispatch is set.
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions")
{
ScopedFastFlag _[] = {
{FFlag::LuauSubtypingCheckFunctionGenericCounts, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result;
@ -1547,13 +1524,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions")
local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not inferred
)");
if (FFlag::LuauStuckTypeFunctionsStillDispatch) // FIXME CLI-158432
CHECK("add<X, X> | number" == toString(requireType("b")));
else
CHECK("number" == toString(requireType("b")));
CHECK("add<X, X> | number" == toString(requireType("b"))); // FIXME CLI-161128
CHECK("<a>(a, a, (a, a) -> a) -> a" == toString(requireType("sum")));
CHECK("<a>(a, a, (a, a) -> a) -> a" == toString(requireTypeAtPosition({7, 29})));
LUAU_REQUIRE_ERROR_COUNT(1, result); // FIXME CLI-161128
CHECK(get<ExplicitFunctionAnnotationRecommended>(result.errors[0]));
}
else
{
@ -1567,10 +1542,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions")
local b = sumrec(sum) -- ok
local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not inferred
)");
}
if (!FFlag::LuauStuckTypeFunctionsStillDispatch) // FIXME CLI-158432
LUAU_REQUIRE_NO_ERRORS(result);
}
}
@ -1857,7 +1830,8 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_packs_shouldnt_be_bound_to_themselves")
ScopedFastFlag flags[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauSubtypingCheckFunctionGenericCounts, true},
{FFlag::LuauEagerGeneralization4, true}
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(

View file

@ -10,11 +10,9 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("IntersectionTypes");
@ -337,10 +335,7 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed")
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
CheckResult result = check(R"(
type X = { x: (number) -> number }
@ -463,8 +458,6 @@ Type 'number' could not be converted into 'X')";
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type X = { x: number }
type Y = { y: number }

View file

@ -15,7 +15,8 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
LUAU_FASTINT(LuauSolverConstraintLimit)
using namespace Luau;
@ -854,7 +855,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "internal_types_are_scrubbed_from_module")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::DebugLuauMagicTypes, true},
{FFlag::LuauLimitDynamicConstraintSolving, true}
{FFlag::LuauLimitDynamicConstraintSolving3, true}
};
fileResolver.source["game/A"] = R"(
@ -867,4 +868,55 @@ return function(): _luau_blocked_type return nil :: any end
CHECK("(...any) -> *error-type*" == toString(getFrontend().moduleResolver.getModule("game/A")->returnType));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "internal_type_errors_are_only_reported_once")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::DebugLuauMagicTypes, true},
{FFlag::LuauLimitDynamicConstraintSolving3, true}
};
fileResolver.source["game/A"] = R"(
return function(): { X: _luau_blocked_type, Y: _luau_blocked_type } return nil :: any end
)";
CheckResult result = getFrontend().check("game/A");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<InternalError>(result.errors[0]));
CHECK("(...any) -> { X: *error-type*, Y: *error-type* }" == toString(getFrontend().moduleResolver.getModule("game/A")->returnType));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "scrub_unsealed_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauLimitDynamicConstraintSolving3, true}
};
ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 5};
fileResolver.source["game/A"] = R"(
type Array<T> = {T}
type Hello = Array<Array<Array<Array<Array<Array<Array<Array<Array<Array<number>>>>>>>>>>
local X = {}
X.foo = 42
X.bar = ""
return X
)";
fileResolver.source["game/B"] = R"(
local x = require(game.A)
x.lmao = 42
return {}
)";
CheckResult result = getFrontend().check("game/B");
// This is going to have a _ton_ of errors
LUAU_REQUIRE_ERRORS(result);
LUAU_CHECK_ERROR(result, CodeTooComplex);
LUAU_CHECK_ERROR(result, ConstraintSolvingIncompleteError);
LUAU_CHECK_ERROR(result, InternalError);
LUAU_CHECK_ERROR(result, CannotExtendTable);
}
TEST_SUITE_END();

View file

@ -10,6 +10,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
namespace
{
@ -52,7 +53,10 @@ TEST_CASE_FIXTURE(NegationFixture, "string_is_not_a_subtype_of_negated_string")
TEST_CASE_FIXTURE(Fixture, "cofinite_strings_can_be_compared_for_equality")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(
function f(e)

View file

@ -14,7 +14,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TypeInferOOP");
@ -29,11 +29,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi
someTable.Function1() -- Argument count mismatch
)");
if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE(get<CountMismatch>(result.errors[0]));
}
}
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2")
@ -47,11 +44,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_
someTable.Function2() -- Argument count mismatch
)");
if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE(get<CountMismatch>(result.errors[0]));
}
}
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_another_overload_works")
@ -561,6 +555,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern")
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
@ -593,6 +588,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern_2")
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(

View file

@ -12,17 +12,13 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
LUAU_FASTFLAG(LuauSimplificationTableExternType)
LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauNormalizationIntersectTablesPreservesExternTypes)
LUAU_FASTFLAG(LuauNormalizationReorderFreeTypeIntersect)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauRefineNoRefineAlways)
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
using namespace Luau;
@ -661,7 +657,7 @@ TEST_CASE_FIXTURE(Fixture, "free_type_is_equal_to_an_lvalue")
{
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauNormalizationReorderFreeTypeIntersect, true},
};
@ -1633,8 +1629,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "refine_param_of_type_folder_or_p
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "isa_type_refinement_must_be_known_ahead_of_time")
{
ScopedFastFlag sff{FFlag::LuauSimplificationTableExternType, true};
CheckResult result = check(R"(
local function f(x): Instance
if x:IsA("Folder") then
@ -1673,19 +1667,10 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_optional_properties_sh
local pos = part1.Position
)");
if (FFlag::LuauSolverV2 && !FFlag::LuauNormalizationIntersectTablesPreservesExternTypes)
{
// CLI-142467: this is a major regression that we need to address.
CHECK_EQ("never", toString(requireTypeAtPosition({3, 15})));
CHECK_EQ("any", toString(requireTypeAtPosition({6, 29})));
}
else
{
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15})));
CHECK_EQ("Vector3", toString(requireTypeAtPosition({6, 29})));
}
}
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_non_existent_properties_should_not_refine_extern_types_to_never")
@ -1703,20 +1688,11 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_non_existent_propertie
LUAU_REQUIRE_ERRORS(result);
CHECK_EQ(toString(result.errors[0]), "Key 'Part8' not found in class 'WeldConstraint'");
if (FFlag::LuauSolverV2 && !FFlag::LuauNormalizationIntersectTablesPreservesExternTypes)
{
// CLI-142467: this is a major regression that we need to address.
CHECK_EQ("never", toString(requireTypeAtPosition({3, 15})));
CHECK_EQ("any", toString(requireTypeAtPosition({6, 29})));
}
else
{
CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15})));
if (FFlag::LuauSolverV2)
CHECK_EQ("any", toString(requireTypeAtPosition({6, 29})));
else
CHECK_EQ("*error-type*", toString(requireTypeAtPosition({6, 29})));
}
}
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "x_is_not_instance_or_else_not_part")
@ -2252,7 +2228,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_isindexkey_refine_conjunction"
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauForceSimplifyConstraint2, true},
};
@ -2265,7 +2241,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_isindexkey_refine_conjunction"
end
)");
LUAU_REQUIRE_ERROR_COUNT(3, result);
LUAU_CHECK_ERROR_COUNT(3, result);
// For some reason we emit three error here.
for (const auto& e : result.errors)
@ -2712,7 +2688,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_single")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
{FFlag::LuauRefineTablesWithReadType, true},
};
@ -2732,7 +2707,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_union")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
{FFlag::LuauRefineTablesWithReadType, true},
};
@ -2906,7 +2880,6 @@ TEST_CASE_FIXTURE(Fixture, "force_simplify_constraint_doesnt_drop_blocked_type")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauForceSimplifyConstraint2, true},
{FFlag::LuauSimplifyOutOfLine2, true},
};

View file

@ -7,7 +7,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("TypeSingletons");
@ -376,10 +375,7 @@ TEST_CASE_FIXTURE(Fixture, "indexer_can_be_union_of_singletons")
TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
@ -396,8 +392,6 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_string")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type Cat = { tag: 'cat', catfood: string }
type Dog = { tag: 'dog', dogfood: string }

View file

@ -24,23 +24,17 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
LUAU_FASTFLAG(LuauAutocompleteMissingFollows)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
LUAU_FASTFLAG(LuauDfgForwardNilFromAndOr)
LUAU_FASTFLAG(LuauInferActualIfElseExprType)
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TableTests");
@ -90,33 +84,18 @@ TEST_CASE_FIXTURE(Fixture, "basic")
std::optional<Property> fooProp = get(tType->props, "foo");
REQUIRE(bool(fooProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy);
CHECK_EQ(PrimitiveType::String, getPrimitiveType(*fooProp->readTy));
}
else
CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type_DEPRECATED()));
std::optional<Property> bazProp = get(tType->props, "baz");
REQUIRE(bool(bazProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(bazProp->readTy);
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*bazProp->readTy));
}
else
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type_DEPRECATED()));
std::optional<Property> quuxProp = get(tType->props, "quux");
REQUIRE(bool(quuxProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(quuxProp->readTy);
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy));
}
else
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type_DEPRECATED()));
}
TEST_CASE_FIXTURE(Fixture, "augment_table")
@ -146,15 +125,9 @@ TEST_CASE_FIXTURE(Fixture, "augment_nested_table")
REQUIRE(tType->props.find("p") != tType->props.end());
const TableType* pType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& p = tType->props["p"];
REQUIRE(p.readTy);
pType = get<TableType>(p.readTy);
}
else
pType = get<TableType>(tType->props["p"].type_DEPRECATED());
const TableType* pType = get<TableType>(p.readTy);
REQUIRE(pType != nullptr);
CHECK("{ p: { foo: string } }" == toString(requireType("t"), {true}));
@ -298,14 +271,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function")
std::optional<Property> fooProp = get(tableType->props, "foo");
REQUIRE(bool(fooProp));
const FunctionType* methodType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy);
methodType = get<FunctionType>(follow(fooProp->readTy));
}
else
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
const FunctionType* methodType = get<FunctionType>(follow(fooProp->readTy));
REQUIRE(methodType != nullptr);
}
@ -320,14 +287,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
std::optional<Property> uProp = get(tableType->props, "U");
REQUIRE(bool(uProp));
TypeId uType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(uProp->readTy);
uType = *uProp->readTy;
}
else
uType = uProp->type_DEPRECATED();
TypeId uType = *uProp->readTy;
const TableType* uTable = get<TableType>(uType);
REQUIRE(uTable != nullptr);
@ -335,14 +296,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
std::optional<Property> fooProp = get(uTable->props, "foo");
REQUIRE(bool(fooProp));
const FunctionType* methodType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy);
methodType = get<FunctionType>(follow(fooProp->readTy));
}
else
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
const FunctionType* methodType = get<FunctionType>(follow(fooProp->readTy));
REQUIRE(methodType != nullptr);
std::vector<TypeId> methodArgs = flatten(methodType->argTypes).first;
@ -958,8 +913,6 @@ TEST_CASE_FIXTURE(Fixture, "array_factory_function")
TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
function f(a: {number}): {string}
return a
@ -1112,16 +1065,9 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s
REQUIRE(tableType->indexer == std::nullopt);
REQUIRE(0 != tableType->props.count("a"));
TypeId propertyA;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& a = tableType->props["a"];
REQUIRE(a.readTy);
propertyA = *a.readTy;
}
else
propertyA = tableType->props["a"].type_DEPRECATED();
TypeId propertyA = *a.readTy;
REQUIRE(propertyA != nullptr);
CHECK_EQ(*getBuiltins()->stringType, *propertyA);
}
@ -1750,8 +1696,6 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2")
TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type StringToStringMap = { [string]: string }
local rt: StringToStringMap = { ["foo"] = 1 }
@ -1854,8 +1798,6 @@ TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer4")
TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
function f(vec1: {x: number}): {x: number, y: number, z: number}
return vec1
@ -1888,8 +1830,6 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi
TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors2")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type MixedTable = {[number]: number, x: number}
local t: MixedTable = {"fail"}
@ -2098,11 +2038,7 @@ TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound")
TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto result = check(R"(
local function _(t: { [string]: number? }): number
@ -2357,12 +2293,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "quantifying_a_bound_var_works")
REQUIRE_MESSAGE(ttv, "Expected a table but got " << toString(ty, {true}));
REQUIRE(ttv->props.count("new"));
Property& prop = ttv->props["new"];
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
REQUIRE(prop.readTy);
else
REQUIRE(prop.type_DEPRECATED());
const FunctionType* ftv =
(FFlag::LuauRemoveTypeCallsForReadWriteProps) ? get<FunctionType>(follow(*prop.readTy)) : get<FunctionType>(follow(prop.type_DEPRECATED()));
const FunctionType* ftv = get<FunctionType>(follow(*prop.readTy));
REQUIRE(ftv);
const TypePack* res = get<TypePack>(follow(ftv->retTypes));
REQUIRE(res);
@ -2427,7 +2359,6 @@ local t: { a: {Foo}, b: number } = {
// since mutating properties means table properties should be invariant.
TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound")
{
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
CheckResult result = check(R"(
--!strict
@ -3220,15 +3151,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_quantify_table_that_belongs_to_outer_sc
REQUIRE(counterType);
REQUIRE(counterType->props.count("new"));
const FunctionType* newType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& newProp = counterType->props["new"];
REQUIRE(newProp.readTy);
newType = get<FunctionType>(follow(*newProp.readTy));
}
else
newType = get<FunctionType>(follow(counterType->props["new"].type_DEPRECATED()));
const FunctionType* newType = get<FunctionType>(follow(*newProp.readTy));
REQUIRE(newType);
std::optional<TypeId> newRetType = *first(newType->retTypes);
@ -3713,7 +3638,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_leak_free_table_props")
TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
local t: { [string]: number } = { 5, 6, 7 }
)");
@ -3820,7 +3744,10 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap
TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type")
{
ScopedFastFlag _{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(
local function f(s)
@ -4346,10 +4273,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_shifted_tables")
TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauFixIndexerSubtypingOrdering, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauFixIndexerSubtypingOrdering, true};
CheckResult result = check(R"(
type Thing = { name: string, prop: boolean }
@ -4417,10 +4341,7 @@ TEST_CASE_FIXTURE(Fixture, "simple_method_definition")
TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
type T = {
@ -4514,7 +4435,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_write_property")
TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag sff2{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
type W = {read x: number}
@ -4585,7 +4505,6 @@ TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property")
TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
@ -4599,7 +4518,6 @@ TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property")
TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
@ -4615,7 +4533,6 @@ TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property"
TEST_CASE_FIXTURE(Fixture, "read_from_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
@ -4659,15 +4576,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_supported_with_the_new_solver"
end
)");
if (FFlag::LuauEnableWriteOnlyProperties)
LUAU_REQUIRE_NO_ERRORS(result);
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("write keyword is illegal here" == toString(result.errors[0]));
CHECK(Location{{1, 23}, {1, 28}} == result.errors[0].location);
}
}
TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported")
@ -4716,6 +4625,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties")
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
@ -5248,10 +5158,7 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager")
TEST_CASE_FIXTURE(BuiltinsFixture, "magic_functions_bidirectionally_inferred")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
local function getStuff(): (string, number, string)
@ -5355,13 +5262,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "write_only_table_field_duplicate")
}
)");
if (FFlag::LuauEnableWriteOnlyProperties)
LUAU_REQUIRE_NO_ERRORS(result);
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("write keyword is illegal here", toString(result.errors[0]));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert")
@ -5444,10 +5345,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_optional_in_table")
TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto result = check(R"(
local Numbers = { str = ( "" :: string ) }
@ -5466,7 +5364,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
LUAU_CHECK_NO_ERRORS(check(R"(
local t: { (() -> ())? } = {
@ -5520,10 +5418,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1543_optional_generic_param")
TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto result = check(R"(
type Book = { title: string, author: string }
@ -5550,10 +5445,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto result = check((R"(
local function getStatus(): string
@ -5615,7 +5507,7 @@ TEST_CASE_FIXTURE(Fixture, "deeply_nested_classish_inference")
TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto result = check(R"(
type File = {
@ -5724,10 +5616,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
@ -5744,10 +5633,8 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
local dict: { code1: boolean } = {
@ -5763,10 +5650,8 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check")
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
local d1: { code1: boolean } = {
@ -5783,10 +5668,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression")
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
@ -5805,13 +5687,8 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment")
TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
@ -5824,13 +5701,8 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables")
TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
@ -5841,13 +5713,8 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables")
TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
@ -5856,7 +5723,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
)");
LUAU_REQUIRE_ERROR_COUNT(3, result);
for (const auto& err : result.errors)
// Check that all of the errors are localized to `words`, not `otherWords`
// Check that all the errors are localized to `words`, not `otherWords`
CHECK(err.location.begin.line == 2);
}
@ -5865,10 +5732,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0))
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}";
LUAU_REQUIRE_NO_ERRORS(check(source));
@ -5888,10 +5752,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1838")
TEST_CASE_FIXTURE(Fixture, "oss_1859")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
@ -6049,9 +5910,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult results = check(R"(
@ -6114,10 +5975,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1888_and_or_subscriptable")
TEST_CASE_FIXTURE(Fixture, "cli_119126_regression")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult results = check(R"(
type literals = "foo" | "bar" | "foobar"

View file

@ -28,19 +28,16 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauSkipLvalueForCompoundAssignment)
LUAU_FASTFLAG(LuauMissingFollowInAssignIndexConstraint)
LUAU_FASTFLAG(LuauOccursCheckForRefinement)
LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauInferActualIfElseExprType)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauMissingFollowMappedGenericPacks)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
using namespace Luau;
@ -1218,7 +1215,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
if (FFlag::LuauSolverV2)
{
CHECK(4 == result.errors.size());
REQUIRE(4 == result.errors.size());
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
CHECK(Location{{3, 45}, {3, 46}} == result.errors[2].location);
@ -2021,6 +2018,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
auto result = check(R"(
@ -2056,6 +2054,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(
@ -2089,6 +2088,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
LUAU_REQUIRE_ERRORS(check(R"(
@ -2258,8 +2258,6 @@ end
TEST_CASE_FIXTURE(Fixture, "self_bound_due_to_compound_assign")
{
ScopedFastFlag _{FFlag::LuauSkipLvalueForCompoundAssignment, true};
loadDefinition(R"(
declare class Camera
CameraType: string
@ -2368,7 +2366,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "is_safe_integer_example")
TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
LUAU_REQUIRE_ERRORS(check(R"(
_ = if l0.n0.n0 then {n4(...,setmetatable(setmetatable(_),_)),_ == _,} elseif _.ceil._ then _ elseif _ then not _
@ -2388,8 +2389,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free")
TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_assign_index_constraint")
{
ScopedFastFlag _{FFlag::LuauMissingFollowInAssignIndexConstraint, true};
LUAU_REQUIRE_ERRORS(check(R"(
_._G = nil
for _ in ... do
@ -2421,7 +2420,6 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_infer_divergent_rw_props")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEnableWriteOnlyProperties, true},
{FFlag::LuauInferPolarityOfReadWriteProperties, true},
};
@ -2446,11 +2444,10 @@ TEST_CASE_FIXTURE(Fixture, "oss_1815_verbatim")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauInferActualIfElseExprType, true},
{FFlag::LuauInferActualIfElseExprType2, true},
// This is needed so that we don't hide the string literal free types
// behind a `union<_, _>`
{FFlag::LuauSimplifyOutOfLine2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult results = check(R"(
@ -2482,8 +2479,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_bidirectional_inference")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauInferActualIfElseExprType, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauInferActualIfElseExprType2, true},
};
CheckResult results = check(R"(
@ -2504,8 +2500,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_two_errors")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauInferActualIfElseExprType, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauInferActualIfElseExprType2, true},
};
CheckResult results = check(R"(

View file

@ -13,8 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
TEST_SUITE_BEGIN("TypePackTests");
@ -338,10 +336,7 @@ local c: Packed<string, number, boolean>
REQUIRE(ttvA->instantiatedTypeParams.size() == 1);
REQUIRE(ttvA->instantiatedTypePackParams.size() == 1);
CHECK_EQ(toString(ttvA->instantiatedTypeParams[0], {true}), "number");
if (FFlag::LuauFixEmptyTypePackStringification)
CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "()");
else
CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "");
auto ttvB = get<TableType>(requireType("b"));
REQUIRE(ttvB);
@ -1095,8 +1090,6 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments")
TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
function foo<T...>(...: T...): T...
return ...

View file

@ -5,7 +5,7 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
using namespace Luau;
@ -63,8 +63,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "assign_different_values_to_x")
TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
// Parameter `x` has a fresh type `'x` bounded by `never` and `unknown`.
// The first use of `x` constrains `x`'s upper bound by `string | number`.
// The second use of `x`, aliased by `y`, constrains `x`'s upper bound by `string?`.
@ -404,6 +402,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"(

View file

@ -11,7 +11,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("UnionTypes");
@ -583,8 +582,6 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all")
TEST_CASE_FIXTURE(Fixture, "error_detailed_optional")
{
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"(
type X = { x: number }

View file

@ -8,8 +8,8 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch);
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TypeInferUnknownNever");
@ -332,7 +332,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_i
{
ScopedFastFlag sffs[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauForceSimplifyConstraint2, true},
};
@ -360,7 +360,7 @@ TEST_CASE_FIXTURE(Fixture, "math_operators_and_never")
{
ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
};
CheckResult result = check(R"(

View file

@ -9,6 +9,11 @@
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 1">cl</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 2">dl</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 3">bl</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 4">spl</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 5">bpl</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 6">sil</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index == 7">dil</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::byte &amp;&amp; index >= 8">e{(int)index,d}b</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::dword &amp;&amp; index == 0">eax</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::dword &amp;&amp; index == 1">ecx</DisplayString>