Sync to upstream/release/682 (#1912)

# What's changed?

Another somewhat quiet week! Don't let the large PR fool you, this is
mostly ...

## New Solver
* The code for type functions has been re-organized: instead of
_everything_ living in `TypeFunction.h` and `TypeFunction.cpp`, we now
have separate files for the type function inference machinery
(`TypeFunction.h`), definitions of built-in type functions
(`BuiltinTypeFunctions.h`), and the implementation of user defined type
functions (`UserDefinedTypeFunction.h`).
* Refinements against `*no-refine*`, a sentinel type indicating that no
refinements should occur, are now _always_ resolved, even if the target
of the refinement would be otherwise pending, such as another type
function.

## Autocomplete
* Fixed autocomplete to prefer table property completion to string
singleton completion. In the below example, the types associated with
each member of `foo` will be displayed in autocomplete popups.
```
local foo = {
    ["Item/Foo"] = 42,
    ["Item/Bar"] = "it's true",
    ["Item/Baz"] = true,
}
foo["|"] -- cursor at `|`
```

## Native Codegen
* Fixed native compilation lowering of the new global lookup
instruction, which caused code generation to fail with an error or to
evaluate incorrect results. Issue affected 678-681 releases when all
flags were enabled.

---

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: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
Hunter Goldstein 2025-07-11 11:36:47 -07:00 committed by GitHub
parent 60cd88af32
commit 6ff0650a8d
Signed by: DevComp
GPG key ID: B5690EEEBB952194
54 changed files with 3794 additions and 3941 deletions

View file

@ -0,0 +1,56 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeFunction.h"
namespace Luau
{
struct BuiltinTypeFunctions
{
BuiltinTypeFunctions();
TypeFunction userFunc;
TypeFunction notFunc;
TypeFunction lenFunc;
TypeFunction unmFunc;
TypeFunction addFunc;
TypeFunction subFunc;
TypeFunction mulFunc;
TypeFunction divFunc;
TypeFunction idivFunc;
TypeFunction powFunc;
TypeFunction modFunc;
TypeFunction concatFunc;
TypeFunction andFunc;
TypeFunction orFunc;
TypeFunction ltFunc;
TypeFunction leFunc;
TypeFunction eqFunc;
TypeFunction refineFunc;
TypeFunction singletonFunc;
TypeFunction unionFunc;
TypeFunction intersectFunc;
TypeFunction keyofFunc;
TypeFunction rawkeyofFunc;
TypeFunction indexFunc;
TypeFunction rawgetFunc;
TypeFunction setmetatableFunc;
TypeFunction getmetatableFunc;
TypeFunction weakoptionalFunc;
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
};
const BuiltinTypeFunctions& builtinTypeFunctions();
}

View file

@ -25,43 +25,7 @@ struct TypeFunctionRuntimeBuilderState;
struct TypeFunctionContext;
class Normalizer;
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
struct TypeFunctionRuntime
{
TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits);
~TypeFunctionRuntime();
// Return value is an error message if registration failed
std::optional<std::string> registerFunction(AstStatTypeFunction* function);
// For user-defined type functions, we store all generated types and packs for the duration of the typecheck
TypedAllocator<TypeFunctionType> typeArena;
TypedAllocator<TypeFunctionTypePackVar> typePackArena;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
StateRef state;
// Set of functions which have their environment table initialized
DenseHashSet<AstStatTypeFunction*> initialized{nullptr};
// Evaluation of type functions should only be performed in the absence of parse errors in the source module
bool allowEvaluation = true;
// Root scope in which the type function operates in, set up by ConstraintGenerator
ScopePtr rootScope;
// Output created by 'print' function
std::vector<std::string> messages;
// Type builder, valid for the duration of a single evaluation
TypeFunctionRuntimeBuilderState* runtimeBuilder = nullptr;
private:
void prepareState();
};
struct TypeFunctionRuntime;
struct TypeFunctionContext
{
@ -203,7 +167,7 @@ struct FunctionGraphReductionResult
* @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs
*/
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext, bool force = false);
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, NotNull<TypeFunctionContext> ctx, bool force = false);
/**
* Attempt to reduce all instances of any type or type pack functions in the type
@ -217,53 +181,13 @@ FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location loc
* @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs
*/
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext, bool force = false);
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, NotNull<TypeFunctionContext> ctx, bool force = false);
struct BuiltinTypeFunctions
{
BuiltinTypeFunctions();
TypeFunction userFunc;
TypeFunction notFunc;
TypeFunction lenFunc;
TypeFunction unmFunc;
TypeFunction addFunc;
TypeFunction subFunc;
TypeFunction mulFunc;
TypeFunction divFunc;
TypeFunction idivFunc;
TypeFunction powFunc;
TypeFunction modFunc;
TypeFunction concatFunc;
TypeFunction andFunc;
TypeFunction orFunc;
TypeFunction ltFunc;
TypeFunction leFunc;
TypeFunction eqFunc;
TypeFunction refineFunc;
TypeFunction singletonFunc;
TypeFunction unionFunc;
TypeFunction intersectFunc;
TypeFunction keyofFunc;
TypeFunction rawkeyofFunc;
TypeFunction indexFunc;
TypeFunction rawgetFunc;
TypeFunction setmetatableFunc;
TypeFunction getmetatableFunc;
TypeFunction weakoptionalFunc;
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
};
const BuiltinTypeFunctions& builtinTypeFunctions();
/* Returns true if the type provided should block a type function from reducing.
*
* Most type functions cannot dispatch if one of their operands is a
* BlockedType, a PendingExpansionType, or an unsolved TypeFunctionInstanceType.
*/
bool isPending(TypeId ty, ConstraintSolver* solver);
} // namespace Luau

View file

@ -2,8 +2,10 @@
#pragma once
#include "Luau/Common.h"
#include "Luau/Scope.h"
#include "Luau/TypeFunctionRuntimeBuilder.h"
#include "Luau/Type.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"
#include <optional>
#include <string>
@ -15,17 +17,22 @@ using lua_State = struct lua_State;
namespace Luau
{
struct TypeFunctionRuntime;
struct InternalErrorReporter;
struct TypeCheckLimits;
struct TypeFunctionRuntimeBuilderState;
struct LuauTempThreadPopper
{
explicit LuauTempThreadPopper(lua_State* L);
~LuauTempThreadPopper();
lua_State* L = nullptr;
};
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
// Replica of types from Type.h
struct TypeFunctionType;
using TypeFunctionTypeId = const TypeFunctionType*;
struct TypeFunctionTypePackVar;
using TypeFunctionTypePackId = const TypeFunctionTypePackVar*;
struct TypeFunctionPrimitiveType
{
enum Type
@ -274,6 +281,42 @@ T* getMutable(TypeFunctionTypeId tv)
return tv ? Luau::get_if<T>(&const_cast<TypeFunctionType*>(tv)->type) : nullptr;
}
struct TypeFunctionRuntime
{
TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits);
~TypeFunctionRuntime();
// Return value is an error message if registration failed
std::optional<std::string> registerFunction(AstStatTypeFunction* function);
// For user-defined type functions, we store all generated types and packs for the duration of the typecheck
TypedAllocator<TypeFunctionType> typeArena;
TypedAllocator<TypeFunctionTypePackVar> typePackArena;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
StateRef state;
// Set of functions which have their environment table initialized
DenseHashSet<AstStatTypeFunction*> initialized{nullptr};
// Evaluation of type functions should only be performed in the absence of parse errors in the source module
bool allowEvaluation = true;
// Root scope in which the type function operates in, set up by ConstraintGenerator
ScopePtr rootScope;
// Output created by 'print' function
std::vector<std::string> messages;
// Type builder, valid for the duration of a single evaluation
TypeFunctionRuntimeBuilderState* runtimeBuilder = nullptr;
private:
void prepareState();
};
std::optional<std::string> checkResultForError(lua_State* L, const char* typeFunctionName, int luaResult);
TypeFunctionRuntime* getTypeFunctionRuntime(lua_State* L);

View file

@ -1,20 +1,12 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Type.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFunctionRuntime.h"
namespace Luau
{
using Kind = Variant<TypeId, TypePackId>;
template<typename T>
const T* get(const Kind& kind)
{
return get_if<T>(&kind);
}
struct TypeFunctionContext;
using TypeFunctionKind = Variant<TypeFunctionTypeId, TypeFunctionTypePackId>;

View file

@ -56,4 +56,10 @@ struct BuiltinTypes;
using TypeOrPack = Variant<TypeId, TypePackId>;
struct TypeFunctionType;
using TypeFunctionTypeId = const TypeFunctionType*;
struct TypeFunctionTypePackVar;
using TypeFunctionTypePackId = const TypeFunctionTypePackVar*;
} // namespace Luau

View file

@ -0,0 +1,17 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
TypeId instance,
const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& packParams,
NotNull<TypeFunctionContext> ctx
);
}

View file

