mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 21:40:43 +00:00
Sync to upstream/release/655 (#1563)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
## New Solver * Type functions should be able to signal whether or not irreducibility is due to an error * Do not generate extra expansion constraint for uninvoked user-defined type functions * Print in a user-defined type function reports as an error instead of logging to stdout * Many e-graphs bugfixes and performance improvements * Many general bugfixes and improvements to the new solver as a whole * Fixed issue with used-defined type functions not being able to call each other * Infer types of globals under new type solver ## Fragment Autocomplete * Miscellaneous fixes to make interop with the old solver better ## Runtime * Support disabling specific built-in functions from being fast-called or constant-evaluated (Closes #1538) * New compiler option `disabledBuiltins` accepts a list of library function names like "tonumber" or "math.cos" * Added constant folding for vector arithmetic * Added constant propagation and type inference for vector globals (Fixes #1511) * New compiler option `librariesWithKnownMembers` accepts a list of libraries for members of which a request for constant value and/or type will be made * `libraryMemberTypeCb` callback is called to get the type of a global, return one of the `LuauBytecodeType` values. 'boolean', 'number', 'string' and 'vector' type are supported. * `libraryMemberConstantCb` callback is called to setup the constant value of a global. To set a value, C API `luau_set_compile_constant_*` or C++ API `setCompileConstant*` functions should be used. --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@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> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Andrew Miranti <amiranti@roblox.com> Co-authored-by: Shiqi Ai <sai@roblox.com> Co-authored-by: Yohoo Lin <yohoo@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
This commit is contained in:
parent
8b8118b027
commit
2e6fdd90a0
170 changed files with 2562 additions and 904 deletions
|
@ -53,7 +53,7 @@ LUAU_EQSAT_NODE_SET(Intersection);
|
|||
|
||||
LUAU_EQSAT_NODE_ARRAY(Negation, 1);
|
||||
|
||||
LUAU_EQSAT_NODE_ATOM_WITH_VECTOR(TTypeFun, const TypeFunction*);
|
||||
LUAU_EQSAT_NODE_ATOM_WITH_VECTOR(TTypeFun, std::shared_ptr<const TypeFunctionInstanceType>);
|
||||
|
||||
LUAU_EQSAT_UNIT(TNoRefine);
|
||||
LUAU_EQSAT_UNIT(Invalid);
|
||||
|
@ -218,6 +218,7 @@ struct Simplifier
|
|||
void simplifyUnion(Id id);
|
||||
void uninhabitedIntersection(Id id);
|
||||
void intersectWithNegatedClass(Id id);
|
||||
void intersectWithNegatedAtom(Id id);
|
||||
void intersectWithNoRefine(Id id);
|
||||
void cyclicIntersectionOfUnion(Id id);
|
||||
void cyclicUnionOfIntersection(Id id);
|
||||
|
@ -228,6 +229,7 @@ struct Simplifier
|
|||
void unneededTableModification(Id id);
|
||||
void builtinTypeFunctions(Id id);
|
||||
void iffyTypeFunctions(Id id);
|
||||
void strictMetamethods(Id id);
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
|
|
|
@ -15,6 +15,7 @@ struct TypeCheckLimits;
|
|||
|
||||
void checkNonStrict(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
|
|
|
@ -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/EqSatSimplification.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
|
@ -21,8 +22,22 @@ struct Scope;
|
|||
|
||||
using ModulePtr = std::shared_ptr<Module>;
|
||||
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
bool isSubtype(
|
||||
TypeId subTy,
|
||||
TypeId superTy,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
InternalErrorReporter& ice
|
||||
);
|
||||
bool isSubtype(
|
||||
TypePackId subPack,
|
||||
TypePackId superPack,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
InternalErrorReporter& ice
|
||||
);
|
||||
|
||||
class TypeIds
|
||||
{
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/InsertionOrderedMap.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/InsertionOrderedMap.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -34,6 +35,7 @@ struct OverloadResolver
|
|||
OverloadResolver(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<Scope> scope,
|
||||
|
@ -44,6 +46,7 @@ struct OverloadResolver
|
|||
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<Normalizer> normalizer;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
NotNull<Scope> scope;
|
||||
|
@ -110,6 +113,7 @@ struct SolveResult
|
|||
SolveResult solveFunctionCall(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> iceReporter,
|
||||
|
|
|
@ -85,6 +85,10 @@ struct Scope
|
|||
void inheritAssignments(const ScopePtr& childScope);
|
||||
void inheritRefinements(const ScopePtr& childScope);
|
||||
|
||||
// Track globals that should emit warnings during type checking.
|
||||
DenseHashSet<std::string> globalsToWarn{""};
|
||||
bool shouldWarnGlobal(std::string name) const;
|
||||
|
||||
// For mutually recursive type aliases, it's important that
|
||||
// they use the same types for the same names.
|
||||
// For instance, in `type Tree<T> { data: T, children: Forest<T> } type Forest<T> = {Tree<T>}`
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/TypePairHash.h"
|
||||
#include "Luau/TypePath.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
@ -134,6 +135,7 @@ struct Subtyping
|
|||
{
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<Normalizer> normalizer;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
NotNull<InternalErrorReporter> iceReporter;
|
||||
|
@ -155,6 +157,7 @@ struct Subtyping
|
|||
Subtyping(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> typeArena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> iceReporter
|
||||
|
|
|
@ -608,7 +608,8 @@ struct UserDefinedFunctionData
|
|||
// References to AST elements are owned by the Module allocator which also stores this type
|
||||
AstStatTypeFunction* definition = nullptr;
|
||||
|
||||
DenseHashMap<Name, AstStatTypeFunction*> environment{""};
|
||||
DenseHashMap<Name, std::pair<AstStatTypeFunction*, size_t>> environment{""};
|
||||
DenseHashMap<Name, AstStatTypeFunction*> environment_DEPRECATED{""};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/TypeOrPack.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -60,6 +61,7 @@ struct Reasonings
|
|||
|
||||
void check(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<UnifierSharedState> sharedState,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
|
@ -71,6 +73,7 @@ void check(
|
|||
struct TypeChecker2
|
||||
{
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
DcrLogger* logger;
|
||||
const NotNull<TypeCheckLimits> limits;
|
||||
|
@ -90,6 +93,7 @@ struct TypeChecker2
|
|||
|
||||
TypeChecker2(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
|
@ -213,6 +217,9 @@ private:
|
|||
std::vector<TypeError>& errors
|
||||
);
|
||||
|
||||
// Avoid duplicate warnings being emitted for the same global variable.
|
||||
DenseHashSet<std::string> warnedGlobals{""};
|
||||
|
||||
void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const;
|
||||
bool isErrorSuppressing(Location loc, TypeId ty);
|
||||
bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
|
@ -41,9 +42,15 @@ struct TypeFunctionRuntime
|
|||
|
||||
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;
|
||||
|
||||
// Output created by 'print' function
|
||||
std::vector<std::string> messages;
|
||||
|
||||
private:
|
||||
void prepareState();
|
||||
};
|
||||
|
@ -53,6 +60,7 @@ struct TypeFunctionContext
|
|||
NotNull<TypeArena> arena;
|
||||
NotNull<BuiltinTypes> builtins;
|
||||
NotNull<Scope> scope;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<Normalizer> normalizer;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
NotNull<InternalErrorReporter> ice;
|
||||
|
@ -71,6 +79,7 @@ struct TypeFunctionContext
|
|||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtins,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
|
@ -79,6 +88,7 @@ struct TypeFunctionContext
|
|||
: arena(arena)
|
||||
, builtins(builtins)
|
||||
, scope(scope)
|
||||
, simplifier(simplifier)
|
||||
, normalizer(normalizer)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, ice(ice)
|
||||
|
@ -91,19 +101,31 @@ struct TypeFunctionContext
|
|||
NotNull<Constraint> pushConstraint(ConstraintV&& c) const;
|
||||
};
|
||||
|
||||
enum class Reduction
|
||||
{
|
||||
// The type function is either known to be reducible or the determination is blocked.
|
||||
MaybeOk,
|
||||
// The type function is known to be irreducible, but maybe not be erroneous, e.g. when it's over generics or free types.
|
||||
Irreducible,
|
||||
// The type function is known to be irreducible, and is definitely erroneous.
|
||||
Erroneous,
|
||||
};
|
||||
|
||||
/// Represents a reduction result, which may have successfully reduced the type,
|
||||
/// may have concretely failed to reduce the type, or may simply be stuck
|
||||
/// without more information.
|
||||
template<typename Ty>
|
||||
struct TypeFunctionReductionResult
|
||||
{
|
||||
|
||||
/// The result of the reduction, if any. If this is nullopt, the type function
|
||||
/// could not be reduced.
|
||||
std::optional<Ty> result;
|
||||
/// Whether the result is uninhabited: whether we know, unambiguously and
|
||||
/// permanently, whether this type function reduction results in an
|
||||
/// uninhabitable type. This will trigger an error to be reported.
|
||||
bool uninhabited;
|
||||
/// Indicates the status of this reduction: is `Reduction::Irreducible` if
|
||||
/// the this result indicates the type function is irreducible, and
|
||||
/// `Reduction::Erroneous` if this result indicates the type function is
|
||||
/// erroneous. `Reduction::MaybeOk` otherwise.
|
||||
Reduction reductionStatus;
|
||||
/// Any types that need to be progressed or mutated before the reduction may
|
||||
/// proceed.
|
||||
std::vector<TypeId> blockedTypes;
|
||||
|
@ -112,6 +134,8 @@ struct TypeFunctionReductionResult
|
|||
std::vector<TypePackId> blockedPacks;
|
||||
/// A runtime error message from user-defined type functions
|
||||
std::optional<std::string> error;
|
||||
/// Messages printed out from user-defined type functions
|
||||
std::vector<std::string> messages;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -145,6 +169,7 @@ struct TypePackFunction
|
|||
struct FunctionGraphReductionResult
|
||||
{
|
||||
ErrorVec errors;
|
||||
ErrorVec messages;
|
||||
DenseHashSet<TypeId> blockedTypes{nullptr};
|
||||
DenseHashSet<TypePackId> blockedPacks{nullptr};
|
||||
DenseHashSet<TypeId> reducedTypes{nullptr};
|
||||
|
|
|
@ -150,6 +150,7 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
|||
{
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
|
||||
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -162,7 +163,9 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
|||
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||
|
||||
Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}};
|
||||
Subtyping subtyping{
|
||||
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
|
||||
};
|
||||
|
||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,15 @@ LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
|||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunExportedAndLocal)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNoExtraConstraint)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(InferGlobalTypes)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -819,9 +823,10 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
}
|
||||
|
||||
// Fill it with all visible type functions
|
||||
if (mainTypeFun)
|
||||
if (FFlag::LuauUserTypeFunUpdateAllEnvs && mainTypeFun)
|
||||
{
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
size_t level = 0;
|
||||
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
{
|
||||
|
@ -831,7 +836,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
continue;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment[name] = ty->userFuncData.definition;
|
||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||
}
|
||||
|
||||
for (auto& [name, tf] : curr->exportedTypeBindings)
|
||||
|
@ -840,7 +845,34 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
continue;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment[name] = ty->userFuncData.definition;
|
||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||
}
|
||||
|
||||
level++;
|
||||
}
|
||||
}
|
||||
else if (mainTypeFun)
|
||||
{
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
{
|
||||
for (auto& [name, tf] : curr->privateTypeBindings)
|
||||
{
|
||||
if (userFuncData.environment_DEPRECATED.find(name))
|
||||
continue;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
|
||||
}
|
||||
|
||||
for (auto& [name, tf] : curr->exportedTypeBindings)
|
||||
{
|
||||
if (userFuncData.environment_DEPRECATED.find(name))
|
||||
continue;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1571,18 +1603,22 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeAlias*
|
|||
|
||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunction* function)
|
||||
{
|
||||
// If a type function with the same name was already defined, we skip over
|
||||
auto bindingIt = scope->privateTypeBindings.find(function->name.value);
|
||||
if (bindingIt == scope->privateTypeBindings.end())
|
||||
return ControlFlow::None;
|
||||
|
||||
TypeFun typeFunction = bindingIt->second;
|
||||
|
||||
// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
|
||||
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(follow(typeFunction.type)))
|
||||
if (!FFlag::LuauUserTypeFunNoExtraConstraint)
|
||||
{
|
||||
TypeId expansionTy = arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
|
||||
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
|
||||
// If a type function with the same name was already defined, we skip over
|
||||
auto bindingIt = scope->privateTypeBindings.find(function->name.value);
|
||||
if (bindingIt == scope->privateTypeBindings.end())
|
||||
return ControlFlow::None;
|
||||
|
||||
TypeFun typeFunction = bindingIt->second;
|
||||
|
||||
// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
|
||||
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(follow(typeFunction.type)))
|
||||
{
|
||||
TypeId expansionTy =
|
||||
arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
|
||||
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
|
||||
}
|
||||
}
|
||||
|
||||
return ControlFlow::None;
|
||||
|
@ -2790,6 +2826,14 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* glob
|
|||
DefId def = dfg->getDef(global);
|
||||
rootScope->lvalueTypes[def] = rhsType;
|
||||
|
||||
if (FFlag::InferGlobalTypes)
|
||||
{
|
||||
// Sketchy: We're specifically looking for BlockedTypes that were
|
||||
// initially created by ConstraintGenerator::prepopulateGlobalScope.
|
||||
if (auto bt = get<BlockedType>(follow(*annotatedTy)); bt && !bt->getOwner())
|
||||
emplaceType<BoundType>(asMutable(*annotatedTy), rhsType);
|
||||
}
|
||||
|
||||
addConstraint(scope, global->location, SubtypeConstraint{rhsType, *annotatedTy});
|
||||
}
|
||||
}
|
||||
|
@ -3182,9 +3226,9 @@ TypeId ConstraintGenerator::resolveReferenceType(
|
|||
|
||||
if (alias.has_value())
|
||||
{
|
||||
// If the alias is not generic, we don't need to set up a blocked
|
||||
// type and an instantiation constraint.
|
||||
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty())
|
||||
// If the alias is not generic, we don't need to set up a blocked type and an instantiation constraint
|
||||
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty() &&
|
||||
(!FFlag::LuauUserTypeFunNoExtraConstraint || !ref->hasParameterList))
|
||||
{
|
||||
result = alias->type;
|
||||
}
|
||||
|
@ -3694,6 +3738,26 @@ struct GlobalPrepopulator : AstVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatAssign* assign) override
|
||||
{
|
||||
if (FFlag::InferGlobalTypes)
|
||||
{
|
||||
for (const Luau::AstExpr* expr : assign->vars)
|
||||
{
|
||||
if (const AstExprGlobal* g = expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (!globalScope->lookup(g->name))
|
||||
globalScope->globalsToWarn.insert(g->name.value);
|
||||
|
||||
TypeId bt = arena->addType(BlockedType{});
|
||||
globalScope->bindings[g->name] = Binding{bt, g->location};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatFunction* function) override
|
||||
{
|
||||
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
||||
|
|
|
@ -35,6 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack)
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -939,12 +940,14 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||
if (auto typeFn = get<TypeFunctionInstanceType>(follow(tf->type)))
|
||||
pushConstraint(NotNull(constraint->scope.get()), constraint->location, ReduceConstraint{tf->type});
|
||||
|
||||
// If there are no parameters to the type function we can just use the type
|
||||
// directly.
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
if (!FFlag::LuauUserTypeFunNoExtraConstraint)
|
||||
{
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
// If there are no parameters to the type function we can just use the type directly
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
{
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Due to how pending expansion types and TypeFun's are created
|
||||
|
@ -959,6 +962,16 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||
return true;
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunNoExtraConstraint)
|
||||
{
|
||||
// If there are no parameters to the type function we can just use the type directly
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
{
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
auto [typeArguments, packArguments] = saturateArguments(arena, builtinTypes, *tf, petv->typeArguments, petv->packArguments);
|
||||
|
||||
bool sameTypes = std::equal(
|
||||
|
@ -1263,6 +1276,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
|||
OverloadResolver resolver{
|
||||
builtinTypes,
|
||||
NotNull{arena},
|
||||
simplifier,
|
||||
normalizer,
|
||||
typeFunctionRuntime,
|
||||
constraint->scope,
|
||||
|
@ -2103,6 +2117,11 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
|
|||
|
||||
if (force || reductionFinished)
|
||||
{
|
||||
for (auto& message : result.messages)
|
||||
{
|
||||
reportError(std::move(message));
|
||||
}
|
||||
|
||||
// if we're completely dispatching this constraint, we want to record any uninhabited type functions to unblock.
|
||||
for (auto error : result.errors)
|
||||
{
|
||||
|
|
|
@ -143,6 +143,62 @@ static bool isTerminal(const EType& node)
|
|||
node.get<TNever>() || node.get<TNoRefine>();
|
||||
}
|
||||
|
||||
static bool areTerminalAndDefinitelyDisjoint(const EType& lhs, const EType& rhs)
|
||||
{
|
||||
// If either node is non-terminal, then we early exit: we're not going to
|
||||
// do a state space search for whether something like:
|
||||
// (A | B | C | D) & (E | F | G | H)
|
||||
// ... is a disjoint intersection.
|
||||
if (!isTerminal(lhs) || !isTerminal(rhs))
|
||||
return false;
|
||||
|
||||
// Special case some types that aren't strict, disjoint subsets.
|
||||
if (lhs.get<TTopClass>() || lhs.get<TClass>())
|
||||
return !(rhs.get<TTopClass>() || rhs.get<TClass>());
|
||||
|
||||
// Handling strings / booleans: these are the types for which we
|
||||
// expect something like:
|
||||
//
|
||||
// "foo" & ~"bar"
|
||||
//
|
||||
// ... to simplify to "foo".
|
||||
if (lhs.get<TString>())
|
||||
return !(rhs.get<TString>() || rhs.get<SString>());
|
||||
|
||||
if (lhs.get<TBoolean>())
|
||||
return !(rhs.get<TBoolean>() || rhs.get<SBoolean>());
|
||||
|
||||
if (auto lhsSString = lhs.get<SString>())
|
||||
{
|
||||
auto rhsSString = rhs.get<SString>();
|
||||
if (!rhsSString)
|
||||
return !rhs.get<TString>();
|
||||
return lhsSString->value() != rhsSString->value();
|
||||
}
|
||||
|
||||
if (auto lhsSBoolean = lhs.get<SBoolean>())
|
||||
{
|
||||
auto rhsSBoolean = rhs.get<SBoolean>();
|
||||
if (!rhsSBoolean)
|
||||
return !rhs.get<TBoolean>();
|
||||
return lhsSBoolean->value() != rhsSBoolean->value();
|
||||
}
|
||||
|
||||
// At this point:
|
||||
// - We know both nodes are terminal
|
||||
// - We know that the LHS is not any boolean, string, or class
|
||||
// At this point, we have two classes of checks left:
|
||||
// - Whether the two enodes are exactly the same set (now that the static
|
||||
// sets have been covered).
|
||||
// - Whether one of the enodes is a large semantic set such as TAny,
|
||||
// TUnknown, or TError.
|
||||
return !(
|
||||
lhs.index() == rhs.index() ||
|
||||
lhs.get<TUnknown>() || rhs.get<TUnknown>() || lhs.get<TAny>() || rhs.get<TAny>() || lhs.get<TNoRefine>() || rhs.get<TNoRefine>() ||
|
||||
lhs.get<TError>() || rhs.get<TError>() || lhs.get<TOpaque>() || rhs.get<TOpaque>()
|
||||
);
|
||||
}
|
||||
|
||||
static bool isTerminal(const EGraph& egraph, Id eclass)
|
||||
{
|
||||
const auto& nodes = egraph[eclass].nodes;
|
||||
|
@ -336,10 +392,20 @@ Id toId(
|
|||
LUAU_ASSERT(tfun->packArguments.empty());
|
||||
|
||||
std::vector<Id> parts;
|
||||
parts.reserve(tfun->typeArguments.size());
|
||||
for (TypeId part : tfun->typeArguments)
|
||||
parts.push_back(toId(egraph, builtinTypes, mappingIdToClass, typeToMappingId, boundNodes, strings, part));
|
||||
|
||||
return cache(egraph.add(TTypeFun{tfun->function.get(), std::move(parts)}));
|
||||
// This looks sily, but we're making a copy of the specific
|
||||
// `TypeFunctionInstanceType` outside of the provided arena so that
|
||||
// we can access the members without fear of the specific TFIT being
|
||||
// overwritten with a bound type.
|
||||
return cache(egraph.add(TTypeFun{
|
||||
std::make_shared<const TypeFunctionInstanceType>(
|
||||
tfun->function, tfun->typeArguments, tfun->packArguments, tfun->userFuncName, tfun->userFuncData
|
||||
),
|
||||
std::move(parts)
|
||||
}));
|
||||
}
|
||||
else if (get<NoRefineType>(ty))
|
||||
return egraph.add(TNoRefine{});
|
||||
|
@ -574,18 +640,24 @@ TypeId flattenTableNode(
|
|||
// If a TTable is its own basis, it must be the case that some other
|
||||
// node on this eclass is a TImportedTable. Let's use that.
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (size_t i = 0; i < eclass.nodes.size(); ++i)
|
||||
{
|
||||
if (eclass.nodes[i].get<TImportedTable>())
|
||||
{
|
||||
found = true;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find one, we don't know what to do. Use ErrorType.
|
||||
LUAU_ASSERT(0);
|
||||
return builtinTypes->errorType;
|
||||
if (!found)
|
||||
{
|
||||
// If we couldn't find one, we don't know what to do. Use ErrorType.
|
||||
LUAU_ASSERT(0);
|
||||
return builtinTypes->errorType;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& node = eclass.nodes[index];
|
||||
|
@ -703,7 +775,20 @@ TypeId fromId(
|
|||
if (parts.empty())
|
||||
return builtinTypes->neverType;
|
||||
else if (parts.size() == 1)
|
||||
return fromId(egraph, strings, builtinTypes, arena, bestNodes, seen, newTypeFunctions, parts[0]);
|
||||
{
|
||||
TypeId placeholder = arena->addType(BlockedType{});
|
||||
seen[rootId] = placeholder;
|
||||
auto result = fromId(egraph, strings, builtinTypes, arena, bestNodes, seen, newTypeFunctions, parts[0]);
|
||||
if (follow(result) == placeholder)
|
||||
{
|
||||
emplaceType<GenericType>(asMutable(placeholder), "EGRAPH-SINGLETON-CYCLE");
|
||||
}
|
||||
else
|
||||
{
|
||||
emplaceType<BoundType>(asMutable(placeholder), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId res = arena->addType(BlockedType{});
|
||||
|
@ -768,7 +853,11 @@ TypeId fromId(
|
|||
for (Id part : tfun->operands())
|
||||
args.push_back(fromId(egraph, strings, builtinTypes, arena, bestNodes, seen, newTypeFunctions, part));
|
||||
|
||||
asMutable(res)->ty.emplace<TypeFunctionInstanceType>(*tfun->value(), std::move(args));
|
||||
auto oldInstance = tfun->value();
|
||||
|
||||
asMutable(res)->ty.emplace<TypeFunctionInstanceType>(
|
||||
oldInstance->function, std::move(args), std::vector<TypePackId>(), oldInstance->userFuncName, oldInstance->userFuncData
|
||||
);
|
||||
|
||||
newTypeFunctions.push_back(res);
|
||||
|
||||
|
@ -906,7 +995,7 @@ static std::string getNodeName(const StringCache& strings, const EType& node)
|
|||
else if (node.get<TNever>())
|
||||
return "never";
|
||||
else if (auto tfun = node.get<TTypeFun>())
|
||||
return "tfun " + tfun->value()->name;
|
||||
return "tfun " + tfun->value()->function->name;
|
||||
else if (node.get<Negation>())
|
||||
return "~";
|
||||
else if (node.get<Invalid>())
|
||||
|
@ -1552,11 +1641,6 @@ std::optional<EType> intersectOne(EGraph& egraph, Id hereId, const EType* hereNo
|
|||
thereNode->get<Intersection>() || thereNode->get<Negation>() || hereNode->get<TOpaque>() || thereNode->get<TOpaque>())
|
||||
return std::nullopt;
|
||||
|
||||
if (hereNode->get<TAny>())
|
||||
return *thereNode;
|
||||
if (thereNode->get<TAny>())
|
||||
return *hereNode;
|
||||
|
||||
if (hereNode->get<TUnknown>())
|
||||
return *thereNode;
|
||||
if (thereNode->get<TUnknown>())
|
||||
|
@ -1839,6 +1923,74 @@ void Simplifier::intersectWithNegatedClass(Id id)
|
|||
}
|
||||
}
|
||||
|
||||
void Simplifier::intersectWithNegatedAtom(Id id)
|
||||
{
|
||||
// Let I and ~J be two arbitrary distinct operands of an intersection where
|
||||
// I and J are terminal but are not type variables. (free, generic, or
|
||||
// otherwise opaque)
|
||||
//
|
||||
// If I and J are equal, then the whole intersection is equivalent to never.
|
||||
//
|
||||
// If I and J are inequal, then J & ~I == J
|
||||
|
||||
for (const auto [intersection, intersectionIndex] : Query<Intersection>(&egraph, id))
|
||||
{
|
||||
const Slice<const Id>& intersectionOperands = intersection->operands();
|
||||
for (size_t i = 0; i < intersectionOperands.size(); ++i)
|
||||
{
|
||||
for (const auto [negation, negationIndex] : Query<Negation>(&egraph, intersectionOperands[i]))
|
||||
{
|
||||
for (size_t negationOperandIndex = 0; negationOperandIndex < egraph[negation->operands()[0]].nodes.size(); ++negationOperandIndex)
|
||||
{
|
||||
const EType* negationOperand = &egraph[negation->operands()[0]].nodes[negationOperandIndex];
|
||||
if (!isTerminal(*negationOperand) || negationOperand->get<TOpaque>())
|
||||
continue;
|
||||
|
||||
for (size_t j = 0; j < intersectionOperands.size(); ++j)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
|
||||
for (size_t jNodeIndex = 0; jNodeIndex < egraph[intersectionOperands[j]].nodes.size(); ++jNodeIndex)
|
||||
{
|
||||
const EType* jNode = &egraph[intersectionOperands[j]].nodes[jNodeIndex];
|
||||
if (!isTerminal(*jNode) || jNode->get<TOpaque>())
|
||||
continue;
|
||||
|
||||
if (*negationOperand == *jNode)
|
||||
{
|
||||
// eg "Hello" & ~"Hello"
|
||||
// or boolean & ~boolean
|
||||
subst(
|
||||
id,
|
||||
egraph.add(TNever{}),
|
||||
"intersectWithNegatedAtom",
|
||||
{{id, intersectionIndex}, {intersectionOperands[i], negationIndex}, {intersectionOperands[j], jNodeIndex}}
|
||||
);
|
||||
return;
|
||||
}
|
||||
else if (areTerminalAndDefinitelyDisjoint(*jNode, *negationOperand))
|
||||
{
|
||||
// eg "Hello" & ~"World"
|
||||
// or boolean & ~string
|
||||
std::vector<Id> newOperands(intersectionOperands.begin(), intersectionOperands.end());
|
||||
newOperands.erase(newOperands.begin() + std::vector<Id>::difference_type(i));
|
||||
|
||||
subst(
|
||||
id,
|
||||
egraph.add(Intersection{newOperands}),
|
||||
"intersectWithNegatedAtom",
|
||||
{{id, intersectionIndex}, {intersectionOperands[i], negationIndex}, {intersectionOperands[j], jNodeIndex}}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Simplifier::intersectWithNoRefine(Id id)
|
||||
{
|
||||
for (const auto pair : Query<Intersection>(&egraph, id))
|
||||
|
@ -2160,7 +2312,7 @@ void Simplifier::intersectTableProperty(Id id)
|
|||
|
||||
subst(
|
||||
id,
|
||||
egraph.add(Intersection{std::move(newIntersectionParts)}),
|
||||
mkIntersection(egraph, std::move(newIntersectionParts)),
|
||||
"intersectTableProperty",
|
||||
{{id, intersectionIndex}, {iId, table1Index}, {jId, table2Index}}
|
||||
);
|
||||
|
@ -2250,7 +2402,7 @@ void Simplifier::builtinTypeFunctions(Id id)
|
|||
if (args.size() != 2)
|
||||
continue;
|
||||
|
||||
const std::string& name = tfun->value()->name;
|
||||
const std::string& name = tfun->value()->function->name;
|
||||
if (name == "add" || name == "sub" || name == "mul" || name == "div" || name == "idiv" || name == "pow" || name == "mod")
|
||||
{
|
||||
if (isTag<TNumber>(args[0]) && isTag<TNumber>(args[1]))
|
||||
|
@ -2272,15 +2424,43 @@ void Simplifier::iffyTypeFunctions(Id id)
|
|||
{
|
||||
const Slice<const Id>& args = tfun->operands();
|
||||
|
||||
const std::string& name = tfun->value()->name;
|
||||
const std::string& name = tfun->value()->function->name;
|
||||
|
||||
if (name == "union")
|
||||
subst(id, add(Union{std::vector(args.begin(), args.end())}), "iffyTypeFunctions", {{id, index}});
|
||||
else if (name == "intersect" || name == "refine")
|
||||
else if (name == "intersect")
|
||||
subst(id, add(Intersection{std::vector(args.begin(), args.end())}), "iffyTypeFunctions", {{id, index}});
|
||||
}
|
||||
}
|
||||
|
||||
// Replace instances of `lt<X, Y>` and `le<X, Y>` when either X or Y is `number`
|
||||
// or `string` with `boolean`. Lua semantics are that if we see the expression:
|
||||
//
|
||||
// x < y
|
||||
//
|
||||
// ... we error if `x` and `y` don't have the same type. We know that for
|
||||
// `string` and `number`, comparisons will always return a boolean. So if either
|
||||
// of the arguments to `lt<>` are equivalent to `number` or `string`, then the
|
||||
// type is effectively `boolean`: either the other type is equivalent, in which
|
||||
// case we eval to `boolean`, or we diverge (raise an error).
|
||||
void Simplifier::strictMetamethods(Id id)
|
||||
{
|
||||
for (const auto [tfun, index] : Query<TTypeFun>(&egraph, id))
|
||||
{
|
||||
const Slice<const Id>& args = tfun->operands();
|
||||
|
||||
const std::string& name = tfun->value()->function->name;
|
||||
|
||||
if (!(name == "lt" || name == "le") || args.size() != 2)
|
||||
continue;
|
||||
|
||||
if (isTag<TNumber>(args[0]) || isTag<TString>(args[0]) || isTag<TNumber>(args[1]) || isTag<TString>(args[1]))
|
||||
{
|
||||
subst(id, add(TBoolean{}), __FUNCTION__, {{id, index}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteSimplifier(Simplifier* s)
|
||||
{
|
||||
delete s;
|
||||
|
@ -2308,6 +2488,7 @@ std::optional<EqSatSimplificationResult> eqSatSimplify(NotNull<Simplifier> simpl
|
|||
&Simplifier::simplifyUnion,
|
||||
&Simplifier::uninhabitedIntersection,
|
||||
&Simplifier::intersectWithNegatedClass,
|
||||
&Simplifier::intersectWithNegatedAtom,
|
||||
&Simplifier::intersectWithNoRefine,
|
||||
&Simplifier::cyclicIntersectionOfUnion,
|
||||
&Simplifier::cyclicUnionOfIntersection,
|
||||
|
@ -2318,6 +2499,7 @@ std::optional<EqSatSimplificationResult> eqSatSimplify(NotNull<Simplifier> simpl
|
|||
&Simplifier::unneededTableModification,
|
||||
&Simplifier::builtinTypeFunctions,
|
||||
&Simplifier::iffyTypeFunctions,
|
||||
&Simplifier::strictMetamethods,
|
||||
};
|
||||
|
||||
std::unordered_set<Id> seen;
|
||||
|
|
|
@ -50,6 +50,8 @@ LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
|||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauReferenceAllocatorInNewSolver)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -1317,6 +1319,11 @@ ModulePtr check(
|
|||
result->mode = mode;
|
||||
result->internalTypes.owningModule = result.get();
|
||||
result->interfaceTypes.owningModule = result.get();
|
||||
if (FFlag::LuauReferenceAllocatorInNewSolver)
|
||||
{
|
||||
result->allocator = sourceModule.allocator;
|
||||
result->names = sourceModule.names;
|
||||
}
|
||||
|
||||
iceHandler->moduleName = sourceModule.name;
|
||||
|
||||
|
@ -1427,6 +1434,7 @@ ModulePtr check(
|
|||
case Mode::Nonstrict:
|
||||
Luau::checkNonStrict(
|
||||
builtinTypes,
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
iceHandler,
|
||||
NotNull{&unifierState},
|
||||
|
@ -1440,7 +1448,14 @@ ModulePtr check(
|
|||
// fallthrough intentional
|
||||
case Mode::Strict:
|
||||
Luau::check(
|
||||
builtinTypes, NotNull{&typeFunctionRuntime}, NotNull{&unifierState}, NotNull{&limits}, logger.get(), sourceModule, result.get()
|
||||
builtinTypes,
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull{&unifierState},
|
||||
NotNull{&limits},
|
||||
logger.get(),
|
||||
sourceModule,
|
||||
result.get()
|
||||
);
|
||||
break;
|
||||
case Mode::NoCheck:
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGeneralizationRemoveRecursiveUpperBound)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct MutatingGeneralizer : TypeOnceVisitor
|
||||
{
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
|
||||
NotNull<Scope> scope;
|
||||
|
@ -29,6 +31,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
|||
bool avoidSealingTables = false;
|
||||
|
||||
MutatingGeneralizer(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||
|
@ -37,6 +40,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
|||
bool avoidSealingTables
|
||||
)
|
||||
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
||||
, arena(arena)
|
||||
, builtinTypes(builtinTypes)
|
||||
, scope(scope)
|
||||
, cachedTypes(cachedTypes)
|
||||
|
@ -229,6 +233,53 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
|||
else
|
||||
{
|
||||
TypeId ub = follow(ft->upperBound);
|
||||
if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound)
|
||||
{
|
||||
|
||||
// If the upper bound is a union type or an intersection type,
|
||||
// and one of it's members is the free type we're
|
||||
// generalizing, don't include it in the upper bound. For a
|
||||
// free type such as:
|
||||
//
|
||||
// t1 where t1 = D <: 'a <: (A | B | C | t1)
|
||||
//
|
||||
// Naively replacing it with it's upper bound creates:
|
||||
//
|
||||
// t1 where t1 = A | B | C | t1
|
||||
//
|
||||
// It makes sense to just optimize this and exclude the
|
||||
// recursive component by semantic subtyping rules.
|
||||
|
||||
if (auto itv = get<IntersectionType>(ub))
|
||||
{
|
||||
std::vector<TypeId> newIds;
|
||||
newIds.reserve(itv->parts.size());
|
||||
for (auto part : itv)
|
||||
{
|
||||
if (part != ty)
|
||||
newIds.push_back(part);
|
||||
}
|
||||
if (newIds.size() == 1)
|
||||
ub = newIds[0];
|
||||
else if (newIds.size() > 0)
|
||||
ub = arena->addType(IntersectionType{std::move(newIds)});
|
||||
}
|
||||
else if (auto utv = get<UnionType>(ub))
|
||||
{
|
||||
std::vector<TypeId> newIds;
|
||||
newIds.reserve(utv->options.size());
|
||||
for (auto part : utv)
|
||||
{
|
||||
if (part != ty)
|
||||
newIds.push_back(part);
|
||||
}
|
||||
if (newIds.size() == 1)
|
||||
ub = newIds[0];
|
||||
else if (newIds.size() > 0)
|
||||
ub = arena->addType(UnionType{std::move(newIds)});
|
||||
}
|
||||
}
|
||||
|
||||
if (FreeType* upperFree = getMutable<FreeType>(ub); upperFree && upperFree->lowerBound == ty)
|
||||
upperFree->lowerBound = builtinTypes->neverType;
|
||||
else
|
||||
|
@ -969,7 +1020,7 @@ std::optional<TypeId> generalize(
|
|||
FreeTypeSearcher fts{scope, cachedTypes};
|
||||
fts.traverse(ty);
|
||||
|
||||
MutatingGeneralizer gen{builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes), avoidSealingTables};
|
||||
MutatingGeneralizer gen{arena, builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes), avoidSealingTables};
|
||||
|
||||
gen.traverse(ty);
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNonstrict)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCountSelfCallsNonstrict)
|
||||
|
||||
namespace Luau
|
||||
|
@ -158,6 +157,7 @@ private:
|
|||
struct NonStrictTypeChecker
|
||||
{
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
const NotNull<InternalErrorReporter> ice;
|
||||
NotNull<TypeArena> arena;
|
||||
|
@ -174,6 +174,7 @@ struct NonStrictTypeChecker
|
|||
NonStrictTypeChecker(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
const NotNull<InternalErrorReporter> ice,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
|
@ -182,12 +183,13 @@ struct NonStrictTypeChecker
|
|||
Module* module
|
||||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, simplifier(simplifier)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, ice(ice)
|
||||
, arena(arena)
|
||||
, module(module)
|
||||
, normalizer{arena, builtinTypes, unifierState, /* cache inhabitance */ true}
|
||||
, subtyping{builtinTypes, arena, NotNull(&normalizer), typeFunctionRuntime, ice}
|
||||
, subtyping{builtinTypes, arena, simplifier, NotNull(&normalizer), typeFunctionRuntime, ice}
|
||||
, dfg(dfg)
|
||||
, limits(limits)
|
||||
{
|
||||
|
@ -232,13 +234,14 @@ struct NonStrictTypeChecker
|
|||
if (noTypeFunctionErrors.find(instance))
|
||||
return instance;
|
||||
|
||||
ErrorVec errors = reduceTypeFunctions(
|
||||
instance,
|
||||
location,
|
||||
TypeFunctionContext{arena, builtinTypes, stack.back(), NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
|
||||
true
|
||||
)
|
||||
.errors;
|
||||
ErrorVec errors =
|
||||
reduceTypeFunctions(
|
||||
instance,
|
||||
location,
|
||||
TypeFunctionContext{arena, builtinTypes, stack.back(), simplifier, NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
|
||||
true
|
||||
)
|
||||
.errors;
|
||||
|
||||
if (errors.empty())
|
||||
noTypeFunctionErrors.insert(instance);
|
||||
|
@ -424,9 +427,6 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatTypeFunction* typeFunc)
|
||||
{
|
||||
if (!FFlag::LuauUserTypeFunNonstrict)
|
||||
reportError(GenericError{"This syntax is not supported"}, typeFunc->location);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -888,6 +888,7 @@ private:
|
|||
|
||||
void checkNonStrict(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
|
@ -899,7 +900,9 @@ void checkNonStrict(
|
|||
{
|
||||
LUAU_TIMETRACE_SCOPE("checkNonStrict", "Typechecking");
|
||||
|
||||
NonStrictTypeChecker typeChecker{NotNull{&module->internalTypes}, builtinTypes, typeFunctionRuntime, ice, unifierState, dfg, limits, module};
|
||||
NonStrictTypeChecker typeChecker{
|
||||
NotNull{&module->internalTypes}, builtinTypes, simplifier, typeFunctionRuntime, ice, unifierState, dfg, limits, module
|
||||
};
|
||||
typeChecker.visit(sourceModule.root);
|
||||
unfreeze(module->interfaceTypes);
|
||||
copyErrors(module->errors, module->interfaceTypes, builtinTypes);
|
||||
|
|
|
@ -3465,7 +3465,14 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
|||
return arena->addType(UnionType{std::move(result)});
|
||||
}
|
||||
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
||||
bool isSubtype(
|
||||
TypeId subTy,
|
||||
TypeId superTy,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
InternalErrorReporter& ice
|
||||
)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
TypeArena arena;
|
||||
|
@ -3478,7 +3485,7 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<Built
|
|||
// Subtyping under DCR is not implemented using unification!
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&ice}};
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, simplifier, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&ice}};
|
||||
|
||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||
}
|
||||
|
@ -3491,7 +3498,14 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<Built
|
|||
}
|
||||
}
|
||||
|
||||
bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
||||
bool isSubtype(
|
||||
TypePackId subPack,
|
||||
TypePackId superPack,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
InternalErrorReporter& ice
|
||||
)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
TypeArena arena;
|
||||
|
@ -3504,7 +3518,7 @@ bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, N
|
|||
// Subtyping under DCR is not implemented using unification!
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&ice}};
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, simplifier, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&ice}};
|
||||
|
||||
return subtyping.isSubtype(subPack, superPack, scope).isSubtype;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Luau
|
|||
OverloadResolver::OverloadResolver(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<Scope> scope,
|
||||
|
@ -25,12 +26,13 @@ OverloadResolver::OverloadResolver(
|
|||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, arena(arena)
|
||||
, simplifier(simplifier)
|
||||
, normalizer(normalizer)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, scope(scope)
|
||||
, ice(reporter)
|
||||
, limits(limits)
|
||||
, subtyping({builtinTypes, arena, normalizer, typeFunctionRuntime, ice})
|
||||
, subtyping({builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, ice})
|
||||
, callLoc(callLocation)
|
||||
{
|
||||
}
|
||||
|
@ -202,7 +204,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
|||
)
|
||||
{
|
||||
FunctionGraphReductionResult result = reduceTypeFunctions(
|
||||
fnTy, callLoc, TypeFunctionContext{arena, builtinTypes, scope, normalizer, typeFunctionRuntime, ice, limits}, /*force=*/true
|
||||
fnTy, callLoc, TypeFunctionContext{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, ice, limits}, /*force=*/true
|
||||
);
|
||||
if (!result.errors.empty())
|
||||
return {OverloadIsNonviable, result.errors};
|
||||
|
@ -404,9 +406,10 @@ void OverloadResolver::add(Analysis analysis, TypeId ty, ErrorVec&& errors)
|
|||
|
||||
// we wrap calling the overload resolver in a separate function to reduce overall stack pressure in `solveFunctionCall`.
|
||||
// this limits the lifetime of `OverloadResolver`, a large type, to only as long as it is actually needed.
|
||||
std::optional<TypeId> selectOverload(
|
||||
static std::optional<TypeId> selectOverload(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<Scope> scope,
|
||||
|
@ -417,7 +420,7 @@ std::optional<TypeId> selectOverload(
|
|||
TypePackId argsPack
|
||||
)
|
||||
{
|
||||
auto resolver = std::make_unique<OverloadResolver>(builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
|
||||
auto resolver = std::make_unique<OverloadResolver>(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
|
||||
auto [status, overload] = resolver->selectOverload(fn, argsPack);
|
||||
|
||||
if (status == OverloadResolver::Analysis::Ok)
|
||||
|
@ -432,6 +435,7 @@ std::optional<TypeId> selectOverload(
|
|||
SolveResult solveFunctionCall(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> iceReporter,
|
||||
|
@ -443,7 +447,7 @@ SolveResult solveFunctionCall(
|
|||
)
|
||||
{
|
||||
std::optional<TypeId> overloadToUse =
|
||||
selectOverload(builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location, fn, argsPack);
|
||||
selectOverload(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location, fn, argsPack);
|
||||
if (!overloadToUse)
|
||||
return {SolveResult::NoMatchingOverload};
|
||||
|
||||
|
|
|
@ -211,6 +211,16 @@ void Scope::inheritRefinements(const ScopePtr& childScope)
|
|||
}
|
||||
}
|
||||
|
||||
bool Scope::shouldWarnGlobal(std::string name) const
|
||||
{
|
||||
for (const Scope* current = this; current; current = current->parent.get())
|
||||
{
|
||||
if (current->globalsToWarn.contains(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool subsumesStrict(Scope* left, Scope* right)
|
||||
{
|
||||
while (right)
|
||||
|
|
|
@ -396,12 +396,14 @@ TypePackId* SubtypingEnvironment::getMappedPackBounds(TypePackId tp)
|
|||
Subtyping::Subtyping(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> typeArena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> iceReporter
|
||||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, arena(typeArena)
|
||||
, simplifier(simplifier)
|
||||
, normalizer(normalizer)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, iceReporter(iceReporter)
|
||||
|
@ -1861,7 +1863,7 @@ TypeId Subtyping::makeAggregateType(const Container& container, TypeId orElse)
|
|||
|
||||
std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull<Scope> scope)
|
||||
{
|
||||
TypeFunctionContext context{arena, builtinTypes, scope, normalizer, typeFunctionRuntime, iceReporter, NotNull{&limits}};
|
||||
TypeFunctionContext context{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, iceReporter, NotNull{&limits}};
|
||||
TypeId function = arena->addType(*functionInstance);
|
||||
FunctionGraphReductionResult result = reduceTypeFunctions(function, {}, context, true);
|
||||
ErrorVec errors;
|
||||
|
|
|
@ -386,8 +386,12 @@ public:
|
|||
}
|
||||
AstType* operator()(const NegationType& ntv)
|
||||
{
|
||||
// FIXME: do the same thing we do with ErrorType
|
||||
throw InternalCompilerError("Cannot convert NegationType into AstNode");
|
||||
AstArray<AstTypeOrPack> params;
|
||||
params.size = 1;
|
||||
params.data = static_cast<AstTypeOrPack*>(allocator->allocate(sizeof(AstType*)));
|
||||
params.data[0] = AstTypeOrPack{Luau::visit(*this, ntv.ty->ty), nullptr};
|
||||
|
||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("negate"), std::nullopt, Location(), true, params);
|
||||
}
|
||||
AstType* operator()(const TypeFunctionInstanceType& tfit)
|
||||
{
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAG(InferGlobalTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableKeysAreRValues)
|
||||
|
||||
namespace Luau
|
||||
|
@ -268,6 +269,7 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor
|
|||
|
||||
void check(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
|
@ -278,7 +280,7 @@ void check(
|
|||
{
|
||||
LUAU_TIMETRACE_SCOPE("check", "Typechecking");
|
||||
|
||||
TypeChecker2 typeChecker{builtinTypes, typeFunctionRuntime, unifierState, limits, logger, &sourceModule, module};
|
||||
TypeChecker2 typeChecker{builtinTypes, simplifier, typeFunctionRuntime, unifierState, limits, logger, &sourceModule, module};
|
||||
|
||||
typeChecker.visit(sourceModule.root);
|
||||
|
||||
|
@ -295,6 +297,7 @@ void check(
|
|||
|
||||
TypeChecker2::TypeChecker2(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
|
@ -303,6 +306,7 @@ TypeChecker2::TypeChecker2(
|
|||
Module* module
|
||||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, simplifier(simplifier)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, logger(logger)
|
||||
, limits(limits)
|
||||
|
@ -310,7 +314,7 @@ TypeChecker2::TypeChecker2(
|
|||
, sourceModule(sourceModule)
|
||||
, module(module)
|
||||
, normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true}
|
||||
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, typeFunctionRuntime, NotNull{unifierState->iceHandler}}
|
||||
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, simplifier, NotNull{&normalizer}, typeFunctionRuntime, NotNull{unifierState->iceHandler}}
|
||||
, subtyping(&_subtyping)
|
||||
{
|
||||
}
|
||||
|
@ -492,7 +496,9 @@ TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location l
|
|||
reduceTypeFunctions(
|
||||
instance,
|
||||
location,
|
||||
TypeFunctionContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
|
||||
TypeFunctionContext{
|
||||
NotNull{&module->internalTypes}, builtinTypes, stack.back(), simplifier, NotNull{&normalizer}, typeFunctionRuntime, ice, limits
|
||||
},
|
||||
true
|
||||
)
|
||||
.errors;
|
||||
|
@ -1349,7 +1355,17 @@ void TypeChecker2::visit(AstExprGlobal* expr)
|
|||
{
|
||||
NotNull<Scope> scope = stack.back();
|
||||
if (!scope->lookup(expr->name))
|
||||
{
|
||||
reportError(UnknownSymbol{expr->name.value, UnknownSymbol::Binding}, expr->location);
|
||||
}
|
||||
else if (FFlag::InferGlobalTypes)
|
||||
{
|
||||
if (scope->shouldWarnGlobal(expr->name.value) && !warnedGlobals.contains(expr->name.value))
|
||||
{
|
||||
reportError(UnknownSymbol{expr->name.value, UnknownSymbol::Binding}, expr->location);
|
||||
warnedGlobals.insert(expr->name.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstExprVarargs* expr)
|
||||
|
@ -1448,6 +1464,7 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
|||
OverloadResolver resolver{
|
||||
builtinTypes,
|
||||
NotNull{&module->internalTypes},
|
||||
simplifier,
|
||||
NotNull{&normalizer},
|
||||
typeFunctionRuntime,
|
||||
NotNull{stack.back()},
|
||||
|
@ -3024,7 +3041,7 @@ PropertyType TypeChecker2::hasIndexTypeFromType(
|
|||
{
|
||||
TypeId indexType = follow(tt->indexer->indexType);
|
||||
TypeId givenType = module->internalTypes.addType(SingletonType{StringSingleton{prop}});
|
||||
if (isSubtype(givenType, indexType, NotNull{module->getModuleScope().get()}, builtinTypes, *ice))
|
||||
if (isSubtype(givenType, indexType, NotNull{module->getModuleScope().get()}, builtinTypes, simplifier, *ice))
|
||||
return {NormalizationResult::True, {tt->indexer->indexResultType}};
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@
|
|||
#include <vector>
|
||||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixRegister)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunPrintToError)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunThreadBuffer)
|
||||
|
||||
|
@ -1408,8 +1408,6 @@ static int isEqualToType(lua_State* L)
|
|||
|
||||
void registerTypesLibrary(lua_State* L)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUserTypeFunFixRegister);
|
||||
|
||||
luaL_Reg fields[] = {
|
||||
{"unknown", createUnknown},
|
||||
{"never", createNever},
|
||||
|
@ -1464,170 +1462,99 @@ static int typeUserdataIndex(lua_State* L)
|
|||
|
||||
void registerTypeUserData(lua_State* L)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunFixRegister)
|
||||
{
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
{"is", checkTag},
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
{"is", checkTag},
|
||||
|
||||
// Negation type methods
|
||||
{"inner", getNegatedValue},
|
||||
// Negation type methods
|
||||
{"inner", getNegatedValue},
|
||||
|
||||
// Singleton type methods
|
||||
{"value", getSingletonValue},
|
||||
// Singleton type methods
|
||||
{"value", getSingletonValue},
|
||||
|
||||
// Table type methods
|
||||
{"setproperty", setTableProp},
|
||||
{"setreadproperty", setReadTableProp},
|
||||
{"setwriteproperty", setWriteTableProp},
|
||||
{"readproperty", readTableProp},
|
||||
{"writeproperty", writeTableProp},
|
||||
{"properties", getProps},
|
||||
{"setindexer", setTableIndexer},
|
||||
{"setreadindexer", setTableReadIndexer},
|
||||
{"setwriteindexer", setTableWriteIndexer},
|
||||
{"indexer", getIndexer},
|
||||
{"readindexer", getReadIndexer},
|
||||
{"writeindexer", getWriteIndexer},
|
||||
{"setmetatable", setTableMetatable},
|
||||
{"metatable", getMetatable},
|
||||
// Table type methods
|
||||
{"setproperty", setTableProp},
|
||||
{"setreadproperty", setReadTableProp},
|
||||
{"setwriteproperty", setWriteTableProp},
|
||||
{"readproperty", readTableProp},
|
||||
{"writeproperty", writeTableProp},
|
||||
{"properties", getProps},
|
||||
{"setindexer", setTableIndexer},
|
||||
{"setreadindexer", setTableReadIndexer},
|
||||
{"setwriteindexer", setTableWriteIndexer},
|
||||
{"indexer", getIndexer},
|
||||
{"readindexer", getReadIndexer},
|
||||
{"writeindexer", getWriteIndexer},
|
||||
{"setmetatable", setTableMetatable},
|
||||
{"metatable", getMetatable},
|
||||
|
||||
// Function type methods
|
||||
{"setparameters", setFunctionParameters},
|
||||
{"parameters", getFunctionParameters},
|
||||
{"setreturns", setFunctionReturns},
|
||||
{"returns", getFunctionReturns},
|
||||
// Function type methods
|
||||
{"setparameters", setFunctionParameters},
|
||||
{"parameters", getFunctionParameters},
|
||||
{"setreturns", setFunctionReturns},
|
||||
{"returns", getFunctionReturns},
|
||||
|
||||
// Union and Intersection type methods
|
||||
{"components", getComponents},
|
||||
// Union and Intersection type methods
|
||||
{"components", getComponents},
|
||||
|
||||
// Class type methods
|
||||
{"parent", getClassParent},
|
||||
// Class type methods
|
||||
{"parent", getClassParent},
|
||||
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
// Protect metatable from being changed
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
// Protect metatable from being changed
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
|
||||
// Indexing will be a dynamic function because some type fields are dynamic
|
||||
lua_newtable(L);
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pushcclosure(L, typeUserdataIndex, "__index", 1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
// Indexing will be a dynamic function because some type fields are dynamic
|
||||
lua_newtable(L);
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pushcclosure(L, typeUserdataIndex, "__index", 1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// List of fields for type userdata
|
||||
luaL_Reg typeUserdataFields[] = {
|
||||
{"unknown", createUnknown},
|
||||
{"never", createNever},
|
||||
{"any", createAny},
|
||||
{"boolean", createBoolean},
|
||||
{"number", createNumber},
|
||||
{"string", createString},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// List of methods for type userdata
|
||||
luaL_Reg typeUserdataMethods[] = {
|
||||
{"singleton", createSingleton},
|
||||
{"negationof", createNegation},
|
||||
{"unionof", createUnion},
|
||||
{"intersectionof", createIntersection},
|
||||
{"newtable", createTable},
|
||||
{"newfunction", createFunction},
|
||||
{"copy", deepCopy},
|
||||
|
||||
// Common methods
|
||||
{"is", checkTag},
|
||||
|
||||
// Negation type methods
|
||||
{"inner", getNegatedValue},
|
||||
|
||||
// Singleton type methods
|
||||
{"value", getSingletonValue},
|
||||
|
||||
// Table type methods
|
||||
{"setproperty", setTableProp},
|
||||
{"setreadproperty", setReadTableProp},
|
||||
{"setwriteproperty", setWriteTableProp},
|
||||
{"readproperty", readTableProp},
|
||||
{"writeproperty", writeTableProp},
|
||||
{"properties", getProps},
|
||||
{"setindexer", setTableIndexer},
|
||||
{"setreadindexer", setTableReadIndexer},
|
||||
{"setwriteindexer", setTableWriteIndexer},
|
||||
{"indexer", getIndexer},
|
||||
{"readindexer", getReadIndexer},
|
||||
{"writeindexer", getWriteIndexer},
|
||||
{"setmetatable", setTableMetatable},
|
||||
{"metatable", getMetatable},
|
||||
|
||||
// Function type methods
|
||||
{"setparameters", setFunctionParameters},
|
||||
{"parameters", getFunctionParameters},
|
||||
{"setreturns", setFunctionReturns},
|
||||
{"returns", getFunctionReturns},
|
||||
|
||||
// Union and Intersection type methods
|
||||
{"components", getComponents},
|
||||
|
||||
// Class type methods
|
||||
{"parent", getClassParent},
|
||||
{"indexer", getIndexer},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
// Protect metatable from being fetched.
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
// Set type userdata metatable's __eq to type_equals()
|
||||
lua_pushcfunction(L, isEqualToType, "__eq");
|
||||
lua_setfield(L, -2, "__eq");
|
||||
|
||||
// Set type userdata metatable's __index to itself
|
||||
lua_pushvalue(L, -1); // Push a copy of type userdata metatable
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
luaL_register(L, nullptr, typeUserdataMethods);
|
||||
|
||||
// Set fields for type userdata
|
||||
for (luaL_Reg* l = typeUserdataFields; l->name; l++)
|
||||
{
|
||||
l->func(L);
|
||||
lua_setfield(L, -2, l->name);
|
||||
}
|
||||
|
||||
// Set types library as a global name "types"
|
||||
lua_setglobal(L, "types");
|
||||
}
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Sets up a destructor for the type userdata.
|
||||
lua_setuserdatadtor(L, kTypeUserdataTag, deallocTypeUserData);
|
||||
}
|
||||
|
||||
// Used to redirect all the removed global functions to say "this function is unsupported"
|
||||
int unsupportedFunction(lua_State* L)
|
||||
static int unsupportedFunction(lua_State* L)
|
||||
{
|
||||
luaL_errorL(L, "this function is not supported in type functions");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print(lua_State* L)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
int n = lua_gettop(L);
|
||||
for (int i = 1; i <= n; i++)
|
||||
{
|
||||
size_t l = 0;
|
||||
const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
|
||||
if (i > 1)
|
||||
result.append('\t', 1);
|
||||
result.append(s, l);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
auto ctx = getTypeFunctionRuntime(L);
|
||||
|
||||
ctx->messages.push_back(std::move(result));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add libraries / globals for type function environment
|
||||
void setTypeFunctionEnvironment(lua_State* L)
|
||||
{
|
||||
|
@ -1659,12 +1586,28 @@ void setTypeFunctionEnvironment(lua_State* L)
|
|||
luaopen_base(L);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Remove certain global functions from the base library
|
||||
static const std::string unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
{
|
||||
lua_pushcfunction(L, unsupportedFunction, "Removing global function from type function environment");
|
||||
lua_setglobal(L, name.c_str());
|
||||
// Remove certain global functions from the base library
|
||||
static const char* unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
{
|
||||
lua_pushcfunction(L, unsupportedFunction, name);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
lua_pushcfunction(L, print, "print");
|
||||
lua_setglobal(L, "print");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove certain global functions from the base library
|
||||
static const std::string unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
{
|
||||
lua_pushcfunction(L, unsupportedFunction, "Removing global function from type function environment");
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
// currently, controls serialization, deserialization, and `type.copy`
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixMetatable)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||
|
||||
namespace Luau
|
||||
|
@ -383,21 +382,9 @@ private:
|
|||
|
||||
void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunFixMetatable)
|
||||
{
|
||||
// Serialize main part of the metatable immediately
|
||||
if (auto tableTy = get<TableType>(m1->table))
|
||||
serializeChildren(tableTy, m2);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmpTable = get<TypeFunctionTableType>(shallowSerialize(m1->table));
|
||||
if (!tmpTable)
|
||||
state->ctx->ice->ice("Serializing user defined type function arguments: metatable's table is not a TableType");
|
||||
|
||||
m2->props = tmpTable->props;
|
||||
m2->indexer = tmpTable->indexer;
|
||||
}
|
||||
// Serialize main part of the metatable immediately
|
||||
if (auto tableTy = get<TableType>(m1->table))
|
||||
serializeChildren(tableTy, m2);
|
||||
|
||||
m2->metatable = shallowSerialize(m1->metatable);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
|||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMetatableFollow)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOldSolverCreatesChildScopePointers)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -2866,7 +2866,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
|||
std::optional<TypeId> metamethod = findMetatableEntry(lhsType, metamethodName, expr.location, /* addErrors= */ true);
|
||||
if (metamethod)
|
||||
{
|
||||
if (const FunctionType* ftv = get<FunctionType>(FFlag::LuauMetatableFollow ? follow(*metamethod) : *metamethod))
|
||||
if (const FunctionType* ftv = get<FunctionType>(follow(*metamethod)))
|
||||
{
|
||||
if (isEquality)
|
||||
{
|
||||
|
@ -5205,6 +5205,13 @@ LUAU_NOINLINE void TypeChecker::reportErrorCodeTooComplex(const Location& locati
|
|||
ScopePtr TypeChecker::childFunctionScope(const ScopePtr& parent, const Location& location, int subLevel)
|
||||
{
|
||||
ScopePtr scope = std::make_shared<Scope>(parent, subLevel);
|
||||
if (FFlag::LuauOldSolverCreatesChildScopePointers)
|
||||
{
|
||||
scope->location = location;
|
||||
scope->returnType = parent->returnType;
|
||||
parent->children.emplace_back(scope.get());
|
||||
}
|
||||
|
||||
currentModule->scopes.push_back(std::make_pair(location, scope));
|
||||
return scope;
|
||||
}
|
||||
|
@ -5215,6 +5222,12 @@ ScopePtr TypeChecker::childScope(const ScopePtr& parent, const Location& locatio
|
|||
ScopePtr scope = std::make_shared<Scope>(parent);
|
||||
scope->level = parent->level;
|
||||
scope->varargPack = parent->varargPack;
|
||||
if (FFlag::LuauOldSolverCreatesChildScopePointers)
|
||||
{
|
||||
scope->location = location;
|
||||
scope->returnType = parent->returnType;
|
||||
parent->children.emplace_back(scope.get());
|
||||
}
|
||||
|
||||
currentModule->scopes.push_back(std::make_pair(location, scope));
|
||||
return scope;
|
||||
|
|
|
@ -230,17 +230,6 @@ private:
|
|||
bool skipComments;
|
||||
bool readNames;
|
||||
|
||||
// This offset represents a column offset to be applied to any positions created by the lexer until the next new line.
|
||||
// For example:
|
||||
// local x = 4
|
||||
// local y = 5
|
||||
// If we start lexing from the position of `l` in `local x = 4`, the line number will be 1, and the column will be 4
|
||||
// However, because the lexer calculates line offsets by 'index in source buffer where there is a newline', the column
|
||||
// count will start at 0. For this reason, for just the first line, we'll need to store the offset.
|
||||
unsigned int lexResumeOffset;
|
||||
|
||||
|
||||
|
||||
enum class BraceType
|
||||
{
|
||||
InterpolatedString,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LexerResumesFromPosition)
|
||||
LUAU_FASTFLAGVARIABLE(LexerResumesFromPosition2)
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -308,16 +308,15 @@ Lexer::Lexer(const char* buffer, size_t bufferSize, AstNameTable& names, Positio
|
|||
: buffer(buffer)
|
||||
, bufferSize(bufferSize)
|
||||
, offset(0)
|
||||
, line(FFlag::LexerResumesFromPosition ? startPosition.line : 0)
|
||||
, lineOffset(0)
|
||||
, line(FFlag::LexerResumesFromPosition2 ? startPosition.line : 0)
|
||||
, lineOffset(FFlag::LexerResumesFromPosition2 ? 0u - startPosition.column : 0)
|
||||
, lexeme(
|
||||
(FFlag::LexerResumesFromPosition ? Location(Position(startPosition.line, startPosition.column), 0) : Location(Position(0, 0), 0)),
|
||||
(FFlag::LexerResumesFromPosition2 ? Location(Position(startPosition.line, startPosition.column), 0) : Location(Position(0, 0), 0)),
|
||||
Lexeme::Eof
|
||||
)
|
||||
, names(names)
|
||||
, skipComments(false)
|
||||
, readNames(true)
|
||||
, lexResumeOffset(FFlag::LexerResumesFromPosition ? startPosition.column : 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -372,7 +371,6 @@ Lexeme Lexer::lookahead()
|
|||
Location currentPrevLocation = prevLocation;
|
||||
size_t currentBraceStackSize = braceStack.size();
|
||||
BraceType currentBraceType = braceStack.empty() ? BraceType::Normal : braceStack.back();
|
||||
unsigned int currentLexResumeOffset = lexResumeOffset;
|
||||
|
||||
Lexeme result = next();
|
||||
|
||||
|
@ -381,7 +379,6 @@ Lexeme Lexer::lookahead()
|
|||
lineOffset = currentLineOffset;
|
||||
lexeme = currentLexeme;
|
||||
prevLocation = currentPrevLocation;
|
||||
lexResumeOffset = currentLexResumeOffset;
|
||||
|
||||
if (braceStack.size() < currentBraceStackSize)
|
||||
braceStack.push_back(currentBraceType);
|
||||
|
@ -412,9 +409,10 @@ char Lexer::peekch(unsigned int lookahead) const
|
|||
return (offset + lookahead < bufferSize) ? buffer[offset + lookahead] : 0;
|
||||
}
|
||||
|
||||
LUAU_FORCEINLINE
|
||||
Position Lexer::position() const
|
||||
{
|
||||
return Position(line, offset - lineOffset + (FFlag::LexerResumesFromPosition ? lexResumeOffset : 0));
|
||||
return Position(line, offset - lineOffset);
|
||||
}
|
||||
|
||||
LUAU_FORCEINLINE
|
||||
|
@ -433,9 +431,6 @@ void Lexer::consumeAny()
|
|||
{
|
||||
line++;
|
||||
lineOffset = offset + 1;
|
||||
// every new line, we reset
|
||||
if (FFlag::LexerResumesFromPosition)
|
||||
lexResumeOffset = 0;
|
||||
}
|
||||
|
||||
offset++;
|
||||
|
|
|
@ -18,10 +18,8 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
|||
// flag so that we don't break production games by reverting syntax changes.
|
||||
// See docs/SyntaxChanges.md for an explanation.
|
||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunParseExport)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPortableStringZeroCheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames)
|
||||
|
@ -910,11 +908,8 @@ AstStat* Parser::parseReturn()
|
|||
AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||
{
|
||||
// parsing a type function
|
||||
if (FFlag::LuauUserDefinedTypeFunctionsSyntax2)
|
||||
{
|
||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
return parseTypeFunction(start, exported);
|
||||
}
|
||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
return parseTypeFunction(start, exported);
|
||||
|
||||
// parsing a type alias
|
||||
|
||||
|
@ -1134,8 +1129,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
AstType* type = parseType();
|
||||
|
||||
// since AstName contains a char*, it can't contain null
|
||||
bool containsNull = chars && (FFlag::LuauPortableStringZeroCheck ? memchr(chars->data, 0, chars->size) != nullptr
|
||||
: strnlen(chars->data, chars->size) < chars->size);
|
||||
bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr);
|
||||
|
||||
if (chars && !containsNull)
|
||||
{
|
||||
|
@ -1647,8 +1641,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
|||
AstType* type = parseType();
|
||||
|
||||
// since AstName contains a char*, it can't contain null
|
||||
bool containsNull = chars && (FFlag::LuauPortableStringZeroCheck ? memchr(chars->data, 0, chars->size) != nullptr
|
||||
: strnlen(chars->data, chars->size) < chars->size);
|
||||
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});
|
||||
|
@ -2352,11 +2345,8 @@ AstExpr* Parser::parseNameExpr(const char* context)
|
|||
{
|
||||
AstLocal* local = *value;
|
||||
|
||||
if (FFlag::LuauUserDefinedTypeFunctionsSyntax2)
|
||||
{
|
||||
if (local->functionDepth < typeFunctionDepth)
|
||||
return reportExprError(lexer.current().location, {}, "Type function cannot reference outer local '%s'", local->name.value);
|
||||
}
|
||||
if (local->functionDepth < typeFunctionDepth)
|
||||
return reportExprError(lexer.current().location, {}, "Type function cannot reference outer local '%s'", local->name.value);
|
||||
|
||||
return allocator.alloc<AstExprLocal>(name->location, local, local->functionDepth != functionStack.size() - 1);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,16 @@ struct ParseResult;
|
|||
class BytecodeBuilder;
|
||||
class BytecodeEncoder;
|
||||
|
||||
using CompileConstant = void*;
|
||||
|
||||
// return a type identifier for a global library member
|
||||
// values are defined by 'enum LuauBytecodeType' in Bytecode.h
|
||||
using LibraryMemberTypeCallback = int (*)(const char* library, const char* member);
|
||||
|
||||
// setup a value of a constant for a global library member
|
||||
// use setCompileConstant*** set of functions for values
|
||||
using LibraryMemberConstantCallback = void (*)(const char* library, const char* member, CompileConstant* constant);
|
||||
|
||||
// Note: this structure is duplicated in luacode.h, don't forget to change these in sync!
|
||||
struct CompileOptions
|
||||
{
|
||||
|
@ -49,6 +59,15 @@ struct CompileOptions
|
|||
|
||||
// null-terminated array of userdata types that will be included in the type information
|
||||
const char* const* userdataTypes = nullptr;
|
||||
|
||||
// null-terminated array of globals which act as libraries and have members with known type and/or constant value
|
||||
// when an import of one of these libraries is accessed, callbacks below will be called to receive that information
|
||||
const char* const* librariesWithKnownMembers = nullptr;
|
||||
LibraryMemberTypeCallback libraryMemberTypeCb = nullptr;
|
||||
LibraryMemberConstantCallback libraryMemberConstantCb = nullptr;
|
||||
|
||||
// null-terminated array of library functions that should not be compiled into a built-in fastcall ("name" "lib.name")
|
||||
const char* const* disabledBuiltins = nullptr;
|
||||
};
|
||||
|
||||
class CompileError : public std::exception
|
||||
|
@ -81,4 +100,10 @@ std::string compile(
|
|||
BytecodeEncoder* encoder = nullptr
|
||||
);
|
||||
|
||||
void setCompileConstantNil(CompileConstant* constant);
|
||||
void setCompileConstantBoolean(CompileConstant* constant, bool b);
|
||||
void setCompileConstantNumber(CompileConstant* constant, double n);
|
||||
void setCompileConstantVector(CompileConstant* constant, float x, float y, float z, float w);
|
||||
void setCompileConstantString(CompileConstant* constant, const char* s, size_t l);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -3,12 +3,21 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
// Can be used to reconfigure visibility/exports for public APIs
|
||||
// can be used to reconfigure visibility/exports for public APIs
|
||||
#ifndef LUACODE_API
|
||||
#define LUACODE_API extern
|
||||
#endif
|
||||
|
||||
typedef struct lua_CompileOptions lua_CompileOptions;
|
||||
typedef void* lua_CompileConstant;
|
||||
|
||||
// return a type identifier for a global library member
|
||||
// values are defined by 'enum LuauBytecodeType' in Bytecode.h
|
||||
typedef int (*lua_LibraryMemberTypeCallback)(const char* library, const char* member);
|
||||
|
||||
// setup a value of a constant for a global library member
|
||||
// use luau_set_compile_constant_*** set of functions for values
|
||||
typedef void (*lua_LibraryMemberConstantCallback)(const char* library, const char* member, lua_CompileConstant* constant);
|
||||
|
||||
struct lua_CompileOptions
|
||||
{
|
||||
|
@ -45,7 +54,25 @@ struct lua_CompileOptions
|
|||
|
||||
// null-terminated array of userdata types that will be included in the type information
|
||||
const char* const* userdataTypes;
|
||||
|
||||
// null-terminated array of globals which act as libraries and have members with known type and/or constant value
|
||||
// when an import of one of these libraries is accessed, callbacks below will be called to receive that information
|
||||
const char* const* librariesWithKnownMembers;
|
||||
lua_LibraryMemberTypeCallback libraryMemberTypeCb;
|
||||
lua_LibraryMemberConstantCallback libraryMemberConstantCb;
|
||||
|
||||
// null-terminated array of library functions that should not be compiled into a built-in fastcall ("name" "lib.name")
|
||||
const char* const* disabledBuiltins;
|
||||
};
|
||||
|
||||
// compile source to bytecode; when source compilation fails, the resulting bytecode contains the encoded error. use free() to destroy
|
||||
LUACODE_API char* luau_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize);
|
||||
|
||||
// when libraryMemberConstantCb is called, these methods can be used to set a value of the opaque lua_CompileConstant struct
|
||||
// vector component 'w' is not visible to VM runtime configured with LUA_VECTOR_SIZE == 3, but can affect constant folding during compilation
|
||||
// string storage must outlive the invocation of 'luau_compile' which used the callback
|
||||
LUACODE_API void luau_set_compile_constant_nil(lua_CompileConstant* constant);
|
||||
LUACODE_API void luau_set_compile_constant_boolean(lua_CompileConstant* constant, int b);
|
||||
LUACODE_API void luau_set_compile_constant_number(lua_CompileConstant* constant, double n);
|
||||
LUACODE_API void luau_set_compile_constant_vector(lua_CompileConstant* constant, float x, float y, float z, float w);
|
||||
LUACODE_API void luau_set_compile_constant_string(lua_CompileConstant* constant, const char* s, size_t l);
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
#include "Luau/Bytecode.h"
|
||||
#include "Luau/Compiler.h"
|
||||
#include "Luau/Lexer.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauVectorBuiltins)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileDisabledBuiltins)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -270,23 +274,61 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
|||
struct BuiltinVisitor : AstVisitor
|
||||
{
|
||||
DenseHashMap<AstExprCall*, int>& result;
|
||||
std::array<bool, 256> builtinIsDisabled;
|
||||
|
||||
const DenseHashMap<AstName, Global>& globals;
|
||||
const DenseHashMap<AstLocal*, Variable>& variables;
|
||||
|
||||
const CompileOptions& options;
|
||||
const AstNameTable& names;
|
||||
|
||||
BuiltinVisitor(
|
||||
DenseHashMap<AstExprCall*, int>& result,
|
||||
const DenseHashMap<AstName, Global>& globals,
|
||||
const DenseHashMap<AstLocal*, Variable>& variables,
|
||||
const CompileOptions& options
|
||||
const CompileOptions& options,
|
||||
const AstNameTable& names
|
||||
)
|
||||
: result(result)
|
||||
, globals(globals)
|
||||
, variables(variables)
|
||||
, options(options)
|
||||
, names(names)
|
||||
{
|
||||
if (FFlag::LuauCompileDisabledBuiltins)
|
||||
{
|
||||
builtinIsDisabled.fill(false);
|
||||
|
||||
if (const char* const* ptr = options.disabledBuiltins)
|
||||
{
|
||||
for (; *ptr; ++ptr)
|
||||
{
|
||||
if (const char* dot = strchr(*ptr, '.'))
|
||||
{
|
||||
AstName library = names.getWithType(*ptr, dot - *ptr).first;
|
||||
AstName name = names.get(dot + 1);
|
||||
|
||||
if (library.value && name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
Builtin builtin = Builtin{library, name};
|
||||
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
Builtin builtin = Builtin{AstName(), name};
|
||||
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool visit(AstExprCall* node) override
|
||||
|
@ -297,6 +339,9 @@ struct BuiltinVisitor : AstVisitor
|
|||
|
||||
int bfid = getBuiltinFunctionId(builtin, options);
|
||||
|
||||
if (FFlag::LuauCompileDisabledBuiltins && bfid >= 0 && builtinIsDisabled[bfid])
|
||||
bfid = -1;
|
||||
|
||||
// getBuiltinFunctionId optimistically assumes all select() calls are builtin but actually the second argument must be a vararg
|
||||
if (bfid == LBF_SELECT_VARARG && !(node->args.size == 2 && node->args.data[1]->is<AstExprVarargs>()))
|
||||
bfid = -1;
|
||||
|
@ -313,10 +358,11 @@ void analyzeBuiltins(
|
|||
const DenseHashMap<AstName, Global>& globals,
|
||||
const DenseHashMap<AstLocal*, Variable>& variables,
|
||||
const CompileOptions& options,
|
||||
AstNode* root
|
||||
AstNode* root,
|
||||
const AstNameTable& names
|
||||
)
|
||||
{
|
||||
BuiltinVisitor visitor{result, globals, variables, options};
|
||||
BuiltinVisitor visitor{result, globals, variables, options, names};
|
||||
root->visit(&visitor);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ void analyzeBuiltins(
|
|||
const DenseHashMap<AstName, Global>& globals,
|
||||
const DenseHashMap<AstLocal*, Variable>& variables,
|
||||
const CompileOptions& options,
|
||||
AstNode* root
|
||||
AstNode* root,
|
||||
const AstNameTable& names
|
||||
);
|
||||
|
||||
struct BuiltinInfo
|
||||
|
|
|
@ -27,6 +27,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
|
|||
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileOptimizeRevArith)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileLibraryConstants)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -725,7 +726,7 @@ struct Compiler
|
|||
inlineFrames.push_back({func, oldLocals, target, targetCount});
|
||||
|
||||
// fold constant values updated above into expressions in the function body
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldMathK, func->body);
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body);
|
||||
|
||||
bool usedFallthrough = false;
|
||||
|
||||
|
@ -770,7 +771,7 @@ struct Compiler
|
|||
var->type = Constant::Type_Unknown;
|
||||
}
|
||||
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldMathK, func->body);
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body);
|
||||
}
|
||||
|
||||
void compileExprCall(AstExprCall* expr, uint8_t target, uint8_t targetCount, bool targetTop = false, bool multRet = false)
|
||||
|
@ -3052,7 +3053,7 @@ struct Compiler
|
|||
locstants[var].type = Constant::Type_Number;
|
||||
locstants[var].valueNumber = from + iv * step;
|
||||
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldMathK, stat);
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, stat);
|
||||
|
||||
size_t iterJumps = loopJumps.size();
|
||||
|
||||
|
@ -3080,7 +3081,7 @@ struct Compiler
|
|||
// clean up fold state in case we need to recompile - normally we compile the loop body once, but due to inlining we may need to do it again
|
||||
locstants[var].type = Constant::Type_Unknown;
|
||||
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldMathK, stat);
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, stat);
|
||||
}
|
||||
|
||||
void compileStatFor(AstStatFor* stat)
|
||||
|
@ -4141,7 +4142,7 @@ struct Compiler
|
|||
BuiltinAstTypes builtinTypes;
|
||||
|
||||
const DenseHashMap<AstExprCall*, int>* builtinsFold = nullptr;
|
||||
bool builtinsFoldMathK = false;
|
||||
bool builtinsFoldLibraryK = false;
|
||||
|
||||
// compileFunction state, gets reset for every function
|
||||
unsigned int regTop = 0;
|
||||
|
@ -4221,16 +4222,40 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
compiler.builtinsFold = &compiler.builtins;
|
||||
|
||||
if (AstName math = names.get("math"); math.value && getGlobalState(compiler.globals, math) == Global::Default)
|
||||
compiler.builtinsFoldMathK = true;
|
||||
{
|
||||
compiler.builtinsFoldLibraryK = true;
|
||||
}
|
||||
else if (FFlag::LuauCompileLibraryConstants)
|
||||
{
|
||||
if (const char* const* ptr = options.librariesWithKnownMembers)
|
||||
{
|
||||
for (; *ptr; ++ptr)
|
||||
{
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(compiler.globals, name) == Global::Default)
|
||||
{
|
||||
compiler.builtinsFoldLibraryK = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.optimizationLevel >= 1)
|
||||
{
|
||||
// this pass tracks which calls are builtins and can be compiled more efficiently
|
||||
analyzeBuiltins(compiler.builtins, compiler.globals, compiler.variables, options, root);
|
||||
analyzeBuiltins(compiler.builtins, compiler.globals, compiler.variables, options, root, names);
|
||||
|
||||
// this pass analyzes constantness of expressions
|
||||
foldConstants(compiler.constants, compiler.variables, compiler.locstants, compiler.builtinsFold, compiler.builtinsFoldMathK, root);
|
||||
foldConstants(
|
||||
compiler.constants,
|
||||
compiler.variables,
|
||||
compiler.locstants,
|
||||
compiler.builtinsFold,
|
||||
compiler.builtinsFoldLibraryK,
|
||||
options.libraryMemberConstantCb,
|
||||
root
|
||||
);
|
||||
|
||||
// this pass analyzes table assignments to estimate table shapes for initially empty tables
|
||||
predictTableShapes(compiler.tableShapes, root);
|
||||
|
@ -4261,6 +4286,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
compiler.builtinTypes,
|
||||
compiler.builtins,
|
||||
compiler.globals,
|
||||
options.libraryMemberTypeCb,
|
||||
bytecode
|
||||
);
|
||||
|
||||
|
@ -4277,9 +4303,9 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/*attributes=*/AstArray<AstAttr*>({nullptr, 0}),
|
||||
/*generics= */ AstArray<AstGenericType>(),
|
||||
/*genericPacks= */ AstArray<AstGenericTypePack>(),
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
/* generics= */ AstArray<AstGenericType>(),
|
||||
/* genericPacks= */ AstArray<AstGenericTypePack>(),
|
||||
/* self= */ nullptr,
|
||||
AstArray<AstLocal*>(),
|
||||
/* vararg= */ true,
|
||||
|
@ -4340,4 +4366,50 @@ std::string compile(const std::string& source, const CompileOptions& options, co
|
|||
}
|
||||
}
|
||||
|
||||
void setCompileConstantNil(CompileConstant* constant)
|
||||
{
|
||||
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
|
||||
|
||||
target->type = Compile::Constant::Type_Nil;
|
||||
}
|
||||
|
||||
void setCompileConstantBoolean(CompileConstant* constant, bool b)
|
||||
{
|
||||
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
|
||||
|
||||
target->type = Compile::Constant::Type_Boolean;
|
||||
target->valueBoolean = b;
|
||||
}
|
||||
|
||||
void setCompileConstantNumber(CompileConstant* constant, double n)
|
||||
{
|
||||
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
|
||||
|
||||
target->type = Compile::Constant::Type_Number;
|
||||
target->valueNumber = n;
|
||||
}
|
||||
|
||||
void setCompileConstantVector(CompileConstant* constant, float x, float y, float z, float w)
|
||||
{
|
||||
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
|
||||
|
||||
target->type = Compile::Constant::Type_Vector;
|
||||
target->valueVector[0] = x;
|
||||
target->valueVector[1] = y;
|
||||
target->valueVector[2] = z;
|
||||
target->valueVector[3] = w;
|
||||
}
|
||||
|
||||
void setCompileConstantString(CompileConstant* constant, const char* s, size_t l)
|
||||
{
|
||||
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
|
||||
|
||||
if (l > std::numeric_limits<unsigned int>::max())
|
||||
CompileError::raise({}, "Exceeded custom string constant length limit");
|
||||
|
||||
target->type = Compile::Constant::Type_String;
|
||||
target->stringLength = l;
|
||||
target->valueString = s;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
LUAU_FASTFLAGVARIABLE(LuauVectorFolding)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -57,6 +60,14 @@ static void foldUnary(Constant& result, AstExprUnary::Op op, const Constant& arg
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = -arg.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && arg.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = -arg.valueVector[0];
|
||||
result.valueVector[1] = -arg.valueVector[1];
|
||||
result.valueVector[2] = -arg.valueVector[2];
|
||||
result.valueVector[3] = -arg.valueVector[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprUnary::Len:
|
||||
|
@ -82,6 +93,14 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber + ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] + ra.valueVector[0];
|
||||
result.valueVector[1] = la.valueVector[1] + ra.valueVector[1];
|
||||
result.valueVector[2] = la.valueVector[2] + ra.valueVector[2];
|
||||
result.valueVector[3] = la.valueVector[3] + ra.valueVector[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprBinary::Sub:
|
||||
|
@ -90,6 +109,14 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber - ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] - ra.valueVector[0];
|
||||
result.valueVector[1] = la.valueVector[1] - ra.valueVector[1];
|
||||
result.valueVector[2] = la.valueVector[2] - ra.valueVector[2];
|
||||
result.valueVector[3] = la.valueVector[3] - ra.valueVector[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprBinary::Mul:
|
||||
|
@ -98,6 +125,48 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber * ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] * ra.valueVector[3];
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] * ra.valueVector[0];
|
||||
result.valueVector[1] = la.valueVector[1] * ra.valueVector[1];
|
||||
result.valueVector[2] = la.valueVector[2] * ra.valueVector[2];
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = float(la.valueNumber) * ra.valueVector[3];
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = float(la.valueNumber) * ra.valueVector[0];
|
||||
result.valueVector[1] = float(la.valueNumber) * ra.valueVector[1];
|
||||
result.valueVector[2] = float(la.valueNumber) * ra.valueVector[2];
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] * float(ra.valueNumber);
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] * float(ra.valueNumber);
|
||||
result.valueVector[1] = la.valueVector[1] * float(ra.valueNumber);
|
||||
result.valueVector[2] = la.valueVector[2] * float(ra.valueNumber);
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprBinary::Div:
|
||||
|
@ -106,6 +175,48 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber / ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] / ra.valueVector[3];
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] / ra.valueVector[0];
|
||||
result.valueVector[1] = la.valueVector[1] / ra.valueVector[1];
|
||||
result.valueVector[2] = la.valueVector[2] / ra.valueVector[2];
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = float(la.valueNumber) / ra.valueVector[3];
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = float(la.valueNumber) / ra.valueVector[0];
|
||||
result.valueVector[1] = float(la.valueNumber) / ra.valueVector[1];
|
||||
result.valueVector[2] = float(la.valueNumber) / ra.valueVector[2];
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] / float(ra.valueNumber);
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] / float(ra.valueNumber);
|
||||
result.valueVector[1] = la.valueVector[1] / float(ra.valueNumber);
|
||||
result.valueVector[2] = la.valueVector[2] / float(ra.valueNumber);
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprBinary::FloorDiv:
|
||||
|
@ -114,6 +225,48 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = floor(la.valueNumber / ra.valueNumber);
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = floor(la.valueVector[3] / ra.valueVector[3]);
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = floor(la.valueVector[0] / ra.valueVector[0]);
|
||||
result.valueVector[1] = floor(la.valueVector[1] / ra.valueVector[1]);
|
||||
result.valueVector[2] = floor(la.valueVector[2] / ra.valueVector[2]);
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = floor(float(la.valueNumber) / ra.valueVector[3]);
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = floor(float(la.valueNumber) / ra.valueVector[0]);
|
||||
result.valueVector[1] = floor(float(la.valueNumber) / ra.valueVector[1]);
|
||||
result.valueVector[2] = floor(float(la.valueNumber) / ra.valueVector[2]);
|
||||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = floor(la.valueVector[3] / float(ra.valueNumber));
|
||||
|
||||
if (resultW == 0.0f || hadW)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = floor(la.valueVector[0] / float(ra.valueNumber));
|
||||
result.valueVector[1] = floor(la.valueVector[1] / float(ra.valueNumber));
|
||||
result.valueVector[2] = floor(la.valueVector[2] / float(ra.valueNumber));
|
||||
result.valueVector[3] = floor(la.valueVector[3] / float(ra.valueNumber));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AstExprBinary::Mod:
|
||||
|
@ -209,7 +362,8 @@ struct ConstantVisitor : AstVisitor
|
|||
DenseHashMap<AstLocal*, Constant>& locals;
|
||||
|
||||
const DenseHashMap<AstExprCall*, int>* builtins;
|
||||
bool foldMathK = false;
|
||||
bool foldLibraryK = false;
|
||||
LibraryMemberConstantCallback libraryMemberConstantCb;
|
||||
|
||||
bool wasEmpty = false;
|
||||
|
||||
|
@ -220,13 +374,15 @@ struct ConstantVisitor : AstVisitor
|
|||
DenseHashMap<AstLocal*, Variable>& variables,
|
||||
DenseHashMap<AstLocal*, Constant>& locals,
|
||||
const DenseHashMap<AstExprCall*, int>* builtins,
|
||||
bool foldMathK
|
||||
bool foldLibraryK,
|
||||
LibraryMemberConstantCallback libraryMemberConstantCb
|
||||
)
|
||||
: constants(constants)
|
||||
, variables(variables)
|
||||
, locals(locals)
|
||||
, builtins(builtins)
|
||||
, foldMathK(foldMathK)
|
||||
, foldLibraryK(foldLibraryK)
|
||||
, libraryMemberConstantCb(libraryMemberConstantCb)
|
||||
{
|
||||
// since we do a single pass over the tree, if the initial state was empty we don't need to clear out old entries
|
||||
wasEmpty = constants.empty() && locals.empty();
|
||||
|
@ -316,11 +472,26 @@ struct ConstantVisitor : AstVisitor
|
|||
{
|
||||
analyze(expr->expr);
|
||||
|
||||
if (foldMathK)
|
||||
if (foldLibraryK)
|
||||
{
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>(); eg && eg->name == "math")
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
{
|
||||
result = foldBuiltinMath(expr->index);
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (eg->name == "math")
|
||||
result = foldBuiltinMath(expr->index);
|
||||
|
||||
// if we have a custom handler and the constant hasn't been resolved
|
||||
if (libraryMemberConstantCb && result.type == Constant::Type_Unknown)
|
||||
libraryMemberConstantCb(eg->name.value, expr->index.value, reinterpret_cast<Luau::CompileConstant*>(&result));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>(); eg && eg->name == "math")
|
||||
{
|
||||
result = foldBuiltinMath(expr->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -468,11 +639,12 @@ void foldConstants(
|
|||
DenseHashMap<AstLocal*, Variable>& variables,
|
||||
DenseHashMap<AstLocal*, Constant>& locals,
|
||||
const DenseHashMap<AstExprCall*, int>* builtins,
|
||||
bool foldMathK,
|
||||
bool foldLibraryK,
|
||||
LibraryMemberConstantCallback libraryMemberConstantCb,
|
||||
AstNode* root
|
||||
)
|
||||
{
|
||||
ConstantVisitor visitor{constants, variables, locals, builtins, foldMathK};
|
||||
ConstantVisitor visitor{constants, variables, locals, builtins, foldLibraryK, libraryMemberConstantCb};
|
||||
root->visit(&visitor);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Compiler.h"
|
||||
|
||||
#include "ValueTracking.h"
|
||||
|
||||
namespace Luau
|
||||
|
@ -49,7 +51,8 @@ void foldConstants(
|
|||
DenseHashMap<AstLocal*, Variable>& variables,
|
||||
DenseHashMap<AstLocal*, Constant>& locals,
|
||||
const DenseHashMap<AstExprCall*, int>* builtins,
|
||||
bool foldMathK,
|
||||
bool foldLibraryK,
|
||||
LibraryMemberConstantCallback libraryMemberConstantCb,
|
||||
AstNode* root
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Luau/BytecodeBuilder.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileVectorTypeInfo)
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -175,16 +176,32 @@ static bool isMatchingGlobal(const DenseHashMap<AstName, Compile::Global>& globa
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool isMatchingGlobalMember(
|
||||
const DenseHashMap<AstName, Compile::Global>& globals,
|
||||
AstExprIndexName* expr,
|
||||
const char* library,
|
||||
const char* member
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauCompileLibraryConstants);
|
||||
|
||||
if (AstExprGlobal* object = expr->expr->as<AstExprGlobal>())
|
||||
return getGlobalState(globals, object->name) == Compile::Global::Default && object->name == library && expr->index == member;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct TypeMapVisitor : AstVisitor
|
||||
{
|
||||
DenseHashMap<AstExprFunction*, std::string>& functionTypes;
|
||||
DenseHashMap<AstLocal*, LuauBytecodeType>& localTypes;
|
||||
DenseHashMap<AstExpr*, LuauBytecodeType>& exprTypes;
|
||||
const char* hostVectorType;
|
||||
const char* hostVectorType = nullptr;
|
||||
const DenseHashMap<AstName, uint8_t>& userdataTypes;
|
||||
const BuiltinAstTypes& builtinTypes;
|
||||
const DenseHashMap<AstExprCall*, int>& builtinCalls;
|
||||
const DenseHashMap<AstName, Compile::Global>& globals;
|
||||
LibraryMemberTypeCallback libraryMemberTypeCb = nullptr;
|
||||
BytecodeBuilder& bytecode;
|
||||
|
||||
DenseHashMap<AstName, AstStatTypeAlias*> typeAliases;
|
||||
|
@ -201,6 +218,7 @@ struct TypeMapVisitor : AstVisitor
|
|||
const BuiltinAstTypes& builtinTypes,
|
||||
const DenseHashMap<AstExprCall*, int>& builtinCalls,
|
||||
const DenseHashMap<AstName, Compile::Global>& globals,
|
||||
LibraryMemberTypeCallback libraryMemberTypeCb,
|
||||
BytecodeBuilder& bytecode
|
||||
)
|
||||
: functionTypes(functionTypes)
|
||||
|
@ -211,6 +229,7 @@ struct TypeMapVisitor : AstVisitor
|
|||
, builtinTypes(builtinTypes)
|
||||
, builtinCalls(builtinCalls)
|
||||
, globals(globals)
|
||||
, libraryMemberTypeCb(libraryMemberTypeCb)
|
||||
, bytecode(bytecode)
|
||||
, typeAliases(AstName())
|
||||
, resolvedLocals(nullptr)
|
||||
|
@ -461,7 +480,53 @@ struct TypeMapVisitor : AstVisitor
|
|||
if (*typeBcPtr == LBC_TYPE_VECTOR)
|
||||
{
|
||||
if (node->index == "X" || node->index == "Y" || node->index == "Z")
|
||||
{
|
||||
recordResolvedType(node, &builtinTypes.numberType);
|
||||
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
{
|
||||
if (isMatchingGlobalMember(globals, node, "vector", "zero") || isMatchingGlobalMember(globals, node, "vector", "one"))
|
||||
{
|
||||
recordResolvedType(node, &builtinTypes.vectorType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (libraryMemberTypeCb)
|
||||
{
|
||||
if (AstExprGlobal* object = node->expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (LuauBytecodeType ty = LuauBytecodeType(libraryMemberTypeCb(object->name.value, node->index.value)); ty != LBC_TYPE_ANY)
|
||||
{
|
||||
// TODO: 'resolvedExprs' is more limited than 'exprTypes' which limits full inference of more complex types that a user
|
||||
// callback can return
|
||||
switch (ty)
|
||||
{
|
||||
case LBC_TYPE_BOOLEAN:
|
||||
resolvedExprs[node] = &builtinTypes.booleanType;
|
||||
break;
|
||||
case LBC_TYPE_NUMBER:
|
||||
resolvedExprs[node] = &builtinTypes.numberType;
|
||||
break;
|
||||
case LBC_TYPE_STRING:
|
||||
resolvedExprs[node] = &builtinTypes.stringType;
|
||||
break;
|
||||
case LBC_TYPE_VECTOR:
|
||||
resolvedExprs[node] = &builtinTypes.vectorType;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exprTypes[node] = ty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -733,10 +798,13 @@ void buildTypeMap(
|
|||
const BuiltinAstTypes& builtinTypes,
|
||||
const DenseHashMap<AstExprCall*, int>& builtinCalls,
|
||||
const DenseHashMap<AstName, Compile::Global>& globals,
|
||||
LibraryMemberTypeCallback libraryMemberTypeCb,
|
||||
BytecodeBuilder& bytecode
|
||||
)
|
||||
{
|
||||
TypeMapVisitor visitor(functionTypes, localTypes, exprTypes, hostVectorType, userdataTypes, builtinTypes, builtinCalls, globals, bytecode);
|
||||
TypeMapVisitor visitor(
|
||||
functionTypes, localTypes, exprTypes, hostVectorType, userdataTypes, builtinTypes, builtinCalls, globals, libraryMemberTypeCb, bytecode
|
||||
);
|
||||
root->visit(&visitor);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Bytecode.h"
|
||||
#include "Luau/Compiler.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "ValueTracking.h"
|
||||
|
||||
|
@ -19,7 +20,7 @@ struct BuiltinAstTypes
|
|||
{
|
||||
}
|
||||
|
||||
// AstName use here will not match the AstNameTable, but the was we use them here always force a full string compare
|
||||
// AstName use here will not match the AstNameTable, but the way we use them here always forces a full string compare
|
||||
AstTypeReference booleanType{{}, std::nullopt, AstName{"boolean"}, std::nullopt, {}};
|
||||
AstTypeReference numberType{{}, std::nullopt, AstName{"number"}, std::nullopt, {}};
|
||||
AstTypeReference stringType{{}, std::nullopt, AstName{"string"}, std::nullopt, {}};
|
||||
|
@ -38,6 +39,7 @@ void buildTypeMap(
|
|||
const BuiltinAstTypes& builtinTypes,
|
||||
const DenseHashMap<AstExprCall*, int>& builtinCalls,
|
||||
const DenseHashMap<AstName, Compile::Global>& globals,
|
||||
LibraryMemberTypeCallback libraryMemberTypeCb,
|
||||
BytecodeBuilder& bytecode
|
||||
);
|
||||
|
||||
|
|
|
@ -27,3 +27,28 @@ char* luau_compile(const char* source, size_t size, lua_CompileOptions* options,
|
|||
*outsize = result.size();
|
||||
return copy;
|
||||
}
|
||||
|
||||
void luau_set_compile_constant_nil(lua_CompileConstant* constant)
|
||||
{
|
||||
Luau::setCompileConstantNil(constant);
|
||||
}
|
||||
|
||||
void luau_set_compile_constant_boolean(lua_CompileConstant* constant, int b)
|
||||
{
|
||||
Luau::setCompileConstantBoolean(constant, b != 0);
|
||||
}
|
||||
|
||||
void luau_set_compile_constant_number(lua_CompileConstant* constant, double n)
|
||||
{
|
||||
Luau::setCompileConstantNumber(constant, n);
|
||||
}
|
||||
|
||||
void luau_set_compile_constant_vector(lua_CompileConstant* constant, float x, float y, float z, float w)
|
||||
{
|
||||
Luau::setCompileConstantVector(constant, x, y, z, w);
|
||||
}
|
||||
|
||||
void luau_set_compile_constant_string(lua_CompileConstant* constant, const char* s, size_t l)
|
||||
{
|
||||
Luau::setCompileConstantString(constant, s, l);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ struct Config
|
|||
{
|
||||
std::string value;
|
||||
std::string_view configLocation;
|
||||
std::string originalCase; // The alias in its original case.
|
||||
};
|
||||
|
||||
DenseHashMap<std::string, AliasInfo> aliases{""};
|
||||
|
|
|
@ -26,9 +26,9 @@ Config::Config(const Config& other)
|
|||
, typeErrors(other.typeErrors)
|
||||
, globals(other.globals)
|
||||
{
|
||||
for (const auto& [alias, aliasInfo] : other.aliases)
|
||||
for (const auto& [_, aliasInfo] : other.aliases)
|
||||
{
|
||||
setAlias(alias, aliasInfo.value, std::string(aliasInfo.configLocation));
|
||||
setAlias(aliasInfo.originalCase, aliasInfo.value, std::string(aliasInfo.configLocation));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,8 +44,20 @@ Config& Config::operator=(const Config& other)
|
|||
|
||||
void Config::setAlias(std::string alias, std::string value, const std::string& configLocation)
|
||||
{
|
||||
AliasInfo& info = aliases[alias];
|
||||
std::string lowercasedAlias = alias;
|
||||
std::transform(
|
||||
lowercasedAlias.begin(),
|
||||
lowercasedAlias.end(),
|
||||
lowercasedAlias.begin(),
|
||||
[](unsigned char c)
|
||||
{
|
||||
return ('A' <= c && c <= 'Z') ? (c + ('a' - 'A')) : c;
|
||||
}
|
||||
);
|
||||
|
||||
AliasInfo& info = aliases[lowercasedAlias];
|
||||
info.value = std::move(value);
|
||||
info.originalCase = std::move(alias);
|
||||
|
||||
if (!configLocationCache.contains(configLocation))
|
||||
configLocationCache[configLocation] = std::make_unique<std::string>(configLocation);
|
||||
|
@ -175,7 +187,7 @@ bool isValidAlias(const std::string& alias)
|
|||
|
||||
static Error parseAlias(
|
||||
Config& config,
|
||||
std::string aliasKey,
|
||||
const std::string& aliasKey,
|
||||
const std::string& aliasValue,
|
||||
const std::optional<ConfigOptions::AliasOptions>& aliasOptions
|
||||
)
|
||||
|
@ -183,21 +195,11 @@ static Error parseAlias(
|
|||
if (!isValidAlias(aliasKey))
|
||||
return Error{"Invalid alias " + aliasKey};
|
||||
|
||||
std::transform(
|
||||
aliasKey.begin(),
|
||||
aliasKey.end(),
|
||||
aliasKey.begin(),
|
||||
[](unsigned char c)
|
||||
{
|
||||
return ('A' <= c && c <= 'Z') ? (c + ('a' - 'A')) : c;
|
||||
}
|
||||
);
|
||||
|
||||
if (!aliasOptions)
|
||||
return Error("Cannot parse aliases without alias options");
|
||||
|
||||
if (aliasOptions->overwriteAliases || !config.aliases.contains(aliasKey))
|
||||
config.setAlias(std::move(aliasKey), aliasValue, aliasOptions->configLocation);
|
||||
config.setAlias(aliasKey, aliasValue, aliasOptions->configLocation);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -198,8 +198,7 @@ private:
|
|||
{
|
||||
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
||||
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
||||
for (Id& id : enode.mutableOperands())
|
||||
id = find(id);
|
||||
Luau::EqSat::canonicalize(enode, [&](Id id) { return find(id); });
|
||||
}
|
||||
|
||||
bool isCanonical(const L& enode) const
|
||||
|
|
|
@ -244,6 +244,9 @@ private:
|
|||
template<typename Phantom, typename T>
|
||||
struct NodeSet
|
||||
{
|
||||
template <typename P_, typename T_, typename Find>
|
||||
friend void canonicalize(NodeSet<P_, T_>& node, Find&& find);
|
||||
|
||||
template<typename... Args>
|
||||
NodeSet(Args&&... args)
|
||||
: vector{std::forward<Args>(args)...}
|
||||
|
@ -299,6 +302,9 @@ struct Language final
|
|||
template<typename T>
|
||||
using WithinDomain = std::disjunction<std::is_same<std::decay_t<T>, Ts>...>;
|
||||
|
||||
template <typename Find, typename... Vs>
|
||||
friend void canonicalize(Language<Vs...>& enode, Find&& find);
|
||||
|
||||
template<typename T>
|
||||
Language(T&& t, std::enable_if_t<WithinDomain<T>::value>* = 0) noexcept
|
||||
: v(std::forward<T>(t))
|
||||
|
@ -382,4 +388,37 @@ private:
|
|||
VariantTy v;
|
||||
};
|
||||
|
||||
template <typename Node, typename Find>
|
||||
void canonicalize(Node& node, Find&& find)
|
||||
{
|
||||
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
||||
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
||||
for (Id& id : node.mutableOperands())
|
||||
id = find(id);
|
||||
}
|
||||
|
||||
// Canonicalizing the Ids in a NodeSet may result in the set decreasing in size.
|
||||
template <typename Phantom, typename T, typename Find>
|
||||
void canonicalize(NodeSet<Phantom, T>& node, Find&& find)
|
||||
{
|
||||
for (Id& id : node.vector)
|
||||
id = find(id);
|
||||
|
||||
std::sort(begin(node.vector), end(node.vector));
|
||||
auto endIt = std::unique(begin(node.vector), end(node.vector));
|
||||
node.vector.erase(endIt, end(node.vector));
|
||||
}
|
||||
|
||||
template <typename Find, typename... Vs>
|
||||
void canonicalize(Language<Vs...>& enode, Find&& find)
|
||||
{
|
||||
visit(
|
||||
[&](auto&& v)
|
||||
{
|
||||
Luau::EqSat::canonicalize(v, find);
|
||||
},
|
||||
enode.v
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace Luau::EqSat
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "lstate.h"
|
||||
#include "lvm.h"
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauCoroCheckStack, false)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
||||
|
||||
#define CO_STATUS_ERROR -1
|
||||
|
@ -41,7 +40,7 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
|
|||
luaL_error(L, "too many arguments to resume");
|
||||
lua_xmove(L, co, narg);
|
||||
}
|
||||
else if (DFFlag::LuauCoroCheckStack)
|
||||
else
|
||||
{
|
||||
// coroutine might be completely full already
|
||||
if ((co->top - co->base) > LUAI_MAXCSTACK)
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauCoroCheckStack)
|
||||
|
||||
/*
|
||||
* Luau uses an incremental non-generational non-moving mark&sweep garbage collector.
|
||||
*
|
||||
|
@ -439,26 +437,13 @@ static void shrinkstack(lua_State* L)
|
|||
if (L->size_ci > LUAI_MAXCALLS) // handling overflow?
|
||||
return; // do not touch the stacks
|
||||
|
||||
if (DFFlag::LuauCoroCheckStack)
|
||||
{
|
||||
if (3 * size_t(ci_used) < size_t(L->size_ci) && 2 * BASIC_CI_SIZE < L->size_ci)
|
||||
luaD_reallocCI(L, L->size_ci / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
|
||||
if (3 * size_t(ci_used) < size_t(L->size_ci) && 2 * BASIC_CI_SIZE < L->size_ci)
|
||||
luaD_reallocCI(L, L->size_ci / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
|
||||
|
||||
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
||||
luaD_reallocstack(L, L->stacksize / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocstack(L, s_used));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (3 * ci_used < L->size_ci && 2 * BASIC_CI_SIZE < L->size_ci)
|
||||
luaD_reallocCI(L, L->size_ci / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
|
||||
|
||||
if (3 * s_used < L->stacksize && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
||||
luaD_reallocstack(L, L->stacksize / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocstack(L, s_used));
|
||||
}
|
||||
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
||||
luaD_reallocstack(L, L->stacksize / 2); // still big enough...
|
||||
condhardstacktests(luaD_reallocstack(L, s_used));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -66,7 +66,7 @@ end
|
|||
-- and 'false' otherwise.
|
||||
--
|
||||
-- Example usage:
|
||||
-- local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
-- local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
-- local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
-- function testFunc()
|
||||
-- ...
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--!nonstrict
|
||||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
local stretchTreeDepth = 18 -- about 16Mb
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
]]
|
||||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
bench.runCode(function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
|
||||
local function prequire(name) local success, result = pcall(require, name); return success and result end
|
||||
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
|
||||
|
||||
function test()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue