mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
Sync to upstream/release/679 (#1884)
# What's Changed? We've been hard at work fixing bugs and introducing new features! ## VM * Include constant-folding information in Luau cost model for inlining and loop unrolling * ~1% improvement in compile times ## New Type Solver * `Luau::shallowClone`'s last argument, whether to clone persistent (builtin) types, is now non-optional. * Refinements on properties of tables are now computed with a `read` table property. This resolves some issues around refining table properies and then trying to set them. Fixes #1344. Fixes #1651. ``` if foo.bar then -- Prior to this release, this would be `typeof(foo) & { bar: ~(false?) } -- Now, this is `typeof(foo) & { read bar: ~(false?) } end ``` * The type function `keyof` should respect the empty string as a property, as in: ``` -- equivalent to type Foo ="" type Foo = keyof<{ [""]: number }> ``` * Descend into literals to report subtyping errors for function calls: this both improves bidirectional inference and makes errors more specific. Before, the error reporting for a table with incorrect members passed to a function would cite the entire table, but now it only cites the members that are incorrectly typed. * Fixes a case where intersecting two tables without any common properties would create `never`, instead of a table with both of their properties. # Internal Contributors Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com>
This commit is contained in:
parent
713ee2ff8b
commit
8fe64db609
87 changed files with 3202 additions and 4077 deletions
|
@ -29,11 +29,10 @@ struct CloneState
|
|||
* Be mindful about which behavior you actually _want_.
|
||||
*
|
||||
* Persistent types are not cloned as an optimization.
|
||||
* If a type is cloned in order to mutate it, 'ignorePersistent' has to be set
|
||||
* If a type is cloned in order to mutate it, 'clonePersistentTypes' has to be set
|
||||
*/
|
||||
|
||||
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false);
|
||||
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false);
|
||||
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes);
|
||||
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes);
|
||||
|
||||
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState);
|
||||
TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState);
|
||||
|
|
|
@ -143,8 +143,8 @@ struct ConstraintGenerator
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
const ScopePtr& globalScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
ScopePtr globalScope,
|
||||
ScopePtr typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
DcrLogger* logger,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "Luau/Scope.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
@ -116,6 +115,8 @@ struct FrontendOptions
|
|||
// An optional callback which is called for every *dirty* module was checked
|
||||
// Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe
|
||||
std::function<void(const SourceModule& sourceModule, const Luau::Module& module)> customModuleCheck;
|
||||
|
||||
bool collectTypeAllocationStats = false;
|
||||
};
|
||||
|
||||
struct CheckResult
|
||||
|
@ -139,6 +140,7 @@ struct FrontendModuleResolver : ModuleResolver
|
|||
bool setModule(const ModuleName& moduleName, ModulePtr module);
|
||||
void clearModules();
|
||||
|
||||
|
||||
private:
|
||||
Frontend* frontend;
|
||||
|
||||
|
@ -156,6 +158,13 @@ struct Frontend
|
|||
size_t filesStrict = 0;
|
||||
size_t filesNonstrict = 0;
|
||||
|
||||
size_t typesAllocated = 0;
|
||||
size_t typePacksAllocated = 0;
|
||||
|
||||
size_t boolSingletonsMinted = 0;
|
||||
size_t strSingletonsMinted = 0;
|
||||
size_t uniqueStrSingletonsMinted = 0;
|
||||
|
||||
double timeRead = 0;
|
||||
double timeParse = 0;
|
||||
double timeCheck = 0;
|
||||
|
@ -164,6 +173,11 @@ struct Frontend
|
|||
|
||||
Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {});
|
||||
|
||||
void setLuauSolverSelectionFromWorkspace(bool newSolverEnabled);
|
||||
bool getLuauSolverSelection() const;
|
||||
bool getLuauSolverSelectionFlagged() const;
|
||||
// The default value assuming there is no workspace setup yet
|
||||
std::atomic<bool> useNewLuauSolver{FFlag::LuauSolverV2};
|
||||
// Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking
|
||||
void parse(const ModuleName& name);
|
||||
void parseModules(const std::vector<ModuleName>& name);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
namespace Luau
|
||||
|
@ -27,6 +29,13 @@ SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeAre
|
|||
SimplifyResult simplifyIntersectWithTruthy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||
SimplifyResult simplifyIntersectWithFalsy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||
|
||||
std::optional<TypeId> intersectWithSimpleDiscriminant(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
TypeId target,
|
||||
TypeId discriminant
|
||||
);
|
||||
|
||||
enum class Relation
|
||||
{
|
||||
Disjoint, // No A is a B or vice versa
|
||||
|
|
|
@ -456,9 +456,8 @@ struct Property
|
|||
std::optional<Location> typeLocation = std::nullopt
|
||||
);
|
||||
|
||||
// DEPRECATED: Should only be called in non-RWP! We assert that the `readTy` is not nullopt.
|
||||
// TODO: Kill once we don't have non-RWP.
|
||||
TypeId type() const;
|
||||
// DEPRECATED: This should be removed with `LuauTypeSolverV2` clean up
|
||||
TypeId type_DEPRECATED() const;
|
||||
void setType(TypeId ty);
|
||||
|
||||
// If this property has a present `writeTy`, set it equal to the `readTy`.
|
||||
|
@ -565,13 +564,13 @@ struct ExternType
|
|||
ModuleName definitionModuleName,
|
||||
std::optional<Location> definitionLocation
|
||||
)
|
||||
: name(name)
|
||||
, props(props)
|
||||
: name(std::move(name))
|
||||
, props(std::move(props))
|
||||
, parent(parent)
|
||||
, metatable(metatable)
|
||||
, tags(tags)
|
||||
, userData(userData)
|
||||
, definitionModuleName(definitionModuleName)
|
||||
, tags(std::move(tags))
|
||||
, userData(std::move(userData))
|
||||
, definitionModuleName(std::move(definitionModuleName))
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
@ -587,13 +586,13 @@ struct ExternType
|
|||
std::optional<Location> definitionLocation,
|
||||
std::optional<TableIndexer> indexer
|
||||
)
|
||||
: name(name)
|
||||
, props(props)
|
||||
: name(std::move(name))
|
||||
, props(std::move(props))
|
||||
, parent(parent)
|
||||
, metatable(metatable)
|
||||
, tags(tags)
|
||||
, userData(userData)
|
||||
, definitionModuleName(definitionModuleName)
|
||||
, tags(std::move(tags))
|
||||
, userData(std::move(userData))
|
||||
, definitionModuleName(std::move(definitionModuleName))
|
||||
, definitionLocation(definitionLocation)
|
||||
, indexer(indexer)
|
||||
{
|
||||
|
@ -638,31 +637,31 @@ struct TypeFunctionInstanceType
|
|||
UserDefinedFunctionData userFuncData
|
||||
)
|
||||
: function(function)
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
, typeArguments(std::move(typeArguments))
|
||||
, packArguments(std::move(packArguments))
|
||||
, userFuncName(userFuncName)
|
||||
, userFuncData(userFuncData)
|
||||
, userFuncData(std::move(userFuncData))
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments)
|
||||
: function{&function}
|
||||
, typeArguments(typeArguments)
|
||||
, typeArguments(std::move(typeArguments))
|
||||
, packArguments{}
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: function{&function}
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
, typeArguments(std::move(typeArguments))
|
||||
, packArguments(std::move(packArguments))
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(NotNull<const TypeFunction> function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: function{function}
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
, typeArguments(std::move(typeArguments))
|
||||
, packArguments(std::move(packArguments))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -713,7 +712,7 @@ struct LazyType
|
|||
{
|
||||
LazyType() = default;
|
||||
LazyType(std::function<void(LazyType&)> unwrap)
|
||||
: unwrap(unwrap)
|
||||
: unwrap(std::move(unwrap))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -800,8 +799,8 @@ struct Type final
|
|||
{
|
||||
}
|
||||
|
||||
Type(const TypeVariant& ty, bool persistent)
|
||||
: ty(ty)
|
||||
Type(TypeVariant ty, bool persistent)
|
||||
: ty(std::move(ty))
|
||||
, persistent(persistent)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
LUAU_FASTFLAG(LuauTrackTypeAllocations)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
struct Module;
|
||||
|
@ -20,6 +22,11 @@ struct TypeArena
|
|||
// Owning module, if any
|
||||
Module* owningModule = nullptr;
|
||||
|
||||
bool collectSingletonStats = false;
|
||||
size_t boolSingletonsMinted = 0;
|
||||
size_t strSingletonsMinted = 0;
|
||||
DenseHashSet<std::optional<std::string>> uniqueStrSingletonsMinted{std::nullopt};
|
||||
|
||||
void clear();
|
||||
|
||||
template<typename T>
|
||||
|
@ -28,6 +35,12 @@ struct TypeArena
|
|||
if constexpr (std::is_same_v<T, UnionType>)
|
||||
LUAU_ASSERT(tv.options.size() >= 2);
|
||||
|
||||
if constexpr (std::is_same_v<T, SingletonType>)
|
||||
{
|
||||
if (FFlag::LuauTrackTypeAllocations && collectSingletonStats)
|
||||
recordSingletonStats(NotNull{&tv});
|
||||
}
|
||||
|
||||
return addTV(Type(std::move(tv)));
|
||||
}
|
||||
|
||||
|
@ -54,6 +67,8 @@ struct TypeArena
|
|||
TypeId addTypeFunction(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||
TypePackId addTypePackFunction(const TypePackFunction& function, std::initializer_list<TypeId> types);
|
||||
TypePackId addTypePackFunction(const TypePackFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||
|
||||
void recordSingletonStats(NotNull<SingletonType> singleton);
|
||||
};
|
||||
|
||||
void freeze(TypeArena& arena);
|
||||
|
|
|
@ -318,6 +318,32 @@ std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, Type
|
|||
*/
|
||||
bool isRecord(const AstExprTable::Item& item);
|
||||
|
||||
/**
|
||||
* Do a quick check for whether the type `ty` is exactly `false | nil`. This
|
||||
* will *not* do any sort of semantic analysis, for example the type:
|
||||
*
|
||||
* (boolean?) & (false | nil)
|
||||
*
|
||||
* ... will not be considered falsy, despite it being semantically equivalent
|
||||
* to `false | nil`.
|
||||
*
|
||||
* @return Whether the input is approximately `false | nil`.
|
||||
*/
|
||||
bool isApproximatelyFalsyType(TypeId ty);
|
||||
|
||||
/**
|
||||
* Do a quick check for whether the type `ty` is exactly `~(false | nil)`.
|
||||
* This will *not* do any sort of semantic analysis, for example the type:
|
||||
*
|
||||
* unknown & ~(false | nil)
|
||||
*
|
||||
* ... will not be considered falsy, despite it being semantically equivalent
|
||||
* to `~(false | nil)`.
|
||||
*
|
||||
* @return Whether the input is approximately `~(false | nil)`.
|
||||
*/
|
||||
bool isApproximatelyTruthyType(TypeId ty);
|
||||
|
||||
// Unwraps any grouping expressions iteratively.
|
||||
AstExpr* unwrapGroup(AstExpr* expr);
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ struct GenericTypeVisitor
|
|||
traverse(*ty);
|
||||
}
|
||||
else
|
||||
traverse(prop.type());
|
||||
traverse(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
if (ttv->indexer)
|
||||
|
@ -332,7 +332,7 @@ struct GenericTypeVisitor
|
|||
traverse(*ty);
|
||||
}
|
||||
else
|
||||
traverse(prop.type());
|
||||
traverse(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
if (etv->parent)
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -433,16 +431,8 @@ struct AstJsonEncoder : public AstVisitor
|
|||
if (node->self)
|
||||
PROP(self);
|
||||
PROP(args);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (node->returnAnnotation)
|
||||
PROP(returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node->returnAnnotation_DEPRECATED)
|
||||
write("returnAnnotation", node->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (node->returnAnnotation)
|
||||
PROP(returnAnnotation);
|
||||
PROP(vararg);
|
||||
PROP(varargLocation);
|
||||
if (node->varargAnnotation)
|
||||
|
@ -912,10 +902,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
PROP(paramNames);
|
||||
PROP(vararg);
|
||||
PROP(varargLocation);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
PROP(retTypes);
|
||||
else
|
||||
write("retTypes", node->retTypes_DEPRECATED);
|
||||
PROP(retTypes);
|
||||
PROP(generics);
|
||||
PROP(genericPacks);
|
||||
}
|
||||
|
@ -1061,10 +1048,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
PROP(genericPacks);
|
||||
PROP(argTypes);
|
||||
PROP(argNames);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
PROP(returnTypes);
|
||||
else
|
||||
write("returnTypes", node->returnTypes_DEPRECATED);
|
||||
PROP(returnTypes);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -524,7 +525,18 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
|
|||
if (indexIt == mtable->props.end())
|
||||
return std::nullopt;
|
||||
|
||||
TypeId followed = follow(indexIt->second.type());
|
||||
TypeId followed;
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (indexIt->second.readTy)
|
||||
followed = follow(*indexIt->second.readTy);
|
||||
else if (indexIt->second.writeTy)
|
||||
followed = follow(*indexIt->second.writeTy);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
followed = follow(indexIt->second.type_DEPRECATED());
|
||||
const TableType* ttv = get<TableType>(followed);
|
||||
if (!ttv)
|
||||
return std::nullopt;
|
||||
|
@ -539,7 +551,7 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
|
|||
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
else
|
||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -571,7 +583,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
else
|
||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(parentTy))
|
||||
|
@ -587,7 +599,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
}
|
||||
else
|
||||
return checkOverloadedDocumentationSymbol(
|
||||
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
||||
module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol
|
||||
);
|
||||
}
|
||||
etv = etv->parent ? Luau::get<Luau::ExternType>(*etv->parent) : nullptr;
|
||||
|
|
|
@ -21,11 +21,12 @@
|
|||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2)
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
@ -336,7 +337,7 @@ static void autocompleteProps(
|
|||
continue;
|
||||
}
|
||||
else
|
||||
type = follow(prop.type());
|
||||
type = follow(prop.type_DEPRECATED());
|
||||
|
||||
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key
|
||||
? TypeCorrectKind::Correct
|
||||
|
@ -368,7 +369,11 @@ static void autocompleteProps(
|
|||
auto indexIt = mtable->props.find("__index");
|
||||
if (indexIt != mtable->props.end())
|
||||
{
|
||||
TypeId followed = follow(indexIt->second.type());
|
||||
TypeId followed;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
followed = follow(*indexIt->second.readTy);
|
||||
else
|
||||
followed = follow(indexIt->second.type_DEPRECATED());
|
||||
if (get<TableType>(followed) || get<MetatableType>(followed))
|
||||
{
|
||||
autocompleteProps(module, typeArena, builtinTypes, rootTy, followed, indexType, nodes, result, seen);
|
||||
|
@ -652,7 +657,6 @@ static std::optional<TypeId> findTypeElementAt(const AstTypeList& astTypeList, T
|
|||
|
||||
static std::optional<TypeId> findTypeElementAt(AstTypePack* astTypePack, TypePackId tp, Position position)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
if (const auto typePack = astTypePack->as<AstTypePackExplicit>())
|
||||
{
|
||||
return findTypeElementAt(typePack->typeList, tp, position);
|
||||
|
@ -694,16 +698,8 @@ static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Posi
|
|||
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
|
||||
return element;
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes_DEPRECATED, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
|
||||
// It's possible to walk through other types like intrsection and unions if we find value in doing that
|
||||
|
@ -1033,65 +1029,14 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
if (!node->returnAnnotation)
|
||||
return result;
|
||||
|
||||
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
|
||||
{
|
||||
if (!node->returnAnnotation)
|
||||
return result;
|
||||
|
||||
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
|
||||
for (size_t i = 0; i < typePack->typeList.types.size; i++)
|
||||
{
|
||||
for (size_t i = 0; i < typePack->typeList.types.size; i++)
|
||||
{
|
||||
AstType* ret = typePack->typeList.types.data[i];
|
||||
|
||||
if (ret->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, i))
|
||||
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
|
||||
}
|
||||
|
||||
// TODO: with additional type information, we could suggest inferred return type here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (AstTypePack* retTp = typePack->typeList.tailType)
|
||||
{
|
||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||
{
|
||||
if (variadic->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
|
||||
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto variadic = node->returnAnnotation->as<AstTypePackVariadic>())
|
||||
{
|
||||
if (variadic->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
|
||||
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!node->returnAnnotation_DEPRECATED)
|
||||
return result;
|
||||
|
||||
for (size_t i = 0; i < node->returnAnnotation_DEPRECATED->types.size; i++)
|
||||
{
|
||||
AstType* ret = node->returnAnnotation_DEPRECATED->types.data[i];
|
||||
AstType* ret = typePack->typeList.types.data[i];
|
||||
|
||||
if (ret->location.containsClosed(position))
|
||||
{
|
||||
|
@ -1106,7 +1051,7 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
|
||||
if (AstTypePack* retTp = node->returnAnnotation_DEPRECATED->tailType)
|
||||
if (AstTypePack* retTp = typePack->typeList.tailType)
|
||||
{
|
||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||
{
|
||||
|
@ -1121,6 +1066,17 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (auto variadic = node->returnAnnotation->as<AstTypePackVariadic>())
|
||||
{
|
||||
if (variadic->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
|
||||
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -2074,8 +2030,11 @@ AutocompleteResult autocomplete_(
|
|||
{
|
||||
AutocompleteEntryMap result;
|
||||
|
||||
if (auto it = module->astExpectedTypes.find(node->asExpr()))
|
||||
autocompleteStringSingleton(*it, false, node, position, result);
|
||||
if (!FFlag::LuauImplicitTableIndexerKeys2)
|
||||
{
|
||||
if (auto it = module->astExpectedTypes.find(node->asExpr()))
|
||||
autocompleteStringSingleton(*it, false, node, position, result);
|
||||
}
|
||||
|
||||
if (ancestry.size() >= 2)
|
||||
{
|
||||
|
@ -2094,6 +2053,12 @@ AutocompleteResult autocomplete_(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauImplicitTableIndexerKeys2)
|
||||
{
|
||||
if (auto it = module->astExpectedTypes.find(node->asExpr()))
|
||||
autocompleteStringSingleton(*it, false, node, position, result);
|
||||
}
|
||||
|
||||
return {std::move(result), ancestry, AutocompleteContext::String};
|
||||
}
|
||||
else if (stringPartOfInterpString(node, position))
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
||||
|
@ -336,7 +337,14 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
auto it = stringMetatableTable->props.find("__index");
|
||||
LUAU_ASSERT(it != stringMetatableTable->props.end());
|
||||
|
||||
addGlobalBinding(globals, "string", it->second.type(), "@luau");
|
||||
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
addGlobalBinding(globals, "string", *it->second.readTy, "@luau");
|
||||
addGlobalBinding(globals, "string", *it->second.writeTy, "@luau");
|
||||
}
|
||||
else
|
||||
addGlobalBinding(globals, "string", it->second.type_DEPRECATED(), "@luau");
|
||||
|
||||
// Setup 'vector' metatable
|
||||
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
|
||||
|
@ -494,10 +502,20 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
ttv->props["foreach"].deprecated = true;
|
||||
ttv->props["foreachi"].deprecated = true;
|
||||
|
||||
attachMagicFunction(ttv->props["pack"].type(), std::make_shared<MagicPack>());
|
||||
if (FFlag::LuauTableCloneClonesType3)
|
||||
attachMagicFunction(ttv->props["clone"].type(), std::make_shared<MagicClone>());
|
||||
attachMagicFunction(ttv->props["freeze"].type(), std::make_shared<MagicFreeze>());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared<MagicPack>());
|
||||
if (FFlag::LuauTableCloneClonesType3)
|
||||
attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared<MagicClone>());
|
||||
attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared<MagicFreeze>());
|
||||
}
|
||||
else
|
||||
{
|
||||
attachMagicFunction(ttv->props["pack"].type_DEPRECATED(), std::make_shared<MagicPack>());
|
||||
if (FFlag::LuauTableCloneClonesType3)
|
||||
attachMagicFunction(ttv->props["clone"].type_DEPRECATED(), std::make_shared<MagicClone>());
|
||||
attachMagicFunction(ttv->props["freeze"].type_DEPRECATED(), std::make_shared<MagicFreeze>());
|
||||
}
|
||||
}
|
||||
|
||||
TypeId requireTy = getGlobalBinding(globals, "require");
|
||||
|
@ -1649,7 +1667,7 @@ std::optional<WithPredicate<TypePackId>> MagicClone::handleOldSolver(
|
|||
return std::nullopt;
|
||||
|
||||
CloneState cloneState{typechecker.builtinTypes};
|
||||
TypeId resultType = shallowClone(inputType, arena, cloneState);
|
||||
TypeId resultType = shallowClone(inputType, arena, cloneState, /* clonePersistentTypes */ false);
|
||||
|
||||
TypePackId clonedTypePack = arena.addTypePack({resultType});
|
||||
return WithPredicate<TypePackId>{clonedTypePack};
|
||||
|
|
|
@ -232,7 +232,7 @@ private:
|
|||
else
|
||||
{
|
||||
return Property{
|
||||
shallowClone(p.type()),
|
||||
shallowClone(p.type_DEPRECATED()),
|
||||
p.deprecated,
|
||||
p.deprecatedSuggestion,
|
||||
p.location,
|
||||
|
@ -551,9 +551,9 @@ public:
|
|||
|
||||
} // namespace
|
||||
|
||||
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
|
||||
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
|
||||
{
|
||||
if (tp->persistent && !ignorePersistent)
|
||||
if (tp->persistent && !clonePersistentTypes)
|
||||
return tp;
|
||||
|
||||
TypeCloner cloner{
|
||||
|
@ -562,15 +562,15 @@ TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState,
|
|||
NotNull{&cloneState.seenTypes},
|
||||
NotNull{&cloneState.seenTypePacks},
|
||||
nullptr,
|
||||
ignorePersistent ? tp : nullptr
|
||||
clonePersistentTypes ? tp : nullptr
|
||||
};
|
||||
|
||||
return cloner.shallowClone(tp);
|
||||
}
|
||||
|
||||
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
|
||||
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
|
||||
{
|
||||
if (typeId->persistent && !ignorePersistent)
|
||||
if (typeId->persistent && !clonePersistentTypes)
|
||||
return typeId;
|
||||
|
||||
TypeCloner cloner{
|
||||
|
@ -578,7 +578,7 @@ TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool
|
|||
cloneState.builtinTypes,
|
||||
NotNull{&cloneState.seenTypes},
|
||||
NotNull{&cloneState.seenTypePacks},
|
||||
ignorePersistent ? typeId : nullptr,
|
||||
clonePersistentTypes ? typeId : nullptr,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
|||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
|
@ -49,8 +48,10 @@ LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
|
|||
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFollowTypeAlias)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFollowExistingTypeFunction)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -206,8 +207,8 @@ ConstraintGenerator::ConstraintGenerator(
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
const ScopePtr& globalScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
ScopePtr globalScope,
|
||||
ScopePtr typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
DcrLogger* logger,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
|
@ -223,8 +224,8 @@ ConstraintGenerator::ConstraintGenerator(
|
|||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, moduleResolver(moduleResolver)
|
||||
, ice(ice)
|
||||
, globalScope(globalScope)
|
||||
, typeFunctionScope(typeFunctionScope)
|
||||
, globalScope(std::move(globalScope))
|
||||
, typeFunctionScope(std::move(typeFunctionScope))
|
||||
, prepareModuleScope(std::move(prepareModuleScope))
|
||||
, requireCycles(std::move(requireCycles))
|
||||
, logger(logger)
|
||||
|
@ -607,11 +608,18 @@ void ConstraintGenerator::computeRefinement(
|
|||
|
||||
TypeId nextDiscriminantTy = arena->addType(TableType{});
|
||||
NotNull<TableType> table{getMutable<TableType>(nextDiscriminantTy)};
|
||||
// When we fully support read-write properties (i.e. when we allow properties with
|
||||
// completely disparate read and write types), then the following property can be
|
||||
// set to read-only since refinements only tell us about what we read. This cannot
|
||||
// be allowed yet though because it causes read and write types to diverge.
|
||||
table->props[*key->propName] = Property::rw(discriminantTy);
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
table->props[*key->propName] = Property::readonly(discriminantTy);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When we fully support read-write properties (i.e. when we allow properties with
|
||||
// completely disparate read and write types), then the following property can be
|
||||
// set to read-only since refinements only tell us about what we read. This cannot
|
||||
// be allowed yet though because it causes read and write types to diverge.
|
||||
table->props[*key->propName] = Property::rw(discriminantTy);
|
||||
}
|
||||
table->scope = scope.get();
|
||||
table->state = TableState::Sealed;
|
||||
|
||||
|
@ -1998,29 +2006,91 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
|||
else
|
||||
{
|
||||
Luau::Property& prop = props[propName];
|
||||
TypeId currentTy = prop.type();
|
||||
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
||||
if (auto readTy = prop.readTy)
|
||||
{
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(*readTy))
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
||||
|
||||
prop.readTy = newItv;
|
||||
prop.writeTy = newItv;
|
||||
}
|
||||
else if (get<FunctionType>(currentTy))
|
||||
{
|
||||
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
|
||||
prop.readTy = newItv;
|
||||
}
|
||||
else if (get<FunctionType>(*readTy))
|
||||
{
|
||||
TypeId intersection = arena->addType(IntersectionType{{*readTy, propTy}});
|
||||
|
||||
prop.readTy = intersection;
|
||||
prop.writeTy = intersection;
|
||||
prop.readTy = intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(
|
||||
declaredExternType->location,
|
||||
GenericError{format("Cannot overload read type of non-function class member '%s'", propName.c_str())}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto writeTy = prop.writeTy)
|
||||
{
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(*writeTy))
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
||||
|
||||
prop.writeTy = newItv;
|
||||
}
|
||||
else if (get<FunctionType>(*writeTy))
|
||||
{
|
||||
TypeId intersection = arena->addType(IntersectionType{{*writeTy, propTy}});
|
||||
|
||||
prop.writeTy = intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(
|
||||
declaredExternType->location,
|
||||
GenericError{format("Cannot overload write type of non-function class member '%s'", propName.c_str())}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
TypeId currentTy = prop.type_DEPRECATED();
|
||||
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
||||
|
||||
prop.readTy = newItv;
|
||||
prop.writeTy = newItv;
|
||||
}
|
||||
else if (get<FunctionType>(currentTy))
|
||||
{
|
||||
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
|
||||
|
||||
prop.readTy = intersection;
|
||||
prop.writeTy = intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(
|
||||
declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2052,8 +2122,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
|||
funScope = childScope(global, scope);
|
||||
|
||||
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
|
||||
TypePackId retPack = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false)
|
||||
: resolveTypePack(funScope, global->retTypes_DEPRECATED, /* inTypeArguments */ false);
|
||||
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false);
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
|
@ -3538,7 +3607,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||
|
||||
// If there is both an annotation and an expected type, the annotation wins.
|
||||
// Type checking will sort out any discrepancies later.
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation)
|
||||
if (fn->returnAnnotation)
|
||||
{
|
||||
TypePackId annotatedRetType =
|
||||
resolveTypePack(signatureScope, fn->returnAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
||||
|
@ -3548,16 +3617,6 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||
LUAU_ASSERT(get<FreeTypePack>(returnType));
|
||||
emplaceTypePack<BoundTypePack>(asMutable(returnType), annotatedRetType);
|
||||
}
|
||||
else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation_DEPRECATED)
|
||||
{
|
||||
TypePackId annotatedRetType =
|
||||
resolveTypePack(signatureScope, *fn->returnAnnotation_DEPRECATED, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
||||
// We bind the annotated type directly here so that, when we need to
|
||||
// generate constraints for return types, we have a guarantee that we
|
||||
// know the annotated return type already, if one was provided.
|
||||
LUAU_ASSERT(get<FreeTypePack>(returnType));
|
||||
emplaceTypePack<BoundTypePack>(asMutable(returnType), annotatedRetType);
|
||||
}
|
||||
else if (expectedFunction)
|
||||
{
|
||||
emplaceTypePack<BoundTypePack>(asMutable(returnType), expectedFunction->retTypes);
|
||||
|
@ -3805,16 +3864,7 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
|||
AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes};
|
||||
TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
|
||||
TypePackId returnTypes;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes_DEPRECATED};
|
||||
returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
}
|
||||
TypePackId returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
|
||||
// TODO: FunctionType needs a pointer to the scope so that we know
|
||||
// how to quantify/instantiate it.
|
||||
|
|
|
@ -39,6 +39,8 @@ LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
|
|||
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1834,15 +1836,26 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||
}
|
||||
}
|
||||
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
|
||||
expr->is<AstExprConstantNil>())
|
||||
expr->is<AstExprConstantNil>() || (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is<AstExprTable>()))
|
||||
{
|
||||
ReferentialReplacer replacer{arena, NotNull{&replacements}, NotNull{&replacementPacks}};
|
||||
if (auto res = replacer.substitute(expectedArgTy))
|
||||
{
|
||||
if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
{
|
||||
// If we do this replacement and there are type
|
||||
// functions in the final type, then we need to
|
||||
// ensure those get reduced.
|
||||
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
|
||||
queuer.traverse(*res);
|
||||
}
|
||||
u2.unify(actualArgTy, *res);
|
||||
}
|
||||
else
|
||||
u2.unify(actualArgTy, expectedArgTy);
|
||||
}
|
||||
else if (expr->is<AstExprTable>() && !ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks}))
|
||||
else if (!FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is<AstExprTable>() &&
|
||||
!ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks}))
|
||||
{
|
||||
Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
|
||||
std::vector<TypeId> toBlock;
|
||||
|
@ -1852,6 +1865,27 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||
LUAU_ASSERT(toBlock.empty());
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
{
|
||||
// Consider:
|
||||
//
|
||||
// local Direction = { Left = 1, Right = 2 }
|
||||
// type Direction = keyof<Direction>
|
||||
//
|
||||
// local function move(dirs: { Direction }) --[[...]] end
|
||||
//
|
||||
// move({ "Left", "Right", "Left", "Right" })
|
||||
//
|
||||
// We need `keyof<Direction>` to reduce prior to inferring that the
|
||||
// arguments to `move` must generalize to their lower bounds. This
|
||||
// is how we ensure that ordering.
|
||||
for (auto& c : u2.incompleteSubtypes)
|
||||
{
|
||||
NotNull<Constraint> addition = pushConstraint(constraint->scope, constraint->location, std::move(c));
|
||||
inheritBlocks(constraint, addition);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3173,8 +3207,16 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
return {{}, result.propType};
|
||||
|
||||
// TODO: __index can be an overloaded function.
|
||||
//
|
||||
|
||||
TypeId indexType = follow(indexProp->second.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
// if the property is write-only, then surely we cannot read from it.
|
||||
if (indexProp->second.isWriteOnly())
|
||||
return {{}, builtinTypes->errorType};
|
||||
}
|
||||
|
||||
TypeId indexType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED());
|
||||
|
||||
if (auto ft = get<FunctionType>(indexType))
|
||||
{
|
||||
|
@ -3212,7 +3254,16 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
if (indexProp == metatable->props.end())
|
||||
return {{}, std::nullopt};
|
||||
|
||||
return lookupTableProp(constraint, indexProp->second.type(), propName, context, inConditional, suppressSimplification, seen);
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
// if the property is write-only, then surely we cannot read from it.
|
||||
if (indexProp->second.isWriteOnly())
|
||||
return {{}, builtinTypes->errorType};
|
||||
|
||||
return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen);
|
||||
}
|
||||
else
|
||||
return lookupTableProp(constraint, indexProp->second.type_DEPRECATED(), propName, context, inConditional, suppressSimplification, seen);
|
||||
}
|
||||
else if (auto ft = get<FreeType>(subjectType))
|
||||
{
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops)
|
||||
|
@ -951,10 +950,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
|||
visitGenerics(d->generics);
|
||||
visitGenericPacks(d->genericPacks);
|
||||
visitTypeList(d->params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visitTypePack(d->retTypes);
|
||||
else
|
||||
visitTypeList(d->retTypes_DEPRECATED);
|
||||
visitTypePack(d->retTypes);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
@ -1169,16 +1165,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
if (f->varargAnnotation)
|
||||
visitTypePack(f->varargAnnotation);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (f->returnAnnotation)
|
||||
visitTypePack(f->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f->returnAnnotation_DEPRECATED)
|
||||
visitTypeList(*f->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (f->returnAnnotation)
|
||||
visitTypePack(f->returnAnnotation);
|
||||
|
||||
// TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be
|
||||
// visible to the beginning of the function, so statically speaking, the body of the function has an exit point
|
||||
|
@ -1420,10 +1408,7 @@ void DataFlowGraphBuilder::visitType(AstTypeFunction* f)
|
|||
visitGenerics(f->generics);
|
||||
visitGenericPacks(f->genericPacks);
|
||||
visitTypeList(f->argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visitTypePack(f->returnTypes);
|
||||
else
|
||||
visitTypeList(f->returnTypes_DEPRECATED);
|
||||
visitTypePack(f->returnTypes);
|
||||
}
|
||||
|
||||
void DataFlowGraphBuilder::visitType(AstTypeTypeof* t)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplification)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplificationToDot)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauExtraEqSatSanityChecks)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau::EqSatSimplification
|
||||
{
|
||||
|
@ -2369,10 +2370,16 @@ void Simplifier::intersectTableProperty(Id id)
|
|||
newIntersectionParts.push_back(intersectionParts[index]);
|
||||
}
|
||||
|
||||
Id newTableProp = egraph.add(Intersection{
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type()),
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type())
|
||||
});
|
||||
Id newTableProp =
|
||||
FFlag::LuauRemoveTypeCallsForReadWriteProps
|
||||
? egraph.add(Intersection{
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, *it->second.readTy),
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, *table1Ty->props.begin()->second.readTy)
|
||||
})
|
||||
: egraph.add(Intersection{
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type_DEPRECATED()),
|
||||
toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type_DEPRECATED())
|
||||
});
|
||||
|
||||
newIntersectionParts.push_back(egraph.add(TTable{jId, {stringCache.add(it->first)}, {newTableProp}}));
|
||||
|
||||
|
@ -2444,7 +2451,10 @@ void Simplifier::unneededTableModification(Id id)
|
|||
StringId propName = tbl->propNames[i];
|
||||
const Id propType = tbl->propTypes()[i];
|
||||
|
||||
Id importedProp = toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type());
|
||||
Id importedProp =
|
||||
FFlag::LuauRemoveTypeCallsForReadWriteProps
|
||||
? toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy)
|
||||
: toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type_DEPRECATED());
|
||||
|
||||
if (find(importedProp) != find(propType))
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
|
||||
|
||||
static std::string wrongNumberOfArgsString(
|
||||
|
@ -418,7 +419,12 @@ struct ErrorConverter
|
|||
|
||||
auto it = mtt->props.find("__call");
|
||||
if (it != mtt->props.end())
|
||||
return it->second.type();
|
||||
{
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
return it->second.readTy;
|
||||
else
|
||||
return it->second.type_DEPRECATED();
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys)
|
||||
LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ struct IndexCollector : public TypeOnceVisitor
|
|||
|
||||
bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr)
|
||||
{
|
||||
if (!FFlag::LuauImplicitTableIndexerKeys)
|
||||
if (!FFlag::LuauImplicitTableIndexerKeys2)
|
||||
return true;
|
||||
|
||||
if (auto ty = astTypes->find(expr->expr))
|
||||
|
|
|
@ -34,7 +34,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
|||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
|
||||
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver)
|
||||
|
@ -51,17 +50,10 @@ Location getFunctionDeclarationExtents(AstExprFunction* exprFn, AstExpr* exprNam
|
|||
{
|
||||
auto fnBegin = exprFn->location.begin;
|
||||
auto fnEnd = exprFn->location.end;
|
||||
if (auto returnAnnot = exprFn->returnAnnotation; FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||
if (auto returnAnnot = exprFn->returnAnnotation)
|
||||
{
|
||||
fnEnd = returnAnnot->location.end;
|
||||
}
|
||||
else if (auto returnAnnot = exprFn->returnAnnotation_DEPRECATED; !FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||
{
|
||||
if (returnAnnot->tailType)
|
||||
fnEnd = returnAnnot->tailType->location.end;
|
||||
else if (returnAnnot->types.size != 0)
|
||||
fnEnd = returnAnnot->types.data[returnAnnot->types.size - 1]->location.end;
|
||||
}
|
||||
else if (exprFn->args.size != 0)
|
||||
{
|
||||
auto last = exprFn->args.data[exprFn->args.size - 1];
|
||||
|
@ -581,7 +573,7 @@ struct UsageFinder : public AstVisitor
|
|||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatTypeAlias* alias) override
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "Luau/Frontend.h"
|
||||
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Config.h"
|
||||
#include "Luau/ConstraintGenerator.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
|
@ -18,8 +18,6 @@
|
|||
#include "Luau/Scope.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/TimeTrace.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Transpiler.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
|
@ -48,6 +46,8 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackTypeAllocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUseWorkspacePropToChooseSolver)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -399,6 +399,21 @@ Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, c
|
|||
{
|
||||
}
|
||||
|
||||
void Frontend::setLuauSolverSelectionFromWorkspace(bool newSolverEnabled)
|
||||
{
|
||||
useNewLuauSolver.store(newSolverEnabled);
|
||||
}
|
||||
|
||||
bool Frontend::getLuauSolverSelection() const
|
||||
{
|
||||
return useNewLuauSolver.load();
|
||||
}
|
||||
|
||||
bool Frontend::getLuauSolverSelectionFlagged() const
|
||||
{
|
||||
return FFlag::LuauUseWorkspacePropToChooseSolver ? getLuauSolverSelection() : FFlag::LuauSolverV2;
|
||||
}
|
||||
|
||||
void Frontend::parse(const ModuleName& name)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Frontend::parse", "Frontend");
|
||||
|
@ -983,6 +998,15 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
item.stats.timeCheck += duration;
|
||||
item.stats.filesStrict += 1;
|
||||
|
||||
if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats)
|
||||
{
|
||||
item.stats.typesAllocated += moduleForAutocomplete->internalTypes.types.size();
|
||||
item.stats.typePacksAllocated += moduleForAutocomplete->internalTypes.typePacks.size();
|
||||
item.stats.boolSingletonsMinted += moduleForAutocomplete->internalTypes.boolSingletonsMinted;
|
||||
item.stats.strSingletonsMinted += moduleForAutocomplete->internalTypes.strSingletonsMinted;
|
||||
item.stats.uniqueStrSingletonsMinted += moduleForAutocomplete->internalTypes.uniqueStrSingletonsMinted.size();
|
||||
}
|
||||
|
||||
if (item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *moduleForAutocomplete);
|
||||
|
||||
|
@ -1003,6 +1027,15 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
item.stats.filesStrict += mode == Mode::Strict;
|
||||
item.stats.filesNonstrict += mode == Mode::Nonstrict;
|
||||
|
||||
if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats)
|
||||
{
|
||||
item.stats.typesAllocated += module->internalTypes.types.size();
|
||||
item.stats.typePacksAllocated += module->internalTypes.typePacks.size();
|
||||
item.stats.boolSingletonsMinted += module->internalTypes.boolSingletonsMinted;
|
||||
item.stats.strSingletonsMinted += module->internalTypes.strSingletonsMinted;
|
||||
item.stats.uniqueStrSingletonsMinted += module->internalTypes.uniqueStrSingletonsMinted.size();
|
||||
}
|
||||
|
||||
if (item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *module);
|
||||
|
||||
|
@ -1124,6 +1157,16 @@ void Frontend::recordItemResult(const BuildQueueItem& item)
|
|||
|
||||
stats.filesStrict += item.stats.filesStrict;
|
||||
stats.filesNonstrict += item.stats.filesNonstrict;
|
||||
|
||||
if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats)
|
||||
{
|
||||
stats.typesAllocated += item.stats.typesAllocated;
|
||||
stats.typePacksAllocated += item.stats.typePacksAllocated;
|
||||
|
||||
stats.boolSingletonsMinted += item.stats.boolSingletonsMinted;
|
||||
stats.strSingletonsMinted += item.stats.strSingletonsMinted;
|
||||
stats.uniqueStrSingletonsMinted += item.stats.uniqueStrSingletonsMinted;
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state, size_t itemPos)
|
||||
|
@ -1393,6 +1436,8 @@ ModulePtr check(
|
|||
result->mode = mode;
|
||||
result->internalTypes.owningModule = result.get();
|
||||
result->interfaceTypes.owningModule = result.get();
|
||||
if (FFlag::LuauTrackTypeAllocations)
|
||||
result->internalTypes.collectSingletonStats = options.collectTypeAllocationStats;
|
||||
result->allocator = sourceModule.allocator;
|
||||
result->names = sourceModule.names;
|
||||
result->root = sourceModule.root;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules)
|
||||
|
@ -562,7 +563,10 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
{
|
||||
Polarity p = polarity;
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
traverse(*prop.readTy);
|
||||
else
|
||||
traverse(prop.type_DEPRECATED());
|
||||
polarity = p;
|
||||
}
|
||||
else
|
||||
|
@ -586,7 +590,10 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
|
||||
Polarity p = polarity;
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
traverse(*prop.readTy);
|
||||
else
|
||||
traverse(prop.type_DEPRECATED());
|
||||
polarity = p;
|
||||
}
|
||||
}
|
||||
|
@ -1518,7 +1525,10 @@ struct GenericCounter : TypeVisitor
|
|||
{
|
||||
Polarity p = polarity;
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
traverse(*prop.readTy);
|
||||
else
|
||||
traverse(prop.type_DEPRECATED());
|
||||
polarity = p;
|
||||
}
|
||||
else
|
||||
|
@ -1542,7 +1552,10 @@ struct GenericCounter : TypeVisitor
|
|||
|
||||
Polarity p = polarity;
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
traverse(*prop.readTy);
|
||||
else
|
||||
traverse(prop.type_DEPRECATED());
|
||||
polarity = p;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties)
|
||||
|
||||
namespace Luau
|
||||
|
@ -49,7 +50,7 @@ struct InferPolarity : TypeVisitor
|
|||
return false;
|
||||
|
||||
const Polarity p = polarity;
|
||||
if (FFlag::LuauInferPolarityOfReadWriteProperties)
|
||||
if (FFlag::LuauInferPolarityOfReadWriteProperties || FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
for (const auto& [name, prop] : tt.props)
|
||||
{
|
||||
|
@ -80,7 +81,7 @@ struct InferPolarity : TypeVisitor
|
|||
if (prop.isShared())
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
traverse(prop.type_DEPRECATED());
|
||||
}
|
||||
else if (prop.isReadOnly())
|
||||
{
|
||||
|
|
|
@ -16,8 +16,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4)
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -907,7 +905,7 @@ private:
|
|||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypeReference* node) override
|
||||
|
@ -1974,7 +1972,7 @@ private:
|
|||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypeTable* node) override
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
|
||||
|
||||
namespace Luau
|
||||
|
@ -773,13 +772,7 @@ struct NonStrictTypeChecker
|
|||
{
|
||||
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(exprFn->returnAnnotation);
|
||||
else
|
||||
{
|
||||
if (exprFn->returnAnnotation_DEPRECATED)
|
||||
visit(*exprFn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
visit(exprFn->returnAnnotation);
|
||||
|
||||
if (exprFn->varargAnnotation)
|
||||
visit(exprFn->varargAnnotation);
|
||||
|
|
|
@ -23,6 +23,7 @@ LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationIntersectTablesPreservesExternTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationReorderFreeTypeIntersect)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -404,7 +405,7 @@ NormalizationResult Normalizer::isInhabited(TypeId ty, Set<TypeId>& seen)
|
|||
}
|
||||
else
|
||||
{
|
||||
NormalizationResult res = isInhabited(prop.type(), seen);
|
||||
NormalizationResult res = isInhabited(prop.type_DEPRECATED(), seen);
|
||||
if (res != NormalizationResult::True)
|
||||
return res;
|
||||
}
|
||||
|
@ -2513,9 +2514,9 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
|
|||
}
|
||||
else
|
||||
{
|
||||
prop.setType(intersectionType(hprop.type(), tprop.type()));
|
||||
hereSubThere &= (prop.type() == hprop.type());
|
||||
thereSubHere &= (prop.type() == tprop.type());
|
||||
prop.setType(intersectionType(hprop.type_DEPRECATED(), tprop.type_DEPRECATED()));
|
||||
hereSubThere &= (prop.type_DEPRECATED() == hprop.type_DEPRECATED());
|
||||
thereSubHere &= (prop.type_DEPRECATED() == tprop.type_DEPRECATED());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3327,7 +3328,8 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
|||
result.reserve(result.size() + norm.tables.size());
|
||||
for (auto table : norm.tables)
|
||||
{
|
||||
makeTableShared(table);
|
||||
if (!FFlag::LuauRefineTablesWithReadType)
|
||||
makeTableShared(table);
|
||||
result.push_back(table);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "Luau/Ast.h"
|
||||
#include "Luau/Module.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -70,7 +68,7 @@ struct RequireTracer : AstVisitor
|
|||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
// allow resolving require inside `typeof` annotations
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
return true;
|
||||
}
|
||||
|
||||
AstExpr* getDependent_DEPRECATED(AstExpr* node)
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
#include "Luau/Simplify.h"
|
||||
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypeIds.h"
|
||||
#include "Luau/TypePairHash.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
|
@ -17,6 +19,9 @@ LUAU_FASTINT(LuauTypeReductionRecursionLimit)
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRelateTablesAreNeverDisjoint)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -37,10 +42,13 @@ struct TypeSimplifier
|
|||
TypeId intersectFromParts(std::set<TypeId> parts);
|
||||
|
||||
TypeId intersectUnionWithType(TypeId left, TypeId right);
|
||||
|
||||
TypeId intersectUnions(TypeId left, TypeId right);
|
||||
|
||||
TypeId intersectNegatedUnion(TypeId left, TypeId right);
|
||||
|
||||
TypeId intersectTypeWithNegation(TypeId left, TypeId right);
|
||||
|
||||
TypeId intersectNegations(TypeId left, TypeId right);
|
||||
|
||||
TypeId intersectIntersectionWithType(TypeId left, TypeId right);
|
||||
|
@ -48,18 +56,32 @@ struct TypeSimplifier
|
|||
// Attempt to intersect the two types. Does not recurse. Does not handle
|
||||
// unions, intersections, or negations.
|
||||
std::optional<TypeId> basicIntersect(TypeId left, TypeId right);
|
||||
|
||||
std::optional<TypeId> basicIntersectWithTruthy(TypeId target) const;
|
||||
|
||||
std::optional<TypeId> basicIntersectWithFalsy(TypeId target) const;
|
||||
|
||||
TypeId intersect(TypeId left, TypeId right);
|
||||
|
||||
TypeId union_(TypeId left, TypeId right);
|
||||
|
||||
TypeId simplify(TypeId ty);
|
||||
|
||||
TypeId simplify(TypeId ty, DenseHashSet<TypeId>& seen);
|
||||
|
||||
std::optional<TypeId> intersectOne(TypeId target, TypeId discriminant) const;
|
||||
|
||||
std::optional<TypeId> subtractOne(TypeId target, TypeId discriminant) const;
|
||||
|
||||
std::optional<Property> intersectProperty(const Property& target, const Property& discriminant, DenseHashSet<TypeId>& seen) const;
|
||||
|
||||
std::optional<TypeId> intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet<TypeId>& seen) const;
|
||||
|
||||
std::optional<TypeId> intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const;
|
||||
};
|
||||
|
||||
// Match the exact type false|nil
|
||||
static bool isFalsyType(TypeId ty)
|
||||
static bool isFalsyType_DEPRECATED(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
const UnionType* ut = get<UnionType>(ty);
|
||||
|
@ -103,7 +125,7 @@ static bool isFalsyType(TypeId ty)
|
|||
}
|
||||
|
||||
// Match the exact type ~(false|nil)
|
||||
bool isTruthyType(TypeId ty)
|
||||
bool isTruthyType_DEPRECATED(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
|
@ -111,7 +133,7 @@ bool isTruthyType(TypeId ty)
|
|||
if (!nt)
|
||||
return false;
|
||||
|
||||
return isFalsyType(nt->ty);
|
||||
return isFalsyType_DEPRECATED(nt->ty);
|
||||
}
|
||||
|
||||
Relation flip(Relation rel)
|
||||
|
@ -266,7 +288,7 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
);
|
||||
|
||||
if (!foundPropFromLeftInRight && !foundPropFromRightInLeft && leftTable->props.size() >= 1 && rightTable->props.size() >= 1)
|
||||
return Relation::Disjoint;
|
||||
return FFlag::LuauRelateTablesAreNeverDisjoint ? Relation::Intersects : Relation::Disjoint;
|
||||
|
||||
const auto [propName, rightProp] = *begin(rightTable->props);
|
||||
|
||||
|
@ -283,7 +305,11 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
if (!leftProp.isShared() || !rightProp.isShared())
|
||||
return Relation::Intersects;
|
||||
|
||||
Relation r = relate(leftProp.type(), rightProp.type(), seen);
|
||||
Relation r;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
r = relate(*leftProp.readTy, *rightProp.readTy, seen);
|
||||
else
|
||||
r = relate(leftProp.type_DEPRECATED(), rightProp.type_DEPRECATED(), seen);
|
||||
if (r == Relation::Coincident && 1 != leftTable->props.size())
|
||||
{
|
||||
// eg {tag: "cat", prop: string} & {tag: "cat"}
|
||||
|
@ -570,7 +596,14 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (auto propInExternType = re->props.find(name); propInExternType != re->props.end())
|
||||
{
|
||||
Relation propRel = relate(prop.type(), propInExternType->second.type());
|
||||
Relation propRel;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
LUAU_ASSERT(prop.readTy && propInExternType->second.readTy);
|
||||
propRel = relate(*prop.readTy, *propInExternType->second.readTy);
|
||||
}
|
||||
else
|
||||
propRel = relate(prop.type_DEPRECATED(), propInExternType->second.type_DEPRECATED());
|
||||
|
||||
if (propRel == Relation::Disjoint)
|
||||
return Relation::Disjoint;
|
||||
|
@ -604,6 +637,15 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (FFlag::LuauRefineTablesWithReadType && is<TableType>(right))
|
||||
{
|
||||
// FIXME: This could be better in that we can say a table only
|
||||
// intersects with an extern type if they share a property, but
|
||||
// for now it is within the contract of the function to claim
|
||||
// the two intersect.
|
||||
return Relation::Intersects;
|
||||
}
|
||||
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
|
@ -857,13 +899,13 @@ TypeId TypeSimplifier::intersectNegatedUnion(TypeId left, TypeId right)
|
|||
newParts.insert(right);
|
||||
break;
|
||||
case Relation::Coincident:
|
||||
// If A is coincident with or a superset of B, then ~A & B is never.
|
||||
//
|
||||
// ~(false?) & false
|
||||
// (~false & false) & (~nil & false)
|
||||
// never & false
|
||||
//
|
||||
// fallthrough
|
||||
// If A is coincident with or a superset of B, then ~A & B is never.
|
||||
//
|
||||
// ~(false?) & false
|
||||
// (~false & false) & (~nil & false)
|
||||
// never & false
|
||||
//
|
||||
// fallthrough
|
||||
case Relation::Superset:
|
||||
// If A is a superset of B, then ~A & B is never.
|
||||
//
|
||||
|
@ -898,6 +940,15 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) co
|
|||
{
|
||||
target = follow(target);
|
||||
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
if (isApproximatelyTruthyType(target))
|
||||
return target;
|
||||
|
||||
if (isApproximatelyFalsyType(target))
|
||||
return builtinTypes->neverType;
|
||||
}
|
||||
|
||||
if (is<UnknownType>(target))
|
||||
return builtinTypes->truthyType;
|
||||
|
||||
|
@ -934,6 +985,15 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) con
|
|||
{
|
||||
target = follow(target);
|
||||
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
if (isApproximatelyTruthyType(target))
|
||||
return builtinTypes->neverType;
|
||||
|
||||
if (isApproximatelyFalsyType(target))
|
||||
return target;
|
||||
}
|
||||
|
||||
if (is<NeverType, ErrorType>(target))
|
||||
return target;
|
||||
|
||||
|
@ -990,11 +1050,11 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
|||
switch (r)
|
||||
{
|
||||
case Relation::Coincident:
|
||||
// ~(false?) & nil
|
||||
// (~false & nil) & (~nil & nil)
|
||||
// nil & never
|
||||
//
|
||||
// fallthrough
|
||||
// ~(false?) & nil
|
||||
// (~false & nil) & (~nil & nil)
|
||||
// nil & never
|
||||
//
|
||||
// fallthrough
|
||||
case Relation::Superset:
|
||||
// ~(boolean | string) & true
|
||||
// (~boolean & true) & (~boolean & string)
|
||||
|
@ -1008,8 +1068,8 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
|||
break;
|
||||
|
||||
case Relation::Subset:
|
||||
// ~false & boolean
|
||||
// fallthrough
|
||||
// ~false & boolean
|
||||
// fallthrough
|
||||
case Relation::Intersects:
|
||||
// FIXME: The mkNegation here is pretty unfortunate.
|
||||
// Memoizing this will probably be important.
|
||||
|
@ -1046,7 +1106,7 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
|||
changed = true;
|
||||
continue;
|
||||
case Relation::Subset:
|
||||
// fallthrough
|
||||
// fallthrough
|
||||
case Relation::Intersects:
|
||||
changed = true;
|
||||
newParts.insert(arena->addType(IntersectionType{{left, part}}));
|
||||
|
@ -1085,15 +1145,15 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
|||
// ~boolean & string
|
||||
return right;
|
||||
case Relation::Coincident:
|
||||
// ~string & string
|
||||
// fallthrough
|
||||
// ~string & string
|
||||
// fallthrough
|
||||
case Relation::Superset:
|
||||
// ~string & "hello"
|
||||
return builtinTypes->neverType;
|
||||
case Relation::Subset:
|
||||
// ~string & unknown
|
||||
// ~"hello" & string
|
||||
// fallthrough
|
||||
// ~string & unknown
|
||||
// ~"hello" & string
|
||||
// fallthrough
|
||||
case Relation::Intersects:
|
||||
// ~("hello" | boolean) & string
|
||||
// fallthrough
|
||||
|
@ -1252,7 +1312,11 @@ std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
|||
auto it = rt->props.find(propName);
|
||||
if (it != rt->props.end() && leftProp.isShared() && it->second.isShared())
|
||||
{
|
||||
Relation r = relate(leftProp.type(), it->second.type());
|
||||
Relation r;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
r = relate(*leftProp.readTy, *it->second.readTy);
|
||||
else
|
||||
r = relate(leftProp.type_DEPRECATED(), it->second.type_DEPRECATED());
|
||||
|
||||
switch (r)
|
||||
{
|
||||
|
@ -1302,21 +1366,43 @@ std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (isTruthyType(left))
|
||||
if (auto res = basicIntersectWithTruthy(right))
|
||||
return res;
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
if (isApproximatelyTruthyType(left))
|
||||
if (auto res = basicIntersectWithTruthy(right))
|
||||
return res;
|
||||
|
||||
if (isTruthyType(right))
|
||||
if (auto res = basicIntersectWithTruthy(left))
|
||||
return res;
|
||||
if (isApproximatelyTruthyType(right))
|
||||
if (auto res = basicIntersectWithTruthy(left))
|
||||
return res;
|
||||
|
||||
if (isFalsyType(left))
|
||||
if (auto res = basicIntersectWithFalsy(right))
|
||||
return res;
|
||||
if (isApproximatelyFalsyType(left))
|
||||
if (auto res = basicIntersectWithFalsy(right))
|
||||
return res;
|
||||
|
||||
if (isApproximatelyFalsyType(right))
|
||||
if (auto res = basicIntersectWithFalsy(left))
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isTruthyType_DEPRECATED(left))
|
||||
if (auto res = basicIntersectWithTruthy(right))
|
||||
return res;
|
||||
|
||||
if (isTruthyType_DEPRECATED(right))
|
||||
if (auto res = basicIntersectWithTruthy(left))
|
||||
return res;
|
||||
|
||||
if (isFalsyType_DEPRECATED(left))
|
||||
if (auto res = basicIntersectWithFalsy(right))
|
||||
return res;
|
||||
|
||||
if (isFalsyType_DEPRECATED(right))
|
||||
if (auto res = basicIntersectWithFalsy(left))
|
||||
return res;
|
||||
}
|
||||
|
||||
if (isFalsyType(right))
|
||||
if (auto res = basicIntersectWithFalsy(left))
|
||||
return res;
|
||||
|
||||
Relation relation = relate(left, right);
|
||||
if (left == right || Relation::Coincident == relation)
|
||||
|
@ -1548,6 +1634,276 @@ TypeId TypeSimplifier::simplify(TypeId ty, DenseHashSet<TypeId>& seen)
|
|||
return ty;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool isSimpleDiscriminant(TypeId ty, DenseHashSet<TypeId>& seen)
|
||||
{
|
||||
ty = follow(ty);
|
||||
// If we *ever* see a recursive type, bail right away, clearly that is
|
||||
// not simple.
|
||||
if (seen.contains(ty))
|
||||
return false;
|
||||
seen.insert(ty);
|
||||
|
||||
// NOTE: We could probably support `{}` as a simple discriminant.
|
||||
if (auto ttv = get<TableType>(ty); ttv && ttv->props.size() == 1 && !ttv->indexer)
|
||||
{
|
||||
auto prop = begin(ttv->props)->second;
|
||||
return (!prop.readTy || isSimpleDiscriminant(*prop.readTy, seen)) && (!prop.writeTy || isSimpleDiscriminant(*prop.writeTy, seen));
|
||||
}
|
||||
|
||||
if (auto nt = get<NegationType>(ty))
|
||||
return isSimpleDiscriminant(nt->ty, seen);
|
||||
|
||||
return is<PrimitiveType, SingletonType, ExternType>(ty) || isApproximatelyTruthyType(ty) || isApproximatelyFalsyType(ty);
|
||||
}
|
||||
|
||||
/**
|
||||
* There are some types that are "simple", and thus easy to intersect against:
|
||||
* - The "truthy" (`~(false?)`) and "falsy" (`false?`) types are simple.
|
||||
* - Primitive types, singleton types, and extern types are simple
|
||||
* - Table types are simple if they have no indexer, and have a single property
|
||||
* who's read and write types are also simple.
|
||||
* - Cyclic types are never simple.
|
||||
*/
|
||||
bool isSimpleDiscriminant(TypeId ty)
|
||||
{
|
||||
DenseHashSet<TypeId> seenSet{nullptr};
|
||||
return isSimpleDiscriminant(ty, seenSet);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::intersectOne(TypeId target, TypeId discriminant) const
|
||||
{
|
||||
switch (relate(target, discriminant))
|
||||
{
|
||||
case Relation::Disjoint: // No A is a B or vice versa
|
||||
return builtinTypes->neverType;
|
||||
case Relation::Subset: // Every A is in B
|
||||
case Relation::Coincident: // Every A is in B and vice versa
|
||||
return target;
|
||||
case Relation::Superset: // Every B is in A
|
||||
return discriminant;
|
||||
case Relation::Intersects:
|
||||
default:
|
||||
// Some As are in B and some Bs are in A. ex (number | string) <-> (string | boolean).
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::subtractOne(TypeId target, TypeId discriminant) const
|
||||
{
|
||||
target = follow(target);
|
||||
discriminant = follow(discriminant);
|
||||
|
||||
if (auto nt = get<NegationType>(discriminant))
|
||||
return intersectOne(target, nt->ty);
|
||||
|
||||
switch (relate(target, discriminant))
|
||||
{
|
||||
case Relation::Disjoint: // A v B is empty => A - B is equivalent to A
|
||||
return target;
|
||||
case Relation::Subset: // A v B is A => A - B is empty
|
||||
case Relation::Coincident: // Same as above: A == B so A - B = {}
|
||||
return builtinTypes->neverType;
|
||||
case Relation::Superset:
|
||||
case Relation::Intersects:
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Property> TypeSimplifier::intersectProperty(const Property& target, const Property& discriminant, DenseHashSet<TypeId>& seen) const
|
||||
{
|
||||
// NOTE: I invite the reader to refactor the below code as a fun coding
|
||||
// exercise. It looks ugly to me, but I don't think we can make it
|
||||
// any cleaner.
|
||||
|
||||
Property prop;
|
||||
prop.deprecated = target.deprecated || discriminant.deprecated;
|
||||
|
||||
// We're trying to follow the following rules for both read and write types:
|
||||
// * If the type is present on both properties, intersect it, and return
|
||||
// `std::nullopt` if we fail.
|
||||
// * If the type only exists on one property or the other, take that.
|
||||
|
||||
if (target.readTy && discriminant.readTy)
|
||||
{
|
||||
prop.readTy = intersectWithSimpleDiscriminant(*target.readTy, *discriminant.readTy, seen);
|
||||
if (!prop.readTy)
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (target.readTy && !discriminant.readTy)
|
||||
prop.readTy = target.readTy;
|
||||
else if (!target.readTy && discriminant.readTy)
|
||||
prop.readTy = discriminant.readTy;
|
||||
|
||||
if (target.writeTy && discriminant.writeTy)
|
||||
{
|
||||
prop.writeTy = intersectWithSimpleDiscriminant(*target.writeTy, *discriminant.writeTy, seen);
|
||||
if (!prop.writeTy)
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (target.writeTy && !discriminant.writeTy)
|
||||
prop.writeTy = target.writeTy;
|
||||
else if (!target.writeTy && discriminant.writeTy)
|
||||
prop.writeTy = discriminant.writeTy;
|
||||
|
||||
return {prop};
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet<TypeId>& seen) const
|
||||
{
|
||||
if (seen.contains(target))
|
||||
return std::nullopt;
|
||||
|
||||
target = follow(target);
|
||||
discriminant = follow(discriminant);
|
||||
|
||||
if (auto ut = get<UnionType>(target))
|
||||
{
|
||||
seen.insert(target);
|
||||
TypeIds options;
|
||||
for (TypeId option : ut)
|
||||
{
|
||||
auto result = intersectWithSimpleDiscriminant(option, discriminant, seen);
|
||||
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
|
||||
if (is<UnknownType>(result))
|
||||
return builtinTypes->unknownType;
|
||||
|
||||
if (!is<NeverType>(*result))
|
||||
options.insert(*result);
|
||||
}
|
||||
if (options.empty())
|
||||
return builtinTypes->neverType;
|
||||
if (options.size() == 1)
|
||||
return *options.begin();
|
||||
return arena->addType(UnionType{options.take()});
|
||||
}
|
||||
|
||||
if (auto it = get<IntersectionType>(target))
|
||||
{
|
||||
seen.insert(target);
|
||||
TypeIds parts;
|
||||
for (TypeId part : it)
|
||||
{
|
||||
auto result = intersectWithSimpleDiscriminant(part, discriminant, seen);
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
|
||||
if (is<NeverType>(*result))
|
||||
return builtinTypes->neverType;
|
||||
|
||||
if (auto subIntersection = get<IntersectionType>(*result))
|
||||
{
|
||||
for (TypeId subOption : subIntersection)
|
||||
{
|
||||
if (is<NeverType>(subOption))
|
||||
return builtinTypes->neverType;
|
||||
if (!is<UnknownType>(result))
|
||||
parts.insert(*result);
|
||||
}
|
||||
}
|
||||
else if (!is<UnknownType>(*result))
|
||||
parts.insert(*result);
|
||||
}
|
||||
if (parts.empty())
|
||||
return builtinTypes->unknownType;
|
||||
if (parts.size() == 1)
|
||||
return *parts.begin();
|
||||
return arena->addType(IntersectionType{parts.take()});
|
||||
}
|
||||
|
||||
if (auto ttv = get<TableType>(target))
|
||||
{
|
||||
if (auto discTtv = get<TableType>(discriminant))
|
||||
{
|
||||
// The precondition of this function is that `discriminant` is
|
||||
// simple, so if it's a table it *must* be a sealed table with
|
||||
// a single property and no indexer.
|
||||
LUAU_ASSERT(discTtv->props.size() == 1 && !discTtv->indexer);
|
||||
const auto discProp = begin(discTtv->props);
|
||||
if (auto tyProp = ttv->props.find(discProp->first); tyProp != ttv->props.end())
|
||||
{
|
||||
auto property = intersectProperty(tyProp->second, discProp->second, seen);
|
||||
if (!property)
|
||||
return std::nullopt;
|
||||
if (property->readTy && is<NeverType>(follow(property->readTy)))
|
||||
return builtinTypes->neverType;
|
||||
if (property->writeTy && is<NeverType>(follow(property->writeTy)))
|
||||
return builtinTypes->neverType;
|
||||
|
||||
// If the property we get back is pointer identical to the
|
||||
// original property, return the underlying property as an
|
||||
// optimization.
|
||||
if (tyProp->second.readTy == property->readTy && tyProp->second.writeTy == property->writeTy)
|
||||
return target;
|
||||
|
||||
CloneState cs{builtinTypes};
|
||||
TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true);
|
||||
auto resultTtv = getMutable<TableType>(result);
|
||||
LUAU_ASSERT(resultTtv);
|
||||
resultTtv->props[tyProp->first] = *property;
|
||||
// Shallow cloning clears out scopes, so let's put back the
|
||||
// scope from the original type.
|
||||
resultTtv->scope = ttv->scope;
|
||||
return result;
|
||||
}
|
||||
|
||||
CloneState cs{builtinTypes};
|
||||
TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true);
|
||||
// Shallow cloning clears out scopes, so let's put back the
|
||||
// scope from the original type.
|
||||
auto resultTtv = getMutable<TableType>(result);
|
||||
LUAU_ASSERT(resultTtv);
|
||||
resultTtv->props.emplace(discProp->first, discProp->second);
|
||||
resultTtv->scope = ttv->scope;
|
||||
return result;
|
||||
}
|
||||
|
||||
// At this point, we're doing something like:
|
||||
//
|
||||
// { ... } & ~nil
|
||||
//
|
||||
// Which can be handled via fallthrough.
|
||||
}
|
||||
|
||||
// FIXME: We could probably return to this.
|
||||
if (is<FreeType, GenericType, BlockedType, PendingExpansionType, TypeFunctionInstanceType>(target))
|
||||
return std::nullopt;
|
||||
|
||||
if (isApproximatelyTruthyType(discriminant))
|
||||
return basicIntersectWithTruthy(target);
|
||||
|
||||
if (isApproximatelyTruthyType(target))
|
||||
return basicIntersectWithTruthy(discriminant);
|
||||
|
||||
if (isApproximatelyFalsyType(discriminant))
|
||||
return basicIntersectWithFalsy(target);
|
||||
|
||||
if (isApproximatelyFalsyType(target))
|
||||
return basicIntersectWithFalsy(discriminant);
|
||||
|
||||
if (is<AnyType>(target))
|
||||
return arena->addType(UnionType{{builtinTypes->errorType, discriminant}});
|
||||
|
||||
if (auto nty = get<NegationType>(discriminant))
|
||||
return subtractOne(target, nty->ty);
|
||||
|
||||
return intersectOne(target, discriminant);
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const
|
||||
{
|
||||
DenseHashSet<TypeId> seenSet{nullptr};
|
||||
return intersectWithSimpleDiscriminant(target, discriminant, seenSet);
|
||||
}
|
||||
|
||||
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right)
|
||||
{
|
||||
TypeSimplifier s{builtinTypes, arena};
|
||||
|
@ -1581,4 +1937,25 @@ SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeAre
|
|||
return SimplifyResult{res, std::move(s.blockedTypes)};
|
||||
}
|
||||
|
||||
|
||||
std::optional<TypeId> intersectWithSimpleDiscriminant(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
TypeId target,
|
||||
TypeId discriminant
|
||||
)
|
||||
{
|
||||
if (!isSimpleDiscriminant(discriminant))
|
||||
{
|
||||
if (isSimpleDiscriminant(target))
|
||||
return intersectWithSimpleDiscriminant(builtinTypes, arena, discriminant, target);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TypeSimplifier s{builtinTypes, arena};
|
||||
|
||||
return s.intersectWithSimpleDiscriminant(target, discriminant);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAG(LuauSolverAgnosticClone)
|
||||
|
||||
namespace Luau
|
||||
|
@ -196,7 +197,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
|||
visitChild(prop.writeTy);
|
||||
}
|
||||
else
|
||||
visitChild(prop.type());
|
||||
visitChild(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
if (ttv->indexer)
|
||||
|
@ -245,7 +246,15 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
|||
else if (const ExternType* etv = get<ExternType>(ty))
|
||||
{
|
||||
for (const auto& [name, prop] : etv->props)
|
||||
visitChild(prop.type());
|
||||
{
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
visitChild(prop.readTy);
|
||||
visitChild(prop.writeTy);
|
||||
}
|
||||
else
|
||||
visitChild(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
if (etv->parent)
|
||||
visitChild(*etv->parent);
|
||||
|
@ -782,7 +791,7 @@ void Substitution::replaceChildren(TypeId ty)
|
|||
prop.writeTy = replace(prop.writeTy);
|
||||
}
|
||||
else
|
||||
prop.setType(replace(prop.type()));
|
||||
prop.setType(replace(prop.type_DEPRECATED()));
|
||||
}
|
||||
|
||||
if (ttv->indexer)
|
||||
|
@ -831,7 +840,17 @@ void Substitution::replaceChildren(TypeId ty)
|
|||
else if (ExternType* etv = getMutable<ExternType>(ty))
|
||||
{
|
||||
for (auto& [name, prop] : etv->props)
|
||||
prop.setType(replace(prop.type()));
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
if (prop.readTy)
|
||||
prop.readTy = replace(prop.readTy);
|
||||
if (prop.writeTy)
|
||||
prop.writeTy = replace(prop.writeTy);
|
||||
}
|
||||
else
|
||||
prop.setType(replace(prop.type_DEPRECATED()));
|
||||
}
|
||||
|
||||
if (etv->parent)
|
||||
etv->parent = replace(*etv->parent);
|
||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
|||
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1466,9 +1467,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
|||
if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType, scope).isSubtype)
|
||||
{
|
||||
if (superProp.isShared())
|
||||
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type(), scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::read(name)));
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::read(name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type_DEPRECATED(), scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::read(name)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (superProp.readTy)
|
||||
|
@ -1643,10 +1655,22 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim
|
|||
{
|
||||
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type()))
|
||||
result.orElse(
|
||||
isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
);
|
||||
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
// the `string` metatable should not have any write-only types.
|
||||
LUAU_ASSERT(*it->second.readTy);
|
||||
|
||||
if (auto stringTable = get<TableType>(*it->second.readTy))
|
||||
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
|
||||
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type_DEPRECATED()))
|
||||
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
|
||||
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1676,10 +1700,21 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
{
|
||||
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type()))
|
||||
result.orElse(
|
||||
isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
);
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
// the `string` metatable should not have any write-only types.
|
||||
LUAU_ASSERT(*it->second.readTy);
|
||||
|
||||
if (auto stringTable = get<TableType>(*it->second.readTy))
|
||||
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
|
||||
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type_DEPRECATED()))
|
||||
result.orElse(isCovariantWith(env, stringTable, superTable, scope)
|
||||
.withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1712,7 +1747,12 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
SubtypingResult res{true};
|
||||
|
||||
if (superProp.isShared() && subProp.isShared())
|
||||
res.andAlso(isInvariantWith(env, subProp.type(), superProp.type(), scope).withBothComponent(TypePath::Property::read(name)));
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name)));
|
||||
else
|
||||
res.andAlso(isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (superProp.readTy.has_value() && subProp.readTy.has_value())
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -188,7 +189,29 @@ void StateDot::visitChildren(TypeId ty, int index)
|
|||
return visitChild(*t.boundTo, index, "boundTo");
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
visitChild(prop.type(), index, name.c_str());
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (prop.isShared())
|
||||
visitChild(*prop.readTy, index, name.c_str());
|
||||
else
|
||||
{
|
||||
if (prop.readTy)
|
||||
{
|
||||
std::string readName = "read " + name;
|
||||
visitChild(*prop.readTy, index, readName.c_str());
|
||||
}
|
||||
|
||||
if (prop.writeTy)
|
||||
{
|
||||
std::string writeName = "write " + name;
|
||||
visitChild(*prop.writeTy, index, writeName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
visitChild(prop.type_DEPRECATED(), index, name.c_str());
|
||||
}
|
||||
if (t.indexer)
|
||||
{
|
||||
visitChild(t.indexer->indexType, index, "[index]");
|
||||
|
@ -306,7 +329,29 @@ void StateDot::visitChildren(TypeId ty, int index)
|
|||
finishNode();
|
||||
|
||||
for (const auto& [name, prop] : t.props)
|
||||
visitChild(prop.type(), index, name.c_str());
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (prop.isShared())
|
||||
visitChild(*prop.readTy, index, name.c_str());
|
||||
else
|
||||
{
|
||||
if (prop.readTy)
|
||||
{
|
||||
std::string readName = "read " + name;
|
||||
visitChild(*prop.readTy, index, readName.c_str());
|
||||
}
|
||||
|
||||
if (prop.writeTy)
|
||||
{
|
||||
std::string writeName = "write " + name;
|
||||
visitChild(*prop.writeTy, index, writeName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
visitChild(prop.type_DEPRECATED(), index, name.c_str());
|
||||
}
|
||||
|
||||
if (t.parent)
|
||||
visitChild(*t.parent, index, "[parent]");
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
/*
|
||||
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
||||
|
@ -409,7 +410,10 @@ struct TypeStringifier
|
|||
if (prop.isShared())
|
||||
{
|
||||
emitKey(name);
|
||||
stringify(prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
stringify(*prop.readTy);
|
||||
else
|
||||
stringify(prop.type_DEPRECATED());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -440,7 +444,7 @@ struct TypeStringifier
|
|||
return _newStringify(name, prop);
|
||||
|
||||
emitKey(name);
|
||||
stringify(prop.type());
|
||||
stringify(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
void stringify(TypePackId tp);
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -302,7 +300,7 @@ struct ArcCollector : public AstVisitor
|
|||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,6 +30,7 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -708,8 +709,11 @@ Property Property::create(std::optional<TypeId> read, std::optional<TypeId> writ
|
|||
}
|
||||
}
|
||||
|
||||
TypeId Property::type() const
|
||||
TypeId Property::type_DEPRECATED() const
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_ASSERT(!FFlag::LuauSolverV2);
|
||||
|
||||
LUAU_ASSERT(readTy);
|
||||
return *readTy;
|
||||
}
|
||||
|
@ -834,7 +838,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs)
|
|||
if (l->first != r->first)
|
||||
return false;
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauSubtypingCheckFunctionGenericCounts)
|
||||
if (FFlag::LuauSolverV2 && (FFlag::LuauSubtypingCheckFunctionGenericCounts || FFlag::LuauRemoveTypeCallsForReadWriteProps))
|
||||
{
|
||||
if (l->second.readTy && r->second.readTy)
|
||||
{
|
||||
|
@ -852,7 +856,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs)
|
|||
else if (l->second.writeTy || r->second.writeTy)
|
||||
return false;
|
||||
}
|
||||
else if (!areEqual(seen, *l->second.type(), *r->second.type()))
|
||||
else if (!areEqual(seen, *l->second.type_DEPRECATED(), *r->second.type_DEPRECATED()))
|
||||
return false;
|
||||
++l;
|
||||
++r;
|
||||
|
@ -1082,7 +1086,18 @@ void persist(TypeId ty)
|
|||
LUAU_ASSERT(ttv->state != TableState::Free && ttv->state != TableState::Unsealed);
|
||||
|
||||
for (const auto& [_name, prop] : ttv->props)
|
||||
queue.push_back(prop.type());
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
if (prop.readTy)
|
||||
queue.push_back(*prop.readTy);
|
||||
if (prop.writeTy)
|
||||
queue.push_back(*prop.writeTy);
|
||||
}
|
||||
else
|
||||
queue.push_back(prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
|
||||
if (ttv->indexer)
|
||||
{
|
||||
|
@ -1093,7 +1108,17 @@ void persist(TypeId ty)
|
|||
else if (auto etv = get<ExternType>(t))
|
||||
{
|
||||
for (const auto& [_name, prop] : etv->props)
|
||||
queue.push_back(prop.type());
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
if (prop.readTy)
|
||||
queue.push_back(*prop.readTy);
|
||||
if (prop.writeTy)
|
||||
queue.push_back(*prop.writeTy);
|
||||
}
|
||||
else
|
||||
queue.push_back(prop.type_DEPRECATED());
|
||||
}
|
||||
}
|
||||
else if (auto utv = get<UnionType>(t))
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "Luau/TypeArena.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena);
|
||||
LUAU_FASTFLAG(LuauTrackTypeAllocations)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -114,6 +115,29 @@ TypePackId TypeArena::addTypePackFunction(const TypePackFunction& function, std:
|
|||
return addTypePack(TypeFunctionInstanceTypePack{NotNull{&function}, std::move(typeArguments), std::move(packArguments)});
|
||||
}
|
||||
|
||||
void TypeArena::recordSingletonStats(const NotNull<SingletonType> singleton)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauTrackTypeAllocations);
|
||||
|
||||
auto record = [this](auto&& s)
|
||||
{
|
||||
using T = std::decay_t<decltype(s)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, BooleanSingleton>)
|
||||
boolSingletonsMinted += 1;
|
||||
else if constexpr (std::is_same_v<T, StringSingleton>)
|
||||
{
|
||||
strSingletonsMinted += 1;
|
||||
if (!s.value.empty())
|
||||
uniqueStrSingletonsMinted.insert(s.value);
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Missing allocation count support for this kind of singleton");
|
||||
};
|
||||
|
||||
visit(record, singleton->variant);
|
||||
}
|
||||
|
||||
void freeze(TypeArena& arena)
|
||||
{
|
||||
if (!FFlag::DebugLuauFreezeArena)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/TypeAttach.h"
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
|
@ -13,8 +14,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||
{
|
||||
|
@ -197,10 +197,44 @@ public:
|
|||
|
||||
char* name = allocateString(*allocator, propName);
|
||||
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, prop.type()->ty);
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (prop.isShared())
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::ReadWrite;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prop.readTy)
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::Read;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (prop.writeTy)
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::Write;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty);
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
AstTableIndexer* indexer = nullptr;
|
||||
|
@ -238,10 +272,44 @@ public:
|
|||
{
|
||||
char* name = allocateString(*allocator, propName);
|
||||
|
||||
props.data[idx].name = AstName{name};
|
||||
props.data[idx].type = Luau::visit(*this, prop.type()->ty);
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (prop.isShared())
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::ReadWrite;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prop.readTy)
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::Read;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (prop.writeTy)
|
||||
{
|
||||
props.data[idx].name = AstName(name);
|
||||
props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty);
|
||||
props.data[idx].access = AstTableAccess::Write;
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
props.data[idx].name = AstName{name};
|
||||
props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty);
|
||||
props.data[idx].location = Location();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
AstTableIndexer* indexer = nullptr;
|
||||
|
@ -308,8 +376,7 @@ public:
|
|||
std::optional<AstArgumentName>* arg = &argNames.data[i++];
|
||||
|
||||
if (el)
|
||||
new (arg)
|
||||
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location));
|
||||
new (arg) std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), Location()));
|
||||
else
|
||||
new (arg) std::optional<AstArgumentName>();
|
||||
}
|
||||
|
@ -329,19 +396,10 @@ public:
|
|||
if (retTail)
|
||||
retTailAnnotation = rehydrate(*retTail);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{returnTypes, retTailAnnotation});
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, returnAnnotation
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
|
||||
);
|
||||
}
|
||||
auto returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{returnTypes, retTailAnnotation});
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, returnAnnotation
|
||||
);
|
||||
}
|
||||
AstType* operator()(const ErrorType&)
|
||||
{
|
||||
|
@ -596,40 +654,19 @@ public:
|
|||
visitLocal(arg);
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
if (!fn->returnAnnotation)
|
||||
{
|
||||
if (!fn->returnAnnotation)
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
TypePackId ret = result->returnType;
|
||||
TypePackId ret = result->returnType;
|
||||
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fn->returnAnnotation_DEPRECATED)
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
TypePackId ret = result->returnType;
|
||||
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation_DEPRECATED = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
fn->returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,13 +31,13 @@
|
|||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2)
|
||||
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1316,10 +1316,7 @@ void TypeChecker2::visit(AstStatDeclareFunction* stat)
|
|||
{
|
||||
visitGenerics(stat->generics, stat->genericPacks);
|
||||
visit(stat->params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(stat->retTypes);
|
||||
else
|
||||
visit(stat->retTypes_DEPRECATED);
|
||||
visit(stat->retTypes);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
||||
|
@ -1573,26 +1570,104 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
|||
argExprs.push_back(indexExpr->expr);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < call->args.size; ++i)
|
||||
if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
{
|
||||
AstExpr* arg = call->args.data[i];
|
||||
argExprs.push_back(arg);
|
||||
TypeId* argTy = module->astTypes.find(arg);
|
||||
if (argTy)
|
||||
args.head.push_back(*argTy);
|
||||
else if (i == call->args.size - 1)
|
||||
// FIXME: Similar to bidirectional inference prior, this does not support
|
||||
// overloaded functions nor generic types (yet).
|
||||
if (auto fty = get<FunctionType>(fnTy); fty && fty->generics.empty() && fty->genericPacks.empty() && call->args.size > 0)
|
||||
{
|
||||
if (auto argTail = module->astTypePacks.find(arg))
|
||||
size_t selfOffset = call->self ? 1 : 0;
|
||||
|
||||
auto [paramsHead, _] = extendTypePack(module->internalTypes, builtinTypes, fty->argTypes, call->args.size + selfOffset);
|
||||
|
||||
for (size_t idx = 0; idx < call->args.size - 1; ++idx)
|
||||
{
|
||||
auto [head, tail] = flatten(*argTail);
|
||||
args.head.insert(args.head.end(), head.begin(), head.end());
|
||||
args.tail = tail;
|
||||
auto argExpr = call->args.data[idx];
|
||||
auto argExprType = lookupType(argExpr);
|
||||
argExprs.push_back(argExpr);
|
||||
if (idx + selfOffset >= paramsHead.size() || isErrorSuppressing(argExpr->location, argExprType))
|
||||
{
|
||||
args.head.push_back(argExprType);
|
||||
continue;
|
||||
}
|
||||
testLiteralOrAstTypeIsSubtype(argExpr, paramsHead[idx + selfOffset]);
|
||||
args.head.push_back(paramsHead[idx + selfOffset]);
|
||||
}
|
||||
|
||||
auto lastExpr = call->args.data[call->args.size - 1];
|
||||
argExprs.push_back(lastExpr);
|
||||
|
||||
if (auto argTail = module->astTypePacks.find(lastExpr))
|
||||
{
|
||||
auto [lastExprHead, lastExprTail] = flatten(*argTail);
|
||||
args.head.insert(args.head.end(), lastExprHead.begin(), lastExprHead.end());
|
||||
args.tail = lastExprTail;
|
||||
}
|
||||
else if (paramsHead.size() >= call->args.size + selfOffset)
|
||||
{
|
||||
auto lastType = paramsHead[call->args.size - 1 + selfOffset];
|
||||
auto lastExprType = lookupType(lastExpr);
|
||||
if (isErrorSuppressing(lastExpr->location, lastExprType))
|
||||
{
|
||||
args.head.push_back(lastExprType);
|
||||
}
|
||||
else
|
||||
{
|
||||
testLiteralOrAstTypeIsSubtype(lastExpr, lastType);
|
||||
args.head.push_back(lastType);
|
||||
}
|
||||
}
|
||||
else
|
||||
args.tail = builtinTypes->anyTypePack;
|
||||
}
|
||||
else
|
||||
args.head.push_back(builtinTypes->anyType);
|
||||
{
|
||||
for (size_t i = 0; i < call->args.size; ++i)
|
||||
{
|
||||
AstExpr* arg = call->args.data[i];
|
||||
argExprs.push_back(arg);
|
||||
TypeId* argTy = module->astTypes.find(arg);
|
||||
if (argTy)
|
||||
args.head.push_back(*argTy);
|
||||
else if (i == call->args.size - 1)
|
||||
{
|
||||
if (auto argTail = module->astTypePacks.find(arg))
|
||||
{
|
||||
auto [head, tail] = flatten(*argTail);
|
||||
args.head.insert(args.head.end(), head.begin(), head.end());
|
||||
args.tail = tail;
|
||||
}
|
||||
else
|
||||
args.tail = builtinTypes->anyTypePack;
|
||||
}
|
||||
else
|
||||
args.head.push_back(builtinTypes->anyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < call->args.size; ++i)
|
||||
{
|
||||
AstExpr* arg = call->args.data[i];
|
||||
argExprs.push_back(arg);
|
||||
TypeId* argTy = module->astTypes.find(arg);
|
||||
if (argTy)
|
||||
args.head.push_back(*argTy);
|
||||
else if (i == call->args.size - 1)
|
||||
{
|
||||
if (auto argTail = module->astTypePacks.find(arg))
|
||||
{
|
||||
auto [head, tail] = flatten(*argTail);
|
||||
args.head.insert(args.head.end(), head.begin(), head.end());
|
||||
args.tail = tail;
|
||||
}
|
||||
else
|
||||
args.tail = builtinTypes->anyTypePack;
|
||||
}
|
||||
else
|
||||
args.head.push_back(builtinTypes->anyType);
|
||||
}
|
||||
}
|
||||
|
||||
TypePackId argsTp = module->internalTypes.addTypePack(args);
|
||||
|
@ -1964,16 +2039,8 @@ void TypeChecker2::visit(AstExprFunction* fn)
|
|||
visit(fn->body);
|
||||
|
||||
// we need to typecheck the return annotation itself, if it exists.
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (fn->returnAnnotation)
|
||||
visit(fn->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fn->returnAnnotation_DEPRECATED)
|
||||
visit(*fn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (fn->returnAnnotation)
|
||||
visit(fn->returnAnnotation);
|
||||
|
||||
|
||||
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
||||
|
@ -2751,10 +2818,7 @@ void TypeChecker2::visit(AstTypeFunction* ty)
|
|||
{
|
||||
visitGenerics(ty->generics, ty->genericPacks);
|
||||
visit(ty->argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(ty->returnTypes);
|
||||
else
|
||||
visit(ty->returnTypes_DEPRECATED);
|
||||
visit(ty->returnTypes);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstTypeTypeof* ty)
|
||||
|
|
|
@ -52,11 +52,13 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
|
|||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEmptyStringInKeyOf)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -370,7 +372,7 @@ struct TypeFunctionReducer
|
|||
}
|
||||
|
||||
if (FFlag::DebugLuauLogTypeFamilies)
|
||||
printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str());
|
||||
printf("%s => %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str());
|
||||
|
||||
asMutable(subject)->ty.template emplace<Unifiable::Bound<T>>(replacement);
|
||||
|
||||
|
@ -2259,38 +2261,11 @@ struct ContainsRefinableType : TypeOnceVisitor
|
|||
|
||||
namespace
|
||||
{
|
||||
bool isApproximateFalsy(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
bool seenNil = false;
|
||||
bool seenFalse = false;
|
||||
if (auto ut = get<UnionType>(ty))
|
||||
{
|
||||
for (auto option : ut)
|
||||
{
|
||||
if (auto pt = get<PrimitiveType>(option); pt && pt->type == PrimitiveType::NilType)
|
||||
seenNil = true;
|
||||
else if (auto st = get<SingletonType>(option); st && st->variant == BooleanSingleton{false})
|
||||
seenFalse = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return seenFalse && seenNil;
|
||||
}
|
||||
|
||||
bool isApproximateTruthy(TypeId ty)
|
||||
bool isTruthyOrFalsyType(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (auto nt = get<NegationType>(ty))
|
||||
return isApproximateFalsy(nt->ty);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSimpleDiscriminant(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
return isApproximateTruthy(ty) || isApproximateFalsy(ty);
|
||||
return isApproximatelyTruthyType(ty) || isApproximatelyFalsyType(ty);
|
||||
}
|
||||
|
||||
struct RefineTypeScrubber : public Substitution
|
||||
|
@ -2486,6 +2461,13 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
if (!crt.found)
|
||||
return {target, {}};
|
||||
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
if (auto ty = intersectWithSimpleDiscriminant(ctx->builtins, ctx->arena, target, discriminant))
|
||||
return {*ty, {}};
|
||||
}
|
||||
|
||||
// NOTE: This block causes us to refine too early in some cases.
|
||||
if (auto negation = get<NegationType>(discriminant))
|
||||
{
|
||||
if (auto primitive = get<PrimitiveType>(follow(negation->ty)); primitive && primitive->type == PrimitiveType::NilType)
|
||||
|
@ -2497,12 +2479,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
|
||||
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
||||
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
||||
// We also fire for simple discriminants such as false? and ~(false?): the falsy and truthy types respectively
|
||||
// NOTE: It would be nice to be able to do a simple intersection for something like:
|
||||
//
|
||||
// { a: A, b: B, ... } & { x: X }
|
||||
//
|
||||
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
||||
// We also fire for simple discriminants such as false? and ~(false?): the falsy and truthy types respectively.
|
||||
if (is<TableType>(target) || isTruthyOrFalsyType(discriminant))
|
||||
{
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||
if (FFlag::LuauEagerGeneralization4)
|
||||
|
@ -2551,6 +2529,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
|
||||
return {resultTy, {}};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// refine target with each discriminant type in sequence (reverse of insertion order)
|
||||
|
@ -2749,19 +2728,27 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
|||
if (get<NoRefineType>(ty))
|
||||
continue;
|
||||
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
||||
|
||||
if (FFlag::LuauNarrowIntersectionNevers)
|
||||
if (FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
// If simplifying the intersection returned never, note the type we tried to intersect it with, and continue trying to intersect with the
|
||||
// rest
|
||||
if (get<NeverType>(result.result))
|
||||
if (auto simpleResult = intersectWithSimpleDiscriminant(ctx->builtins, ctx->arena, resultTy, ty))
|
||||
{
|
||||
unintersectableTypes.insert(follow(ty));
|
||||
if (get<NeverType>(*simpleResult))
|
||||
unintersectableTypes.insert(follow(ty));
|
||||
else
|
||||
resultTy = *simpleResult;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty);
|
||||
|
||||
// If simplifying the intersection returned never, note the type we tried to intersect it with, and continue trying to intersect with the
|
||||
// rest
|
||||
if (get<NeverType>(result.result))
|
||||
{
|
||||
unintersectableTypes.insert(follow(ty));
|
||||
continue;
|
||||
}
|
||||
for (TypeId blockedType : result.blockedTypes)
|
||||
{
|
||||
if (!get<GenericType>(blockedType))
|
||||
|
@ -2771,24 +2758,20 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
|||
resultTy = result.result;
|
||||
}
|
||||
|
||||
if (FFlag::LuauNarrowIntersectionNevers)
|
||||
if (!unintersectableTypes.empty())
|
||||
{
|
||||
if (!unintersectableTypes.empty())
|
||||
unintersectableTypes.insert(resultTy);
|
||||
if (unintersectableTypes.size() > 1)
|
||||
{
|
||||
unintersectableTypes.insert(resultTy);
|
||||
if (unintersectableTypes.size() > 1)
|
||||
{
|
||||
TypeId intersection =
|
||||
ctx->arena->addType(IntersectionType{std::vector<TypeId>(unintersectableTypes.begin(), unintersectableTypes.end())});
|
||||
return {intersection, Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {*unintersectableTypes.begin(), Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
TypeId intersection =
|
||||
ctx->arena->addType(IntersectionType{std::vector<TypeId>(unintersectableTypes.begin(), unintersectableTypes.end())});
|
||||
return {intersection, Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {*unintersectableTypes.begin(), Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
}
|
||||
|
||||
// if the intersection simplifies to `never`, this gives us bad autocomplete.
|
||||
// we'll just produce the intersection plainly instead, but this might be revisitable
|
||||
// if we ever give `never` some kind of "explanation" trail.
|
||||
|
@ -2804,7 +2787,7 @@ TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
|||
// computes the keys of `ty` into `result`
|
||||
// `isRaw` parameter indicates whether or not we should follow __index metamethods
|
||||
// returns `false` if `result` should be ignored because the answer is "all strings"
|
||||
bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& seen, bool isRaw, NotNull<TypeFunctionContext> ctx)
|
||||
bool computeKeysOf_DEPRECATED(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& seen, bool isRaw, NotNull<TypeFunctionContext> ctx)
|
||||
{
|
||||
// if the type is the top table type, the answer is just "all strings"
|
||||
if (get<PrimitiveType>(ty))
|
||||
|
@ -2830,6 +2813,89 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||
return true;
|
||||
}
|
||||
|
||||
// otherwise, we have a metatable to deal with
|
||||
if (auto metatableTy = get<MetatableType>(ty))
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
if (!isRaw)
|
||||
{
|
||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
||||
// the necessary state to do that, even if we intend to just eat the errors.
|
||||
ErrorVec dummy;
|
||||
|
||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{});
|
||||
if (mmType)
|
||||
res = res && computeKeysOf_DEPRECATED(*mmType, result, seen, isRaw, ctx);
|
||||
}
|
||||
|
||||
res = res && computeKeysOf_DEPRECATED(metatableTy->table, result, seen, isRaw, ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (auto classTy = get<ExternType>(ty))
|
||||
{
|
||||
for (auto [key, _] : classTy->props) // NOLINT(performance-for-range-copy)
|
||||
result.insert(key);
|
||||
|
||||
bool res = true;
|
||||
if (classTy->metatable && !isRaw)
|
||||
{
|
||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
||||
// the necessary state to do that, even if we intend to just eat the errors.
|
||||
ErrorVec dummy;
|
||||
|
||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{});
|
||||
if (mmType)
|
||||
res = res && computeKeysOf_DEPRECATED(*mmType, result, seen, isRaw, ctx);
|
||||
}
|
||||
|
||||
if (classTy->parent)
|
||||
res = res && computeKeysOf_DEPRECATED(follow(*classTy->parent), result, seen, isRaw, ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// this should not be reachable since the type should be a valid tables or extern types part from normalization.
|
||||
LUAU_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Computes the keys of `ty` into `result`
|
||||
* `isRaw` parameter indicates whether or not we should follow __index metamethods
|
||||
* returns `false` if `result` should be ignored because the answer is "all strings"
|
||||
*/
|
||||
bool computeKeysOf(TypeId ty, Set<std::optional<std::string>>& result, DenseHashSet<TypeId>& seen, bool isRaw, NotNull<TypeFunctionContext> ctx)
|
||||
{
|
||||
|
||||
// if the type is the top table type, the answer is just "all strings"
|
||||
if (get<PrimitiveType>(ty))
|
||||
return false;
|
||||
|
||||
// if we've already seen this type, we can do nothing
|
||||
if (seen.contains(ty))
|
||||
return true;
|
||||
seen.insert(ty);
|
||||
|
||||
// if we have a particular table type, we can insert the keys
|
||||
if (auto tableTy = get<TableType>(ty))
|
||||
{
|
||||
if (tableTy->indexer)
|
||||
{
|
||||
// if we have a string indexer, the answer is, again, "all strings"
|
||||
if (isString(tableTy->indexer->indexType))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& [key, _] : tableTy->props)
|
||||
result.insert(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, we have a metatable to deal with
|
||||
if (auto metatableTy = get<MetatableType>(ty))
|
||||
{
|
||||
|
@ -2853,7 +2919,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||
|
||||
if (auto classTy = get<ExternType>(ty))
|
||||
{
|
||||
for (auto [key, _] : classTy->props)
|
||||
for (const auto& [key, _] : classTy->props)
|
||||
result.insert(key);
|
||||
|
||||
bool res = true;
|
||||
|
@ -2879,6 +2945,8 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||
const std::vector<TypeId>& typeParams,
|
||||
const std::vector<TypePackId>& packParams,
|
||||
|
@ -2910,98 +2978,201 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
|||
normTy->hasThreads() || normTy->hasBuffers() || normTy->hasFunctions() || normTy->hasTyvars())
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
// we're going to collect the keys in here
|
||||
Set<std::string> keys{{}};
|
||||
|
||||
// computing the keys for extern types
|
||||
if (normTy->hasExternTypes())
|
||||
if (FFlag::LuauEmptyStringInKeyOf)
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasTables());
|
||||
// We're going to collect the keys in here, and we use optional strings
|
||||
// so that we can differentiate between the empty string and _no_ string.
|
||||
Set<std::optional<std::string>> keys{std::nullopt};
|
||||
|
||||
// seen set for key computation for extern types
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
auto externTypeIter = normTy->externTypes.ordering.begin();
|
||||
auto externTypeIterEnd = normTy->externTypes.ordering.end();
|
||||
LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier
|
||||
|
||||
// collect all the properties from the first class type
|
||||
if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type!
|
||||
|
||||
// we need to look at each class to remove any keys that are not common amongst them all
|
||||
while (++externTypeIter != externTypeIterEnd)
|
||||
// computing the keys for extern types
|
||||
if (normTy->hasExternTypes())
|
||||
{
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
LUAU_ASSERT(!normTy->hasTables());
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
// seen set for key computation for extern types
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
// we can skip to the next class if this one is a top type
|
||||
if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
auto externTypeIter = normTy->externTypes.ordering.begin();
|
||||
auto externTypeIterEnd = normTy->externTypes.ordering.end();
|
||||
LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier
|
||||
|
||||
for (auto& key : keys)
|
||||
// collect all the properties from the first class type
|
||||
if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type!
|
||||
|
||||
// we need to look at each class to remove any keys that are not common amongst them all
|
||||
while (++externTypeIter != externTypeIterEnd)
|
||||
{
|
||||
// remove any keys that are not present in each class
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::optional<std::string>> localKeys{std::nullopt};
|
||||
|
||||
// we can skip to the next class if this one is a top type
|
||||
if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto& key : keys)
|
||||
{
|
||||
// remove any keys that are not present in each class
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computing the keys for tables
|
||||
if (normTy->hasTables())
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasExternTypes());
|
||||
|
||||
// seen set for key computation for tables
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
auto tablesIter = normTy->tables.begin();
|
||||
LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier
|
||||
|
||||
// collect all the properties from the first table type
|
||||
if (!computeKeysOf(*tablesIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type!
|
||||
|
||||
// we need to look at each tables to remove any keys that are not common amongst them all
|
||||
while (++tablesIter != normTy->tables.end())
|
||||
// computing the keys for tables
|
||||
if (normTy->hasTables())
|
||||
{
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
LUAU_ASSERT(!normTy->hasExternTypes());
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
// seen set for key computation for tables
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
// we can skip to the next table if this one is the top table type
|
||||
if (!computeKeysOf(*tablesIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
auto tablesIter = normTy->tables.begin();
|
||||
LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier
|
||||
|
||||
for (auto& key : keys)
|
||||
// collect all the properties from the first table type
|
||||
if (!computeKeysOf(*tablesIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type!
|
||||
|
||||
// we need to look at each tables to remove any keys that are not common amongst them all
|
||||
while (++tablesIter != normTy->tables.end())
|
||||
{
|
||||
// remove any keys that are not present in each table
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::optional<std::string>> localKeys{std::nullopt};
|
||||
|
||||
// we can skip to the next table if this one is the top table type
|
||||
if (!computeKeysOf(*tablesIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto& key : keys)
|
||||
{
|
||||
// remove any keys that are not present in each table
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the set of keys is empty, `keyof<T>` is `never`
|
||||
if (keys.empty())
|
||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// everything is validated, we need only construct our big union of singletons now!
|
||||
std::vector<TypeId> singletons;
|
||||
singletons.reserve(keys.size());
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (key)
|
||||
singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{*key}}));
|
||||
}
|
||||
|
||||
// If there's only one entry, we don't need a UnionType.
|
||||
// We can take straight take it from the first entry
|
||||
// because it was added into the type arena already.
|
||||
if (singletons.size() == 1)
|
||||
return {singletons.front(), Reduction::MaybeOk, {}, {}};
|
||||
|
||||
return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// if the set of keys is empty, `keyof<T>` is `never`
|
||||
if (keys.empty())
|
||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||
// we're going to collect the keys in here
|
||||
Set<std::string> keys{{}};
|
||||
|
||||
// everything is validated, we need only construct our big union of singletons now!
|
||||
std::vector<TypeId> singletons;
|
||||
singletons.reserve(keys.size());
|
||||
// computing the keys for extern types
|
||||
if (normTy->hasExternTypes())
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasTables());
|
||||
|
||||
for (const std::string& key : keys)
|
||||
singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}}));
|
||||
// seen set for key computation for extern types
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
// If there's only one entry, we don't need a UnionType.
|
||||
// We can take straight take it from the first entry
|
||||
// because it was added into the type arena already.
|
||||
if (singletons.size() == 1)
|
||||
return {singletons.front(), Reduction::MaybeOk, {}, {}};
|
||||
auto externTypeIter = normTy->externTypes.ordering.begin();
|
||||
auto externTypeIterEnd = normTy->externTypes.ordering.end();
|
||||
LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier
|
||||
|
||||
return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}};
|
||||
// collect all the properties from the first class type
|
||||
if (!computeKeysOf_DEPRECATED(*externTypeIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type!
|
||||
|
||||
// we need to look at each class to remove any keys that are not common amongst them all
|
||||
while (++externTypeIter != externTypeIterEnd)
|
||||
{
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
|
||||
// we can skip to the next class if this one is a top type
|
||||
if (!computeKeysOf_DEPRECATED(*externTypeIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto& key : keys)
|
||||
{
|
||||
// remove any keys that are not present in each class
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computing the keys for tables
|
||||
if (normTy->hasTables())
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasExternTypes());
|
||||
|
||||
// seen set for key computation for tables
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
auto tablesIter = normTy->tables.begin();
|
||||
LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier
|
||||
|
||||
// collect all the properties from the first table type
|
||||
if (!computeKeysOf_DEPRECATED(*tablesIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type!
|
||||
|
||||
// we need to look at each tables to remove any keys that are not common amongst them all
|
||||
while (++tablesIter != normTy->tables.end())
|
||||
{
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
|
||||
// we can skip to the next table if this one is the top table type
|
||||
if (!computeKeysOf_DEPRECATED(*tablesIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto& key : keys)
|
||||
{
|
||||
// remove any keys that are not present in each table
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the set of keys is empty, `keyof<T>` is `never`
|
||||
if (keys.empty())
|
||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// everything is validated, we need only construct our big union of singletons now!
|
||||
std::vector<TypeId> singletons;
|
||||
singletons.reserve(keys.size());
|
||||
|
||||
for (const std::string& key : keys)
|
||||
singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}}));
|
||||
|
||||
// If there's only one entry, we don't need a UnionType.
|
||||
// We can take straight take it from the first entry
|
||||
// because it was added into the type arena already.
|
||||
if (singletons.size() == 1)
|
||||
return {singletons.front(), Reduction::MaybeOk, {}, {}};
|
||||
|
||||
return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
}
|
||||
|
||||
TypeFunctionReductionResult<TypeId> keyofTypeFunction(
|
||||
|
@ -3054,7 +3225,21 @@ bool searchPropsAndIndexer(
|
|||
{
|
||||
if (tblProps.find(stringSingleton->value) != tblProps.end())
|
||||
{
|
||||
TypeId propTy = follow(tblProps.at(stringSingleton->value).type());
|
||||
|
||||
TypeId propTy;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
Property& prop = tblProps.at(stringSingleton->value);
|
||||
|
||||
if (prop.readTy)
|
||||
propTy = follow(*prop.readTy);
|
||||
else if (prop.writeTy)
|
||||
propTy = follow(*prop.writeTy);
|
||||
else // found the property, but there was no type associated with it
|
||||
return false;
|
||||
}
|
||||
else
|
||||
propTy = follow(tblProps.at(stringSingleton->value).type_DEPRECATED());
|
||||
|
||||
// property is a union type -> we need to extend our reduction type
|
||||
if (auto propUnionTy = get<UnionType>(propTy))
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
// currently, controls serialization, deserialization, and `type.copy`
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunctionSerializeFollowMetatable)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -388,7 +390,7 @@ private:
|
|||
void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2)
|
||||
{
|
||||
// Serialize main part of the metatable immediately
|
||||
if (auto tableTy = get<TableType>(m1->table))
|
||||
if (auto tableTy = get<TableType>(FFlag::LuauTypeFunctionSerializeFollowMetatable ? follow(m1->table) : m1->table))
|
||||
serializeChildren(tableTy, m2);
|
||||
|
||||
m2->metatable = shallowSerialize(m1->metatable);
|
||||
|
|
|
@ -32,7 +32,6 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
|||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||
|
||||
|
@ -1773,7 +1772,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExtern
|
|||
else
|
||||
{
|
||||
Luau::Property& prop = assignTo[propName];
|
||||
TypeId currentTy = prop.type();
|
||||
TypeId currentTy = prop.type_DEPRECATED();
|
||||
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
|
@ -1834,8 +1833,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti
|
|||
);
|
||||
|
||||
TypePackId argPack = resolveTypePack(funScope, global.params);
|
||||
TypePackId retPack =
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, *global.retTypes) : resolveTypePack(funScope, global.retTypes_DEPRECATED);
|
||||
TypePackId retPack = resolveTypePack(funScope, *global.retTypes);
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
|
@ -2096,7 +2094,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
|||
if (TableType* tableType = getMutableTableType(type))
|
||||
{
|
||||
if (auto it = tableType->props.find(name); it != tableType->props.end())
|
||||
return it->second.type();
|
||||
return it->second.type_DEPRECATED();
|
||||
else if (auto indexer = tableType->indexer)
|
||||
{
|
||||
// TODO: Property lookup should work with string singletons or unions thereof as the indexer key type.
|
||||
|
@ -2124,7 +2122,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
|||
{
|
||||
const Property* prop = lookupExternTypeProp(cls, name);
|
||||
if (prop)
|
||||
return prop->type();
|
||||
return prop->type_DEPRECATED();
|
||||
|
||||
if (auto indexer = cls->indexer)
|
||||
{
|
||||
|
@ -2332,9 +2330,9 @@ TypeId TypeChecker::checkExprTable(
|
|||
if (it != expectedTable->props.end())
|
||||
{
|
||||
Property expectedProp = it->second;
|
||||
ErrorVec errors = tryUnify(exprType, expectedProp.type(), scope, k->location);
|
||||
ErrorVec errors = tryUnify(exprType, expectedProp.type_DEPRECATED(), scope, k->location);
|
||||
if (errors.empty())
|
||||
exprType = expectedProp.type();
|
||||
exprType = expectedProp.type_DEPRECATED();
|
||||
}
|
||||
else if (expectedTable->indexer && maybeString(expectedTable->indexer->indexType))
|
||||
{
|
||||
|
@ -2428,7 +2426,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
if (expectedTable)
|
||||
{
|
||||
if (auto prop = expectedTable->props.find(key->value.data); prop != expectedTable->props.end())
|
||||
expectedResultType = prop->second.type();
|
||||
expectedResultType = prop->second.type_DEPRECATED();
|
||||
else if (expectedIndexType && maybeString(*expectedIndexType))
|
||||
expectedResultType = expectedIndexResultType;
|
||||
}
|
||||
|
@ -2440,7 +2438,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
if (const TableType* ttv = get<TableType>(follow(expectedOption)))
|
||||
{
|
||||
if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end())
|
||||
expectedResultTypes.push_back(prop->second.type());
|
||||
expectedResultTypes.push_back(prop->second.type_DEPRECATED());
|
||||
else if (ttv->indexer && maybeString(ttv->indexer->indexType))
|
||||
expectedResultTypes.push_back(ttv->indexer->indexResultType);
|
||||
}
|
||||
|
@ -3408,7 +3406,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
const auto& it = lhsTable->props.find(name);
|
||||
if (it != lhsTable->props.end())
|
||||
{
|
||||
return it->second.type();
|
||||
return it->second.type_DEPRECATED();
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && lhsTable->state == TableState::Unsealed) || lhsTable->state == TableState::Free)
|
||||
{
|
||||
|
@ -3449,7 +3447,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
{
|
||||
if (const Property* prop = lookupExternTypeProp(lhsExternType, name))
|
||||
{
|
||||
return prop->type();
|
||||
return prop->type_DEPRECATED();
|
||||
}
|
||||
|
||||
if (auto indexer = lhsExternType->indexer)
|
||||
|
@ -3508,7 +3506,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
{
|
||||
if (const Property* prop = lookupExternTypeProp(exprExternType, value->value.data))
|
||||
{
|
||||
return prop->type();
|
||||
return prop->type_DEPRECATED();
|
||||
}
|
||||
|
||||
if (auto indexer = exprExternType->indexer)
|
||||
|
@ -3618,7 +3616,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
const auto& it = table->props.find(value->value.data);
|
||||
if (it != table->props.end())
|
||||
{
|
||||
propTypes.insert(it->second.type());
|
||||
propTypes.insert(it->second.type_DEPRECATED());
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free)
|
||||
{
|
||||
|
@ -3759,12 +3757,12 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T
|
|||
Name name = indexName->index.value;
|
||||
|
||||
if (ttv->props.count(name))
|
||||
return ttv->props[name].type();
|
||||
return ttv->props[name].type_DEPRECATED();
|
||||
|
||||
Property& property = ttv->props[name];
|
||||
property.setType(freshTy());
|
||||
property.location = indexName->indexLocation;
|
||||
return property.type();
|
||||
return property.type_DEPRECATED();
|
||||
}
|
||||
else if (funName.is<AstExprError>())
|
||||
return errorRecoveryType(scope);
|
||||
|
@ -3831,10 +3829,8 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
|||
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
||||
|
||||
TypePackId retPack;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation)
|
||||
if (expr.returnAnnotation)
|
||||
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
|
||||
else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation_DEPRECATED)
|
||||
retPack = resolveTypePack(funScope, *expr.returnAnnotation_DEPRECATED);
|
||||
else if (isNonstrictMode())
|
||||
retPack = anyTypePack;
|
||||
else if (expectedFunctionType && expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())
|
||||
|
@ -4041,8 +4037,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
|||
// If we're in nonstrict mode we want to only report this missing return
|
||||
// statement if there are type annotations on the function. In strict mode
|
||||
// we report it regardless.
|
||||
if (!isNonstrictMode() ||
|
||||
(FFlag::LuauStoreReturnTypesAsPackOnAst ? function.returnAnnotation != nullptr : function.returnAnnotation_DEPRECATED.has_value()))
|
||||
if (!isNonstrictMode() || function.returnAnnotation != nullptr)
|
||||
{
|
||||
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
||||
}
|
||||
|
@ -5785,8 +5780,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
||||
|
||||
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
||||
TypePackId retTypes = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funcScope, *func->returnTypes)
|
||||
: resolveTypePack(funcScope, func->returnTypes_DEPRECATED);
|
||||
TypePackId retTypes = resolveTypePack(funcScope, *func->returnTypes);
|
||||
|
||||
std::vector<TypeId> genericTys;
|
||||
genericTys.reserve(generics.size());
|
||||
|
|
|
@ -344,7 +344,7 @@ struct TraversalState
|
|||
if (FFlag::LuauSolverV2)
|
||||
maybeType = property.isRead ? prop->readTy : prop->writeTy;
|
||||
else
|
||||
maybeType = prop->type();
|
||||
maybeType = prop->type_DEPRECATED();
|
||||
|
||||
if (maybeType)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -75,7 +76,17 @@ std::optional<Property> findTableProperty(NotNull<BuiltinTypes> builtinTypes, Er
|
|||
{
|
||||
const auto& fit = itt->props.find(name);
|
||||
if (fit != itt->props.end())
|
||||
return fit->second.type();
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
if (fit->second.readTy)
|
||||
return fit->second.readTy;
|
||||
else
|
||||
return fit->second.writeTy;
|
||||
}
|
||||
else
|
||||
return fit->second.type_DEPRECATED();
|
||||
}
|
||||
}
|
||||
else if (const auto& itf = get<FunctionType>(index))
|
||||
{
|
||||
|
@ -124,7 +135,17 @@ std::optional<TypeId> findMetatableEntry(
|
|||
|
||||
auto it = mtt->props.find(entry);
|
||||
if (it != mtt->props.end())
|
||||
return it->second.type();
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
if (it->second.readTy)
|
||||
return it->second.readTy;
|
||||
else
|
||||
return it->second.writeTy;
|
||||
}
|
||||
else
|
||||
return it->second.type_DEPRECATED();
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -168,7 +189,7 @@ std::optional<TypeId> findTablePropertyRespectingMeta(
|
|||
}
|
||||
}
|
||||
else
|
||||
return it->second.type();
|
||||
return it->second.type_DEPRECATED();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,7 +208,20 @@ std::optional<TypeId> findTablePropertyRespectingMeta(
|
|||
{
|
||||
const auto& fit = itt->props.find(name);
|
||||
if (fit != itt->props.end())
|
||||
return fit->second.type();
|
||||
{
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case ValueContext::RValue:
|
||||
return fit->second.readTy;
|
||||
case ValueContext::LValue:
|
||||
return fit->second.writeTy;
|
||||
}
|
||||
}
|
||||
else
|
||||
return fit->second.type_DEPRECATED();
|
||||
}
|
||||
}
|
||||
else if (const auto& itf = get<FunctionType>(index))
|
||||
{
|
||||
|
@ -703,6 +737,34 @@ AstExpr* unwrapGroup(AstExpr* expr)
|
|||
return expr;
|
||||
}
|
||||
|
||||
bool isApproximatelyFalsyType(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
bool seenNil = false;
|
||||
bool seenFalse = false;
|
||||
if (auto ut = get<UnionType>(ty))
|
||||
{
|
||||
for (auto option : ut)
|
||||
{
|
||||
if (auto pt = get<PrimitiveType>(option); pt && pt->type == PrimitiveType::NilType)
|
||||
seenNil = true;
|
||||
else if (auto st = get<SingletonType>(option); st && st->variant == BooleanSingleton{false})
|
||||
seenFalse = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return seenFalse && seenNil;
|
||||
}
|
||||
|
||||
bool isApproximatelyTruthyType(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (auto nt = get<NegationType>(ty))
|
||||
return isApproximatelyFalsyType(nt->ty);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -342,7 +342,7 @@ static std::optional<std::pair<Luau::Name, const SingletonType*>> getTableMatchT
|
|||
{
|
||||
for (auto&& [name, prop] : ttv->props)
|
||||
{
|
||||
if (auto sing = get<SingletonType>(follow(prop.type())))
|
||||
if (auto sing = get<SingletonType>(follow(prop.type_DEPRECATED())))
|
||||
return {{name, sing}};
|
||||
}
|
||||
}
|
||||
|
@ -1938,7 +1938,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
{
|
||||
auto subIter = subTable->props.find(propName);
|
||||
|
||||
if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type()))
|
||||
if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type_DEPRECATED()))
|
||||
missingProperties.push_back(propName);
|
||||
}
|
||||
|
||||
|
@ -1980,7 +1980,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
variance = Invariant;
|
||||
|
||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||
innerState->tryUnify_(r->second.type(), prop.type());
|
||||
innerState->tryUnify_(r->second.type_DEPRECATED(), prop.type_DEPRECATED());
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy);
|
||||
|
||||
|
@ -1997,7 +1997,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
variance = Invariant;
|
||||
|
||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||
innerState->tryUnify_(subTable->indexer->indexResultType, prop.type());
|
||||
innerState->tryUnify_(subTable->indexer->indexResultType, prop.type_DEPRECATED());
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy);
|
||||
|
||||
|
@ -2005,7 +2005,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
log.concat(std::move(innerState->log));
|
||||
failure |= innerState->failure;
|
||||
}
|
||||
else if (subTable->state == TableState::Unsealed && isOptional(prop.type()))
|
||||
else if (subTable->state == TableState::Unsealed && isOptional(prop.type_DEPRECATED()))
|
||||
// This is sound because unsealed table types are precise, so `{ p : T } <: { p : T, q : U? }`
|
||||
// since if `t : { p : T }` then we are guaranteed that `t.q` is `nil`.
|
||||
// TODO: if the supertype is written to, the subtype may no longer be precise (alias analysis?)
|
||||
|
@ -2076,11 +2076,11 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
|
||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||
if (FFlag::LuauFixIndexerSubtypingOrdering)
|
||||
innerState->tryUnify_(prop.type(), superTable->indexer->indexResultType);
|
||||
innerState->tryUnify_(prop.type_DEPRECATED(), superTable->indexer->indexResultType);
|
||||
else
|
||||
{
|
||||
// Incredibly, the old solver depends on this bug somehow.
|
||||
innerState->tryUnify_(superTable->indexer->indexResultType, prop.type());
|
||||
innerState->tryUnify_(superTable->indexer->indexResultType, prop.type_DEPRECATED());
|
||||
}
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy);
|
||||
|
@ -2095,7 +2095,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection,
|
|||
// TODO: file a JIRA
|
||||
// TODO: hopefully readonly/writeonly properties will fix this.
|
||||
Property clone = prop;
|
||||
clone.setType(deeplyOptional(clone.type()));
|
||||
clone.setType(deeplyOptional(clone.type_DEPRECATED()));
|
||||
|
||||
PendingType* pendingSuper = log.queue(superTy);
|
||||
TableType* pendingSuperTtv = getMutable<TableType>(pendingSuper);
|
||||
|
@ -2271,7 +2271,7 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
|
|||
|
||||
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
|
||||
{
|
||||
TypeId ty = it->second.type();
|
||||
TypeId ty = it->second.type_DEPRECATED();
|
||||
std::unique_ptr<Unifier> child = makeChildUnifier();
|
||||
child->tryUnify_(ty, superTy);
|
||||
|
||||
|
@ -2323,7 +2323,7 @@ TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> see
|
|||
result = types->addType(*ttv);
|
||||
TableType* resultTtv = getMutable<TableType>(result);
|
||||
for (auto& [name, prop] : resultTtv->props)
|
||||
prop.setType(deeplyOptional(prop.type(), seen));
|
||||
prop.setType(deeplyOptional(prop.type_DEPRECATED(), seen));
|
||||
return types->addType(UnionType{{builtinTypes->nilType, result}});
|
||||
}
|
||||
else
|
||||
|
@ -2442,7 +2442,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed
|
|||
else
|
||||
{
|
||||
std::unique_ptr<Unifier> innerState = makeChildUnifier();
|
||||
innerState->tryUnify_(classProp->type(), prop.type());
|
||||
innerState->tryUnify_(classProp->type_DEPRECATED(), prop.type_DEPRECATED());
|
||||
|
||||
checkChildUnifierTypeMismatch(innerState->errors, propName, reversed ? subTy : superTy, reversed ? superTy : subTy);
|
||||
|
||||
|
@ -2621,7 +2621,7 @@ static void tryUnifyWithAny(
|
|||
else if (auto table = state.log.getMutable<TableType>(ty))
|
||||
{
|
||||
for (const auto& [_name, prop] : table->props)
|
||||
queue.push_back(prop.type());
|
||||
queue.push_back(prop.type_DEPRECATED());
|
||||
|
||||
if (table->indexer)
|
||||
{
|
||||
|
|
|
@ -20,10 +20,18 @@
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static bool isOptionalOrFree(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
return isOptional(ty) || (get<FreeType>(ty) != nullptr);
|
||||
}
|
||||
|
||||
static bool areCompatible(TypeId left, TypeId right)
|
||||
{
|
||||
auto p = get2<TableType, TableType>(follow(left), follow(right));
|
||||
|
@ -43,14 +51,32 @@ static bool areCompatible(TypeId left, TypeId right)
|
|||
// the right table is free (and therefore potentially has an indexer or
|
||||
// a compatible property)
|
||||
|
||||
LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps || FFlag::LuauRefineTablesWithReadType)
|
||||
{
|
||||
if (rightTable->state == TableState::Free || rightTable->indexer.has_value())
|
||||
return true;
|
||||
|
||||
const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type());
|
||||
if (leftProp.isReadOnly() || leftProp.isShared())
|
||||
{
|
||||
if (isOptionalOrFree(*leftProp.readTy))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isOptional(leftType) || get<FreeType>(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value())
|
||||
return true;
|
||||
// FIXME: Could this create an issue for write only / divergent properties?
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared());
|
||||
|
||||
const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type_DEPRECATED());
|
||||
|
||||
if (isOptional(leftType) || get<FreeType>(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for (const auto& [name, leftProp] : leftTable->props)
|
||||
|
@ -426,13 +452,13 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
|
|||
if (subProp.isReadOnly() && superProp.isReadOnly())
|
||||
result &= unify(*subProp.readTy, *superPropOpt->second.readTy);
|
||||
else if (subProp.isReadOnly())
|
||||
result &= unify(*subProp.readTy, superProp.type());
|
||||
result &= unify(*subProp.readTy, superProp.type_DEPRECATED());
|
||||
else if (superProp.isReadOnly())
|
||||
result &= unify(subProp.type(), *superProp.readTy);
|
||||
result &= unify(subProp.type_DEPRECATED(), *superProp.readTy);
|
||||
else
|
||||
{
|
||||
result &= unify(subProp.type(), superProp.type());
|
||||
result &= unify(superProp.type(), subProp.type());
|
||||
result &= unify(subProp.type_DEPRECATED(), superProp.type_DEPRECATED());
|
||||
result &= unify(superProp.type_DEPRECATED(), subProp.type_DEPRECATED());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -451,24 +451,6 @@ public:
|
|||
const std::optional<Location>& argLocation = std::nullopt
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstExprFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
AstLocal* self,
|
||||
const AstArray<AstLocal*>& args,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
AstStatBlock* body,
|
||||
size_t functionDepth,
|
||||
const AstName& debugname,
|
||||
const std::optional<AstTypeList>& returnAnnotation,
|
||||
AstTypePack* varargAnnotation = nullptr,
|
||||
const std::optional<Location>& argLocation = std::nullopt
|
||||
);
|
||||
|
||||
void visit(AstVisitor* visitor) override;
|
||||
|
||||
bool hasNativeAttribute() const;
|
||||
|
@ -479,8 +461,6 @@ public:
|
|||
AstArray<AstGenericTypePack*> genericPacks;
|
||||
AstLocal* self;
|
||||
AstArray<AstLocal*> args;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::optional<AstTypeList> returnAnnotation_DEPRECATED;
|
||||
AstTypePack* returnAnnotation = nullptr;
|
||||
bool vararg = false;
|
||||
Location varargLocation;
|
||||
|
@ -949,7 +929,6 @@ class AstStatDeclareFunction : public AstStat
|
|||
public:
|
||||
LUAU_RTTI(AstStatDeclareFunction)
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
|
@ -963,7 +942,6 @@ public:
|
|||
AstTypePack* retTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
|
@ -978,36 +956,6 @@ public:
|
|||
AstTypePack* retTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
const Location& nameLocation,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& params,
|
||||
const AstArray<AstArgumentName>& paramNames,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
const AstTypeList& retTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstName& name,
|
||||
const Location& nameLocation,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& params,
|
||||
const AstArray<AstArgumentName>& paramNames,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
const AstTypeList& retTypes
|
||||
);
|
||||
|
||||
|
||||
void visit(AstVisitor* visitor) override;
|
||||
|
||||
bool isCheckedFunction() const;
|
||||
|
@ -1023,8 +971,6 @@ public:
|
|||
bool vararg = false;
|
||||
Location varargLocation;
|
||||
AstTypePack* retTypes;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeList retTypes_DEPRECATED;
|
||||
};
|
||||
|
||||
struct AstDeclaredExternTypeProperty
|
||||
|
@ -1167,27 +1113,6 @@ public:
|
|||
AstTypePack* returnTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& argTypes,
|
||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||
const AstTypeList& returnTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& argTypes,
|
||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||
const AstTypeList& returnTypes
|
||||
);
|
||||
|
||||
void visit(AstVisitor* visitor) override;
|
||||
|
||||
bool isCheckedFunction() const;
|
||||
|
@ -1198,8 +1123,6 @@ public:
|
|||
AstArray<AstGenericTypePack*> genericPacks;
|
||||
AstTypeList argTypes;
|
||||
AstArray<std::optional<AstArgumentName>> argNames;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeList returnTypes_DEPRECATED;
|
||||
AstTypePack* returnTypes;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class CstNode;
|
|||
class ParseError : public std::exception
|
||||
{
|
||||
public:
|
||||
ParseError(const Location& location, const std::string& message);
|
||||
ParseError(const Location& location, std::string message);
|
||||
|
||||
virtual const char* what() const throw();
|
||||
|
||||
|
|
|
@ -183,14 +183,6 @@ private:
|
|||
const Name* localName,
|
||||
const AstArray<AstAttr*>& attributes
|
||||
);
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::pair<AstExprFunction*, AstLocal*> parseFunctionBody_DEPRECATED(
|
||||
bool hasself,
|
||||
const Lexeme& matchFunction,
|
||||
const AstName& debugname,
|
||||
const Name* localName,
|
||||
const AstArray<AstAttr*>& attributes
|
||||
);
|
||||
|
||||
// explist ::= {exp `,'} exp
|
||||
void parseExprList(TempVector<AstExpr*>& result, TempVector<Position>* commaPositions = nullptr);
|
||||
|
@ -233,10 +225,6 @@ private:
|
|||
AstTypePack* parseOptionalReturnType(Position* returnSpecifierPosition = nullptr);
|
||||
AstTypePack* parseReturnType();
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::optional<AstTypeList> parseOptionalReturnType_DEPRECATED(Position* returnSpecifierPosition = nullptr);
|
||||
std::pair<Location, AstTypeList> parseReturnType_DEPRECATED();
|
||||
|
||||
struct TableIndexerResult
|
||||
{
|
||||
AstTableIndexer* node;
|
||||
|
@ -246,8 +234,6 @@ private:
|
|||
};
|
||||
|
||||
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
||||
// Remove with FFlagLuauStoreCSTData2
|
||||
AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
||||
|
||||
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
|
||||
AstType* parseFunctionTypeTail(
|
||||
|
|
163
Ast/src/Ast.cpp
163
Ast/src/Ast.cpp
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include "Luau/Common.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -258,41 +256,6 @@ AstExprFunction::AstExprFunction(
|
|||
, debugname(debugname)
|
||||
, argLocation(argLocation)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
AstExprFunction::AstExprFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
AstLocal* self,
|
||||
const AstArray<AstLocal*>& args,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
AstStatBlock* body,
|
||||
size_t functionDepth,
|
||||
const AstName& debugname,
|
||||
const std::optional<AstTypeList>& returnAnnotation,
|
||||
AstTypePack* varargAnnotation,
|
||||
const std::optional<Location>& argLocation
|
||||
)
|
||||
: AstExpr(ClassIndex(), location)
|
||||
, attributes(attributes)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, self(self)
|
||||
, args(args)
|
||||
, returnAnnotation_DEPRECATED(returnAnnotation)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, varargAnnotation(varargAnnotation)
|
||||
, body(body)
|
||||
, functionDepth(functionDepth)
|
||||
, debugname(debugname)
|
||||
, argLocation(argLocation)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
void AstExprFunction::visit(AstVisitor* visitor)
|
||||
|
@ -308,16 +271,8 @@ void AstExprFunction::visit(AstVisitor* visitor)
|
|||
if (varargAnnotation)
|
||||
varargAnnotation->visit(visitor);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (returnAnnotation)
|
||||
returnAnnotation->visit(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (returnAnnotation_DEPRECATED)
|
||||
visitTypeList(visitor, *returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (returnAnnotation)
|
||||
returnAnnotation->visit(visitor);
|
||||
|
||||
body->visit(visitor);
|
||||
}
|
||||
|
@ -908,7 +863,6 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
|||
, varargLocation(varargLocation)
|
||||
, retTypes(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
AstStatDeclareFunction::AstStatDeclareFunction(
|
||||
|
@ -936,64 +890,6 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
|||
, varargLocation(varargLocation)
|
||||
, retTypes(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction::AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
const Location& nameLocation,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& params,
|
||||
const AstArray<AstArgumentName>& paramNames,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
const AstTypeList& retTypes
|
||||
)
|
||||
: AstStat(ClassIndex(), location)
|
||||
, attributes()
|
||||
, name(name)
|
||||
, nameLocation(nameLocation)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, params(params)
|
||||
, paramNames(paramNames)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, retTypes_DEPRECATED(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction::AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstName& name,
|
||||
const Location& nameLocation,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& params,
|
||||
const AstArray<AstArgumentName>& paramNames,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
const AstTypeList& retTypes
|
||||
)
|
||||
: AstStat(ClassIndex(), location)
|
||||
, attributes(attributes)
|
||||
, name(name)
|
||||
, nameLocation(nameLocation)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, params(params)
|
||||
, paramNames(paramNames)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, retTypes_DEPRECATED(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
||||
|
@ -1001,10 +897,7 @@ void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
|||
if (visitor->visit(this))
|
||||
{
|
||||
visitTypeList(visitor, params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
retTypes->visit(visitor);
|
||||
else
|
||||
visitTypeList(visitor, retTypes_DEPRECATED);
|
||||
retTypes->visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1144,7 +1037,6 @@ AstTypeFunction::AstTypeFunction(
|
|||
, argNames(argNames)
|
||||
, returnTypes(returnTypes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||
}
|
||||
|
||||
|
@ -1165,50 +1057,6 @@ AstTypeFunction::AstTypeFunction(
|
|||
, argNames(argNames)
|
||||
, returnTypes(returnTypes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||
}
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeFunction::AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& argTypes,
|
||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||
const AstTypeList& returnTypes
|
||||
)
|
||||
: AstType(ClassIndex(), location)
|
||||
, attributes()
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, argTypes(argTypes)
|
||||
, argNames(argNames)
|
||||
, returnTypes_DEPRECATED(returnTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||
}
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeFunction::AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& argTypes,
|
||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||
const AstTypeList& returnTypes
|
||||
)
|
||||
: AstType(ClassIndex(), location)
|
||||
, attributes(attributes)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, argTypes(argTypes)
|
||||
, argNames(argNames)
|
||||
, returnTypes_DEPRECATED(returnTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||
}
|
||||
|
||||
|
@ -1217,10 +1065,7 @@ void AstTypeFunction::visit(AstVisitor* visitor)
|
|||
if (visitor->visit(this))
|
||||
{
|
||||
visitTypeList(visitor, argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
returnTypes->visit(visitor);
|
||||
else
|
||||
visitTypeList(visitor, returnTypes_DEPRECATED);
|
||||
returnTypes->visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1335
Ast/src/Parser.cpp
1335
Ast/src/Parser.cpp
File diff suppressed because it is too large
Load diff
|
@ -18,6 +18,7 @@ inline bool isAnalysisFlagExperimental(const char* flag)
|
|||
"LuauTableCloneClonesType3", // requires fixes in lua-apps code, terrifyingly
|
||||
"LuauNormalizationReorderFreeTypeIntersect", // requires fixes in lua-apps code, also terrifyingly
|
||||
"LuauSolverV2",
|
||||
"UseNewLuauTypeSolverDefaultEnabled", // This can change the default solver used in cli applications, so it also needs to be disabled. Will require fixes in lua-apps code
|
||||
// makes sure we always have at least one entry
|
||||
nullptr,
|
||||
};
|
||||
|
|
|
@ -73,7 +73,7 @@ struct CompileOptions
|
|||
class CompileError : public std::exception
|
||||
{
|
||||
public:
|
||||
CompileError(const Location& location, const std::string& message);
|
||||
CompileError(const Location& location, std::string message);
|
||||
|
||||
virtual ~CompileError() throw();
|
||||
|
||||
|
|
|
@ -26,9 +26,9 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
|
|||
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileInlineNonConstInit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileFixTypeFunctionSkip)
|
||||
|
||||
namespace Luau
|
||||
|
@ -45,9 +45,9 @@ static const uint8_t kInvalidReg = 255;
|
|||
|
||||
static const uint32_t kDefaultAllocPc = ~0u;
|
||||
|
||||
CompileError::CompileError(const Location& location, const std::string& message)
|
||||
CompileError::CompileError(const Location& location, std::string message)
|
||||
: location(location)
|
||||
, message(message)
|
||||
, message(std::move(message))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ struct Compiler
|
|||
{
|
||||
f.canInline = true;
|
||||
f.stackSize = stackSize;
|
||||
f.costModel = modelCost(func->body, func->args.data, func->args.size, builtins);
|
||||
f.costModel = modelCost(func->body, func->args.data, func->args.size, builtins, constants);
|
||||
|
||||
// track functions that only ever return a single value so that we can convert multret calls to fixedret calls
|
||||
if (alwaysTerminates(func->body))
|
||||
|
@ -696,7 +696,10 @@ struct Compiler
|
|||
// if the argument is a local that isn't mutated, we will simply reuse the existing register
|
||||
if (int reg = le ? getExprLocalReg(le) : -1; reg >= 0 && (!lv || !lv->written))
|
||||
{
|
||||
args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc});
|
||||
if (FFlag::LuauCompileInlineNonConstInit)
|
||||
args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc, lv ? lv->init : nullptr});
|
||||
else
|
||||
args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -719,9 +722,19 @@ struct Compiler
|
|||
for (InlineArg& arg : args)
|
||||
{
|
||||
if (arg.value.type == Constant::Type_Unknown)
|
||||
{
|
||||
pushLocal(arg.local, arg.reg, arg.allocpc);
|
||||
|
||||
if (FFlag::LuauCompileInlineNonConstInit && arg.init)
|
||||
{
|
||||
if (Variable* lv = variables.find(arg.local))
|
||||
lv->init = arg.init;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
locstants[arg.local] = arg.value;
|
||||
}
|
||||
}
|
||||
|
||||
// the inline frame will be used to compile return statements as well as to reject recursive inlining attempts
|
||||
|
@ -771,6 +784,12 @@ struct Compiler
|
|||
|
||||
if (Constant* var = locstants.find(local))
|
||||
var->type = Constant::Type_Unknown;
|
||||
|
||||
if (FFlag::LuauCompileInlineNonConstInit)
|
||||
{
|
||||
if (Variable* lv = variables.find(local))
|
||||
lv->init = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body);
|
||||
|
@ -3016,7 +3035,7 @@ struct Compiler
|
|||
}
|
||||
|
||||
AstLocal* var = stat->var;
|
||||
uint64_t costModel = modelCost(stat->body, &var, 1, builtins);
|
||||
uint64_t costModel = modelCost(stat->body, &var, 1, builtins, constants);
|
||||
|
||||
// we use a dynamic cost threshold that's based on the fixed limit boosted by the cost advantage we gain due to unrolling
|
||||
bool varc = true;
|
||||
|
@ -4109,6 +4128,8 @@ struct Compiler
|
|||
uint8_t reg;
|
||||
Constant value;
|
||||
uint32_t allocpc;
|
||||
|
||||
AstExpr* init;
|
||||
};
|
||||
|
||||
struct InlineFrame
|
||||
|
@ -4325,54 +4346,27 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
mainFlags |= LPF_NATIVE_FUNCTION;
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
/* generics= */ AstArray<AstGenericType*>(),
|
||||
/* genericPacks= */ AstArray<AstGenericTypePack*>(),
|
||||
/* self= */ nullptr,
|
||||
AstArray<AstLocal*>(),
|
||||
/* vararg= */ true,
|
||||
/* varargLocation= */ Luau::Location(),
|
||||
root,
|
||||
/* functionDepth= */ 0,
|
||||
/* debugname= */ AstName(),
|
||||
/* returnAnnotation= */ nullptr
|
||||
);
|
||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
/* generics= */ AstArray<AstGenericType*>(),
|
||||
/* genericPacks= */ AstArray<AstGenericTypePack*>(),
|
||||
/* self= */ nullptr,
|
||||
AstArray<AstLocal*>(),
|
||||
/* vararg= */ true,
|
||||
/* varargLocation= */ Luau::Location(),
|
||||
root,
|
||||
/* functionDepth= */ 0,
|
||||
/* debugname= */ AstName(),
|
||||
/* returnAnnotation= */ nullptr
|
||||
);
|
||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||
|
||||
const Compiler::Function* mainf = compiler.functions.find(&main);
|
||||
LUAU_ASSERT(mainf && mainf->upvals.empty());
|
||||
const Compiler::Function* mainf = compiler.functions.find(&main);
|
||||
LUAU_ASSERT(mainf && mainf->upvals.empty());
|
||||
|
||||
bytecode.setMainFunction(mainid);
|
||||
bytecode.finalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
/* generics= */ AstArray<AstGenericType*>(),
|
||||
/* genericPacks= */ AstArray<AstGenericTypePack*>(),
|
||||
/* self= */ nullptr,
|
||||
AstArray<AstLocal*>(),
|
||||
/* vararg= */ true,
|
||||
/* varargLocation= */ Luau::Location(),
|
||||
root,
|
||||
/* functionDepth= */ 0,
|
||||
/* debugname= */ AstName(),
|
||||
/* returnAnnotation= */ std::nullopt
|
||||
);
|
||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||
|
||||
const Compiler::Function* mainf = compiler.functions.find(&main);
|
||||
LUAU_ASSERT(mainf && mainf->upvals.empty());
|
||||
|
||||
bytecode.setMainFunction(mainid);
|
||||
bytecode.finalize();
|
||||
}
|
||||
bytecode.setMainFunction(mainid);
|
||||
bytecode.finalize();
|
||||
}
|
||||
|
||||
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions)
|
||||
|
|
|
@ -4,8 +4,12 @@
|
|||
#include "Luau/Common.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
|
||||
#include "ConstantFolding.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileCostModelConstants)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -39,7 +43,7 @@ static uint64_t parallelMulSat(uint64_t a, int b)
|
|||
return r | (s - (s >> 7));
|
||||
}
|
||||
|
||||
inline bool getNumber(AstExpr* node, double& result)
|
||||
inline bool getNumber_DEPRECATED(AstExpr* node, double& result)
|
||||
{
|
||||
// since constant model doesn't use constant folding atm, we perform the basic extraction that's sufficient to handle positive/negative literals
|
||||
if (AstExprConstantNumber* ne = node->as<AstExprConstantNumber>())
|
||||
|
@ -114,18 +118,23 @@ struct Cost
|
|||
struct CostVisitor : AstVisitor
|
||||
{
|
||||
const DenseHashMap<AstExprCall*, int>& builtins;
|
||||
const DenseHashMap<AstExpr*, Constant>& constants;
|
||||
|
||||
DenseHashMap<AstLocal*, uint64_t> vars;
|
||||
Cost result;
|
||||
|
||||
CostVisitor(const DenseHashMap<AstExprCall*, int>& builtins)
|
||||
CostVisitor(const DenseHashMap<AstExprCall*, int>& builtins, const DenseHashMap<AstExpr*, Constant>& constants)
|
||||
: builtins(builtins)
|
||||
, constants(constants)
|
||||
, vars(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Cost model(AstExpr* node)
|
||||
{
|
||||
if (FFlag::LuauCompileCostModelConstants && constants.contains(node))
|
||||
return Cost(0, Cost::kLiteral);
|
||||
|
||||
if (AstExprGroup* expr = node->as<AstExprGroup>())
|
||||
{
|
||||
return model(expr->expr);
|
||||
|
@ -270,8 +279,18 @@ struct CostVisitor : AstVisitor
|
|||
|
||||
int tripCount = -1;
|
||||
double from, to, step = 1;
|
||||
if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step)))
|
||||
tripCount = getTripCount(from, to, step);
|
||||
|
||||
if (FFlag::LuauCompileCostModelConstants)
|
||||
{
|
||||
if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step)))
|
||||
tripCount = getTripCount(from, to, step);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getNumber_DEPRECATED(node->from, from) && getNumber_DEPRECATED(node->to, to) &&
|
||||
(!node->step || getNumber_DEPRECATED(node->step, step)))
|
||||
tripCount = getTripCount(from, to, step);
|
||||
}
|
||||
|
||||
loop(node->body, 1, tripCount < 0 ? 3 : tripCount);
|
||||
return false;
|
||||
|
@ -369,11 +388,31 @@ struct CostVisitor : AstVisitor
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getNumber(AstExpr* node, double& result)
|
||||
{
|
||||
if (const Constant* constant = constants.find(node))
|
||||
{
|
||||
if (constant->type == Constant::Type_Number)
|
||||
{
|
||||
result = constant->valueNumber;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap<AstExprCall*, int>& builtins)
|
||||
uint64_t modelCost(
|
||||
AstNode* root,
|
||||
AstLocal* const* vars,
|
||||
size_t varCount,
|
||||
const DenseHashMap<AstExprCall*, int>& builtins,
|
||||
const DenseHashMap<AstExpr*, Constant>& constants
|
||||
)
|
||||
{
|
||||
CostVisitor visitor{builtins};
|
||||
CostVisitor visitor{builtins, constants};
|
||||
for (size_t i = 0; i < varCount && i < 7; ++i)
|
||||
visitor.vars[vars[i]] = 0xffull << (i * 8 + 8);
|
||||
|
||||
|
@ -382,6 +421,14 @@ uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const
|
|||
return visitor.result.model;
|
||||
}
|
||||
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount)
|
||||
{
|
||||
DenseHashMap<AstExprCall*, int> builtins{nullptr};
|
||||
DenseHashMap<AstExpr*, Constant> constants{nullptr};
|
||||
|
||||
return modelCost(root, vars, varCount, builtins, constants);
|
||||
}
|
||||
|
||||
int computeCost(uint64_t model, const bool* varsConst, size_t varCount)
|
||||
{
|
||||
int cost = int(model & 0x7f);
|
||||
|
|
|
@ -9,8 +9,18 @@ namespace Luau
|
|||
namespace Compile
|
||||
{
|
||||
|
||||
struct Constant;
|
||||
|
||||
// cost model: 8 bytes, where first byte is the baseline cost, and the next 7 bytes are discounts for when variable #i is constant
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap<AstExprCall*, int>& builtins);
|
||||
uint64_t modelCost(
|
||||
AstNode* root,
|
||||
AstLocal* const* vars,
|
||||
size_t varCount,
|
||||
const DenseHashMap<AstExprCall*, int>& builtins,
|
||||
const DenseHashMap<AstExpr*, Constant>& constants
|
||||
);
|
||||
// when additional data is not available, used to test the cost model
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount);
|
||||
|
||||
// cost is computed as B - sum(Di * Ci), where B is baseline cost, Di is the discount for each variable and Ci is 1 when variable #i is constant
|
||||
int computeCost(uint64_t model, const bool* varsConst, size_t varCount);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "Luau/Lexer.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileInlineNonConstInit)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -80,6 +82,17 @@ struct ValueVisitor : AstVisitor
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprFunction* node) override
|
||||
{
|
||||
if (FFlag::LuauCompileInlineNonConstInit)
|
||||
{
|
||||
for (AstLocal* arg : node->args)
|
||||
variables[arg].init = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void assignMutable(DenseHashMap<AstName, Global>& globals, const AstNameTable& names, const char* const* mutableGlobals)
|
||||
|
|
|
@ -12,16 +12,11 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCurrentLineBounds)
|
||||
|
||||
static const char* getfuncname(Closure* f);
|
||||
|
||||
static int currentpc(lua_State* L, CallInfo* ci)
|
||||
{
|
||||
if (FFlag::LuauCurrentLineBounds)
|
||||
return pcRel(ci->savedpc, ci_func(ci)->l.p);
|
||||
else
|
||||
return pcRel_DEPRECATED(ci->savedpc, ci_func(ci)->l.p);
|
||||
return pcRel(ci->savedpc, ci_func(ci)->l.p);
|
||||
}
|
||||
|
||||
static int currentline(lua_State* L, CallInfo* ci)
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include "lstate.h"
|
||||
|
||||
#define pcRel(pc, p) ((pc) && (pc) != (p)->code ? cast_to(int, (pc) - (p)->code) - 1 : 0)
|
||||
// TODO: remove with FFlagLuauCurrentLineBounds
|
||||
#define pcRel_DEPRECATED(pc, p) ((pc) ? cast_to(int, (pc) - (p)->code) - 1 : 0)
|
||||
|
||||
#define luaG_typeerror(L, o, opname) luaG_typeerrorL(L, o, opname)
|
||||
#define luaG_forerror(L, o, what) luaG_forerrorL(L, o, what)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||
LUAU_FASTFLAGVARIABLE(LuauHeapNameDetails)
|
||||
|
||||
static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
||||
|
@ -465,7 +464,7 @@ static void dumpthread(FILE* f, lua_State* th)
|
|||
else if (isLua(ci))
|
||||
{
|
||||
Proto* p = ci_func(ci)->l.p;
|
||||
int pc = FFlag::LuauCurrentLineBounds ? pcRel(ci->savedpc, p) : pcRel_DEPRECATED(ci->savedpc, p);
|
||||
int pc = pcRel(ci->savedpc, p);
|
||||
const LocVar* var = luaF_findlocal(p, int(v - ci->base), pc);
|
||||
|
||||
if (var && var->varname)
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||
|
||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||
#ifdef __clang__
|
||||
#if __has_warning("-Wc99-designator")
|
||||
|
@ -149,53 +147,27 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
|||
L->base = L->ci->base;
|
||||
}
|
||||
|
||||
if (FFlag::LuauCurrentLineBounds)
|
||||
{
|
||||
Closure* cl = clvalue(L->ci->func);
|
||||
Closure* cl = clvalue(L->ci->func);
|
||||
|
||||
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
||||
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
||||
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
||||
const Instruction* oldsavedpc = L->ci->savedpc;
|
||||
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
||||
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
||||
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
||||
const Instruction* oldsavedpc = L->ci->savedpc;
|
||||
|
||||
if (L->ci->savedpc && L->ci->savedpc != cl->l.p->code + cl->l.p->sizecode)
|
||||
L->ci->savedpc++;
|
||||
if (L->ci->savedpc && L->ci->savedpc != cl->l.p->code + cl->l.p->sizecode)
|
||||
L->ci->savedpc++;
|
||||
|
||||
luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size
|
||||
L->ci->top = L->top + LUA_MINSTACK;
|
||||
LUAU_ASSERT(L->ci->top <= L->stack_last);
|
||||
luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size
|
||||
L->ci->top = L->top + LUA_MINSTACK;
|
||||
LUAU_ASSERT(L->ci->top <= L->stack_last);
|
||||
|
||||
lua_Debug ar;
|
||||
ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p));
|
||||
ar.userdata = userdata;
|
||||
lua_Debug ar;
|
||||
ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p));
|
||||
ar.userdata = userdata;
|
||||
|
||||
hook(L, &ar);
|
||||
hook(L, &ar);
|
||||
|
||||
L->ci->savedpc = oldsavedpc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// note: the pc expectations of the hook are matching the general "pc points to next instruction"
|
||||
// however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction
|
||||
// this needs to be called before luaD_checkstack in case it fails to reallocate stack
|
||||
if (L->ci->savedpc)
|
||||
L->ci->savedpc++;
|
||||
|
||||
luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size
|
||||
L->ci->top = L->top + LUA_MINSTACK;
|
||||
LUAU_ASSERT(L->ci->top <= L->stack_last);
|
||||
|
||||
Closure* cl = clvalue(L->ci->func);
|
||||
|
||||
lua_Debug ar;
|
||||
ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p));
|
||||
ar.userdata = userdata;
|
||||
|
||||
hook(L, &ar);
|
||||
|
||||
if (L->ci->savedpc)
|
||||
L->ci->savedpc--;
|
||||
}
|
||||
L->ci->savedpc = oldsavedpc;
|
||||
|
||||
L->ci->top = restorestack(L, ci_top);
|
||||
L->top = restorestack(L, top);
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLoadNoOomThrow)
|
||||
|
||||
template<typename T>
|
||||
struct TempBuffer
|
||||
{
|
||||
|
@ -27,15 +25,6 @@ struct TempBuffer
|
|||
, data(NULL)
|
||||
, count(0)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauLoadNoOomThrow);
|
||||
}
|
||||
|
||||
TempBuffer(lua_State* L, size_t count)
|
||||
: L(L)
|
||||
, data(luaM_newarray(L, count, T, 0))
|
||||
, count(count)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauLoadNoOomThrow);
|
||||
}
|
||||
|
||||
TempBuffer(const TempBuffer&) = delete;
|
||||
|
@ -614,362 +603,8 @@ static int loadsafe(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int luau_load_DEPRECATED(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
uint8_t version = read<uint8_t>(data, size, offset);
|
||||
|
||||
|
||||
// 0 means the rest of the bytecode is the error message
|
||||
if (version == 0)
|
||||
{
|
||||
char chunkbuf[LUA_IDSIZE];
|
||||
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||
lua_pushfstring(L, "%s%.*s", chunkid, int(size - offset), data + offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
||||
{
|
||||
char chunkbuf[LUA_IDSIZE];
|
||||
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||
lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// we will allocate a fair amount of memory so check GC before we do
|
||||
luaC_checkGC(L);
|
||||
|
||||
// pause GC for the duration of deserialization - some objects we're creating aren't rooted
|
||||
const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX};
|
||||
|
||||
// env is 0 for current environment and a stack index otherwise
|
||||
LuaTable* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
||||
|
||||
TString* source = luaS_new(L, chunkname);
|
||||
|
||||
uint8_t typesversion = 0;
|
||||
|
||||
if (version >= 4)
|
||||
{
|
||||
typesversion = read<uint8_t>(data, size, offset);
|
||||
|
||||
if (typesversion < LBC_TYPE_VERSION_MIN || typesversion > LBC_TYPE_VERSION_MAX)
|
||||
{
|
||||
char chunkbuf[LUA_IDSIZE];
|
||||
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
||||
lua_pushfstring(
|
||||
L, "%s: bytecode type version mismatch (expected [%d..%d], got %d)", chunkid, LBC_TYPE_VERSION_MIN, LBC_TYPE_VERSION_MAX, typesversion
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// string table
|
||||
unsigned int stringCount = readVarInt(data, size, offset);
|
||||
TempBuffer<TString*> strings(L, stringCount);
|
||||
|
||||
for (unsigned int i = 0; i < stringCount; ++i)
|
||||
{
|
||||
unsigned int length = readVarInt(data, size, offset);
|
||||
|
||||
strings[i] = luaS_newlstr(L, data + offset, length);
|
||||
offset += length;
|
||||
}
|
||||
|
||||
// userdata type remapping table
|
||||
// for unknown userdata types, the entry will remap to common 'userdata' type
|
||||
const uint32_t userdataTypeLimit = LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE;
|
||||
uint8_t userdataRemapping[userdataTypeLimit];
|
||||
|
||||
if (typesversion == 3)
|
||||
{
|
||||
memset(userdataRemapping, LBC_TYPE_USERDATA, userdataTypeLimit);
|
||||
|
||||
uint8_t index = read<uint8_t>(data, size, offset);
|
||||
|
||||
while (index != 0)
|
||||
{
|
||||
TString* name = readString(strings, data, size, offset);
|
||||
|
||||
if (uint32_t(index - 1) < userdataTypeLimit)
|
||||
{
|
||||
if (auto cb = L->global->ecb.gettypemapping)
|
||||
userdataRemapping[index - 1] = cb(L, getstr(name), name->len);
|
||||
}
|
||||
|
||||
index = read<uint8_t>(data, size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// proto table
|
||||
unsigned int protoCount = readVarInt(data, size, offset);
|
||||
TempBuffer<Proto*> protos(L, protoCount);
|
||||
|
||||
for (unsigned int i = 0; i < protoCount; ++i)
|
||||
{
|
||||
Proto* p = luaF_newproto(L);
|
||||
p->source = source;
|
||||
p->bytecodeid = int(i);
|
||||
|
||||
p->maxstacksize = read<uint8_t>(data, size, offset);
|
||||
p->numparams = read<uint8_t>(data, size, offset);
|
||||
p->nups = read<uint8_t>(data, size, offset);
|
||||
p->is_vararg = read<uint8_t>(data, size, offset);
|
||||
|
||||
if (version >= 4)
|
||||
{
|
||||
p->flags = read<uint8_t>(data, size, offset);
|
||||
|
||||
if (typesversion == 1)
|
||||
{
|
||||
uint32_t typesize = readVarInt(data, size, offset);
|
||||
|
||||
if (typesize)
|
||||
{
|
||||
uint8_t* types = (uint8_t*)data + offset;
|
||||
|
||||
LUAU_ASSERT(typesize == unsigned(2 + p->numparams));
|
||||
LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION);
|
||||
LUAU_ASSERT(types[1] == p->numparams);
|
||||
|
||||
// transform v1 into v2 format
|
||||
int headersize = typesize > 127 ? 4 : 3;
|
||||
|
||||
p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat);
|
||||
p->sizetypeinfo = headersize + typesize;
|
||||
|
||||
if (headersize == 4)
|
||||
{
|
||||
p->typeinfo[0] = (typesize & 127) | (1 << 7);
|
||||
p->typeinfo[1] = typesize >> 7;
|
||||
p->typeinfo[2] = 0;
|
||||
p->typeinfo[3] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->typeinfo[0] = uint8_t(typesize);
|
||||
p->typeinfo[1] = 0;
|
||||
p->typeinfo[2] = 0;
|
||||
}
|
||||
|
||||
memcpy(p->typeinfo + headersize, types, typesize);
|
||||
}
|
||||
|
||||
offset += typesize;
|
||||
}
|
||||
else if (typesversion == 2 || typesversion == 3)
|
||||
{
|
||||
uint32_t typesize = readVarInt(data, size, offset);
|
||||
|
||||
if (typesize)
|
||||
{
|
||||
uint8_t* types = (uint8_t*)data + offset;
|
||||
|
||||
p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat);
|
||||
p->sizetypeinfo = typesize;
|
||||
memcpy(p->typeinfo, types, typesize);
|
||||
offset += typesize;
|
||||
|
||||
if (typesversion == 3)
|
||||
{
|
||||
remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int sizecode = readVarInt(data, size, offset);
|
||||
p->code = luaM_newarray(L, sizecode, Instruction, p->memcat);
|
||||
p->sizecode = sizecode;
|
||||
|
||||
for (int j = 0; j < p->sizecode; ++j)
|
||||
p->code[j] = read<uint32_t>(data, size, offset);
|
||||
|
||||
p->codeentry = p->code;
|
||||
|
||||
const int sizek = readVarInt(data, size, offset);
|
||||
p->k = luaM_newarray(L, sizek, TValue, p->memcat);
|
||||
p->sizek = sizek;
|
||||
|
||||
// Initialize the constants to nil to ensure they have a valid state
|
||||
// in the event that some operation in the following loop fails with
|
||||
// an exception.
|
||||
for (int j = 0; j < p->sizek; ++j)
|
||||
{
|
||||
setnilvalue(&p->k[j]);
|
||||
}
|
||||
|
||||
for (int j = 0; j < p->sizek; ++j)
|
||||
{
|
||||
switch (read<uint8_t>(data, size, offset))
|
||||
{
|
||||
case LBC_CONSTANT_NIL:
|
||||
// All constants have already been pre-initialized to nil
|
||||
break;
|
||||
|
||||
case LBC_CONSTANT_BOOLEAN:
|
||||
{
|
||||
uint8_t v = read<uint8_t>(data, size, offset);
|
||||
setbvalue(&p->k[j], v);
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_NUMBER:
|
||||
{
|
||||
double v = read<double>(data, size, offset);
|
||||
setnvalue(&p->k[j], v);
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_VECTOR:
|
||||
{
|
||||
float x = read<float>(data, size, offset);
|
||||
float y = read<float>(data, size, offset);
|
||||
float z = read<float>(data, size, offset);
|
||||
float w = read<float>(data, size, offset);
|
||||
(void)w;
|
||||
setvvalue(&p->k[j], x, y, z, w);
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_STRING:
|
||||
{
|
||||
TString* v = readString(strings, data, size, offset);
|
||||
setsvalue(L, &p->k[j], v);
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_IMPORT:
|
||||
{
|
||||
uint32_t iid = read<uint32_t>(data, size, offset);
|
||||
resolveImportSafe(L, envt, p->k, iid);
|
||||
setobj(L, &p->k[j], L->top - 1);
|
||||
L->top--;
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_TABLE:
|
||||
{
|
||||
int keys = readVarInt(data, size, offset);
|
||||
LuaTable* h = luaH_new(L, 0, keys);
|
||||
for (int i = 0; i < keys; ++i)
|
||||
{
|
||||
int key = readVarInt(data, size, offset);
|
||||
TValue* val = luaH_set(L, h, &p->k[key]);
|
||||
setnvalue(val, 0.0);
|
||||
}
|
||||
sethvalue(L, &p->k[j], h);
|
||||
break;
|
||||
}
|
||||
|
||||
case LBC_CONSTANT_CLOSURE:
|
||||
{
|
||||
uint32_t fid = readVarInt(data, size, offset);
|
||||
Closure* cl = luaF_newLclosure(L, protos[fid]->nups, envt, protos[fid]);
|
||||
cl->preload = (cl->nupvalues > 0);
|
||||
setclvalue(L, &p->k[j], cl);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LUAU_ASSERT(!"Unexpected constant kind");
|
||||
}
|
||||
}
|
||||
|
||||
const int sizep = readVarInt(data, size, offset);
|
||||
p->p = luaM_newarray(L, sizep, Proto*, p->memcat);
|
||||
p->sizep = sizep;
|
||||
|
||||
for (int j = 0; j < p->sizep; ++j)
|
||||
{
|
||||
uint32_t fid = readVarInt(data, size, offset);
|
||||
p->p[j] = protos[fid];
|
||||
}
|
||||
|
||||
p->linedefined = readVarInt(data, size, offset);
|
||||
p->debugname = readString(strings, data, size, offset);
|
||||
|
||||
uint8_t lineinfo = read<uint8_t>(data, size, offset);
|
||||
|
||||
if (lineinfo)
|
||||
{
|
||||
p->linegaplog2 = read<uint8_t>(data, size, offset);
|
||||
|
||||
int intervals = ((p->sizecode - 1) >> p->linegaplog2) + 1;
|
||||
int absoffset = (p->sizecode + 3) & ~3;
|
||||
|
||||
const int sizelineinfo = absoffset + intervals * sizeof(int);
|
||||
p->lineinfo = luaM_newarray(L, sizelineinfo, uint8_t, p->memcat);
|
||||
p->sizelineinfo = sizelineinfo;
|
||||
|
||||
p->abslineinfo = (int*)(p->lineinfo + absoffset);
|
||||
|
||||
uint8_t lastoffset = 0;
|
||||
for (int j = 0; j < p->sizecode; ++j)
|
||||
{
|
||||
lastoffset += read<uint8_t>(data, size, offset);
|
||||
p->lineinfo[j] = lastoffset;
|
||||
}
|
||||
|
||||
int lastline = 0;
|
||||
for (int j = 0; j < intervals; ++j)
|
||||
{
|
||||
lastline += read<int32_t>(data, size, offset);
|
||||
p->abslineinfo[j] = lastline;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t debuginfo = read<uint8_t>(data, size, offset);
|
||||
|
||||
if (debuginfo)
|
||||
{
|
||||
const int sizelocvars = readVarInt(data, size, offset);
|
||||
p->locvars = luaM_newarray(L, sizelocvars, LocVar, p->memcat);
|
||||
p->sizelocvars = sizelocvars;
|
||||
|
||||
for (int j = 0; j < p->sizelocvars; ++j)
|
||||
{
|
||||
p->locvars[j].varname = readString(strings, data, size, offset);
|
||||
p->locvars[j].startpc = readVarInt(data, size, offset);
|
||||
p->locvars[j].endpc = readVarInt(data, size, offset);
|
||||
p->locvars[j].reg = read<uint8_t>(data, size, offset);
|
||||
}
|
||||
|
||||
const int sizeupvalues = readVarInt(data, size, offset);
|
||||
LUAU_ASSERT(sizeupvalues == p->nups);
|
||||
|
||||
p->upvalues = luaM_newarray(L, sizeupvalues, TString*, p->memcat);
|
||||
p->sizeupvalues = sizeupvalues;
|
||||
|
||||
for (int j = 0; j < p->sizeupvalues; ++j)
|
||||
{
|
||||
p->upvalues[j] = readString(strings, data, size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
protos[i] = p;
|
||||
}
|
||||
|
||||
// "main" proto is pushed to Lua stack
|
||||
uint32_t mainid = readVarInt(data, size, offset);
|
||||
Proto* main = protos[mainid];
|
||||
|
||||
luaC_threadbarrier(L);
|
||||
|
||||
Closure* cl = luaF_newLclosure(L, 0, envt, main);
|
||||
setclvalue(L, L->top, cl);
|
||||
incr_top(L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
||||
{
|
||||
if (!FFlag::LuauLoadNoOomThrow)
|
||||
return luau_load_DEPRECATED(L, chunkname, data, size, env);
|
||||
|
||||
// we will allocate a fair amount of memory so check GC before we do
|
||||
luaC_checkGC(L);
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
|||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
const double kTypecheckTimeoutSec = 4.0;
|
||||
|
||||
std::chrono::milliseconds kInterruptTimeout(10);
|
||||
std::chrono::time_point<std::chrono::system_clock> interruptDeadline;
|
||||
|
||||
|
@ -175,6 +177,19 @@ static void setupFrontend(Luau::Frontend& frontend)
|
|||
};
|
||||
}
|
||||
|
||||
static Luau::FrontendOptions getFrontendOptions()
|
||||
{
|
||||
Luau::FrontendOptions options;
|
||||
|
||||
options.retainFullTypeGraphs = true;
|
||||
options.forAutocomplete = false;
|
||||
options.runLintChecks = kFuzzLinter;
|
||||
|
||||
options.moduleTimeLimitSec = kTypecheckTimeoutSec;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
struct FuzzFileResolver : Luau::FileResolver
|
||||
{
|
||||
std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override
|
||||
|
@ -286,7 +301,7 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message)
|
|||
{
|
||||
static FuzzFileResolver fileResolver;
|
||||
static FuzzConfigResolver configResolver;
|
||||
static Luau::FrontendOptions defaultOptions{/*retainFullTypeGraphs*/ true, /*forAutocomplete*/ false, /*runLintChecks*/ kFuzzLinter};
|
||||
static Luau::FrontendOptions defaultOptions = getFrontendOptions();
|
||||
static Luau::Frontend frontend(&fileResolver, &configResolver, defaultOptions);
|
||||
|
||||
static int once = (setupFrontend(frontend), 0);
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
struct JsonEncoderFixture
|
||||
{
|
||||
Allocator allocator;
|
||||
|
@ -419,39 +417,19 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
|
|||
{
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number): string");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypePackExplicit","location":"0,33 - 0,39","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypePackExplicit","location":"0,33 - 0,39","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
||||
{
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypePackExplicit","location":"0,46 - 0,52","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypePackExplicit","location":"0,46 - 0,52","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr")
|
||||
|
@ -479,18 +457,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
|
|||
|
||||
REQUIRE(2 == root->body.size);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected1 =
|
||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypePackExplicit","location":"3,48 - 3,54","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}},"location":"3,12 - 3,54"}],"indexer":null})";
|
||||
CHECK(toJson(root->body.data[0]) == expected1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected1 =
|
||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}},"location":"3,12 - 3,54"}],"indexer":null})";
|
||||
CHECK(toJson(root->body.data[0]) == expected1);
|
||||
}
|
||||
std::string_view expected1 =
|
||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypePackExplicit","location":"3,48 - 3,54","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}},"location":"3,12 - 3,54"}],"indexer":null})";
|
||||
CHECK(toJson(root->body.data[0]) == expected1);
|
||||
|
||||
std::string_view expected2 =
|
||||
R"({"type":"AstStatDeclareClass","location":"6,22 - 8,11","name":"Bar","superName":"Foo","props":[{"name":"prop2","nameLocation":"7,12 - 7,17","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"7,19 - 7,25","name":"string","nameLocation":"7,19 - 7,25","parameters":[]},"location":"7,12 - 7,25"}],"indexer":null})";
|
||||
|
@ -501,18 +470,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
|||
{
|
||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_type_literal")
|
||||
|
@ -541,18 +501,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction")
|
|||
{
|
||||
AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypePackExplicit","location":"0,44 - 0,46","typeList":{"type":"AstTypeList","types":[]}}},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypePackExplicit","location":"0,44 - 0,46","typeList":{"type":"AstTypeList","types":[]}}},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError")
|
||||
|
|
|
@ -21,7 +21,7 @@ LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys)
|
||||
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -4621,7 +4621,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr")
|
|||
// Somewhat surprisingly, the old solver didn't cover this case.
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauExpectedTypeVisitor, true},
|
||||
{FFlag::LuauImplicitTableIndexerKeys, true},
|
||||
{FFlag::LuauImplicitTableIndexerKeys2, true},
|
||||
};
|
||||
|
||||
check(R"(
|
||||
|
@ -4636,8 +4636,62 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr")
|
|||
|
||||
auto ac = autocomplete('1');
|
||||
CHECK_EQ(ac.entryMap.count("A"), 1);
|
||||
CHECK_EQ(ac.entryMap["A"].kind, AutocompleteEntryKind::String);
|
||||
CHECK_EQ(ac.entryMap.count("B"), 1);
|
||||
CHECK_EQ(ac.entryMap["B"].kind, AutocompleteEntryKind::String);
|
||||
CHECK_EQ(ac.entryMap.count("C"), 1);
|
||||
CHECK_EQ(ac.entryMap["C"].kind, AutocompleteEntryKind::String);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_without_annotation")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauExpectedTypeVisitor, true},
|
||||
{FFlag::LuauImplicitTableIndexerKeys2, false},
|
||||
};
|
||||
|
||||
check(R"(
|
||||
local foo = {
|
||||
["Item/Foo"] = 42,
|
||||
["Item/Bar"] = "it's true",
|
||||
["Item/Baz"] = true,
|
||||
}
|
||||
foo["@1"]
|
||||
)");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
auto checkEntry = [&](auto key, auto type)
|
||||
{
|
||||
REQUIRE_EQ(ac.entryMap.count(key), 1);
|
||||
auto entry = ac.entryMap.at(key);
|
||||
CHECK_EQ(entry.kind, AutocompleteEntryKind::Property);
|
||||
REQUIRE(entry.type);
|
||||
CHECK_EQ(type, toString(*entry.type));
|
||||
};
|
||||
|
||||
checkEntry("Item/Foo", "number");
|
||||
checkEntry("Item/Bar", "string");
|
||||
checkEntry("Item/Baz", "boolean");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauExpectedTypeVisitor, true},
|
||||
};
|
||||
|
||||
check(R"(
|
||||
local function take(_: { choice: "left" | "right" }) end
|
||||
|
||||
take({ choice = "@1" })
|
||||
)");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
CHECK_EQ(ac.entryMap.count("left"), 1);
|
||||
CHECK_EQ(ac.entryMap.count("right"), 1);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Luau
|
|||
std::string rep(const std::string& s, size_t n);
|
||||
}
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileInlineNonConstInit)
|
||||
LUAU_FASTINT(LuauCompileInlineDepth)
|
||||
LUAU_FASTINT(LuauCompileInlineThreshold)
|
||||
LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
||||
|
@ -24,6 +25,7 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
|||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||
LUAU_FASTINT(LuauRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauCompileFixTypeFunctionSkip)
|
||||
LUAU_FASTFLAG(LuauCompileCostModelConstants)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -121,6 +123,20 @@ static std::string compileTypeTable(const char* source)
|
|||
return bcb.dumpTypeInfo();
|
||||
}
|
||||
|
||||
static std::string compileWithRemarks(const char* source)
|
||||
{
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
||||
bcb.setDumpSource(source);
|
||||
|
||||
Luau::CompileOptions options;
|
||||
options.optimizationLevel = 2;
|
||||
|
||||
Luau::compileOrThrow(bcb, source, options);
|
||||
|
||||
return bcb.dumpSourceRemarks();
|
||||
}
|
||||
|
||||
TEST_SUITE_BEGIN("Compiler");
|
||||
|
||||
TEST_CASE("BytecodeIsStable")
|
||||
|
@ -3598,9 +3614,12 @@ RETURN R4 1
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("SourceRemarks")
|
||||
TEST_CASE("CostModelRemarks")
|
||||
{
|
||||
const char* source = R"(
|
||||
ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, true};
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local a, b = ...
|
||||
|
||||
local function foo(x)
|
||||
|
@ -3608,20 +3627,8 @@ local function foo(x)
|
|||
end
|
||||
|
||||
return foo(a) + foo(assert(b))
|
||||
)";
|
||||
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
||||
bcb.setDumpSource(source);
|
||||
|
||||
Luau::CompileOptions options;
|
||||
options.optimizationLevel = 2;
|
||||
|
||||
Luau::compileOrThrow(bcb, source, options);
|
||||
|
||||
std::string remarks = bcb.dumpSourceRemarks();
|
||||
|
||||
CHECK_EQ(remarks, R"(
|
||||
)"),
|
||||
R"(
|
||||
local a, b = ...
|
||||
|
||||
local function foo(x)
|
||||
|
@ -3632,7 +3639,149 @@ end
|
|||
-- remark: builtin assert/1
|
||||
-- remark: inlining succeeded (cost 2, profit 2.50x, depth 0)
|
||||
return foo(a) + foo(assert(b))
|
||||
)");
|
||||
)"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local value = true
|
||||
|
||||
local function foo()
|
||||
return value
|
||||
end
|
||||
|
||||
return foo()
|
||||
)"),
|
||||
R"(
|
||||
local value = true
|
||||
|
||||
local function foo()
|
||||
return value
|
||||
end
|
||||
|
||||
-- remark: inlining succeeded (cost 0, profit 3.00x, depth 0)
|
||||
return foo()
|
||||
)"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local value = true
|
||||
|
||||
local function foo()
|
||||
return not value
|
||||
end
|
||||
|
||||
return foo()
|
||||
)"),
|
||||
R"(
|
||||
local value = true
|
||||
|
||||
local function foo()
|
||||
return not value
|
||||
end
|
||||
|
||||
-- remark: inlining succeeded (cost 0, profit 3.00x, depth 0)
|
||||
return foo()
|
||||
)"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local function foo()
|
||||
local s = 0
|
||||
for i = 1, 100 do s += i end
|
||||
return s
|
||||
end
|
||||
|
||||
return foo()
|
||||
)"),
|
||||
R"(
|
||||
local function foo()
|
||||
local s = 0
|
||||
-- remark: loop unroll failed: too many iterations (100)
|
||||
for i = 1, 100 do s += i end
|
||||
return s
|
||||
end
|
||||
|
||||
-- remark: inlining failed: too expensive (cost 127, profit 1.02x)
|
||||
return foo()
|
||||
)"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local function foo()
|
||||
local s = 0
|
||||
for i = 1, 4 * 25 do s += i end
|
||||
return s
|
||||
end
|
||||
|
||||
return foo()
|
||||
)"),
|
||||
R"(
|
||||
local function foo()
|
||||
local s = 0
|
||||
-- remark: loop unroll failed: too many iterations (100)
|
||||
for i = 1, 4 * 25 do s += i end
|
||||
return s
|
||||
end
|
||||
|
||||
-- remark: inlining failed: too expensive (cost 127, profit 1.02x)
|
||||
return foo()
|
||||
)"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
compileWithRemarks(R"(
|
||||
local x = ...
|
||||
local function test(a)
|
||||
while a < 0 do
|
||||
a += 1
|
||||
end
|
||||
for i=10,1,-1 do
|
||||
a += 1
|
||||
end
|
||||
for i in pairs({}) do
|
||||
a += 1
|
||||
if a % 2 == 0 then continue end
|
||||
end
|
||||
repeat
|
||||
a += 1
|
||||
if a % 2 == 0 then break end
|
||||
until a > 10
|
||||
return a
|
||||
end
|
||||
local a = test(x)
|
||||
local b = test(2)
|
||||
)"),
|
||||
R"(
|
||||
local x = ...
|
||||
local function test(a)
|
||||
while a < 0 do
|
||||
a += 1
|
||||
end
|
||||
-- remark: loop unroll succeeded (iterations 10, cost 10, profit 2.00x)
|
||||
for i=10,1,-1 do
|
||||
a += 1
|
||||
end
|
||||
-- remark: allocation: table hash 0
|
||||
for i in pairs({}) do
|
||||
a += 1
|
||||
if a % 2 == 0 then continue end
|
||||
end
|
||||
repeat
|
||||
a += 1
|
||||
if a % 2 == 0 then break end
|
||||
until a > 10
|
||||
return a
|
||||
end
|
||||
-- remark: inlining failed: too expensive (cost 76, profit 1.03x)
|
||||
local a = test(x)
|
||||
-- remark: inlining failed: too expensive (cost 73, profit 1.08x)
|
||||
local b = test(2)
|
||||
)"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("AssignmentConflict")
|
||||
|
@ -7473,6 +7622,74 @@ RETURN R1 1
|
|||
);
|
||||
}
|
||||
|
||||
TEST_CASE("InlineNonConstInitializers")
|
||||
{
|
||||
ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + compileFunction(
|
||||
R"(
|
||||
local function caller(f)
|
||||
f(1)
|
||||
end
|
||||
|
||||
local function callback(n)
|
||||
print(n + 5)
|
||||
end
|
||||
|
||||
caller(callback)
|
||||
)",
|
||||
2,
|
||||
2
|
||||
),
|
||||
R"(
|
||||
DUPCLOSURE R0 K0 ['caller']
|
||||
DUPCLOSURE R1 K1 ['callback']
|
||||
GETIMPORT R2 3 [print]
|
||||
LOADN R3 6
|
||||
CALL R2 1 0
|
||||
RETURN R0 0
|
||||
)"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("InlineNonConstInitializers2")
|
||||
{
|
||||
ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + compileFunction(
|
||||
R"(
|
||||
local x, y, z = ...
|
||||
local function test(a, b, c, comp)
|
||||
return comp(a, b) and comp(b, c)
|
||||
end
|
||||
|
||||
local function greater(a, b)
|
||||
return a > b
|
||||
end
|
||||
|
||||
test(x, y, z, greater)
|
||||
)",
|
||||
2,
|
||||
2
|
||||
),
|
||||
R"(
|
||||
GETVARARGS R0 3
|
||||
DUPCLOSURE R3 K0 ['test']
|
||||
DUPCLOSURE R4 K1 ['greater']
|
||||
JUMPIFLT R1 R0 L0
|
||||
LOADB R5 0 +1
|
||||
L0: LOADB R5 1
|
||||
L1: JUMPIFNOT R5 L3
|
||||
JUMPIFLT R2 R1 L2
|
||||
LOADB R5 0 +1
|
||||
L2: LOADB R5 1
|
||||
L3: RETURN R0 0
|
||||
)"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("ReturnConsecutive")
|
||||
{
|
||||
// we can return a single local directly
|
||||
|
|
|
@ -37,9 +37,8 @@ void luau_callhook(lua_State* L, lua_Hook hook, void* userdata);
|
|||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||
LUAU_FASTFLAG(LuauCurrentLineBounds)
|
||||
LUAU_FASTFLAG(LuauLoadNoOomThrow)
|
||||
LUAU_FASTFLAG(LuauHeapNameDetails)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauGcAgainstOom)
|
||||
|
||||
static lua_CompileOptions defaultOptions()
|
||||
|
@ -1211,7 +1210,16 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
|||
|
||||
for (const auto& [name, prop] : t->props)
|
||||
{
|
||||
populateRTTI(L, prop.type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
if (prop.readTy)
|
||||
populateRTTI(L, *prop.readTy);
|
||||
else if (prop.writeTy)
|
||||
populateRTTI(L, *prop.writeTy);
|
||||
}
|
||||
else
|
||||
populateRTTI(L, prop.type_DEPRECATED());
|
||||
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -1469,8 +1477,6 @@ TEST_CASE("Debugger")
|
|||
|
||||
TEST_CASE("InterruptInspection")
|
||||
{
|
||||
ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true};
|
||||
|
||||
static bool skipbreak = false;
|
||||
|
||||
runConformance(
|
||||
|
@ -1517,8 +1523,6 @@ TEST_CASE("InterruptInspection")
|
|||
|
||||
TEST_CASE("InterruptErrorInspection")
|
||||
{
|
||||
ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true};
|
||||
|
||||
// for easy access in no-capture lambda
|
||||
static int target = 0;
|
||||
static int step = 0;
|
||||
|
@ -3237,8 +3241,6 @@ TEST_CASE("HugeFunction")
|
|||
|
||||
TEST_CASE("HugeFunctionLoadFailure")
|
||||
{
|
||||
ScopedFastFlag luauLoadNoOomThrow{FFlag::LuauLoadNoOomThrow, true};
|
||||
|
||||
// This test case verifies that if an out-of-memory error occurs inside of
|
||||
// luau_load, we are not left with any GC objects in inconsistent states
|
||||
// that would cause issues during garbage collection.
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileCostModelConstants)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
namespace Luau
|
||||
|
@ -12,7 +14,7 @@ namespace Luau
|
|||
namespace Compile
|
||||
{
|
||||
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap<AstExprCall*, int>& builtins);
|
||||
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount);
|
||||
int computeCost(uint64_t model, const bool* varsConst, size_t varCount);
|
||||
|
||||
} // namespace Compile
|
||||
|
@ -31,7 +33,7 @@ static uint64_t modelFunction(const char* source)
|
|||
AstStatFunction* func = result.root->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(func);
|
||||
|
||||
return Luau::Compile::modelCost(func->func->body, func->func->args.data, func->func->args.size, DenseHashMap<AstExprCall*, int>{nullptr});
|
||||
return Luau::Compile::modelCost(func->func->body, func->func->args.data, func->func->args.size);
|
||||
}
|
||||
|
||||
TEST_CASE("Expression")
|
||||
|
@ -133,6 +135,8 @@ end
|
|||
|
||||
TEST_CASE("ControlFlow")
|
||||
{
|
||||
ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, false};
|
||||
|
||||
uint64_t model = modelFunction(R"(
|
||||
function test(a)
|
||||
while a < 0 do
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
||||
LUAU_FASTINT(LuauTypeCloneIterationLimit);
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTINT(LuauTypeCloneIterationLimit)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
TEST_SUITE_BEGIN("ModuleTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "is_within_comment")
|
||||
|
@ -136,7 +138,8 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table")
|
|||
|
||||
CHECK_EQ(std::optional<std::string>{"Cyclic"}, ttv->syntheticName);
|
||||
|
||||
TypeId methodType = ttv->props["get"].type();
|
||||
|
||||
TypeId methodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ttv->props["get"].readTy : ttv->props["get"].type_DEPRECATED();
|
||||
REQUIRE(methodType != nullptr);
|
||||
|
||||
const FunctionType* ftv = get<FunctionType>(methodType);
|
||||
|
@ -169,7 +172,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2")
|
|||
TableType* ctt = getMutable<TableType>(cloneTy);
|
||||
REQUIRE(ctt);
|
||||
|
||||
TypeId clonedMethodType = ctt->props["get"].type();
|
||||
TypeId clonedMethodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ctt->props["get"].readTy : ctt->props["get"].type_DEPRECATED();
|
||||
REQUIRE(clonedMethodType);
|
||||
|
||||
const FunctionType* cmf = get<FunctionType>(clonedMethodType);
|
||||
|
@ -198,7 +201,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena")
|
|||
TableType* exportsTable = getMutable<TableType>(*exports);
|
||||
REQUIRE(exportsTable != nullptr);
|
||||
|
||||
TypeId signType = exportsTable->props["sign"].type();
|
||||
TypeId signType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED();
|
||||
REQUIRE(signType != nullptr);
|
||||
|
||||
CHECK(!isInArena(signType, module->interfaceTypes));
|
||||
|
@ -352,8 +355,17 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit")
|
|||
for (int i = 0; i < nesting; i++)
|
||||
{
|
||||
TableType* ttv = getMutable<TableType>(nested);
|
||||
ttv->props["a"].setType(src.addType(TableType{}));
|
||||
nested = ttv->props["a"].type();
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
ttv->props["a"].readTy = src.addType(TableType{});
|
||||
ttv->props["a"].writeTy = ttv->props["a"].readTy;
|
||||
nested = *ttv->props["a"].readTy;
|
||||
}
|
||||
else
|
||||
{
|
||||
ttv->props["a"].setType(src.addType(TableType{}));
|
||||
nested = ttv->props["a"].type_DEPRECATED();
|
||||
}
|
||||
}
|
||||
|
||||
TypeArena dest;
|
||||
|
@ -444,7 +456,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports")
|
|||
TypeId typeB = modBiter->second.type;
|
||||
TableType* tableB = getMutable<TableType>(typeB);
|
||||
REQUIRE(tableB);
|
||||
CHECK(typeA == tableB->props["q"].type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
CHECK(typeA == tableB->props["q"].readTy);
|
||||
else
|
||||
CHECK(typeA == tableB->props["q"].type_DEPRECATED());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values")
|
||||
|
@ -478,7 +493,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values")
|
|||
TableType* tableB = getMutable<TableType>(*typeB);
|
||||
REQUIRE_MESSAGE(tableB, "Expected a table, but got " << toString(*typeB));
|
||||
|
||||
CHECK(tableA->props["a"].type() == tableB->props["b"].type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
CHECK(tableA->props["a"].readTy == tableB->props["b"].readTy);
|
||||
CHECK(tableA->props["a"].writeTy == tableB->props["b"].writeTy);
|
||||
}
|
||||
else
|
||||
CHECK(tableA->props["a"].type_DEPRECATED() == tableB->props["b"].type_DEPRECATED());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table")
|
||||
|
|
|
@ -177,7 +177,7 @@ TEST_CASE_FIXTURE(Fixture, "table_props_are_any")
|
|||
REQUIRE(ttv != nullptr);
|
||||
|
||||
REQUIRE(ttv->props.count("foo"));
|
||||
TypeId fooProp = ttv->props["foo"].type();
|
||||
TypeId fooProp = ttv->props["foo"].type_DEPRECATED();
|
||||
REQUIRE(fooProp != nullptr);
|
||||
|
||||
CHECK_EQ(*fooProp, *getBuiltins()->anyType);
|
||||
|
@ -200,9 +200,9 @@ TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any")
|
|||
TableType* ttv = getMutable<TableType>(requireType("T"));
|
||||
REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T")));
|
||||
|
||||
CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type());
|
||||
CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type());
|
||||
CHECK_MESSAGE(get<FunctionType>(follow(ttv->props["three"].type())), "Should be a function: " << *ttv->props["three"].type());
|
||||
CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type_DEPRECATED());
|
||||
CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type_DEPRECATED());
|
||||
CHECK_MESSAGE(get<FunctionType>(follow(ttv->props["three"].type_DEPRECATED())), "Should be a function: " << *ttv->props["three"].type_DEPRECATED());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_iterator_variables_are_any")
|
||||
|
|
|
@ -17,10 +17,8 @@ LUAU_FASTINT(LuauRecursionLimit)
|
|||
LUAU_FASTINT(LuauTypeLengthLimit)
|
||||
LUAU_FASTINT(LuauParseErrorLimit)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauParseStringIndexer)
|
||||
LUAU_FASTFLAG(LuauDeclareExternType)
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
|
||||
|
||||
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
||||
|
@ -152,20 +150,11 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_return_annotations")
|
|||
AstStatFunction* statFunction = block->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(statFunction != nullptr);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
REQUIRE(statFunction->func->returnAnnotation);
|
||||
auto typePack = statFunction->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK_EQ(typePack->typeList.types.size, 1);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(statFunction->func->returnAnnotation_DEPRECATED.has_value());
|
||||
CHECK_EQ(statFunction->func->returnAnnotation_DEPRECATED->types.size, 1);
|
||||
CHECK(statFunction->func->returnAnnotation_DEPRECATED->tailType == nullptr);
|
||||
}
|
||||
REQUIRE(statFunction->func->returnAnnotation);
|
||||
auto typePack = statFunction->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK_EQ(typePack->typeList.types.size, 1);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
|
||||
|
@ -180,28 +169,15 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
|
|||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(statFunc != nullptr);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value());
|
||||
CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
}
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_function_type_and_multiple_returns")
|
||||
|
@ -216,38 +192,20 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_functi
|
|||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(statFunc != nullptr);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 2);
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 2);
|
||||
|
||||
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty0 != nullptr);
|
||||
REQUIRE(ty0->name == "number");
|
||||
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty0 != nullptr);
|
||||
REQUIRE(ty0->name == "number");
|
||||
|
||||
AstTypeReference* ty1 = retTypes.data[1]->as<AstTypeReference>();
|
||||
REQUIRE(ty1 != nullptr);
|
||||
REQUIRE(ty1->name == "string");
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value());
|
||||
CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types;
|
||||
REQUIRE(retTypes.size == 2);
|
||||
|
||||
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty0 != nullptr);
|
||||
REQUIRE(ty0->name == "number");
|
||||
|
||||
AstTypeReference* ty1 = retTypes.data[1]->as<AstTypeReference>();
|
||||
REQUIRE(ty1 != nullptr);
|
||||
REQUIRE(ty1->name == "string");
|
||||
}
|
||||
AstTypeReference* ty1 = retTypes.data[1]->as<AstTypeReference>();
|
||||
REQUIRE(ty1 != nullptr);
|
||||
REQUIRE(ty1->name == "string");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_annotation_with_no_args")
|
||||
|
@ -262,45 +220,25 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_a
|
|||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(statFunc != nullptr);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
REQUIRE(statFunc->func->returnAnnotation);
|
||||
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(typePack);
|
||||
CHECK(typePack->typeList.tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = typePack->typeList.types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
REQUIRE(funTy->argTypes.types.size == 0);
|
||||
CHECK(funTy->argTypes.tailType == nullptr);
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
REQUIRE(funTy->argTypes.types.size == 0);
|
||||
CHECK(funTy->argTypes.tailType == nullptr);
|
||||
|
||||
auto funReturnPack = funTy->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(funReturnPack);
|
||||
CHECK(funReturnPack->typeList.tailType == nullptr);
|
||||
auto funReturnPack = funTy->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(funReturnPack);
|
||||
CHECK(funReturnPack->typeList.tailType == nullptr);
|
||||
|
||||
AstTypeReference* ty = funReturnPack->typeList.types.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty != nullptr);
|
||||
REQUIRE(ty->name == "nil");
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value());
|
||||
CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr);
|
||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types;
|
||||
REQUIRE(retTypes.size == 1);
|
||||
|
||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funTy != nullptr);
|
||||
REQUIRE(funTy->argTypes.types.size == 0);
|
||||
CHECK(funTy->argTypes.tailType == nullptr);
|
||||
CHECK(funTy->returnTypes_DEPRECATED.tailType == nullptr);
|
||||
|
||||
AstTypeReference* ty = funTy->returnTypes_DEPRECATED.types.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty != nullptr);
|
||||
REQUIRE(ty->name == "nil");
|
||||
}
|
||||
AstTypeReference* ty = funReturnPack->typeList.types.data[0]->as<AstTypeReference>();
|
||||
REQUIRE(ty != nullptr);
|
||||
REQUIRE(ty->name == "nil");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "annotations_can_be_tables")
|
||||
|
@ -442,17 +380,9 @@ TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_
|
|||
AstTypeFunction* annotation = local->vars.data[0]->annotation->as<AstTypeFunction>();
|
||||
REQUIRE(annotation != nullptr);
|
||||
|
||||
AstTypeIntersection* returnAnnotation;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto returnTypePack = annotation->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
returnAnnotation = returnTypePack->typeList.types.data[0]->as<AstTypeIntersection>();
|
||||
}
|
||||
else
|
||||
{
|
||||
returnAnnotation = annotation->returnTypes_DEPRECATED.types.data[0]->as<AstTypeIntersection>();
|
||||
}
|
||||
auto returnTypePack = annotation->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
AstTypeIntersection* returnAnnotation = returnTypePack->typeList.types.data[0]->as<AstTypeIntersection>();
|
||||
REQUIRE(returnAnnotation != nullptr);
|
||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
|
||||
CHECK(returnAnnotation->types.data[1]->as<AstTypeFunction>());
|
||||
|
@ -2000,16 +1930,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations")
|
|||
CHECK(func->name == "bar");
|
||||
CHECK(func->nameLocation == Location({2, 25}, {2, 28}));
|
||||
REQUIRE_EQ(func->params.types.size, 1);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto retTypePack = func->retTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(retTypePack);
|
||||
REQUIRE_EQ(retTypePack->typeList.types.size, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE_EQ(func->retTypes_DEPRECATED.types.size, 1);
|
||||
}
|
||||
|
||||
auto retTypePack = func->retTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(retTypePack);
|
||||
REQUIRE_EQ(retTypePack->typeList.types.size, 1);
|
||||
|
||||
AstStatDeclareFunction* varFunc = stat->body.data[2]->as<AstStatDeclareFunction>();
|
||||
REQUIRE(varFunc);
|
||||
|
@ -2392,15 +2316,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_variadics")
|
|||
REQUIRE(fnFoo);
|
||||
CHECK_EQ(fnFoo->argTypes.types.size, 2);
|
||||
CHECK(fnFoo->argTypes.tailType);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
CHECK(fnFoo->returnTypes->is<AstTypePackVariadic>());
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(fnFoo->returnTypes_DEPRECATED.types.size, 0);
|
||||
CHECK(fnFoo->returnTypes_DEPRECATED.tailType);
|
||||
}
|
||||
CHECK(fnFoo->returnTypes->is<AstTypePackVariadic>());
|
||||
|
||||
AstStatTypeAlias* bar = stat->body.data[2]->as<AstStatTypeAlias>();
|
||||
REQUIRE(bar);
|
||||
|
@ -2408,18 +2324,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_variadics")
|
|||
REQUIRE(fnBar);
|
||||
CHECK_EQ(fnBar->argTypes.types.size, 0);
|
||||
CHECK(!fnBar->argTypes.tailType);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto returnTypePack = fnBar->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
CHECK_EQ(returnTypePack->typeList.types.size, 1);
|
||||
CHECK(returnTypePack->typeList.tailType);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(fnBar->returnTypes_DEPRECATED.types.size, 1);
|
||||
CHECK(fnBar->returnTypes_DEPRECATED.tailType);
|
||||
}
|
||||
auto returnTypePack = fnBar->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
CHECK_EQ(returnTypePack->typeList.types.size, 1);
|
||||
CHECK(returnTypePack->typeList.tailType);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "variadics_must_be_last")
|
||||
|
@ -2485,8 +2393,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
|
|||
REQUIRE(argAnnot != nullptr);
|
||||
CHECK(argAnnot->genericName == "a");
|
||||
|
||||
AstTypePackGeneric* retAnnot = FFlag::LuauStoreReturnTypesAsPackOnAst ? fnTy->returnTypes->as<AstTypePackGeneric>()
|
||||
: fnTy->returnTypes_DEPRECATED.tailType->as<AstTypePackGeneric>();
|
||||
AstTypePackGeneric* retAnnot = fnTy->returnTypes->as<AstTypePackGeneric>();
|
||||
REQUIRE(retAnnot != nullptr);
|
||||
CHECK(retAnnot->genericName == "b");
|
||||
}
|
||||
|
@ -2571,9 +2478,7 @@ TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments")
|
|||
REQUIRE_EQ(func->argNames.size, 3);
|
||||
REQUIRE(func->argNames.data[2]);
|
||||
CHECK_EQ(func->argNames.data[2]->first, "c");
|
||||
AstTypeFunction* funcRet = FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||
? func->returnTypes->as<AstTypePackExplicit>()->typeList.types.data[0]->as<AstTypeFunction>()
|
||||
: func->returnTypes_DEPRECATED.types.data[0]->as<AstTypeFunction>();
|
||||
AstTypeFunction* funcRet = func->returnTypes->as<AstTypePackExplicit>()->typeList.types.data[0]->as<AstTypeFunction>();
|
||||
REQUIRE(funcRet != nullptr);
|
||||
REQUIRE_EQ(funcRet->argTypes.types.size, 3);
|
||||
REQUIRE_EQ(funcRet->argNames.size, 3);
|
||||
|
@ -2817,20 +2722,11 @@ TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
|
|||
auto funcType = alias1->type->as<AstTypeFunction>();
|
||||
REQUIRE(funcType);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto returnTypePack = funcType->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
REQUIRE_EQ(1, returnTypePack->typeList.types.size);
|
||||
REQUIRE(!returnTypePack->typeList.tailType);
|
||||
CHECK(returnTypePack->typeList.types.data[0]->is<AstTypeGroup>());
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE_EQ(1, funcType->returnTypes_DEPRECATED.types.size);
|
||||
REQUIRE(!funcType->returnTypes_DEPRECATED.tailType);
|
||||
CHECK(funcType->returnTypes_DEPRECATED.types.data[0]->is<AstTypeGroup>());
|
||||
}
|
||||
auto returnTypePack = funcType->returnTypes->as<AstTypePackExplicit>();
|
||||
REQUIRE(returnTypePack);
|
||||
REQUIRE_EQ(1, returnTypePack->typeList.types.size);
|
||||
REQUIRE(!returnTypePack->typeList.tailType);
|
||||
CHECK(returnTypePack->typeList.types.data[0]->is<AstTypeGroup>());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "inner_and_outer_scope_of_functions_have_correct_end_position")
|
||||
|
@ -2899,8 +2795,6 @@ TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
|
||||
ParseOptions parseOptions;
|
||||
parseOptions.storeCstData = true;
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
namespace
|
||||
|
@ -177,24 +175,13 @@ TEST_CASE_FIXTURE(RequireTracerFixture, "follow_typeof_in_return_type")
|
|||
AstStatFunction* func = block->body.data[0]->as<AstStatFunction>();
|
||||
REQUIRE(func != nullptr);
|
||||
|
||||
AstTypeTypeof* typeofAnnotation;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
AstTypePack* retAnnotation = func->func->returnAnnotation;
|
||||
REQUIRE(retAnnotation);
|
||||
AstTypePack* retAnnotation = func->func->returnAnnotation;
|
||||
REQUIRE(retAnnotation);
|
||||
|
||||
AstTypePackExplicit* tp = retAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(tp);
|
||||
REQUIRE_EQ(tp->typeList.types.size, 1);
|
||||
typeofAnnotation = tp->typeList.types.data[0]->as<AstTypeTypeof>();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::optional<AstTypeList> retAnnotation = func->func->returnAnnotation_DEPRECATED;
|
||||
REQUIRE(retAnnotation);
|
||||
REQUIRE_EQ(retAnnotation->types.size, 1);
|
||||
typeofAnnotation = retAnnotation->types.data[0]->as<AstTypeTypeof>();
|
||||
}
|
||||
AstTypePackExplicit* tp = retAnnotation->as<AstTypePackExplicit>();
|
||||
REQUIRE(tp);
|
||||
REQUIRE_EQ(tp->typeList.types.size, 1);
|
||||
AstTypeTypeof* typeofAnnotation = tp->typeList.types.data[0]->as<AstTypeTypeof>();
|
||||
REQUIRE(typeofAnnotation != nullptr);
|
||||
|
||||
AstExprIndexName* indexName = typeofAnnotation->expr->as<AstExprIndexName>();
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions)
|
||||
LUAU_FASTFLAG(LuauCSTForReturnTypeFunctionTail)
|
||||
|
||||
|
@ -49,7 +47,6 @@ TEST_CASE("string_literals_containing_utf8")
|
|||
|
||||
TEST_CASE("if_stmt_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( if This then Once() end)";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -98,31 +95,15 @@ TEST_CASE("elseif_chains_indent_sensibly")
|
|||
TEST_CASE("strips_type_annotations")
|
||||
{
|
||||
const std::string code = R"( local s: string= 'hello there' )";
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
{
|
||||
const std::string expected = R"( local s = 'hello there' )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"( local s = 'hello there' )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
const std::string expected = R"( local s = 'hello there' )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("strips_type_assertion_expressions")
|
||||
{
|
||||
const std::string code = R"( local s= some_function() :: any+ something_else() :: number )";
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
{
|
||||
const std::string expected = R"( local s= some_function() + something_else() )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"( local s= some_function() + something_else() )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
const std::string expected = R"( local s= some_function() + something_else() )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_taking_ellipsis")
|
||||
|
@ -149,7 +130,6 @@ TEST_CASE("for_loop")
|
|||
|
||||
TEST_CASE("for_loop_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( for index = 1, 10 do call(index) end )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -174,7 +154,6 @@ TEST_CASE("for_in_loop")
|
|||
|
||||
TEST_CASE("for_in_loop_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( for k, v in ipairs(x) do end )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -193,7 +172,6 @@ TEST_CASE("for_in_loop_spaces_around_tokens")
|
|||
|
||||
TEST_CASE("for_in_single_variable")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( for key in pairs(x) do end )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
}
|
||||
|
@ -206,7 +184,6 @@ TEST_CASE("while_loop")
|
|||
|
||||
TEST_CASE("while_loop_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( while f(x) do print() end )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -228,7 +205,6 @@ TEST_CASE("repeat_until_loop")
|
|||
|
||||
TEST_CASE("repeat_until_loop_condition_on_new_line")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
repeat
|
||||
print()
|
||||
|
@ -260,7 +236,6 @@ TEST_CASE("local_assignment")
|
|||
|
||||
TEST_CASE("local_assignment_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( local x = 1 )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -294,7 +269,6 @@ TEST_CASE("local_function")
|
|||
|
||||
TEST_CASE("local_function_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( local function p(o, m, ...) end )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -313,7 +287,6 @@ TEST_CASE("function")
|
|||
|
||||
TEST_CASE("function_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string two = R"( function p(o, m, ...) end )";
|
||||
CHECK_EQ(two, transpile(two).code);
|
||||
|
||||
|
@ -342,8 +315,6 @@ TEST_CASE("function_spaces_around_tokens")
|
|||
TEST_CASE("function_with_types_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||
|
@ -403,7 +374,6 @@ TEST_CASE("function_with_types_spaces_around_tokens")
|
|||
|
||||
TEST_CASE("returns_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string one = R"( return 1 )";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -416,7 +386,6 @@ TEST_CASE("returns_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type Foo = string )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -465,7 +434,6 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type Foo<X = string, Z... = ...any> = string )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -526,7 +494,6 @@ TEST_CASE("table_literal_closing_brace_at_correct_position")
|
|||
|
||||
TEST_CASE("table_literal_with_semicolon_separators")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
local t = { x = 1; y = 2 }
|
||||
)";
|
||||
|
@ -536,7 +503,6 @@ TEST_CASE("table_literal_with_semicolon_separators")
|
|||
|
||||
TEST_CASE("table_literal_with_trailing_separators")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
local t = { x = 1, y = 2, }
|
||||
)";
|
||||
|
@ -546,7 +512,6 @@ TEST_CASE("table_literal_with_trailing_separators")
|
|||
|
||||
TEST_CASE("table_literal_with_spaces_around_separator")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
local t = { x = 1 , y = 2 }
|
||||
)";
|
||||
|
@ -556,7 +521,6 @@ TEST_CASE("table_literal_with_spaces_around_separator")
|
|||
|
||||
TEST_CASE("table_literal_with_spaces_around_equals")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
local t = { x = 1 }
|
||||
)";
|
||||
|
@ -566,7 +530,6 @@ TEST_CASE("table_literal_with_spaces_around_equals")
|
|||
|
||||
TEST_CASE("table_literal_multiline_with_indexers")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
local t = {
|
||||
["my first value"] = "x";
|
||||
|
@ -591,18 +554,8 @@ TEST_CASE("method_definitions")
|
|||
|
||||
TEST_CASE("spaces_between_keywords_even_if_it_pushes_the_line_estimation_off")
|
||||
{
|
||||
// Luau::Parser doesn't exactly preserve the string representation of numbers in Lua, so we can find ourselves
|
||||
// falling out of sync with the original code. We need to push keywords out so that there's at least one space between them.
|
||||
const std::string code = R"( if math.abs(raySlope) < .01 then return 0 end )";
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
{
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"( if math.abs(raySlope) < 0.01 then return 0 end)";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("numbers")
|
||||
|
@ -614,34 +567,23 @@ TEST_CASE("numbers")
|
|||
TEST_CASE("infinity")
|
||||
{
|
||||
const std::string code = R"( local a = 1e500 local b = 1e400 )";
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
{
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"( local a = 1e500 local b = 1e500 )";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("numbers_with_separators")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = 123_456_789 )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("hexadecimal_numbers")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = 0xFFFF )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("binary_numbers")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = 0b0101 )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
@ -654,28 +596,24 @@ TEST_CASE("single_quoted_strings")
|
|||
|
||||
TEST_CASE("double_quoted_strings")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = "hello world" )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("simple_interp_string")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = `hello world` )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("raw_strings")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = [[ hello world ]] )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("raw_strings_with_blocks")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local a = [==[ hello world ]==] )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
@ -694,7 +632,6 @@ TEST_CASE("escaped_strings_2")
|
|||
|
||||
TEST_CASE("escaped_strings_newline")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
print("foo \
|
||||
bar")
|
||||
|
@ -704,14 +641,12 @@ TEST_CASE("escaped_strings_newline")
|
|||
|
||||
TEST_CASE("escaped_strings_raw")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( local x = [=[\v<((do|load)file|require)\s*\(?['"]\zs[^'"]+\ze['"]]=] )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("position_correctly_updated_when_writing_multiline_string")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
call([[
|
||||
testing
|
||||
|
@ -757,56 +692,48 @@ TEST_CASE("function_call_parentheses_multiple_args_no_space")
|
|||
|
||||
TEST_CASE("function_call_parentheses_multiple_args_space_before_commas")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call(arg1 ,arg3 ,arg3) )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_spaces_before_parentheses")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call () )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_spaces_within_parentheses")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call( ) )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_string_double_quotes")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call "string" )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_string_single_quotes")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call 'string' )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_string_no_space")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call'string' )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_table_literal")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call { x = 1 } )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE("function_call_table_literal_no_space")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( call{x=1} )";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
@ -851,7 +778,6 @@ TEST_CASE("emit_a_do_block_in_cases_of_potentially_ambiguous_syntax")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "parentheses_multiline")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
local test = (
|
||||
x
|
||||
|
@ -863,9 +789,6 @@ local test = (
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "stmt_semicolon")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"( local test = 1; )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -875,7 +798,6 @@ TEST_CASE_FIXTURE(Fixture, "stmt_semicolon")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
do
|
||||
return;
|
||||
|
@ -886,9 +808,6 @@ TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
if init then
|
||||
x = string.sub(x, utf8.offset(x, init));
|
||||
|
@ -899,9 +818,6 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
if (t < 1) then return c/2*t*t + b end;
|
||||
)";
|
||||
|
@ -910,9 +826,6 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
for i,v in ... do
|
||||
end;
|
||||
|
@ -922,9 +835,6 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "while_do_semicolon")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
while true do
|
||||
end;
|
||||
|
@ -934,9 +844,6 @@ TEST_CASE_FIXTURE(Fixture, "while_do_semicolon")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_definition_semicolon")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
function foo()
|
||||
end;
|
||||
|
@ -1014,16 +921,7 @@ TEST_CASE("a_table_key_can_be_the_empty_string")
|
|||
TEST_CASE("always_emit_a_space_after_local_keyword")
|
||||
{
|
||||
std::string code = "do local aZZZZ = Workspace.P1.Shape local bZZZZ = Enum.PartType.Cylinder end";
|
||||
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
{
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string expected = "do local aZZZZ = Workspace.P1 .Shape local bZZZZ= Enum.PartType.Cylinder end";
|
||||
CHECK_EQ(expected, transpile(code).code);
|
||||
}
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "types_should_not_be_considered_cyclic_if_they_are_not_recursive")
|
||||
|
@ -1060,23 +958,13 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly")
|
|||
end
|
||||
)";
|
||||
|
||||
std::string expected = FFlag::LuauStoreCSTData2 ? R"(
|
||||
std::string expected = R"(
|
||||
local a:(a:string,b:number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
|
||||
end
|
||||
|
||||
local b:(...string)->(...number)=function(...:string): ...number
|
||||
end
|
||||
|
||||
local c:()->()=function(): ()
|
||||
end
|
||||
)"
|
||||
: R"(
|
||||
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
|
||||
end
|
||||
|
||||
local b:(...string)->(...number)=function(...:string): ...number
|
||||
end
|
||||
|
||||
local c:()->()=function(): ()
|
||||
end
|
||||
)";
|
||||
|
@ -1116,7 +1004,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_assertion_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = "local a = 5 :: number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1133,7 +1020,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = "local a = if 1 then 2 elseif 3 then 4 else 5";
|
||||
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
|
@ -1141,7 +1027,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
local x = if yes
|
||||
then nil
|
||||
|
@ -1157,7 +1042,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = "local a = if 1 then 2 else 3";
|
||||
CHECK_EQ(code, transpile(code).code);
|
||||
|
||||
|
@ -1194,7 +1078,6 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_between_else_if")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
return
|
||||
if a then "was a" else
|
||||
|
@ -1222,7 +1105,6 @@ local a: Import.Type
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( local _: Foo.Type )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1251,7 +1133,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens")
|
|||
TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( local _: Type )";
|
||||
|
@ -1273,7 +1154,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens")
|
|||
TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( for i: number = 1, 10 do end )";
|
||||
|
@ -1314,7 +1194,6 @@ local b: Packed<(number, string)>
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_packs_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type _ = Packed< T...> )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1372,11 +1251,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2")
|
|||
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
|
||||
{
|
||||
std::string code = "local a: nil | (string & number)";
|
||||
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
else
|
||||
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested")
|
||||
|
@ -1395,10 +1270,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = "type FnB<U...> = () -> U... & T";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1406,9 +1277,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: | string | number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1418,9 +1286,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: string | number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1430,9 +1295,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: & string & number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1442,9 +1304,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: string & number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1454,9 +1313,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: string | (Foo & Bar)";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1481,9 +1337,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = "local a: string | nil";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1515,7 +1368,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_varargs")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string one = "local _ = a.name";
|
||||
CHECK_EQ(one, transpile(one, {}, true).code);
|
||||
|
||||
|
@ -1528,7 +1380,6 @@ TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "index_name_ends_with_digit")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = "sparkles.Color = Color3.new()";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
@ -1542,7 +1393,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_index_expr")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "index_expr_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string one = "local _ = a[2]";
|
||||
CHECK_EQ(one, transpile(one, {}, true).code);
|
||||
|
||||
|
@ -1586,7 +1436,6 @@ local _ = # e
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "binary_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
local _ = 1+1
|
||||
local _ = 1 +1
|
||||
|
@ -1628,7 +1477,6 @@ a ..= ' - result'
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "compound_assignment_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string one = R"( a += 1 )";
|
||||
CHECK_EQ(one, transpile(one, {}, true).code);
|
||||
|
||||
|
@ -1645,7 +1493,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_assign_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string one = "a = 1";
|
||||
CHECK_EQ(one, transpile(one).code);
|
||||
|
||||
|
@ -1681,11 +1528,7 @@ local f: <T,S...>(T, S...)->(number) = foo
|
|||
TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse")
|
||||
{
|
||||
std::string code = "local a: nil | number";
|
||||
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
else
|
||||
CHECK_EQ("local a: number?", transpile(code, {}, true).code);
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple")
|
||||
|
@ -1800,9 +1643,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple_types")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"( local _ = `hello {name}` )";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1810,9 +1650,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"( local _ = `hello {
|
||||
name
|
||||
}!` )";
|
||||
|
@ -1822,9 +1659,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"(
|
||||
error(
|
||||
`a {b} c`
|
||||
|
@ -1836,7 +1670,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( local _ = `hello \
|
||||
world!` )";
|
||||
|
||||
|
@ -1845,9 +1678,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )";
|
||||
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1862,7 +1692,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type function foo() end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1878,7 +1707,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_typeof_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type X = typeof(x) )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1903,14 +1731,12 @@ TEST_CASE("transpile_single_quoted_string_types")
|
|||
|
||||
TEST_CASE("transpile_double_quoted_string_types")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( type a = "hello world" )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE("transpile_raw_string_types")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type a = [[ hello world ]] )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -1920,14 +1746,12 @@ TEST_CASE("transpile_raw_string_types")
|
|||
|
||||
TEST_CASE("transpile_escaped_string_types")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"( type a = "\\b\\t\\n\\\\" )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE("transpile_type_table_semicolon_separators")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
const std::string code = R"(
|
||||
type Foo = {
|
||||
bar: number;
|
||||
|
@ -1939,7 +1763,6 @@ TEST_CASE("transpile_type_table_semicolon_separators")
|
|||
|
||||
TEST_CASE("transpile_type_table_access_modifiers")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
type Foo = {
|
||||
read bar: number,
|
||||
|
@ -1960,7 +1783,6 @@ TEST_CASE("transpile_type_table_access_modifiers")
|
|||
|
||||
TEST_CASE("transpile_type_table_spaces_between_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"( type Foo = { bar: number, } )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2003,7 +1825,6 @@ TEST_CASE("transpile_type_table_spaces_between_tokens")
|
|||
|
||||
TEST_CASE("transpile_type_table_preserve_original_indexer_style")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
type Foo = {
|
||||
[number]: string
|
||||
|
@ -2019,7 +1840,6 @@ TEST_CASE("transpile_type_table_preserve_original_indexer_style")
|
|||
|
||||
TEST_CASE("transpile_type_table_preserve_indexer_location")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
type Foo = {
|
||||
[number]: string,
|
||||
|
@ -2048,7 +1868,6 @@ TEST_CASE("transpile_type_table_preserve_indexer_location")
|
|||
|
||||
TEST_CASE("transpile_type_table_preserve_property_definition_style")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
type Foo = {
|
||||
["$$typeof1"]: string,
|
||||
|
@ -2060,7 +1879,6 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style")
|
|||
|
||||
TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
type Foo = {
|
||||
[ "$$typeof1"]: string,
|
||||
|
@ -2072,10 +1890,6 @@ TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens")
|
|||
|
||||
TEST_CASE("transpile_types_preserve_parentheses_style")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
};
|
||||
|
||||
std::string code = R"( type Foo = number )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2113,10 +1927,6 @@ end
|
|||
|
||||
TEST_CASE("transpile_type_function_unnamed_arguments")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = R"( type Foo = () -> () )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2150,10 +1960,6 @@ TEST_CASE("transpile_type_function_unnamed_arguments")
|
|||
|
||||
TEST_CASE("transpile_type_function_named_arguments")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = R"( type Foo = (x: string) -> () )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2181,10 +1987,6 @@ TEST_CASE("transpile_type_function_named_arguments")
|
|||
|
||||
TEST_CASE("transpile_type_function_generics")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = R"( type Foo = <X, Y, Z...>() -> () )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2218,10 +2020,6 @@ TEST_CASE("transpile_type_function_generics")
|
|||
|
||||
TEST_CASE("transpile_type_function_return_types")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = R"( type Foo = () -> () )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
|
@ -2268,8 +2066,6 @@ TEST_CASE("transpile_type_function_return_types")
|
|||
TEST_CASE("transpile_chained_function_types")
|
||||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
{FFlag::LuauCSTForReturnTypeFunctionTail, true},
|
||||
};
|
||||
std::string code = R"( type Foo = () -> () -> () )";
|
||||
|
@ -2290,7 +2086,6 @@ TEST_CASE("fuzzer_nil_optional")
|
|||
|
||||
TEST_CASE("transpile_function_attributes")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
||||
std::string code = R"(
|
||||
@native
|
||||
function foo()
|
||||
|
|
|
@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauEagerGeneralization4)
|
|||
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
|
||||
LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs)
|
||||
LUAU_FASTFLAG(LuauEmptyStringInKeyOf)
|
||||
|
||||
struct TypeFunctionFixture : Fixture
|
||||
{
|
||||
|
@ -1706,6 +1707,31 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "error_suppression_should_work_on_type_functi
|
|||
CHECK("Unknown type 'Colours'" == toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_should_not_assert_on_empty_string_props")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag _{FFlag::LuauEmptyStringInKeyOf, true};
|
||||
|
||||
loadDefinition(R"(
|
||||
declare class Foobar
|
||||
one: boolean
|
||||
[""]: number
|
||||
end
|
||||
)");
|
||||
|
||||
CheckResult results = check(R"(
|
||||
export type FoobarKeys = keyof<Foobar>;
|
||||
export type TableKeys = keyof<{ [""]: string, two: boolean }>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(results);
|
||||
CHECK_EQ(R"("" | "one")", toString(requireTypeAlias("FoobarKeys")));
|
||||
CHECK_EQ(R"("" | "two")", toString(requireTypeAlias("TableKeys")));
|
||||
|
||||
}
|
||||
|
||||
struct TFFixture
|
||||
{
|
||||
TypeArena arena_;
|
||||
|
|
|
@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
|
|||
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||
LUAU_FASTFLAG(LuauFollowTypeAlias)
|
||||
LUAU_FASTFLAG(LuauFollowExistingTypeFunction)
|
||||
LUAU_FASTFLAG(LuauTypeFunctionSerializeFollowMetatable)
|
||||
|
||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||
|
||||
|
@ -2468,6 +2469,20 @@ end
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "udtf_metatable_serialization_follows")
|
||||
{
|
||||
ScopedFastFlag luauTypeFunctionSerializeFollowMetatable{FFlag::LuauTypeFunctionSerializeFollowMetatable, true};
|
||||
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
_ = setmetatable(_(),_),(_) or _ == _ or f
|
||||
while _() do
|
||||
export type function t0<O,I>(l0,l0)
|
||||
end
|
||||
type t39<A> = t0<{write [Vector3]:any},typeof(_),l0.t0,any>
|
||||
end
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "udtf_double_definition")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -443,7 +444,10 @@ TEST_CASE_FIXTURE(Fixture, "self_referential_type_alias")
|
|||
std::optional<Property> incr = get(oTable->props, "incr");
|
||||
REQUIRE(incr);
|
||||
|
||||
const FunctionType* incrFunc = get<FunctionType>(incr->type());
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
REQUIRE(incr->readTy);
|
||||
|
||||
const FunctionType* incrFunc = FFlag::LuauRemoveTypeCallsForReadWriteProps ? get<FunctionType>(*incr->readTy) : get<FunctionType>(incr->type_DEPRECATED());
|
||||
REQUIRE(incrFunc);
|
||||
|
||||
std::optional<TypeId> firstArg = first(incrFunc->argTypes);
|
||||
|
@ -602,7 +606,14 @@ TEST_CASE_FIXTURE(Fixture, "interface_types_belong_to_interface_arena")
|
|||
TableType* exportsTable = getMutable<TableType>(*exportsType);
|
||||
REQUIRE(exportsTable != nullptr);
|
||||
|
||||
TypeId n = exportsTable->props["n"].type();
|
||||
TypeId n;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(exportsTable->props["n"].readTy);
|
||||
n = *exportsTable->props["n"].readTy;
|
||||
}
|
||||
else
|
||||
n = exportsTable->props["n"].type_DEPRECATED();
|
||||
REQUIRE(n != nullptr);
|
||||
|
||||
CHECK(isInArena(n, mod.interfaceTypes));
|
||||
|
@ -657,10 +668,24 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti
|
|||
TableType* exportsTable = getMutable<TableType>(*exportsType);
|
||||
REQUIRE(exportsTable != nullptr);
|
||||
|
||||
TypeId aType = exportsTable->props["a"].type();
|
||||
TypeId aType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(exportsTable->props["a"].readTy);
|
||||
aType = *exportsTable->props["a"].readTy;
|
||||
}
|
||||
else
|
||||
aType = exportsTable->props["a"].type_DEPRECATED();
|
||||
REQUIRE(aType);
|
||||
|
||||
TypeId bType = exportsTable->props["b"].type();
|
||||
TypeId bType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(exportsTable->props["b"].readTy);
|
||||
bType = *exportsTable->props["b"].readTy;
|
||||
}
|
||||
else
|
||||
bType = exportsTable->props["b"].type_DEPRECATED();
|
||||
REQUIRE(bType);
|
||||
|
||||
CHECK(isInArena(recordType, mod.interfaceTypes));
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferAnyError");
|
||||
|
||||
|
@ -294,7 +295,13 @@ TEST_CASE_FIXTURE(Fixture, "assign_prop_to_table_by_calling_any_yields_any")
|
|||
REQUIRE(ttv);
|
||||
REQUIRE(ttv->props.count("prop"));
|
||||
|
||||
REQUIRE_EQ("any", toString(ttv->props["prop"].type()));
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(ttv->props["prop"].readTy);
|
||||
CHECK_EQ("any", toString(*ttv->props["prop"].readTy));
|
||||
}
|
||||
else
|
||||
REQUIRE_EQ("any", toString(ttv->props["prop"].type_DEPRECATED()));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself")
|
||||
|
|
|
@ -14,7 +14,11 @@ LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
|||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
LUAU_FASTFLAG(LuauStringFormatImprovements)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling)
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
|
||||
|
||||
TEST_SUITE_BEGIN("BuiltinTests");
|
||||
|
||||
|
@ -1313,7 +1317,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change")
|
|||
REQUIRE(mathTy);
|
||||
TableType* ttv = getMutable<TableType>(mathTy);
|
||||
REQUIRE(ttv);
|
||||
const FunctionType* ftv = get<FunctionType>(ttv->props["frexp"].type());
|
||||
const FunctionType* ftv;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(ttv->props["frexp"].readTy);
|
||||
ftv = get<FunctionType>(*ttv->props["frexp"].readTy);
|
||||
}
|
||||
else
|
||||
ftv = get<FunctionType>(ttv->props["frexp"].type_DEPRECATED());
|
||||
REQUIRE(ftv);
|
||||
auto original = ftv->level;
|
||||
|
||||
|
@ -1720,12 +1731,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_strin
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauWriteOnlyPropertyMangling, true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauEnableWriteOnlyProperties, true},
|
||||
{FFlag::LuauWriteOnlyPropertyMangling, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
};
|
||||
|
||||
// CLI-157307: This currently errors as we claim the literal is not a
|
||||
// `{ write foo: number }`, which is wrong as every table literal is
|
||||
// trivially a write-only table.
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
local function accept(t: { write foo: number })
|
||||
end
|
||||
|
||||
|
@ -1733,4 +1746,42 @@ TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion")
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_known_property_identity")
|
||||
{
|
||||
// This will not result in a real refinement, as we refine `bnot`, a function, to be truthy
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
if bit32.bnot then
|
||||
end
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_unknown_property")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauTypeCheckerStricterIndexCheck, true};
|
||||
|
||||
CheckResult results = check(R"(
|
||||
if bit32.scrambleEggs then
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, results);
|
||||
auto err = get<UnknownProperty>(results.errors[0]);
|
||||
CHECK_EQ(err->key, "scrambleEggs");
|
||||
CHECK_EQ(toString(err->table), "typeof(bit32)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_known_property_narrow")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
local myutf8 = utf8
|
||||
if myutf8.charpattern == "lol" then
|
||||
local foobar = myutf8.charpattern
|
||||
local _ = foobar
|
||||
end
|
||||
)"));
|
||||
CHECK_EQ("\"lol\"", toString(requireTypeAtPosition(Position{4, 23})));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -15,6 +15,7 @@ using namespace Luau;
|
|||
using std::nullopt;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferExternTypes");
|
||||
|
||||
|
@ -441,6 +442,8 @@ b.X = 2 -- real Vector2.X is also read-only
|
|||
|
||||
TEST_CASE_FIXTURE(ExternTypeFixture, "detailed_class_unification_error")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function foo(v)
|
||||
return v.X :: number + string.len(v.Y)
|
||||
|
@ -455,7 +458,10 @@ b(a)
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors.at(0)));
|
||||
const std::string expected = "Type 'Vector2' could not be converted into '{ read X: unknown, read Y: string }'; \n"
|
||||
"this is because accessing `Y` results in `number` in the former type and `string` in the latter type, "
|
||||
"and `number` is not a subtype of `string`";
|
||||
CHECK_EQ(expected, toString(result.errors.at(0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
TEST_SUITE_BEGIN("DefinitionTests");
|
||||
|
||||
|
@ -170,10 +171,23 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function")
|
|||
REQUIRE(!result.success);
|
||||
CHECK_EQ(result.parseResult.errors.size(), 0);
|
||||
REQUIRE(bool(result.module));
|
||||
REQUIRE_EQ(result.module->errors.size(), 1);
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
REQUIRE_EQ(result.module->errors.size(), 2);
|
||||
else
|
||||
REQUIRE_EQ(result.module->errors.size(), 1);
|
||||
GenericError* ge = get<GenericError>(result.module->errors[0]);
|
||||
REQUIRE(ge);
|
||||
CHECK_EQ("Cannot overload non-function class member 'X'", ge->message);
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
CHECK_EQ("Cannot overload read type of non-function class member 'X'", ge->message);
|
||||
else
|
||||
CHECK_EQ("Cannot overload non-function class member 'X'", ge->message);
|
||||
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
GenericError* ge2 = get<GenericError>(result.module->errors[1]);
|
||||
REQUIRE(ge2);
|
||||
CHECK_EQ("Cannot overload write type of non-function class member 'X'", ge2->message);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class")
|
||||
|
@ -366,7 +380,14 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re
|
|||
const auto& method = cls->props["myMethod"];
|
||||
CHECK_EQ(method.documentationSymbol, "@test/globaltype/MyClass.myMethod");
|
||||
|
||||
FunctionType* function = getMutable<FunctionType>(method.type());
|
||||
FunctionType* function;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(method.readTy);
|
||||
function = getMutable<FunctionType>(*method.readTy);
|
||||
}
|
||||
else
|
||||
function = getMutable<FunctionType>(method.type_DEPRECATED());
|
||||
REQUIRE(function);
|
||||
|
||||
REQUIRE(function->definition.has_value());
|
||||
|
|
|
@ -29,6 +29,7 @@ LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
|||
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
|
||||
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||
|
||||
|
@ -190,7 +191,15 @@ TEST_CASE_FIXTURE(Fixture, "generalize_table_property")
|
|||
const TableType* tt = get<TableType>(follow(t));
|
||||
REQUIRE(tt);
|
||||
|
||||
TypeId fooTy = tt->props.at("foo").type();
|
||||
TypeId fooTy;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
const Property& foo = tt->props.at("foo");
|
||||
REQUIRE(foo.readTy);
|
||||
fooTy = *foo.readTy;
|
||||
}
|
||||
else
|
||||
fooTy = tt->props.at("foo").type_DEPRECATED();
|
||||
CHECK("<a>(a) -> a" == toString(fooTy));
|
||||
}
|
||||
|
||||
|
@ -235,7 +244,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified")
|
|||
REQUIRE(ttv);
|
||||
|
||||
REQUIRE(ttv->props.count("f"));
|
||||
TypeId k = ttv->props["f"].type();
|
||||
|
||||
TypeId k;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
const Property& f = ttv->props["f"];
|
||||
REQUIRE(f.readTy);
|
||||
k = *f.readTy;
|
||||
}
|
||||
else
|
||||
k = ttv->props["f"].type_DEPRECATED();
|
||||
REQUIRE(k);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ LUAU_FASTFLAG(LuauIntersectNotNil)
|
|||
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization4)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1142,7 +1143,15 @@ TEST_CASE_FIXTURE(Fixture, "generic_table_method")
|
|||
REQUIRE(tTable != nullptr);
|
||||
|
||||
REQUIRE(tTable->props.count("bar"));
|
||||
TypeId barType = tTable->props["bar"].type();
|
||||
TypeId barType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
Property& bar = tTable->props["bar"];
|
||||
REQUIRE(bar.readTy);
|
||||
barType = *bar.readTy;
|
||||
}
|
||||
else
|
||||
barType = tTable->props["bar"].type_DEPRECATED();
|
||||
REQUIRE(barType != nullptr);
|
||||
|
||||
const FunctionType* ftv = get<FunctionType>(follow(barType));
|
||||
|
@ -1177,7 +1186,15 @@ TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions")
|
|||
std::optional<Property> fooProp = get(t->props, "foo");
|
||||
REQUIRE(bool(fooProp));
|
||||
|
||||
const FunctionType* foo = get<FunctionType>(follow(fooProp->type()));
|
||||
|
||||
const FunctionType* foo;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(fooProp->readTy);
|
||||
foo = get<FunctionType>(follow(*fooProp->readTy));
|
||||
}
|
||||
else
|
||||
foo = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
|
||||
REQUIRE(bool(foo));
|
||||
|
||||
std::optional<TypeId> ret_ = first(foo->retTypes);
|
||||
|
@ -1224,7 +1241,14 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_cyclic_generic_function")
|
|||
std::optional<Property> methodProp = get(argTable->props, "method");
|
||||
REQUIRE(bool(methodProp));
|
||||
|
||||
const FunctionType* methodFunction = get<FunctionType>(follow(methodProp->type()));
|
||||
const FunctionType* methodFunction;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(methodProp->readTy);
|
||||
methodFunction = get<FunctionType>(follow(*methodProp->readTy));
|
||||
}
|
||||
else
|
||||
methodFunction = get<FunctionType>(follow(methodProp->type_DEPRECATED()));
|
||||
REQUIRE(methodFunction != nullptr);
|
||||
|
||||
std::optional<TypeId> methodArg = first(methodFunction->argTypes);
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauNarrowIntersectionNevers)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
TEST_SUITE_BEGIN("IntersectionTypes");
|
||||
|
||||
|
@ -1458,8 +1458,10 @@ TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_more_realistic_intersections")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "narrow_intersection_nevers")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag narrowIntersections{FFlag::LuauNarrowIntersectionNevers, true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauRefineTablesWithReadType, true},
|
||||
};
|
||||
|
||||
loadDefinition(R"(
|
||||
declare class Player
|
||||
|
@ -1474,7 +1476,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "narrow_intersection_nevers")
|
|||
end
|
||||
)"));
|
||||
|
||||
CHECK_EQ("Player & { Character: ~(false?) }", toString(requireTypeAtPosition({3, 23})));
|
||||
CHECK_EQ("Player & { read Character: ~(false?) }", toString(requireTypeAtPosition({3, 23})));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -13,7 +13,6 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTINT(LuauNormalizeCacheLimit)
|
||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
|
@ -49,7 +48,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
|||
end
|
||||
)";
|
||||
|
||||
const std::string expected = FFlag::LuauStoreCSTData2 ? R"(
|
||||
const std::string expected = R"(
|
||||
function f(a:{fn:()->(a,b...)}): ()
|
||||
if type(a) == 'boolean' then
|
||||
local a1:boolean=a
|
||||
|
@ -57,18 +56,9 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
|||
local a2:{fn:()->(a,b...)}=a
|
||||
end
|
||||
end
|
||||
)"
|
||||
: R"(
|
||||
function f(a:{fn:()->(a,b...)}): ()
|
||||
if type(a) == 'boolean'then
|
||||
local a1:boolean=a
|
||||
elseif a.fn()then
|
||||
local a2:{fn:()->(a,b...)}=a
|
||||
end
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string expectedWithNewSolver = FFlag::LuauStoreCSTData2 ? R"(
|
||||
const std::string expectedWithNewSolver = R"(
|
||||
function f(a:{fn:()->(unknown,...unknown)}): ()
|
||||
if type(a) == 'boolean' then
|
||||
local a1:{fn:()->(unknown,...unknown)}&boolean=a
|
||||
|
@ -76,18 +66,9 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
|||
local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a
|
||||
end
|
||||
end
|
||||
)"
|
||||
: R"(
|
||||
function f(a:{fn:()->(unknown,...unknown)}): ()
|
||||
if type(a) == 'boolean'then
|
||||
local a1:{fn:()->(unknown,...unknown)}&boolean=a
|
||||
elseif a.fn()then
|
||||
local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a
|
||||
end
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string expectedWithEqSat = FFlag::LuauStoreCSTData2 ? R"(
|
||||
const std::string expectedWithEqSat = R"(
|
||||
function f(a:{fn:()->(unknown,...unknown)}): ()
|
||||
if type(a) == 'boolean' then
|
||||
local a1:{fn:()->(unknown,...unknown)}&boolean=a
|
||||
|
@ -95,15 +76,6 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
|||
local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a
|
||||
end
|
||||
end
|
||||
)"
|
||||
: R"(
|
||||
function f(a:{fn:()->(unknown,...unknown)}): ()
|
||||
if type(a) == 'boolean'then
|
||||
local a1:{fn:()->(unknown,...unknown)}&boolean=a
|
||||
elseif a.fn()then
|
||||
local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a
|
||||
end
|
||||
end
|
||||
)";
|
||||
|
||||
if (FFlag::LuauSolverV2 && !FFlag::DebugLuauEqSatSimplification)
|
||||
|
|
|
@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive)
|
|||
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauNormalizationIntersectTablesPreservesExternTypes)
|
||||
LUAU_FASTFLAG(LuauAvoidDoubleNegation)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -509,6 +510,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "impossible_type_narrow_is_not_an_error")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "truthy_constraint_on_properties")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?} = {x = 1}
|
||||
|
||||
|
@ -530,8 +533,7 @@ TEST_CASE_FIXTURE(Fixture, "truthy_constraint_on_properties")
|
|||
}
|
||||
else
|
||||
{
|
||||
// CLI-115281 - Types produced by refinements don't always get simplified
|
||||
CHECK("{ x: number? } & { x: ~(false?) }" == toString(requireTypeAtPosition({4, 23})));
|
||||
CHECK("{ read x: number, write x: number? }" == toString(requireTypeAtPosition({4, 23})));
|
||||
}
|
||||
CHECK("number" == toString(requireTypeAtPosition({5, 26})));
|
||||
}
|
||||
|
@ -998,6 +1000,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "either_number_or_string")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(t: {x: boolean}?)
|
||||
if not t or t.x then
|
||||
|
@ -1010,8 +1014,14 @@ TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// CLI-115281 Types produced by refinements do not consistently get simplified
|
||||
CHECK_EQ("({ x: boolean } & { x: ~(false?) })?", toString(requireTypeAtPosition({3, 28})));
|
||||
// CLI-115281 Types produced by refinements do not consistently get simplified: we are minting a type like:
|
||||
//
|
||||
// intersect<{ x: boolean } | nil, { read x: ~(false?) } | false | nil>
|
||||
//
|
||||
// ... which we can't _quite_ refine into the type it ought to be:
|
||||
//
|
||||
// { write x: boolean, read x: true } | nil
|
||||
CHECK_EQ("({ read x: ~(false?) } & { x: boolean })?", toString(requireTypeAtPosition({3, 28})));
|
||||
}
|
||||
else
|
||||
CHECK_EQ("{| x: boolean |}?", toString(requireTypeAtPosition({3, 28})));
|
||||
|
@ -1244,7 +1254,10 @@ TEST_CASE_FIXTURE(Fixture, "apply_refinements_on_astexprindexexpr_whose_subscrip
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAvoidDoubleNegation, true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauAvoidDoubleNegation, true},
|
||||
{FFlag::LuauRefineTablesWithReadType, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = {tag: "missing", x: nil} | {tag: "exists", x: string}
|
||||
|
@ -1262,15 +1275,8 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// CLI-115281 Types produced by refinements do not consistently get
|
||||
// simplified. Sometimes this is due to not refining at the correct
|
||||
// time, sometimes this is due to hitting the simplifier rather than
|
||||
// normalization.
|
||||
CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28})));
|
||||
CHECK(
|
||||
R"(({ tag: "exists", x: string } & { x: false? }) | ({ tag: "missing", x: nil } & { x: false? }))" ==
|
||||
toString(requireTypeAtPosition({7, 28}))
|
||||
);
|
||||
CHECK(R"({ tag: "exists", x: string })" == toString(requireTypeAtPosition({5, 28})));
|
||||
CHECK(R"({ tag: "missing", x: nil })" == toString(requireTypeAtPosition({7, 28})));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2684,9 +2690,35 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451")
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
|
||||
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_single")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
|
||||
{FFlag::LuauTypeCheckerStricterIndexCheck, true},
|
||||
{FFlag::LuauRefineTablesWithReadType, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function invokeDisconnect(d: unknown)
|
||||
if type(d) == "function" then
|
||||
d()
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ("The type function is not precise enough for us to determine the appropriate result type of this call.", toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_union")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBetterCannotCallFunctionPrimitive, true},
|
||||
{FFlag::LuauTypeCheckerStricterIndexCheck, true},
|
||||
{FFlag::LuauRefineTablesWithReadType, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Disconnectable = {
|
||||
|
@ -2704,35 +2736,16 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauTypeCheckerStricterIndexCheck)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: "
|
||||
"(t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> "
|
||||
"(...any) }'"
|
||||
);
|
||||
// FIXME CLI-157125: It's a bit clowny that we return a union of
|
||||
// functions containing `function` here, but it looks like a side
|
||||
// effect of how we execute `hasProp`.
|
||||
std::string expectedError = "Cannot call a value of type function in union:\n"
|
||||
" ((ExternScriptConnection) -> ()) | function | t2 where t1 = ExternScriptConnection | { Disconnect: t2 } | { "
|
||||
"disconnect: (t1) -> (...any) } ; t2 = (t1) -> (...any)";
|
||||
|
||||
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
|
||||
CHECK_EQ(
|
||||
toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call."
|
||||
);
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call."
|
||||
);
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function");
|
||||
}
|
||||
CHECK_EQ(toString(result.errors[1]), expectedError);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1835")
|
||||
|
|
|
@ -31,6 +31,11 @@ LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
|||
LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables)
|
||||
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
|
||||
LUAU_FASTFLAG(LuauAutocompleteMissingFollows)
|
||||
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
|
||||
LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls)
|
||||
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
|
||||
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
|
||||
|
||||
TEST_SUITE_BEGIN("TableTests");
|
||||
|
||||
|
@ -80,15 +85,33 @@ TEST_CASE_FIXTURE(Fixture, "basic")
|
|||
|
||||
std::optional<Property> fooProp = get(tType->props, "foo");
|
||||
REQUIRE(bool(fooProp));
|
||||
CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type()));
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(fooProp->readTy);
|
||||
CHECK_EQ(PrimitiveType::String, getPrimitiveType(*fooProp->readTy));
|
||||
}
|
||||
else
|
||||
CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type_DEPRECATED()));
|
||||
|
||||
std::optional<Property> bazProp = get(tType->props, "baz");
|
||||
REQUIRE(bool(bazProp));
|
||||
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type()));
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(bazProp->readTy);
|
||||
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*bazProp->readTy));
|
||||
}
|
||||
else
|
||||
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type_DEPRECATED()));
|
||||
|
||||
std::optional<Property> quuxProp = get(tType->props, "quux");
|
||||
REQUIRE(bool(quuxProp));
|
||||
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type()));
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(quuxProp->readTy);
|
||||
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy));
|
||||
}
|
||||
else
|
||||
CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type_DEPRECATED()));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "augment_table")
|
||||
|
@ -117,7 +140,16 @@ TEST_CASE_FIXTURE(Fixture, "augment_nested_table")
|
|||
REQUIRE(tType != nullptr);
|
||||
|
||||
REQUIRE(tType->props.find("p") != tType->props.end());
|
||||
const TableType* pType = get<TableType>(tType->props["p"].type());
|
||||
|
||||
const TableType* pType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
Property& p = tType->props["p"];
|
||||
REQUIRE(p.readTy);
|
||||
pType = get<TableType>(p.readTy);
|
||||
}
|
||||
else
|
||||
pType = get<TableType>(tType->props["p"].type_DEPRECATED());
|
||||
REQUIRE(pType != nullptr);
|
||||
|
||||
CHECK("{ p: { foo: string } }" == toString(requireType("t"), {true}));
|
||||
|
@ -261,7 +293,14 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function")
|
|||
std::optional<Property> fooProp = get(tableType->props, "foo");
|
||||
REQUIRE(bool(fooProp));
|
||||
|
||||
const FunctionType* methodType = get<FunctionType>(follow(fooProp->type()));
|
||||
const FunctionType* methodType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(fooProp->readTy);
|
||||
methodType = get<FunctionType>(follow(fooProp->readTy));
|
||||
}
|
||||
else
|
||||
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
|
||||
REQUIRE(methodType != nullptr);
|
||||
}
|
||||
|
||||
|
@ -275,7 +314,15 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
|
|||
|
||||
std::optional<Property> uProp = get(tableType->props, "U");
|
||||
REQUIRE(bool(uProp));
|
||||
TypeId uType = uProp->type();
|
||||
|
||||
TypeId uType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(uProp->readTy);
|
||||
uType = *uProp->readTy;
|
||||
}
|
||||
else
|
||||
uType = uProp->type_DEPRECATED();
|
||||
|
||||
const TableType* uTable = get<TableType>(uType);
|
||||
REQUIRE(uTable != nullptr);
|
||||
|
@ -283,7 +330,14 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2")
|
|||
std::optional<Property> fooProp = get(uTable->props, "foo");
|
||||
REQUIRE(bool(fooProp));
|
||||
|
||||
const FunctionType* methodType = get<FunctionType>(follow(fooProp->type()));
|
||||
const FunctionType* methodType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
REQUIRE(fooProp->readTy);
|
||||
methodType = get<FunctionType>(follow(fooProp->readTy));
|
||||
}
|
||||
else
|
||||
methodType = get<FunctionType>(follow(fooProp->type_DEPRECATED()));
|
||||
REQUIRE(methodType != nullptr);
|
||||
|
||||
std::vector<TypeId> methodArgs = flatten(methodType->argTypes).first;
|
||||
|
@ -477,6 +531,8 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_1")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
function foo(o)
|
||||
|
@ -489,23 +545,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
// CLI 114792 We don't report MissingProperties in many places where the old solver does
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
TypeMismatch* error = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE_MESSAGE(error != nullptr, "Expected TypeMismatch but got " << toString(result.errors[0]));
|
||||
MissingProperties* error = get<MissingProperties>(result.errors[0]);
|
||||
REQUIRE_MESSAGE(error != nullptr, "Expected MissingProperties but got " << toString(result.errors[0]));
|
||||
REQUIRE(error->properties.size() == 1);
|
||||
|
||||
CHECK("{ read bar: string }" == toString(error->givenType));
|
||||
CHECK("{ read bar: string, read baz: string }" == toString(error->wantedType));
|
||||
}
|
||||
else
|
||||
{
|
||||
MissingProperties* error = get<MissingProperties>(result.errors[0]);
|
||||
REQUIRE_MESSAGE(error != nullptr, "Expected MissingProperties but got " << toString(result.errors[0]));
|
||||
REQUIRE(error->properties.size() == 1);
|
||||
|
||||
CHECK_EQ("baz", error->properties[0]);
|
||||
}
|
||||
CHECK_EQ("baz", error->properties[0]);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_3")
|
||||
|
@ -1063,7 +1107,16 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s
|
|||
REQUIRE(tableType->indexer == std::nullopt);
|
||||
REQUIRE(0 != tableType->props.count("a"));
|
||||
|
||||
TypeId propertyA = tableType->props["a"].type();
|
||||
|
||||
TypeId propertyA;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
Property& a = tableType->props["a"];
|
||||
REQUIRE(a.readTy);
|
||||
propertyA = *a.readTy;
|
||||
}
|
||||
else
|
||||
propertyA = tableType->props["a"].type_DEPRECATED();
|
||||
REQUIRE(propertyA != nullptr);
|
||||
CHECK_EQ(*getBuiltins()->stringType, *propertyA);
|
||||
}
|
||||
|
@ -2298,8 +2351,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "quantifying_a_bound_var_works")
|
|||
REQUIRE_MESSAGE(ttv, "Expected a table but got " << toString(ty, {true}));
|
||||
REQUIRE(ttv->props.count("new"));
|
||||
Property& prop = ttv->props["new"];
|
||||
REQUIRE(prop.type());
|
||||
const FunctionType* ftv = get<FunctionType>(follow(prop.type()));
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
REQUIRE(prop.readTy);
|
||||
else
|
||||
REQUIRE(prop.type_DEPRECATED());
|
||||
const FunctionType* ftv =
|
||||
(FFlag::LuauRemoveTypeCallsForReadWriteProps) ? get<FunctionType>(follow(*prop.readTy)) : get<FunctionType>(follow(prop.type_DEPRECATED()));
|
||||
REQUIRE(ftv);
|
||||
const TypePack* res = get<TypePack>(follow(ftv->retTypes));
|
||||
REQUIRE(res);
|
||||
|
@ -3156,7 +3213,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_quantify_table_that_belongs_to_outer_sc
|
|||
REQUIRE(counterType);
|
||||
|
||||
REQUIRE(counterType->props.count("new"));
|
||||
const FunctionType* newType = get<FunctionType>(follow(counterType->props["new"].type()));
|
||||
const FunctionType* newType;
|
||||
if (FFlag::LuauRemoveTypeCallsForReadWriteProps)
|
||||
{
|
||||
Property& newProp = counterType->props["new"];
|
||||
REQUIRE(newProp.readTy);
|
||||
newType = get<FunctionType>(follow(*newProp.readTy));
|
||||
}
|
||||
else
|
||||
newType = get<FunctionType>(follow(counterType->props["new"].type_DEPRECATED()));
|
||||
REQUIRE(newType);
|
||||
|
||||
std::optional<TypeId> newRetType = *first(newType->retTypes);
|
||||
|
@ -3517,6 +3582,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_invalidate_the_properties_iterator_of_free_tabl
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {x: number?}? = {x = nil}
|
||||
local u = t.x and t or 5
|
||||
|
@ -3527,7 +3594,7 @@ TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early")
|
|||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ("Value of type '{ x: number? }?' could be nil", toString(result.errors[0]));
|
||||
CHECK_EQ("number | { x: number }", toString(requireType("u")));
|
||||
CHECK_EQ("number | { read x: number, write x: number? }", toString(requireType("u")));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3896,6 +3963,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
|
|||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{FFlag::LuauInstantiateInSubtyping, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -3915,12 +3983,12 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// FIXME. We really should be reporting just one error in this case. CLI-114509
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
CHECK(get<TypePackMismatch>(result.errors[0]));
|
||||
CHECK(get<TypeMismatch>(result.errors[1]));
|
||||
CHECK(get<TypeMismatch>(result.errors[2]));
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
auto err = get<TypeMismatch>(result.errors[0]);
|
||||
CHECK_EQ(result.errors[0].location, Location{{10, 10}, {10, 11}});
|
||||
REQUIRE(err);
|
||||
CHECK_EQ("{ m: (number) -> number }", toString(err->wantedType));
|
||||
CHECK_EQ("{ m: <T>(T) -> T }", toString(err->givenType, {true}));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4457,6 +4525,8 @@ TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function one(tbl: {x: any}) end
|
||||
function two(tbl: {x: string}) one(tbl) end -- ok, string <: any and any <: string
|
||||
|
@ -4476,8 +4546,8 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression")
|
|||
// honestly not sure which of these is a better developer experience.
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(*tm->wantedType, *getBuiltins()->stringType);
|
||||
CHECK_EQ(*tm->givenType, *getBuiltins()->numberType);
|
||||
CHECK_EQ("{ x: any, y: string }", toString(tm->wantedType));
|
||||
CHECK_EQ("{ x: string, y: number }", toString(tm->givenType));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5583,7 +5653,7 @@ TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation")
|
|||
{
|
||||
// It's kind of suspect that we allow multiple definitions of keys in
|
||||
// a single table.
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
CheckResult result = check(R"(
|
||||
type F = {
|
||||
_G: () -> ()
|
||||
}
|
||||
|
@ -5595,7 +5665,9 @@ TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation")
|
|||
_G = {},
|
||||
_G = _,
|
||||
})
|
||||
)"));
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_in_indexer_with_compound_assign")
|
||||
|
@ -5643,6 +5715,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
|
|||
}
|
||||
)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
|
@ -5847,4 +5920,167 @@ TEST_CASE_FIXTURE(Fixture, "oss_1859")
|
|||
CHECK_EQ("{ actions: { meow: (...any) -> string }, age: number, name: string }", toString(err->givenType));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1797_intersection_of_tables_arent_disjoint")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauRelateTablesAreNeverDisjoint, true},
|
||||
{FFlag::LuauRefineTablesWithReadType, true},
|
||||
};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
--!strict
|
||||
|
||||
export type Foo = {
|
||||
foo: string,
|
||||
}
|
||||
|
||||
export type Bar = Foo & {
|
||||
copy: (...any) -> any
|
||||
}
|
||||
|
||||
local function _test(nd: { bar: Bar? })
|
||||
local bar = nd.bar
|
||||
if not bar then
|
||||
return
|
||||
end
|
||||
print(bar)
|
||||
end
|
||||
)"));
|
||||
|
||||
// This might be better as the expanded intersection, but the point of
|
||||
// this test is that this _isn't_ `never`.
|
||||
CHECK_EQ("Foo & { copy: (...any) -> any }", toString(requireTypeAtPosition({16, 20})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "oss_1344")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
--!strict
|
||||
type t = {
|
||||
value: string?,
|
||||
}
|
||||
|
||||
local t: t = {}
|
||||
|
||||
if not t.value then
|
||||
t.value = ""
|
||||
end
|
||||
|
||||
local s: string? = nil
|
||||
|
||||
if not s then
|
||||
s = ""
|
||||
end
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1651")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
--!strict
|
||||
local MyModule = {}
|
||||
MyModule._isEnabled = true :: boolean
|
||||
|
||||
assert(MyModule._isEnabled, `type solver`)
|
||||
MyModule._isEnabled = false
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
local function take(_: { foo: string? }) end
|
||||
|
||||
take({ foo = "bar" })
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_incorrect")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
};
|
||||
|
||||
CheckResult results = check(R"(
|
||||
local function take(_: { foo: string?, bing: number }) end
|
||||
|
||||
take({ foo = "bar", bing = true })
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, results);
|
||||
|
||||
auto err = get<TypeMismatch>(results.errors[0]);
|
||||
CHECK_EQ(results.errors[0].location, Location{{3, 35}, {3, 39}});
|
||||
CHECK_EQ(toString(err->givenType), "boolean");
|
||||
CHECK_EQ(toString(err->wantedType), "number");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_singleton")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
};
|
||||
|
||||
CheckResult results = check(R"(
|
||||
local function take(_: { foo: "foo" }) end
|
||||
|
||||
take({ foo = "foo" })
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(results);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
|
||||
{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true},
|
||||
{FFlag::LuauEagerGeneralization4, true},
|
||||
};
|
||||
|
||||
CheckResult results = check(R"(
|
||||
local keycodes = {
|
||||
Alt = 2,
|
||||
Space = 3,
|
||||
Tab = 4,
|
||||
}
|
||||
|
||||
type Keycode = keyof<typeof(keycodes)>
|
||||
local function sendInput(keycodes: { Keycode })
|
||||
print(keycodes)
|
||||
end
|
||||
|
||||
sendInput({"Alt"}) -- shouldn't error
|
||||
sendInput(
|
||||
{
|
||||
"Alt",
|
||||
"Space",
|
||||
"Ctrl", -- should error
|
||||
}
|
||||
)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, results);
|
||||
auto err = get<TypeMismatch>(results.errors[0]);
|
||||
REQUIRE(err);
|
||||
// NOTE: Single line with just `"Ctrl"`
|
||||
CHECK_EQ(results.errors[0].location, Location{{17, 16}, {17, 22}});
|
||||
CHECK_EQ(R"("Alt" | "Space" | "Tab")", toString(err->wantedType));
|
||||
// FIXME: CLI-157899
|
||||
CHECK_EQ(R"("Alt" | "Ctrl" | "Space" | "Tab")", toString(err->givenType));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -2443,4 +2443,15 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_infer_divergent_rw_props")
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "read_table_type_refinements_persist_scope")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
_ = {n0=_,},if _._ then ... else if _[if _ then _ else ({nil,})].setmetatable then if _ then _ elseif l0 then ... elseif _.n0 then _ elseif function<A>(l0)
|
||||
return _._G,_
|
||||
end then _._G else ...
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -93,7 +93,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified")
|
|||
TableType{{{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed},
|
||||
}};
|
||||
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type(), *getMutable<TableType>(&tableTwo)->props["foo"].type());
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable<TableType>(&tableTwo)->props["foo"].type_DEPRECATED());
|
||||
|
||||
state.tryUnify(&tableTwo, &tableOne);
|
||||
|
||||
|
@ -102,7 +102,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified")
|
|||
|
||||
state.log.commit();
|
||||
|
||||
CHECK_EQ(*getMutable<TableType>(&tableOne)->props["foo"].type(), *getMutable<TableType>(&tableTwo)->props["foo"].type());
|
||||
CHECK_EQ(*getMutable<TableType>(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable<TableType>(&tableTwo)->props["foo"].type_DEPRECATED());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved")
|
||||
|
@ -125,14 +125,14 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved")
|
|||
},
|
||||
}};
|
||||
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type(), *getMutable<TableType>(&tableTwo)->props["foo"].type());
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable<TableType>(&tableTwo)->props["foo"].type_DEPRECATED());
|
||||
|
||||
state.tryUnify(&tableTwo, &tableOne);
|
||||
|
||||
CHECK(state.failure);
|
||||
CHECK_EQ(1, state.errors.size());
|
||||
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type(), *getMutable<TableType>(&tableTwo)->props["foo"].type());
|
||||
CHECK_NE(*getMutable<TableType>(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable<TableType>(&tableTwo)->props["foo"].type_DEPRECATED());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "uninhabited_intersection_sub_never")
|
||||
|
|
Loading…
Add table
Reference in a new issue