@ -26,7 +26,7 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
static const std::unordered_set<std::string> kStatementStartingKeywords =
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
@ -578,6 +578,29 @@ static void autocompleteStringSingleton(TypeId ty, bool addQuotes, AstNode* node
ty = follow(ty);
if (FFlag::LuauImplicitTableIndexerKeys3)
{
if (auto ss = get<StringSingleton>(get<SingletonType>(ty)))
{
// This is purposefully `try_emplace` as we don't want to override any existing entries.
result.try_emplace(formatKey(ss->value), AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct});
}
else if (auto uty = get<UnionType>(ty))
{
for (auto el : uty)
{
if (auto ss = get<StringSingleton>(get<SingletonType>(el)))
{
// This is purposefully `try_emplace` as we don't want to override any existing entries.
result.try_emplace(
formatKey(ss->value), AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct}
);
}
}
}
}
else
{
if (auto ss = get<StringSingleton>(get<SingletonType>(ty)))
{
result[formatKey(ss->value)] = AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct};
@ -590,6 +613,7 @@ static void autocompleteStringSingleton(TypeId ty, bool addQuotes, AstNode* node
result[formatKey(ss->value)] = AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct};
}
}
}
};
static bool canSuggestInferredType(ScopePtr scope, TypeId ty)
@ -2030,7 +2054,7 @@ AutocompleteResult autocomplete_(
{
AutocompleteEntryMap result;
if (!FFlag::LuauImplicitTableIndexerKeys2)
if (!FFlag::LuauImplicitTableIndexerKeys3)
{
if (auto it = module->astExpectedTypes.find(node->asExpr()))
autocompleteStringSingleton(*it, false, node, position, result);
@ -2053,7 +2077,7 @@ AutocompleteResult autocomplete_(
}
}
if (FFlag::LuauImplicitTableIndexerKeys2)
if (FFlag::LuauImplicitTableIndexerKeys3)
{
if (auto it = module->astExpectedTypes.find(node->asExpr()))
autocompleteStringSingleton(*it, false, node, position, result);

View file

@ -2,6 +2,7 @@
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Ast.h"
#include "Luau/BuiltinTypeFunctions.h"
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/ConstraintGenerator.h"
@ -35,7 +36,6 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements)
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
LUAU_FASTFLAGVARIABLE(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
@ -1781,8 +1781,6 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
if (FFlag::LuauMagicFreezeCheckBlocked2)
{
if (resultTy && !get<BlockedType>(follow(resultTy)))
{
// If there's an existing result type, but it's _not_ blocked, then
@ -1790,7 +1788,6 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
// regular inference.
return false;
}
}
std::optional<TypeId> frozenType = freezeTable(inputType, context);

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#include "Luau/Common.h"
#include "Luau/NotNull.h"
#include "Luau/Type.h"
#include "Luau/TypeOrPack.h"
#include "Luau/TypePack.h"
#include "Luau/Unifiable.h"
#include "Luau/VisitType.h"
@ -20,14 +21,6 @@ namespace Luau
namespace
{
using Kind = Variant<TypeId, TypePackId>;
template<typename T>
const T* get(const Kind& kind)
{
return get_if<T>(&kind);
}
class TypeCloner
{
@ -38,7 +31,7 @@ protected:
// A queue of kinds where we cloned it, but whose interior types hasn't
// been updated to point to new clones. Once all of its interior types
// has been updated, it gets removed from the queue.
std::vector<Kind> queue;
std::vector<TypeOrPack> queue;
NotNull<SeenTypes> types;
NotNull<SeenTypePacks> packs;
@ -116,7 +109,7 @@ private:
if (hasExceededIterationLimit())
break;
Kind kind = queue.back();
TypeOrPack kind = queue.back();
queue.pop_back();
if (find(kind))
@ -147,7 +140,7 @@ protected:
return std::nullopt;
}
std::optional<Kind> find(Kind kind) const
std::optional<TypeOrPack> find(TypeOrPack kind) const
{
if (auto ty = get<TypeId>(kind))
return find(*ty);
@ -265,7 +258,7 @@ private:
);
}
void cloneChildren(Kind kind)
void cloneChildren(TypeOrPack kind)
{
if (auto ty = get<TypeId>(kind))
return cloneChildren(*ty);

View file

@ -3,6 +3,7 @@
#include "Luau/Ast.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/BuiltinTypeFunctions.h"
#include "Luau/Common.h"
#include "Luau/Constraint.h"
#include "Luau/ControlFlow.h"
@ -38,8 +39,6 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
@ -1330,8 +1329,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes}
);
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
// Add an intersection ReduceConstraint for the key variable to denote that it can't be nil
AstLocal* keyVar = *forIn->vars.begin();
const DefId keyDef = dfg->getDef(keyVar);
@ -1345,7 +1342,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
auto c = addConstraint(loopScope, keyVar->location, ReduceConstraint{intersectionTy});
c->dependencies.push_back(iterable);
}
for (TypeId var : variableTypes)
{
@ -3302,7 +3298,7 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
if (ty)
{
TypeIds* localDomain = localTypes.find(*ty);
if (localDomain && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && local->upvalue))
if (localDomain && !local->upvalue)
localDomain->insert(rhsType);
}
else
@ -3333,10 +3329,6 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
if (annotatedTy)
addConstraint(scope, local->location, SubtypeConstraint{rhsType, *annotatedTy});
// This is vestigial.
if (!FFlag::LuauDoNotAddUpvalueTypesToLocalType)
if (TypeIds* localDomain = localTypes.find(*ty))
localDomain->insert(rhsType);
}
void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId rhsType)

View file

@ -34,7 +34,6 @@ LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
@ -633,8 +632,9 @@ void ConstraintSolver::finalizeTypeFunctions()
TypeId ty = follow(t);
if (get<TypeFunctionInstanceType>(ty))
{
TypeFunctionContext context{NotNull{this}, constraint->scope, NotNull{constraint}};
FunctionGraphReductionResult result =
reduceTypeFunctions(t, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, NotNull{constraint}}, true);
reduceTypeFunctions(t, constraint->location, NotNull{&context}, true);
for (TypeId r : result.reducedTypes)
unblock(r, constraint->location);
@ -2772,8 +2772,9 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force)
{
TypeId ty = follow(c.ty);
FunctionGraphReductionResult result =
reduceTypeFunctions(ty, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, constraint}, force);
TypeFunctionContext context{NotNull{this}, constraint->scope, constraint};
FunctionGraphReductionResult result = reduceTypeFunctions(ty, constraint->location, NotNull{&context}, force);
for (TypeId r : result.reducedTypes)
unblock(r, constraint->location);
@ -2824,8 +2825,9 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force)
{
TypePackId tp = follow(c.tp);
FunctionGraphReductionResult result =
reduceTypeFunctions(tp, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, constraint}, force);
TypeFunctionContext context{NotNull{this}, constraint->scope, constraint};
FunctionGraphReductionResult result = reduceTypeFunctions(tp, constraint->location, NotNull{&context}, force);
for (TypeId r : result.reducedTypes)
unblock(r, constraint->location);
@ -3204,8 +3206,6 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
// the type of the `nextAstFragment` is the `nextTy`.
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
// Construct a FunctionCallConstraint, to help us learn about the type of the loop variables being assigned to in this iterable
TypePackId tableTyPack = arena->addTypePack({tableTy});
@ -3220,34 +3220,6 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
inheritBlocks(constraint, callConstraint);
inheritBlocks(unpackConstraint, callConstraint);
}
else
{
const TypePackId nextRetPack = nextFn->retTypes;
TypePackIterator it = begin(nextRetPack);
std::vector<TypeId> modifiedNextRetHead;
// The first value is never nil in the context of the loop, even if it's nil
// in the next function's return type, because the loop will not advance if
// it's nil.
if (it != end(nextRetPack))
{
TypeId firstRet = *it;
TypeId modifiedFirstRet = stripNil(builtinTypes, *arena, firstRet);
modifiedNextRetHead.push_back(modifiedFirstRet);
++it;
}
for (; it != end(nextRetPack); ++it)
modifiedNextRetHead.push_back(*it);
TypePackId modifiedNextRetPack = arena->addTypePack(std::move(modifiedNextRetHead), it.tail());
auto unpackConstraint = unpackAndAssign(c.variables, modifiedNextRetPack, constraint);
inheritBlocks(constraint, unpackConstraint);
}
return true;
}

View file

