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; std::vector<NotNull<Constraint>> dependencies;
// Clip with LuauUseOrderedTypeSetsInConstraints
DenseHashSet<TypeId> getMaybeMutatedFreeTypes_DEPRECATED() const;
TypeIds getMaybeMutatedFreeTypes() const; TypeIds getMaybeMutatedFreeTypes() const;
}; };

View file

@ -13,6 +13,7 @@
#include "Luau/Normalize.h" #include "Luau/Normalize.h"
#include "Luau/OrderedSet.h" #include "Luau/OrderedSet.h"
#include "Luau/Substitution.h" #include "Luau/Substitution.h"
#include "Luau/SubtypingVariance.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeCheckLimits.h" #include "Luau/TypeCheckLimits.h"
@ -41,6 +42,20 @@ struct HashBlockedConstraintId
size_t operator()(const BlockedConstraintId& bci) const; 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 ModuleResolver;
struct InstantiationSignature struct InstantiationSignature
@ -127,16 +142,14 @@ struct ConstraintSolver
// A mapping from free types to the number of unresolved constraints that mention them. // A mapping from free types to the number of unresolved constraints that mention them.
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}}; DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
// 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<NotNull<const Constraint>, TypeIds> maybeMutatedFreeTypes;
std::unordered_map<TypeId, OrderedSet<const Constraint*>> mutatedFreeTypeToConstraint; std::unordered_map<TypeId, OrderedSet<const Constraint*>> mutatedFreeTypeToConstraint;
// Irreducible/uninhabited type functions or type pack functions. // Irreducible/uninhabited type functions or type pack functions.
DenseHashSet<const void*> uninhabitedTypeFunctions{{}}; DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
DenseHashMap<SubtypeConstraintRecord, Constraint*, HashSubtypeConstraintRecord> seenConstraints{{}};
// The set of types that will definitely be unchanged by generalization. // The set of types that will definitely be unchanged by generalization.
DenseHashSet<TypeId> generalizedTypes_{nullptr}; DenseHashSet<TypeId> generalizedTypes_{nullptr};
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_}; 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; std::optional<DefId> lookup(DefId def, const std::string& key) const;
void inherit(const DfgScope* childScope); void inherit(const DfgScope* childScope);
bool canUpdateDefinition(Symbol symbol) const;
bool canUpdateDefinition(DefId def, const std::string& key) const;
}; };
struct DataFlowResult struct DataFlowResult
@ -133,7 +130,6 @@ private:
/// A stack of scopes used by the visitor to see where we are. /// A stack of scopes used by the visitor to see where we are.
ScopeStack scopeStack; ScopeStack scopeStack;
NotNull<DfgScope> currentScope(); NotNull<DfgScope> currentScope();
DfgScope* currentScope_DEPRECATED();
struct FunctionCapture struct FunctionCapture
{ {

View file

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

View file

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

View file

@ -4,6 +4,7 @@
#include "Luau/DenseHash.h" #include "Luau/DenseHash.h"
#include "Luau/EqSatSimplification.h" #include "Luau/EqSatSimplification.h"
#include "Luau/Set.h" #include "Luau/Set.h"
#include "Luau/SubtypingVariance.h"
#include "Luau/TypeCheckLimits.h" #include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFunction.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
@ -32,17 +33,6 @@ struct TableIndexer;
struct TypeArena; struct TypeArena;
struct TypeCheckLimits; 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 struct SubtypingReasoning
{ {
// The path, relative to the _root subtype_, where subtyping failed. // The path, relative to the _root subtype_, where subtyping failed.
@ -327,6 +317,12 @@ private:
[[noreturn]] void unexpected(TypeId ty); [[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp); [[noreturn]] void unexpected(TypePackId tp);
SubtypingResult trySemanticSubtyping(SubtypingEnvironment& env,
TypeId subTy,
TypeId superTy,
NotNull<Scope> scope,
SubtypingResult& original);
}; };
} // namespace Luau } // 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 = {} const PredicateVec& predicates = {}
); );
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt); WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr); WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr); WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt); WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);

View file

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

View file

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

View file

@ -33,9 +33,7 @@
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements)
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature) LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
LUAU_FASTFLAGVARIABLE(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAGVARIABLE(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
@ -339,14 +337,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
auto it = stringMetatableTable->props.find("__index"); auto it = stringMetatableTable->props.find("__index");
LUAU_ASSERT(it != stringMetatableTable->props.end()); LUAU_ASSERT(it != stringMetatableTable->props.end());
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
addGlobalBinding(globals, "string", *it->second.readTy, "@luau"); addGlobalBinding(globals, "string", *it->second.readTy, "@luau");
addGlobalBinding(globals, "string", *it->second.writeTy, "@luau"); addGlobalBinding(globals, "string", *it->second.writeTy, "@luau");
}
else
addGlobalBinding(globals, "string", it->second.type_DEPRECATED(), "@luau");
// Setup 'vector' metatable // Setup 'vector' metatable
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end()) 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["foreach"].deprecated = true;
ttv->props["foreachi"].deprecated = true; ttv->props["foreachi"].deprecated = true;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared<MagicPack>()); attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared<MagicPack>());
if (FFlag::LuauTableCloneClonesType3) if (FFlag::LuauTableCloneClonesType3)
attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared<MagicClone>()); attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared<MagicClone>());
attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared<MagicFreeze>()); 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"); TypeId requireTy = getGlobalBinding(globals, "require");
attachTag(requireTy, kRequireTagName); attachTag(requireTy, kRequireTagName);
@ -665,8 +647,6 @@ bool MagicFormat::infer(const MagicFunctionCallContext& context)
{ {
TypeArena* arena = context.solver->arena; TypeArena* arena = context.solver->arena;
if (FFlag::LuauStringFormatImprovements)
{
auto iter = begin(context.arguments); auto iter = begin(context.arguments);
// we'll suppress any errors for `string.format` if the format string is error suppressing. // 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); asMutable(context.result)->ty.emplace<BoundTypePack>(resultPack);
return true; 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) bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
{ {
if (FFlag::LuauStringFormatImprovements)
{
auto iter = begin(context.arguments); auto iter = begin(context.arguments);
if (iter == end(context.arguments)) if (iter == end(context.arguments))
@ -848,73 +784,6 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
} }
return true; 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) 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/UserDefinedTypeFunction.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauNotAllBinaryTypeFunsHaveDefaults)
LUAU_FASTFLAG(LuauEmptyStringInKeyOf) LUAU_FASTFLAG(LuauEmptyStringInKeyOf)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying) LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
LUAU_FASTFLAG(LuauOccursCheckForRefinement) LUAU_FASTFLAG(LuauOccursCheckForRefinement)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAGVARIABLE(LuauDoNotBlockOnStuckTypeFunctions)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways) LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways)
@ -682,6 +680,16 @@ TypeFunctionReductionResult<TypeId> concatTypeFunction(
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; 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( TypeFunctionReductionResult<TypeId> andTypeFunction(
TypeId instance, TypeId instance,
const std::vector<TypeId>& typeParams, 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 // check to see if both operand types are resolved enough, and wait to reduce if not
if (FFlag::LuauEagerGeneralization4) 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)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy)) else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
} }
}
else else
{ {
if (isPending(lhsTy, ctx->solver)) if (isPending(lhsTy, ctx->solver))
@ -794,12 +812,22 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
if (FFlag::LuauEagerGeneralization4) 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)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy)) else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
} }
}
else else
{ {
if (isPending(lhsTy, ctx->solver)) if (isPending(lhsTy, ctx->solver))
@ -1302,8 +1330,18 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
return {targetTy, {}}; return {targetTy, {}};
} }
const bool targetIsPending = FFlag::LuauEagerGeneralization4 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy) bool targetIsPending = false;
: isPending(targetTy, ctx->solver);
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 // check to see if both operand types are resolved enough, and wait to reduce if not
if (targetIsPending) if (targetIsPending)
@ -2133,20 +2171,15 @@ bool searchPropsAndIndexer(
if (tblProps.find(stringSingleton->value) != tblProps.end()) if (tblProps.find(stringSingleton->value) != tblProps.end())
{ {
TypeId propTy;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& prop = tblProps.at(stringSingleton->value); Property& prop = tblProps.at(stringSingleton->value);
TypeId propTy;
if (prop.readTy) if (prop.readTy)
propTy = follow(*prop.readTy); propTy = follow(*prop.readTy);
else if (prop.writeTy) else if (prop.writeTy)
propTy = follow(*prop.writeTy); propTy = follow(*prop.writeTy);
else // found the property, but there was no type associated with it else // found the property, but there was no type associated with it
return false; return false;
}
else
propTy = follow(tblProps.at(stringSingleton->value).type_DEPRECATED());
// property is a union type -> we need to extend our reduction type // property is a union type -> we need to extend our reduction type
if (auto propUnionTy = get<UnionType>(propTy)) 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[keyofFunc.name] = mkUnaryTypeFunction(&keyofFunc);
scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc); scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc);
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
{
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc); scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc);
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc); 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); scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
else
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc);
scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc); 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. // 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_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticClone)
namespace Luau namespace Luau
{ {
@ -202,8 +201,6 @@ public:
private: private:
Property shallowClone(const Property& p) Property shallowClone(const Property& p)
{
if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{ {
std::optional<TypeId> cloneReadTy; std::optional<TypeId> cloneReadTy;
if (auto ty = p.readTy) if (auto ty = p.readTy)
@ -222,19 +219,6 @@ private:
cloned.typeLocation = p.typeLocation; cloned.typeLocation = p.typeLocation;
return cloned; 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) 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 struct ReferenceCountInitializer : TypeOnceVisitor
{ {
NotNull<TypeIds> result; NotNull<TypeIds> result;
@ -140,110 +85,6 @@ bool isReferenceCountedType(const TypeId typ)
return get<FreeType>(typ) || get<BlockedType>(typ) || get<PendingExpansionType>(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 TypeIds Constraint::getMaybeMutatedFreeTypes() const
{ {
// For the purpose of this function and reference counting in general, we are only considering // 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(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType) LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType)
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType) LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType2)
LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex) LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) LUAU_FASTFLAGVARIABLE(LuauTrackFreeInteriorTypePacks)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
namespace Luau namespace Luau
{ {
@ -233,6 +228,11 @@ ConstraintGenerator::ConstraintGenerator(
, logger(logger) , logger(logger)
{ {
LUAU_ASSERT(module); LUAU_ASSERT(module);
if (FFlag::LuauEagerGeneralization4)
{
LUAU_ASSERT(FFlag::LuauTrackFreeInteriorTypePacks);
}
} }
ConstraintSet ConstraintGenerator::run(AstStatBlock* block) ConstraintSet ConstraintGenerator::run(AstStatBlock* block)
@ -261,7 +261,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
rootScope->location = block->location; rootScope->location = block->location;
module->astScopes[block] = NotNull{scope.get()}; module->astScopes[block] = NotNull{scope.get()};
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); 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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); 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 // We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block); prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
// Pre // Pre
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); DEPRECATED_interiorTypes.emplace_back();
visitBlockWithoutChildScope(resumeScope, block); visitBlockWithoutChildScope(resumeScope, block);
// Post // Post
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -390,9 +390,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
{ {
if (FFlag::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); interiorFreeTypes.back().types.push_back(ft);
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauEagerGeneralization4)
@ -412,7 +415,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
{ {
FreeTypePack f{scope.get(), polarity}; FreeTypePack f{scope.get(), polarity};
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.back().typePacks.push_back(result); interiorFreeTypes.back().typePacks.push_back(result);
return result; return result;
} }
@ -1036,6 +1039,11 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat)
{ {
RecursionLimiter limiter{"ConstraintGenerator", &recursionCount, FInt::LuauCheckRecursionLimit}; 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>()) if (auto s = stat->as<AstStatBlock>())
return visit(scope, s); return visit(scope, s);
else if (auto i = stat->as<AstStatIf>()) 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; TypeId resultTy = checkAstExprBinary(scope, assign->location, assign->op, assign->var, assign->value, std::nullopt).ty;
module->astCompoundAssignResultTypes[assign] = resultTy; module->astCompoundAssignResultTypes[assign] = resultTy;
// NOTE: We do not update lvalues for compound assignments. This is
if (!FFlag::LuauSkipLvalueForCompoundAssignment) // intentional.
{
TypeId lhsType = check(scope, assign->var).ty;
visitLValue(scope, assign->var, lhsType);
follow(lhsType);
follow(resultTy);
}
return ControlFlow::None; 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 // Place this function as a child of the non-type function scope
scope->children.push_back(NotNull{sig.signatureScope.get()}); scope->children.push_back(NotNull{sig.signatureScope.get()});
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); 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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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()); sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
getMutable<BlockedType>(generalizedTy)->setOwner(gc); getMutable<BlockedType>(generalizedTy)->setOwner(gc);
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); 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 // 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 // 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]; Luau::Property& prop = props[propName];
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
if (auto readTy = prop.readTy) if (auto readTy = prop.readTy)
{ {
// We special-case this logic to keep the intersection flat; otherwise we // 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; 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 // The intent is (probably) not for this to be an array-like table with a massive
// union for the value, but instead a `{ string }`. // union for the value, but instead a `{ string }`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0) if (largeTableDepth > 0)
return Inference{builtinTypes->stringType}; return Inference{builtinTypes->stringType};
TypeId freeTy = nullptr; 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 // 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`. // is potentially `true` or `false` as a singleton, but just `boolean`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0) if (largeTableDepth > 0)
return Inference{builtinTypes->booleanType}; return Inference{builtinTypes->booleanType};
TypeId freeTy = nullptr; TypeId freeTy = nullptr;
@ -2866,7 +2834,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
Checkpoint startCheckpoint = checkpoint(this); Checkpoint startCheckpoint = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); 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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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)); applyRefinements(elseScope, ifElse->falseExpr->location, refinementArena.negation(refinement));
TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty; TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty;
if (FFlag::LuauInferActualIfElseExprType) if (FFlag::LuauInferActualIfElseExprType2)
return Inference{makeUnion(scope, ifElse->location, thenType, elseType)}; return Inference{makeUnion(scope, ifElse->location, thenType, elseType)};
else else
return Inference{expectedType ? *expectedType : makeUnion(scope, ifElse->location, thenType, elseType)}; 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->definitionLocation = expr->location;
ttv->scope = scope.get(); ttv->scope = scope.get();
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth++; largeTableDepth++;
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauTrackFreeInteriorTypePacks)
interiorFreeTypes.back().types.push_back(ty); interiorFreeTypes.back().types.push_back(ty);
else else
DEPRECATED_interiorTypes.back().push_back(ty); DEPRECATED_interiorTypes.back().push_back(ty);
@ -3503,23 +3470,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
} }
} }
if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck2) if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
{
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))
largeTableDepth--; largeTableDepth--;
return Inference{ty}; return Inference{ty};
@ -3775,7 +3726,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
else else
return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments); 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{}); return arena->addType(BlockedType{});
} }
@ -3871,11 +3822,6 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
p.readTy = propTy; p.readTy = propTy;
break; break;
case AstTableAccess::Write: case AstTableAccess::Write:
if (!FFlag::LuauEnableWriteOnlyProperties)
{
reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"});
p.readTy = propTy;
}
p.writeTy = propTy; p.writeTy = propTy;
break; break;
default: default:
@ -4401,17 +4347,6 @@ struct GlobalPrepopulator : AstVisitor
void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program) void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program)
{ {
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 // Handle type function globals as well, without preparing a module scope since they have a separate environment
GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg}; GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg};
program->visit(&tfgp); program->visit(&tfgp);

View file

