mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/646 (#1458)
# General Updates * Fix some cases where documentation symbols would not be available when mouseovering at certain positions in the code * Scaffolding to help embedders have more control over how `typeof(x)` refines types * Refinements to require-by-string semantics. See https://github.com/luau-lang/rfcs/pull/56 for details. * Fix for https://github.com/luau-lang/luau/issues/1405 # New Solver * Fix many crashes (thanks you for your bug reports!) * Type functions can now call each other * Type functions all evaluate in a single VM. This should improve typechecking performance and reduce memory use. * `export type function` is now forbidden and fails with a clear error message * Type functions that access locals in the surrounding environment are now properly a parse error * You can now use `:setindexer(types.never, types.never)` to delete an indexer from a table type. # Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
02241b6d24
commit
543de6e939
@ -28,6 +28,7 @@ struct Scope;
|
|||||||
using ScopePtr = std::shared_ptr<Scope>;
|
using ScopePtr = std::shared_ptr<Scope>;
|
||||||
|
|
||||||
struct DcrLogger;
|
struct DcrLogger;
|
||||||
|
struct TypeFunctionRuntime;
|
||||||
|
|
||||||
struct Inference
|
struct Inference
|
||||||
{
|
{
|
||||||
@ -108,6 +109,8 @@ struct ConstraintGenerator
|
|||||||
|
|
||||||
// Needed to be able to enable error-suppression preservation for immediate refinements.
|
// Needed to be able to enable error-suppression preservation for immediate refinements.
|
||||||
NotNull<Normalizer> normalizer;
|
NotNull<Normalizer> normalizer;
|
||||||
|
// Needed to register all available type functions for execution at later stages.
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||||
// Needed to resolve modules to make 'require' import types properly.
|
// Needed to resolve modules to make 'require' import types properly.
|
||||||
NotNull<ModuleResolver> moduleResolver;
|
NotNull<ModuleResolver> moduleResolver;
|
||||||
// Occasionally constraint generation needs to produce an ICE.
|
// Occasionally constraint generation needs to produce an ICE.
|
||||||
@ -125,6 +128,7 @@ struct ConstraintGenerator
|
|||||||
ConstraintGenerator(
|
ConstraintGenerator(
|
||||||
ModulePtr module,
|
ModulePtr module,
|
||||||
NotNull<Normalizer> normalizer,
|
NotNull<Normalizer> normalizer,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<ModuleResolver> moduleResolver,
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<InternalErrorReporter> ice,
|
NotNull<InternalErrorReporter> ice,
|
||||||
@ -223,7 +227,10 @@ private:
|
|||||||
);
|
);
|
||||||
void applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement);
|
void applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement);
|
||||||
|
|
||||||
|
LUAU_NOINLINE void checkAliases(const ScopePtr& scope, AstStatBlock* block);
|
||||||
|
|
||||||
ControlFlow visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
|
ControlFlow visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
|
||||||
|
ControlFlow visitBlockWithoutChildScope_DEPRECATED(const ScopePtr& scope, AstStatBlock* block);
|
||||||
|
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStat* stat);
|
ControlFlow visit(const ScopePtr& scope, AstStat* stat);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatBlock* block);
|
ControlFlow visit(const ScopePtr& scope, AstStatBlock* block);
|
||||||
|
@ -44,21 +44,6 @@ struct LoadDefinitionFileResult
|
|||||||
|
|
||||||
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
|
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
|
||||||
|
|
||||||
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
|
|
||||||
|
|
||||||
// Exported only for convenient testing.
|
|
||||||
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& expr);
|
|
||||||
|
|
||||||
/** Try to convert an AST fragment into a ModuleName.
|
|
||||||
* Returns std::nullopt if the expression cannot be resolved. This will most likely happen in cases where
|
|
||||||
* the import path involves some dynamic computation that we cannot see into at typechecking time.
|
|
||||||
*
|
|
||||||
* Unintuitively, weirdly-formulated modules (like game.Parent.Parent.Parent.Foo) will successfully produce a ModuleName
|
|
||||||
* as long as it falls within the permitted syntax. This is ok because we will fail to find the module and produce an
|
|
||||||
* error when we try during typechecking.
|
|
||||||
*/
|
|
||||||
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& expr);
|
|
||||||
|
|
||||||
struct SourceNode
|
struct SourceNode
|
||||||
{
|
{
|
||||||
bool hasDirtySourceModule() const
|
bool hasDirtySourceModule() const
|
||||||
|
@ -20,8 +20,6 @@ struct ModuleResolver
|
|||||||
virtual ~ModuleResolver() {}
|
virtual ~ModuleResolver() {}
|
||||||
|
|
||||||
/** Compute a ModuleName from an AST fragment. This AST fragment is generally the argument to the require() function.
|
/** Compute a ModuleName from an AST fragment. This AST fragment is generally the argument to the require() function.
|
||||||
*
|
|
||||||
* You probably want to implement this with some variation of pathExprToModuleName.
|
|
||||||
*
|
*
|
||||||
* @returns The ModuleInfo if the expression is a syntactically legal path.
|
* @returns The ModuleInfo if the expression is a syntactically legal path.
|
||||||
* @returns std::nullopt if we are unable to determine whether or not the expression is a valid path. Type inference will
|
* @returns std::nullopt if we are unable to determine whether or not the expression is a valid path. Type inference will
|
||||||
|
@ -9,11 +9,13 @@ namespace Luau
|
|||||||
{
|
{
|
||||||
|
|
||||||
struct BuiltinTypes;
|
struct BuiltinTypes;
|
||||||
|
struct TypeFunctionRuntime;
|
||||||
struct UnifierSharedState;
|
struct UnifierSharedState;
|
||||||
struct TypeCheckLimits;
|
struct TypeCheckLimits;
|
||||||
|
|
||||||
void checkNonStrict(
|
void checkNonStrict(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<InternalErrorReporter> ice,
|
NotNull<InternalErrorReporter> ice,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<const DataFlowGraph> dfg,
|
NotNull<const DataFlowGraph> dfg,
|
||||||
|
@ -613,20 +613,17 @@ struct TypeFunctionInstanceType
|
|||||||
std::vector<TypePackId> packArguments;
|
std::vector<TypePackId> packArguments;
|
||||||
|
|
||||||
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
|
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
|
||||||
std::optional<AstExprFunction*> userFuncBody; // Body of the user-defined type function; only available for UDTFs
|
|
||||||
|
|
||||||
TypeFunctionInstanceType(
|
TypeFunctionInstanceType(
|
||||||
NotNull<const TypeFunction> function,
|
NotNull<const TypeFunction> function,
|
||||||
std::vector<TypeId> typeArguments,
|
std::vector<TypeId> typeArguments,
|
||||||
std::vector<TypePackId> packArguments,
|
std::vector<TypePackId> packArguments,
|
||||||
std::optional<AstName> userFuncName = std::nullopt,
|
std::optional<AstName> userFuncName = std::nullopt
|
||||||
std::optional<AstExprFunction*> userFuncBody = std::nullopt
|
|
||||||
)
|
)
|
||||||
: function(function)
|
: function(function)
|
||||||
, typeArguments(typeArguments)
|
, typeArguments(typeArguments)
|
||||||
, packArguments(packArguments)
|
, packArguments(packArguments)
|
||||||
, userFuncName(userFuncName)
|
, userFuncName(userFuncName)
|
||||||
, userFuncBody(userFuncBody)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1159,6 +1156,10 @@ TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, S
|
|||||||
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
|
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
|
||||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||||
|
|
||||||
|
// A tag to mark a type which doesn't derive directly from the root type as overriding the return of `typeof`.
|
||||||
|
// Any classes which derive from this type will have typeof return this type.
|
||||||
|
static constexpr char kTypeofRootTag[] = "typeofRoot";
|
||||||
|
|
||||||
void attachTag(TypeId ty, const std::string& tagName);
|
void attachTag(TypeId ty, const std::string& tagName);
|
||||||
void attachTag(Property& prop, const std::string& tagName);
|
void attachTag(Property& prop, const std::string& tagName);
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ struct Reasonings
|
|||||||
|
|
||||||
void check(
|
void check(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<UnifierSharedState> sharedState,
|
NotNull<UnifierSharedState> sharedState,
|
||||||
NotNull<TypeCheckLimits> limits,
|
NotNull<TypeCheckLimits> limits,
|
||||||
DcrLogger* logger,
|
DcrLogger* logger,
|
||||||
@ -70,6 +71,7 @@ void check(
|
|||||||
struct TypeChecker2
|
struct TypeChecker2
|
||||||
{
|
{
|
||||||
NotNull<BuiltinTypes> builtinTypes;
|
NotNull<BuiltinTypes> builtinTypes;
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||||
DcrLogger* logger;
|
DcrLogger* logger;
|
||||||
const NotNull<TypeCheckLimits> limits;
|
const NotNull<TypeCheckLimits> limits;
|
||||||
const NotNull<InternalErrorReporter> ice;
|
const NotNull<InternalErrorReporter> ice;
|
||||||
@ -83,12 +85,12 @@ struct TypeChecker2
|
|||||||
DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};
|
DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};
|
||||||
|
|
||||||
Normalizer normalizer;
|
Normalizer normalizer;
|
||||||
TypeFunctionRuntime typeFunctionRuntime;
|
|
||||||
Subtyping _subtyping;
|
Subtyping _subtyping;
|
||||||
NotNull<Subtyping> subtyping;
|
NotNull<Subtyping> subtyping;
|
||||||
|
|
||||||
TypeChecker2(
|
TypeChecker2(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<TypeCheckLimits> limits,
|
NotNull<TypeCheckLimits> limits,
|
||||||
DcrLogger* logger,
|
DcrLogger* logger,
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
struct lua_State;
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -20,11 +22,30 @@ struct TxnLog;
|
|||||||
struct ConstraintSolver;
|
struct ConstraintSolver;
|
||||||
class Normalizer;
|
class Normalizer;
|
||||||
|
|
||||||
|
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
|
||||||
|
|
||||||
struct TypeFunctionRuntime
|
struct TypeFunctionRuntime
|
||||||
{
|
{
|
||||||
|
TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits);
|
||||||
|
~TypeFunctionRuntime();
|
||||||
|
|
||||||
|
// Return value is an error message if registration failed
|
||||||
|
std::optional<std::string> registerFunction(AstStatTypeFunction* function);
|
||||||
|
|
||||||
// For user-defined type functions, we store all generated types and packs for the duration of the typecheck
|
// For user-defined type functions, we store all generated types and packs for the duration of the typecheck
|
||||||
TypedAllocator<TypeFunctionType> typeArena;
|
TypedAllocator<TypeFunctionType> typeArena;
|
||||||
TypedAllocator<TypeFunctionTypePackVar> typePackArena;
|
TypedAllocator<TypeFunctionTypePackVar> typePackArena;
|
||||||
|
|
||||||
|
NotNull<InternalErrorReporter> ice;
|
||||||
|
NotNull<TypeCheckLimits> limits;
|
||||||
|
|
||||||
|
StateRef state;
|
||||||
|
|
||||||
|
// Evaluation of type functions should only be performed in the absence of parse errors in the source module
|
||||||
|
bool allowEvaluation = true;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void prepareState();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeFunctionContext
|
struct TypeFunctionContext
|
||||||
@ -43,7 +64,6 @@ struct TypeFunctionContext
|
|||||||
const Constraint* constraint;
|
const Constraint* constraint;
|
||||||
|
|
||||||
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
|
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
|
||||||
std::optional<AstExprFunction*> userFuncBody; // Body of the user-defined type function; only available for UDTFs
|
|
||||||
|
|
||||||
TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint);
|
TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint);
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ struct Index
|
|||||||
/// Represents fields of a type or pack that contain a type.
|
/// Represents fields of a type or pack that contain a type.
|
||||||
enum class TypeField
|
enum class TypeField
|
||||||
{
|
{
|
||||||
|
/// The table of a metatable type.
|
||||||
|
Table,
|
||||||
/// The metatable of a type. This could be a metatable type, a primitive
|
/// The metatable of a type. This could be a metatable type, a primitive
|
||||||
/// type, a class type, or perhaps even a string singleton type.
|
/// type, a class type, or perhaps even a string singleton type.
|
||||||
Metatable,
|
Metatable,
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -509,6 +511,38 @@ static std::optional<DocumentationSymbol> checkOverloadedDocumentationSymbol(
|
|||||||
return documentationSymbol;
|
return documentationSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::optional<DocumentationSymbol> getMetatableDocumentation(
|
||||||
|
const Module& module,
|
||||||
|
AstExpr* parentExpr,
|
||||||
|
const TableType* mtable,
|
||||||
|
const AstName& index
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauDocumentationAtPosition);
|
||||||
|
auto indexIt = mtable->props.find("__index");
|
||||||
|
if (indexIt == mtable->props.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
TypeId followed = follow(indexIt->second.type());
|
||||||
|
const TableType* ttv = get<TableType>(followed);
|
||||||
|
if (!ttv)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto propIt = ttv->props.find(index.value);
|
||||||
|
if (propIt == ttv->props.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
if (auto ty = propIt->second.readTy)
|
||||||
|
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const SourceModule& source, const Module& module, Position position)
|
std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const SourceModule& source, const Module& module, Position position)
|
||||||
{
|
{
|
||||||
std::vector<AstNode*> ancestry = findAstAncestryOfPosition(source, position);
|
std::vector<AstNode*> ancestry = findAstAncestryOfPosition(source, position);
|
||||||
@ -541,15 +575,50 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
||||||
{
|
{
|
||||||
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
if (FFlag::LuauDocumentationAtPosition)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
while (ctv)
|
||||||
{
|
{
|
||||||
if (auto ty = propIt->second.readTy)
|
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
||||||
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
{
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
if (auto ty = propIt->second.readTy)
|
||||||
|
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return checkOverloadedDocumentationSymbol(
|
||||||
|
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
||||||
|
{
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
if (auto ty = propIt->second.readTy)
|
||||||
|
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return checkOverloadedDocumentationSymbol(
|
||||||
|
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FFlag::LuauDocumentationAtPosition)
|
||||||
|
{
|
||||||
|
if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
||||||
|
{
|
||||||
|
if (auto mtable = get<TableType>(*ptv->metatable))
|
||||||
|
{
|
||||||
|
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
|
||||||
|
return docSymbol;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,10 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
|||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
TypeFunctionRuntime typeFunctionRuntime; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{
|
||||||
|
NotNull{&iceReporter}, NotNull{&limits}
|
||||||
|
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
||||||
|
|
||||||
if (FFlag::LuauAutocompleteNewSolverLimit)
|
if (FFlag::LuauAutocompleteNewSolverLimit)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
LUAU_FASTINT(LuauCheckRecursionLimit);
|
LUAU_FASTINT(LuauCheckRecursionLimit);
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||||
|
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease);
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
@ -191,6 +192,7 @@ bool hasFreeType(TypeId ty)
|
|||||||
ConstraintGenerator::ConstraintGenerator(
|
ConstraintGenerator::ConstraintGenerator(
|
||||||
ModulePtr module,
|
ModulePtr module,
|
||||||
NotNull<Normalizer> normalizer,
|
NotNull<Normalizer> normalizer,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<ModuleResolver> moduleResolver,
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<InternalErrorReporter> ice,
|
NotNull<InternalErrorReporter> ice,
|
||||||
@ -206,6 +208,7 @@ ConstraintGenerator::ConstraintGenerator(
|
|||||||
, rootScope(nullptr)
|
, rootScope(nullptr)
|
||||||
, dfg(dfg)
|
, dfg(dfg)
|
||||||
, normalizer(normalizer)
|
, normalizer(normalizer)
|
||||||
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
, moduleResolver(moduleResolver)
|
, moduleResolver(moduleResolver)
|
||||||
, ice(ice)
|
, ice(ice)
|
||||||
, globalScope(globalScope)
|
, globalScope(globalScope)
|
||||||
@ -237,7 +240,8 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||||||
|
|
||||||
Checkpoint start = checkpoint(this);
|
Checkpoint start = checkpoint(this);
|
||||||
|
|
||||||
ControlFlow cf = visitBlockWithoutChildScope(scope, block);
|
ControlFlow cf =
|
||||||
|
DFInt::LuauTypeSolverRelease >= 646 ? visitBlockWithoutChildScope(scope, block) : visitBlockWithoutChildScope_DEPRECATED(scope, block);
|
||||||
if (cf == ControlFlow::None)
|
if (cf == ControlFlow::None)
|
||||||
addConstraint(scope, block->location, PackSubtypeConstraint{builtinTypes->emptyTypePack, rootScope->returnType});
|
addConstraint(scope, block->location, PackSubtypeConstraint{builtinTypes->emptyTypePack, rootScope->returnType});
|
||||||
|
|
||||||
@ -643,6 +647,109 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
|||||||
addConstraint(scope, location, c);
|
addConstraint(scope, location, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* block)
|
||||||
|
{
|
||||||
|
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
||||||
|
|
||||||
|
// In order to enable mutually-recursive type aliases, we need to
|
||||||
|
// populate the type bindings before we actually check any of the
|
||||||
|
// alias statements.
|
||||||
|
for (AstStat* stat : block->body)
|
||||||
|
{
|
||||||
|
if (auto alias = stat->as<AstStatTypeAlias>())
|
||||||
|
{
|
||||||
|
if (scope->exportedTypeBindings.count(alias->name.value) || scope->privateTypeBindings.count(alias->name.value))
|
||||||
|
{
|
||||||
|
auto it = aliasDefinitionLocations.find(alias->name.value);
|
||||||
|
LUAU_ASSERT(it != aliasDefinitionLocations.end());
|
||||||
|
reportError(alias->location, DuplicateTypeDefinition{alias->name.value, it->second});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A type alias might have no name if the code is syntactically
|
||||||
|
// illegal. We mustn't prepopulate anything in this case.
|
||||||
|
if (alias->name == kParseNameError || alias->name == "typeof")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ScopePtr defnScope = childScope(alias, scope);
|
||||||
|
|
||||||
|
TypeId initialType = arena->addType(BlockedType{});
|
||||||
|
TypeFun initialFun{initialType};
|
||||||
|
|
||||||
|
for (const auto& [name, gen] : createGenerics(defnScope, alias->generics, /* useCache */ true))
|
||||||
|
{
|
||||||
|
initialFun.typeParams.push_back(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [name, genPack] : createGenericPacks(defnScope, alias->genericPacks, /* useCache */ true))
|
||||||
|
{
|
||||||
|
initialFun.typePackParams.push_back(genPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alias->exported)
|
||||||
|
scope->exportedTypeBindings[alias->name.value] = std::move(initialFun);
|
||||||
|
else
|
||||||
|
scope->privateTypeBindings[alias->name.value] = std::move(initialFun);
|
||||||
|
|
||||||
|
astTypeAliasDefiningScopes[alias] = defnScope;
|
||||||
|
aliasDefinitionLocations[alias->name.value] = alias->location;
|
||||||
|
}
|
||||||
|
else if (auto function = stat->as<AstStatTypeFunction>())
|
||||||
|
{
|
||||||
|
// If a type function w/ same name has already been defined, error for having duplicates
|
||||||
|
if (scope->exportedTypeBindings.count(function->name.value) || scope->privateTypeBindings.count(function->name.value))
|
||||||
|
{
|
||||||
|
auto it = aliasDefinitionLocations.find(function->name.value);
|
||||||
|
LUAU_ASSERT(it != aliasDefinitionLocations.end());
|
||||||
|
reportError(function->location, DuplicateTypeDefinition{function->name.value, it->second});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope->parent != globalScope)
|
||||||
|
{
|
||||||
|
reportError(function->location, GenericError{"Local user-defined functions are not supported yet"});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopePtr defnScope = childScope(function, scope);
|
||||||
|
|
||||||
|
// Create TypeFunctionInstanceType
|
||||||
|
|
||||||
|
std::vector<TypeId> typeParams;
|
||||||
|
typeParams.reserve(function->body->args.size);
|
||||||
|
|
||||||
|
std::vector<GenericTypeDefinition> quantifiedTypeParams;
|
||||||
|
quantifiedTypeParams.reserve(function->body->args.size);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < function->body->args.size; i++)
|
||||||
|
{
|
||||||
|
std::string name = format("T%zu", i);
|
||||||
|
TypeId ty = arena->addType(GenericType{name});
|
||||||
|
typeParams.push_back(ty);
|
||||||
|
|
||||||
|
GenericTypeDefinition genericTy{ty};
|
||||||
|
quantifiedTypeParams.push_back(genericTy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::optional<std::string> error = typeFunctionRuntime->registerFunction(function))
|
||||||
|
reportError(function->location, GenericError{*error});
|
||||||
|
|
||||||
|
TypeId typeFunctionTy = arena->addType(TypeFunctionInstanceType{
|
||||||
|
NotNull{&builtinTypeFunctions().userFunc},
|
||||||
|
std::move(typeParams),
|
||||||
|
{},
|
||||||
|
function->name,
|
||||||
|
});
|
||||||
|
|
||||||
|
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
||||||
|
|
||||||
|
// Set type bindings and definition locations for this user-defined type function
|
||||||
|
scope->privateTypeBindings[function->name.value] = std::move(typeFunction);
|
||||||
|
aliasDefinitionLocations[function->name.value] = function->location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
RecursionCounter counter{&recursionCount};
|
RecursionCounter counter{&recursionCount};
|
||||||
@ -653,6 +760,29 @@ ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& sco
|
|||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkAliases(scope, block);
|
||||||
|
|
||||||
|
std::optional<ControlFlow> firstControlFlow;
|
||||||
|
for (AstStat* stat : block->body)
|
||||||
|
{
|
||||||
|
ControlFlow cf = visit(scope, stat);
|
||||||
|
if (cf != ControlFlow::None && !firstControlFlow)
|
||||||
|
firstControlFlow = cf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstControlFlow.value_or(ControlFlow::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlow ConstraintGenerator::visitBlockWithoutChildScope_DEPRECATED(const ScopePtr& scope, AstStatBlock* block)
|
||||||
|
{
|
||||||
|
RecursionCounter counter{&recursionCount};
|
||||||
|
|
||||||
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
||||||
|
{
|
||||||
|
reportCodeTooComplex(block->location);
|
||||||
|
return ControlFlow::None;
|
||||||
|
}
|
||||||
|
|
||||||
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
||||||
|
|
||||||
// In order to enable mutually-recursive type aliases, we need to
|
// In order to enable mutually-recursive type aliases, we need to
|
||||||
@ -709,6 +839,12 @@ ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& sco
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scope->parent != globalScope)
|
||||||
|
{
|
||||||
|
reportError(function->location, GenericError{"Local user-defined functions are not supported yet"});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ScopePtr defnScope = childScope(function, scope);
|
ScopePtr defnScope = childScope(function, scope);
|
||||||
|
|
||||||
// Create TypeFunctionInstanceType
|
// Create TypeFunctionInstanceType
|
||||||
@ -729,12 +865,14 @@ ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& sco
|
|||||||
quantifiedTypeParams.push_back(genericTy);
|
quantifiedTypeParams.push_back(genericTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::optional<std::string> error = typeFunctionRuntime->registerFunction(function))
|
||||||
|
reportError(function->location, GenericError{*error});
|
||||||
|
|
||||||
TypeId typeFunctionTy = arena->addType(TypeFunctionInstanceType{
|
TypeId typeFunctionTy = arena->addType(TypeFunctionInstanceType{
|
||||||
NotNull{&builtinTypeFunctions().userFunc},
|
NotNull{&builtinTypeFunctions().userFunc},
|
||||||
std::move(typeParams),
|
std::move(typeParams),
|
||||||
{},
|
{},
|
||||||
function->name,
|
function->name,
|
||||||
function->body,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
||||||
@ -1091,7 +1229,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatRepeat* rep
|
|||||||
{
|
{
|
||||||
ScopePtr repeatScope = childScope(repeat, scope);
|
ScopePtr repeatScope = childScope(repeat, scope);
|
||||||
|
|
||||||
visitBlockWithoutChildScope(repeatScope, repeat->body);
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
visitBlockWithoutChildScope(repeatScope, repeat->body);
|
||||||
|
else
|
||||||
|
visitBlockWithoutChildScope_DEPRECATED(repeatScope, repeat->body);
|
||||||
|
|
||||||
check(repeatScope, repeat->condition);
|
check(repeatScope, repeat->condition);
|
||||||
|
|
||||||
@ -1265,7 +1406,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatBlock* bloc
|
|||||||
{
|
{
|
||||||
ScopePtr innerScope = childScope(block, scope);
|
ScopePtr innerScope = childScope(block, scope);
|
||||||
|
|
||||||
ControlFlow flow = visitBlockWithoutChildScope(innerScope, block);
|
ControlFlow flow = DFInt::LuauTypeSolverRelease >= 646 ? visitBlockWithoutChildScope(innerScope, block)
|
||||||
|
: visitBlockWithoutChildScope_DEPRECATED(innerScope, block);
|
||||||
|
|
||||||
// An AstStatBlock has linear control flow, i.e. one entry and one exit, so we can inherit
|
// An AstStatBlock has linear control flow, i.e. one entry and one exit, so we can inherit
|
||||||
// all the changes to the environment occurred by the statements in that block.
|
// all the changes to the environment occurred by the statements in that block.
|
||||||
@ -1456,7 +1598,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||||||
TypeFun typeFunction = bindingIt->second;
|
TypeFun typeFunction = bindingIt->second;
|
||||||
|
|
||||||
// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
|
// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
|
||||||
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(typeFunction.type))
|
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(DFInt::LuauTypeSolverRelease >= 646 ? follow(typeFunction.type) : typeFunction.type))
|
||||||
{
|
{
|
||||||
TypeId expansionTy = arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
|
TypeId expansionTy = arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
|
||||||
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
|
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
|
||||||
@ -2511,7 +2653,7 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
|
|||||||
TypeId ty = follow(typeFun->type);
|
TypeId ty = follow(typeFun->type);
|
||||||
|
|
||||||
// We're only interested in the root class of any classes.
|
// We're only interested in the root class of any classes.
|
||||||
if (auto ctv = get<ClassType>(ty); ctv && ctv->parent == builtinTypes->classType)
|
if (auto ctv = get<ClassType>(ty); ctv && (ctv->parent == builtinTypes->classType || hasTag(ty, kTypeofRootTag)))
|
||||||
discriminantTy = ty;
|
discriminantTy = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2944,7 +3086,8 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||||||
void ConstraintGenerator::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
void ConstraintGenerator::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
||||||
{
|
{
|
||||||
// If it is possible for execution to reach the end of the function, the return type must be compatible with ()
|
// If it is possible for execution to reach the end of the function, the return type must be compatible with ()
|
||||||
ControlFlow cf = visitBlockWithoutChildScope(scope, fn->body);
|
ControlFlow cf =
|
||||||
|
DFInt::LuauTypeSolverRelease >= 646 ? visitBlockWithoutChildScope(scope, fn->body) : visitBlockWithoutChildScope_DEPRECATED(scope, fn->body);
|
||||||
if (cf == ControlFlow::None)
|
if (cf == ControlFlow::None)
|
||||||
addConstraint(scope, fn->location, PackSubtypeConstraint{builtinTypes->emptyTypePack, scope->returnType});
|
addConstraint(scope, fn->location, PackSubtypeConstraint{builtinTypes->emptyTypePack, scope->returnType});
|
||||||
}
|
}
|
||||||
|
@ -915,9 +915,19 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||||||
|
|
||||||
auto bindResult = [this, &c, constraint](TypeId result)
|
auto bindResult = [this, &c, constraint](TypeId result)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(get<PendingExpansionType>(c.target));
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
shiftReferences(c.target, result);
|
{
|
||||||
bind(constraint, c.target, result);
|
auto cTarget = follow(c.target);
|
||||||
|
LUAU_ASSERT(get<PendingExpansionType>(cTarget));
|
||||||
|
shiftReferences(cTarget, result);
|
||||||
|
bind(constraint, cTarget, result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(get<PendingExpansionType>(c.target));
|
||||||
|
shiftReferences(c.target, result);
|
||||||
|
bind(constraint, c.target, result);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
|
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
|
||||||
@ -945,7 +955,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||||||
// Due to how pending expansion types and TypeFun's are created
|
// Due to how pending expansion types and TypeFun's are created
|
||||||
// If this check passes, we have created a cyclic / corecursive type alias
|
// If this check passes, we have created a cyclic / corecursive type alias
|
||||||
// of size 0
|
// of size 0
|
||||||
TypeId lhs = c.target;
|
TypeId lhs = DFInt::LuauTypeSolverRelease >= 646 ? follow(c.target) : c.target;
|
||||||
TypeId rhs = tf->type;
|
TypeId rhs = tf->type;
|
||||||
if (occursCheck(lhs, rhs))
|
if (occursCheck(lhs, rhs))
|
||||||
{
|
{
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauImproveNonFunctionCallError, false)
|
|
||||||
|
|
||||||
static std::string wrongNumberOfArgsString(
|
static std::string wrongNumberOfArgsString(
|
||||||
size_t expectedCount,
|
size_t expectedCount,
|
||||||
std::optional<size_t> maximumCount,
|
std::optional<size_t> maximumCount,
|
||||||
@ -408,35 +406,30 @@ struct ErrorConverter
|
|||||||
|
|
||||||
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
||||||
{
|
{
|
||||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
if (auto unionTy = get<UnionType>(follow(e.ty)))
|
||||||
{
|
{
|
||||||
if (auto unionTy = get<UnionType>(follow(e.ty)))
|
std::string err = "Cannot call a value of the union type:";
|
||||||
|
|
||||||
|
for (auto option : unionTy)
|
||||||
{
|
{
|
||||||
std::string err = "Cannot call a value of the union type:";
|
option = follow(option);
|
||||||
|
|
||||||
for (auto option : unionTy)
|
if (get<FunctionType>(option) || findCallMetamethod(option))
|
||||||
{
|
{
|
||||||
option = follow(option);
|
err += "\n | " + toString(option);
|
||||||
|
continue;
|
||||||
if (get<FunctionType>(option) || findCallMetamethod(option))
|
|
||||||
{
|
|
||||||
err += "\n | " + toString(option);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// early-exit if we find something that isn't callable in the union.
|
|
||||||
return "Cannot call a value of type " + toString(option) + " in union:\n " + toString(e.ty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err += "\nWe are unable to determine the appropriate result type for such a call.";
|
// early-exit if we find something that isn't callable in the union.
|
||||||
|
return "Cannot call a value of type " + toString(option) + " in union:\n " + toString(e.ty);
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Cannot call a value of type " + toString(e.ty);
|
err += "\nWe are unable to determine the appropriate result type for such a call.";
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Cannot call non-function " + toString(e.ty);
|
return "Cannot call a value of type " + toString(e.ty);
|
||||||
}
|
}
|
||||||
std::string operator()(const Luau::ExtraInformation& e) const
|
std::string operator()(const Luau::ExtraInformation& e) const
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
|
|||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSourceModuleUpdatedWithSelectedMode, false)
|
LUAU_FASTFLAGVARIABLE(LuauSourceModuleUpdatedWithSelectedMode, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation, false)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
||||||
|
|
||||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
@ -205,72 +206,6 @@ LoadDefinitionFileResult Frontend::loadDefinitionFile(
|
|||||||
return LoadDefinitionFileResult{true, parseResult, sourceModule, checkedModule};
|
return LoadDefinitionFileResult{true, parseResult, sourceModule, checkedModule};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr)
|
|
||||||
{
|
|
||||||
const AstExprIndexName* indexName = pathExpr.as<AstExprIndexName>();
|
|
||||||
if (!indexName)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::vector<std::string_view> segments{indexName->index.value};
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (AstExprIndexName* in = indexName->expr->as<AstExprIndexName>())
|
|
||||||
{
|
|
||||||
segments.push_back(in->index.value);
|
|
||||||
indexName = in;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (AstExprGlobal* indexNameAsGlobal = indexName->expr->as<AstExprGlobal>())
|
|
||||||
{
|
|
||||||
segments.push_back(indexNameAsGlobal->name.value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (AstExprLocal* indexNameAsLocal = indexName->expr->as<AstExprLocal>())
|
|
||||||
{
|
|
||||||
segments.push_back(indexNameAsLocal->local->name.value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::reverse(segments.begin(), segments.end());
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments)
|
|
||||||
{
|
|
||||||
if (segments.empty())
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
std::vector<std::string_view> result;
|
|
||||||
|
|
||||||
auto it = segments.begin();
|
|
||||||
|
|
||||||
if (*it == "script" && !currentModuleName.empty())
|
|
||||||
{
|
|
||||||
result = split(currentModuleName, '/');
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; it != segments.end(); ++it)
|
|
||||||
{
|
|
||||||
if (result.size() > 1 && *it == "Parent")
|
|
||||||
result.pop_back();
|
|
||||||
else
|
|
||||||
result.push_back(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
return join(result, "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& pathExpr)
|
|
||||||
{
|
|
||||||
std::vector<std::string_view> segments = parsePathExpr(pathExpr);
|
|
||||||
return pathExprToModuleName(currentModuleName, segments);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1383,11 +1318,15 @@ ModulePtr check(
|
|||||||
unifierState.counters.iterationLimit = limits.unifierIterationLimit.value_or(FInt::LuauTypeInferIterationLimit);
|
unifierState.counters.iterationLimit = limits.unifierIterationLimit.value_or(FInt::LuauTypeInferIterationLimit);
|
||||||
|
|
||||||
Normalizer normalizer{&result->internalTypes, builtinTypes, NotNull{&unifierState}};
|
Normalizer normalizer{&result->internalTypes, builtinTypes, NotNull{&unifierState}};
|
||||||
TypeFunctionRuntime typeFunctionRuntime;
|
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
||||||
|
|
||||||
|
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
|
||||||
|
typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty();
|
||||||
|
|
||||||
ConstraintGenerator cg{
|
ConstraintGenerator cg{
|
||||||
result,
|
result,
|
||||||
NotNull{&normalizer},
|
NotNull{&normalizer},
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
moduleResolver,
|
moduleResolver,
|
||||||
builtinTypes,
|
builtinTypes,
|
||||||
iceHandler,
|
iceHandler,
|
||||||
@ -1463,12 +1402,23 @@ ModulePtr check(
|
|||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case Mode::Nonstrict:
|
case Mode::Nonstrict:
|
||||||
Luau::checkNonStrict(builtinTypes, iceHandler, NotNull{&unifierState}, NotNull{&dfg}, NotNull{&limits}, sourceModule, result.get());
|
Luau::checkNonStrict(
|
||||||
|
builtinTypes,
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
|
iceHandler,
|
||||||
|
NotNull{&unifierState},
|
||||||
|
NotNull{&dfg},
|
||||||
|
NotNull{&limits},
|
||||||
|
sourceModule,
|
||||||
|
result.get()
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case Mode::Definition:
|
case Mode::Definition:
|
||||||
// fallthrough intentional
|
// fallthrough intentional
|
||||||
case Mode::Strict:
|
case Mode::Strict:
|
||||||
Luau::check(builtinTypes, NotNull{&unifierState}, NotNull{&limits}, logger.get(), sourceModule, result.get());
|
Luau::check(
|
||||||
|
builtinTypes, NotNull{&typeFunctionRuntime}, NotNull{&unifierState}, NotNull{&limits}, logger.get(), sourceModule, result.get()
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case Mode::NoCheck:
|
case Mode::NoCheck:
|
||||||
break;
|
break;
|
||||||
|
@ -528,7 +528,12 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
DenseHashSet<TypePackId> uncacheablePacks{nullptr};
|
DenseHashSet<TypePackId> uncacheablePacks{nullptr};
|
||||||
|
|
||||||
explicit TypeCacher(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
explicit TypeCacher(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
||||||
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
// CLI-120975: once we roll out release 646, we _want_ to visit bound
|
||||||
|
// types to ensure they're marked as uncacheable if the types they are
|
||||||
|
// bound to are also uncacheable. Hence: if LuauTypeSolverRelease is
|
||||||
|
// less than 646, skip bound types (the prior behavior). Otherwise,
|
||||||
|
// do not skip bound types.
|
||||||
|
: TypeOnceVisitor(/* skipBoundTypes */ DFInt::LuauTypeSolverRelease < 646)
|
||||||
, cachedTypes(cachedTypes)
|
, cachedTypes(cachedTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -565,9 +570,33 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override
|
||||||
{
|
{
|
||||||
if (isUncacheable(ty) || isCached(ty))
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
// NOTE: `TypeCacher` should explicitly visit _all_ types and type packs,
|
||||||
|
// otherwise it's prone to marking types that cannot be cached as
|
||||||
|
// cacheable.
|
||||||
|
LUAU_ASSERT(false);
|
||||||
|
LUAU_UNREACHABLE();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const BoundType& btv) override
|
||||||
|
{
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
traverse(btv.boundTo);
|
||||||
|
if (isUncacheable(btv.boundTo))
|
||||||
|
markUncacheable(ty);
|
||||||
return false;
|
return false;
|
||||||
return true;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
@ -592,6 +621,19 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const ErrorType&) override
|
||||||
|
{
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
cache(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const PrimitiveType&) override
|
bool visit(TypeId ty, const PrimitiveType&) override
|
||||||
{
|
{
|
||||||
cache(ty);
|
cache(ty);
|
||||||
@ -729,6 +771,24 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const MetatableType& mtv) override
|
||||||
|
{
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
traverse(mtv.table);
|
||||||
|
traverse(mtv.metatable);
|
||||||
|
if (isUncacheable(mtv.table) || isUncacheable(mtv.metatable))
|
||||||
|
markUncacheable(ty);
|
||||||
|
else
|
||||||
|
cache(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ClassType&) override
|
||||||
{
|
{
|
||||||
cache(ty);
|
cache(ty);
|
||||||
@ -843,12 +903,38 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp) override
|
||||||
|
{
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
// NOTE: `TypeCacher` should explicitly visit _all_ types and type packs,
|
||||||
|
// otherwise it's prone to marking types that cannot be cached as
|
||||||
|
// cacheable, which will segfault down the line.
|
||||||
|
LUAU_ASSERT(false);
|
||||||
|
LUAU_UNREACHABLE();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp, const FreeTypePack&) override
|
bool visit(TypePackId tp, const FreeTypePack&) override
|
||||||
{
|
{
|
||||||
markUncacheable(tp);
|
markUncacheable(tp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp, const GenericTypePack& gtp) override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp, const Unifiable::Error& etp) override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp, const VariadicTypePack& vtp) override
|
bool visit(TypePackId tp, const VariadicTypePack& vtp) override
|
||||||
{
|
{
|
||||||
if (isUncacheable(tp))
|
if (isUncacheable(tp))
|
||||||
@ -884,6 +970,27 @@ struct TypeCacher : TypeOnceVisitor
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp, const TypePack& typ) override
|
||||||
|
{
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
bool uncacheable = false;
|
||||||
|
for (TypeId ty : typ.head)
|
||||||
|
{
|
||||||
|
traverse(ty);
|
||||||
|
uncacheable |= isUncacheable(ty);
|
||||||
|
}
|
||||||
|
if (typ.tail)
|
||||||
|
{
|
||||||
|
traverse(*typ.tail);
|
||||||
|
uncacheable |= isUncacheable(*typ.tail);
|
||||||
|
}
|
||||||
|
if (uncacheable)
|
||||||
|
markUncacheable(tp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<TypeId> generalize(
|
std::optional<TypeId> generalize(
|
||||||
|
@ -146,7 +146,20 @@ struct ClonePublicInterface : Substitution
|
|||||||
{
|
{
|
||||||
if (auto freety = getMutable<FreeType>(result))
|
if (auto freety = getMutable<FreeType>(result))
|
||||||
{
|
{
|
||||||
freety->scope = nullptr;
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
module->errors.emplace_back(
|
||||||
|
freety->scope->location,
|
||||||
|
module->name,
|
||||||
|
InternalError{"Free type is escaping its module; please report this bug at "
|
||||||
|
"https://github.com/luau-lang/luau/issues"}
|
||||||
|
);
|
||||||
|
result = builtinTypes->errorRecoveryType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
freety->scope = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (auto genericty = getMutable<GenericType>(result))
|
else if (auto genericty = getMutable<GenericType>(result))
|
||||||
{
|
{
|
||||||
@ -159,7 +172,35 @@ struct ClonePublicInterface : Substitution
|
|||||||
|
|
||||||
TypePackId clean(TypePackId tp) override
|
TypePackId clean(TypePackId tp) override
|
||||||
{
|
{
|
||||||
return clone(tp);
|
if (FFlag::LuauSolverV2 && DFInt::LuauTypeSolverRelease >= 645)
|
||||||
|
{
|
||||||
|
auto clonedTp = clone(tp);
|
||||||
|
if (auto ftp = getMutable<FreeTypePack>(clonedTp))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
module->errors.emplace_back(
|
||||||
|
ftp->scope->location,
|
||||||
|
module->name,
|
||||||
|
InternalError{"Free type pack is escaping its module; please report this bug at "
|
||||||
|
"https://github.com/luau-lang/luau/issues"}
|
||||||
|
);
|
||||||
|
clonedTp = builtinTypes->errorRecoveryTypePack();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ftp->scope = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto gtp = getMutable<GenericTypePack>(clonedTp))
|
||||||
|
gtp->scope = nullptr;
|
||||||
|
return clonedTp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return clone(tp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId cloneType(TypeId ty)
|
TypeId cloneType(TypeId ty)
|
||||||
|
@ -154,13 +154,12 @@ private:
|
|||||||
|
|
||||||
struct NonStrictTypeChecker
|
struct NonStrictTypeChecker
|
||||||
{
|
{
|
||||||
|
|
||||||
NotNull<BuiltinTypes> builtinTypes;
|
NotNull<BuiltinTypes> builtinTypes;
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||||
const NotNull<InternalErrorReporter> ice;
|
const NotNull<InternalErrorReporter> ice;
|
||||||
NotNull<TypeArena> arena;
|
NotNull<TypeArena> arena;
|
||||||
Module* module;
|
Module* module;
|
||||||
Normalizer normalizer;
|
Normalizer normalizer;
|
||||||
TypeFunctionRuntime typeFunctionRuntime;
|
|
||||||
Subtyping subtyping;
|
Subtyping subtyping;
|
||||||
NotNull<const DataFlowGraph> dfg;
|
NotNull<const DataFlowGraph> dfg;
|
||||||
DenseHashSet<TypeId> noTypeFunctionErrors{nullptr};
|
DenseHashSet<TypeId> noTypeFunctionErrors{nullptr};
|
||||||
@ -172,6 +171,7 @@ struct NonStrictTypeChecker
|
|||||||
NonStrictTypeChecker(
|
NonStrictTypeChecker(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
const NotNull<InternalErrorReporter> ice,
|
const NotNull<InternalErrorReporter> ice,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<const DataFlowGraph> dfg,
|
NotNull<const DataFlowGraph> dfg,
|
||||||
@ -179,11 +179,12 @@ struct NonStrictTypeChecker
|
|||||||
Module* module
|
Module* module
|
||||||
)
|
)
|
||||||
: builtinTypes(builtinTypes)
|
: builtinTypes(builtinTypes)
|
||||||
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
, ice(ice)
|
, ice(ice)
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
, module(module)
|
, module(module)
|
||||||
, normalizer{arena, builtinTypes, unifierState, /* cache inhabitance */ true}
|
, normalizer{arena, builtinTypes, unifierState, /* cache inhabitance */ true}
|
||||||
, subtyping{builtinTypes, arena, NotNull(&normalizer), NotNull(&typeFunctionRuntime), ice}
|
, subtyping{builtinTypes, arena, NotNull(&normalizer), typeFunctionRuntime, ice}
|
||||||
, dfg(dfg)
|
, dfg(dfg)
|
||||||
, limits(limits)
|
, limits(limits)
|
||||||
{
|
{
|
||||||
@ -228,14 +229,13 @@ struct NonStrictTypeChecker
|
|||||||
if (noTypeFunctionErrors.find(instance))
|
if (noTypeFunctionErrors.find(instance))
|
||||||
return instance;
|
return instance;
|
||||||
|
|
||||||
ErrorVec errors =
|
ErrorVec errors = reduceTypeFunctions(
|
||||||
reduceTypeFunctions(
|
instance,
|
||||||
instance,
|
location,
|
||||||
location,
|
TypeFunctionContext{arena, builtinTypes, stack.back(), NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
|
||||||
TypeFunctionContext{arena, builtinTypes, stack.back(), NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, ice, limits},
|
true
|
||||||
true
|
)
|
||||||
)
|
.errors;
|
||||||
.errors;
|
|
||||||
|
|
||||||
if (errors.empty())
|
if (errors.empty())
|
||||||
noTypeFunctionErrors.insert(instance);
|
noTypeFunctionErrors.insert(instance);
|
||||||
@ -760,6 +760,7 @@ private:
|
|||||||
|
|
||||||
void checkNonStrict(
|
void checkNonStrict(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<InternalErrorReporter> ice,
|
NotNull<InternalErrorReporter> ice,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<const DataFlowGraph> dfg,
|
NotNull<const DataFlowGraph> dfg,
|
||||||
@ -770,7 +771,7 @@ void checkNonStrict(
|
|||||||
{
|
{
|
||||||
LUAU_TIMETRACE_SCOPE("checkNonStrict", "Typechecking");
|
LUAU_TIMETRACE_SCOPE("checkNonStrict", "Typechecking");
|
||||||
|
|
||||||
NonStrictTypeChecker typeChecker{NotNull{&module->internalTypes}, builtinTypes, ice, unifierState, dfg, limits, module};
|
NonStrictTypeChecker typeChecker{NotNull{&module->internalTypes}, builtinTypes, typeFunctionRuntime, ice, unifierState, dfg, limits, module};
|
||||||
typeChecker.visit(sourceModule.root);
|
typeChecker.visit(sourceModule.root);
|
||||||
unfreeze(module->interfaceTypes);
|
unfreeze(module->interfaceTypes);
|
||||||
copyErrors(module->errors, module->interfaceTypes, builtinTypes);
|
copyErrors(module->errors, module->interfaceTypes, builtinTypes);
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
#include "Luau/Unifier.h"
|
#include "Luau/Unifier.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeAwayUninhabitableTables, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeNotUnknownIntersection, false);
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixReduceStackPressure, false);
|
LUAU_FASTFLAGVARIABLE(LuauFixReduceStackPressure, false);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicTablesBlowingStack, false);
|
LUAU_FASTFLAGVARIABLE(LuauFixCyclicTablesBlowingStack, false);
|
||||||
|
|
||||||
@ -40,12 +38,6 @@ static bool fixCyclicTablesBlowingStack()
|
|||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
// helper to make `FFlag::LuauNormalizeAwayUninhabitableTables` not explicitly required when DCR is enabled.
|
|
||||||
static bool normalizeAwayUninhabitableTables()
|
|
||||||
{
|
|
||||||
return FFlag::LuauNormalizeAwayUninhabitableTables || FFlag::LuauSolverV2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool shouldEarlyExit(NormalizationResult res)
|
static bool shouldEarlyExit(NormalizationResult res)
|
||||||
{
|
{
|
||||||
// if res is hit limits, return control flow
|
// if res is hit limits, return control flow
|
||||||
@ -1621,7 +1613,7 @@ void Normalizer::unionTablesWithTable(TypeIds& heres, TypeId there)
|
|||||||
// TODO: remove unions of tables where possible
|
// TODO: remove unions of tables where possible
|
||||||
|
|
||||||
// we can always skip `never`
|
// we can always skip `never`
|
||||||
if (normalizeAwayUninhabitableTables() && get<NeverType>(there))
|
if (get<NeverType>(there))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
heres.insert(there);
|
heres.insert(there);
|
||||||
@ -2619,13 +2611,12 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
|||||||
seenSet.erase(*tprop.readTy);
|
seenSet.erase(*tprop.readTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalizeAwayUninhabitableTables() && NormalizationResult::True != res)
|
if (NormalizationResult::True != res)
|
||||||
return {builtinTypes->neverType};
|
return {builtinTypes->neverType};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (normalizeAwayUninhabitableTables() &&
|
if (NormalizationResult::False == isIntersectionInhabited(*hprop.readTy, *tprop.readTy))
|
||||||
NormalizationResult::False == isIntersectionInhabited(*hprop.readTy, *tprop.readTy))
|
|
||||||
return {builtinTypes->neverType};
|
return {builtinTypes->neverType};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3258,7 +3249,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(NormalizedType& here, Type
|
|||||||
// this is a noop since an intersection with `unknown` is trivial.
|
// this is a noop since an intersection with `unknown` is trivial.
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
else if ((FFlag::LuauNormalizeNotUnknownIntersection || FFlag::LuauSolverV2) && get<UnknownType>(t))
|
else if (get<UnknownType>(t))
|
||||||
{
|
{
|
||||||
// if we're intersecting with `~unknown`, this is equivalent to intersecting with `never`
|
// if we're intersecting with `~unknown`, this is equivalent to intersecting with `never`
|
||||||
// this means we should clear the type entirely.
|
// this means we should clear the type entirely.
|
||||||
@ -3434,7 +3425,10 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<Built
|
|||||||
UnifierSharedState sharedState{&ice};
|
UnifierSharedState sharedState{&ice};
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
TypeFunctionRuntime typeFunctionRuntime; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{
|
||||||
|
NotNull{&ice}, NotNull{&limits}
|
||||||
|
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
||||||
|
|
||||||
// Subtyping under DCR is not implemented using unification!
|
// Subtyping under DCR is not implemented using unification!
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
@ -3457,7 +3451,10 @@ bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, N
|
|||||||
UnifierSharedState sharedState{&ice};
|
UnifierSharedState sharedState{&ice};
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
TypeFunctionRuntime typeFunctionRuntime; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{
|
||||||
|
NotNull{&ice}, NotNull{&limits}
|
||||||
|
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
|
||||||
|
|
||||||
// Subtyping under DCR is not implemented using unification!
|
// Subtyping under DCR is not implemented using unification!
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -127,7 +127,7 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
|||||||
return dest.addType(NegationType{a.ty});
|
return dest.addType(NegationType{a.ty});
|
||||||
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
||||||
{
|
{
|
||||||
TypeFunctionInstanceType clone{a.function, a.typeArguments, a.packArguments, a.userFuncName, a.userFuncBody};
|
TypeFunctionInstanceType clone{a.function, a.typeArguments, a.packArguments, a.userFuncName};
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1455,8 +1455,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
|||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope)
|
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope)
|
||||||
{
|
{
|
||||||
return isCovariantWith(env, subMt->table, superMt->table, scope)
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
.andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable, scope).withBothComponent(TypePath::TypeField::Metatable));
|
{
|
||||||
|
return isCovariantWith(env, subMt->table, superMt->table, scope)
|
||||||
|
.withBothComponent(TypePath::TypeField::Table)
|
||||||
|
.andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable, scope).withBothComponent(TypePath::TypeField::Metatable));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return isCovariantWith(env, subMt->table, superMt->table, scope)
|
||||||
|
.andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable, scope).withBothComponent(TypePath::TypeField::Metatable));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope)
|
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope)
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
@ -268,6 +268,7 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor
|
|||||||
|
|
||||||
void check(
|
void check(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<TypeCheckLimits> limits,
|
NotNull<TypeCheckLimits> limits,
|
||||||
DcrLogger* logger,
|
DcrLogger* logger,
|
||||||
@ -277,7 +278,7 @@ void check(
|
|||||||
{
|
{
|
||||||
LUAU_TIMETRACE_SCOPE("check", "Typechecking");
|
LUAU_TIMETRACE_SCOPE("check", "Typechecking");
|
||||||
|
|
||||||
TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module};
|
TypeChecker2 typeChecker{builtinTypes, typeFunctionRuntime, unifierState, limits, logger, &sourceModule, module};
|
||||||
|
|
||||||
typeChecker.visit(sourceModule.root);
|
typeChecker.visit(sourceModule.root);
|
||||||
|
|
||||||
@ -294,6 +295,7 @@ void check(
|
|||||||
|
|
||||||
TypeChecker2::TypeChecker2(
|
TypeChecker2::TypeChecker2(
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
NotNull<UnifierSharedState> unifierState,
|
NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<TypeCheckLimits> limits,
|
NotNull<TypeCheckLimits> limits,
|
||||||
DcrLogger* logger,
|
DcrLogger* logger,
|
||||||
@ -301,13 +303,14 @@ TypeChecker2::TypeChecker2(
|
|||||||
Module* module
|
Module* module
|
||||||
)
|
)
|
||||||
: builtinTypes(builtinTypes)
|
: builtinTypes(builtinTypes)
|
||||||
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
, logger(logger)
|
, logger(logger)
|
||||||
, limits(limits)
|
, limits(limits)
|
||||||
, ice(unifierState->iceHandler)
|
, ice(unifierState->iceHandler)
|
||||||
, sourceModule(sourceModule)
|
, sourceModule(sourceModule)
|
||||||
, module(module)
|
, module(module)
|
||||||
, normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true}
|
, normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true}
|
||||||
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{unifierState->iceHandler}}
|
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, typeFunctionRuntime, NotNull{unifierState->iceHandler}}
|
||||||
, subtyping(&_subtyping)
|
, subtyping(&_subtyping)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -489,9 +492,7 @@ TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location l
|
|||||||
reduceTypeFunctions(
|
reduceTypeFunctions(
|
||||||
instance,
|
instance,
|
||||||
location,
|
location,
|
||||||
TypeFunctionContext{
|
TypeFunctionContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, typeFunctionRuntime, ice, limits},
|
||||||
NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, ice, limits
|
|
||||||
},
|
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
.errors;
|
.errors;
|
||||||
@ -1198,7 +1199,7 @@ void TypeChecker2::visit(AstStatTypeAlias* stat)
|
|||||||
void TypeChecker2::visit(AstStatTypeFunction* stat)
|
void TypeChecker2::visit(AstStatTypeFunction* stat)
|
||||||
{
|
{
|
||||||
// TODO: add type checking for user-defined type functions
|
// TODO: add type checking for user-defined type functions
|
||||||
if (!FFlag::LuauUserDefinedTypeFunctions)
|
if (!FFlag::LuauUserDefinedTypeFunctions2)
|
||||||
reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}});
|
reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1450,7 +1451,7 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
|||||||
builtinTypes,
|
builtinTypes,
|
||||||
NotNull{&module->internalTypes},
|
NotNull{&module->internalTypes},
|
||||||
NotNull{&normalizer},
|
NotNull{&normalizer},
|
||||||
NotNull{&typeFunctionRuntime},
|
typeFunctionRuntime,
|
||||||
NotNull{stack.back()},
|
NotNull{stack.back()},
|
||||||
ice,
|
ice,
|
||||||
limits,
|
limits,
|
||||||
|
@ -46,7 +46,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
|||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions, false)
|
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2, false)
|
||||||
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||||
|
|
||||||
@ -375,7 +376,6 @@ struct TypeFunctionReducer
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ctx.userFuncName = tfit->userFuncName;
|
ctx.userFuncName = tfit->userFuncName;
|
||||||
ctx.userFuncBody = tfit->userFuncBody;
|
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
TypeFunctionReductionResult<TypeId> result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
||||||
handleTypeFunctionReduction(subject, result);
|
handleTypeFunctionReduction(subject, result);
|
||||||
@ -416,6 +416,20 @@ struct TypeFunctionReducer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LuauTempThreadPopper
|
||||||
|
{
|
||||||
|
explicit LuauTempThreadPopper(lua_State* L)
|
||||||
|
: L(L)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~LuauTempThreadPopper()
|
||||||
|
{
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_State* L = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
static FunctionGraphReductionResult reduceFunctionsInternal(
|
static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||||
VecDeque<TypeId> queuedTys,
|
VecDeque<TypeId> queuedTys,
|
||||||
VecDeque<TypePackId> queuedTps,
|
VecDeque<TypePackId> queuedTps,
|
||||||
@ -586,8 +600,6 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
|
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
TypeId instance,
|
TypeId instance,
|
||||||
const std::vector<TypeId>& typeParams,
|
const std::vector<TypeId>& typeParams,
|
||||||
@ -595,12 +607,19 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||||||
NotNull<TypeFunctionContext> ctx
|
NotNull<TypeFunctionContext> ctx
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!ctx->userFuncName || !ctx->userFuncBody)
|
if (!ctx->userFuncName)
|
||||||
{
|
{
|
||||||
ctx->ice->ice("all user-defined type functions must have an associated function definition");
|
ctx->ice->ice("all user-defined type functions must have an associated function definition");
|
||||||
return {std::nullopt, true, {}, {}};
|
return {std::nullopt, true, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
|
||||||
|
{
|
||||||
|
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
||||||
|
if (!ctx->typeFunctionRuntime->allowEvaluation)
|
||||||
|
return {ctx->builtins->errorRecoveryType(), false, {}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
{
|
{
|
||||||
TypeId ty = follow(typeParam);
|
TypeId ty = follow(typeParam);
|
||||||
@ -611,62 +630,18 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AstName name = *ctx->userFuncName;
|
AstName name = *ctx->userFuncName;
|
||||||
AstExprFunction* function = *ctx->userFuncBody;
|
|
||||||
|
|
||||||
// Construct ParseResult containing the type function
|
lua_State* global = ctx->typeFunctionRuntime->state.get();
|
||||||
Allocator allocator;
|
|
||||||
AstNameTable names(allocator);
|
|
||||||
|
|
||||||
AstExprGlobal globalName{Location{}, name};
|
if (global == nullptr)
|
||||||
AstStatFunction typeFunction{Location{}, &globalName, function};
|
return {std::nullopt, true, {}, {}, format("'%s' type function: cannot be evaluated in this context", name.value)};
|
||||||
AstStat* stmtArray[] = {&typeFunction};
|
|
||||||
AstArray<AstStat*> stmts{stmtArray, 1};
|
|
||||||
AstStatBlock exec{Location{}, stmts};
|
|
||||||
ParseResult parseResult{&exec, 1};
|
|
||||||
|
|
||||||
BytecodeBuilder builder;
|
// Separate sandboxed thread for individual execution and private globals
|
||||||
try
|
lua_State* L = lua_newthread(global);
|
||||||
{
|
LuauTempThreadPopper popper(global);
|
||||||
compileOrThrow(builder, parseResult, names);
|
|
||||||
}
|
|
||||||
catch (CompileError& e)
|
|
||||||
{
|
|
||||||
std::string errMsg = format("'%s' type function failed to compile with error message: %s", name.value, e.what());
|
|
||||||
return {std::nullopt, true, {}, {}, errMsg};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string bytecode = builder.getBytecode();
|
lua_getglobal(global, name.value);
|
||||||
|
lua_xmove(global, L, 1);
|
||||||
// Initialize Lua state
|
|
||||||
StateRef globalState(lua_newstate(typeFunctionAlloc, nullptr), lua_close);
|
|
||||||
lua_State* L = globalState.get();
|
|
||||||
|
|
||||||
lua_setthreaddata(L, ctx.get());
|
|
||||||
|
|
||||||
setTypeFunctionEnvironment(L);
|
|
||||||
|
|
||||||
// Register type userdata
|
|
||||||
registerTypeUserData(L);
|
|
||||||
|
|
||||||
luaL_sandbox(L);
|
|
||||||
luaL_sandboxthread(L);
|
|
||||||
|
|
||||||
// Load bytecode into Luau state
|
|
||||||
if (auto error = checkResultForError(L, name.value, luau_load(L, name.value, bytecode.data(), bytecode.size(), 0)))
|
|
||||||
return {std::nullopt, true, {}, {}, error};
|
|
||||||
|
|
||||||
// Execute the loaded chunk to register the function in the global environment
|
|
||||||
if (auto error = checkResultForError(L, name.value, lua_pcall(L, 0, 0, 0)))
|
|
||||||
return {std::nullopt, true, {}, {}, error};
|
|
||||||
|
|
||||||
// Get type function from the global environment
|
|
||||||
lua_getglobal(L, name.value);
|
|
||||||
if (!lua_isfunction(L, -1))
|
|
||||||
{
|
|
||||||
std::string errMsg = format("Could not find '%s' type function in the global scope", name.value);
|
|
||||||
|
|
||||||
return {std::nullopt, true, {}, {}, errMsg};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push serialized arguments onto the stack
|
// Push serialized arguments onto the stack
|
||||||
|
|
||||||
@ -690,15 +665,15 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||||||
// Set up an interrupt handler for type functions to respect type checking limits and LSP cancellation requests.
|
// Set up an interrupt handler for type functions to respect type checking limits and LSP cancellation requests.
|
||||||
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
|
lua_callbacks(L)->interrupt = [](lua_State* L, int gc)
|
||||||
{
|
{
|
||||||
auto ctx = static_cast<const TypeFunctionContext*>(lua_getthreaddata(lua_mainthread(L)));
|
auto ctx = static_cast<const TypeFunctionRuntime*>(lua_getthreaddata(lua_mainthread(L)));
|
||||||
if (ctx->limits->finishTime && TimeTrace::getClock() > *ctx->limits->finishTime)
|
if (ctx->limits->finishTime && TimeTrace::getClock() > *ctx->limits->finishTime)
|
||||||
ctx->solver->throwTimeLimitError();
|
throw TimeLimitError(ctx->ice->moduleName);
|
||||||
|
|
||||||
if (ctx->limits->cancellationToken && ctx->limits->cancellationToken->requested())
|
if (ctx->limits->cancellationToken && ctx->limits->cancellationToken->requested())
|
||||||
ctx->solver->throwUserCancelError();
|
throw UserCancelError(ctx->ice->moduleName);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (auto error = checkResultForError(L, name.value, lua_resume(L, nullptr, int(typeParams.size()))))
|
if (auto error = checkResultForError(L, name.value, lua_pcall(L, int(typeParams.size()), 1, 0)))
|
||||||
return {std::nullopt, true, {}, {}, error};
|
return {std::nullopt, true, {}, {}, error};
|
||||||
|
|
||||||
// If the return value is not a type userdata, return with error message
|
// If the return value is not a type userdata, return with error message
|
||||||
@ -796,7 +771,8 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
|||||||
return {ctx->builtins->numberType, false, {}, {}};
|
return {ctx->builtins->numberType, false, {}, {}};
|
||||||
|
|
||||||
// we use the normalized operand here in case there was an intersection or union.
|
// we use the normalized operand here in case there was an intersection or union.
|
||||||
TypeId normalizedOperand = ctx->normalizer->typeFromNormal(*normTy);
|
TypeId normalizedOperand =
|
||||||
|
DFInt::LuauTypeSolverRelease >= 646 ? follow(ctx->normalizer->typeFromNormal(*normTy)) : ctx->normalizer->typeFromNormal(*normTy);
|
||||||
if (normTy->hasTopTable() || get<TableType>(normalizedOperand))
|
if (normTy->hasTopTable() || get<TableType>(normalizedOperand))
|
||||||
return {ctx->builtins->numberType, false, {}, {}};
|
return {ctx->builtins->numberType, false, {}, {}};
|
||||||
|
|
||||||
@ -947,6 +923,108 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
|||||||
return {std::nullopt, true, {}, {}};
|
return {std::nullopt, true, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dummyStateClose(lua_State*) {}
|
||||||
|
|
||||||
|
TypeFunctionRuntime::TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits)
|
||||||
|
: ice(ice)
|
||||||
|
, limits(limits)
|
||||||
|
, state(nullptr, dummyStateClose)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeFunctionRuntime::~TypeFunctionRuntime() {}
|
||||||
|
|
||||||
|
std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunction* function)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
|
||||||
|
{
|
||||||
|
// If evaluation is disabled, we do not generate additional error messages
|
||||||
|
if (!allowEvaluation)
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareState();
|
||||||
|
|
||||||
|
AstName name = function->name;
|
||||||
|
|
||||||
|
// Construct ParseResult containing the type function
|
||||||
|
Allocator allocator;
|
||||||
|
AstNameTable names(allocator);
|
||||||
|
|
||||||
|
AstExpr* exprFunction = function->body;
|
||||||
|
AstArray<AstExpr*> exprReturns{&exprFunction, 1};
|
||||||
|
AstStatReturn stmtReturn{Location{}, exprReturns};
|
||||||
|
AstStat* stmtArray[] = {&stmtReturn};
|
||||||
|
AstArray<AstStat*> stmts{stmtArray, 1};
|
||||||
|
AstStatBlock exec{Location{}, stmts};
|
||||||
|
ParseResult parseResult{&exec, 1};
|
||||||
|
|
||||||
|
BytecodeBuilder builder;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compileOrThrow(builder, parseResult, names);
|
||||||
|
}
|
||||||
|
catch (CompileError& e)
|
||||||
|
{
|
||||||
|
return format("'%s' type function failed to compile with error message: %s", name.value, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bytecode = builder.getBytecode();
|
||||||
|
|
||||||
|
lua_State* global = state.get();
|
||||||
|
|
||||||
|
// Separate sandboxed thread for individual execution and private globals
|
||||||
|
lua_State* L = lua_newthread(global);
|
||||||
|
LuauTempThreadPopper popper(global);
|
||||||
|
|
||||||
|
// Create individual environment for the type function
|
||||||
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
|
// Do not allow global writes to that environment
|
||||||
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
lua_setreadonly(L, -1, true);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// Load bytecode into Luau state
|
||||||
|
if (auto error = checkResultForError(L, name.value, luau_load(L, name.value, bytecode.data(), bytecode.size(), 0)))
|
||||||
|
return error;
|
||||||
|
|
||||||
|
// Execute the global function which should return our user-defined type function
|
||||||
|
if (auto error = checkResultForError(L, name.value, lua_resume(L, nullptr, 0)))
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (!lua_isfunction(L, -1))
|
||||||
|
{
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return format("Could not find '%s' type function in the global scope", name.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store resulting function in the global environment
|
||||||
|
lua_xmove(L, global, 1);
|
||||||
|
lua_setglobal(global, name.value);
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeFunctionRuntime::prepareState()
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = StateRef(lua_newstate(typeFunctionAlloc, nullptr), lua_close);
|
||||||
|
lua_State* L = state.get();
|
||||||
|
|
||||||
|
lua_setthreaddata(L, this);
|
||||||
|
|
||||||
|
setTypeFunctionEnvironment(L);
|
||||||
|
|
||||||
|
// Register type userdata
|
||||||
|
registerTypeUserData(L);
|
||||||
|
|
||||||
|
luaL_sandbox(L);
|
||||||
|
luaL_sandboxthread(L);
|
||||||
|
}
|
||||||
|
|
||||||
TypeFunctionContext::TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint)
|
TypeFunctionContext::TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint)
|
||||||
: arena(cs->arena)
|
: arena(cs->arena)
|
||||||
, builtins(cs->builtinTypes)
|
, builtins(cs->builtinTypes)
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// defined in TypeFunctionRuntimeBuilder.cpp
|
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit);
|
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
@ -63,21 +63,21 @@ std::optional<std::string> checkResultForError(lua_State* L, const char* typeFun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeFunctionContext* getTypeFunctionContext(lua_State* L)
|
static TypeFunctionRuntime* getTypeFunctionRuntime(lua_State* L)
|
||||||
{
|
{
|
||||||
return static_cast<const TypeFunctionContext*>(lua_getthreaddata(lua_mainthread(L)));
|
return static_cast<TypeFunctionRuntime*>(lua_getthreaddata(lua_mainthread(L)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionType* allocateTypeFunctionType(lua_State* L, TypeFunctionTypeVariant type)
|
TypeFunctionType* allocateTypeFunctionType(lua_State* L, TypeFunctionTypeVariant type)
|
||||||
{
|
{
|
||||||
auto ctx = getTypeFunctionContext(L);
|
auto ctx = getTypeFunctionRuntime(L);
|
||||||
return ctx->typeFunctionRuntime->typeArena.allocate(std::move(type));
|
return ctx->typeArena.allocate(std::move(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionTypePackVar* allocateTypeFunctionTypePack(lua_State* L, TypeFunctionTypePackVariant type)
|
TypeFunctionTypePackVar* allocateTypeFunctionTypePack(lua_State* L, TypeFunctionTypePackVariant type)
|
||||||
{
|
{
|
||||||
auto ctx = getTypeFunctionContext(L);
|
auto ctx = getTypeFunctionRuntime(L);
|
||||||
return ctx->typeFunctionRuntime->typePackArena.allocate(std::move(type));
|
return ctx->typePackArena.allocate(std::move(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a new type userdata onto the stack
|
// Pushes a new type userdata onto the stack
|
||||||
@ -678,7 +678,7 @@ static int writeTableProp(lua_State* L)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Luau: `self:setindexer(key: type, value: type)`
|
// Luau: `self:setindexer(key: type, value: type)`
|
||||||
// Sets the indexer of the table
|
// Sets the indexer of the table, if the key type is `never`, the indexer is removed
|
||||||
static int setTableIndexer(lua_State* L)
|
static int setTableIndexer(lua_State* L)
|
||||||
{
|
{
|
||||||
int argumentCount = lua_gettop(L);
|
int argumentCount = lua_gettop(L);
|
||||||
@ -693,8 +693,16 @@ static int setTableIndexer(lua_State* L)
|
|||||||
TypeFunctionTypeId key = getTypeUserData(L, 2);
|
TypeFunctionTypeId key = getTypeUserData(L, 2);
|
||||||
TypeFunctionTypeId value = getTypeUserData(L, 3);
|
TypeFunctionTypeId value = getTypeUserData(L, 3);
|
||||||
|
|
||||||
tftt->indexer = TypeFunctionTableIndexer{key, value};
|
if (DFInt::LuauTypeSolverRelease >= 646)
|
||||||
|
{
|
||||||
|
if (auto tfnt = get<TypeFunctionNeverType>(key))
|
||||||
|
{
|
||||||
|
tftt->indexer = std::nullopt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tftt->indexer = TypeFunctionTableIndexer{key, value};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,7 +1361,7 @@ static int deepCopy(lua_State* L)
|
|||||||
|
|
||||||
TypeFunctionTypeId arg = getTypeUserData(L, 1);
|
TypeFunctionTypeId arg = getTypeUserData(L, 1);
|
||||||
|
|
||||||
TypeFunctionTypeId copy = deepClone(getTypeFunctionContext(L)->typeFunctionRuntime, arg);
|
TypeFunctionTypeId copy = deepClone(NotNull{getTypeFunctionRuntime(L)}, arg);
|
||||||
allocTypeUserData(L, copy->type);
|
allocTypeUserData(L, copy->type);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
|||||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections, false)
|
LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
@ -2794,35 +2793,20 @@ TypeId TypeChecker::checkRelationalOperation(
|
|||||||
{
|
{
|
||||||
reportErrors(state.errors);
|
reportErrors(state.errors);
|
||||||
|
|
||||||
if (FFlag::LuauRemoveBadRelationalOperatorWarning)
|
// The original version of this check also produced this error when we had a union type.
|
||||||
|
// However, the old solver does not readily have the ability to discern if the union is comparable.
|
||||||
|
// This is the case when the lhs is e.g. a union of singletons and the rhs is the combined type.
|
||||||
|
// The new solver has much more powerful logic for resolving relational operators, but for now,
|
||||||
|
// we need to be conservative in the old solver to deliver a reasonable developer experience.
|
||||||
|
if (!isEquality && state.errors.empty() && isBoolean(leftType))
|
||||||
{
|
{
|
||||||
// The original version of this check also produced this error when we had a union type.
|
reportError(
|
||||||
// However, the old solver does not readily have the ability to discern if the union is comparable.
|
expr.location,
|
||||||
// This is the case when the lhs is e.g. a union of singletons and the rhs is the combined type.
|
GenericError{
|
||||||
// The new solver has much more powerful logic for resolving relational operators, but for now,
|
format("Type '%s' cannot be compared with relational operator %s", toString(leftType).c_str(), toString(expr.op).c_str())
|
||||||
// we need to be conservative in the old solver to deliver a reasonable developer experience.
|
}
|
||||||
if (!isEquality && state.errors.empty() && isBoolean(leftType))
|
|
||||||
{
|
|
||||||
reportError(
|
|
||||||
expr.location,
|
|
||||||
GenericError{
|
|
||||||
format("Type '%s' cannot be compared with relational operator %s", toString(leftType).c_str(), toString(expr.op).c_str())
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!isEquality && state.errors.empty() && (get<UnionType>(leftType) || isBoolean(leftType)))
|
|
||||||
{
|
|
||||||
reportError(
|
|
||||||
expr.location,
|
|
||||||
GenericError{
|
|
||||||
format("Type '%s' cannot be compared with relational operator %s", toString(leftType).c_str(), toString(expr.op).c_str())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return booleanType;
|
return booleanType;
|
||||||
}
|
}
|
||||||
@ -6408,7 +6392,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We're only interested in the root class of any classes.
|
// We're only interested in the root class of any classes.
|
||||||
if (auto ctv = get<ClassType>(type); !ctv || ctv->parent != builtinTypes->classType)
|
if (auto ctv = get<ClassType>(type); !ctv || (ctv->parent != builtinTypes->classType && !hasTag(type, kTypeofRootTag)))
|
||||||
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
||||||
|
|
||||||
// This probably hints at breaking out type filtering functions from the predicate solver so that typeof is not tightly coupled with IsA.
|
// This probably hints at breaking out type filtering functions from the predicate solver so that typeof is not tightly coupled with IsA.
|
||||||
|
@ -415,6 +415,14 @@ struct TraversalState
|
|||||||
|
|
||||||
switch (field)
|
switch (field)
|
||||||
{
|
{
|
||||||
|
case TypePath::TypeField::Table:
|
||||||
|
if (auto mt = get<MetatableType>(current))
|
||||||
|
{
|
||||||
|
updateCurrent(mt->table);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
case TypePath::TypeField::Metatable:
|
case TypePath::TypeField::Metatable:
|
||||||
if (auto currentType = get<TypeId>(current))
|
if (auto currentType = get<TypeId>(current))
|
||||||
{
|
{
|
||||||
@ -561,6 +569,9 @@ std::string toString(const TypePath::Path& path, bool prefixDot)
|
|||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
|
case TypePath::TypeField::Table:
|
||||||
|
result << "table";
|
||||||
|
break;
|
||||||
case TypePath::TypeField::Metatable:
|
case TypePath::TypeField::Metatable:
|
||||||
result << "metatable";
|
result << "metatable";
|
||||||
break;
|
break;
|
||||||
|
@ -21,7 +21,6 @@ LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
|
|||||||
LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping, false)
|
LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping, false)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false)
|
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifierShouldNotCopyError, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false)
|
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
@ -2974,10 +2973,7 @@ bool Unifier::occursCheck(TypePackId needle, TypePackId haystack, bool reversed)
|
|||||||
if (occurs)
|
if (occurs)
|
||||||
{
|
{
|
||||||
reportError(location, OccursCheckFailed{});
|
reportError(location, OccursCheckFailed{});
|
||||||
if (FFlag::LuauUnifierShouldNotCopyError)
|
log.replace(needle, BoundTypePack{builtinTypes->errorRecoveryTypePack()});
|
||||||
log.replace(needle, BoundTypePack{builtinTypes->errorRecoveryTypePack()});
|
|
||||||
else
|
|
||||||
log.replace(needle, *builtinTypes->errorRecoveryTypePack());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return occurs;
|
return occurs;
|
||||||
|
@ -146,7 +146,7 @@ private:
|
|||||||
AstStat* parseTypeAlias(const Location& start, bool exported);
|
AstStat* parseTypeAlias(const Location& start, bool exported);
|
||||||
|
|
||||||
// type function Name ... end
|
// type function Name ... end
|
||||||
AstStat* parseTypeFunction(const Location& start);
|
AstStat* parseTypeFunction(const Location& start, bool exported);
|
||||||
|
|
||||||
AstDeclaredClassProp parseDeclaredClassMethod();
|
AstDeclaredClassProp parseDeclaredClassMethod();
|
||||||
|
|
||||||
@ -423,6 +423,7 @@ private:
|
|||||||
MatchLexeme endMismatchSuspect;
|
MatchLexeme endMismatchSuspect;
|
||||||
|
|
||||||
std::vector<Function> functionStack;
|
std::vector<Function> functionStack;
|
||||||
|
size_t typeFunctionDepth = 0;
|
||||||
|
|
||||||
DenseHashMap<AstName, AstLocal*> localMap;
|
DenseHashMap<AstName, AstLocal*> localMap;
|
||||||
std::vector<AstLocal*> localStack;
|
std::vector<AstLocal*> localStack;
|
||||||
|
@ -19,7 +19,7 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
|||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2, false)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false)
|
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false)
|
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax, false)
|
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing, false)
|
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
@ -901,10 +901,10 @@ AstStat* Parser::parseReturn()
|
|||||||
AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||||
{
|
{
|
||||||
// parsing a type function
|
// parsing a type function
|
||||||
if (FFlag::LuauUserDefinedTypeFunctionsSyntax)
|
if (FFlag::LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
{
|
{
|
||||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||||
return parseTypeFunction(start);
|
return parseTypeFunction(start, exported);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsing a type alias
|
// parsing a type alias
|
||||||
@ -927,11 +927,14 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// type function Name `(' arglist `)' `=' funcbody `end'
|
// type function Name `(' arglist `)' `=' funcbody `end'
|
||||||
AstStat* Parser::parseTypeFunction(const Location& start)
|
AstStat* Parser::parseTypeFunction(const Location& start, bool exported)
|
||||||
{
|
{
|
||||||
Lexeme matchFn = lexer.current();
|
Lexeme matchFn = lexer.current();
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
|
if (exported)
|
||||||
|
report(start, "Type function cannot be exported");
|
||||||
|
|
||||||
// parse the name of the type function
|
// parse the name of the type function
|
||||||
std::optional<Name> fnName = parseNameOpt("type function name");
|
std::optional<Name> fnName = parseNameOpt("type function name");
|
||||||
if (!fnName)
|
if (!fnName)
|
||||||
@ -939,8 +942,13 @@ AstStat* Parser::parseTypeFunction(const Location& start)
|
|||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
||||||
|
|
||||||
|
size_t oldTypeFunctionDepth = typeFunctionDepth;
|
||||||
|
typeFunctionDepth = functionStack.size();
|
||||||
|
|
||||||
AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
|
AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
|
||||||
|
|
||||||
|
typeFunctionDepth = oldTypeFunctionDepth;
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
||||||
|
|
||||||
return allocator.alloc<AstStatTypeFunction>(Location(start, body->location), fnName->name, fnName->location, body);
|
return allocator.alloc<AstStatTypeFunction>(Location(start, body->location), fnName->name, fnName->location, body);
|
||||||
@ -2291,6 +2299,12 @@ AstExpr* Parser::parseNameExpr(const char* context)
|
|||||||
{
|
{
|
||||||
AstLocal* local = *value;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
return allocator.alloc<AstExprLocal>(name->location, local, local->functionDepth != functionStack.size() - 1);
|
return allocator.alloc<AstExprLocal>(name->location, local, local->functionDepth != functionStack.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +57,6 @@ bool isAbsolutePath(std::string_view path)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isExplicitlyRelative(std::string_view path)
|
|
||||||
{
|
|
||||||
return (path == ".") || (path == "..") || (path.size() >= 2 && path[0] == '.' && path[1] == '/') ||
|
|
||||||
(path.size() >= 3 && path[0] == '.' && path[1] == '.' && path[2] == '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> getCurrentWorkingDirectory()
|
std::optional<std::string> getCurrentWorkingDirectory()
|
||||||
{
|
{
|
||||||
// 2^17 - derived from the Windows path length limit
|
// 2^17 - derived from the Windows path length limit
|
||||||
@ -353,6 +347,20 @@ bool traverseDirectory(const std::string& path, const std::function<void(const s
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool isFile(const std::string& path)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD fileAttributes = GetFileAttributesW(fromUtf8(path).c_str());
|
||||||
|
if (fileAttributes == INVALID_FILE_ATTRIBUTES)
|
||||||
|
return false;
|
||||||
|
return (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||||
|
#else
|
||||||
|
struct stat st = {};
|
||||||
|
lstat(path.c_str(), &st);
|
||||||
|
return (st.st_mode & S_IFMT) == S_IFREG;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool isDirectory(const std::string& path)
|
bool isDirectory(const std::string& path)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -16,7 +16,7 @@ std::optional<std::string> readFile(const std::string& name);
|
|||||||
std::optional<std::string> readStdin();
|
std::optional<std::string> readStdin();
|
||||||
|
|
||||||
bool isAbsolutePath(std::string_view path);
|
bool isAbsolutePath(std::string_view path);
|
||||||
bool isExplicitlyRelative(std::string_view path);
|
bool isFile(const std::string& path);
|
||||||
bool isDirectory(const std::string& path);
|
bool isDirectory(const std::string& path);
|
||||||
bool traverseDirectory(const std::string& path, const std::function<void(const std::string& name)>& callback);
|
bool traverseDirectory(const std::string& path, const std::function<void(const std::string& name)>& callback);
|
||||||
|
|
||||||
|
@ -127,6 +127,8 @@ static int lua_require(lua_State* L)
|
|||||||
|
|
||||||
if (resolvedRequire.status == RequireResolver::ModuleStatus::Cached)
|
if (resolvedRequire.status == RequireResolver::ModuleStatus::Cached)
|
||||||
return finishrequire(L);
|
return finishrequire(L);
|
||||||
|
else if (resolvedRequire.status == RequireResolver::ModuleStatus::Ambiguous)
|
||||||
|
luaL_errorL(L, "require path could not be resolved to a unique file");
|
||||||
else if (resolvedRequire.status == RequireResolver::ModuleStatus::NotFound)
|
else if (resolvedRequire.status == RequireResolver::ModuleStatus::NotFound)
|
||||||
luaL_errorL(L, "error requiring module");
|
luaL_errorL(L, "error requiring module");
|
||||||
|
|
||||||
|
@ -24,6 +24,9 @@ RequireResolver::RequireResolver(lua_State* L, std::string path)
|
|||||||
|
|
||||||
std::replace(pathToResolve.begin(), pathToResolve.end(), '\\', '/');
|
std::replace(pathToResolve.begin(), pathToResolve.end(), '\\', '/');
|
||||||
|
|
||||||
|
if (!isPrefixValid())
|
||||||
|
luaL_argerrorL(L, 1, "require path must start with a valid prefix: ./, ../, or @");
|
||||||
|
|
||||||
substituteAliasIfPresent(pathToResolve);
|
substituteAliasIfPresent(pathToResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,44 +47,14 @@ RequireResolver::ModuleStatus RequireResolver::findModule()
|
|||||||
// Put _MODULES table on stack for checking and saving to the cache
|
// Put _MODULES table on stack for checking and saving to the cache
|
||||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
|
||||||
RequireResolver::ModuleStatus moduleStatus = findModuleImpl();
|
return findModuleImpl();
|
||||||
|
|
||||||
if (moduleStatus != RequireResolver::ModuleStatus::NotFound)
|
|
||||||
return moduleStatus;
|
|
||||||
|
|
||||||
if (!shouldSearchPathsArray())
|
|
||||||
return moduleStatus;
|
|
||||||
|
|
||||||
if (!isConfigFullyResolved)
|
|
||||||
parseNextConfig();
|
|
||||||
|
|
||||||
// Index-based iteration because std::iterator may be invalidated if config.paths is reallocated
|
|
||||||
for (size_t i = 0; i < config.paths.size(); ++i)
|
|
||||||
{
|
|
||||||
// "placeholder" acts as a requiring file in the relevant directory
|
|
||||||
std::optional<std::string> absolutePathOpt = resolvePath(pathToResolve, joinPaths(config.paths[i], "placeholder"));
|
|
||||||
|
|
||||||
if (!absolutePathOpt)
|
|
||||||
luaL_errorL(L, "error requiring module");
|
|
||||||
|
|
||||||
chunkname = *absolutePathOpt;
|
|
||||||
absolutePath = *absolutePathOpt;
|
|
||||||
|
|
||||||
moduleStatus = findModuleImpl();
|
|
||||||
|
|
||||||
if (moduleStatus != RequireResolver::ModuleStatus::NotFound)
|
|
||||||
return moduleStatus;
|
|
||||||
|
|
||||||
// Before finishing the loop, parse more config files if there are any
|
|
||||||
if (i == config.paths.size() - 1 && !isConfigFullyResolved)
|
|
||||||
parseNextConfig(); // could reallocate config.paths when paths are parsed and added
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequireResolver::ModuleStatus::NotFound;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RequireResolver::ModuleStatus RequireResolver::findModuleImpl()
|
RequireResolver::ModuleStatus RequireResolver::findModuleImpl()
|
||||||
{
|
{
|
||||||
|
if (isPathAmbiguous(absolutePath))
|
||||||
|
return ModuleStatus::Ambiguous;
|
||||||
|
|
||||||
static const std::array<const char*, 4> possibleSuffixes = {".luau", ".lua", "/init.luau", "/init.lua"};
|
static const std::array<const char*, 4> possibleSuffixes = {".luau", ".lua", "/init.luau", "/init.lua"};
|
||||||
|
|
||||||
size_t unsuffixedAbsolutePathSize = absolutePath.size();
|
size_t unsuffixedAbsolutePathSize = absolutePath.size();
|
||||||
@ -113,15 +86,34 @@ RequireResolver::ModuleStatus RequireResolver::findModuleImpl()
|
|||||||
return ModuleStatus::NotFound;
|
return ModuleStatus::NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::isPathAmbiguous(const std::string& path)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (const char* suffix : {".luau", ".lua"})
|
||||||
|
{
|
||||||
|
if (isFile(path + suffix))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDirectory(path) && found)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool RequireResolver::isRequireAllowed(std::string_view sourceChunkname)
|
bool RequireResolver::isRequireAllowed(std::string_view sourceChunkname)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!sourceChunkname.empty());
|
LUAU_ASSERT(!sourceChunkname.empty());
|
||||||
return (sourceChunkname[0] == '=' || sourceChunkname[0] == '@');
|
return (sourceChunkname[0] == '=' || sourceChunkname[0] == '@');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequireResolver::shouldSearchPathsArray()
|
bool RequireResolver::isPrefixValid()
|
||||||
{
|
{
|
||||||
return !isAbsolutePath(pathToResolve) && !isExplicitlyRelative(pathToResolve);
|
return pathToResolve.compare(0, 2, "./") == 0 || pathToResolve.compare(0, 3, "../") == 0 || pathToResolve.compare(0, 1, "@") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequireResolver::resolveAndStoreDefaultPaths()
|
void RequireResolver::resolveAndStoreDefaultPaths()
|
||||||
@ -283,24 +275,10 @@ void RequireResolver::parseConfigInDirectory(const std::string& directory)
|
|||||||
{
|
{
|
||||||
std::string configPath = joinPaths(directory, Luau::kConfigName);
|
std::string configPath = joinPaths(directory, Luau::kConfigName);
|
||||||
|
|
||||||
size_t numPaths = config.paths.size();
|
|
||||||
|
|
||||||
if (std::optional<std::string> contents = readFile(configPath))
|
if (std::optional<std::string> contents = readFile(configPath))
|
||||||
{
|
{
|
||||||
std::optional<std::string> error = Luau::parseConfig(*contents, config);
|
std::optional<std::string> error = Luau::parseConfig(*contents, config);
|
||||||
if (error)
|
if (error)
|
||||||
luaL_errorL(L, "error parsing %s (%s)", configPath.c_str(), (*error).c_str());
|
luaL_errorL(L, "error parsing %s (%s)", configPath.c_str(), (*error).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve any newly obtained relative paths in "paths" in relation to configPath
|
|
||||||
for (auto it = config.paths.begin() + numPaths; it != config.paths.end(); ++it)
|
|
||||||
{
|
|
||||||
if (!isAbsolutePath(*it))
|
|
||||||
{
|
|
||||||
if (std::optional<std::string> resolvedPath = resolvePath(*it, configPath))
|
|
||||||
*it = std::move(*resolvedPath);
|
|
||||||
else
|
|
||||||
luaL_errorL(L, "error requiring module");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ public:
|
|||||||
{
|
{
|
||||||
Cached,
|
Cached,
|
||||||
FileRead,
|
FileRead,
|
||||||
|
Ambiguous,
|
||||||
NotFound
|
NotFound
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,10 +47,11 @@ private:
|
|||||||
bool isConfigFullyResolved = false;
|
bool isConfigFullyResolved = false;
|
||||||
|
|
||||||
bool isRequireAllowed(std::string_view sourceChunkname);
|
bool isRequireAllowed(std::string_view sourceChunkname);
|
||||||
bool shouldSearchPathsArray();
|
bool isPrefixValid();
|
||||||
|
|
||||||
void resolveAndStoreDefaultPaths();
|
void resolveAndStoreDefaultPaths();
|
||||||
ModuleStatus findModuleImpl();
|
ModuleStatus findModuleImpl();
|
||||||
|
bool isPathAmbiguous(const std::string& path);
|
||||||
|
|
||||||
std::optional<std::string> getRequiringContextAbsolute();
|
std::optional<std::string> getRequiringContextAbsolute();
|
||||||
std::string getRequiringContextRelative();
|
std::string getRequiringContextRelative();
|
||||||
|
@ -3634,6 +3634,10 @@ struct Compiler
|
|||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
else if (node->is<AstStatTypeFunction>())
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!"Unknown statement type");
|
LUAU_ASSERT(!"Unknown statement type");
|
||||||
|
@ -32,7 +32,6 @@ struct Config
|
|||||||
|
|
||||||
std::vector<std::string> globals;
|
std::vector<std::string> globals;
|
||||||
|
|
||||||
std::vector<std::string> paths;
|
|
||||||
std::unordered_map<std::string, std::string> aliases;
|
std::unordered_map<std::string, std::string> aliases;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -304,11 +304,6 @@ Error parseConfig(const std::string& contents, Config& config, bool compat)
|
|||||||
config.globals.push_back(value);
|
config.globals.push_back(value);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
else if (keys.size() == 1 && keys[0] == "paths")
|
|
||||||
{
|
|
||||||
config.paths.push_back(value);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
else if (keys.size() == 2 && keys[0] == "aliases")
|
else if (keys.size() == 2 && keys[0] == "aliases")
|
||||||
return parseAlias(config.aliases, keys[1], value);
|
return parseAlias(config.aliases, keys[1], value);
|
||||||
else if (compat && keys.size() == 2 && keys[0] == "language" && keys[1] == "mode")
|
else if (compat && keys.size() == 2 && keys[0] == "language" && keys[1] == "mode")
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauDocumentationAtPosition)
|
||||||
|
|
||||||
struct DocumentationSymbolFixture : BuiltinsFixture
|
struct DocumentationSymbolFixture : BuiltinsFixture
|
||||||
{
|
{
|
||||||
std::optional<DocumentationSymbol> getDocSymbol(const std::string& source, Position position)
|
std::optional<DocumentationSymbol> getDocSymbol(const std::string& source, Position position)
|
||||||
@ -163,6 +165,44 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "table_overloaded_function_prop")
|
|||||||
CHECK_EQ(symbol, "@test/global/Foo.new/overload/(string) -> number");
|
CHECK_EQ(symbol, "@test/global/Foo.new/overload/(string) -> number");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "string_metatable_method")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauDocumentationAtPosition, true};
|
||||||
|
std::optional<DocumentationSymbol> symbol = getDocSymbol(
|
||||||
|
R"(
|
||||||
|
local x: string = "Foo"
|
||||||
|
x:rep(2)
|
||||||
|
)",
|
||||||
|
Position(2, 12)
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_EQ(symbol, "@luau/global/string.rep");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "parent_class_method")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauDocumentationAtPosition, true};
|
||||||
|
loadDefinition(R"(
|
||||||
|
declare class Foo
|
||||||
|
function bar(self, x: string): number
|
||||||
|
end
|
||||||
|
|
||||||
|
declare class Bar extends Foo
|
||||||
|
function notbar(self, x: string): number
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
std::optional<DocumentationSymbol> symbol = getDocSymbol(
|
||||||
|
R"(
|
||||||
|
local x: Bar = Bar.new()
|
||||||
|
x:bar("asdf")
|
||||||
|
)",
|
||||||
|
Position(2, 11)
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_EQ(symbol, "@test/globaltype/Foo.bar");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("AstQuery");
|
TEST_SUITE_BEGIN("AstQuery");
|
||||||
|
@ -21,6 +21,7 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
|||||||
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
@ -2796,6 +2797,16 @@ TEST_CASE("TypeAliasing")
|
|||||||
CHECK_NOTHROW(Luau::compileOrThrow(bcb, "type A = number local a: A = 1", options, parseOptions));
|
CHECK_NOTHROW(Luau::compileOrThrow(bcb, "type A = number local a: A = 1", options, parseOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("TypeFunction")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
|
||||||
|
Luau::BytecodeBuilder bcb;
|
||||||
|
Luau::CompileOptions options;
|
||||||
|
Luau::ParseOptions parseOptions;
|
||||||
|
CHECK_NOTHROW(Luau::compileOrThrow(bcb, "type function a() return types.any end", options, parseOptions));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("DebugLineInfo")
|
TEST_CASE("DebugLineInfo")
|
||||||
{
|
{
|
||||||
Luau::BytecodeBuilder bcb;
|
Luau::BytecodeBuilder bcb;
|
||||||
|
@ -25,6 +25,7 @@ void ConstraintGeneratorFixture::generateConstraints(const std::string& code)
|
|||||||
cg = std::make_unique<ConstraintGenerator>(
|
cg = std::make_unique<ConstraintGenerator>(
|
||||||
mainModule,
|
mainModule,
|
||||||
NotNull{&normalizer},
|
NotNull{&normalizer},
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
NotNull(&moduleResolver),
|
NotNull(&moduleResolver),
|
||||||
builtinTypes,
|
builtinTypes,
|
||||||
NotNull(&ice),
|
NotNull(&ice),
|
||||||
|
@ -20,7 +20,8 @@ struct ConstraintGeneratorFixture : Fixture
|
|||||||
DcrLogger logger;
|
DcrLogger logger;
|
||||||
UnifierSharedState sharedState{&ice};
|
UnifierSharedState sharedState{&ice};
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
TypeFunctionRuntime typeFunctionRuntime;
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{NotNull{&ice}, NotNull{&limits}};
|
||||||
|
|
||||||
std::unique_ptr<DataFlowGraph> dfg;
|
std::unique_ptr<DataFlowGraph> dfg;
|
||||||
std::unique_ptr<ConstraintGenerator> cg;
|
std::unique_ptr<ConstraintGenerator> cg;
|
||||||
|
@ -598,6 +598,72 @@ BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
|
|||||||
Luau::freeze(frontend.globalsForAutocomplete.globalTypes);
|
Luau::freeze(frontend.globalsForAutocomplete.globalTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr)
|
||||||
|
{
|
||||||
|
const AstExprIndexName* indexName = pathExpr.as<AstExprIndexName>();
|
||||||
|
if (!indexName)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<std::string_view> segments{indexName->index.value};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (AstExprIndexName* in = indexName->expr->as<AstExprIndexName>())
|
||||||
|
{
|
||||||
|
segments.push_back(in->index.value);
|
||||||
|
indexName = in;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (AstExprGlobal* indexNameAsGlobal = indexName->expr->as<AstExprGlobal>())
|
||||||
|
{
|
||||||
|
segments.push_back(indexNameAsGlobal->name.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (AstExprLocal* indexNameAsLocal = indexName->expr->as<AstExprLocal>())
|
||||||
|
{
|
||||||
|
segments.push_back(indexNameAsLocal->local->name.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse(segments.begin(), segments.end());
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments)
|
||||||
|
{
|
||||||
|
if (segments.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::vector<std::string_view> result;
|
||||||
|
|
||||||
|
auto it = segments.begin();
|
||||||
|
|
||||||
|
if (*it == "script" && !currentModuleName.empty())
|
||||||
|
{
|
||||||
|
result = split(currentModuleName, '/');
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; it != segments.end(); ++it)
|
||||||
|
{
|
||||||
|
if (result.size() > 1 && *it == "Parent")
|
||||||
|
result.pop_back();
|
||||||
|
else
|
||||||
|
result.push_back(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(result, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& pathExpr)
|
||||||
|
{
|
||||||
|
std::vector<std::string_view> segments = parsePathExpr(pathExpr);
|
||||||
|
return pathExprToModuleName(currentModuleName, segments);
|
||||||
|
}
|
||||||
|
|
||||||
ModuleName fromString(std::string_view name)
|
ModuleName fromString(std::string_view name)
|
||||||
{
|
{
|
||||||
return ModuleName(name);
|
return ModuleName(name);
|
||||||
|
@ -20,8 +20,10 @@
|
|||||||
|
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
@ -159,6 +161,9 @@ struct BuiltinsFixture : Fixture
|
|||||||
BuiltinsFixture(bool freeze = true, bool prepareAutocomplete = false);
|
BuiltinsFixture(bool freeze = true, bool prepareAutocomplete = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
|
||||||
|
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& pathExpr);
|
||||||
|
|
||||||
ModuleName fromString(std::string_view name);
|
ModuleName fromString(std::string_view name);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNormalizeNotUnknownIntersection)
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
@ -970,8 +969,6 @@ TEST_CASE_FIXTURE(NormalizeFixture, "non_final_types_can_be_normalized_but_are_n
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_with_not_unknown")
|
TEST_CASE_FIXTURE(NormalizeFixture, "intersect_with_not_unknown")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNormalizeNotUnknownIntersection, true};
|
|
||||||
|
|
||||||
TypeId notUnknown = arena.addType(NegationType{builtinTypes->unknownType});
|
TypeId notUnknown = arena.addType(NegationType{builtinTypes->unknownType});
|
||||||
TypeId type = arena.addType(IntersectionType{{builtinTypes->numberType, notUnknown}});
|
TypeId type = arena.addType(IntersectionType{{builtinTypes->numberType, notUnknown}});
|
||||||
std::shared_ptr<const NormalizedType> normalized = normalizer.normalize(type);
|
std::shared_ptr<const NormalizedType> normalized = normalizer.normalize(type);
|
||||||
|
@ -17,7 +17,7 @@ LUAU_FASTINT(LuauTypeLengthLimit)
|
|||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr)
|
LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -2380,7 +2380,7 @@ TEST_CASE_FIXTURE(Fixture, "invalid_type_forms")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
|
TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
|
||||||
AstStat* stat = parse(R"(
|
AstStat* stat = parse(R"(
|
||||||
type function foo()
|
type function foo()
|
||||||
@ -2394,6 +2394,38 @@ TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
|
|||||||
REQUIRE(f->name == "foo");
|
REQUIRE(f->name == "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "parse_nested_type_function")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
|
||||||
|
AstStat* stat = parse(R"(
|
||||||
|
local v1 = 1
|
||||||
|
type function foo()
|
||||||
|
local v2 = 2
|
||||||
|
local function bar()
|
||||||
|
v2 += 1
|
||||||
|
type function inner() end
|
||||||
|
v2 += 2
|
||||||
|
end
|
||||||
|
local function bar2()
|
||||||
|
v2 += 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function bar() v1 += 1 end
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(stat != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "invalid_user_defined_type_functions")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
|
||||||
|
matchParseError("export type function foo() end", "Type function cannot be exported");
|
||||||
|
matchParseError("local foo = 1; type function bar() print(foo) end", "Type function cannot reference outer local 'foo'");
|
||||||
|
matchParseError("type function foo() local v1 = 1; type function bar() print(v1) end end", "Type function cannot reference outer local 'v1'");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
TEST_SUITE_BEGIN("ParseErrorRecovery");
|
||||||
|
@ -308,6 +308,22 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireInitLua")
|
|||||||
assertOutputContainsAll({"true", "result from init.lua"});
|
assertOutputContainsAll({"true", "result from init.lua"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithFileAmbiguity")
|
||||||
|
{
|
||||||
|
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_file_requirer";
|
||||||
|
|
||||||
|
runProtectedRequire(ambiguousPath);
|
||||||
|
assertOutputContainsAll({"false", "require path could not be resolved to a unique file"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithDirectoryAmbiguity")
|
||||||
|
{
|
||||||
|
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_directory_requirer";
|
||||||
|
|
||||||
|
runProtectedRequire(ambiguousPath);
|
||||||
|
assertOutputContainsAll({"false", "require path could not be resolved to a unique file"});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCacheAfterRequireLuau")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCacheAfterRequireLuau")
|
||||||
{
|
{
|
||||||
std::string relativePath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
std::string relativePath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
||||||
@ -401,25 +417,11 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireAbsolutePath")
|
|||||||
assertOutputContainsAll({"false", "cannot require an absolute path"});
|
assertOutputContainsAll({"false", "cannot require an absolute path"});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "PathsArrayRelativePath")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireUnprefixedPath")
|
||||||
{
|
{
|
||||||
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/with_config/src/requirer";
|
std::string path = "an/unprefixed/path";
|
||||||
runProtectedRequire(path);
|
runProtectedRequire(path);
|
||||||
assertOutputContainsAll({"true", "result from library"});
|
assertOutputContainsAll({"false", "require path must start with a valid prefix: ./, ../, or @"});
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "PathsArrayExplicitlyRelativePath")
|
|
||||||
{
|
|
||||||
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/with_config/src/fail_requirer";
|
|
||||||
runProtectedRequire(path);
|
|
||||||
assertOutputContainsAll({"false", "error requiring module"});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "PathsArrayFromParent")
|
|
||||||
{
|
|
||||||
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/with_config/src/global_library_requirer";
|
|
||||||
runProtectedRequire(path);
|
|
||||||
assertOutputContainsAll({"true", "result from global_library"});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithAlias")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithAlias")
|
||||||
|
@ -66,7 +66,8 @@ struct SubtypeFixture : Fixture
|
|||||||
InternalErrorReporter iceReporter;
|
InternalErrorReporter iceReporter;
|
||||||
UnifierSharedState sharedState{&ice};
|
UnifierSharedState sharedState{&ice};
|
||||||
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
||||||
TypeFunctionRuntime typeFunctionRuntime;
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime typeFunctionRuntime{NotNull{&iceReporter}, NotNull{&limits}};
|
||||||
|
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ using namespace Luau;
|
|||||||
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
|
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAttributeSyntax);
|
LUAU_FASTFLAG(LuauAttributeSyntax);
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ToString");
|
TEST_SUITE_BEGIN("ToString");
|
||||||
|
|
||||||
@ -964,12 +964,11 @@ TEST_CASE_FIXTURE(Fixture, "correct_stringification_user_defined_type_functions"
|
|||||||
std::vector<TypeId>{builtinTypes->numberType}, // Type Function Arguments
|
std::vector<TypeId>{builtinTypes->numberType}, // Type Function Arguments
|
||||||
{},
|
{},
|
||||||
{AstName{"woohoo"}}, // Type Function Name
|
{AstName{"woohoo"}}, // Type Function Name
|
||||||
std::nullopt
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Type tv{tftt};
|
Type tv{tftt};
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && FFlag::LuauUserDefinedTypeFunctions)
|
if (FFlag::LuauSolverV2 && FFlag::LuauUserDefinedTypeFunctions2)
|
||||||
CHECK_EQ(toString(&tv, {}), "woohoo<number>");
|
CHECK_EQ(toString(&tv, {}), "woohoo<number>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TranspilerTests");
|
TEST_SUITE_BEGIN("TranspilerTests");
|
||||||
|
|
||||||
@ -698,7 +698,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_functions")
|
TEST_CASE_FIXTURE(Fixture, "transpile_type_functions")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
|
||||||
std::string code = R"( type function foo(arg1, arg2) if arg1 == arg2 then return arg1 end return arg2 end )";
|
std::string code = R"( type function foo(arg1, arg2) if arg1 == arg2 then return arg1 end return arg2 end )";
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
||||||
|
|
||||||
struct TypeFunctionFixture : Fixture
|
struct TypeFunctionFixture : Fixture
|
||||||
@ -1247,4 +1247,20 @@ TEST_CASE_FIXTURE(ClassFixture, "rawget_type_function_errors_w_classes")
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'");
|
CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "fuzz_len_type_function_follow")
|
||||||
|
{
|
||||||
|
// Should not fail assertions
|
||||||
|
check(R"(
|
||||||
|
local _
|
||||||
|
_ = true
|
||||||
|
for l0=_,_,# _ do
|
||||||
|
end
|
||||||
|
for l0=_,_ do
|
||||||
|
if _ then
|
||||||
|
_ += _
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -8,16 +8,17 @@
|
|||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||||
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_nil(arg)
|
type function serialize_nil(arg)
|
||||||
@ -33,8 +34,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getnil()
|
type function getnil()
|
||||||
@ -54,8 +55,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_unknown(arg)
|
type function serialize_unknown(arg)
|
||||||
@ -71,8 +72,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getunknown()
|
type function getunknown()
|
||||||
@ -92,8 +93,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_never(arg)
|
type function serialize_never(arg)
|
||||||
@ -109,8 +110,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getnever()
|
type function getnever()
|
||||||
@ -130,8 +131,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_any(arg)
|
type function serialize_any(arg)
|
||||||
@ -147,8 +148,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getany()
|
type function getany()
|
||||||
@ -168,8 +169,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_bool(arg)
|
type function serialize_bool(arg)
|
||||||
@ -185,8 +186,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getboolean()
|
type function getboolean()
|
||||||
@ -206,8 +207,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_num(arg)
|
type function serialize_num(arg)
|
||||||
@ -223,8 +224,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getnumber()
|
type function getnumber()
|
||||||
@ -244,8 +245,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_str(arg)
|
type function serialize_str(arg)
|
||||||
@ -261,8 +262,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getstring()
|
type function getstring()
|
||||||
@ -282,8 +283,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_boolsingleton(arg)
|
type function serialize_boolsingleton(arg)
|
||||||
@ -299,8 +300,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getboolsingleton()
|
type function getboolsingleton()
|
||||||
@ -320,8 +321,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_strsingleton(arg)
|
type function serialize_strsingleton(arg)
|
||||||
@ -337,8 +338,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getstrsingleton()
|
type function getstrsingleton()
|
||||||
@ -358,8 +359,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_union(arg)
|
type function serialize_union(arg)
|
||||||
@ -379,8 +380,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getunion()
|
type function getunion()
|
||||||
@ -409,8 +410,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_intersection(arg)
|
type function serialize_intersection(arg)
|
||||||
@ -430,8 +431,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getintersection()
|
type function getintersection()
|
||||||
@ -466,8 +467,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getnegation()
|
type function getnegation()
|
||||||
@ -492,8 +493,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_table(arg)
|
type function serialize_table(arg)
|
||||||
@ -513,8 +514,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function gettable()
|
type function gettable()
|
||||||
@ -553,8 +554,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getmetatable()
|
type function getmetatable()
|
||||||
@ -587,8 +588,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_func(arg)
|
type function serialize_func(arg)
|
||||||
@ -604,8 +605,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getfunction()
|
type function getfunction()
|
||||||
@ -635,8 +636,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
|
|||||||
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works")
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_class(arg)
|
type function serialize_class(arg)
|
||||||
@ -651,8 +652,8 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
@ -675,8 +676,8 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function checkmut()
|
type function checkmut()
|
||||||
@ -708,8 +709,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function getcopy()
|
type function getcopy()
|
||||||
@ -742,8 +743,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_simple_cyclic_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_simple_cyclic_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_cycle(arg)
|
type function serialize_cycle(arg)
|
||||||
@ -764,8 +765,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_simple_cyclic_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_createtable_bad_metatable")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_createtable_bad_metatable")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function badmetatable()
|
type function badmetatable()
|
||||||
@ -786,8 +787,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_createtable_bad_metatable")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_complex_cyclic_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_complex_cyclic_serialization_works")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function serialize_cycle2(arg)
|
type function serialize_cycle2(arg)
|
||||||
@ -816,8 +817,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_complex_cyclic_serialization_works")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_user_error_is_reported")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_user_error_is_reported")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function errors_if_string(arg)
|
type function errors_if_string(arg)
|
||||||
@ -839,8 +840,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_user_error_is_reported")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_call_metamethod")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_call_metamethod")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function hello(arg)
|
type function hello(arg)
|
||||||
@ -858,8 +859,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_call_metamethod")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_eq_metamethod")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_eq_metamethod")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function hello()
|
type function hello()
|
||||||
@ -884,8 +885,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_eq_metamethod")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function hello(arg)
|
type function hello(arg)
|
||||||
@ -903,34 +904,62 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props")
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_cannot_call_other")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function foo()
|
type function foo()
|
||||||
return "hi"
|
return "hi"
|
||||||
end
|
end
|
||||||
local x = true;
|
type function bar()
|
||||||
type function cannot_call_others()
|
return types.singleton(foo())
|
||||||
return foo()
|
|
||||||
end
|
end
|
||||||
local function ok(idx: cannot_call_others<>): string return idx end
|
local function ok(idx: bar<>): nil return idx end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_CHECK_ERROR_COUNT(4, result); // There are 2 type function uninhabited error, 2 user defined type function error
|
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||||
UserDefinedTypeFunctionError* e = get<UserDefinedTypeFunctionError>(result.errors[0]);
|
TypePackMismatch* tpm = get<TypePackMismatch>(result.errors[0]);
|
||||||
REQUIRE(e);
|
REQUIRE(tpm);
|
||||||
CHECK(e->message == "'cannot_call_others' type function errored at runtime: [string \"cannot_call_others\"]:7: attempt to call a nil value");
|
CHECK(toString(tpm->givenTp) == "\"hi\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_no_shared_state")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function foo()
|
||||||
|
if not glob then
|
||||||
|
glob = 'a'
|
||||||
|
else
|
||||||
|
glob ..= 'b'
|
||||||
|
end
|
||||||
|
|
||||||
|
return glob
|
||||||
|
end
|
||||||
|
type function bar(prefix)
|
||||||
|
return types.singleton(prefix:value() .. foo())
|
||||||
|
end
|
||||||
|
local function ok1(idx: bar<'x'>): nil return idx end
|
||||||
|
local function ok2(idx: bar<'y'>): nil return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
// We are only checking first errors, others are mostly duplicates
|
||||||
|
LUAU_CHECK_ERROR_COUNT(8, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"('bar' type function errored at runtime: [string "foo"]:4: attempt to modify a readonly table)");
|
||||||
|
CHECK(toString(result.errors[1]) == R"(Type function instance bar<"x"> is uninhabited)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function optionify(tbl)
|
type function optionify(tbl)
|
||||||
@ -959,8 +988,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions, true};
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function illegal(arg)
|
type function illegal(arg)
|
||||||
@ -980,9 +1009,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{ FFlag::LuauSolverV2, true };
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag udtfSyntax{ FFlag::LuauUserDefinedTypeFunctionsSyntax, true };
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag udtf{ FFlag::LuauUserDefinedTypeFunctions, true };
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function foo(tbl)
|
type function foo(tbl)
|
||||||
@ -1004,4 +1033,72 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
|
|||||||
REQUIRE(tpm);
|
REQUIRE(tpm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recovery_no_upvalues")
|
||||||
|
{
|
||||||
|
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
ScopedFastFlag userDefinedTypeFunctionNoEvaluation{FFlag::LuauUserDefinedTypeFunctionNoEvaluation, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local var
|
||||||
|
|
||||||
|
type function save_upvalue(arg)
|
||||||
|
var = 1
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = "test"
|
||||||
|
local function ok(idx: save_upvalue<test>): "test"
|
||||||
|
return idx
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Type function cannot reference outer local 'var')");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_follow")
|
||||||
|
{
|
||||||
|
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type t0 = any
|
||||||
|
type function t0()
|
||||||
|
return types.any
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Redefinition of type 't0', previously defined at line 2)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
|
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function stripindexer(tbl)
|
||||||
|
if not tbl:is("table") then
|
||||||
|
error("can only strip the indexer on a table!")
|
||||||
|
end
|
||||||
|
tbl:setindexer(types.never, types.never)
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
type map = { [number]: string, foo: string }
|
||||||
|
-- forcing an error here to check the exact type
|
||||||
|
local function ok(tbl: stripindexer<map>): never return tbl end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
TypePackMismatch* tpm = get<TypePackMismatch>(result.errors[0]);
|
||||||
|
REQUIRE(tpm);
|
||||||
|
CHECK(toString(tpm->givenTp) == "{ foo: string }");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions)
|
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeAliases");
|
TEST_SUITE_BEGIN("TypeAliases");
|
||||||
|
|
||||||
@ -1156,7 +1156,7 @@ type Foo<T> = Foo<T> | string
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_function")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_function")
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauSolverV2 || !FFlag::LuauUserDefinedTypeFunctions)
|
if (!FFlag::LuauSolverV2 || !FFlag::LuauUserDefinedTypeFunctions2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
@ -1170,8 +1170,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_f
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "user_defined_type_function_errors")
|
TEST_CASE_FIXTURE(Fixture, "user_defined_type_function_errors")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax, true};
|
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
|
||||||
ScopedFastFlag noUDTFimpl{FFlag::LuauUserDefinedTypeFunctions, false};
|
ScopedFastFlag noUDTFimpl{FFlag::LuauUserDefinedTypeFunctions2, false};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type function foo()
|
type function foo()
|
||||||
@ -1182,4 +1182,18 @@ TEST_CASE_FIXTURE(Fixture, "user_defined_type_function_errors")
|
|||||||
CHECK(toString(result.errors[0]) == "This syntax is not supported");
|
CHECK(toString(result.errors[0]) == "This syntax is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
|
--!nonstrict
|
||||||
|
type Map<T, V> = {[ K]: V}
|
||||||
|
function foo:bar(): Config<any, any> end
|
||||||
|
type Config<TSource, TContext> = Map<TSource, TContext> & { fields: FieldConfigMap<any, any>}
|
||||||
|
export type FieldConfig<TSource, TContext, TArgs> = {[ string]: any}
|
||||||
|
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -20,8 +20,6 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping);
|
|||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit);
|
LUAU_FASTINT(LuauTarjanChildLimit);
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "general_case_table_literal_blocks")
|
TEST_CASE_FIXTURE(Fixture, "general_case_table_literal_blocks")
|
||||||
@ -2340,20 +2338,10 @@ TEST_CASE_FIXTURE(Fixture, "attempt_to_call_an_intersection_of_tables")
|
|||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type { x: number } & { y: string }");
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type { x: number } & { y: string }");
|
|
||||||
else
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type {| x: number |}");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type {| x: number |}");
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Cannot call non-function { x: number } & { y: string }");
|
|
||||||
else
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Cannot call non-function {| x: number |}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "attempt_to_call_an_intersection_of_tables_with_call_metamethod")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "attempt_to_call_an_intersection_of_tables_with_call_metamethod")
|
||||||
@ -2845,17 +2833,12 @@ TEST_CASE_FIXTURE(Fixture, "cannot_call_union_of_functions")
|
|||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
std::string expected = R"(Cannot call a value of the union type:
|
||||||
{
|
|
||||||
std::string expected = R"(Cannot call a value of the union type:
|
|
||||||
| () -> ()
|
| () -> ()
|
||||||
| () -> () -> ()
|
| () -> () -> ()
|
||||||
We are unable to determine the appropriate result type for such a call.)";
|
We are unable to determine the appropriate result type for such a call.)";
|
||||||
|
|
||||||
CHECK(expected == toString(result.errors[0]));
|
CHECK(expected == toString(result.errors[0]));
|
||||||
}
|
|
||||||
else
|
|
||||||
CHECK("Cannot call non-function (() -> () -> ()) | (() -> ())" == toString(result.errors[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_ast_stat_fun")
|
TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_ast_stat_fun")
|
||||||
|
@ -16,8 +16,6 @@ using namespace Luau;
|
|||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferLoops");
|
TEST_SUITE_BEGIN("TypeInferLoops");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "for_loop")
|
TEST_CASE_FIXTURE(Fixture, "for_loop")
|
||||||
@ -194,10 +192,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_should_fail_with_non_function_iterator")
|
|||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
CHECK_EQ("Cannot call a value of type string", toString(result.errors[0]));
|
||||||
CHECK_EQ("Cannot call a value of type string", toString(result.errors[0]));
|
|
||||||
else
|
|
||||||
CHECK_EQ("Cannot call non-function string", toString(result.errors[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_with_just_one_iterator_is_ok")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_with_just_one_iterator_is_ok")
|
||||||
|
@ -608,4 +608,92 @@ local ReactShallowRenderer = require(game.A);
|
|||||||
)"));
|
)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "untitled_segfault_number_13")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
fileResolver.source["game/A"] = R"(
|
||||||
|
-- minimized from roblox-requests/http/src/response.lua
|
||||||
|
local Response = {}
|
||||||
|
Response.__index = Response
|
||||||
|
function Response.new(content_type)
|
||||||
|
-- creates response object from original request and roblox http response
|
||||||
|
local self = setmetatable({}, Response)
|
||||||
|
self.content_type = content_type
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Response:xml(ignore_content_type)
|
||||||
|
if ignore_content_type or self.content_type:find("+xml") or self.content_type:find("/xml") then
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------
|
||||||
|
|
||||||
|
return Response
|
||||||
|
)";
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local _ = require(game.A);
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "spooky_blocked_type_laundered_by_bound_type")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
fileResolver.source["game/A"] = R"(
|
||||||
|
local Cache = {}
|
||||||
|
|
||||||
|
Cache.settings = {}
|
||||||
|
|
||||||
|
Cache.data = {}
|
||||||
|
|
||||||
|
function Cache.should_cache(url)
|
||||||
|
url = url:split("?")[1]
|
||||||
|
|
||||||
|
for key, _ in pairs(Cache.settings) do
|
||||||
|
if url:match('') then
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
function Cache.is_cached(url, req_id)
|
||||||
|
-- check local server cache first
|
||||||
|
|
||||||
|
local setting_key = Cache.should_cache(url)
|
||||||
|
local settings = Cache.settings[setting_key]
|
||||||
|
|
||||||
|
if not setting_key then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if Cache.data[req_id] ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if Cache.settings[setting_key].cache_globally then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Cache.get_expire(url)
|
||||||
|
local setting_key = Cache.should_cache(url)
|
||||||
|
return Cache.settings[setting_key].expires or math.huge
|
||||||
|
end
|
||||||
|
|
||||||
|
return Cache
|
||||||
|
)";
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local _ = require(game.A);
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauRemoveBadRelationalOperatorWarning)
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferOperators");
|
TEST_SUITE_BEGIN("TypeInferOperators");
|
||||||
|
|
||||||
@ -860,7 +859,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
// If DCR is off and the flag to remove this check in the old solver is on, the expected behavior is no errors.
|
// If DCR is off and the flag to remove this check in the old solver is on, the expected behavior is no errors.
|
||||||
if (!FFlag::LuauSolverV2 && FFlag::LuauRemoveBadRelationalOperatorWarning)
|
if (!FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
return;
|
return;
|
||||||
@ -1578,10 +1577,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compare_singleton_string_to_string")
|
|||||||
|
|
||||||
// There is a flag to gate turning this off, and this warning is not
|
// There is a flag to gate turning this off, and this warning is not
|
||||||
// implemented in the new solver, so assert there are no errors.
|
// implemented in the new solver, so assert there are no errors.
|
||||||
if (FFlag::LuauRemoveBadRelationalOperatorWarning || FFlag::LuauSolverV2)
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
|
||||||
else
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "no_infinite_expansion_of_free_type" * doctest::timeout(1.0))
|
TEST_CASE_FIXTURE(BuiltinsFixture, "no_infinite_expansion_of_free_type" * doctest::timeout(1.0))
|
||||||
|
@ -2371,4 +2371,57 @@ end
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RefinementClassFixture, "typeof_instance_refinement")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local function f(x: Instance | Vector3)
|
||||||
|
if typeof(x) == "Instance" then
|
||||||
|
local foo = x
|
||||||
|
else
|
||||||
|
local foo = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("Instance", toString(requireTypeAtPosition({3, 28})));
|
||||||
|
CHECK_EQ("Vector3", toString(requireTypeAtPosition({5, 28})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RefinementClassFixture, "typeof_instance_error")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local function f(x: Part)
|
||||||
|
if typeof(x) == "Instance" then
|
||||||
|
local foo : Folder = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RefinementClassFixture, "typeof_instance_isa_refinement")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local function f(x: Part | Folder | string)
|
||||||
|
if typeof(x) == "Instance" then
|
||||||
|
local foo = x
|
||||||
|
if foo:IsA("Folder") then
|
||||||
|
local bar = foo
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local foo = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("Folder | Part", toString(requireTypeAtPosition({3, 28})));
|
||||||
|
CHECK_EQ("Folder", toString(requireTypeAtPosition({5, 32})));
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({8, 28})));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -20,7 +20,6 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
|||||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections)
|
LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections)
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
|
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
@ -2407,7 +2406,7 @@ could not be converted into
|
|||||||
//
|
//
|
||||||
// Second, nil <: unknown, so we consider that parameter to be optional.
|
// Second, nil <: unknown, so we consider that parameter to be optional.
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK("Type 'b1' could not be converted into 'a1'; at [read \"y\"], string is not exactly number" == toString(result.errors[0]));
|
CHECK("Type 'b1' could not be converted into 'a1'; at table()[read \"y\"], string is not exactly number" == toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauInstantiateInSubtyping)
|
else if (FFlag::LuauInstantiateInSubtyping)
|
||||||
{
|
{
|
||||||
@ -2583,10 +2582,7 @@ b()
|
|||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
CHECK_EQ(toString(result.errors[0]), R"(Cannot call a value of type t1 where t1 = { @metatable { __call: t1 }, { } })");
|
||||||
CHECK_EQ(toString(result.errors[0]), R"(Cannot call a value of type t1 where t1 = { @metatable { __call: t1 }, { } })");
|
|
||||||
else
|
|
||||||
CHECK_EQ(toString(result.errors[0]), R"(Cannot call non-function t1 where t1 = { @metatable { __call: t1 }, { } })");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_shouldn't_add_optional_properties_to_sealed_tables")
|
TEST_CASE_FIXTURE(Fixture, "table_subtyping_shouldn't_add_optional_properties_to_sealed_tables")
|
||||||
@ -3265,7 +3261,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_must_be_callable")
|
|||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (!FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
CHECK("Cannot call a value of type a" == toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
TypeError e{
|
TypeError e{
|
||||||
Location{{5, 20}, {5, 21}},
|
Location{{5, 20}, {5, 21}},
|
||||||
@ -3273,14 +3273,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_must_be_callable")
|
|||||||
};
|
};
|
||||||
CHECK(result.errors[0] == e);
|
CHECK(result.errors[0] == e);
|
||||||
}
|
}
|
||||||
else if (DFFlag::LuauImproveNonFunctionCallError)
|
|
||||||
{
|
|
||||||
CHECK("Cannot call a value of type a" == toString(result.errors[0]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHECK("Cannot call non-function a" == toString(result.errors[0]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_generic")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_generic")
|
||||||
@ -4851,4 +4843,27 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "length_of_array_is_number")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
||||||
|
{
|
||||||
|
// Builtin functions have to be setup for the new solver
|
||||||
|
if (!FFlag::LuauSolverV2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type self = {} & {}
|
||||||
|
type Class = typeof(setmetatable())
|
||||||
|
local function _(): Class
|
||||||
|
return setmetatable({}::self, {})
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK_EQ(
|
||||||
|
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; at [0].metatable(), { } is not a subtype of nil\n"
|
||||||
|
"\ttype { @metatable { }, { } & { } }[0].table()[0] ({ }) is not a subtype of Class[0].table() (nil)\n"
|
||||||
|
"\ttype { @metatable { }, { } & { } }[0].table()[1] ({ }) is not a subtype of Class[0].table() (nil)",
|
||||||
|
toString(result.errors[0])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -235,6 +235,23 @@ TEST_CASE_FIXTURE(ClassFixture, "metatables")
|
|||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("table")
|
SUBCASE("table")
|
||||||
|
{
|
||||||
|
TYPESOLVE_CODE(R"(
|
||||||
|
type Table = { foo: number }
|
||||||
|
type Metatable = { bar: number }
|
||||||
|
local tbl: Table = { foo = 123 }
|
||||||
|
local mt: Metatable = { bar = 456 }
|
||||||
|
local res = setmetatable(tbl, mt)
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Tricky test setup because 'setmetatable' mutates the argument 'tbl' type
|
||||||
|
auto result = traverseForType(requireType("res"), Path(TypeField::Table), builtinTypes);
|
||||||
|
auto expected = lookupType("Table");
|
||||||
|
REQUIRE(expected);
|
||||||
|
CHECK(result == follow(*expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("metatable")
|
||||||
{
|
{
|
||||||
TYPESOLVE_CODE(R"(
|
TYPESOLVE_CODE(R"(
|
||||||
local mt = { foo = 123 }
|
local mt = { foo = 123 }
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"paths": ["GlobalLuauLibraries"],
|
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"dep": "this_should_be_overwritten_by_child_luaurc",
|
"dep": "this_should_be_overwritten_by_child_luaurc",
|
||||||
"otherdep": "src/other_dependency"
|
"otherdep": "src/other_dependency"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"paths": ["../ProjectLuauLibraries"],
|
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"dep": "dependency",
|
"dep": "dependency",
|
||||||
"subdir": "subdirectory"
|
"subdir": "subdirectory"
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
-- shouldn't attempt to search paths array because of "./" prefix
|
|
||||||
return require("./library")
|
|
@ -1,2 +0,0 @@
|
|||||||
-- should be required using the paths array in the parent directory's .luaurc
|
|
||||||
return require("global_library")
|
|
@ -1,2 +0,0 @@
|
|||||||
-- should be required using the paths array in .luaurc
|
|
||||||
return require("library")
|
|
@ -0,0 +1 @@
|
|||||||
|
return {"result from dependency"}
|
@ -0,0 +1 @@
|
|||||||
|
return {"result from dependency"}
|
@ -0,0 +1 @@
|
|||||||
|
return {"result from dependency"}
|
@ -0,0 +1 @@
|
|||||||
|
return {"result from dependency"}
|
@ -0,0 +1,3 @@
|
|||||||
|
local result = require("./ambiguous/directory/dependency")
|
||||||
|
result[#result+1] = "required into module"
|
||||||
|
return result
|
@ -0,0 +1,3 @@
|
|||||||
|
local result = require("./ambiguous/file/dependency")
|
||||||
|
result[#result+1] = "required into module"
|
||||||
|
return result
|
@ -1,3 +1,3 @@
|
|||||||
local result = require("dependency")
|
local result = require("./dependency")
|
||||||
result[#result+1] = "required into module"
|
result[#result+1] = "required into module"
|
||||||
return result
|
return result
|
||||||
|
Loading…
Reference in New Issue
Block a user