@ -14,8 +14,6 @@
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauDfgForwardNilFromAndOr)
@ -510,8 +508,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
}
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
if (FFlag::LuauDfgIfBlocksShouldRespectControlFlow)
{
// If the control flow from the `if` or `else` block is non-linear,
// then we should assume that the _other_ branch is the one taken.
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
@ -520,16 +516,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
scope->inherit(thenScope);
else if ((thencf | elsecf) == ControlFlow::None)
join(scope, thenScope, elseScope);
}
else
{
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
join(scope, scope, elseScope);
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
join(scope, thenScope, scope);
else if ((thencf | elsecf) == ControlFlow::None)
join(scope, thenScope, elseScope);
}
if (thencf == elsecf)
return thencf;
@ -1311,7 +1297,7 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
// In order to avoid alias tracking, we need to clip the reference to the parent def.
if (scope->canUpdateDefinition(l->local) && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && l->upvalue))
if (scope->canUpdateDefinition(l->local) && !l->upvalue)
{
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
scope->bindings[l->local] = updated;

View file

@ -8,7 +8,7 @@
#include "Luau/TypeUtils.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys2)
LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys3)
namespace Luau
{
@ -147,7 +147,7 @@ struct IndexCollector : public TypeOnceVisitor
bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr)
{
if (!FFlag::LuauImplicitTableIndexerKeys2)
if (!FFlag::LuauImplicitTableIndexerKeys3)
return true;
if (auto ty = astTypes->find(expr->expr))

View file

@ -22,7 +22,6 @@
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictNoErrorsPassingNever)
@ -240,14 +239,8 @@ struct NonStrictTypeChecker
if (noTypeFunctionErrors.find(instance))
return instance;
ErrorVec errors =
reduceTypeFunctions(
instance,
location,
TypeFunctionContext{arena, builtinTypes, stack.back(), simplifier, NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
true
)
.errors;
TypeFunctionContext context{arena, builtinTypes, stack.back(), simplifier, NotNull{&normalizer}, typeFunctionRuntime, ice, limits};
ErrorVec errors = reduceTypeFunctions(instance, location, NotNull{&context}, true).errors;
if (errors.empty())
noTypeFunctionErrors.insert(instance);
@ -340,7 +333,6 @@ struct NonStrictTypeChecker
{
ctx.remove(dfg->getDef(local));
if (FFlag::LuauNewNonStrictVisitTypes2)
visit(local->annotation);
}
}
@ -426,7 +418,6 @@ struct NonStrictTypeChecker
NonStrictContext visit(AstStatFor* forStatement)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
visit(forStatement->var->annotation);
// TODO: throwing out context based on same principle as existing code?
@ -440,12 +431,9 @@ struct NonStrictTypeChecker
}
NonStrictContext visit(AstStatForIn* forInStatement)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
{
for (auto var : forInStatement->vars)
visit(var->annotation);
}
for (AstExpr* rhs : forInStatement->values)
visit(rhs, ValueContext::RValue);
@ -481,12 +469,9 @@ struct NonStrictTypeChecker
}
NonStrictContext visit(AstStatTypeAlias* typeAlias)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
{
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
visit(typeAlias->type);
}
return {};
}
@ -497,28 +482,22 @@ struct NonStrictTypeChecker
}
NonStrictContext visit(AstStatDeclareFunction* declFn)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
{
visitGenerics(declFn->generics, declFn->genericPacks);
visit(declFn->params);
visit(declFn->retTypes);
}
return {};
}
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
visit(declGlobal->type);
return {};
}
NonStrictContext visit(AstStatDeclareExternType* declClass)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
{
if (declClass->indexer)
{
@ -528,7 +507,6 @@ struct NonStrictTypeChecker
for (auto prop : declClass->props)
visit(prop.ty);
}
return {};
}
@ -793,19 +771,15 @@ struct NonStrictTypeChecker
}
remainder.remove(dfg->getDef(local));
if (FFlag::LuauNewNonStrictVisitTypes2)
visit(local->annotation);
}
if (FFlag::LuauNewNonStrictVisitTypes2)
{
visitGenerics(exprFn->generics, exprFn->genericPacks);
visit(exprFn->returnAnnotation);
if (exprFn->varargAnnotation)
visit(exprFn->varargAnnotation);
}
return remainder;
}
@ -836,7 +810,6 @@ struct NonStrictTypeChecker
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
{
if (FFlag::LuauNewNonStrictVisitTypes2)
visit(typeAssertion->annotation);
return visit(typeAssertion->expr, ValueContext::RValue);
@ -868,8 +841,6 @@ struct NonStrictTypeChecker
void visit(AstType* ty)
{
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
// If this node is `nullptr`, early exit.
if (!ty)
return;
@ -1081,8 +1052,6 @@ struct NonStrictTypeChecker
void visit(AstTypePack* pack)
{
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
// If there is no pack node, early exit.
if (!pack)
return;

View file

@ -246,8 +246,9 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
const std::vector<AstExpr*>* argExprs
)
{
TypeFunctionContext context{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, ice, limits};
FunctionGraphReductionResult result = reduceTypeFunctions(
fnTy, callLoc, TypeFunctionContext{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, ice, limits}, /*force=*/true
fnTy, callLoc, NotNull{&context}, /*force=*/true
);
if (!result.errors.empty())
return {OverloadIsNonviable, result.errors};

View file

@ -2151,7 +2151,7 @@ std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const T
{
TypeFunctionContext context{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, iceReporter, NotNull{&limits}};
TypeId function = arena->addType(*functionInstance);
FunctionGraphReductionResult result = reduceTypeFunctions(function, {}, context, true);
FunctionGraphReductionResult result = reduceTypeFunctions(function, {}, NotNull{&context}, true);
ErrorVec errors;
if (result.blockedTypes.size() != 0 || result.blockedPacks.size() != 0)
{

View file

@ -16,7 +16,6 @@
#include "Luau/TypeOrPack.h"
#include <algorithm>
#include <stdexcept>
#include <string>
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)

View file

@ -30,10 +30,8 @@
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
@ -499,16 +497,11 @@ TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location l
return instance;
seenTypeFunctionInstances.insert(instance);
ErrorVec errors =
reduceTypeFunctions(
instance,
location,
TypeFunctionContext{
TypeFunctionContext context{
NotNull{&module->internalTypes}, builtinTypes, stack.back(), simplifier, NotNull{&normalizer}, typeFunctionRuntime, ice, limits
},
true
)
.errors;
};
ErrorVec errors = reduceTypeFunctions(instance, location, NotNull{&context}, true).errors;
if (!isErrorSuppressing(location, instance))
reportErrors(std::move(errors));
return instance;
@ -1417,8 +1410,6 @@ void TypeChecker2::visit(AstExprConstantBool* expr)
NotNull<Scope> scope{findInnermostScope(expr->location)};
SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
if (FFlag::LuauReportSubtypingErrors)
{
if (!isErrorSuppressing(expr->location, inferredType))
{
if (!r.isSubtype)
@ -1431,12 +1422,6 @@ void TypeChecker2::visit(AstExprConstantBool* expr)
reportErrors(r.errors);
}
}
else
{
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
reportError(TypeMismatch{inferredType, bestType}, expr->location);
}
}
void TypeChecker2::visit(AstExprConstantNumber* expr)
{
@ -1459,8 +1444,6 @@ void TypeChecker2::visit(AstExprConstantString* expr)
NotNull<Scope> scope{findInnermostScope(expr->location)};
SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
if (FFlag::LuauReportSubtypingErrors)
{
if (!isErrorSuppressing(expr->location, inferredType))
{
if (!r.isSubtype)
@ -1473,12 +1456,6 @@ void TypeChecker2::visit(AstExprConstantString* expr)
reportErrors(r.errors);
}
}
else
{
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
reportError(TypeMismatch{inferredType, bestType}, expr->location);
}
}
void TypeChecker2::visit(AstExprLocal* expr)
{
@ -1545,8 +1522,6 @@ void TypeChecker2::visitCall(AstExprCall* call)
if (result.isSubtype)
fnTy = follow(*selectedOverloadTy);
if (FFlag::LuauReportSubtypingErrors)
{
if (!isErrorSuppressing(call->location, *selectedOverloadTy))
if (FFlag::LuauSubtypingCheckFunctionGenericCounts)
{
@ -1554,8 +1529,6 @@ void TypeChecker2::visitCall(AstExprCall* call)
e.location = call->location;
}
reportErrors(std::move(result.errors));
}
if (result.normalizationTooComplex)
{
reportError(NormalizationTooComplex{}, call->func->location);
@ -3220,8 +3193,6 @@ bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location
NotNull<Scope> scope{findInnermostScope(location)};
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
if (FFlag::LuauReportSubtypingErrors)
{
if (!isErrorSuppressing(location, subTy))
if (FFlag::LuauSubtypingCheckFunctionGenericCounts)
{
@ -3229,8 +3200,6 @@ bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location
e.location = location;
}
reportErrors(std::move(r.errors));
}
if (r.normalizationTooComplex)
reportError(NormalizationTooComplex{}, location);
@ -3245,8 +3214,6 @@ bool TypeChecker2::testIsSubtype(TypePackId subTy, TypePackId superTy, Location
NotNull<Scope> scope{findInnermostScope(location)};
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
if (FFlag::LuauReportSubtypingErrors)
{
if (!isErrorSuppressing(location, subTy))
if (FFlag::LuauSubtypingCheckFunctionGenericCounts)
{
@ -3254,8 +3221,6 @@ bool TypeChecker2::testIsSubtype(TypePackId subTy, TypePackId superTy, Location
e.location = location;
}
reportErrors(std::move(r.errors));
}
if (r.normalizationTooComplex)
reportError(NormalizationTooComplex{}, location);
@ -3501,18 +3466,8 @@ PropertyType TypeChecker2::hasIndexTypeFromType(
return {NormalizationResult::True, {tt->indexer->indexResultType}};
}
if (FFlag::LuauTypeCheckerStricterIndexCheck)
{
return {NormalizationResult::False, {builtinTypes->unknownType}};
}
else
{
// if we are in a conditional context, we treat the property as present and `unknown` because
// we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit
// in the future once luau has support for exact tables since this only applies when inexact.
return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}};
}
}
else if (const ExternType* cls = get<ExternType>(ty))
{
// If the property doesn't exist on the class, we consult the indexer

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,16 @@
#include "Luau/TypeFunctionRuntime.h"
#include "Luau/Allocator.h"
#include "Luau/Lexer.h"
#include "Luau/BuiltinTypeFunctions.h"
#include "Luau/BytecodeBuilder.h"
#include "Luau/ParseResult.h"
#include "Luau/Compiler.h"
#include "Luau/DenseHash.h"
#include "Luau/StringUtils.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFunctionRuntimeBuilder.h"
#include "lua.h"
#include "lualib.h"
@ -19,6 +26,133 @@ LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
namespace Luau
{
LuauTempThreadPopper::LuauTempThreadPopper(lua_State* L)
: L(L)
{
}
LuauTempThreadPopper::~LuauTempThreadPopper()
{
lua_pop(L, 1);
}
static void dummyStateClose(lua_State*) {}
TypeFunctionRuntime::TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits)
: ice(ice)
, limits(limits)
, state(nullptr, dummyStateClose)
{
}
TypeFunctionRuntime::~TypeFunctionRuntime() {}
std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunction* function)
{
// If evaluation is disabled, we do not generate additional error messages
if (!allowEvaluation)
return std::nullopt;
// Do not evaluate type functions with parse errors inside
if (function->hasErrors)
return std::nullopt;
prepareState();
lua_State* global = state.get();
// Fetch to check if function is already registered
lua_pushlightuserdata(global, function);
lua_gettable(global, LUA_REGISTRYINDEX);
if (!lua_isnil(global, -1))
{
lua_pop(global, 1);
return std::nullopt;
}
lua_pop(global, 1);
AstName name = function->name;
// Construct ParseResult containing the type function
Allocator allocator;
AstNameTable names(allocator);
AstExpr* exprFunction = function->body;
AstArray<AstExpr*> exprReturns{&exprFunction, 1};
AstStatReturn stmtReturn{Location{}, exprReturns};
AstStat* stmtArray[] = {&stmtReturn};
AstArray<AstStat*> stmts{stmtArray, 1};
AstStatBlock exec{Location{}, stmts};
ParseResult parseResult{&exec, 1, {}, {}, {}, CstNodeMap{nullptr}};
BytecodeBuilder builder;
try
{
compileOrThrow(builder, parseResult, names);
}
catch (CompileError& e)
{
return format("'%s' type function failed to compile with error message: %s", name.value, e.what());
}
std::string bytecode = builder.getBytecode();
// Separate sandboxed thread for individual execution and private globals
lua_State* L = lua_newthread(global);
LuauTempThreadPopper popper(global);
// Create individual environment for the type function
luaL_sandboxthread(L);
// Do not allow global writes to that environment
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setreadonly(L, -1, true);
lua_pop(L, 1);
// Load bytecode into Luau state
if (auto error = checkResultForError(L, name.value, luau_load(L, name.value, bytecode.data(), bytecode.size(), 0)))
return error;
// Execute the global function which should return our user-defined type function
if (auto error = checkResultForError(L, name.value, lua_resume(L, nullptr, 0)))
return error;
if (!lua_isfunction(L, -1))
{
lua_pop(L, 1);
return format("Could not find '%s' type function in the global scope", name.value);
}
// Store resulting function in the registry
lua_pushlightuserdata(global, function);
lua_xmove(L, global, 1);
lua_settable(global, LUA_REGISTRYINDEX);
return std::nullopt;
}
void TypeFunctionRuntime::prepareState()
{
if (state)
return;
state = StateRef(lua_newstate(typeFunctionAlloc, nullptr), lua_close);
lua_State* L = state.get();
lua_setthreaddata(L, this);
setTypeFunctionEnvironment(L);
registerTypeUserData(L);
registerTypesLibrary(L);
luaL_sandbox(L);
luaL_sandboxthread(L);
}
constexpr int kTypeUserdataTag = 42;
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize)
@ -2108,13 +2242,6 @@ bool TypeFunctionProperty::isWriteOnly() const
* Below is a helper class for type.copy()
* Forked version of Clone.cpp
*/
using TypeFunctionKind = Variant<TypeFunctionTypeId, TypeFunctionTypePackId>;
template<typename T>
const T* get(const TypeFunctionKind& kind)
{
return get_if<T>(&kind);
}
class TypeFunctionCloner
{

View file

@ -2,7 +2,6 @@
#include "Luau/TypeFunctionRuntimeBuilder.h"
#include "Luau/Ast.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Common.h"
#include "Luau/DenseHash.h"
@ -12,6 +11,7 @@
#include "Luau/TypeFwd.h"
#include "Luau/TypeFunctionRuntime.h"
#include "Luau/TypePack.h"
#include "Luau/TypeOrPack.h"
#include "Luau/ToString.h"
#include <optional>
@ -41,7 +41,7 @@ class TypeFunctionSerializer
// queue.back() should always return two of same type in their respective sides
// For example `auto [first, second] = queue.back()`: if first is PrimitiveType,
// second must be TypeFunctionPrimitiveType; else there should be an error
std::vector<std::tuple<Kind, TypeFunctionKind>> queue;
std::vector<std::tuple<TypeOrPack, TypeFunctionKind>> queue;
SeenTypes types; // Mapping of TypeIds that have been shallow serialized to TypeFunctionTypeIds
SeenTypePacks packs; // Mapping of TypePackIds that have been shallow serialized to TypeFunctionTypePackIds
@ -121,7 +121,7 @@ private:
return std::nullopt;
}
std::optional<TypeFunctionKind> find(Kind kind) const
std::optional<TypeFunctionKind> find(TypeOrPack kind) const
{
if (auto ty = get<TypeId>(kind))
return find(*ty);
@ -316,7 +316,7 @@ private:
}
}
void serializeChildren(Kind kind, TypeFunctionKind tfkind)
void serializeChildren(TypeOrPack kind, TypeFunctionKind tfkind)
{
if (auto [ty, tfty] = std::tuple{get<TypeId>(kind), get<TypeFunctionTypeId>(tfkind)}; ty && tfty)
serializeChildren(*ty, *tfty);
@ -496,7 +496,7 @@ class TypeFunctionDeserializer
// queue.back() should always return two of same type in their respective sides
// For example `auto [first, second] = queue.back()`: if first is TypeFunctionPrimitiveType,
// second must be PrimitiveType; else there should be an error
std::vector<std::tuple<TypeFunctionKind, Kind>> queue;
std::vector<std::tuple<TypeFunctionKind, TypeOrPack>> queue;
// Generic types and packs currently in scope
// Generics are resolved by name even if runtime generic type pointers are different
@ -600,7 +600,7 @@ private:
return std::nullopt;
}
std::optional<Kind> find(TypeFunctionKind kind) const
std::optional<TypeOrPack> find(TypeFunctionKind kind) const
{
if (auto ty = get<TypeFunctionTypeId>(kind))
return find(*ty);
@ -824,7 +824,7 @@ private:
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
}
void deserializeChildren(TypeFunctionKind tfkind, Kind kind)
void deserializeChildren(TypeFunctionKind tfkind, TypeOrPack kind)
{
if (auto [ty, tfty] = std::tuple{get<TypeId>(kind), get<TypeFunctionTypeId>(tfkind)}; ty && tfty)
deserializeChildren(*tfty, *ty);

View file

@ -737,8 +737,6 @@ std::string toString(const TypePath::Path& path, bool prefixDot)
std::string toStringHuman(const TypePath::Path& path)
{
LUAU_ASSERT(FFlag::LuauSolverV2);
enum class State
{
Initial,

View file

@ -0,0 +1,391 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ApplyTypeFunction.h"
#include "Luau/BuiltinTypeFunctions.h"
#include "Luau/ConstraintSolver.h"
#include "Luau/Normalize.h"
#include "Luau/StringUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/UserDefinedTypeFunction.h"
#include "Luau/VisitType.h"
#include "lua.h"
#include "lualib.h"
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
namespace Luau
{
namespace
{
template<typename T>
class ScopedAssign
{
public:
ScopedAssign(T& target, const T& value)
: target(&target)
, oldValue(target)
{
target = value;
}
~ScopedAssign()
{
*target = oldValue;
}
private:
T* target = nullptr;
T oldValue;
};
}
struct FindUserTypeFunctionBlockers : TypeOnceVisitor
{
NotNull<TypeFunctionContext> ctx;
DenseHashSet<TypeId> blockingTypeMap{nullptr};
std::vector<TypeId> blockingTypes;
explicit FindUserTypeFunctionBlockers(NotNull<TypeFunctionContext> ctx)
: TypeOnceVisitor(/* skipBoundTypes */ true)
, ctx(ctx)
{
}
bool visit(TypeId ty) override
{
if (isPending(ty, ctx->solver))
{
if (!blockingTypeMap.contains(ty))
{
blockingTypeMap.insert(ty);
blockingTypes.push_back(ty);
}
}
return true;
}
bool visit(TypePackId tp) override
{
return true;
}
bool visit(TypeId ty, const ExternType&) override
{
return false;
}
};
static int evaluateTypeAliasCall(lua_State* L)
{
TypeFun* tf = static_cast<TypeFun*>(lua_tolightuserdata(L, lua_upvalueindex(1)));
TypeFunctionRuntime* runtime = getTypeFunctionRuntime(L);
TypeFunctionRuntimeBuilderState* runtimeBuilder = runtime->runtimeBuilder;
ApplyTypeFunction applyTypeFunction{runtimeBuilder->ctx->arena};
int argumentCount = lua_gettop(L);
std::vector<TypeId> rawTypeArguments;
for (int i = 0; i < argumentCount; i++)
{
TypeFunctionTypeId tfty = getTypeUserData(L, i + 1);
TypeId ty = deserialize(tfty, runtimeBuilder);
if (!runtimeBuilder->errors.empty())
luaL_error(L, "failed to deserialize type at argument %d", i + 1);
rawTypeArguments.push_back(ty);
}
// Check if we have enough arguments, by typical typechecking rules
size_t typesRequired = tf->typeParams.size();
size_t packsRequired = tf->typePackParams.size();
size_t typesProvided = rawTypeArguments.size() > typesRequired ? typesRequired : rawTypeArguments.size();
size_t extraTypes = rawTypeArguments.size() > typesRequired ? rawTypeArguments.size() - typesRequired : 0;
size_t packsProvided = 0;
if (extraTypes != 0 && packsProvided == 0)
{
// Extra types are only collected into a pack if a pack is expected
if (packsRequired != 0)
packsProvided += 1;
else
typesProvided += extraTypes;
}
for (size_t i = typesProvided; i < typesRequired; ++i)
{
if (tf->typeParams[i].defaultValue)
typesProvided += 1;
}
for (size_t i = packsProvided; i < packsRequired; ++i)
{
if (tf->typePackParams[i].defaultValue)
packsProvided += 1;
}
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
packsProvided += 1;
if (typesProvided != typesRequired || packsProvided != packsRequired)
luaL_error(L, "not enough arguments to call");
// Prepare final types and packs
auto [types, packs] = saturateArguments(runtimeBuilder->ctx->arena, runtimeBuilder->ctx->builtins, *tf, rawTypeArguments, {});
for (size_t i = 0; i < types.size(); ++i)
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = types[i];
for (size_t i = 0; i < packs.size(); ++i)
applyTypeFunction.typePackArguments[tf->typePackParams[i].tp] = packs[i];
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf->type);
if (!maybeInstantiated.has_value())
{
luaL_error(L, "failed to instantiate type alias");
return 1;
}
TypeId target = follow(*maybeInstantiated);
FunctionGraphReductionResult result = reduceTypeFunctions(target, Location{}, runtimeBuilder->ctx);
if (!result.errors.empty())
luaL_error(L, "failed to reduce type function with: %s", toString(result.errors.front()).c_str());
TypeFunctionTypeId serializedTy = serialize(follow(target), runtimeBuilder);
if (!runtimeBuilder->errors.empty())
luaL_error(L, "%s", runtimeBuilder->errors.front().c_str());
allocTypeUserData(L, serializedTy->type);
return 1;
}
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
TypeId instance,
const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& packParams,
NotNull<TypeFunctionContext> ctx
)
{
auto typeFunction = getMutable<TypeFunctionInstanceType>(instance);
if (typeFunction->userFuncData.owner.expired())
{
ctx->ice->ice("user-defined type function module has expired");
return {std::nullopt, Reduction::Erroneous, {}, {}};
}
if (!typeFunction->userFuncName || !typeFunction->userFuncData.definition)
{
ctx->ice->ice("all user-defined type functions must have an associated function definition");
return {std::nullopt, Reduction::Erroneous, {}, {}};
}
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
if (!ctx->typeFunctionRuntime->allowEvaluation || typeFunction->userFuncData.definition->hasErrors)
return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
FindUserTypeFunctionBlockers check{ctx};
for (auto typeParam : typeParams)
check.traverse(follow(typeParam));
if (FFlag::LuauUserTypeFunctionAliases)
{
// Check that our environment doesn't depend on any type aliases that are blocked
for (auto& [name, definition] : typeFunction->userFuncData.environmentAlias)
{
if (definition.first->typeParams.empty() && definition.first->typePackParams.empty())
check.traverse(follow(definition.first->type));
}
}
if (!check.blockingTypes.empty())
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
// Ensure that whole type function environment is registered
for (auto& [name, definition] : typeFunction->userFuncData.environmentFunction)
{
// Cannot evaluate if a potential dependency couldn't be parsed
if (definition.first->hasErrors)
return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
{
// Failure to register at this point means that original definition had to error out and should not have been present in the
// environment
ctx->ice->ice("user-defined type function reference cannot be registered");
return {std::nullopt, Reduction::Erroneous, {}, {}};
}
}
AstName name = typeFunction->userFuncData.definition->name;
lua_State* global = ctx->typeFunctionRuntime->state.get();
if (global == nullptr)
return {std::nullopt, Reduction::Erroneous, {}, {}, format("'%s' type function: cannot be evaluated in this context", name.value)};
// Separate sandboxed thread for individual execution and private globals
lua_State* L = lua_newthread(global);
LuauTempThreadPopper popper(global);
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
ScopedAssign setRuntimeBuilder(ctx->typeFunctionRuntime->runtimeBuilder, runtimeBuilder.get());
ScopedAssign enableReduction(ctx->normalizer->sharedState->reentrantTypeReduction, false);
// Build up the environment table of each function we have visible
for (auto& [_, curr] : typeFunction->userFuncData.environmentFunction)
{
// Environment table has to be filled only once in the current execution context
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
continue;
ctx->typeFunctionRuntime->initialized.insert(curr.first);
lua_pushlightuserdata(L, curr.first);
lua_gettable(L, LUA_REGISTRYINDEX);
if (!lua_isfunction(L, -1))
{
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
return {std::nullopt, Reduction::Erroneous, {}, {}};
}
// Build up the environment of the current function, where some might not be visible
lua_getfenv(L, -1);
lua_setreadonly(L, -1, false);
for (auto& [name, definition] : typeFunction->userFuncData.environmentFunction)
{
// Filter visibility based on original scope depth
if (definition.second >= curr.second)
{
lua_pushlightuserdata(L, definition.first);
lua_gettable(L, LUA_REGISTRYINDEX);
if (!lua_isfunction(L, -1))
break; // Don't have to report an error here, we will visit each function in outer loop
lua_setfield(L, -2, name.c_str());
}
}
if (FFlag::LuauUserTypeFunctionAliases)
{
for (auto& [name, definition] : typeFunction->userFuncData.environmentAlias)
{
// Filter visibility based on original scope depth
if (definition.second >= curr.second)
{
if (definition.first->typeParams.empty() && definition.first->typePackParams.empty())
{
TypeId ty = follow(definition.first->type);
// This is checked at the top of the function, and should still be true.
LUAU_ASSERT(!isPending(ty, ctx->solver));
TypeFunctionTypeId serializedTy = serialize(ty, runtimeBuilder.get());
// Only register aliases that are representable in type environment
if (runtimeBuilder->errors.empty())
{
allocTypeUserData(L, serializedTy->type);
lua_setfield(L, -2, name.c_str());
}
}
else
{
lua_pushlightuserdata(L, definition.first);
lua_pushcclosure(L, evaluateTypeAliasCall, name.c_str(), 1);
lua_setfield(L, -2, name.c_str());
}
}
}
}
lua_setreadonly(L, -1, true);
lua_pop(L, 2);
}
// Fetch the function we want to evaluate
lua_pushlightuserdata(L, typeFunction->userFuncData.definition);
lua_gettable(L, LUA_REGISTRYINDEX);
if (!lua_isfunction(L, -1))
{
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
return {std::nullopt, Reduction::Erroneous, {}, {}};
}
resetTypeFunctionState(L);
// Push serialized arguments onto the stack
for (auto typeParam : typeParams)
{
TypeId ty = follow(typeParam);
// This is checked at the top of the function, and should still be true.
LUAU_ASSERT(!isPending(ty, ctx->solver));
TypeFunctionTypeId serializedTy = serialize(ty, runtimeBuilder.get());
// Check if there were any errors while serializing
if (runtimeBuilder->errors.size() != 0)
return {std::nullopt, Reduction::Erroneous, {}, {}, runtimeBuilder->errors.front()};
allocTypeUserData(L, serializedTy->type);
}
// Set up an interrupt handler for type functions to respect type checking limits and LSP cancellation requests.
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
{
auto ctx = static_cast<const TypeFunctionRuntime*>(lua_getthreaddata(lua_mainthread(L)));
if (ctx->limits->finishTime && TimeTrace::getClock() > *ctx->limits->finishTime)
throw TimeLimitError(ctx->ice->moduleName);
if (ctx->limits->cancellationToken && ctx->limits->cancellationToken->requested())
throw UserCancelError(ctx->ice->moduleName);
};
ctx->typeFunctionRuntime->messages.clear();
if (auto error = checkResultForError(L, name.value, lua_pcall(L, int(typeParams.size()), 1, 0)))
return {std::nullopt, Reduction::Erroneous, {}, {}, std::move(error), ctx->typeFunctionRuntime->messages};
// If the return value is not a type userdata, return with error message
if (!isTypeUserData(L, 1))
{
return {
std::nullopt,
Reduction::Erroneous,
{},
{},
format("'%s' type function: returned a non-type value", name.value),
ctx->typeFunctionRuntime->messages
};
}
TypeFunctionTypeId retTypeFunctionTypeId = getTypeUserData(L, 1);
// No errors should be present here since we should've returned already if any were raised during serialization.
LUAU_ASSERT(runtimeBuilder->errors.size() == 0);
TypeId retTypeId = deserialize(retTypeFunctionTypeId, runtimeBuilder.get());
// At least 1 error occurred while deserializing
if (runtimeBuilder->errors.size() > 0)
return {std::nullopt, Reduction::Erroneous, {}, {}, runtimeBuilder->errors.front(), ctx->typeFunctionRuntime->messages};
return {retTypeId, Reduction::MaybeOk, {}, {}, std::nullopt, ctx->typeFunctionRuntime->messages};
}
}

View file

@ -20,7 +20,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail)
LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit)
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
@ -1903,10 +1902,8 @@ AstTypePack* Parser::parseReturnType()
// possibly () -> ReturnType
if (lexer.current().type != ')')
{
if (FFlag::LuauCSTForReturnTypeFunctionTail && options.storeCstData)
if (options.storeCstData)
varargAnnotation = parseTypeList(result, resultNames, &commaPositions, &nameColonPositions);
else if (options.storeCstData)
varargAnnotation = parseTypeList(result, resultNames, &commaPositions);
else
varargAnnotation = parseTypeList(result, resultNames);
}
@ -1950,7 +1947,7 @@ AstTypePack* Parser::parseReturnType()
Position returnArrowPosition = lexer.current().location.begin;
AstType* tail = parseFunctionTypeTail(begin, {nullptr, 0}, {}, {}, copy(result), copy(resultNames), varargAnnotation);
if (FFlag::LuauCSTForReturnTypeFunctionTail && options.storeCstData && tail->is<AstTypeFunction>())
if (options.storeCstData && tail->is<AstTypeFunction>())
{
cstNodeMap[tail] = allocator.alloc<CstTypeFunction>(
Position{0, 0},

View file

@ -392,7 +392,7 @@ enum class IrCmd : uint8_t
// C: Rn or unsigned int (key)
SET_TABLE,
// TODO: remove with FFlagLuauCodeGenSimplifyImport
// TODO: remove with FFlagLuauCodeGenSimplifyImport2
// Lookup a value in the environment
// A: Rn (where to store the result)
// B: unsigned int (import path)

View file

@ -1384,7 +1384,10 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
break;
case IrCmd::GET_CACHED_IMPORT:
{
regs.spill(build, index);
Label skip, exit;
RegisterA64 tempTag = regs.allocTemp(KindA64::w);
AddressA64 addrConstTag = tempAddr(inst.b, offsetof(TValue, tt));
@ -1395,8 +1398,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
build.cbnz(tempTag, skip);
{
size_t spills = regs.spill(build, index);
build.mov(x0, rState);
build.add(x1, rBase, uint16_t(vmRegOp(inst.a) * sizeof(TValue)));
build.mov(w2, importOp(inst.c));
@ -1404,16 +1405,14 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, getImport)));
build.blr(x4);
regs.restore(build, spills); // Need to restore before skip so that registers are in a consistent state
emitUpdateBase(build);
build.b(exit);
}
RegisterA64 tempTv = regs.allocTemp(KindA64::q);
build.setLabel(skip);
RegisterA64 tempTv = regs.allocTemp(KindA64::q);
AddressA64 addrConst = tempAddr(inst.b, 0);
build.ldr(tempTv, addrConst);

View file

@ -1221,6 +1221,9 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
}
case IrCmd::GET_CACHED_IMPORT:
{
regs.assertAllFree();
regs.assertNoSpills();
Label skip, exit;
// If the constant for the import is set, we will use it directly, otherwise we have to call an import path lookup function
@ -1241,9 +1244,10 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
emitUpdateBase(build);
build.jmp(exit);
build.setLabel(skip);
ScopedRegX64 tmp1{regs, SizeX64::xmmword};
build.setLabel(skip);
build.vmovups(tmp1.reg, luauConstant(vmConstOp(inst.b)));
build.vmovups(luauReg(vmRegOp(inst.a)), tmp1.reg);
build.setLabel(exit);

View file

@ -12,7 +12,7 @@
#include "lstate.h"
#include "ltm.h"
LUAU_FASTFLAGVARIABLE(LuauCodeGenSimplifyImport)
LUAU_FASTFLAGVARIABLE(LuauCodeGenSimplifyImport2)
namespace Luau
{
@ -1217,7 +1217,7 @@ void translateInstGetImport(IrBuilder& build, const Instruction* pc, int pcpos)
int k = LUAU_INSN_D(*pc);
uint32_t aux = pc[1];
if (FFlag::LuauCodeGenSimplifyImport)
if (FFlag::LuauCodeGenSimplifyImport2)
{
build.inst(IrCmd::CHECK_SAFE_ENV, build.vmExit(pcpos));
build.inst(IrCmd::GET_CACHED_IMPORT, build.vmReg(ra), build.vmConst(k), build.constImport(aux), build.constUint(pcpos + 1));

View file

@ -1545,6 +1545,8 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
// Outside of safe environment, environment traversal for an import can execute custom code
if (!state.inSafeEnv)
state.invalidateUserCall();
state.invalidateValuePropagation();
break;
case IrCmd::CONCAT:
state.invalidateRegisterRange(vmRegOp(inst.a), function.uintOp(inst.b));

View file

@ -4300,8 +4300,6 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
}
// computes type information for all functions based on type annotations
if (FFlag::LuauSeparateCompilerTypeInfo)
{
if (options.typeInfoLevel >= 1 || options.optimizationLevel >= 2)
buildTypeMap(
compiler.functionTypes,
@ -4316,24 +4314,6 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
options.libraryMemberTypeCb,
bytecode
);
}
else
{
if (options.typeInfoLevel >= 1)
buildTypeMap(
compiler.functionTypes,
compiler.localTypes,
compiler.exprTypes,
root,
options.vectorType,
compiler.userdataTypes,
compiler.builtinTypes,
compiler.builtins,
compiler.globals,
options.libraryMemberTypeCb,
bytecode
);
}
for (AstExprFunction* expr : functions)
{

View file

@ -175,6 +175,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/Autocomplete.h
Analysis/include/Luau/AutocompleteTypes.h
Analysis/include/Luau/BuiltinDefinitions.h
Analysis/include/Luau/BuiltinTypeFunctions.h
Analysis/include/Luau/Cancellation.h
Analysis/include/Luau/Clone.h
Analysis/include/Luau/Constraint.h
@ -248,6 +249,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/Unifier.h
Analysis/include/Luau/Unifier2.h
Analysis/include/Luau/UnifierSharedState.h
Analysis/include/Luau/UserDefinedTypeFunction.h
Analysis/include/Luau/VisitType.h
Analysis/src/Anyification.cpp
@ -257,6 +259,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/src/Autocomplete.cpp
Analysis/src/AutocompleteCore.cpp
Analysis/src/BuiltinDefinitions.cpp
Analysis/src/BuiltinTypeFunctions.cpp
Analysis/src/Clone.cpp
Analysis/src/Constraint.cpp
Analysis/src/ConstraintGenerator.cpp
@ -316,6 +319,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/src/Unifiable.cpp
Analysis/src/Unifier.cpp
Analysis/src/Unifier2.cpp
Analysis/src/UserDefinedTypeFunction.cpp
)
# Luau.EqSat Sources

View file

@ -21,7 +21,7 @@ LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
using namespace Luau;
@ -4622,7 +4622,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr")
// Somewhat surprisingly, the old solver didn't cover this case.
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys2, true},
{FFlag::LuauImplicitTableIndexerKeys3, true},
};
check(R"(
@ -4649,7 +4649,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauExpectedTypeVisitor, true},
{FFlag::LuauImplicitTableIndexerKeys2, false},
{FFlag::LuauImplicitTableIndexerKeys3, true},
};
check(R"(

View file

@ -13,7 +13,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
struct DataFlowGraphFixture
@ -447,8 +446,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node_3")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
dfg(R"(
local x = 5

View file

@ -1,6 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/BuiltinTypeFunctions.h"
#include "Luau/Config.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Error.h"

View file

@ -16,7 +16,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
namespace
@ -977,9 +976,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments")
LUAU_REQUIRE_NO_ERRORS(resultA);
CheckResult resultB = getFrontend().check("B");
if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes2)
LUAU_REQUIRE_NO_ERRORS(resultB);
else
LUAU_REQUIRE_ERROR_COUNT(1, resultB);
CheckResult resultC = getFrontend().check("C");

View file

@ -16,7 +16,7 @@
#include <memory>
#include <string_view>
LUAU_FASTFLAG(LuauCodeGenSimplifyImport)
LUAU_FASTFLAG(LuauCodeGenSimplifyImport2)
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant)
{
@ -500,7 +500,7 @@ bb_bytecode_1:
TEST_CASE("DseInitialStackState")
{
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true};
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport2, true};
CHECK_EQ(
"\n" + getCodegenAssembly(R"(
@ -1567,7 +1567,7 @@ end
TEST_CASE("ForInManualAnnotation")
{
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true};
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport2, true};
CHECK_EQ(
"\n" + getCodegenAssembly(

View file

@ -15,7 +15,6 @@
#include "doctest.h"
#include <iostream>
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
@ -737,8 +736,6 @@ TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict_1")
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
{
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes2, true};
CheckResult result = check(Mode::Nonstrict, R"(
--!nonstrict
local foo: Foo = 1
@ -752,8 +749,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict_2")
{
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes2, true};
CheckResult result = check(Mode::Nonstrict, R"(
--!nonstrict
local foo = 1 :: Foo

View file

@ -12,8 +12,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauCSTForReturnTypeFunctionTail)
TEST_SUITE_BEGIN("TranspilerTests");
TEST_CASE("test_1")
@ -2055,9 +2053,6 @@ TEST_CASE("transpile_type_function_return_types")
TEST_CASE("transpile_chained_function_types")
{
ScopedFastFlag fflags[] = {
{FFlag::LuauCSTForReturnTypeFunctionTail, true},
};
std::string code = R"( type Foo = () -> () -> () )";
CHECK_EQ(code, transpile(code, {}, true).code);

View file

@ -1813,7 +1813,7 @@ struct TFFixture
BuiltinTypeFunctions builtinTypeFunctions;
TypeFunctionContext tfc{
TypeFunctionContext tfc_{
arena,
getBuiltins(),
NotNull{globalScope.get()},
@ -1823,6 +1823,8 @@ struct TFFixture
NotNull{&ice},
NotNull{&limits}
};
NotNull<TypeFunctionContext> tfc{&tfc_};
};
TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")

View file

@ -10,7 +10,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAG(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
@ -1180,10 +1179,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault")
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
)");
if (FFlag::LuauNewNonStrictVisitTypes2)
LUAU_CHECK_ERROR_COUNT(2, result);
else
LUAU_CHECK_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "gh1632_no_infinite_recursion_in_normalization")

View file

@ -14,7 +14,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
@ -36,16 +35,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK("(*error-type* | ~nil)?" == toString(requireType("a")));
}
else
{
CHECK("any?" == toString(requireType("a")));
}
}
else
CHECK(getBuiltins()->anyType == requireType("a"));
}
@ -66,16 +56,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2")
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK("(*error-type* | ~nil)?" == toString(requireType("a")));
}
else
{
CHECK("any?" == toString(requireType("a")));
}
}
else
CHECK("any" == toString(requireType("a")));
}
@ -94,16 +75,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any")
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK("(*error-type* | ~nil)?" == toString(requireType("a")));
}
else
{
CHECK("any?" == toString(requireType("a")));
}
}
else
CHECK("any" == toString(requireType("a")));
}
@ -120,16 +92,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2")
)");
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK("(*error-type* | ~nil)?" == toString(requireType("a")));
}
else
{
CHECK("any?" == toString(requireType("a")));
}
}
else
CHECK("any" == toString(requireType("a")));
}
@ -148,16 +111,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any_pack")
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK("(*error-type* | ~nil)?" == toString(requireType("a")));
}
else
{
CHECK("any?" == toString(requireType("a")));
}
}
else
CHECK("any" == toString(requireType("a")));
}