@ -35,22 +35,40 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint)
LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAGVARIABLE(LuauUseOrderedTypeSetsInConstraints)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying) LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2) LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2)
LUAU_FASTFLAGVARIABLE(LuauCollapseShouldNotCrash) LUAU_FASTFLAGVARIABLE(LuauCollapseShouldNotCrash)
LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking) LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking)
LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving) LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving3)
LUAU_FASTFLAGVARIABLE(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
namespace Luau 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); static void dump(ConstraintSolver* cs, ToStringOptions& opts);
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const 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. // Free types that have no constraints at all can be generalized right away.
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauEagerGeneralization4)
{
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{ {
for (TypeId ty : constraintSet.freeTypes) for (TypeId ty : constraintSet.freeTypes)
{ {
@ -435,16 +451,6 @@ void ConstraintSolver::run()
generalizeOneType(ty); 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(); constraintSet.freeTypes.clear();
@ -469,7 +475,7 @@ void ConstraintSolver::run()
// If we were _given_ a limit, and the current limit has hit zero, ] // If we were _given_ a limit, and the current limit has hit zero, ]
// then early exit from constraint solving. // then early exit from constraint solving.
if (FFlag::LuauLimitDynamicConstraintSolving && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0) if (FFlag::LuauLimitDynamicConstraintSolving3 && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0)
break; break;
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{}; std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
@ -492,8 +498,6 @@ void ConstraintSolver::run()
unblock(c); unblock(c);
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i)); unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
if (const auto maybeMutated = maybeMutatedFreeTypes.find(c); maybeMutated != maybeMutatedFreeTypes.end()) if (const auto maybeMutated = maybeMutatedFreeTypes.find(c); maybeMutated != maybeMutatedFreeTypes.end())
{ {
DenseHashSet<TypeId> seen{nullptr}; DenseHashSet<TypeId> seen{nullptr};
@ -527,44 +531,6 @@ void ConstraintSolver::run()
generalizeOneType(ty); 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) if (logger)
@ -731,8 +697,6 @@ struct TypeSearcher : TypeVisitor
void ConstraintSolver::initFreeTypeTracking() void ConstraintSolver::initFreeTypeTracking()
{ {
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
for (auto c : this->constraints) for (auto c : this->constraints)
{ {
unsolvedConstraints.emplace_back(c); unsolvedConstraints.emplace_back(c);
@ -756,34 +720,6 @@ void ConstraintSolver::initFreeTypeTracking()
block(dep, c); 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) void ConstraintSolver::generalizeOneType(TypeId ty)
@ -1838,9 +1774,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
DenseHashMap<TypeId, TypeId> replacements{nullptr}; DenseHashMap<TypeId, TypeId> replacements{nullptr};
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr}; DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
if (FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck)
{
DenseHashSet<const void*> genericTypesAndPacks{nullptr}; DenseHashSet<const void*> genericTypesAndPacks{nullptr};
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
@ -1966,98 +1899,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
inheritBlocks(constraint, addition); 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; return true;
} }
@ -2620,23 +2461,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
}; };
if (auto lhsFree = getMutable<FreeType>(lhsType)) if (auto lhsFree = getMutable<FreeType>(lhsType))
{
if (FFlag::LuauMissingFollowInAssignIndexConstraint)
{ {
if (auto lhsTable = getMutable<TableType>(follow(lhsFree->upperBound))) if (auto lhsTable = getMutable<TableType>(follow(lhsFree->upperBound)))
{ {
if (auto res = tableStuff(lhsTable)) if (auto res = tableStuff(lhsTable))
return *res; return *res;
} }
}
else
{
if (auto lhsTable = getMutable<TableType>(lhsFree->upperBound))
{
if (auto res = tableStuff(lhsTable))
return *res;
}
}
TypeId newUpperBound = TypeId newUpperBound =
arena->addType(TableType{/*props*/ {}, TableIndexer{indexType, rhsType}, TypeLevel{}, constraint->scope, TableState::Free}); arena->addType(TableType{/*props*/ {}, TableIndexer{indexType, rhsType}, TypeLevel{}, constraint->scope, TableState::Free});
@ -3400,17 +3230,12 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
return {{}, result.propType}; return {{}, result.propType};
// TODO: __index can be an overloaded function. // TODO: __index can be an overloaded function.
//
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// if the property is write-only, then surely we cannot read from it. // if the property is write-only, then surely we cannot read from it.
if (indexProp->second.isWriteOnly()) if (indexProp->second.isWriteOnly())
return {{}, builtinTypes->errorType}; return {{}, builtinTypes->errorType};
}
TypeId indexType = TypeId indexType = follow(*indexProp->second.readTy);
FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED());
if (auto ft = get<FunctionType>(indexType)) if (auto ft = get<FunctionType>(indexType))
{ {
@ -3448,17 +3273,12 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
if (indexProp == metatable->props.end()) if (indexProp == metatable->props.end())
return {{}, std::nullopt}; return {{}, std::nullopt};
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
// if the property is write-only, then surely we cannot read from it. // if the property is write-only, then surely we cannot read from it.
if (indexProp->second.isWriteOnly()) if (indexProp->second.isWriteOnly())
return {{}, builtinTypes->errorType}; return {{}, builtinTypes->errorType};
return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen); 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)) else if (auto ft = get<FreeType>(subjectType))
{ {
const TypeId upperBound = follow(ft->upperBound); const TypeId upperBound = follow(ft->upperBound);
@ -3826,7 +3646,7 @@ bool ConstraintSolver::isBlocked(TypeId ty) const
if (auto tfit = get<TypeFunctionInstanceType>(ty)) if (auto tfit = get<TypeFunctionInstanceType>(ty))
{ {
if (FFlag::LuauStuckTypeFunctionsStillDispatch && tfit->state != TypeFunctionInstanceState::Unsolved) if (FFlag::LuauEagerGeneralization4 && tfit->state != TypeFunctionInstanceState::Unsolved)
return false; return false;
return uninhabitedTypeFunctions.contains(ty) == 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) 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)); std::unique_ptr<Constraint> c = std::make_unique<Constraint>(scope, location, std::move(cv));
NotNull<Constraint> borrow = NotNull(c.get()); NotNull<Constraint> borrow = NotNull(c.get());
if (scr)
seenConstraints[*scr] = borrow;
solverConstraints.push_back(std::move(c)); solverConstraints.push_back(std::move(c));
unsolvedConstraints.emplace_back(borrow); unsolvedConstraints.emplace_back(borrow);
if (FFlag::LuauLimitDynamicConstraintSolving) if (FFlag::LuauLimitDynamicConstraintSolving3)
{ {
if (solverConstraintLimit > 0) if (solverConstraintLimit > 0)
{ {
@ -3962,9 +3801,6 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
if (FFlag::LuauEagerGeneralization4) if (FFlag::LuauEagerGeneralization4)
{ {
if (FFlag::LuauUseOrderedTypeSetsInConstraints)
{
if (auto it = mutatedFreeTypeToConstraint.find(source); it != mutatedFreeTypeToConstraint.end()) if (auto it = mutatedFreeTypeToConstraint.find(source); it != mutatedFreeTypeToConstraint.end())
{ {
const OrderedSet<const Constraint*>& constraintsAffectedBySource = it->second; 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) std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type)

View file

@ -13,7 +13,6 @@
LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauDfgForwardNilFromAndOr) 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) DataFlowGraphBuilder::DataFlowGraphBuilder(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena)
: graph{defArena, keyArena} : graph{defArena, keyArena}
, defArena{defArena} , defArena{defArena}
@ -185,14 +172,7 @@ DataFlowGraph DataFlowGraphBuilder::build(
DataFlowGraphBuilder builder(defArena, keyArena); DataFlowGraphBuilder builder(defArena, keyArena);
builder.handle = handle; builder.handle = handle;
DfgScope* moduleScope; DfgScope* moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get();
// 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();
PushScope ps{builder.scopeStack, moduleScope}; PushScope ps{builder.scopeStack, moduleScope};
builder.visitBlockWithoutChildScope(block); builder.visitBlockWithoutChildScope(block);
builder.resolveCaptures(); builder.resolveCaptures();
@ -230,19 +210,9 @@ NotNull<DfgScope> DataFlowGraphBuilder::currentScope()
return NotNull{scopeStack.back()}; 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) DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
{ {
if (FFlag::LuauDfgScopeStackNotNull)
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get(); 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) 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) 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. // true if any of the considered scopes are a loop.
bool outsideLoopScope = false; 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) 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) for (DfgScope* current = scope; current; current = current->parent)
{ {
if (auto props = current->props.find(def)) if (auto props = current->props.find(def))
@ -398,10 +368,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
cf = visitBlockWithoutChildScope(b); cf = visitBlockWithoutChildScope(b);
} }
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(child); currentScope()->inherit(child);
else
currentScope_DEPRECATED()->inherit(child);
return cf; return cf;
} }
@ -486,7 +453,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
elsecf = visit(i->elsebody); 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, // 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. // then we should assume that the _other_ branch is the one taken.
if (thencf != ControlFlow::None && elsecf == ControlFlow::None) if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
@ -615,10 +582,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
} }
} }
graph.localDefs[local] = def; graph.localDefs[local] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[local] = def; currentScope()->bindings[local] = def;
else
currentScope_DEPRECATED()->bindings[local] = def;
captures[local].allVersions.push_back(def); captures[local].allVersions.push_back(def);
} }
@ -675,10 +639,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
DefId def = defArena->freshCell(local, local->location); DefId def = defArena->freshCell(local, local->location);
graph.localDefs[local] = def; graph.localDefs[local] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[local] = def; currentScope()->bindings[local] = def;
else
currentScope_DEPRECATED()->bindings[local] = def;
captures[local].allVersions.push_back(def); captures[local].allVersions.push_back(def);
} }
@ -757,10 +718,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
{ {
DefId def = defArena->freshCell(l->name, l->location); DefId def = defArena->freshCell(l->name, l->location);
graph.localDefs[l->name] = def; graph.localDefs[l->name] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[l->name] = def; currentScope()->bindings[l->name] = def;
else
currentScope_DEPRECATED()->bindings[l->name] = def;
captures[l->name].allVersions.push_back(def); captures[l->name].allVersions.push_back(def);
visitExpr(l->func); visitExpr(l->func);
@ -793,10 +751,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
{ {
DefId def = defArena->freshCell(d->name, d->nameLocation); DefId def = defArena->freshCell(d->name, d->nameLocation);
graph.declaredDefs[d] = def; graph.declaredDefs[d] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[d->name] = def; currentScope()->bindings[d->name] = def;
else
currentScope_DEPRECATED()->bindings[d->name] = def;
captures[d->name].allVersions.push_back(def); captures[d->name].allVersions.push_back(def);
visitType(d->type); visitType(d->type);
@ -808,10 +763,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
{ {
DefId def = defArena->freshCell(d->name, d->nameLocation); DefId def = defArena->freshCell(d->name, d->nameLocation);
graph.declaredDefs[d] = def; graph.declaredDefs[d] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[d->name] = def; currentScope()->bindings[d->name] = def;
else
currentScope_DEPRECATED()->bindings[d->name] = def;
captures[d->name].allVersions.push_back(def); captures[d->name].allVersions.push_back(def);
DfgScope* unreachable = makeChildScope(); DfgScope* unreachable = makeChildScope();
@ -1058,10 +1010,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
{ {
DefId tableCell = defArena->freshCell(Symbol{}, t->location); DefId tableCell = defArena->freshCell(Symbol{}, t->location);
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->props[tableCell] = {}; currentScope()->props[tableCell] = {};
else
currentScope_DEPRECATED()->props[tableCell] = {};
for (AstExprTable::Item item : t->items) for (AstExprTable::Item item : t->items)
{ {
DataFlowResult result = visitExpr(item.value); DataFlowResult result = visitExpr(item.value);
@ -1070,10 +1019,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
visitExpr(item.key); visitExpr(item.key);
if (auto string = item.key->as<AstExprConstantString>()) if (auto string = item.key->as<AstExprConstantString>())
{ {
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->props[tableCell][string->value.data] = result.def; 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) 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. // 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)); DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
scope->bindings[l->local] = updated; scope->bindings[l->local] = updated;
@ -1184,33 +1130,22 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, 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)); DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef));
scope->bindings[g->name] = updated; scope->bindings[g->name] = updated;
captures[g->name].allVersions.push_back(updated); captures[g->name].allVersions.push_back(updated);
return updated; return updated;
}
else
return visitExpr(static_cast<AstExpr*>(g)).def;
} }
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef) DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef)
{ {
DefId parentDef = visitExpr(i->expr).def; DefId parentDef = visitExpr(i->expr).def;
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); DfgScope* scope = currentScope();
if (scope->canUpdateDefinition(parentDef, i->index.value))
{
DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef)); DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef));
scope->props[parentDef][i->index.value] = updated; scope->props[parentDef][i->index.value] = updated;
return updated; return updated;
}
else
return visitExpr(static_cast<AstExpr*>(i)).def;
} }
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef) DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
@ -1218,18 +1153,13 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
DefId parentDef = visitExpr(i->expr).def; DefId parentDef = visitExpr(i->expr).def;
visitExpr(i->index); visitExpr(i->index);
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); DfgScope* scope = currentScope();
if (auto string = i->index->as<AstExprConstantString>()) if (auto string = i->index->as<AstExprConstantString>())
{
if (scope->canUpdateDefinition(parentDef, string->value.data))
{ {
DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef)); DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef));
scope->props[parentDef][string->value.data] = updated; scope->props[parentDef][string->value.data] = updated;
return updated; return updated;
} }
else
return visitExpr(static_cast<AstExpr*>(i)).def;
}
else else
return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true); 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 // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h" #include "Luau/BuiltinDefinitions.h"
LUAU_FASTFLAG(LuauTypeFunOptional)
namespace Luau namespace Luau
{ {
@ -364,29 +362,6 @@ export type type = {
static constexpr const char* kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC( 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: { declare types: {
unknown: type, unknown: type,
never: type, never: type,
@ -415,9 +390,6 @@ std::string getTypeFunctionDefinitionSource()
std::string result = kBuiltinDefinitionTypeMethodSrc; std::string result = kBuiltinDefinitionTypeMethodSrc;
if (FFlag::LuauTypeFunOptional)
result += kBuiltinDefinitionTypesLibWithOptionalSrc;
else
result += kBuiltinDefinitionTypesLibSrc; result += kBuiltinDefinitionTypesLibSrc;
return result; return result;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,6 @@
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties) LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties)
namespace Luau namespace Luau
@ -51,8 +50,6 @@ struct InferPolarity : TypeVisitor
return false; return false;
const Polarity p = polarity; const Polarity p = polarity;
if (FFlag::LuauInferPolarityOfReadWriteProperties || FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
for (const auto& [name, prop] : tt.props) for (const auto& [name, prop] : tt.props)
{ {
if (prop.isShared()) if (prop.isShared())
@ -74,30 +71,6 @@ struct InferPolarity : TypeVisitor
traverse(*prop.writeTy); 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) if (tt.indexer)
{ {

View file

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

View file

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

View file

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

View file

@ -10,7 +10,6 @@
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
namespace Luau namespace Luau
@ -298,8 +297,6 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
// If any of the unsatisfied arguments are not supertypes of // If any of the unsatisfied arguments are not supertypes of
// nil or are `unknown`, then this overload does not match. // nil or are `unknown`, then this overload does not match.
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i) for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
{
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
{ {
if (get<UnknownType>(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype) 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)}}; 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, {}}; return {Analysis::Ok, {}};
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -34,8 +34,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
namespace Luau namespace Luau
{ {
@ -1912,7 +1910,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
else if (auto a = expr.as<AstExprUnary>()) else if (auto a = expr.as<AstExprUnary>())
result = checkExpr(scope, *a); result = checkExpr(scope, *a);
else if (auto a = expr.as<AstExprBinary>()) else if (auto a = expr.as<AstExprBinary>())
result = FFlag::LuauReduceCheckBinaryExprStackPressure ? checkExpr(scope, *a, expectedType) : checkExpr_DEPRECATED(scope, *a, expectedType); result = checkExpr(scope, *a, expectedType);
else if (auto a = expr.as<AstExprTypeAssertion>()) else if (auto a = expr.as<AstExprTypeAssertion>())
result = checkExpr(scope, *a); result = checkExpr(scope, *a);
else if (auto a = expr.as<AstExprError>()) else if (auto a = expr.as<AstExprError>())
@ -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) WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr)
{ {
TypeId annotationType = resolveType(scope, *expr.annotation); TypeId annotationType = resolveType(scope, *expr.annotation);

View file

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

View file

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

View file

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

View file

@ -130,7 +130,6 @@ private:
// function funcname funcbody // function funcname funcbody
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0}); 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); std::optional<AstAttr::Type> validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes);
// attribute ::= '@' NAME // 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() LUAU_NOINLINE Lexeme Lexer::readUtf8Error()
{ {
Position start = position(); 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. // flag so that we don't break production games by reverting syntax changes.
// See docs/SyntaxChanges.md for an explanation. // See docs/SyntaxChanges.md for an explanation.
LUAU_FASTFLAGVARIABLE(LuauSolverV2) LUAU_FASTFLAGVARIABLE(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit)
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
LUAU_FASTFLAGVARIABLE(LuauParseIncompleteInterpStringsWithLocation)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false; bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false;
@ -769,53 +768,8 @@ AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
return node; 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) std::optional<AstAttr::Type> Parser::validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes)
{ {
LUAU_ASSERT(FFlag::LuauParseAttributeFixUninit);
// check if the attribute name is valid // check if the attribute name is valid
std::optional<AstAttr::Type> type; std::optional<AstAttr::Type> type;
@ -855,8 +809,6 @@ void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
Location loc = lexer.current().location; Location loc = lexer.current().location;
if (FFlag::LuauParseAttributeFixUninit)
{
const char* name = lexer.current().name; const char* name = lexer.current().name;
std::optional<AstAttr::Type> type = validateAttribute(name, attributes); std::optional<AstAttr::Type> type = validateAttribute(name, attributes);
@ -864,17 +816,6 @@ void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
if (type) if (type)
attributes.push_back(allocator.alloc<AstAttr>(loc, *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} // 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. // There are two possibilities: Either it's a property or a function.
if (lexer.current().type == Lexeme::ReservedFunction) 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; Location classEnd = lexer.current().location;
nextLexeme(); // skip past `end` nextLexeme(); // skip past `end`
@ -1982,12 +1853,6 @@ std::pair<CstExprConstantString::QuoteStyle, unsigned int> Parser::extractString
// TableIndexer ::= `[' Type `]' `:' Type // TableIndexer ::= `[' Type `]' `:' Type
Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin) Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin)
{ {
if (!FFlag::LuauParseStringIndexer)
{
begin = lexer.current();
nextLexeme(); // [
}
AstType* index = parseType(); AstType* index = parseType();
Position indexerClosePosition = lexer.current().location.begin; Position indexerClosePosition = lexer.current().location.begin;
@ -2046,8 +1911,6 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
} }
} }
if (FFlag::LuauParseStringIndexer)
{
if (lexer.current().type == '[') if (lexer.current().type == '[')
{ {
const Lexeme begin = lexer.current(); const Lexeme begin = lexer.current();
@ -2153,113 +2016,6 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
lexer.current().location.begin 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 == ';') 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 '\\{'?"); return reportExprError(endLocation, {}, "Double braces are not permitted within interpolated strings; did you mean '\\{'?");
case Lexeme::BrokenString: case Lexeme::BrokenString:
nextLexeme(); nextLexeme();
if (!FFlag::LuauParseIncompleteInterpStringsWithLocation)
return reportExprError(endLocation, {}, "Malformed interpolated string; did you forget to add a '}'?"); 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: default:
return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str()); return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str());
} }

View file

@ -6,6 +6,8 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauCodeGenFixRexw, false)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -37,7 +39,10 @@ static_assert(sizeof(cmovTextForCondition) / sizeof(cmovTextForCondition[0]) ==
#define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc)) #define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc))
#define REX_W_BIT(value) (value ? 0x8 : 0x0) #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_R(reg) (((reg).index & 0x8) >> 1)
#define REX_X(reg) (((reg).index & 0x8) >> 2) #define REX_X(reg) (((reg).index & 0x8) >> 2)
#define REX_B(reg) (((reg).index & 0x8) >> 3) #define REX_B(reg) (((reg).index & 0x8) >> 3)
@ -1390,18 +1395,30 @@ void AssemblyBuilderX64::
void AssemblyBuilderX64::placeRex(RegisterX64 op) 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) if (code != 0)
place(code | 0x40); 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) void AssemblyBuilderX64::placeRex(OperandX64 op)
{ {
if (DFFlag::LuauCodeGenFixRexw)
{
uint8_t code = 0; uint8_t code = 0;
if (op.cat == CategoryX64::reg) 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) else if (op.cat == CategoryX64::mem)
code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base); code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base);
else else
@ -1409,6 +1426,21 @@ void AssemblyBuilderX64::placeRex(OperandX64 op)
if (code != 0) if (code != 0)
place(code | 0x40); 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) void AssemblyBuilderX64::placeRexNoW(OperandX64 op)
@ -1428,7 +1460,21 @@ void AssemblyBuilderX64::placeRexNoW(OperandX64 op)
void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs) 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) if (rhs.cat == CategoryX64::imm)
code |= REX_B(lhs); code |= REX_B(lhs);
@ -1437,6 +1483,7 @@ void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs)
if (code != 0) if (code != 0)
place(code | 0x40); place(code | 0x40);
}
} }
void AssemblyBuilderX64::placeVex(OperandX64 dst, OperandX64 src1, OperandX64 src2, bool setW, uint8_t mode, uint8_t prefix) 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(LuauSeparateCompilerTypeInfo)
LUAU_FASTFLAGVARIABLE(LuauCompileCli162537)
namespace Luau namespace Luau
{ {
@ -1928,6 +1929,10 @@ struct Compiler
CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile"); CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile");
LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength); LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength);
if (FFlag::LuauCompileCli162537)
shape.keys[shape.length++] = cid;
else
shape.keys[shape.length++] = int16_t(cid); 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) static int checkRegisteredModules(lua_State* L, const char* path)
{ {
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1); 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)) if (lua_isnil(L, -1))
{ {
lua_pop(L, 2); lua_pop(L, 2);
@ -243,6 +251,16 @@ int registerModuleImpl(lua_State* L)
if (pathView.empty() || pathView[0] != '@') if (pathView.empty() || pathView[0] != '@')
luaL_argerrorL(L, 1, "path must begin with '@'"); 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); luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
// (1) path, (2) result, (3) cache table // (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/Simplify.h
Analysis/include/Luau/Substitution.h Analysis/include/Luau/Substitution.h
Analysis/include/Luau/Subtyping.h Analysis/include/Luau/Subtyping.h
Analysis/include/Luau/SubtypingVariance.h
Analysis/include/Luau/Symbol.h Analysis/include/Luau/Symbol.h
Analysis/include/Luau/TableLiteralInference.h Analysis/include/Luau/TableLiteralInference.h
Analysis/include/Luau/ToDot.h Analysis/include/Luau/ToDot.h

View file

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

View file

@ -20,7 +20,6 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3) LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
@ -4560,8 +4559,6 @@ end
TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment") TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment")
{ {
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"( check(R"(
local function foobar(tbl: { tag: "left" | "right" }) local function foobar(tbl: { tag: "left" | "right" })
tbl.tag = "@1" tbl.tag = "@1"
@ -4575,8 +4572,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment")
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table") TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table")
{ {
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"( check(R"(
type Entry = { field: number, prop: string } type Entry = { field: number, prop: string }
local x : {Entry} = {} local x : {Entry} = {}
@ -4603,8 +4598,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table")
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion") TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion")
{ {
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
check(R"( check(R"(
type Entry = { field: number, prop: string } type Entry = { field: number, prop: string }
return ( { f@1, p@2 } :: Entry ) return ( { f@1, p@2 } :: Entry )
@ -4621,7 +4614,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr")
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
// Somewhat surprisingly, the old solver didn't cover this case. // Somewhat surprisingly, the old solver didn't cover this case.
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys3, true}, {FFlag::LuauImplicitTableIndexerKeys3, true},
}; };
@ -4648,7 +4640,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys3, 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") TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag _{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
};
check(R"( check(R"(
local function take(_: { choice: "left" | "right" }) end 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(LuauHeapDumpStringSizeOverhead)
LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_DYNAMIC_FASTFLAG(LuauErrorYield) LUAU_DYNAMIC_FASTFLAG(LuauErrorYield)
LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck) LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck)
LUAU_FASTFLAG(LuauCompileCli162537)
static lua_CompileOptions defaultOptions() static lua_CompileOptions defaultOptions()
{ {
@ -1210,16 +1209,11 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
lua_newtable(L); lua_newtable(L);
for (const auto& [name, prop] : t->props) for (const auto& [name, prop] : t->props)
{
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{ {
if (prop.readTy) if (prop.readTy)
populateRTTI(L, *prop.readTy); populateRTTI(L, *prop.readTy);
else if (prop.writeTy) else if (prop.writeTy)
populateRTTI(L, *prop.writeTy); populateRTTI(L, *prop.writeTy);
}
else
populateRTTI(L, prop.type_DEPRECATED());
lua_setfield(L, -2, name.c_str()); lua_setfield(L, -2, name.c_str());
} }
@ -3333,6 +3327,60 @@ TEST_CASE("HugeFunctionLoadFailure")
REQUIRE_EQ(largeAllocationToFail, expectedTotalLargeAllocations); 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") TEST_CASE("IrInstructionLimit")
{ {

View file

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

View file

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

View file

@ -27,17 +27,13 @@ using namespace Luau;
LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking) 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(LuauSolverAgnosticStringification)
LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule) LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment) LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) 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) 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"); 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 sffLuauFragmentAutocompleteTracksRValueRefinement{FFlag::LuauFragmentAutocompleteTracksRValueRefinements, true};
ScopedFastFlag sffLuauPopulateSelfTypesInFragment{FFlag::LuauPopulateSelfTypesInFragment, true}; ScopedFastFlag sffLuauPopulateSelfTypesInFragment{FFlag::LuauPopulateSelfTypesInFragment, true};
ScopedFastFlag luauParseIncompleteInterpStringsWithLocation{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
FragmentAutocompleteFixtureImpl() FragmentAutocompleteFixtureImpl()
: BaseType(true) : BaseType(true)
@ -703,6 +694,7 @@ end
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "partial_for_numeric_in_condition") TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "partial_for_numeric_in_condition")
{ {
ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true};
auto region = getAutocompleteRegion( auto region = getAutocompleteRegion(
R"( R"(
for c = 1,3 for c = 1,3
@ -710,7 +702,7 @@ for c = 1,3
Position{1, 11} Position{1, 11}
); );
CHECK_EQ(Location{{1, 0}, {1, 11}}, region.fragmentLocation); CHECK_EQ(Location{{1, 10}, {1, 11}}, region.fragmentLocation);
REQUIRE(region.parentBlock); REQUIRE(region.parentBlock);
CHECK(region.nearestStatement->as<AstStatFor>()); 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) // NOLINTEND(bugprone-unchecked-optional-access)
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -16,7 +16,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
namespace 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") 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}}; Frontend fe{&fileResolver, &configResolver, {false}};
fileResolver.source["Module/A"] = R"( fileResolver.source["Module/A"] = R"(

View file

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

View file

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

View file

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

View file

@ -15,10 +15,10 @@
#include "doctest.h" #include "doctest.h"
#include <iostream> #include <iostream>
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols) LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever) LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
LUAU_FASTFLAG(LuauNewNonStrictSuppressesDynamicRequireErrors) LUAU_FASTFLAG(LuauNewNonStrictSuppressesDynamicRequireErrors)
LUAU_FASTFLAG(LuauNewNonStrictReportsOneIndexedErrors)
using namespace Luau; using namespace Luau;
@ -125,6 +125,7 @@ declare os : {
} }
@checked declare function require(target : any) : any @checked declare function require(target : any) : any
@checked declare function getAllTheArgsWrong(one: string, two: number, three: boolean) : any
)BUILTIN_SRC"; )BUILTIN_SRC";
}; };
@ -629,8 +630,6 @@ optionalArgsAtTheEnd1("a", nil, 3)
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict") TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict")
{ {
ScopedFastFlag sff{FFlag::LuauNewNonStrictFixGenericTypePacks, true};
CheckResult result = checkNonStrict(R"( CheckResult result = checkNonStrict(R"(
--!nonstrict --!nonstrict
local test: <T...>(T...) -> () -- TypeError: Unknown type 'T' local test: <T...>(T...) -> () -- TypeError: Unknown type 'T'
@ -862,4 +861,22 @@ require("@self/NonExistent")
CHECK(req2 != nullptr); 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(); TEST_SUITE_END();

View file

@ -17,8 +17,8 @@ LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTINT(LuauTypeLengthLimit) LUAU_FASTINT(LuauTypeLengthLimit)
LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauParseStringIndexer)
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
extern bool luau_telemetry_parsed_return_type_variadic_with_type_suffix; 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") TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
{ {
ScopedFastFlag sff{FFlag::LuauParseIncompleteInterpStringsWithLocation, true};
auto columnOfEndBraceError = [this](const char* code) auto columnOfEndBraceError = [this](const char* code)
{ {
try 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_EQ(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{abcdefg`"));
CHECK_NE(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{a`"));
} }
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table") 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") TEST_CASE_FIXTURE(Fixture, "parsing_string_union_indexers")
{ {
ScopedFastFlag _{FFlag::LuauParseStringIndexer, true};
parse(R"(type foo = { ["bar" | "baz"]: number })"); 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(); TEST_SUITE_END();

View file

@ -568,6 +568,20 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule")
assertOutputContainsAll({"true"}); 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") TEST_CASE_FIXTURE(ReplWithPathFixture, "ProxyRequire")
{ {
luarequire_pushproxyrequire(L, requireConfigInit, createCliRequireContext(L)); luarequire_pushproxyrequire(L, requireConfigInit, createCliRequireContext(L));

View file

@ -25,7 +25,9 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauIceLess) LUAU_FASTFLAG(LuauIceLess)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauSimplifyAnyAndUnion) LUAU_FASTFLAG(LuauSimplifyAnyAndUnion)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
LUAU_FASTFLAG(LuauDontDynamicallyCreateRedundantSubtypeConstraints)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
struct LimitFixture : BuiltinsFixture 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. // These flags are required to surface the problem.
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauPushFunctionTypesInFunctionStatement, true}, {FFlag::LuauPushFunctionTypesInFunctionStatement, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
// And this flag is the one that fixes it. // And this flag is the one that fixes it.
{FFlag::LuauSimplifyAnyAndUnion, true}, {FFlag::LuauSimplifyAnyAndUnion, true},
@ -341,7 +345,7 @@ TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauLimitDynamicConstraintSolving, true}, {FFlag::LuauLimitDynamicConstraintSolving3, true},
}; };
constexpr const char* src = R"( 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(); TEST_SUITE_END();

View file

@ -17,6 +17,7 @@
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
using namespace Luau; 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") 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()); TypeId argTy = arena.freshType(getBuiltins(), moduleScope.get());
FreeType* freeArg = getMutable<FreeType>(argTy); FreeType* freeArg = getMutable<FreeType>(argTy);

View file

@ -14,8 +14,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauSolverAgnosticStringification) LUAU_FASTFLAG(LuauSolverAgnosticStringification)
TEST_SUITE_BEGIN("ToString"); 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") TEST_CASE_FIXTURE(Fixture, "the_empty_type_pack_should_be_parenthesized")
{ {
ScopedFastFlag sff{FFlag::LuauFixEmptyTypePackStringification, true};
TypePackVar emptyTypePack{TypePack{}}; TypePackVar emptyTypePack{TypePack{}};
CHECK_EQ(toString(&emptyTypePack), "()"); CHECK_EQ(toString(&emptyTypePack), "()");
@ -889,8 +885,6 @@ TEST_CASE_FIXTURE(Fixture, "tostring_unsee_ttv_if_array")
TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch") TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
function f1(t: {a : number, b: string, c: {d: string}}) : {a : number, b : string, c : { d : number}} 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_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauEmptyStringInKeyOf) LUAU_FASTFLAG(LuauEmptyStringInKeyOf)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauDoNotBlockOnStuckTypeFunctions)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
struct TypeFunctionFixture : Fixture struct TypeFunctionFixture : Fixture
{ {
@ -351,8 +352,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
type KeysOfMyObject = keyof<MyObject> type KeysOfMyObject = keyof<MyObject>
@ -374,8 +373,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local metatable = { __index = {w = 1} } local metatable = { __index = {w = 1} }
local obj = setmetatable({x = 1, y = 2, z = 3}, metatable) 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") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
@ -466,8 +461,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
type MyOtherObject = { w: 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
type KeysOfMyObject = rawkeyof<MyObject> type KeysOfMyObject = rawkeyof<MyObject>
@ -527,8 +518,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local metatable = { __index = {w = 1} } local metatable = { __index = {w = 1} }
local obj = setmetatable({x = 1, y = 2, z = 3}, metatable) 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
type MyOtherObject = { w: 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type KeysOfMyObject = keyof<BaseClass> type KeysOfMyObject = keyof<BaseClass>
@ -744,7 +729,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag sff[] = {{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauStuckTypeFunctionsStillDispatch, true}}; ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
local EnumVariants = { local EnumVariants = {
@ -1008,8 +996,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean} type MyObject = {a: string, b: number, c: boolean}
type IdxAType = index<MyObject, "a"> 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> type errType1 = index<MyObject, key>
)"); )");
if (FFlag::LuauErrorSuppressionTypeFunctionArgs)
{
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Unknown type 'key'"); 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") 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean} type MyObject = {a: string, b: number, c: boolean}
type RawAType = rawget<MyObject, "a"> 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); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Unknown type 'key'"); 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") 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag errorSuppressionTypeFunctionArgs{FFlag::LuauErrorSuppressionTypeFunctionArgs, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local Colours = { local Colours = {
Red = 1, Red = 1,
@ -1720,7 +1684,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fully_dispatch_type_function_that_is_paramet
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true}, {FFlag::LuauTrackFreeInteriorTypePacks, true}
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -1747,7 +1711,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "undefined_add_application")
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true}, {FFlag::LuauTrackFreeInteriorTypePacks, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -1806,8 +1770,9 @@ struct TFFixture
TypeCheckLimits limits; TypeCheckLimits limits;
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
const ScopedFastFlag sff[1] = { const ScopedFastFlag sff[2] = {
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
}; };
BuiltinTypeFunctions builtinTypeFunctions; 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") 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 a = arena->addType(GenericType{"A"});
TypeId b = arena->addType(GenericType{"B"}); 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") 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}}); 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); 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(); TEST_SUITE_END();

View file

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

View file

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

View file

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

View file

@ -14,7 +14,6 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
TEST_SUITE_BEGIN("TypeInferAnyError"); 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);
REQUIRE(ttv->props.count("prop")); REQUIRE(ttv->props.count("prop"));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(ttv->props["prop"].readTy); REQUIRE(ttv->props["prop"].readTy);
CHECK_EQ("any", toString(*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") 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(LuauSolverV2)
LUAU_FASTFLAG(LuauTableCloneClonesType3) LUAU_FASTFLAG(LuauTableCloneClonesType3)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauStringFormatImprovements)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling) LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads) LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
@ -713,19 +709,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash")
end end
)"); )");
if (FFlag::LuauSolverV2 && FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) if (FFlag::LuauSolverV2)
{ {
LUAU_REQUIRE_ERROR_COUNT(2, result); 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[0]));
CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[1])); 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 else
{ {
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
@ -1317,14 +1306,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change")
REQUIRE(mathTy); REQUIRE(mathTy);
TableType* ttv = getMutable<TableType>(mathTy); TableType* ttv = getMutable<TableType>(mathTy);
REQUIRE(ttv); REQUIRE(ttv);
const FunctionType* ftv;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(ttv->props["frexp"].readTy); REQUIRE(ttv->props["frexp"].readTy);
ftv = get<FunctionType>(*ttv->props["frexp"].readTy); const FunctionType* ftv = get<FunctionType>(*ttv->props["frexp"].readTy);
}
else
ftv = get<FunctionType>(ttv->props["frexp"].type_DEPRECATED());
REQUIRE(ftv); REQUIRE(ftv);
auto original = ftv->level; 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") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any_2")
{ {
ScopedFastFlag _{FFlag::LuauSolverV2, true}; ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local fmt = "Hello, %s!" :: any 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") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types")
{ {
ScopedFastFlag _{FFlag::LuauSolverV2, true}; ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local fmt: "Hello, %s!" = "Hello, %s!" 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") TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_string_is_dynamic")
{ {
ScopedFastFlag _{FFlag::LuauSolverV2, true}; ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local fmt: string = "Hello, %s!" local fmt: string = "Hello, %s!"
@ -1733,7 +1715,6 @@ TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauEnableWriteOnlyProperties, true},
{FFlag::LuauWriteOnlyPropertyMangling, true}, {FFlag::LuauWriteOnlyPropertyMangling, true},
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
}; };

View file

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

View file

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

View file

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

View file

@ -10,11 +10,9 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("IntersectionTypes"); 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") TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true};
{FFlag::LuauPushFunctionTypesInFunctionStatement, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type X = { x: (number) -> number } 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") TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type X = { x: number } type X = { x: number }
type Y = { y: number } type Y = { y: number }

View file

@ -15,7 +15,8 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3)
LUAU_FASTINT(LuauSolverConstraintLimit)
using namespace Luau; using namespace Luau;
@ -854,7 +855,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "internal_types_are_scrubbed_from_module")
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::DebugLuauMagicTypes, true}, {FFlag::DebugLuauMagicTypes, true},
{FFlag::LuauLimitDynamicConstraintSolving, true} {FFlag::LuauLimitDynamicConstraintSolving3, true}
}; };
fileResolver.source["game/A"] = R"( 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)); 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(); TEST_SUITE_END();

View file

@ -10,6 +10,7 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
namespace 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") 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"( CheckResult result = check(R"(
function f(e) function f(e)

View file

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

View file

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

View file

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

View file

@ -24,23 +24,17 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit) LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
LUAU_FASTFLAG(LuauAutocompleteMissingFollows)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint) LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauSolverAgnosticStringification) LUAU_FASTFLAG(LuauSolverAgnosticStringification)
LUAU_FASTFLAG(LuauDfgForwardNilFromAndOr) LUAU_FASTFLAG(LuauDfgForwardNilFromAndOr)
LUAU_FASTFLAG(LuauInferActualIfElseExprType) LUAU_FASTFLAG(LuauInferActualIfElseExprType2)
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex) LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize) LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TableTests"); TEST_SUITE_BEGIN("TableTests");
@ -90,33 +84,18 @@ TEST_CASE_FIXTURE(Fixture, "basic")
std::optional<Property> fooProp = get(tType->props, "foo"); std::optional<Property> fooProp = get(tType->props, "foo");
REQUIRE(bool(fooProp)); REQUIRE(bool(fooProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy); REQUIRE(fooProp->readTy);
CHECK_EQ(PrimitiveType::String, getPrimitiveType(*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"); std::optional<Property> bazProp = get(tType->props, "baz");
REQUIRE(bool(bazProp)); REQUIRE(bool(bazProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(bazProp->readTy); REQUIRE(bazProp->readTy);
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*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"); std::optional<Property> quuxProp = get(tType->props, "quux");
REQUIRE(bool(quuxProp)); REQUIRE(bool(quuxProp));
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(quuxProp->readTy); REQUIRE(quuxProp->readTy);
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy)); CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy));
}
else
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type_DEPRECATED()));
} }
TEST_CASE_FIXTURE(Fixture, "augment_table") 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()); REQUIRE(tType->props.find("p") != tType->props.end());
const TableType* pType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& p = tType->props["p"]; Property& p = tType->props["p"];
REQUIRE(p.readTy); REQUIRE(p.readTy);
pType = get<TableType>(p.readTy); const TableType* pType = get<TableType>(p.readTy);
}
else
pType = get<TableType>(tType->props["p"].type_DEPRECATED());
REQUIRE(pType != nullptr); REQUIRE(pType != nullptr);
CHECK("{ p: { foo: string } }" == toString(requireType("t"), {true})); 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"); std::optional<Property> fooProp = get(tableType->props, "foo");
REQUIRE(bool(fooProp)); REQUIRE(bool(fooProp));
const FunctionType* methodType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy); REQUIRE(fooProp->readTy);
methodType = get<FunctionType>(follow(fooProp->readTy)); const FunctionType* methodType = get<FunctionType>(follow(fooProp->readTy));
}
else
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
REQUIRE(methodType != nullptr); REQUIRE(methodType != nullptr);
} }
@ -320,14 +287,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
std::optional<Property> uProp = get(tableType->props, "U"); std::optional<Property> uProp = get(tableType->props, "U");
REQUIRE(bool(uProp)); REQUIRE(bool(uProp));
TypeId uType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(uProp->readTy); REQUIRE(uProp->readTy);
uType = *uProp->readTy; TypeId uType = *uProp->readTy;
}
else
uType = uProp->type_DEPRECATED();
const TableType* uTable = get<TableType>(uType); const TableType* uTable = get<TableType>(uType);
REQUIRE(uTable != nullptr); REQUIRE(uTable != nullptr);
@ -335,14 +296,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
std::optional<Property> fooProp = get(uTable->props, "foo"); std::optional<Property> fooProp = get(uTable->props, "foo");
REQUIRE(bool(fooProp)); REQUIRE(bool(fooProp));
const FunctionType* methodType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
REQUIRE(fooProp->readTy); REQUIRE(fooProp->readTy);
methodType = get<FunctionType>(follow(fooProp->readTy)); const FunctionType* methodType = get<FunctionType>(follow(fooProp->readTy));
}
else
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
REQUIRE(methodType != nullptr); REQUIRE(methodType != nullptr);
std::vector<TypeId> methodArgs = flatten(methodType->argTypes).first; 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") TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(a: {number}): {string} function f(a: {number}): {string}
return a 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(tableType->indexer == std::nullopt);
REQUIRE(0 != tableType->props.count("a")); REQUIRE(0 != tableType->props.count("a"));
TypeId propertyA;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& a = tableType->props["a"]; Property& a = tableType->props["a"];
REQUIRE(a.readTy); REQUIRE(a.readTy);
propertyA = *a.readTy; TypeId propertyA = *a.readTy;
}
else
propertyA = tableType->props["a"].type_DEPRECATED();
REQUIRE(propertyA != nullptr); REQUIRE(propertyA != nullptr);
CHECK_EQ(*getBuiltins()->stringType, *propertyA); 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") TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type StringToStringMap = { [string]: string } type StringToStringMap = { [string]: string }
local rt: StringToStringMap = { ["foo"] = 1 } 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") TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(vec1: {x: number}): {x: number, y: number, z: number} function f(vec1: {x: number}): {x: number, y: number, z: number}
return vec1 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") TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors2")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MixedTable = {[number]: number, x: number} type MixedTable = {[number]: number, x: number}
local t: MixedTable = {"fail"} 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") TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
auto result = check(R"( auto result = check(R"(
local function _(t: { [string]: number? }): number 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_MESSAGE(ttv, "Expected a table but got " << toString(ty, {true}));
REQUIRE(ttv->props.count("new")); REQUIRE(ttv->props.count("new"));
Property& prop = ttv->props["new"]; Property& prop = ttv->props["new"];
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
REQUIRE(prop.readTy); REQUIRE(prop.readTy);
else const FunctionType* ftv = get<FunctionType>(follow(*prop.readTy));
REQUIRE(prop.type_DEPRECATED());
const FunctionType* ftv =
(FFlag::LuauRemoveTypeCallsForReadWriteProps) ? get<FunctionType>(follow(*prop.readTy)) : get<FunctionType>(follow(prop.type_DEPRECATED()));
REQUIRE(ftv); REQUIRE(ftv);
const TypePack* res = get<TypePack>(follow(ftv->retTypes)); const TypePack* res = get<TypePack>(follow(ftv->retTypes));
REQUIRE(res); REQUIRE(res);
@ -2427,7 +2359,6 @@ local t: { a: {Foo}, b: number } = {
// since mutating properties means table properties should be invariant. // since mutating properties means table properties should be invariant.
TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound") TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound")
{ {
ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -3220,15 +3151,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_quantify_table_that_belongs_to_outer_sc
REQUIRE(counterType); REQUIRE(counterType);
REQUIRE(counterType->props.count("new")); REQUIRE(counterType->props.count("new"));
const FunctionType* newType;
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
{
Property& newProp = counterType->props["new"]; Property& newProp = counterType->props["new"];
REQUIRE(newProp.readTy); REQUIRE(newProp.readTy);
newType = get<FunctionType>(follow(*newProp.readTy)); const FunctionType* newType = get<FunctionType>(follow(*newProp.readTy));
}
else
newType = get<FunctionType>(follow(counterType->props["new"].type_DEPRECATED()));
REQUIRE(newType); REQUIRE(newType);
std::optional<TypeId> newRetType = *first(newType->retTypes); 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") TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local t: { [string]: number } = { 5, 6, 7 } 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") 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"( CheckResult result = check(R"(
local function f(s) 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") TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauFixIndexerSubtypingOrdering, true};
{FFlag::LuauFixIndexerSubtypingOrdering, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type Thing = { name: string, prop: boolean } 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") TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type T = { type T = {
@ -4514,7 +4435,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_write_property")
TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties") TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag sff2{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type W = {read x: number} 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") TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(t: {write x: number}) 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") TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(t: {write x: number}) 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") TEST_CASE_FIXTURE(Fixture, "read_from_write_only_property")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(t: {write x: number}) function f(t: {write x: number})
@ -4659,15 +4576,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_supported_with_the_new_solver"
end end
)"); )");
if (FFlag::LuauEnableWriteOnlyProperties)
LUAU_REQUIRE_NO_ERRORS(result); 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") 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[] = { ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
}; };
CheckResult result = check(R"( 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") TEST_CASE_FIXTURE(BuiltinsFixture, "magic_functions_bidirectionally_inferred")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
local function getStuff(): (string, number, string) 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); 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") 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") TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
auto result = check(R"( auto result = check(R"(
local Numbers = { str = ( "" :: string ) } 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") 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"( LUAU_CHECK_NO_ERRORS(check(R"(
local t: { (() -> ())? } = { local t: { (() -> ())? } = {
@ -5520,10 +5418,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1543_optional_generic_param")
TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference") TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
auto result = check(R"( auto result = check(R"(
type Book = { title: string, author: string } 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") TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
auto result = check((R"( auto result = check((R"(
local function getStatus(): string 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") 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"( auto result = check(R"(
type File = { type File = {
@ -5724,10 +5616,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict") TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -5744,10 +5633,8 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
local dict: { code1: boolean } = { 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") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
local d1: { code1: boolean } = { 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") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -5805,13 +5687,8 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment")
TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables") TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true}, ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"( CheckResult result = check(R"(
type Word = "foo" | "bar" 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") TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true}, ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"( CheckResult result = check(R"(
type Word = "foo" | "bar" 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") TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true}, ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2};
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"( CheckResult result = check(R"(
type Word = "foo" | "bar" type Word = "foo" | "bar"
@ -5856,7 +5723,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
)"); )");
LUAU_REQUIRE_ERROR_COUNT(3, result); LUAU_REQUIRE_ERROR_COUNT(3, result);
for (const auto& err : result.errors) 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); 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)) TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0))
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag _{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}"; const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}";
LUAU_REQUIRE_NO_ERRORS(check(source)); LUAU_REQUIRE_NO_ERRORS(check(source));
@ -5888,10 +5752,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1838")
TEST_CASE_FIXTURE(Fixture, "oss_1859") TEST_CASE_FIXTURE(Fixture, "oss_1859")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -6049,9 +5910,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true}
}; };
CheckResult results = check(R"( CheckResult results = check(R"(
@ -6114,10 +5975,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1888_and_or_subscriptable")
TEST_CASE_FIXTURE(Fixture, "cli_119126_regression") TEST_CASE_FIXTURE(Fixture, "cli_119126_regression")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sff{FFlag::LuauSolverV2, true};
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
};
CheckResult results = check(R"( CheckResult results = check(R"(
type literals = "foo" | "bar" | "foobar" type literals = "foo" | "bar" | "foobar"

View file

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

View file

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

View file

@ -5,7 +5,7 @@
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
using namespace Luau; 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") 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`. // 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 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?`. // 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[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauTrackFreeInteriorTypePacks, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(

View file

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

View file

@ -8,8 +8,8 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauEagerGeneralization4); LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch);
LUAU_FASTFLAG(LuauForceSimplifyConstraint2) LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks)
TEST_SUITE_BEGIN("TypeInferUnknownNever"); 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[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true}, {FFlag::LuauTrackFreeInteriorTypePacks, true},
{FFlag::LuauForceSimplifyConstraint2, true}, {FFlag::LuauForceSimplifyConstraint2, true},
}; };
@ -360,7 +360,7 @@ TEST_CASE_FIXTURE(Fixture, "math_operators_and_never")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true}, {FFlag::LuauTrackFreeInteriorTypePacks, true}
}; };
CheckResult result = check(R"( 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 == 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 == 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 == 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 == 0">eax</DisplayString>
<DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::dword &amp;&amp; index == 1">ecx</DisplayString> <DisplayString Condition="size == Luau::CodeGen::X64::SizeX64::dword &amp;&amp; index == 1">ecx</DisplayString>