View file

@ -18,7 +18,6 @@ LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads)
TEST_SUITE_BEGIN("BuiltinTests");
@ -1767,8 +1766,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_known_
TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_unknown_property")
{
ScopedFastFlag _{FFlag::LuauTypeCheckerStricterIndexCheck, true};
CheckResult results = check(R"(
if bit32.scrambleEggs then
end

View file

@ -25,7 +25,6 @@ LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauFormatUseLastPosition)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
@ -3162,7 +3161,7 @@ TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generali
TEST_CASE_FIXTURE(Fixture, "fuzz_unwind_mutually_recursive_union_type_func")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDoNotAddUpvalueTypesToLocalType, true}};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
// Previously, this block minted a type like:
//

View file

@ -9,12 +9,10 @@
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauIntersectNotNil)
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
@ -785,7 +783,7 @@ TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver")
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_types")
{
ScopedFastFlag sffs[] = {{FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}};
ScopedFastFlag _{FFlag::LuauSubtypingCheckFunctionGenericCounts, true};
CheckResult result = check(R"(
type C = () -> ()
@ -818,7 +816,7 @@ local d: D = c
}
TEST_CASE_FIXTURE(Fixture, "generic_function_mismatch_with_argument")
{
ScopedFastFlag sffs[] = {{FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}};
ScopedFastFlag _{FFlag::LuauSubtypingCheckFunctionGenericCounts, true};
CheckResult result = check(R"(
type C = (number) -> ()
@ -852,7 +850,7 @@ local d: D = c
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_pack")
{
ScopedFastFlag sffs[] = {{FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}};
ScopedFastFlag _{FFlag::LuauSubtypingCheckFunctionGenericCounts, true};
CheckResult result = check(R"(
type C = () -> ()
@ -1530,7 +1528,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded"
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions")
{
ScopedFastFlag _[] = {
{FFlag::LuauReportSubtypingErrors, true},
{FFlag::LuauSubtypingCheckFunctionGenericCounts, true},
{FFlag::LuauEagerGeneralization4, true},
};

View file

@ -15,9 +15,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
TEST_SUITE_BEGIN("TypeInferLoops");
@ -158,7 +156,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop")
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next")
{
ScopedFastFlag _{FFlag::LuauAddCallConstraintForIterableFunctions, true};
CheckResult result = check(R"(
local n
local s
@ -187,7 +184,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next")
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next_and_multiple_elements")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauAddCallConstraintForIterableFunctions, true},
{FFlag::LuauSimplifyOutOfLine2, true},
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
@ -289,15 +285,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_with_a_custom_iterator_should_type_ch
end
)");
if (FFlag::LuauSolverV2 && FFlag::LuauAddCallConstraintForIterableFunctions)
{
if (FFlag::LuauSolverV2)
LUAU_REQUIRE_NO_ERRORS(result);
}
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
{
@ -1286,7 +1278,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "forin_metatable_iter_mm")
TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_preserves_error_suppression")
{
ScopedFastFlag _{FFlag::LuauAddCallConstraintForIterableFunctions, true};
ScopedFastFlag v1{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
@ -1504,10 +1495,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_is_linearish")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag sffs[] = {
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = nil

View file

@ -12,7 +12,6 @@
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping)

View file

@ -9,6 +9,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauEagerGeneralization4)
namespace
{
@ -50,9 +52,8 @@ TEST_CASE_FIXTURE(NegationFixture, "string_is_not_a_subtype_of_negated_string")
TEST_CASE_FIXTURE(Fixture, "cofinite_strings_can_be_compared_for_equality")
{
// CLI-117082 Cofinite strings cannot be compared for equality because normalization produces a large type with cycles
if (FFlag::LuauSolverV2)
return;
ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
CheckResult result = check(R"(
function f(e)
if e == 'strictEqual' then

View file

@ -12,15 +12,14 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauSimplificationTableExternType)
LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauNormalizationIntersectTablesPreservesExternTypes)
LUAU_FASTFLAG(LuauNormalizationReorderFreeTypeIntersect)
LUAU_FASTFLAG(LuauAvoidDoubleNegation)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauRefineNoRefineAlways)
using namespace Luau;
@ -1944,10 +1943,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_clone_it")
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "refine_a_param_that_got_resolved_during_constraint_solving_stage")
{
// CLI-117134 - Applying a refinement causes an optional value access error.
if (FFlag::LuauSolverV2)
return;
CheckResult result = check(R"(
type Id<T> = T
@ -2216,15 +2211,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table")
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauAddCallConstraintForIterableFunctions)
{
CHECK_EQ("(unknown) -> (~nil, unknown)", toString(requireType("f")));
}
else
{
CHECK_EQ("(unknown) -> (unknown, unknown)", toString(requireType("f")));
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "conditional_refinement_should_stay_error_suppressing")
{
@ -2713,7 +2701,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_single")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
{FFlag::LuauTypeCheckerStricterIndexCheck, true},
{FFlag::LuauRefineTablesWithReadType, true},
};
@ -2734,7 +2721,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_union")
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
{FFlag::LuauTypeCheckerStricterIndexCheck, true},
{FFlag::LuauRefineTablesWithReadType, true},
};
@ -2830,4 +2816,34 @@ TEST_CASE_FIXTURE(Fixture, "limit_complexity_of_arithmetic_type_functions" * doc
LUAU_REQUIRE_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "refine_by_no_refine_should_always_reduce")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag refineNoRefineAlways{FFlag::LuauRefineNoRefineAlways, true};
CheckResult result = check(R"(
function foo(t): boolean return true end
function select<K, V>(t: { [K]: V }, columns: { K }): { [K]: V }
local result = {}
if foo(t) then
for k, v in t do
if table.find(columns, k) then
result[k] = v -- was TypeError: Type function instance refine<intersect<K, ~nil>, *no-refine*> is uninhabited
end
end
else
for k, v in pairs(t) do
if table.find(columns, k) then
result[k] = v
end
end
end
return result
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();

View file

@ -23,8 +23,6 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
@ -3818,10 +3816,7 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap
TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type")
{
ScopedFastFlag sff[] = {
{FFlag::LuauReportSubtypingErrors, true},
{FFlag::LuauEagerGeneralization4, true},
};
ScopedFastFlag _{FFlag::LuauEagerGeneralization4, true};
CheckResult result = check(R"(
local function f(s)
@ -5696,7 +5691,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_in_indexer_with_compound_assig
TEST_CASE_FIXTURE(Fixture, "stop_refining_new_table_indices_for_non_primitive_tables")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
ScopedFastFlag stricterIndexCheck{FFlag::LuauTypeCheckerStricterIndexCheck, true};
CheckResult result = check(R"(
local foo:{val:number} = {val = 1}

View file

@ -24,9 +24,7 @@ LUAU_FASTINT(LuauNormalizeCacheLimit)
LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauAvoidDoubleNegation)
LUAU_FASTFLAG(LuauInsertErrorTypesIntoIndexerResult)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
@ -1213,8 +1211,6 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
REQUIRE_MESSAGE(!result.errors.empty(), getErrors(result));
if (FFlag::LuauSolverV2)
{
if (FFlag::LuauReportSubtypingErrors)
{
CHECK(4 == result.errors.size());
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
@ -1226,17 +1222,6 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(e));
}
else
{
CHECK(3 == result.errors.size());
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
CHECK(Location{{3, 22}, {3, 41}} == result.errors[2].location);
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[1]));
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[2]));
}
}
else
{
CHECK(1 == result.errors.size());
@ -1986,7 +1971,7 @@ TEST_CASE_FIXTURE(Fixture, "assert_allows_singleton_union_or_intersection")
TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local f = table.freeze
f(table)
@ -1995,7 +1980,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
ScopedFastFlag _ {FFlag::LuauSolverV2, true};
// This is the original fuzzer version of the above issue.
CheckResult results = check(R"(
local function l0()
@ -2116,8 +2101,6 @@ local _
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_missing_follow_table_freeze")
{
ScopedFastFlag _{FFlag::LuauMagicFreezeCheckBlocked2, true};
LUAU_REQUIRE_ERRORS(check(R"(
if _:freeze(_)[_][_] then
else

View file

@ -13,7 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
@ -1063,8 +1062,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks")
TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks2")
{
ScopedFastFlag _{FFlag::LuauReportSubtypingErrors, true};
CheckResult result = check(R"(
function _(l0:((typeof((pcall)))|((((t0)->())|(typeof(-67108864)))|(any)))|(any),...):(((typeof(0))|(any))|(any),typeof(-67108864),any)
xpcall(_,_,_)

View file

@ -4,9 +4,6 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
@ -269,8 +266,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "local_assigned_in_only_one_branch_that_fall
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_assigns_but_is_met_with_return")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
@ -288,8 +283,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_as
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_but_is_met_with_return_and_else_branch_assigns")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
@ -350,7 +343,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_as
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_do_not_mutate_upvalue_type")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
CheckResult result = check(R"(
local x = nil
@ -372,7 +364,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_do_not_mutate_upvalue_type"
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_do_not_mutate_upvalue_type_2")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
CheckResult result = check(R"(
local t = {x = nil}
@ -413,7 +404,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauReportSubtypingErrors, true},
{FFlag::LuauEagerGeneralization4, true},
};
@ -477,25 +467,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "typestates_preserve_error_suppression")
CHECK("*error-type* | string" == toString(requireTypeAtPosition({3, 14}), {true}));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_properties")
{
// early return if the flag isn't set since this is blocking gated commits
// unconditional return
// CLI-117098 Type states with error suppressing properties doesn't infer the correct type for properties.
if (!FFlag::LuauSolverV2 || FFlag::LuauSolverV2)
return;
CheckResult result = check(R"(
local a: {x: any} = {x = 51}
a.x = "pickles" -- We'll have a new DefId for this iteration of `a.x`. Its type must also be error-suppressing
print(a.x)
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("*error-type* | string" == toString(requireTypeAtPosition({3, 16}), {true}));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_do_not_apply_to_the_initial_local_definition")
{
// early return if the flag isn't set since this is blocking gated commits
@ -577,8 +548,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_normalized_type_variables_are_bad" *
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1547_simple")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local rand = 0
@ -594,8 +563,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1547_simple")
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1547")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local rand = 0
@ -629,8 +596,6 @@ TEST_CASE_FIXTURE(Fixture, "modify_captured_table_field")
TEST_CASE_FIXTURE(Fixture, "oss_1561")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
loadDefinition(R"(
declare class Vector3
X: number
@ -655,8 +620,6 @@ TEST_CASE_FIXTURE(Fixture, "oss_1561")
TEST_CASE_FIXTURE(Fixture, "oss_1575")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local flag = true
local function Flip()
@ -667,8 +630,6 @@ TEST_CASE_FIXTURE(Fixture, "oss_1575")
TEST_CASE_FIXTURE(Fixture, "capture_upvalue_in_returned_function")
{
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
function def()
local i : number = 0
@ -684,8 +645,6 @@ TEST_CASE_FIXTURE(Fixture, "capture_upvalue_in_returned_function")
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_else_branch")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
--!strict
local x
@ -707,8 +666,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_else_branch")
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
--!strict
local x
@ -731,8 +688,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch")
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
--!strict
type Payload = { payload: number }
@ -756,7 +711,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}
{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}
};
CheckResult result = check(R"(
@ -778,8 +733,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
TEST_CASE_FIXTURE(BuiltinsFixture, "type_refinement_in_loop")
{
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
CheckResult result = check(R"(
--!strict
local function onEachString(t: { string | number })
@ -800,10 +753,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_refinement_in_loop")
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch_and_do_nothing_in_else")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict
@ -825,10 +775,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch_and_do_nothing_in_else")
TEST_CASE_FIXTURE(BuiltinsFixture, "assign_in_an_if_branch_without_else")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict

View file

@ -513,6 +513,23 @@ end
assert(extramath3(2) == "number")
assert(extramath3("2") == "number")
local function scopedrestorefailurea(format: string, b: buffer): (string, buffer)
local H1F, H2F, H3F, H4F = buffer.readu32(b, 0), buffer.readu32(b, 8), buffer.readu32(b, 16), buffer.readu32(b, 24)
local H5F, H6F, _, _ = buffer.readu32(b, 32), buffer.readu32(b, 40), buffer.readu32(b, 48), buffer.readu32(b, 56)
local H1B, H2B, H3B, H4B = buffer.readu32(b, 4), buffer.readu32(b, 12), buffer.readu32(b, 20), buffer.readu32(b, 28)
local H5B, H6B, _, _ = buffer.readu32(b, 36), buffer.readu32(b, 44), buffer.readu32(b, 52), buffer.readu32(b, 60)
return string.format(format, H1F, H1B, H2F, H2B, H3F, H3B, H4F, H4B, H5F, H5B, H6F, H6B), b
end
local function scopedrestorefailureb()
local data = buffer.create(64)
buffer.fill(data, 0, 0x12, 64)
assert(scopedrestorefailurea(string.rep("%08x", 12), data) == string.rep("12", 48))
end
scopedrestorefailureb()
local function slotcachelimit1()
local tbl = {
f1 = function() return 1 end,