mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Sync to upstream/release/671 (#1787)
# General * Internally rename `ClassType` to `ExternType`. In definition files, the syntax to define these types has changed to `declare extern type Foo with prop: type end` * Add `luarequire_registermodule` to Luau.Require * Support yieldable Luau C functions calling other functions * Store return types as `AstTypePack*` on Ast nodes ## New Solver * Improve the logic that determines constraint dispatch ordering * Fix a crash in the type solver that arose when using multi-return functions with `string.format` * Fix https://github.com/luau-lang/luau/issues/1736 * Initial steps toward rethinking function generalization: * Instead of generalizing every type in a function all at once, we will instead generalize individual type variables once their bounds have been fully resolved. This will make it possible to properly interleave type function reduction and generalization. * Magic functions are no longer considered magical in cases where they are not explicitly called by the code. * The most prominent example of this is in `for..in` loops where the function call is part of the desugaring process. * Almost all magic functions work by directly inspecting the AST, so they can't work without an AST fragment anyway. * Further, none of the magic functions we have are usefully used in this way. Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
a2303a6ae6
commit
c51743268b
127 changed files with 4025 additions and 1572 deletions
|
@ -57,7 +57,7 @@ struct AutocompleteEntry
|
||||||
// Set if this suggestion matches the type expected in the context
|
// Set if this suggestion matches the type expected in the context
|
||||||
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
|
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
|
||||||
|
|
||||||
std::optional<const ClassType*> containingClass = std::nullopt;
|
std::optional<const ExternType*> containingExternType = std::nullopt;
|
||||||
std::optional<const Property*> prop = std::nullopt;
|
std::optional<const Property*> prop = std::nullopt;
|
||||||
std::optional<std::string> documentationSymbol = std::nullopt;
|
std::optional<std::string> documentationSymbol = std::nullopt;
|
||||||
Tags tags;
|
Tags tags;
|
||||||
|
@ -85,7 +85,7 @@ struct AutocompleteResult
|
||||||
};
|
};
|
||||||
|
|
||||||
using StringCompletionCallback =
|
using StringCompletionCallback =
|
||||||
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ClassType*> ctx, std::optional<std::string> contents)>;
|
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ExternType*> ctx, std::optional<std::string> contents)>;
|
||||||
|
|
||||||
constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
|
constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
|
#include "Luau/ConstraintSet.h"
|
||||||
#include "Luau/ControlFlow.h"
|
#include "Luau/ControlFlow.h"
|
||||||
#include "Luau/DataFlowGraph.h"
|
#include "Luau/DataFlowGraph.h"
|
||||||
#include "Luau/EqSatSimplification.h"
|
#include "Luau/EqSatSimplification.h"
|
||||||
|
@ -91,9 +92,8 @@ struct ConstraintGenerator
|
||||||
// Constraints that go straight to the solver.
|
// Constraints that go straight to the solver.
|
||||||
std::vector<ConstraintPtr> constraints;
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
// Constraints that do not go to the solver right away. Other constraints
|
// The set of all free types introduced during constraint generation.
|
||||||
// will enqueue them during solving.
|
DenseHashSet<TypeId> freeTypes{nullptr};
|
||||||
std::vector<ConstraintPtr> unqueuedConstraints;
|
|
||||||
|
|
||||||
// Map a function's signature scope back to its signature type.
|
// Map a function's signature scope back to its signature type.
|
||||||
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||||
|
@ -151,6 +151,9 @@ struct ConstraintGenerator
|
||||||
std::vector<RequireCycle> requireCycles
|
std::vector<RequireCycle> requireCycles
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ConstraintSet run(AstStatBlock* block);
|
||||||
|
ConstraintSet runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entry point to the ConstraintGenerator. This will construct a set
|
* The entry point to the ConstraintGenerator. This will construct a set
|
||||||
* of scopes, constraints, and free types that can be solved later.
|
* of scopes, constraints, and free types that can be solved later.
|
||||||
|
@ -269,7 +272,7 @@ private:
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatTypeAlias* alias);
|
ControlFlow visit(const ScopePtr& scope, AstStatTypeAlias* alias);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatTypeFunction* function);
|
ControlFlow visit(const ScopePtr& scope, AstStatTypeFunction* function);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
|
ControlFlow visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareClass* declareClass);
|
ControlFlow visit(const ScopePtr& scope, AstStatDeclareExternType* declareExternType);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
|
ControlFlow visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
|
||||||
ControlFlow visit(const ScopePtr& scope, AstStatError* error);
|
ControlFlow visit(const ScopePtr& scope, AstStatError* error);
|
||||||
|
|
||||||
|
@ -481,9 +484,4 @@ private:
|
||||||
TypeId simplifyUnion(const ScopePtr& scope, Location location, TypeId left, TypeId right);
|
TypeId simplifyUnion(const ScopePtr& scope, Location location, TypeId left, TypeId right);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Borrow a vector of pointers from a vector of owning pointers to constraints.
|
|
||||||
*/
|
|
||||||
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
32
Analysis/include/Luau/ConstraintSet.h
Normal file
32
Analysis/include/Luau/ConstraintSet.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Constraint.h"
|
||||||
|
#include "Luau/DenseHash.h"
|
||||||
|
#include "Luau/Error.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Luau
|
||||||
|
{
|
||||||
|
|
||||||
|
struct ConstraintSet
|
||||||
|
{
|
||||||
|
NotNull<Scope> rootScope;
|
||||||
|
|
||||||
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
|
// The set of all free types created during constraint generation
|
||||||
|
DenseHashSet<TypeId> freeTypes{nullptr};
|
||||||
|
|
||||||
|
// Map a function's signature scope back to its signature type. Once we've
|
||||||
|
// dispatched all of the constraints pertaining to a particular free type,
|
||||||
|
// we use this mapping to generalize that free type.
|
||||||
|
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||||
|
|
||||||
|
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
|
||||||
|
std::vector<TypeError> errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
|
#include "Luau/ConstraintSet.h"
|
||||||
#include "Luau/DataFlowGraph.h"
|
#include "Luau/DataFlowGraph.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/EqSatSimplification.h"
|
#include "Luau/EqSatSimplification.h"
|
||||||
|
@ -87,6 +88,7 @@ struct ConstraintSolver
|
||||||
NotNull<Simplifier> simplifier;
|
NotNull<Simplifier> simplifier;
|
||||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||||
// The entire set of constraints that the solver is trying to resolve.
|
// The entire set of constraints that the solver is trying to resolve.
|
||||||
|
ConstraintSet constraintSet;
|
||||||
std::vector<NotNull<Constraint>> constraints;
|
std::vector<NotNull<Constraint>> constraints;
|
||||||
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
|
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
|
||||||
NotNull<Scope> rootScope;
|
NotNull<Scope> rootScope;
|
||||||
|
@ -140,6 +142,19 @@ struct ConstraintSolver
|
||||||
|
|
||||||
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
|
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
|
||||||
|
|
||||||
|
explicit ConstraintSolver(
|
||||||
|
NotNull<Normalizer> normalizer,
|
||||||
|
NotNull<Simplifier> simplifier,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
|
ModuleName moduleName,
|
||||||
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
|
std::vector<RequireCycle> requireCycles,
|
||||||
|
DcrLogger* logger,
|
||||||
|
NotNull<const DataFlowGraph> dfg,
|
||||||
|
TypeCheckLimits limits,
|
||||||
|
ConstraintSet constraintSet
|
||||||
|
);
|
||||||
|
|
||||||
explicit ConstraintSolver(
|
explicit ConstraintSolver(
|
||||||
NotNull<Normalizer> normalizer,
|
NotNull<Normalizer> normalizer,
|
||||||
NotNull<Simplifier> simplifier,
|
NotNull<Simplifier> simplifier,
|
||||||
|
@ -174,6 +189,9 @@ struct ConstraintSolver
|
||||||
bool isDone() const;
|
bool isDone() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// A helper that does most of the setup work that is shared between the two constructors.
|
||||||
|
void initFreeTypeTracking();
|
||||||
|
|
||||||
void generalizeOneType(TypeId ty);
|
void generalizeOneType(TypeId ty);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -432,6 +450,10 @@ public:
|
||||||
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
|
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Borrow a vector of pointers from a vector of owning pointers to constraints.
|
||||||
|
*/
|
||||||
|
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
|
||||||
|
|
||||||
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -173,7 +173,7 @@ private:
|
||||||
ControlFlow visit(AstStatTypeFunction* f);
|
ControlFlow visit(AstStatTypeFunction* f);
|
||||||
ControlFlow visit(AstStatDeclareGlobal* d);
|
ControlFlow visit(AstStatDeclareGlobal* d);
|
||||||
ControlFlow visit(AstStatDeclareFunction* d);
|
ControlFlow visit(AstStatDeclareFunction* d);
|
||||||
ControlFlow visit(AstStatDeclareClass* d);
|
ControlFlow visit(AstStatDeclareExternType* d);
|
||||||
ControlFlow visit(AstStatError* error);
|
ControlFlow visit(AstStatError* error);
|
||||||
|
|
||||||
DataFlowResult visitExpr(AstExpr* e);
|
DataFlowResult visitExpr(AstExpr* e);
|
||||||
|
|
|
@ -332,11 +332,11 @@ struct TypePackMismatch
|
||||||
bool operator==(const TypePackMismatch& rhs) const;
|
bool operator==(const TypePackMismatch& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DynamicPropertyLookupOnClassesUnsafe
|
struct DynamicPropertyLookupOnExternTypesUnsafe
|
||||||
{
|
{
|
||||||
TypeId ty;
|
TypeId ty;
|
||||||
|
|
||||||
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
|
bool operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UninhabitedTypeFunction
|
struct UninhabitedTypeFunction
|
||||||
|
@ -499,7 +499,7 @@ using TypeErrorData = Variant<
|
||||||
TypesAreUnrelated,
|
TypesAreUnrelated,
|
||||||
NormalizationTooComplex,
|
NormalizationTooComplex,
|
||||||
TypePackMismatch,
|
TypePackMismatch,
|
||||||
DynamicPropertyLookupOnClassesUnsafe,
|
DynamicPropertyLookupOnExternTypesUnsafe,
|
||||||
UninhabitedTypeFunction,
|
UninhabitedTypeFunction,
|
||||||
UninhabitedTypePackFunction,
|
UninhabitedTypePackFunction,
|
||||||
WhereClauseNeeded,
|
WhereClauseNeeded,
|
||||||
|
|
|
@ -117,8 +117,7 @@ struct FileResolver
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make non-virtual when removing FFlagLuauImproveRequireByStringAutocomplete.
|
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||||
virtual std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
|
||||||
|
|
||||||
std::shared_ptr<RequireSuggester> requireSuggester;
|
std::shared_ptr<RequireSuggester> requireSuggester;
|
||||||
};
|
};
|
||||||
|
|
|
@ -133,9 +133,9 @@ struct GenericTypeFinder : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const Luau::ClassType&) override
|
bool visit(TypeId ty, const Luau::ExternType&) override
|
||||||
{
|
{
|
||||||
// During function instantiation, classes are not traversed even if they have generics
|
// During function instantiation, extern types are not traversed even if they have generics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -181,7 +181,7 @@ struct NormalizedStringType
|
||||||
|
|
||||||
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
|
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
|
||||||
|
|
||||||
struct NormalizedClassType
|
struct NormalizedExternType
|
||||||
{
|
{
|
||||||
/** Has the following structure:
|
/** Has the following structure:
|
||||||
*
|
*
|
||||||
|
@ -192,7 +192,7 @@ struct NormalizedClassType
|
||||||
*
|
*
|
||||||
* Each TypeId is a class type.
|
* Each TypeId is a class type.
|
||||||
*/
|
*/
|
||||||
std::unordered_map<TypeId, TypeIds> classes;
|
std::unordered_map<TypeId, TypeIds> externTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In order to maintain a consistent insertion order, we use this vector to
|
* In order to maintain a consistent insertion order, we use this vector to
|
||||||
|
@ -245,7 +245,7 @@ enum class NormalizationResult
|
||||||
};
|
};
|
||||||
|
|
||||||
// A normalized type is either any, unknown, or one of the form P | T | F | G where
|
// A normalized type is either any, unknown, or one of the form P | T | F | G where
|
||||||
// * P is a union of primitive types (including singletons, classes and the error type)
|
// * P is a union of primitive types (including singletons, extern types and the error type)
|
||||||
// * T is a union of table types
|
// * T is a union of table types
|
||||||
// * F is a union of an intersection of function types
|
// * F is a union of an intersection of function types
|
||||||
// * G is a union of generic/free/blocked types, intersected with a normalized type
|
// * G is a union of generic/free/blocked types, intersected with a normalized type
|
||||||
|
@ -260,7 +260,7 @@ struct NormalizedType
|
||||||
// This type is either never, boolean type, or a boolean singleton.
|
// This type is either never, boolean type, or a boolean singleton.
|
||||||
TypeId booleans;
|
TypeId booleans;
|
||||||
|
|
||||||
NormalizedClassType classes;
|
NormalizedExternType externTypes;
|
||||||
|
|
||||||
// The error part of the type.
|
// The error part of the type.
|
||||||
// This type is either never or the error type.
|
// This type is either never or the error type.
|
||||||
|
@ -333,7 +333,7 @@ struct NormalizedType
|
||||||
// Helpers that improve readability of the above (they just say if the component is present)
|
// Helpers that improve readability of the above (they just say if the component is present)
|
||||||
bool hasTops() const;
|
bool hasTops() const;
|
||||||
bool hasBooleans() const;
|
bool hasBooleans() const;
|
||||||
bool hasClasses() const;
|
bool hasExternTypes() const;
|
||||||
bool hasErrors() const;
|
bool hasErrors() const;
|
||||||
bool hasNils() const;
|
bool hasNils() const;
|
||||||
bool hasNumbers() const;
|
bool hasNumbers() const;
|
||||||
|
@ -391,10 +391,10 @@ public:
|
||||||
void unionTysWithTy(TypeIds& here, TypeId there);
|
void unionTysWithTy(TypeIds& here, TypeId there);
|
||||||
TypeId unionOfTops(TypeId here, TypeId there);
|
TypeId unionOfTops(TypeId here, TypeId there);
|
||||||
TypeId unionOfBools(TypeId here, TypeId there);
|
TypeId unionOfBools(TypeId here, TypeId there);
|
||||||
void unionClassesWithClass(TypeIds& heres, TypeId there);
|
void unionExternTypesWithExternType(TypeIds& heres, TypeId there);
|
||||||
void unionClasses(TypeIds& heres, const TypeIds& theres);
|
void unionExternTypes(TypeIds& heres, const TypeIds& theres);
|
||||||
void unionClassesWithClass(NormalizedClassType& heres, TypeId there);
|
void unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
|
||||||
void unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
void unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
|
||||||
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||||
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
|
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
|
||||||
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
|
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
|
||||||
|
@ -423,8 +423,8 @@ public:
|
||||||
// ------- Normalizing intersections
|
// ------- Normalizing intersections
|
||||||
TypeId intersectionOfTops(TypeId here, TypeId there);
|
TypeId intersectionOfTops(TypeId here, TypeId there);
|
||||||
TypeId intersectionOfBools(TypeId here, TypeId there);
|
TypeId intersectionOfBools(TypeId here, TypeId there);
|
||||||
void intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
void intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
|
||||||
void intersectClassesWithClass(NormalizedClassType& heres, TypeId there);
|
void intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
|
||||||
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||||
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
|
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
|
||||||
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
|
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct Scope;
|
||||||
|
|
||||||
void quantify(TypeId ty, TypeLevel level);
|
void quantify(TypeId ty, TypeLevel level);
|
||||||
|
|
||||||
// TODO: This is eerily similar to the pattern that NormalizedClassType
|
// TODO: This is eerily similar to the pattern that NormalizedExternType
|
||||||
// implements. We could, and perhaps should, merge them together.
|
// implements. We could, and perhaps should, merge them together.
|
||||||
template<typename K, typename V>
|
template<typename K, typename V>
|
||||||
struct OrderedMap
|
struct OrderedMap
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct InternalErrorReporter;
|
||||||
|
|
||||||
class TypeIds;
|
class TypeIds;
|
||||||
class Normalizer;
|
class Normalizer;
|
||||||
struct NormalizedClassType;
|
struct NormalizedExternType;
|
||||||
struct NormalizedFunctionType;
|
struct NormalizedFunctionType;
|
||||||
struct NormalizedStringType;
|
struct NormalizedStringType;
|
||||||
struct NormalizedType;
|
struct NormalizedType;
|
||||||
|
@ -121,7 +121,7 @@ struct SubtypingEnvironment
|
||||||
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};
|
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See the test cyclic_tables_are_assumed_to_be_compatible_with_classes for
|
* See the test cyclic_tables_are_assumed_to_be_compatible_with_extern_types for
|
||||||
* details.
|
* details.
|
||||||
*
|
*
|
||||||
* An empty value is equivalent to a nonexistent key.
|
* An empty value is equivalent to a nonexistent key.
|
||||||
|
@ -229,9 +229,8 @@ private:
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope);
|
||||||
SubtypingResult
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||||
isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
|
||||||
SubtypingResult isCovariantWith(
|
SubtypingResult isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const FunctionType* subFunction,
|
const FunctionType* subFunction,
|
||||||
|
@ -259,11 +258,11 @@ private:
|
||||||
);
|
);
|
||||||
SubtypingResult isCovariantWith(
|
SubtypingResult isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const NormalizedClassType& subClass,
|
const NormalizedExternType& subExternType,
|
||||||
const NormalizedClassType& superClass,
|
const NormalizedExternType& superExternType,
|
||||||
NotNull<Scope> scope
|
NotNull<Scope> scope
|
||||||
);
|
);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedExternType& subExternType, const TypeIds& superTables, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(
|
SubtypingResult isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const NormalizedStringType& subString,
|
const NormalizedStringType& subString,
|
||||||
|
|
|
@ -44,6 +44,7 @@ struct ToStringOptions
|
||||||
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
|
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
|
||||||
bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level.
|
bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level.
|
||||||
bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self
|
bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self
|
||||||
|
bool hideTableAliasExpansions = false; // If true, all table aliases will not be expanded
|
||||||
bool useQuestionMarks = true; // If true, use a postfix ? for options, else write them out as unions that include nil.
|
bool useQuestionMarks = true; // If true, use a postfix ? for options, else write them out as unions that include nil.
|
||||||
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypes
|
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypes
|
||||||
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
|
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
|
||||||
|
|
|
@ -291,7 +291,7 @@ struct MagicFunctionCallContext
|
||||||
{
|
{
|
||||||
NotNull<struct ConstraintSolver> solver;
|
NotNull<struct ConstraintSolver> solver;
|
||||||
NotNull<const Constraint> constraint;
|
NotNull<const Constraint> constraint;
|
||||||
const class AstExprCall* callSite;
|
NotNull<const AstExprCall> callSite;
|
||||||
TypePackId arguments;
|
TypePackId arguments;
|
||||||
TypePackId result;
|
TypePackId result;
|
||||||
};
|
};
|
||||||
|
@ -536,15 +536,15 @@ struct ClassUserData
|
||||||
virtual ~ClassUserData() {}
|
virtual ~ClassUserData() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The type of a class.
|
/** The type of an external userdata exposed to Luau.
|
||||||
*
|
*
|
||||||
* Classes behave like tables in many ways, but there are some important differences:
|
* Extern types behave like tables in many ways, but there are some important differences:
|
||||||
*
|
*
|
||||||
* The properties of a class are always exactly known.
|
* The properties of a class are always exactly known.
|
||||||
* Classes optionally have a parent class.
|
* Extern types optionally have a parent type.
|
||||||
* Two different classes that share the same properties are nevertheless distinct and mutually incompatible.
|
* Two different extern types that share the same properties are nevertheless distinct and mutually incompatible.
|
||||||
*/
|
*/
|
||||||
struct ClassType
|
struct ExternType
|
||||||
{
|
{
|
||||||
using Props = TableType::Props;
|
using Props = TableType::Props;
|
||||||
|
|
||||||
|
@ -558,7 +558,7 @@ struct ClassType
|
||||||
std::optional<Location> definitionLocation;
|
std::optional<Location> definitionLocation;
|
||||||
std::optional<TableIndexer> indexer;
|
std::optional<TableIndexer> indexer;
|
||||||
|
|
||||||
ClassType(
|
ExternType(
|
||||||
Name name,
|
Name name,
|
||||||
Props props,
|
Props props,
|
||||||
std::optional<TypeId> parent,
|
std::optional<TypeId> parent,
|
||||||
|
@ -579,7 +579,7 @@ struct ClassType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassType(
|
ExternType(
|
||||||
Name name,
|
Name name,
|
||||||
Props props,
|
Props props,
|
||||||
std::optional<TypeId> parent,
|
std::optional<TypeId> parent,
|
||||||
|
@ -779,7 +779,7 @@ using TypeVariant = Unifiable::Variant<
|
||||||
FunctionType,
|
FunctionType,
|
||||||
TableType,
|
TableType,
|
||||||
MetatableType,
|
MetatableType,
|
||||||
ClassType,
|
ExternType,
|
||||||
AnyType,
|
AnyType,
|
||||||
UnionType,
|
UnionType,
|
||||||
IntersectionType,
|
IntersectionType,
|
||||||
|
@ -990,7 +990,7 @@ public:
|
||||||
const TypeId threadType;
|
const TypeId threadType;
|
||||||
const TypeId bufferType;
|
const TypeId bufferType;
|
||||||
const TypeId functionType;
|
const TypeId functionType;
|
||||||
const TypeId classType;
|
const TypeId externType;
|
||||||
const TypeId tableType;
|
const TypeId tableType;
|
||||||
const TypeId emptyTableType;
|
const TypeId emptyTableType;
|
||||||
const TypeId trueType;
|
const TypeId trueType;
|
||||||
|
@ -1002,6 +1002,7 @@ public:
|
||||||
const TypeId noRefineType;
|
const TypeId noRefineType;
|
||||||
const TypeId falsyType;
|
const TypeId falsyType;
|
||||||
const TypeId truthyType;
|
const TypeId truthyType;
|
||||||
|
const TypeId notNilType;
|
||||||
|
|
||||||
const TypeId optionalNumberType;
|
const TypeId optionalNumberType;
|
||||||
const TypeId optionalStringType;
|
const TypeId optionalStringType;
|
||||||
|
@ -1022,10 +1023,10 @@ TypeLevel* getMutableLevel(TypeId ty);
|
||||||
|
|
||||||
std::optional<TypeLevel> getLevel(TypePackId tp);
|
std::optional<TypeLevel> getLevel(TypePackId tp);
|
||||||
|
|
||||||
const Property* lookupClassProp(const ClassType* cls, const Name& name);
|
const Property* lookupExternTypeProp(const ExternType* cls, const Name& name);
|
||||||
|
|
||||||
// Whether `cls` is a subclass of `parent`
|
// Whether `cls` is a subclass of `parent`
|
||||||
bool isSubclass(const ClassType* cls, const ClassType* parent);
|
bool isSubclass(const ExternType* cls, const ExternType* parent);
|
||||||
|
|
||||||
Type* asMutable(TypeId ty);
|
Type* asMutable(TypeId ty);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Luau
|
||||||
struct TypeRehydrationOptions
|
struct TypeRehydrationOptions
|
||||||
{
|
{
|
||||||
std::unordered_set<std::string> bannedNames;
|
std::unordered_set<std::string> bannedNames;
|
||||||
bool expandClassProps = false;
|
bool expandExternTypeProps = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void attachTypeData(SourceModule& source, Module& result);
|
void attachTypeData(SourceModule& source, Module& result);
|
||||||
|
|
|
@ -160,7 +160,7 @@ private:
|
||||||
void visit(AstTypeList types);
|
void visit(AstTypeList types);
|
||||||
void visit(AstStatDeclareFunction* stat);
|
void visit(AstStatDeclareFunction* stat);
|
||||||
void visit(AstStatDeclareGlobal* stat);
|
void visit(AstStatDeclareGlobal* stat);
|
||||||
void visit(AstStatDeclareClass* stat);
|
void visit(AstStatDeclareExternType* stat);
|
||||||
void visit(AstStatError* stat);
|
void visit(AstStatError* stat);
|
||||||
void visit(AstExpr* expr, ValueContext context);
|
void visit(AstExpr* expr, ValueContext context);
|
||||||
void visit(AstExprGroup* expr, ValueContext context);
|
void visit(AstExprGroup* expr, ValueContext context);
|
||||||
|
|
|
@ -205,7 +205,7 @@ struct TypeFunctionTableType
|
||||||
std::optional<TypeFunctionTypeId> metatable;
|
std::optional<TypeFunctionTypeId> metatable;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeFunctionClassType
|
struct TypeFunctionExternType
|
||||||
{
|
{
|
||||||
using Name = std::string;
|
using Name = std::string;
|
||||||
using Props = std::map<Name, TypeFunctionProperty>;
|
using Props = std::map<Name, TypeFunctionProperty>;
|
||||||
|
@ -222,7 +222,7 @@ struct TypeFunctionClassType
|
||||||
std::optional<TypeFunctionTypeId> readParent;
|
std::optional<TypeFunctionTypeId> readParent;
|
||||||
std::optional<TypeFunctionTypeId> writeParent;
|
std::optional<TypeFunctionTypeId> writeParent;
|
||||||
|
|
||||||
TypeId classTy;
|
TypeId externTy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeFunctionGenericType
|
struct TypeFunctionGenericType
|
||||||
|
@ -244,7 +244,7 @@ using TypeFunctionTypeVariant = Luau::Variant<
|
||||||
TypeFunctionNegationType,
|
TypeFunctionNegationType,
|
||||||
TypeFunctionFunctionType,
|
TypeFunctionFunctionType,
|
||||||
TypeFunctionTableType,
|
TypeFunctionTableType,
|
||||||
TypeFunctionClassType,
|
TypeFunctionExternType,
|
||||||
TypeFunctionGenericType>;
|
TypeFunctionGenericType>;
|
||||||
|
|
||||||
struct TypeFunctionType
|
struct TypeFunctionType
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct SingletonType;
|
||||||
struct FunctionType;
|
struct FunctionType;
|
||||||
struct TableType;
|
struct TableType;
|
||||||
struct MetatableType;
|
struct MetatableType;
|
||||||
struct ClassType;
|
struct ExternType;
|
||||||
struct AnyType;
|
struct AnyType;
|
||||||
struct UnionType;
|
struct UnionType;
|
||||||
struct IntersectionType;
|
struct IntersectionType;
|
||||||
|
|
|
@ -90,11 +90,11 @@ struct TypeChecker
|
||||||
ControlFlow check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatLocalFunction& function);
|
ControlFlow check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatLocalFunction& function);
|
||||||
ControlFlow check(const ScopePtr& scope, const AstStatTypeAlias& typealias);
|
ControlFlow check(const ScopePtr& scope, const AstStatTypeAlias& typealias);
|
||||||
ControlFlow check(const ScopePtr& scope, const AstStatTypeFunction& typefunction);
|
ControlFlow check(const ScopePtr& scope, const AstStatTypeFunction& typefunction);
|
||||||
ControlFlow check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
|
ControlFlow check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
|
||||||
ControlFlow check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
|
ControlFlow check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
|
||||||
|
|
||||||
void prototype(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0);
|
void prototype(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0);
|
||||||
void prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
|
void prototype(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
|
||||||
|
|
||||||
ControlFlow checkBlock(const ScopePtr& scope, const AstStatBlock& statement);
|
ControlFlow checkBlock(const ScopePtr& scope, const AstStatBlock& statement);
|
||||||
ControlFlow checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& statement);
|
ControlFlow checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& statement);
|
||||||
|
@ -487,7 +487,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* A set of incorrect class definitions which is used to avoid a second-pass analysis.
|
* A set of incorrect class definitions which is used to avoid a second-pass analysis.
|
||||||
*/
|
*/
|
||||||
DenseHashSet<const AstStatDeclareClass*> incorrectClassDefinitions{nullptr};
|
DenseHashSet<const AstStatDeclareExternType*> incorrectExternTypeDefinitions{nullptr};
|
||||||
|
|
||||||
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
|
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
|
||||||
};
|
};
|
||||||
|
|
|
@ -140,7 +140,7 @@ private:
|
||||||
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false, const LiteralProperties* aliasableMap = nullptr);
|
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false, const LiteralProperties* aliasableMap = nullptr);
|
||||||
void tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed);
|
void tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed);
|
||||||
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
||||||
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
void tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed);
|
||||||
void tryUnifyNegations(TypeId subTy, TypeId superTy);
|
void tryUnifyNegations(TypeId subTy, TypeId superTy);
|
||||||
|
|
||||||
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
|
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
|
||||||
|
|
|
@ -126,7 +126,7 @@ struct GenericTypeVisitor
|
||||||
{
|
{
|
||||||
return visit(ty);
|
return visit(ty);
|
||||||
}
|
}
|
||||||
virtual bool visit(TypeId ty, const ClassType& ctv)
|
virtual bool visit(TypeId ty, const ExternType& etv)
|
||||||
{
|
{
|
||||||
return visit(ty);
|
return visit(ty);
|
||||||
}
|
}
|
||||||
|
@ -313,11 +313,11 @@ struct GenericTypeVisitor
|
||||||
traverse(mtv->metatable);
|
traverse(mtv->metatable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto ctv = get<ClassType>(ty))
|
else if (auto etv = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
if (visit(ty, *ctv))
|
if (visit(ty, *etv))
|
||||||
{
|
{
|
||||||
for (const auto& [name, prop] : ctv->props)
|
for (const auto& [name, prop] : etv->props)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
|
@ -335,16 +335,16 @@ struct GenericTypeVisitor
|
||||||
traverse(prop.type());
|
traverse(prop.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctv->parent)
|
if (etv->parent)
|
||||||
traverse(*ctv->parent);
|
traverse(*etv->parent);
|
||||||
|
|
||||||
if (ctv->metatable)
|
if (etv->metatable)
|
||||||
traverse(*ctv->metatable);
|
traverse(*etv->metatable);
|
||||||
|
|
||||||
if (ctv->indexer)
|
if (etv->indexer)
|
||||||
{
|
{
|
||||||
traverse(ctv->indexer->indexType);
|
traverse(etv->indexer->indexType);
|
||||||
traverse(ctv->indexer->indexResultType);
|
traverse(etv->indexer->indexResultType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ struct GenericTypeVisitor
|
||||||
traverse(unwrapped);
|
traverse(unwrapped);
|
||||||
|
|
||||||
// Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose.
|
// Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose.
|
||||||
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType
|
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ExternType
|
||||||
// that doesn't need to be expanded.
|
// that doesn't need to be expanded.
|
||||||
}
|
}
|
||||||
else if (auto stv = get<SingletonType>(ty))
|
else if (auto stv = get<SingletonType>(ty))
|
||||||
|
|
|
@ -88,7 +88,7 @@ TypePackId Anyification::clean(TypePackId tp)
|
||||||
|
|
||||||
bool Anyification::ignoreChildren(TypeId ty)
|
bool Anyification::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
if (get<ClassType>(ty))
|
if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return ty->persistent;
|
return ty->persistent;
|
||||||
|
|
|
@ -31,7 +31,7 @@ bool ApplyTypeFunction::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
if (get<GenericType>(ty))
|
if (get<GenericType>(ty))
|
||||||
return true;
|
return true;
|
||||||
else if (get<ClassType>(ty))
|
else if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -431,8 +433,16 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
if (node->self)
|
if (node->self)
|
||||||
PROP(self);
|
PROP(self);
|
||||||
PROP(args);
|
PROP(args);
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (node->returnAnnotation)
|
if (node->returnAnnotation)
|
||||||
PROP(returnAnnotation);
|
PROP(returnAnnotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node->returnAnnotation_DEPRECATED)
|
||||||
|
write("returnAnnotation", node->returnAnnotation_DEPRECATED);
|
||||||
|
}
|
||||||
PROP(vararg);
|
PROP(vararg);
|
||||||
PROP(varargLocation);
|
PROP(varargLocation);
|
||||||
if (node->varargAnnotation)
|
if (node->varargAnnotation)
|
||||||
|
@ -902,7 +912,10 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
PROP(paramNames);
|
PROP(paramNames);
|
||||||
PROP(vararg);
|
PROP(vararg);
|
||||||
PROP(varargLocation);
|
PROP(varargLocation);
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
PROP(retTypes);
|
PROP(retTypes);
|
||||||
|
else
|
||||||
|
write("retTypes", node->retTypes_DEPRECATED);
|
||||||
PROP(generics);
|
PROP(generics);
|
||||||
PROP(genericPacks);
|
PROP(genericPacks);
|
||||||
}
|
}
|
||||||
|
@ -923,7 +936,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const AstDeclaredClassProp& prop)
|
void write(const AstDeclaredExternTypeProperty& prop)
|
||||||
{
|
{
|
||||||
writeRaw("{");
|
writeRaw("{");
|
||||||
bool c = pushComma();
|
bool c = pushComma();
|
||||||
|
@ -936,7 +949,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
writeRaw("}");
|
writeRaw("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(class AstStatDeclareClass* node)
|
void write(class AstStatDeclareExternType* node)
|
||||||
{
|
{
|
||||||
writeNode(
|
writeNode(
|
||||||
node,
|
node,
|
||||||
|
@ -1048,7 +1061,10 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
PROP(genericPacks);
|
PROP(genericPacks);
|
||||||
PROP(argTypes);
|
PROP(argTypes);
|
||||||
PROP(argNames);
|
PROP(argNames);
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
PROP(returnTypes);
|
PROP(returnTypes);
|
||||||
|
else
|
||||||
|
write("returnTypes", node->returnTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1429,7 +1445,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(class AstStatDeclareClass* node) override
|
bool visit(class AstStatDeclareExternType* node) override
|
||||||
{
|
{
|
||||||
write(node);
|
write(node);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -574,11 +574,11 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
||||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
else if (const ExternType* etv = get<ExternType>(parentTy))
|
||||||
{
|
{
|
||||||
while (ctv)
|
while (etv)
|
||||||
{
|
{
|
||||||
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
if (auto propIt = etv->props.find(indexName->index.value); propIt != etv->props.end())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
|
@ -590,7 +590,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
||||||
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
|
etv = etv->parent ? Luau::get<Luau::ExternType>(*etv->parent) : nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
||||||
|
|
|
@ -24,14 +24,10 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -311,7 +307,7 @@ static void autocompleteProps(
|
||||||
const std::vector<AstNode*>& nodes,
|
const std::vector<AstNode*>& nodes,
|
||||||
AutocompleteEntryMap& result,
|
AutocompleteEntryMap& result,
|
||||||
std::unordered_set<TypeId>& seen,
|
std::unordered_set<TypeId>& seen,
|
||||||
std::optional<const ClassType*> containingClass = std::nullopt
|
std::optional<const ExternType*> containingExternType = std::nullopt
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
rootTy = follow(rootTy);
|
rootTy = follow(rootTy);
|
||||||
|
@ -334,8 +330,8 @@ static void autocompleteProps(
|
||||||
if (calledWithSelf == ftv->hasSelf)
|
if (calledWithSelf == ftv->hasSelf)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Calls on classes require strict match between how function is declared and how it's called
|
// Calls on extern types require strict match between how function is declared and how it's called
|
||||||
if (get<ClassType>(rootTy))
|
if (get<ExternType>(rootTy))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
|
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
|
||||||
|
@ -368,7 +364,7 @@ static void autocompleteProps(
|
||||||
return calledWithSelf;
|
return calledWithSelf;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fillProps = [&](const ClassType::Props& props)
|
auto fillProps = [&](const ExternType::Props& props)
|
||||||
{
|
{
|
||||||
for (const auto& [name, prop] : props)
|
for (const auto& [name, prop] : props)
|
||||||
{
|
{
|
||||||
|
@ -401,7 +397,7 @@ static void autocompleteProps(
|
||||||
prop.deprecated,
|
prop.deprecated,
|
||||||
isWrongIndexer(type),
|
isWrongIndexer(type),
|
||||||
typeCorrect,
|
typeCorrect,
|
||||||
containingClass,
|
containingExternType,
|
||||||
&prop,
|
&prop,
|
||||||
prop.documentationSymbol,
|
prop.documentationSymbol,
|
||||||
{},
|
{},
|
||||||
|
@ -432,12 +428,12 @@ static void autocompleteProps(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (auto cls = get<ClassType>(ty))
|
if (auto cls = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
containingClass = containingClass.value_or(cls);
|
containingExternType = containingExternType.value_or(cls);
|
||||||
fillProps(cls->props);
|
fillProps(cls->props);
|
||||||
if (cls->parent)
|
if (cls->parent)
|
||||||
autocompleteProps(module, typeArena, builtinTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingClass);
|
autocompleteProps(module, typeArena, builtinTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingExternType);
|
||||||
}
|
}
|
||||||
else if (auto tbl = get<TableType>(ty))
|
else if (auto tbl = get<TableType>(ty))
|
||||||
fillProps(tbl->props);
|
fillProps(tbl->props);
|
||||||
|
@ -491,7 +487,7 @@ static void autocompleteProps(
|
||||||
// If we don't do this, and we have the misfortune of receiving a
|
// If we don't do this, and we have the misfortune of receiving a
|
||||||
// recursive union like:
|
// recursive union like:
|
||||||
//
|
//
|
||||||
// t1 where t1 = t1 | Class
|
// t1 where t1 = t1 | ExternType
|
||||||
//
|
//
|
||||||
// Then we are on a one way journey to a stack overflow.
|
// Then we are on a one way journey to a stack overflow.
|
||||||
if (FFlag::LuauAutocompleteUnionCopyPreviousSeen)
|
if (FFlag::LuauAutocompleteUnionCopyPreviousSeen)
|
||||||
|
@ -591,7 +587,7 @@ AutocompleteEntryMap autocompleteProps(
|
||||||
AutocompleteEntryMap autocompleteModuleTypes(const Module& module, const ScopePtr& scopeAtPosition, Position position, std::string_view moduleName)
|
AutocompleteEntryMap autocompleteModuleTypes(const Module& module, const ScopePtr& scopeAtPosition, Position position, std::string_view moduleName)
|
||||||
{
|
{
|
||||||
AutocompleteEntryMap result;
|
AutocompleteEntryMap result;
|
||||||
ScopePtr startScope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
ScopePtr startScope = scopeAtPosition;
|
||||||
for (ScopePtr& scope = startScope; scope; scope = scope->parent)
|
for (ScopePtr& scope = startScope; scope; scope = scope->parent)
|
||||||
{
|
{
|
||||||
if (auto it = scope->importedTypeBindings.find(std::string(moduleName)); it != scope->importedTypeBindings.end())
|
if (auto it = scope->importedTypeBindings.find(std::string(moduleName)); it != scope->importedTypeBindings.end())
|
||||||
|
@ -703,6 +699,30 @@ static std::optional<TypeId> findTypeElementAt(const AstTypeList& astTypeList, T
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else if (const auto variadic = astTypePack->as<AstTypePackVariadic>())
|
||||||
|
{
|
||||||
|
if (variadic->location.containsClosed(position))
|
||||||
|
{
|
||||||
|
auto [_, tail] = flatten(tp);
|
||||||
|
|
||||||
|
if (tail)
|
||||||
|
{
|
||||||
|
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*tail)))
|
||||||
|
return findTypeElementAt(variadic->variadicType, vtp->ty, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Position position)
|
static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Position position)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -723,9 +743,17 @@ static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Posi
|
||||||
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
|
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
|
||||||
return element;
|
return element;
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (auto element = findTypeElementAt(type->returnTypes_DEPRECATED, ftv->retTypes, position))
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// It's possible to walk through other types like intrsection and unions if we find value in doing that
|
// It's possible to walk through other types like intrsection and unions if we find value in doing that
|
||||||
return {};
|
return {};
|
||||||
|
@ -733,7 +761,7 @@ static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Posi
|
||||||
|
|
||||||
std::optional<TypeId> getLocalTypeInScopeAt(const Module& module, const ScopePtr& scopeAtPosition, Position position, AstLocal* local)
|
std::optional<TypeId> getLocalTypeInScopeAt(const Module& module, const ScopePtr& scopeAtPosition, Position position, AstLocal* local)
|
||||||
{
|
{
|
||||||
if (ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position))
|
if (ScopePtr scope = scopeAtPosition)
|
||||||
{
|
{
|
||||||
for (const auto& [name, binding] : scope->bindings)
|
for (const auto& [name, binding] : scope->bindings)
|
||||||
{
|
{
|
||||||
|
@ -875,7 +903,7 @@ AutocompleteEntryMap autocompleteTypeNames(
|
||||||
{
|
{
|
||||||
AutocompleteEntryMap result;
|
AutocompleteEntryMap result;
|
||||||
|
|
||||||
ScopePtr startScope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
ScopePtr startScope = scopeAtPosition;
|
||||||
|
|
||||||
for (ScopePtr scope = startScope; scope; scope = scope->parent)
|
for (ScopePtr scope = startScope; scope; scope = scope->parent)
|
||||||
{
|
{
|
||||||
|
@ -1054,12 +1082,16 @@ AutocompleteEntryMap autocompleteTypeNames(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (!node->returnAnnotation)
|
if (!node->returnAnnotation)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
for (size_t i = 0; i < node->returnAnnotation->types.size; i++)
|
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
|
||||||
{
|
{
|
||||||
AstType* ret = node->returnAnnotation->types.data[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 (ret->location.containsClosed(position))
|
||||||
{
|
{
|
||||||
|
@ -1074,7 +1106,7 @@ AutocompleteEntryMap autocompleteTypeNames(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AstTypePack* retTp = node->returnAnnotation->tailType)
|
if (AstTypePack* retTp = typePack->typeList.tailType)
|
||||||
{
|
{
|
||||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||||
{
|
{
|
||||||
|
@ -1089,6 +1121,56 @@ 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, 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];
|
||||||
|
|
||||||
|
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 = node->returnAnnotation_DEPRECATED->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, startScope, topType, *ty, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1208,7 +1290,7 @@ static AutocompleteEntryMap autocompleteStatement(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// This is inefficient. :(
|
// This is inefficient. :(
|
||||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
ScopePtr scope = scopeAtPosition;
|
||||||
|
|
||||||
AutocompleteEntryMap result;
|
AutocompleteEntryMap result;
|
||||||
|
|
||||||
|
@ -1386,7 +1468,7 @@ static AutocompleteContext autocompleteExpression(
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This is inefficient. :(
|
// This is inefficient. :(
|
||||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
ScopePtr scope = scopeAtPosition;
|
||||||
|
|
||||||
while (scope)
|
while (scope)
|
||||||
{
|
{
|
||||||
|
@ -1455,7 +1537,7 @@ static AutocompleteResult autocompleteExpression(
|
||||||
return {result, ancestry, context};
|
return {result, ancestry, context};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<const ClassType*> getMethodContainingClass(const ModulePtr& module, AstExpr* funcExpr)
|
static std::optional<const ExternType*> getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr)
|
||||||
{
|
{
|
||||||
AstExpr* parentExpr = nullptr;
|
AstExpr* parentExpr = nullptr;
|
||||||
if (auto indexName = funcExpr->as<AstExprIndexName>())
|
if (auto indexName = funcExpr->as<AstExprIndexName>())
|
||||||
|
@ -1479,14 +1561,14 @@ static std::optional<const ClassType*> getMethodContainingClass(const ModulePtr&
|
||||||
|
|
||||||
Luau::TypeId parentType = Luau::follow(*parentIt);
|
Luau::TypeId parentType = Luau::follow(*parentIt);
|
||||||
|
|
||||||
if (auto parentClass = Luau::get<ClassType>(parentType))
|
if (auto parentExternType = Luau::get<ExternType>(parentType))
|
||||||
{
|
{
|
||||||
return parentClass;
|
return parentExternType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto parentUnion = Luau::get<UnionType>(parentType))
|
if (auto parentUnion = Luau::get<UnionType>(parentType))
|
||||||
{
|
{
|
||||||
return returnFirstNonnullOptionOfType<ClassType>(parentUnion);
|
return returnFirstNonnullOptionOfType<ExternType>(parentUnion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -1544,10 +1626,7 @@ static std::optional<AutocompleteEntryMap> convertRequireSuggestionsToAutocomple
|
||||||
{
|
{
|
||||||
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
|
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
|
||||||
entry.insertText = std::move(suggestion.fullPath);
|
entry.insertText = std::move(suggestion.fullPath);
|
||||||
if (FFlag::LuauExposeRequireByStringAutocomplete)
|
|
||||||
{
|
|
||||||
entry.tags = std::move(suggestion.tags);
|
entry.tags = std::move(suggestion.tags);
|
||||||
}
|
|
||||||
result[std::move(suggestion.label)] = std::move(entry);
|
result[std::move(suggestion.label)] = std::move(entry);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1608,7 +1687,7 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
|
||||||
{
|
{
|
||||||
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
|
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
|
||||||
}
|
}
|
||||||
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString))
|
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingExternType(module, candidate->func), candidateString))
|
||||||
{
|
{
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1776,7 +1855,7 @@ static std::optional<AutocompleteEntry> makeAnonymousAutofilled(
|
||||||
if (!type)
|
if (!type)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
const ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(*module, position);
|
const ScopePtr scope = scopeAtPosition;
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -344,7 +344,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
|
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
|
||||||
{
|
{
|
||||||
TypeId vectorTy = it->second.type;
|
TypeId vectorTy = it->second.type;
|
||||||
ClassType* vectorCls = getMutable<ClassType>(vectorTy);
|
ExternType* vectorCls = getMutable<ExternType>(vectorTy);
|
||||||
|
|
||||||
vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||||
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);
|
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);
|
||||||
|
@ -705,6 +705,14 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CLI-150726: The block below effectively constructs a type pack and then type checks it by going parameter-by-parameter.
|
||||||
|
// This does _not_ handle cases like:
|
||||||
|
//
|
||||||
|
// local foo : () -> (...string) = (nil :: any)
|
||||||
|
// print(string.format("%s %d %s", foo()))
|
||||||
|
//
|
||||||
|
// ... which should be disallowed.
|
||||||
|
|
||||||
std::vector<TypeId> expected = parseFormatString(context.builtinTypes, fmt->value.data, fmt->value.size);
|
std::vector<TypeId> expected = parseFormatString(context.builtinTypes, fmt->value.data, fmt->value.size);
|
||||||
const auto& [params, tail] = flatten(context.arguments);
|
const auto& [params, tail] = flatten(context.arguments);
|
||||||
|
|
||||||
|
@ -716,7 +724,9 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
{
|
{
|
||||||
TypeId actualTy = params[i + paramOffset];
|
TypeId actualTy = params[i + paramOffset];
|
||||||
TypeId expectedTy = expected[i];
|
TypeId expectedTy = expected[i];
|
||||||
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
Location location = FFlag::LuauFormatUseLastPosition
|
||||||
|
? context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location
|
||||||
|
: context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||||
// use subtyping instead here
|
// use subtyping instead here
|
||||||
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
||||||
|
|
||||||
|
@ -1529,7 +1539,6 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
||||||
tableType->scope = context.constraint->scope.get();
|
tableType->scope = context.constraint->scope.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
trackInteriorFreeType(context.constraint->scope.get(), resultType);
|
trackInteriorFreeType(context.constraint->scope.get(), resultType);
|
||||||
|
|
||||||
TypePackId clonedTypePack = arena->addTypePack({resultType});
|
TypePackId clonedTypePack = arena->addTypePack({resultType});
|
||||||
|
|
|
@ -355,7 +355,7 @@ private:
|
||||||
t->metatable = shallowClone(t->metatable);
|
t->metatable = shallowClone(t->metatable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cloneChildren(ClassType* t)
|
void cloneChildren(ExternType* t)
|
||||||
{
|
{
|
||||||
for (auto& [_, p] : t->props)
|
for (auto& [_, p] : t->props)
|
||||||
p = shallowClone(p);
|
p = shallowClone(p);
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
|
|
||||||
DenseHashSet<TypeId>* result;
|
DenseHashSet<TypeId>* result;
|
||||||
|
|
||||||
ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
explicit ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
||||||
: result(result)
|
: result(result)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
// ClassTypes never contain free types.
|
// ExternTypes never contain free types.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,15 +34,12 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||||
|
|
||||||
|
@ -50,9 +47,11 @@ LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
||||||
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -157,7 +156,7 @@ struct HasFreeType : TypeOnceVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +217,32 @@ ConstraintGenerator::ConstraintGenerator(
|
||||||
LUAU_ASSERT(module);
|
LUAU_ASSERT(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConstraintSet ConstraintGenerator::run(AstStatBlock* block)
|
||||||
|
{
|
||||||
|
visitModuleRoot(block);
|
||||||
|
|
||||||
|
return ConstraintSet{
|
||||||
|
NotNull{rootScope},
|
||||||
|
std::move(constraints),
|
||||||
|
std::move(freeTypes),
|
||||||
|
std::move(scopeToFunction),
|
||||||
|
std::move(errors)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstraintSet ConstraintGenerator::runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block)
|
||||||
|
{
|
||||||
|
visitFragmentRoot(resumeScope, block);
|
||||||
|
|
||||||
|
return ConstraintSet{
|
||||||
|
NotNull{rootScope},
|
||||||
|
std::move(constraints),
|
||||||
|
std::move(freeTypes),
|
||||||
|
std::move(scopeToFunction),
|
||||||
|
std::move(errors)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
{
|
{
|
||||||
LUAU_TIMETRACE_SCOPE("ConstraintGenerator::visitModuleRoot", "Typechecking");
|
LUAU_TIMETRACE_SCOPE("ConstraintGenerator::visitModuleRoot", "Typechecking");
|
||||||
|
@ -263,8 +288,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
result,
|
result,
|
||||||
moduleFnTy,
|
moduleFnTy,
|
||||||
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
std::vector<TypeId>{},
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -273,7 +297,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
else
|
||||||
scope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
scope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
||||||
|
@ -359,17 +383,17 @@ TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
||||||
{
|
{
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
||||||
interiorFreeTypes.back().types.push_back(ft);
|
interiorFreeTypes.back().types.push_back(ft);
|
||||||
return ft;
|
|
||||||
}
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
freeTypes.insert(ft);
|
||||||
{
|
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
|
||||||
DEPRECATED_interiorTypes.back().push_back(ft);
|
|
||||||
return ft;
|
return ft;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Luau::freshType(arena, builtinTypes, scope.get());
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
||||||
|
DEPRECATED_interiorTypes.back().push_back(ft);
|
||||||
|
return ft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,16 +602,9 @@ void ConstraintGenerator::computeRefinement(
|
||||||
|
|
||||||
// When the top-level expression is `t[x]`, we want to refine it into `nil`, not `never`.
|
// When the top-level expression is `t[x]`, we want to refine it into `nil`, not `never`.
|
||||||
LUAU_ASSERT(refis->get(proposition->key->def));
|
LUAU_ASSERT(refis->get(proposition->key->def));
|
||||||
if (FFlag::LuauDoNotLeakNilInRefinement)
|
|
||||||
{
|
|
||||||
refis->get(proposition->key->def)->shouldAppendNilType =
|
refis->get(proposition->key->def)->shouldAppendNilType =
|
||||||
(sense || !eq) && containsSubscriptedDefinition(proposition->key->def) && !proposition->implicitFromCall;
|
(sense || !eq) && containsSubscriptedDefinition(proposition->key->def) && !proposition->implicitFromCall;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
refis->get(proposition->key->def)->shouldAppendNilType = (sense || !eq) && containsSubscriptedDefinition(proposition->key->def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -633,7 +650,7 @@ struct FindSimplificationBlockers : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId, const ClassType&) override
|
bool visit(TypeId, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -858,7 +875,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
aliasDefinitionLocations[function->name.value] = function->location;
|
aliasDefinitionLocations[function->name.value] = function->location;
|
||||||
}
|
}
|
||||||
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
|
else if (auto classDeclaration = stat->as<AstStatDeclareExternType>())
|
||||||
{
|
{
|
||||||
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
||||||
{
|
{
|
||||||
|
@ -1052,7 +1069,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat)
|
||||||
return visit(scope, s);
|
return visit(scope, s);
|
||||||
else if (auto s = stat->as<AstStatDeclareFunction>())
|
else if (auto s = stat->as<AstStatDeclareFunction>())
|
||||||
return visit(scope, s);
|
return visit(scope, s);
|
||||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||||
return visit(scope, s);
|
return visit(scope, s);
|
||||||
else if (auto s = stat->as<AstStatError>())
|
else if (auto s = stat->as<AstStatError>())
|
||||||
return visit(scope, s);
|
return visit(scope, s);
|
||||||
|
@ -1273,11 +1290,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
|
||||||
|
|
||||||
for (AstLocal* var : forIn->vars)
|
for (AstLocal* var : forIn->vars)
|
||||||
{
|
{
|
||||||
TypeId assignee = arena->addType(BlockedType{});
|
|
||||||
variableTypes.push_back(assignee);
|
|
||||||
|
|
||||||
TypeId loopVar = arena->addType(BlockedType{});
|
TypeId loopVar = arena->addType(BlockedType{});
|
||||||
localTypes[loopVar].insert(assignee);
|
variableTypes.push_back(loopVar);
|
||||||
|
|
||||||
if (var->annotation)
|
if (var->annotation)
|
||||||
{
|
{
|
||||||
|
@ -1296,6 +1310,23 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
|
||||||
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes}
|
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (FFlag::LuauAddCallConstraintForIterableFunctions)
|
||||||
|
{
|
||||||
|
// Add an intersection ReduceConstraint for the key variable to denote that it can't be nil
|
||||||
|
AstLocal* keyVar = *forIn->vars.begin();
|
||||||
|
const DefId keyDef = dfg->getDef(keyVar);
|
||||||
|
const TypeId loopVar = loopScope->lvalueTypes[keyDef];
|
||||||
|
|
||||||
|
const TypeId intersectionTy =
|
||||||
|
createTypeFunctionInstance(builtinTypeFunctions().intersectFunc, {loopVar, builtinTypes->notNilType}, {}, loopScope, keyVar->location);
|
||||||
|
|
||||||
|
loopScope->bindings[keyVar] = Binding{intersectionTy, keyVar->location};
|
||||||
|
loopScope->lvalueTypes[keyDef] = intersectionTy;
|
||||||
|
|
||||||
|
auto c = addConstraint(loopScope, keyVar->location, ReduceConstraint{intersectionTy});
|
||||||
|
c->dependencies.push_back(iterable);
|
||||||
|
}
|
||||||
|
|
||||||
for (TypeId var : variableTypes)
|
for (TypeId var : variableTypes)
|
||||||
{
|
{
|
||||||
auto bt = getMutable<BlockedType>(var);
|
auto bt = getMutable<BlockedType>(var);
|
||||||
|
@ -1779,7 +1810,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
generalizedTy,
|
generalizedTy,
|
||||||
sig.signature,
|
sig.signature,
|
||||||
FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(DEPRECATED_interiorTypes.back())
|
std::vector<TypeId>{},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1788,7 +1819,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
else
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
|
@ -1852,71 +1883,71 @@ static bool isMetamethod(const Name& name)
|
||||||
name == "__idiv";
|
name == "__idiv";
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExternType* declaredExternType)
|
||||||
{
|
{
|
||||||
// If a class with the same name was already defined, we skip over
|
// If a class with the same name was already defined, we skip over
|
||||||
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
|
auto bindingIt = scope->exportedTypeBindings.find(declaredExternType->name.value);
|
||||||
if (bindingIt == scope->exportedTypeBindings.end())
|
if (bindingIt == scope->exportedTypeBindings.end())
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
|
|
||||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
std::optional<TypeId> superTy = std::make_optional(builtinTypes->externType);
|
||||||
if (declaredClass->superName)
|
if (declaredExternType->superName)
|
||||||
{
|
{
|
||||||
Name superName = Name(declaredClass->superName->value);
|
Name superName = Name(declaredExternType->superName->value);
|
||||||
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
||||||
|
|
||||||
if (!lookupType)
|
if (!lookupType)
|
||||||
{
|
{
|
||||||
reportError(declaredClass->location, UnknownSymbol{superName, UnknownSymbol::Type});
|
reportError(declaredExternType->location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
// We don't have generic extern types, so this assertion _should_ never be hit.
|
||||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||||
superTy = follow(lookupType->type);
|
superTy = follow(lookupType->type);
|
||||||
|
|
||||||
if (!get<ClassType>(follow(*superTy)))
|
if (!get<ExternType>(follow(*superTy)))
|
||||||
{
|
{
|
||||||
reportError(
|
reportError(
|
||||||
declaredClass->location,
|
declaredExternType->location,
|
||||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass->name.value)}
|
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType->name.value)}
|
||||||
);
|
);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Name className(declaredClass->name.value);
|
Name className(declaredExternType->name.value);
|
||||||
|
|
||||||
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredClass->location));
|
TypeId externTy = arena->addType(ExternType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredExternType->location));
|
||||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
ExternType* etv = getMutable<ExternType>(externTy);
|
||||||
|
|
||||||
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
||||||
TableType* metatable = getMutable<TableType>(metaTy);
|
TableType* metatable = getMutable<TableType>(metaTy);
|
||||||
|
|
||||||
ctv->metatable = metaTy;
|
etv->metatable = metaTy;
|
||||||
|
|
||||||
TypeId classBindTy = bindingIt->second.type;
|
TypeId classBindTy = bindingIt->second.type;
|
||||||
emplaceType<BoundType>(asMutable(classBindTy), classTy);
|
emplaceType<BoundType>(asMutable(classBindTy), externTy);
|
||||||
|
|
||||||
if (declaredClass->indexer)
|
if (declaredExternType->indexer)
|
||||||
{
|
{
|
||||||
RecursionCounter counter{&recursionCount};
|
RecursionCounter counter{&recursionCount};
|
||||||
|
|
||||||
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
||||||
{
|
{
|
||||||
reportCodeTooComplex(declaredClass->indexer->location);
|
reportCodeTooComplex(declaredExternType->indexer->location);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctv->indexer = TableIndexer{
|
etv->indexer = TableIndexer{
|
||||||
resolveType(scope, declaredClass->indexer->indexType, /* inTypeArguments */ false),
|
resolveType(scope, declaredExternType->indexer->indexType, /* inTypeArguments */ false),
|
||||||
resolveType(scope, declaredClass->indexer->resultType, /* inTypeArguments */ false),
|
resolveType(scope, declaredExternType->indexer->resultType, /* inTypeArguments */ false),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const AstDeclaredClassProp& prop : declaredClass->props)
|
for (const AstDeclaredExternTypeProperty& prop : declaredExternType->props)
|
||||||
{
|
{
|
||||||
Name propName(prop.name.value);
|
Name propName(prop.name.value);
|
||||||
TypeId propTy = resolveType(scope, prop.ty, /* inTypeArguments */ false);
|
TypeId propTy = resolveType(scope, prop.ty, /* inTypeArguments */ false);
|
||||||
|
@ -1930,7 +1961,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
||||||
{
|
{
|
||||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||||
ftv->argTypes = addTypePack({classTy}, ftv->argTypes);
|
ftv->argTypes = addTypePack({externTy}, ftv->argTypes);
|
||||||
|
|
||||||
ftv->hasSelf = true;
|
ftv->hasSelf = true;
|
||||||
|
|
||||||
|
@ -1945,7 +1976,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TableType::Props& props = assignToMetatable ? metatable->props : ctv->props;
|
TableType::Props& props = assignToMetatable ? metatable->props : etv->props;
|
||||||
|
|
||||||
if (props.count(propName) == 0)
|
if (props.count(propName) == 0)
|
||||||
{
|
{
|
||||||
|
@ -1976,7 +2007,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reportError(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
reportError(declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2008,7 +2039,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
||||||
funScope = childScope(global, scope);
|
funScope = childScope(global, scope);
|
||||||
|
|
||||||
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
|
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
|
||||||
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false);
|
TypePackId retPack = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false)
|
||||||
|
: resolveTypePack(funScope, global->retTypes_DEPRECATED, /* inTypeArguments */ false);
|
||||||
|
|
||||||
FunctionDefinition defn;
|
FunctionDefinition defn;
|
||||||
|
|
||||||
|
@ -2658,8 +2690,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
generalizedTy,
|
generalizedTy,
|
||||||
sig.signature,
|
sig.signature,
|
||||||
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
std::vector<TypeId>{},
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2673,9 +2704,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
}
|
}
|
||||||
|
@ -2963,7 +2992,7 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
|
||||||
else if (typeguard->type == "userdata")
|
else if (typeguard->type == "userdata")
|
||||||
{
|
{
|
||||||
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
||||||
discriminantTy = builtinTypes->classType;
|
discriminantTy = builtinTypes->externType;
|
||||||
}
|
}
|
||||||
else if (!typeguard->isTypeof && typeguard->type == "vector")
|
else if (!typeguard->isTypeof && typeguard->type == "vector")
|
||||||
discriminantTy = builtinTypes->neverType; // TODO: figure out a way to deal with this quirky type
|
discriminantTy = builtinTypes->neverType; // TODO: figure out a way to deal with this quirky type
|
||||||
|
@ -2973,8 +3002,8 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
|
||||||
{
|
{
|
||||||
TypeId ty = follow(typeFun->type);
|
TypeId ty = follow(typeFun->type);
|
||||||
|
|
||||||
// We're only interested in the root class of any classes.
|
// We're only interested in the root type of any extern type.
|
||||||
if (auto ctv = get<ClassType>(ty); ctv && (ctv->parent == builtinTypes->classType || hasTag(ty, kTypeofRootTag)))
|
if (auto etv = get<ExternType>(ty); etv && (etv->parent == builtinTypes->externType || hasTag(ty, kTypeofRootTag)))
|
||||||
discriminantTy = ty;
|
discriminantTy = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3381,14 +3410,29 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
bodyScope->varargPack = std::nullopt;
|
bodyScope->varargPack = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
genericTypes = argTypes;
|
||||||
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(nullptr != varargPack);
|
LUAU_ASSERT(nullptr != varargPack);
|
||||||
|
|
||||||
// If there is both an annotation and an expected type, the annotation wins.
|
// If there is both an annotation and an expected type, the annotation wins.
|
||||||
// Type checking will sort out any discrepancies later.
|
// Type checking will sort out any discrepancies later.
|
||||||
if (fn->returnAnnotation)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation)
|
||||||
{
|
{
|
||||||
TypePackId annotatedRetType =
|
TypePackId annotatedRetType =
|
||||||
resolveTypePack(signatureScope, *fn->returnAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
resolveTypePack(signatureScope, fn->returnAnnotation, /* 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 (!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
|
// We bind the annotated type directly here so that, when we need to
|
||||||
// generate constraints for return types, we have a guarantee that we
|
// generate constraints for return types, we have a guarantee that we
|
||||||
// know the annotated return type already, if one was provided.
|
// know the annotated return type already, if one was provided.
|
||||||
|
@ -3639,8 +3683,16 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
||||||
AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes};
|
AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes};
|
||||||
TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh);
|
TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh);
|
||||||
|
|
||||||
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes};
|
TypePackId returnTypes;
|
||||||
TypePackId returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes_DEPRECATED};
|
||||||
|
returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: FunctionType needs a pointer to the scope so that we know
|
// TODO: FunctionType needs a pointer to the scope so that we know
|
||||||
// how to quantify/instantiate it.
|
// how to quantify/instantiate it.
|
||||||
|
@ -3724,13 +3776,9 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
{
|
{
|
||||||
if (unionAnnotation->types.size == 1)
|
if (unionAnnotation->types.size == 1)
|
||||||
return resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
return resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
for (AstType* part : unionAnnotation->types)
|
for (AstType* part : unionAnnotation->types)
|
||||||
{
|
{
|
||||||
|
@ -3758,13 +3806,9 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
{
|
{
|
||||||
if (intersectionAnnotation->types.size == 1)
|
if (intersectionAnnotation->types.size == 1)
|
||||||
return resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
return resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
for (AstType* part : intersectionAnnotation->types)
|
for (AstType* part : intersectionAnnotation->types)
|
||||||
{
|
{
|
||||||
|
@ -4294,15 +4338,4 @@ TypeId ConstraintGenerator::simplifyUnion(const ScopePtr& scope, Location locati
|
||||||
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result;
|
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints)
|
|
||||||
{
|
|
||||||
std::vector<NotNull<Constraint>> result;
|
|
||||||
result.reserve(constraints.size());
|
|
||||||
|
|
||||||
for (const auto& c : constraints)
|
|
||||||
result.emplace_back(c.get());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/OverloadResolution.h"
|
#include "Luau/OverloadResolution.h"
|
||||||
#include "Luau/Quantify.h"
|
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Simplify.h"
|
#include "Luau/Simplify.h"
|
||||||
#include "Luau/TableLiteralInference.h"
|
#include "Luau/TableLiteralInference.h"
|
||||||
|
@ -33,20 +32,20 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
||||||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static void dump(ConstraintSolver* cs, ToStringOptions& opts);
|
||||||
|
|
||||||
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
||||||
{
|
{
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
|
@ -276,26 +275,6 @@ size_t HashInstantiationSignature::operator()(const InstantiationSignature& sign
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
|
||||||
{
|
|
||||||
printf("constraints:\n");
|
|
||||||
for (NotNull<const Constraint> c : cs->unsolvedConstraints)
|
|
||||||
{
|
|
||||||
auto it = cs->blockedConstraints.find(c);
|
|
||||||
int blockCount = it == cs->blockedConstraints.end() ? 0 : int(it->second);
|
|
||||||
printf("\t%d\t%s\n", blockCount, toString(*c, opts).c_str());
|
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogSolverIncludeDependencies)
|
|
||||||
{
|
|
||||||
for (NotNull<Constraint> dep : c->dependencies)
|
|
||||||
{
|
|
||||||
if (std::find(cs->unsolvedConstraints.begin(), cs->unsolvedConstraints.end(), dep) != cs->unsolvedConstraints.end())
|
|
||||||
printf("\t\t|\t%s\n", toString(*dep, opts).c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InstantiationQueuer : TypeOnceVisitor
|
struct InstantiationQueuer : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
ConstraintSolver* solver;
|
ConstraintSolver* solver;
|
||||||
|
@ -321,12 +300,44 @@ struct InstantiationQueuer : TypeOnceVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType& ctv) override
|
bool visit(TypeId ty, const ExternType& etv) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConstraintSolver::ConstraintSolver(
|
||||||
|
NotNull<Normalizer> normalizer,
|
||||||
|
NotNull<Simplifier> simplifier,
|
||||||
|
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||||
|
ModuleName moduleName,
|
||||||
|
NotNull<ModuleResolver> moduleResolver,
|
||||||
|
std::vector<RequireCycle> requireCycles,
|
||||||
|
DcrLogger* logger,
|
||||||
|
NotNull<const DataFlowGraph> dfg,
|
||||||
|
TypeCheckLimits limits,
|
||||||
|
ConstraintSet constraintSet_
|
||||||
|
)
|
||||||
|
: arena(normalizer->arena)
|
||||||
|
, builtinTypes(normalizer->builtinTypes)
|
||||||
|
, normalizer(normalizer)
|
||||||
|
, simplifier(simplifier)
|
||||||
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
|
, constraintSet(std::move(constraintSet_))
|
||||||
|
, constraints(borrowConstraints(constraintSet.constraints))
|
||||||
|
, scopeToFunction(&constraintSet.scopeToFunction)
|
||||||
|
, rootScope(constraintSet.rootScope)
|
||||||
|
, currentModuleName(std::move(moduleName))
|
||||||
|
, dfg(dfg)
|
||||||
|
, moduleResolver(moduleResolver)
|
||||||
|
, requireCycles(std::move(requireCycles))
|
||||||
|
, logger(logger)
|
||||||
|
, limits(std::move(limits))
|
||||||
|
, opts{/*exhaustive*/ true}
|
||||||
|
{
|
||||||
|
initFreeTypeTracking();
|
||||||
|
}
|
||||||
|
|
||||||
ConstraintSolver::ConstraintSolver(
|
ConstraintSolver::ConstraintSolver(
|
||||||
NotNull<Normalizer> normalizer,
|
NotNull<Normalizer> normalizer,
|
||||||
NotNull<Simplifier> simplifier,
|
NotNull<Simplifier> simplifier,
|
||||||
|
@ -346,6 +357,7 @@ ConstraintSolver::ConstraintSolver(
|
||||||
, normalizer(normalizer)
|
, normalizer(normalizer)
|
||||||
, simplifier(simplifier)
|
, simplifier(simplifier)
|
||||||
, typeFunctionRuntime(typeFunctionRuntime)
|
, typeFunctionRuntime(typeFunctionRuntime)
|
||||||
|
, constraintSet{rootScope}
|
||||||
, constraints(std::move(constraints))
|
, constraints(std::move(constraints))
|
||||||
, scopeToFunction(scopeToFunction)
|
, scopeToFunction(scopeToFunction)
|
||||||
, rootScope(rootScope)
|
, rootScope(rootScope)
|
||||||
|
@ -355,33 +367,9 @@ ConstraintSolver::ConstraintSolver(
|
||||||
, requireCycles(std::move(requireCycles))
|
, requireCycles(std::move(requireCycles))
|
||||||
, logger(logger)
|
, logger(logger)
|
||||||
, limits(std::move(limits))
|
, limits(std::move(limits))
|
||||||
|
, opts{/*exhaustive*/ true}
|
||||||
{
|
{
|
||||||
opts.exhaustive = true;
|
initFreeTypeTracking();
|
||||||
|
|
||||||
for (NotNull<Constraint> c : this->constraints)
|
|
||||||
{
|
|
||||||
unsolvedConstraints.emplace_back(c);
|
|
||||||
|
|
||||||
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes();
|
|
||||||
for (auto ty : maybeMutatedTypesPerConstraint)
|
|
||||||
{
|
|
||||||
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
|
||||||
refCount += 1;
|
|
||||||
|
|
||||||
if (FFlag::DebugLuauGreedyGeneralization)
|
|
||||||
{
|
|
||||||
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, DenseHashSet<const Constraint*>{nullptr});
|
|
||||||
it->second.insert(c.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
|
||||||
|
|
||||||
|
|
||||||
for (NotNull<const Constraint> dep : c->dependencies)
|
|
||||||
{
|
|
||||||
block(dep, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::randomize(unsigned seed)
|
void ConstraintSolver::randomize(unsigned seed)
|
||||||
|
@ -426,6 +414,18 @@ void ConstraintSolver::run()
|
||||||
logger->captureInitialSolverState(rootScope, unsolvedConstraints);
|
logger->captureInitialSolverState(rootScope, unsolvedConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Free types that have no constraints at all can be generalized right away.
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
for (TypeId ty : constraintSet.freeTypes)
|
||||||
|
{
|
||||||
|
if (auto it = mutatedFreeTypeToConstraint.find(ty); it == mutatedFreeTypeToConstraint.end() || it->second.empty())
|
||||||
|
generalizeOneType(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintSet.freeTypes.clear();
|
||||||
|
|
||||||
auto runSolverPass = [&](bool force)
|
auto runSolverPass = [&](bool force)
|
||||||
{
|
{
|
||||||
bool progress = false;
|
bool progress = false;
|
||||||
|
@ -655,12 +655,40 @@ struct TypeSearcher : TypeVisitor
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ConstraintSolver::initFreeTypeTracking()
|
||||||
|
{
|
||||||
|
for (NotNull<Constraint> c : this->constraints)
|
||||||
|
{
|
||||||
|
unsolvedConstraints.emplace_back(c);
|
||||||
|
|
||||||
|
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes();
|
||||||
|
for (auto ty : maybeMutatedTypesPerConstraint)
|
||||||
|
{
|
||||||
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
|
refCount += 1;
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
|
||||||
|
it->second.insert(c.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
||||||
|
|
||||||
|
|
||||||
|
for (NotNull<const Constraint> dep : c->dependencies)
|
||||||
|
{
|
||||||
|
block(dep, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConstraintSolver::generalizeOneType(TypeId ty)
|
void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -858,8 +886,6 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType());
|
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
{
|
|
||||||
// We check if this member is initialized and then access it, but
|
// We check if this member is initialized and then access it, but
|
||||||
// clang-tidy doesn't understand this is safe.
|
// clang-tidy doesn't understand this is safe.
|
||||||
if (constraint->scope->interiorFreeTypes)
|
if (constraint->scope->interiorFreeTypes)
|
||||||
|
@ -907,13 +933,6 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (TypeId ty : c.interiorTypes)
|
|
||||||
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -989,15 +1008,11 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
{
|
|
||||||
trackInteriorFreeType(constraint->scope, keyTy);
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
trackInteriorFreeType(constraint->scope, valueTy);
|
trackInteriorFreeType(constraint->scope, valueTy);
|
||||||
}
|
|
||||||
TypeId tableTy =
|
TypeId tableTy =
|
||||||
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
|
||||||
trackInteriorFreeType(constraint->scope, tableTy);
|
trackInteriorFreeType(constraint->scope, tableTy);
|
||||||
|
|
||||||
unify(constraint, nextTy, tableTy);
|
unify(constraint, nextTy, tableTy);
|
||||||
|
@ -1322,8 +1337,6 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
||||||
if (!ty)
|
if (!ty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (FFlag::LuauSearchForRefineableType)
|
|
||||||
{
|
|
||||||
if (isBlocked(*ty))
|
if (isBlocked(*ty))
|
||||||
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
||||||
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
||||||
|
@ -1331,20 +1344,7 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
||||||
// We also need to unconditionally unblock these types, otherwise
|
// We also need to unconditionally unblock these types, otherwise
|
||||||
// you end up with funky looking "Blocked on *no-refine*."
|
// you end up with funky looking "Blocked on *no-refine*."
|
||||||
unblock(*ty, constraint->location);
|
unblock(*ty, constraint->location);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
// If the discriminant type has been transmuted, we need to unblock them.
|
|
||||||
if (!isBlocked(*ty))
|
|
||||||
{
|
|
||||||
unblock(*ty, constraint->location);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
|
||||||
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1457,9 +1457,9 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
if (ftv)
|
if (ftv)
|
||||||
{
|
{
|
||||||
if (ftv->magic)
|
if (ftv->magic && c.callSite)
|
||||||
{
|
{
|
||||||
usedMagic = ftv->magic->infer(MagicFunctionCallContext{NotNull{this}, constraint, c.callSite, c.argsPack, result});
|
usedMagic = ftv->magic->infer(MagicFunctionCallContext{NotNull{this}, constraint, NotNull{c.callSite}, c.argsPack, result});
|
||||||
ftv->magic->refine(MagicRefinementContext{constraint->scope, c.callSite, c.discriminantTypes});
|
ftv->magic->refine(MagicRefinementContext{constraint->scope, c.callSite, c.discriminantTypes});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1534,7 +1534,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
// This can potentially contain free types if the return type of
|
// This can potentially contain free types if the return type of
|
||||||
// `inferredTy` is never unified elsewhere.
|
// `inferredTy` is never unified elsewhere.
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInferredFunctionTypeFromCall)
|
if (FFlag::LuauTrackInferredFunctionTypeFromCall)
|
||||||
trackInteriorFreeType(constraint->scope, inferredTy);
|
trackInteriorFreeType(constraint->scope, inferredTy);
|
||||||
|
|
||||||
unblock(c.result, constraint->location);
|
unblock(c.result, constraint->location);
|
||||||
|
@ -1903,7 +1903,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
}
|
}
|
||||||
else if (auto mt = get<MetatableType>(subjectType))
|
else if (auto mt = get<MetatableType>(subjectType))
|
||||||
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
|
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
|
||||||
else if (auto ct = get<ClassType>(subjectType))
|
else if (auto ct = get<ExternType>(subjectType))
|
||||||
{
|
{
|
||||||
if (auto indexer = ct->indexer)
|
if (auto indexer = ct->indexer)
|
||||||
{
|
{
|
||||||
|
@ -2065,9 +2065,9 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
// Important: In every codepath through this function, the type `c.propType`
|
// Important: In every codepath through this function, the type `c.propType`
|
||||||
// must be bound to something, even if it's just the errorType.
|
// must be bound to something, even if it's just the errorType.
|
||||||
|
|
||||||
if (auto lhsClass = get<ClassType>(lhsType))
|
if (auto lhsExternType = get<ExternType>(lhsType))
|
||||||
{
|
{
|
||||||
const Property* prop = lookupClassProp(lhsClass, propName);
|
const Property* prop = lookupExternTypeProp(lhsExternType, propName);
|
||||||
if (!prop || !prop->writeTy.has_value())
|
if (!prop || !prop->writeTy.has_value())
|
||||||
{
|
{
|
||||||
bind(constraint, c.propType, builtinTypes->anyType);
|
bind(constraint, c.propType, builtinTypes->anyType);
|
||||||
|
@ -2088,7 +2088,6 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
{
|
{
|
||||||
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
|
||||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||||
|
|
||||||
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||||
|
@ -2252,20 +2251,20 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
return *res;
|
return *res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto lhsClass = get<ClassType>(lhsType))
|
if (auto lhsExternType = get<ExternType>(lhsType))
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (lhsClass->indexer)
|
if (lhsExternType->indexer)
|
||||||
{
|
{
|
||||||
unify(constraint, indexType, lhsClass->indexer->indexType);
|
unify(constraint, indexType, lhsExternType->indexer->indexType);
|
||||||
unify(constraint, rhsType, lhsClass->indexer->indexResultType);
|
unify(constraint, rhsType, lhsExternType->indexer->indexResultType);
|
||||||
bind(constraint, c.propType, arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}));
|
bind(constraint, c.propType, arena->addType(UnionType{{lhsExternType->indexer->indexResultType, builtinTypes->nilType}}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhsClass->parent)
|
if (lhsExternType->parent)
|
||||||
lhsClass = get<ClassType>(lhsClass->parent);
|
lhsExternType = get<ExternType>(lhsExternType->parent);
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2292,7 +2291,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
parts.insert(rhsType);
|
parts.insert(rhsType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto cls = get<ClassType>(follow(t)))
|
else if (auto cls = get<ExternType>(follow(t)))
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -2304,7 +2303,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls->parent)
|
if (cls->parent)
|
||||||
cls = get<ClassType>(cls->parent);
|
cls = get<ExternType>(cls->parent);
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2355,7 +2354,6 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
|
||||||
// constitute any meaningful constraint, so we replace it
|
// constitute any meaningful constraint, so we replace it
|
||||||
// with a free type.
|
// with a free type.
|
||||||
TypeId f = freshType(arena, builtinTypes, constraint->scope, Polarity::Positive); // FIXME? Is this the right polarity?
|
TypeId f = freshType(arena, builtinTypes, constraint->scope, Polarity::Positive); // FIXME? Is this the right polarity?
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
trackInteriorFreeType(constraint->scope, f);
|
trackInteriorFreeType(constraint->scope, f);
|
||||||
shiftReferences(resultTy, f);
|
shiftReferences(resultTy, f);
|
||||||
emplaceType<BoundType>(asMutable(resultTy), f);
|
emplaceType<BoundType>(asMutable(resultTy), f);
|
||||||
|
@ -2499,11 +2497,8 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
{
|
|
||||||
trackInteriorFreeType(constraint->scope, keyTy);
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
trackInteriorFreeType(constraint->scope, valueTy);
|
trackInteriorFreeType(constraint->scope, valueTy);
|
||||||
}
|
|
||||||
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
|
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
|
||||||
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
|
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
|
||||||
|
|
||||||
|
@ -2653,12 +2648,31 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
|
||||||
const FunctionType* nextFn = get<FunctionType>(nextTy);
|
const FunctionType* nextFn = get<FunctionType>(nextTy);
|
||||||
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.
|
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.
|
||||||
LUAU_ASSERT(nextFn);
|
LUAU_ASSERT(nextFn);
|
||||||
const TypePackId nextRetPack = nextFn->retTypes;
|
|
||||||
|
|
||||||
// the type of the `nextAstFragment` is the `nextTy`.
|
// the type of the `nextAstFragment` is the `nextTy`.
|
||||||
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
|
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
|
||||||
|
|
||||||
auto it = begin(nextRetPack);
|
if (FFlag::LuauAddCallConstraintForIterableFunctions)
|
||||||
|
{
|
||||||
|
// Construct a FunctionCallConstraint, to help us learn about the type of the loop variables being assigned to in this iterable
|
||||||
|
TypePackId tableTyPack = arena->addTypePack({tableTy});
|
||||||
|
|
||||||
|
TypePackId variablesPack = arena->addTypePack(BlockedTypePack{});
|
||||||
|
|
||||||
|
auto callConstraint = pushConstraint(constraint->scope, constraint->location, FunctionCallConstraint{nextTy, tableTyPack, variablesPack});
|
||||||
|
|
||||||
|
getMutable<BlockedTypePack>(variablesPack)->owner = callConstraint.get();
|
||||||
|
|
||||||
|
auto unpackConstraint = unpackAndAssign(c.variables, variablesPack, constraint);
|
||||||
|
|
||||||
|
inheritBlocks(constraint, callConstraint);
|
||||||
|
|
||||||
|
inheritBlocks(unpackConstraint, callConstraint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const TypePackId nextRetPack = nextFn->retTypes;
|
||||||
|
TypePackIterator it = begin(nextRetPack);
|
||||||
std::vector<TypeId> modifiedNextRetHead;
|
std::vector<TypeId> modifiedNextRetHead;
|
||||||
|
|
||||||
// The first value is never nil in the context of the loop, even if it's nil
|
// The first value is never nil in the context of the loop, even if it's nil
|
||||||
|
@ -2680,6 +2694,7 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
|
||||||
auto unpackConstraint = unpackAndAssign(c.variables, modifiedNextRetPack, constraint);
|
auto unpackConstraint = unpackAndAssign(c.variables, modifiedNextRetPack, constraint);
|
||||||
|
|
||||||
inheritBlocks(constraint, unpackConstraint);
|
inheritBlocks(constraint, unpackConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2760,7 +2775,6 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
if (ttv->state == TableState::Free)
|
if (ttv->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId result = freshType(arena, builtinTypes, ttv->scope, Polarity::Mixed);
|
TypeId result = freshType(arena, builtinTypes, ttv->scope, Polarity::Mixed);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
trackInteriorFreeType(ttv->scope, result);
|
trackInteriorFreeType(ttv->scope, result);
|
||||||
switch (context)
|
switch (context)
|
||||||
{
|
{
|
||||||
|
@ -2835,9 +2849,9 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
else if (get<MetatableType>(mtt))
|
else if (get<MetatableType>(mtt))
|
||||||
return lookupTableProp(constraint, mtt, propName, context, inConditional, suppressSimplification, seen);
|
return lookupTableProp(constraint, mtt, propName, context, inConditional, suppressSimplification, seen);
|
||||||
}
|
}
|
||||||
else if (auto ct = get<ClassType>(subjectType))
|
else if (auto ct = get<ExternType>(subjectType))
|
||||||
{
|
{
|
||||||
if (auto p = lookupClassProp(ct, propName))
|
if (auto p = lookupExternTypeProp(ct, propName))
|
||||||
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
|
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
|
||||||
if (ct->indexer)
|
if (ct->indexer)
|
||||||
{
|
{
|
||||||
|
@ -2862,20 +2876,17 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||||
return lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
return lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
||||||
|
|
||||||
// TODO: The upper bound could be an intersection that contains suitable tables or classes.
|
// TODO: The upper bound could be an intersection that contains suitable tables or extern types.
|
||||||
|
|
||||||
NotNull<Scope> scope{ft->scope};
|
NotNull<Scope> scope{ft->scope};
|
||||||
|
|
||||||
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
|
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
|
||||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||||
|
|
||||||
TableType* tt = getMutable<TableType>(newUpperBound);
|
TableType* tt = getMutable<TableType>(newUpperBound);
|
||||||
LUAU_ASSERT(tt);
|
LUAU_ASSERT(tt);
|
||||||
TypeId propType = freshType(arena, builtinTypes, scope, Polarity::Mixed);
|
TypeId propType = freshType(arena, builtinTypes, scope, Polarity::Mixed);
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
trackInteriorFreeType(scope, propType);
|
trackInteriorFreeType(scope, propType);
|
||||||
|
|
||||||
switch (context)
|
switch (context)
|
||||||
|
@ -3095,7 +3106,7 @@ struct Blocker : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3471,4 +3482,35 @@ LUAU_NOINLINE void ConstraintSolver::throwUserCancelError() const
|
||||||
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypeId subTy, TypeId superTy);
|
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypeId subTy, TypeId superTy);
|
||||||
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypePackId subTy, TypePackId superTy);
|
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypePackId subTy, TypePackId superTy);
|
||||||
|
|
||||||
|
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints)
|
||||||
|
{
|
||||||
|
std::vector<NotNull<Constraint>> result;
|
||||||
|
result.reserve(constraints.size());
|
||||||
|
|
||||||
|
for (const auto& c : constraints)
|
||||||
|
result.emplace_back(c.get());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
||||||
|
{
|
||||||
|
printf("constraints:\n");
|
||||||
|
for (NotNull<const Constraint> c : cs->unsolvedConstraints)
|
||||||
|
{
|
||||||
|
auto it = cs->blockedConstraints.find(c);
|
||||||
|
int blockCount = it == cs->blockedConstraints.end() ? 0 : int(it->second);
|
||||||
|
printf("\t%d\t%s\n", blockCount, toString(*c, opts).c_str());
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauLogSolverIncludeDependencies)
|
||||||
|
{
|
||||||
|
for (NotNull<Constraint> dep : c->dependencies)
|
||||||
|
{
|
||||||
|
if (std::find(cs->unsolvedConstraints.begin(), cs->unsolvedConstraints.end(), dep) != cs->unsolvedConstraints.end())
|
||||||
|
printf("\t\t|\t%s\n", toString(*dep, opts).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -16,6 +16,8 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -470,7 +472,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStat* s)
|
||||||
return visit(d);
|
return visit(d);
|
||||||
else if (auto d = s->as<AstStatDeclareFunction>())
|
else if (auto d = s->as<AstStatDeclareFunction>())
|
||||||
return visit(d);
|
return visit(d);
|
||||||
else if (auto d = s->as<AstStatDeclareClass>())
|
else if (auto d = s->as<AstStatDeclareExternType>())
|
||||||
return visit(d);
|
return visit(d);
|
||||||
else if (auto error = s->as<AstStatError>())
|
else if (auto error = s->as<AstStatError>())
|
||||||
return visit(error);
|
return visit(error);
|
||||||
|
@ -808,12 +810,15 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
||||||
visitGenerics(d->generics);
|
visitGenerics(d->generics);
|
||||||
visitGenericPacks(d->genericPacks);
|
visitGenericPacks(d->genericPacks);
|
||||||
visitTypeList(d->params);
|
visitTypeList(d->params);
|
||||||
visitTypeList(d->retTypes);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visitTypePack(d->retTypes);
|
||||||
|
else
|
||||||
|
visitTypeList(d->retTypes_DEPRECATED);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareExternType* d)
|
||||||
{
|
{
|
||||||
// This declaration does not "introduce" any bindings in value namespace,
|
// This declaration does not "introduce" any bindings in value namespace,
|
||||||
// so there's no symbolic value to begin with. We'll traverse the properties
|
// so there's no symbolic value to begin with. We'll traverse the properties
|
||||||
|
@ -821,7 +826,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d)
|
||||||
DfgScope* unreachable = makeChildScope();
|
DfgScope* unreachable = makeChildScope();
|
||||||
PushScope ps{scopeStack, unreachable};
|
PushScope ps{scopeStack, unreachable};
|
||||||
|
|
||||||
for (AstDeclaredClassProp prop : d->props)
|
for (AstDeclaredExternTypeProperty prop : d->props)
|
||||||
visitType(prop.ty);
|
visitType(prop.ty);
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
|
@ -1032,8 +1037,16 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||||
if (f->varargAnnotation)
|
if (f->varargAnnotation)
|
||||||
visitTypePack(f->varargAnnotation);
|
visitTypePack(f->varargAnnotation);
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (f->returnAnnotation)
|
if (f->returnAnnotation)
|
||||||
visitTypeList(*f->returnAnnotation);
|
visitTypePack(f->returnAnnotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (f->returnAnnotation_DEPRECATED)
|
||||||
|
visitTypeList(*f->returnAnnotation_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be
|
// 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
|
// visible to the beginning of the function, so statically speaking, the body of the function has an exit point
|
||||||
|
@ -1275,7 +1288,10 @@ void DataFlowGraphBuilder::visitType(AstTypeFunction* f)
|
||||||
visitGenerics(f->generics);
|
visitGenerics(f->generics);
|
||||||
visitGenericPacks(f->genericPacks);
|
visitGenericPacks(f->genericPacks);
|
||||||
visitTypeList(f->argTypes);
|
visitTypeList(f->argTypes);
|
||||||
visitTypeList(f->returnTypes);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visitTypePack(f->returnTypes);
|
||||||
|
else
|
||||||
|
visitTypeList(f->returnTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowGraphBuilder::visitType(AstTypeTypeof* t)
|
void DataFlowGraphBuilder::visitType(AstTypeTypeof* t)
|
||||||
|
|
|
@ -277,7 +277,7 @@ static DifferResult diffSingleton(DifferEnvironment& env, TypeId left, TypeId ri
|
||||||
static DifferResult diffFunction(DifferEnvironment& env, TypeId left, TypeId right);
|
static DifferResult diffFunction(DifferEnvironment& env, TypeId left, TypeId right);
|
||||||
static DifferResult diffGeneric(DifferEnvironment& env, TypeId left, TypeId right);
|
static DifferResult diffGeneric(DifferEnvironment& env, TypeId left, TypeId right);
|
||||||
static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId right);
|
static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId right);
|
||||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right);
|
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right);
|
||||||
struct FindSeteqCounterexampleResult
|
struct FindSeteqCounterexampleResult
|
||||||
{
|
{
|
||||||
// nullopt if no counterexample found
|
// nullopt if no counterexample found
|
||||||
|
@ -481,14 +481,14 @@ static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId rig
|
||||||
return differResult;
|
return differResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right)
|
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right)
|
||||||
{
|
{
|
||||||
const ClassType* leftClass = get<ClassType>(left);
|
const ExternType* leftExternType = get<ExternType>(left);
|
||||||
const ClassType* rightClass = get<ClassType>(right);
|
const ExternType* rightExternType = get<ExternType>(right);
|
||||||
LUAU_ASSERT(leftClass);
|
LUAU_ASSERT(leftExternType);
|
||||||
LUAU_ASSERT(rightClass);
|
LUAU_ASSERT(rightExternType);
|
||||||
|
|
||||||
if (leftClass == rightClass)
|
if (leftExternType == rightExternType)
|
||||||
{
|
{
|
||||||
return DifferResult{};
|
return DifferResult{};
|
||||||
}
|
}
|
||||||
|
@ -651,9 +651,9 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig
|
||||||
{
|
{
|
||||||
return diffNegation(env, left, right);
|
return diffNegation(env, left, right);
|
||||||
}
|
}
|
||||||
else if (auto lc = get<ClassType>(left))
|
else if (auto lc = get<ExternType>(left))
|
||||||
{
|
{
|
||||||
return diffClass(env, left, right);
|
return diffExternType(env, left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalCompilerError{"Unimplemented Simple TypeId variant for diffing"};
|
throw InternalCompilerError{"Unimplemented Simple TypeId variant for diffing"};
|
||||||
|
@ -960,7 +960,7 @@ bool isSimple(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
// TODO: think about GenericType, etc.
|
// TODO: think about GenericType, etc.
|
||||||
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ClassType>(ty) ||
|
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ExternType>(ty) ||
|
||||||
get<UnknownType>(ty) || get<NeverType>(ty);
|
get<UnknownType>(ty) || get<NeverType>(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauDeclareExternType)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -259,7 +261,37 @@ declare buffer: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionVectorSrc = (FFlag::LuauDeclareExternType)
|
||||||
|
? R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
-- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties
|
||||||
|
declare extern type vector with
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
z: number
|
||||||
|
end
|
||||||
|
|
||||||
|
declare vector: {
|
||||||
|
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||||
|
magnitude: @checked (vec: vector) -> number,
|
||||||
|
normalize: @checked (vec: vector) -> vector,
|
||||||
|
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||||
|
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||||
|
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||||
|
floor: @checked (vec: vector) -> vector,
|
||||||
|
ceil: @checked (vec: vector) -> vector,
|
||||||
|
abs: @checked (vec: vector) -> vector,
|
||||||
|
sign: @checked (vec: vector) -> vector,
|
||||||
|
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||||
|
max: @checked (vector, ...vector) -> vector,
|
||||||
|
min: @checked (vector, ...vector) -> vector,
|
||||||
|
|
||||||
|
zero: vector,
|
||||||
|
one: vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC"
|
||||||
|
: R"BUILTIN_SRC(
|
||||||
|
|
||||||
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
||||||
declare class vector
|
declare class vector
|
||||||
|
|
|
@ -330,9 +330,9 @@ Id toId(
|
||||||
return egraph.add(TOpaque{ty});
|
return egraph.add(TOpaque{ty});
|
||||||
else if (get<FunctionType>(ty))
|
else if (get<FunctionType>(ty))
|
||||||
return egraph.add(TFunction{ty});
|
return egraph.add(TFunction{ty});
|
||||||
else if (ty == builtinTypes->classType)
|
else if (ty == builtinTypes->externType)
|
||||||
return egraph.add(TTopClass{});
|
return egraph.add(TTopClass{});
|
||||||
else if (get<ClassType>(ty))
|
else if (get<ExternType>(ty))
|
||||||
return egraph.add(TClass{ty});
|
return egraph.add(TClass{ty});
|
||||||
else if (get<AnyType>(ty))
|
else if (get<AnyType>(ty))
|
||||||
return egraph.add(TAny{});
|
return egraph.add(TAny{});
|
||||||
|
@ -752,7 +752,7 @@ TypeId fromId(
|
||||||
else if (node.get<TTopTable>())
|
else if (node.get<TTopTable>())
|
||||||
return builtinTypes->tableType;
|
return builtinTypes->tableType;
|
||||||
else if (node.get<TTopClass>())
|
else if (node.get<TTopClass>())
|
||||||
return builtinTypes->classType;
|
return builtinTypes->externType;
|
||||||
else if (node.get<TBuffer>())
|
else if (node.get<TBuffer>())
|
||||||
return builtinTypes->bufferType;
|
return builtinTypes->bufferType;
|
||||||
else if (auto opaque = node.get<TOpaque>())
|
else if (auto opaque = node.get<TOpaque>())
|
||||||
|
@ -1007,7 +1007,7 @@ static std::string getNodeName(const StringCache& strings, const EType& node)
|
||||||
return "\xe2\x88\xa9";
|
return "\xe2\x88\xa9";
|
||||||
else if (auto cls = node.get<TClass>())
|
else if (auto cls = node.get<TClass>())
|
||||||
{
|
{
|
||||||
const ClassType* ct = get<ClassType>(cls->value());
|
const ExternType* ct = get<ExternType>(cls->value());
|
||||||
LUAU_ASSERT(ct);
|
LUAU_ASSERT(ct);
|
||||||
return ct->name;
|
return ct->name;
|
||||||
}
|
}
|
||||||
|
@ -1177,12 +1177,12 @@ enum SubclassRelationship
|
||||||
|
|
||||||
static SubclassRelationship relateClasses(const TClass* leftClass, const TClass* rightClass)
|
static SubclassRelationship relateClasses(const TClass* leftClass, const TClass* rightClass)
|
||||||
{
|
{
|
||||||
const ClassType* leftClassType = Luau::get<ClassType>(leftClass->value());
|
const ExternType* leftExternType = Luau::get<ExternType>(leftClass->value());
|
||||||
const ClassType* rightClassType = Luau::get<ClassType>(rightClass->value());
|
const ExternType* rightExternType = Luau::get<ExternType>(rightClass->value());
|
||||||
|
|
||||||
if (isSubclass(leftClassType, rightClassType))
|
if (isSubclass(leftExternType, rightExternType))
|
||||||
return RightSuper;
|
return RightSuper;
|
||||||
else if (isSubclass(rightClassType, leftClassType))
|
else if (isSubclass(rightExternType, leftExternType))
|
||||||
return LeftSuper;
|
return LeftSuper;
|
||||||
else
|
else
|
||||||
return Unrelated;
|
return Unrelated;
|
||||||
|
|
|
@ -203,7 +203,7 @@ struct ErrorConverter
|
||||||
TypeId t = follow(e.table);
|
TypeId t = follow(e.table);
|
||||||
if (get<TableType>(t))
|
if (get<TableType>(t))
|
||||||
return "Key '" + e.key + "' not found in table '" + Luau::toString(t) + "'";
|
return "Key '" + e.key + "' not found in table '" + Luau::toString(t) + "'";
|
||||||
else if (get<ClassType>(t))
|
else if (get<ExternType>(t))
|
||||||
return "Key '" + e.key + "' not found in class '" + Luau::toString(t) + "'";
|
return "Key '" + e.key + "' not found in class '" + Luau::toString(t) + "'";
|
||||||
else
|
else
|
||||||
return "Type '" + Luau::toString(e.table) + "' does not have key '" + e.key + "'";
|
return "Type '" + Luau::toString(e.table) + "' does not have key '" + e.key + "'";
|
||||||
|
@ -371,7 +371,7 @@ struct ErrorConverter
|
||||||
std::string s = "Key '" + e.key + "' not found in ";
|
std::string s = "Key '" + e.key + "' not found in ";
|
||||||
|
|
||||||
TypeId t = follow(e.table);
|
TypeId t = follow(e.table);
|
||||||
if (get<ClassType>(t))
|
if (get<ExternType>(t))
|
||||||
s += "class";
|
s += "class";
|
||||||
else
|
else
|
||||||
s += "table";
|
s += "table";
|
||||||
|
@ -402,8 +402,8 @@ struct ErrorConverter
|
||||||
std::optional<TypeId> metatable;
|
std::optional<TypeId> metatable;
|
||||||
if (const MetatableType* mtType = get<MetatableType>(type))
|
if (const MetatableType* mtType = get<MetatableType>(type))
|
||||||
metatable = mtType->metatable;
|
metatable = mtType->metatable;
|
||||||
else if (const ClassType* classType = get<ClassType>(type))
|
else if (const ExternType* externType = get<ExternType>(type))
|
||||||
metatable = classType->metatable;
|
metatable = externType->metatable;
|
||||||
|
|
||||||
if (!metatable)
|
if (!metatable)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -611,7 +611,7 @@ struct ErrorConverter
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string operator()(const DynamicPropertyLookupOnClassesUnsafe& e) const
|
std::string operator()(const DynamicPropertyLookupOnExternTypesUnsafe& e) const
|
||||||
{
|
{
|
||||||
return "Attempting a dynamic property access on type '" + Luau::toString(e.ty) + "' is unsafe and may cause exceptions at runtime";
|
return "Attempting a dynamic property access on type '" + Luau::toString(e.ty) + "' is unsafe and may cause exceptions at runtime";
|
||||||
}
|
}
|
||||||
|
@ -1149,7 +1149,7 @@ bool TypePackMismatch::operator==(const TypePackMismatch& rhs) const
|
||||||
return *wantedTp == *rhs.wantedTp && *givenTp == *rhs.givenTp;
|
return *wantedTp == *rhs.wantedTp && *givenTp == *rhs.givenTp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DynamicPropertyLookupOnClassesUnsafe::operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const
|
bool DynamicPropertyLookupOnExternTypesUnsafe::operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const
|
||||||
{
|
{
|
||||||
return ty == rhs.ty;
|
return ty == rhs.ty;
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1391,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
||||||
e.wantedTp = clone(e.wantedTp);
|
e.wantedTp = clone(e.wantedTp);
|
||||||
e.givenTp = clone(e.givenTp);
|
e.givenTp = clone(e.givenTp);
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||||
e.ty = clone(e.ty);
|
e.ty = clone(e.ty);
|
||||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||||
e.ty = clone(e.ty);
|
e.ty = clone(e.ty);
|
||||||
|
|
|
@ -10,10 +10,6 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauExposeRequireByStringAutocomplete)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEscapeCharactersInRequireSuggestions)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauHideImpossibleRequireSuggestions)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -22,13 +18,10 @@ static std::optional<RequireSuggestions> processRequireSuggestions(std::optional
|
||||||
if (!suggestions)
|
if (!suggestions)
|
||||||
return suggestions;
|
return suggestions;
|
||||||
|
|
||||||
if (FFlag::LuauEscapeCharactersInRequireSuggestions)
|
|
||||||
{
|
|
||||||
for (RequireSuggestion& suggestion : *suggestions)
|
for (RequireSuggestion& suggestion : *suggestions)
|
||||||
{
|
{
|
||||||
suggestion.fullPath = escape(suggestion.fullPath);
|
suggestion.fullPath = escape(suggestion.fullPath);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
@ -112,13 +105,11 @@ static RequireSuggestions makeSuggestionsFromNode(std::unique_ptr<RequireNode> n
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string pathComponent = child->getPathComponent();
|
std::string pathComponent = child->getPathComponent();
|
||||||
if (FFlag::LuauHideImpossibleRequireSuggestions)
|
|
||||||
{
|
|
||||||
// If path component contains a slash, it cannot be required by string.
|
// If path component contains a slash, it cannot be required by string.
|
||||||
// There's no point suggesting it.
|
// There's no point suggesting it.
|
||||||
if (pathComponent.find('/') != std::string::npos)
|
if (pathComponent.find('/') != std::string::npos)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
RequireSuggestion suggestion;
|
RequireSuggestion suggestion;
|
||||||
suggestion.label = isPartialPath || path.back() == '/' ? child->getLabel() : "/" + child->getLabel();
|
suggestion.label = isPartialPath || path.back() == '/' ? child->getLabel() : "/" + child->getLabel();
|
||||||
|
@ -163,9 +154,6 @@ std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestions(const
|
||||||
|
|
||||||
std::optional<RequireSuggestions> FileResolver::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
std::optional<RequireSuggestions> FileResolver::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauExposeRequireByStringAutocomplete)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
return requireSuggester ? requireSuggester->getRequireSuggestions(requirer, path) : std::nullopt;
|
return requireSuggester ? requireSuggester->getRequireSuggestions(requirer, path) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
||||||
|
@ -43,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,11 @@ Location getFunctionDeclarationExtents(AstExprFunction* exprFn, AstExpr* exprNam
|
||||||
{
|
{
|
||||||
auto fnBegin = exprFn->location.begin;
|
auto fnBegin = exprFn->location.begin;
|
||||||
auto fnEnd = exprFn->location.end;
|
auto fnEnd = exprFn->location.end;
|
||||||
if (auto returnAnnot = exprFn->returnAnnotation)
|
if (auto returnAnnot = exprFn->returnAnnotation; FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||||
|
{
|
||||||
|
fnEnd = returnAnnot->location.end;
|
||||||
|
}
|
||||||
|
else if (auto returnAnnot = exprFn->returnAnnotation_DEPRECATED; !FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||||
{
|
{
|
||||||
if (returnAnnot->tailType)
|
if (returnAnnot->tailType)
|
||||||
fnEnd = returnAnnot->tailType->location.end;
|
fnEnd = returnAnnot->tailType->location.end;
|
||||||
|
@ -542,6 +546,11 @@ struct UsageFinder : public AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypePack* node) override
|
||||||
|
{
|
||||||
|
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstStatTypeAlias* alias) override
|
bool visit(AstStatTypeAlias* alias) override
|
||||||
{
|
{
|
||||||
declaredAliases.insert(std::string(alias->name.value));
|
declaredAliases.insert(std::string(alias->name.value));
|
||||||
|
@ -1706,7 +1715,6 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
IFragmentAutocompleteReporter* reporter
|
IFragmentAutocompleteReporter* reporter
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
|
||||||
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
||||||
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRethrowKnownExceptions, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRethrowKnownExceptions, false)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||||
|
@ -128,9 +129,9 @@ static void generateDocumentationSymbols(TypeId ty, const std::string& rootName)
|
||||||
prop.documentationSymbol = rootName + "." + name;
|
prop.documentationSymbol = rootName + "." + name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ClassType* ctv = getMutable<ClassType>(ty))
|
else if (ExternType* etv = getMutable<ExternType>(ty))
|
||||||
{
|
{
|
||||||
for (auto& [name, prop] : ctv->props)
|
for (auto& [name, prop] : etv->props)
|
||||||
{
|
{
|
||||||
prop.documentationSymbol = rootName + "." + name;
|
prop.documentationSymbol = rootName + "." + name;
|
||||||
}
|
}
|
||||||
|
@ -1304,7 +1305,7 @@ ModulePtr check(
|
||||||
|
|
||||||
struct InternalTypeFinder : TypeOnceVisitor
|
struct InternalTypeFinder : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
bool visit(TypeId, const ClassType&) override
|
bool visit(TypeId, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1419,10 +1420,36 @@ ModulePtr check(
|
||||||
requireCycles
|
requireCycles
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: Delete this flag when clipping FFlag::DebugLuauGreedyGeneralization.
|
||||||
|
//
|
||||||
|
// This optional<> only exists so that we can run one constructor when the flag
|
||||||
|
// is set, and another when it is unset.
|
||||||
|
std::optional<ConstraintSolver> cs;
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
ConstraintSet constraintSet = cg.run(sourceModule.root);
|
||||||
|
result->errors = std::move(constraintSet.errors);
|
||||||
|
|
||||||
|
cs.emplace(
|
||||||
|
NotNull{&normalizer},
|
||||||
|
NotNull{simplifier.get()},
|
||||||
|
NotNull{&typeFunctionRuntime},
|
||||||
|
result->name,
|
||||||
|
moduleResolver,
|
||||||
|
requireCycles,
|
||||||
|
logger.get(),
|
||||||
|
NotNull{&dfg},
|
||||||
|
limits,
|
||||||
|
std::move(constraintSet)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cg.visitModuleRoot(sourceModule.root);
|
cg.visitModuleRoot(sourceModule.root);
|
||||||
result->errors = std::move(cg.errors);
|
result->errors = std::move(cg.errors);
|
||||||
|
|
||||||
ConstraintSolver cs{
|
cs.emplace(
|
||||||
NotNull{&normalizer},
|
NotNull{&normalizer},
|
||||||
NotNull{simplifier.get()},
|
NotNull{simplifier.get()},
|
||||||
NotNull{&typeFunctionRuntime},
|
NotNull{&typeFunctionRuntime},
|
||||||
|
@ -1435,14 +1462,17 @@ ModulePtr check(
|
||||||
logger.get(),
|
logger.get(),
|
||||||
NotNull{&dfg},
|
NotNull{&dfg},
|
||||||
limits
|
limits
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LUAU_ASSERT(bool(cs));
|
||||||
|
|
||||||
if (options.randomizeConstraintResolutionSeed)
|
if (options.randomizeConstraintResolutionSeed)
|
||||||
cs.randomize(*options.randomizeConstraintResolutionSeed);
|
cs->randomize(*options.randomizeConstraintResolutionSeed);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cs.run();
|
cs->run();
|
||||||
}
|
}
|
||||||
catch (const TimeLimitError&)
|
catch (const TimeLimitError&)
|
||||||
{
|
{
|
||||||
|
@ -1462,12 +1492,12 @@ ModulePtr check(
|
||||||
printf("%s\n", output.c_str());
|
printf("%s\n", output.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TypeError& e : cs.errors)
|
for (TypeError& e : cs->errors)
|
||||||
result->errors.emplace_back(std::move(e));
|
result->errors.emplace_back(std::move(e));
|
||||||
|
|
||||||
result->scopes = std::move(cg.scopes);
|
result->scopes = std::move(cg.scopes);
|
||||||
result->type = sourceModule.type;
|
result->type = sourceModule.type;
|
||||||
result->upperBoundContributors = std::move(cs.upperBoundContributors);
|
result->upperBoundContributors = std::move(cs->upperBoundContributors);
|
||||||
|
|
||||||
if (result->timeout || result->cancelled)
|
if (result->timeout || result->cancelled)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization2)
|
||||||
|
|
||||||
|
@ -549,7 +548,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
traverse(*prop.readTy);
|
traverse(*prop.readTy);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_ASSERT(prop.isShared());
|
||||||
|
|
||||||
Polarity p = polarity;
|
Polarity p = polarity;
|
||||||
polarity = Polarity::Mixed;
|
polarity = Polarity::Mixed;
|
||||||
|
@ -605,7 +604,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId, const ClassType&) override
|
bool visit(TypeId, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -897,7 +896,7 @@ struct TypeCacher : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
cache(ty);
|
cache(ty);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1513,26 +1512,22 @@ void pruneUnnecessaryGenerics(
|
||||||
if (!functionTy)
|
if (!functionTy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Types (and packs) to be removed from the generics list
|
// If a generic has no explicit name and is only referred to in one place in
|
||||||
DenseHashSet<TypeId> clipTypes{nullptr};
|
// the function's signature, it can be replaced with unknown.
|
||||||
DenseHashSet<TypePackId> clipTypePacks{nullptr};
|
|
||||||
|
|
||||||
GenericCounter counter{cachedTypes};
|
GenericCounter counter{cachedTypes};
|
||||||
for (TypeId generic : functionTy->generics)
|
for (TypeId generic : functionTy->generics)
|
||||||
{
|
{
|
||||||
|
generic = follow(generic);
|
||||||
auto g = get<GenericType>(generic);
|
auto g = get<GenericType>(generic);
|
||||||
LUAU_ASSERT(g);
|
if (g && !g->explicitName)
|
||||||
if (!g)
|
|
||||||
clipTypes.insert(generic);
|
|
||||||
else if (!g->explicitName)
|
|
||||||
counter.generics[generic] = 0;
|
counter.generics[generic] = 0;
|
||||||
}
|
}
|
||||||
for (TypePackId genericPack : functionTy->genericPacks)
|
for (TypePackId genericPack : functionTy->genericPacks)
|
||||||
{
|
{
|
||||||
|
genericPack = follow(genericPack);
|
||||||
auto g = get<GenericTypePack>(genericPack);
|
auto g = get<GenericTypePack>(genericPack);
|
||||||
if (!g)
|
if (g && !g->explicitName)
|
||||||
clipTypePacks.insert(genericPack);
|
|
||||||
else if (!g->explicitName)
|
|
||||||
counter.genericPacks[genericPack] = 0;
|
counter.genericPacks[genericPack] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1541,18 +1536,22 @@ void pruneUnnecessaryGenerics(
|
||||||
for (const auto& [generic, count] : counter.generics)
|
for (const auto& [generic, count] : counter.generics)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
|
||||||
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
|
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
|
||||||
clipTypes.insert(generic);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove duplicates and types that aren't actually generics.
|
||||||
|
DenseHashSet<TypeId> seen{nullptr};
|
||||||
auto it = std::remove_if(
|
auto it = std::remove_if(
|
||||||
functionTy->generics.begin(),
|
functionTy->generics.begin(),
|
||||||
functionTy->generics.end(),
|
functionTy->generics.end(),
|
||||||
[&](TypeId ty)
|
[&](TypeId ty)
|
||||||
{
|
{
|
||||||
return clipTypes.contains(ty);
|
ty = follow(ty);
|
||||||
|
if (seen.contains(ty))
|
||||||
|
return true;
|
||||||
|
seen.insert(ty);
|
||||||
|
|
||||||
|
return !get<GenericType>(ty);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1561,18 +1560,21 @@ void pruneUnnecessaryGenerics(
|
||||||
for (const auto& [genericPack, count] : counter.genericPacks)
|
for (const auto& [genericPack, count] : counter.genericPacks)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
||||||
clipTypePacks.insert(genericPack);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DenseHashSet<TypePackId> seen2{nullptr};
|
||||||
auto it2 = std::remove_if(
|
auto it2 = std::remove_if(
|
||||||
functionTy->genericPacks.begin(),
|
functionTy->genericPacks.begin(),
|
||||||
functionTy->genericPacks.end(),
|
functionTy->genericPacks.end(),
|
||||||
[&](TypePackId tp)
|
[&](TypePackId tp)
|
||||||
{
|
{
|
||||||
return clipTypePacks.contains(tp);
|
tp = follow(tp);
|
||||||
|
if (seen2.contains(tp))
|
||||||
|
return true;
|
||||||
|
seen2.insert(tp);
|
||||||
|
|
||||||
|
return !get<GenericTypePack>(tp);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -95,16 +95,16 @@ struct InferPolarity : TypeVisitor
|
||||||
// types.
|
// types.
|
||||||
for (TypeId generic : ft.generics)
|
for (TypeId generic : ft.generics)
|
||||||
{
|
{
|
||||||
|
generic = follow(generic);
|
||||||
const auto gen = get<GenericType>(generic);
|
const auto gen = get<GenericType>(generic);
|
||||||
LUAU_ASSERT(gen);
|
if (gen && subsumes(scope, gen->scope))
|
||||||
if (subsumes(scope, gen->scope))
|
|
||||||
types[generic] = Polarity::None;
|
types[generic] = Polarity::None;
|
||||||
}
|
}
|
||||||
for (TypePackId genericPack : ft.genericPacks)
|
for (TypePackId genericPack : ft.genericPacks)
|
||||||
{
|
{
|
||||||
|
genericPack = follow(genericPack);
|
||||||
const auto gen = get<GenericTypePack>(genericPack);
|
const auto gen = get<GenericTypePack>(genericPack);
|
||||||
LUAU_ASSERT(gen);
|
if (gen && subsumes(scope, gen->scope))
|
||||||
if (subsumes(scope, gen->scope))
|
|
||||||
packs[genericPack] = Polarity::None;
|
packs[genericPack] = Polarity::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ struct InferPolarity : TypeVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId, const ClassType&) override
|
bool visit(TypeId, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ bool Instantiation::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
if (log->getMutable<FunctionType>(ty))
|
if (log->getMutable<FunctionType>(ty))
|
||||||
return true;
|
return true;
|
||||||
else if (get<ClassType>(ty))
|
else if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -120,7 +120,7 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
||||||
// whenever we quantify, so the vectors overlap if and only if they are equal.
|
// whenever we quantify, so the vectors overlap if and only if they are equal.
|
||||||
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
|
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
|
||||||
}
|
}
|
||||||
else if (get<ClassType>(ty))
|
else if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Luau
|
||||||
|
|
||||||
bool Instantiation2::ignoreChildren(TypeId ty)
|
bool Instantiation2::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
if (get<ClassType>(ty))
|
if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (auto ftv = get<FunctionType>(ty))
|
if (auto ftv = get<FunctionType>(ty))
|
||||||
|
|
|
@ -193,8 +193,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
||||||
stream << "NormalizationTooComplex { }";
|
stream << "NormalizationTooComplex { }";
|
||||||
else if constexpr (std::is_same_v<T, TypePackMismatch>)
|
else if constexpr (std::is_same_v<T, TypePackMismatch>)
|
||||||
stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }";
|
stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }";
|
||||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||||
stream << "DynamicPropertyLookupOnClassesUnsafe { " << toString(err.ty) << " }";
|
stream << "DynamicPropertyLookupOnExternTypesUnsafe { " << toString(err.ty) << " }";
|
||||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||||
stream << "UninhabitedTypeFunction { " << toString(err.ty) << " }";
|
stream << "UninhabitedTypeFunction { " << toString(err.ty) << " }";
|
||||||
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
|
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
|
||||||
|
|
|
@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauAttribute)
|
||||||
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -908,6 +909,11 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypePack* node) override
|
||||||
|
{
|
||||||
|
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstTypeReference* node) override
|
bool visit(AstTypeReference* node) override
|
||||||
{
|
{
|
||||||
if (!node->prefix)
|
if (!node->prefix)
|
||||||
|
@ -1970,6 +1976,11 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypePack* node) override
|
||||||
|
{
|
||||||
|
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstTypeTable* node) override
|
bool visit(AstTypeTable* node) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -2372,9 +2383,9 @@ private:
|
||||||
|
|
||||||
void check(AstExprIndexName* node, TypeId ty)
|
void check(AstExprIndexName* node, TypeId ty)
|
||||||
{
|
{
|
||||||
if (const ClassType* cty = get<ClassType>(ty))
|
if (const ExternType* cty = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
const Property* prop = lookupClassProp(cty, node->index.value);
|
const Property* prop = lookupExternTypeProp(cty, node->index.value);
|
||||||
|
|
||||||
if (prop && prop->deprecated)
|
if (prop && prop->deprecated)
|
||||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||||
|
|
|
@ -26,6 +26,7 @@ LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -311,7 +312,7 @@ struct NonStrictTypeChecker
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatError>())
|
else if (auto s = stat->as<AstStatError>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
|
@ -544,7 +545,7 @@ struct NonStrictTypeChecker
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareClass* declClass)
|
NonStrictContext visit(AstStatDeclareExternType* declClass)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||||
{
|
{
|
||||||
|
@ -833,8 +834,16 @@ struct NonStrictTypeChecker
|
||||||
{
|
{
|
||||||
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (exprFn->returnAnnotation)
|
if (exprFn->returnAnnotation)
|
||||||
visit(*exprFn->returnAnnotation);
|
visit(exprFn->returnAnnotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (exprFn->returnAnnotation_DEPRECATED)
|
||||||
|
visit(*exprFn->returnAnnotation_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
if (exprFn->varargAnnotation)
|
if (exprFn->varargAnnotation)
|
||||||
visit(exprFn->varargAnnotation);
|
visit(exprFn->varargAnnotation);
|
||||||
|
|
|
@ -249,23 +249,23 @@ bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& s
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizedClassType::pushPair(TypeId ty, TypeIds negations)
|
void NormalizedExternType::pushPair(TypeId ty, TypeIds negations)
|
||||||
{
|
{
|
||||||
auto result = classes.insert(std::make_pair(ty, std::move(negations)));
|
auto result = externTypes.insert(std::make_pair(ty, std::move(negations)));
|
||||||
if (result.second)
|
if (result.second)
|
||||||
ordering.push_back(ty);
|
ordering.push_back(ty);
|
||||||
LUAU_ASSERT(ordering.size() == classes.size());
|
LUAU_ASSERT(ordering.size() == externTypes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizedClassType::resetToNever()
|
void NormalizedExternType::resetToNever()
|
||||||
{
|
{
|
||||||
ordering.clear();
|
ordering.clear();
|
||||||
classes.clear();
|
externTypes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedClassType::isNever() const
|
bool NormalizedExternType::isNever() const
|
||||||
{
|
{
|
||||||
return classes.empty();
|
return externTypes.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizedFunctionType::resetToTop()
|
void NormalizedFunctionType::resetToTop()
|
||||||
|
@ -307,14 +307,14 @@ bool NormalizedType::isUnknown() const
|
||||||
strings.isString() && isThread(threads) && isBuffer(buffers);
|
strings.isString() && isThread(threads) && isBuffer(buffers);
|
||||||
|
|
||||||
// Check is class
|
// Check is class
|
||||||
bool isTopClass = false;
|
bool isTopExternType = false;
|
||||||
for (auto [t, disj] : classes.classes)
|
for (const auto& [t, disj] : externTypes.externTypes)
|
||||||
{
|
{
|
||||||
if (auto ct = get<ClassType>(t))
|
if (auto ct = get<ExternType>(t))
|
||||||
{
|
{
|
||||||
if (ct->name == "class" && disj.empty())
|
if (ct->name == "class" && disj.empty())
|
||||||
{
|
{
|
||||||
isTopClass = true;
|
isTopExternType = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,24 +330,24 @@ bool NormalizedType::isUnknown() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// any = unknown or error ==> we need to make sure we have all the unknown components, but not errors
|
// any = unknown or error ==> we need to make sure we have all the unknown components, but not errors
|
||||||
return get<NeverType>(errors) && hasAllPrimitives && isTopClass && isTopTable && functions.isTop;
|
return get<NeverType>(errors) && hasAllPrimitives && isTopExternType && isTopTable && functions.isTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedType::isExactlyNumber() const
|
bool NormalizedType::isExactlyNumber() const
|
||||||
{
|
{
|
||||||
return hasNumbers() && !hasTops() && !hasBooleans() && !hasClasses() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
|
return hasNumbers() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
|
||||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedType::isSubtypeOfString() const
|
bool NormalizedType::isSubtypeOfString() const
|
||||||
{
|
{
|
||||||
return hasStrings() && !hasTops() && !hasBooleans() && !hasClasses() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
|
return hasStrings() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
|
||||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedType::isSubtypeOfBooleans() const
|
bool NormalizedType::isSubtypeOfBooleans() const
|
||||||
{
|
{
|
||||||
return hasBooleans() && !hasTops() && !hasClasses() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
return hasBooleans() && !hasTops() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,9 +380,9 @@ bool NormalizedType::hasBooleans() const
|
||||||
return !get<NeverType>(booleans);
|
return !get<NeverType>(booleans);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedType::hasClasses() const
|
bool NormalizedType::hasExternTypes() const
|
||||||
{
|
{
|
||||||
return !classes.isNever();
|
return !externTypes.isNever();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizedType::hasErrors() const
|
bool NormalizedType::hasErrors() const
|
||||||
|
@ -440,7 +440,7 @@ bool NormalizedType::isFalsy() const
|
||||||
hasAFalse = !bs->value;
|
hasAFalse = !bs->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (hasAFalse || hasNils()) && (!hasTops() && !hasClasses() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
return (hasAFalse || hasNils()) && (!hasTops() && !hasExternTypes() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars());
|
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ bool NormalizedType::isTruthy() const
|
||||||
static bool isShallowInhabited(const NormalizedType& norm)
|
static bool isShallowInhabited(const NormalizedType& norm)
|
||||||
{
|
{
|
||||||
// This test is just a shallow check, for example it returns `true` for `{ p : never }`
|
// This test is just a shallow check, for example it returns `true` for `{ p : never }`
|
||||||
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || !norm.classes.isNever() || !get<NeverType>(norm.errors) ||
|
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || !norm.externTypes.isNever() || !get<NeverType>(norm.errors) ||
|
||||||
!get<NeverType>(norm.nils) || !get<NeverType>(norm.numbers) || !norm.strings.isNever() || !get<NeverType>(norm.threads) ||
|
!get<NeverType>(norm.nils) || !get<NeverType>(norm.numbers) || !norm.strings.isNever() || !get<NeverType>(norm.threads) ||
|
||||||
!get<NeverType>(norm.buffers) || !norm.functions.isNever() || !norm.tables.empty() || !norm.tyvars.empty();
|
!get<NeverType>(norm.buffers) || !norm.functions.isNever() || !norm.tables.empty() || !norm.tyvars.empty();
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ NormalizationResult Normalizer::isInhabited(const NormalizedType* norm, Set<Type
|
||||||
return NormalizationResult::HitLimits;
|
return NormalizationResult::HitLimits;
|
||||||
|
|
||||||
if (!get<NeverType>(norm->tops) || !get<NeverType>(norm->booleans) || !get<NeverType>(norm->errors) || !get<NeverType>(norm->nils) ||
|
if (!get<NeverType>(norm->tops) || !get<NeverType>(norm->booleans) || !get<NeverType>(norm->errors) || !get<NeverType>(norm->nils) ||
|
||||||
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || !get<NeverType>(norm->buffers) || !norm->classes.isNever() ||
|
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || !get<NeverType>(norm->buffers) || !norm->externTypes.isNever() ||
|
||||||
!norm->strings.isNever() || !norm->functions.isNever())
|
!norm->strings.isNever() || !norm->functions.isNever())
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
|
|
||||||
|
@ -619,13 +619,13 @@ static int tyvarIndex(TypeId ty)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedClassType& classes)
|
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedExternType& externTypes)
|
||||||
{
|
{
|
||||||
if (classes.classes.size() != 1)
|
if (externTypes.externTypes.size() != 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto first = classes.classes.begin();
|
auto first = externTypes.externTypes.begin();
|
||||||
if (first->first != builtinTypes->classType)
|
if (first->first != builtinTypes->externType)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!first->second.empty())
|
if (!first->second.empty())
|
||||||
|
@ -634,11 +634,11 @@ static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedClassType&
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedClassType& classes)
|
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedExternType& externTypes)
|
||||||
{
|
{
|
||||||
classes.ordering.clear();
|
externTypes.ordering.clear();
|
||||||
classes.classes.clear();
|
externTypes.externTypes.clear();
|
||||||
classes.pushPair(builtinTypes->classType, TypeIds{});
|
externTypes.pushPair(builtinTypes->externType, TypeIds{});
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LUAU_ASSERTENABLED
|
#ifdef LUAU_ASSERTENABLED
|
||||||
|
@ -762,50 +762,50 @@ static bool areNormalizedTables(const TypeIds& tys)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool areNormalizedClasses(const NormalizedClassType& tys)
|
static bool areNormalizedExternTypes(const NormalizedExternType& tys)
|
||||||
{
|
{
|
||||||
for (const auto& [ty, negations] : tys.classes)
|
for (const auto& [ty, negations] : tys.externTypes)
|
||||||
{
|
{
|
||||||
const ClassType* ctv = get<ClassType>(ty);
|
const ExternType* etv = get<ExternType>(ty);
|
||||||
if (!ctv)
|
if (!etv)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TypeId negation : negations)
|
for (TypeId negation : negations)
|
||||||
{
|
{
|
||||||
const ClassType* nctv = get<ClassType>(negation);
|
const ExternType* nctv = get<ExternType>(negation);
|
||||||
if (!nctv)
|
if (!nctv)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSubclass(nctv, ctv))
|
if (!isSubclass(nctv, etv))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [otherTy, otherNegations] : tys.classes)
|
for (const auto& [otherTy, otherNegations] : tys.externTypes)
|
||||||
{
|
{
|
||||||
if (otherTy == ty)
|
if (otherTy == ty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ClassType* octv = get<ClassType>(otherTy);
|
const ExternType* octv = get<ExternType>(otherTy);
|
||||||
if (!octv)
|
if (!octv)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSubclass(ctv, octv))
|
if (isSubclass(etv, octv))
|
||||||
{
|
{
|
||||||
auto iss = [ctv](TypeId t)
|
auto iss = [etv](TypeId t)
|
||||||
{
|
{
|
||||||
const ClassType* c = get<ClassType>(t);
|
const ExternType* c = get<ExternType>(t);
|
||||||
if (!c)
|
if (!c)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return isSubclass(ctv, c);
|
return isSubclass(etv, c);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!std::any_of(otherNegations.begin(), otherNegations.end(), iss))
|
if (!std::any_of(otherNegations.begin(), otherNegations.end(), iss))
|
||||||
|
@ -847,7 +847,7 @@ static void assertInvariant(const NormalizedType& norm)
|
||||||
|
|
||||||
LUAU_ASSERT(isNormalizedTop(norm.tops));
|
LUAU_ASSERT(isNormalizedTop(norm.tops));
|
||||||
LUAU_ASSERT(isNormalizedBoolean(norm.booleans));
|
LUAU_ASSERT(isNormalizedBoolean(norm.booleans));
|
||||||
LUAU_ASSERT(areNormalizedClasses(norm.classes));
|
LUAU_ASSERT(areNormalizedExternTypes(norm.externTypes));
|
||||||
LUAU_ASSERT(isNormalizedError(norm.errors));
|
LUAU_ASSERT(isNormalizedError(norm.errors));
|
||||||
LUAU_ASSERT(isNormalizedNil(norm.nils));
|
LUAU_ASSERT(isNormalizedNil(norm.nils));
|
||||||
LUAU_ASSERT(isNormalizedNumber(norm.numbers));
|
LUAU_ASSERT(isNormalizedNumber(norm.numbers));
|
||||||
|
@ -988,7 +988,7 @@ void Normalizer::clearNormal(NormalizedType& norm)
|
||||||
{
|
{
|
||||||
norm.tops = builtinTypes->neverType;
|
norm.tops = builtinTypes->neverType;
|
||||||
norm.booleans = builtinTypes->neverType;
|
norm.booleans = builtinTypes->neverType;
|
||||||
norm.classes.resetToNever();
|
norm.externTypes.resetToNever();
|
||||||
norm.errors = builtinTypes->neverType;
|
norm.errors = builtinTypes->neverType;
|
||||||
norm.nils = builtinTypes->neverType;
|
norm.nils = builtinTypes->neverType;
|
||||||
norm.numbers = builtinTypes->neverType;
|
norm.numbers = builtinTypes->neverType;
|
||||||
|
@ -1138,17 +1138,17 @@ TypeId Normalizer::unionOfBools(TypeId here, TypeId there)
|
||||||
return builtinTypes->booleanType;
|
return builtinTypes->booleanType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::unionClassesWithClass(TypeIds& heres, TypeId there)
|
void Normalizer::unionExternTypesWithExternType(TypeIds& heres, TypeId there)
|
||||||
{
|
{
|
||||||
if (heres.count(there))
|
if (heres.count(there))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ClassType* tctv = get<ClassType>(there);
|
const ExternType* tctv = get<ExternType>(there);
|
||||||
|
|
||||||
for (auto it = heres.begin(); it != heres.end();)
|
for (auto it = heres.begin(); it != heres.end();)
|
||||||
{
|
{
|
||||||
TypeId here = *it;
|
TypeId here = *it;
|
||||||
const ClassType* hctv = get<ClassType>(here);
|
const ExternType* hctv = get<ExternType>(here);
|
||||||
if (isSubclass(tctv, hctv))
|
if (isSubclass(tctv, hctv))
|
||||||
return;
|
return;
|
||||||
else if (isSubclass(hctv, tctv))
|
else if (isSubclass(hctv, tctv))
|
||||||
|
@ -1160,16 +1160,16 @@ void Normalizer::unionClassesWithClass(TypeIds& heres, TypeId there)
|
||||||
heres.insert(there);
|
heres.insert(there);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::unionClasses(TypeIds& heres, const TypeIds& theres)
|
void Normalizer::unionExternTypes(TypeIds& heres, const TypeIds& theres)
|
||||||
{
|
{
|
||||||
for (TypeId there : theres)
|
for (TypeId there : theres)
|
||||||
unionClassesWithClass(heres, there);
|
unionExternTypesWithExternType(heres, there);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isSubclass(TypeId test, TypeId parent)
|
static bool isSubclass(TypeId test, TypeId parent)
|
||||||
{
|
{
|
||||||
const ClassType* testCtv = get<ClassType>(test);
|
const ExternType* testCtv = get<ExternType>(test);
|
||||||
const ClassType* parentCtv = get<ClassType>(parent);
|
const ExternType* parentCtv = get<ExternType>(parent);
|
||||||
|
|
||||||
LUAU_ASSERT(testCtv);
|
LUAU_ASSERT(testCtv);
|
||||||
LUAU_ASSERT(parentCtv);
|
LUAU_ASSERT(parentCtv);
|
||||||
|
@ -1177,12 +1177,12 @@ static bool isSubclass(TypeId test, TypeId parent)
|
||||||
return isSubclass(testCtv, parentCtv);
|
return isSubclass(testCtv, parentCtv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
|
||||||
{
|
{
|
||||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||||
{
|
{
|
||||||
TypeId hereTy = *it;
|
TypeId hereTy = *it;
|
||||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||||
|
|
||||||
// If the incoming class is a subclass of another class in the map, we
|
// If the incoming class is a subclass of another class in the map, we
|
||||||
// must ensure that it is negated by one of the negations in the same
|
// must ensure that it is negated by one of the negations in the same
|
||||||
|
@ -1204,7 +1204,7 @@ void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
||||||
}
|
}
|
||||||
// If the incoming class is a superclass of one of the
|
// If the incoming class is a superclass of one of the
|
||||||
// negations, then the negation no longer applies and must be
|
// negations, then the negation no longer applies and must be
|
||||||
// removed. This is also true if they are equal. Since classes
|
// removed. This is also true if they are equal. Since extern types
|
||||||
// are, at this time, entirely persistent (we do not clone
|
// are, at this time, entirely persistent (we do not clone
|
||||||
// them), a pointer identity check is sufficient.
|
// them), a pointer identity check is sufficient.
|
||||||
else if (isSubclass(hereNegation, there))
|
else if (isSubclass(hereNegation, there))
|
||||||
|
@ -1231,7 +1231,7 @@ void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
||||||
{
|
{
|
||||||
TypeIds negations = std::move(hereNegations);
|
TypeIds negations = std::move(hereNegations);
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
|
|
||||||
heres.pushPair(there, std::move(negations));
|
heres.pushPair(there, std::move(negations));
|
||||||
return;
|
return;
|
||||||
|
@ -1248,10 +1248,10 @@ void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
||||||
heres.pushPair(there, TypeIds{});
|
heres.pushPair(there, TypeIds{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
|
||||||
{
|
{
|
||||||
// This method bears much similarity with unionClassesWithClass, but is
|
// This method bears much similarity with unionExternTypesWithExternType, but is
|
||||||
// solving a more general problem. In unionClassesWithClass, we are dealing
|
// solving a more general problem. In unionExternTypesWithExternType, we are dealing
|
||||||
// with a singular positive type. Since it's one type, we can use early
|
// with a singular positive type. Since it's one type, we can use early
|
||||||
// returns as control flow. Since it's guaranteed to be positive, we do not
|
// returns as control flow. Since it's guaranteed to be positive, we do not
|
||||||
// have negations to worry about combining. The two aspects combine to make
|
// have negations to worry about combining. The two aspects combine to make
|
||||||
|
@ -1260,9 +1260,9 @@ void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassT
|
||||||
|
|
||||||
for (const TypeId thereTy : theres.ordering)
|
for (const TypeId thereTy : theres.ordering)
|
||||||
{
|
{
|
||||||
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
|
||||||
|
|
||||||
// If it happens that there are _no_ classes in the current map, or the
|
// If it happens that there are _no_ extern types in the current map, or the
|
||||||
// incoming class is completely unrelated to any class in the current
|
// incoming class is completely unrelated to any class in the current
|
||||||
// map, we must insert the incoming pair as-is.
|
// map, we must insert the incoming pair as-is.
|
||||||
bool insert = true;
|
bool insert = true;
|
||||||
|
@ -1270,7 +1270,7 @@ void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassT
|
||||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||||
{
|
{
|
||||||
TypeId hereTy = *it;
|
TypeId hereTy = *it;
|
||||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||||
|
|
||||||
if (isSubclass(thereTy, hereTy))
|
if (isSubclass(thereTy, hereTy))
|
||||||
{
|
{
|
||||||
|
@ -1294,7 +1294,7 @@ void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassT
|
||||||
// If the incoming class is a superclass of one of the
|
// If the incoming class is a superclass of one of the
|
||||||
// negations, then the negation no longer applies and must
|
// negations, then the negation no longer applies and must
|
||||||
// be removed. This is also true if they are equal. Since
|
// be removed. This is also true if they are equal. Since
|
||||||
// classes are, at this time, entirely persistent (we do not
|
// extern types are, at this time, entirely persistent (we do not
|
||||||
// clone them), a pointer identity check is sufficient.
|
// clone them), a pointer identity check is sufficient.
|
||||||
else if (isSubclass(hereNegateTy, thereTy))
|
else if (isSubclass(hereNegateTy, thereTy))
|
||||||
{
|
{
|
||||||
|
@ -1319,17 +1319,17 @@ void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassT
|
||||||
else if (isSubclass(hereTy, thereTy))
|
else if (isSubclass(hereTy, thereTy))
|
||||||
{
|
{
|
||||||
TypeIds negations = std::move(hereNegations);
|
TypeIds negations = std::move(hereNegations);
|
||||||
unionClasses(negations, thereNegations);
|
unionExternTypes(negations, thereNegations);
|
||||||
|
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
heres.pushPair(thereTy, std::move(negations));
|
heres.pushPair(thereTy, std::move(negations));
|
||||||
insert = false;
|
insert = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (hereTy == thereTy)
|
else if (hereTy == thereTy)
|
||||||
{
|
{
|
||||||
unionClasses(hereNegations, thereNegations);
|
unionExternTypes(hereNegations, thereNegations);
|
||||||
insert = false;
|
insert = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1690,7 +1690,7 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
||||||
return NormalizationResult::HitLimits;
|
return NormalizationResult::HitLimits;
|
||||||
|
|
||||||
here.booleans = unionOfBools(here.booleans, there.booleans);
|
here.booleans = unionOfBools(here.booleans, there.booleans);
|
||||||
unionClasses(here.classes, there.classes);
|
unionExternTypes(here.externTypes, there.externTypes);
|
||||||
|
|
||||||
here.errors = (get<NeverType>(there.errors) ? here.errors : there.errors);
|
here.errors = (get<NeverType>(there.errors) ? here.errors : there.errors);
|
||||||
here.nils = (get<NeverType>(there.nils) ? here.nils : there.nils);
|
here.nils = (get<NeverType>(there.nils) ? here.nils : there.nils);
|
||||||
|
@ -1830,8 +1830,8 @@ NormalizationResult Normalizer::unionNormalWithTy(
|
||||||
unionFunctionsWithFunction(here.functions, there);
|
unionFunctionsWithFunction(here.functions, there);
|
||||||
else if (get<TableType>(there) || get<MetatableType>(there))
|
else if (get<TableType>(there) || get<MetatableType>(there))
|
||||||
unionTablesWithTable(here.tables, there);
|
unionTablesWithTable(here.tables, there);
|
||||||
else if (get<ClassType>(there))
|
else if (get<ExternType>(there))
|
||||||
unionClassesWithClass(here.classes, there);
|
unionExternTypesWithExternType(here.externTypes, there);
|
||||||
else if (get<ErrorType>(there))
|
else if (get<ErrorType>(there))
|
||||||
here.errors = there;
|
here.errors = there;
|
||||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
||||||
|
@ -1944,29 +1944,29 @@ std::optional<NormalizedType> Normalizer::negateNormal(const NormalizedType& her
|
||||||
result.booleans = builtinTypes->trueType;
|
result.booleans = builtinTypes->trueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (here.classes.isNever())
|
if (here.externTypes.isNever())
|
||||||
{
|
{
|
||||||
resetToTop(builtinTypes, result.classes);
|
resetToTop(builtinTypes, result.externTypes);
|
||||||
}
|
}
|
||||||
else if (isTop(builtinTypes, result.classes))
|
else if (isTop(builtinTypes, result.externTypes))
|
||||||
{
|
{
|
||||||
result.classes.resetToNever();
|
result.externTypes.resetToNever();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeIds rootNegations{};
|
TypeIds rootNegations{};
|
||||||
|
|
||||||
for (const auto& [hereParent, hereNegations] : here.classes.classes)
|
for (const auto& [hereParent, hereNegations] : here.externTypes.externTypes)
|
||||||
{
|
{
|
||||||
if (hereParent != builtinTypes->classType)
|
if (hereParent != builtinTypes->externType)
|
||||||
rootNegations.insert(hereParent);
|
rootNegations.insert(hereParent);
|
||||||
|
|
||||||
for (TypeId hereNegation : hereNegations)
|
for (TypeId hereNegation : hereNegations)
|
||||||
unionClassesWithClass(result.classes, hereNegation);
|
unionExternTypesWithExternType(result.externTypes, hereNegation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rootNegations.empty())
|
if (!rootNegations.empty())
|
||||||
result.classes.pushPair(builtinTypes->classType, rootNegations);
|
result.externTypes.pushPair(builtinTypes->externType, rootNegations);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
|
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
|
||||||
|
@ -2144,7 +2144,7 @@ TypeId Normalizer::intersectionOfBools(TypeId here, TypeId there)
|
||||||
return there;
|
return there;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
void Normalizer::intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
|
||||||
{
|
{
|
||||||
if (theres.isNever())
|
if (theres.isNever())
|
||||||
{
|
{
|
||||||
|
@ -2178,12 +2178,12 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl
|
||||||
// declare the result of the intersection operation to be never.
|
// declare the result of the intersection operation to be never.
|
||||||
for (const TypeId thereTy : theres.ordering)
|
for (const TypeId thereTy : theres.ordering)
|
||||||
{
|
{
|
||||||
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
|
||||||
|
|
||||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||||
{
|
{
|
||||||
TypeId hereTy = *it;
|
TypeId hereTy = *it;
|
||||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||||
|
|
||||||
if (isSubclass(thereTy, hereTy))
|
if (isSubclass(thereTy, hereTy))
|
||||||
{
|
{
|
||||||
|
@ -2206,10 +2206,10 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unionClasses(negations, thereNegations);
|
unionExternTypes(negations, thereNegations);
|
||||||
|
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
heres.pushPair(thereTy, std::move(negations));
|
heres.pushPair(thereTy, std::move(negations));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2234,15 +2234,15 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl
|
||||||
{
|
{
|
||||||
if (isSubclass(hereTy, *nIt))
|
if (isSubclass(hereTy, *nIt))
|
||||||
{
|
{
|
||||||
// eg SomeClass & (class & ~SomeClass)
|
// eg SomeExternType & (class & ~SomeExternType)
|
||||||
// or SomeClass & (class & ~ParentClass)
|
// or SomeExternType & (class & ~ParentExternType)
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
erasedHere = true;
|
erasedHere = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eg SomeClass & (class & ~Unrelated)
|
// eg SomeExternType & (class & ~Unrelated)
|
||||||
if (!isSubclass(*nIt, hereTy))
|
if (!isSubclass(*nIt, hereTy))
|
||||||
nIt = negations.erase(nIt);
|
nIt = negations.erase(nIt);
|
||||||
else
|
else
|
||||||
|
@ -2251,30 +2251,30 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl
|
||||||
|
|
||||||
if (!erasedHere)
|
if (!erasedHere)
|
||||||
{
|
{
|
||||||
unionClasses(hereNegations, negations);
|
unionExternTypes(hereNegations, negations);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (hereTy == thereTy)
|
else if (hereTy == thereTy)
|
||||||
{
|
{
|
||||||
unionClasses(hereNegations, thereNegations);
|
unionExternTypes(hereNegations, thereNegations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId there)
|
void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
|
||||||
{
|
{
|
||||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||||
{
|
{
|
||||||
TypeId hereTy = *it;
|
TypeId hereTy = *it;
|
||||||
const TypeIds& hereNegations = heres.classes.at(hereTy);
|
const TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||||
|
|
||||||
// If the incoming class _is_ the current class, we skip it. Maybe
|
// If the incoming class _is_ the current class, we skip it. Maybe
|
||||||
// another entry will have a different story. We check for this first
|
// another entry will have a different story. We check for this first
|
||||||
|
@ -2319,7 +2319,7 @@ void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId th
|
||||||
}
|
}
|
||||||
|
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
if (!emptyIntersectWithNegation)
|
if (!emptyIntersectWithNegation)
|
||||||
heres.pushPair(there, std::move(negations));
|
heres.pushPair(there, std::move(negations));
|
||||||
break;
|
break;
|
||||||
|
@ -2335,7 +2335,7 @@ void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId th
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
it = heres.ordering.erase(it);
|
it = heres.ordering.erase(it);
|
||||||
heres.classes.erase(hereTy);
|
heres.externTypes.erase(hereTy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3083,7 +3083,7 @@ NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const Nor
|
||||||
|
|
||||||
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
||||||
|
|
||||||
intersectClasses(here.classes, there.classes);
|
intersectExternTypes(here.externTypes, there.externTypes);
|
||||||
here.errors = (get<NeverType>(there.errors) ? there.errors : here.errors);
|
here.errors = (get<NeverType>(there.errors) ? there.errors : here.errors);
|
||||||
here.nils = (get<NeverType>(there.nils) ? there.nils : here.nils);
|
here.nils = (get<NeverType>(there.nils) ? there.nils : here.nils);
|
||||||
here.numbers = (get<NeverType>(there.numbers) ? there.numbers : here.numbers);
|
here.numbers = (get<NeverType>(there.numbers) ? there.numbers : here.numbers);
|
||||||
|
@ -3205,12 +3205,12 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
|
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
|
||||||
here.tables = std::move(tables);
|
here.tables = std::move(tables);
|
||||||
}
|
}
|
||||||
else if (get<ClassType>(there))
|
else if (get<ExternType>(there))
|
||||||
{
|
{
|
||||||
NormalizedClassType nct = std::move(here.classes);
|
NormalizedExternType nct = std::move(here.externTypes);
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
intersectClassesWithClass(nct, there);
|
intersectExternTypesWithExternType(nct, there);
|
||||||
here.classes = std::move(nct);
|
here.externTypes = std::move(nct);
|
||||||
}
|
}
|
||||||
else if (get<ErrorType>(there))
|
else if (get<ErrorType>(there))
|
||||||
{
|
{
|
||||||
|
@ -3274,7 +3274,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
subtractPrimitive(here, ntv->ty);
|
subtractPrimitive(here, ntv->ty);
|
||||||
else if (const SingletonType* stv = get<SingletonType>(t))
|
else if (const SingletonType* stv = get<SingletonType>(t))
|
||||||
subtractSingleton(here, follow(ntv->ty));
|
subtractSingleton(here, follow(ntv->ty));
|
||||||
else if (get<ClassType>(t))
|
else if (get<ExternType>(t))
|
||||||
{
|
{
|
||||||
NormalizationResult res = intersectNormalWithNegationTy(t, here);
|
NormalizationResult res = intersectNormalWithNegationTy(t, here);
|
||||||
if (shouldEarlyExit(res))
|
if (shouldEarlyExit(res))
|
||||||
|
@ -3334,7 +3334,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
}
|
}
|
||||||
else if (get<NeverType>(there))
|
else if (get<NeverType>(there))
|
||||||
{
|
{
|
||||||
here.classes.resetToNever();
|
here.externTypes.resetToNever();
|
||||||
}
|
}
|
||||||
else if (get<NoRefineType>(there))
|
else if (get<NoRefineType>(there))
|
||||||
{
|
{
|
||||||
|
@ -3403,18 +3403,18 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
||||||
if (!get<NeverType>(norm.booleans))
|
if (!get<NeverType>(norm.booleans))
|
||||||
result.push_back(norm.booleans);
|
result.push_back(norm.booleans);
|
||||||
|
|
||||||
if (isTop(builtinTypes, norm.classes))
|
if (isTop(builtinTypes, norm.externTypes))
|
||||||
{
|
{
|
||||||
result.push_back(builtinTypes->classType);
|
result.push_back(builtinTypes->externType);
|
||||||
}
|
}
|
||||||
else if (!norm.classes.isNever())
|
else if (!norm.externTypes.isNever())
|
||||||
{
|
{
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
parts.reserve(norm.classes.classes.size());
|
parts.reserve(norm.externTypes.externTypes.size());
|
||||||
|
|
||||||
for (const TypeId normTy : norm.classes.ordering)
|
for (const TypeId normTy : norm.externTypes.ordering)
|
||||||
{
|
{
|
||||||
const TypeIds& normNegations = norm.classes.classes.at(normTy);
|
const TypeIds& normNegations = norm.externTypes.externTypes.at(normTy);
|
||||||
|
|
||||||
if (normNegations.empty())
|
if (normNegations.empty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -65,6 +67,12 @@ struct RequireTracer : AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypePack* node) override
|
||||||
|
{
|
||||||
|
// allow resolving require inside `typeof` annotations
|
||||||
|
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||||
|
}
|
||||||
|
|
||||||
AstExpr* getDependent_DEPRECATED(AstExpr* node)
|
AstExpr* getDependent_DEPRECATED(AstExpr* node)
|
||||||
{
|
{
|
||||||
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
||||||
|
|
|
@ -356,7 +356,7 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
// * FunctionType
|
// * FunctionType
|
||||||
// * TableType
|
// * TableType
|
||||||
// * MetatableType
|
// * MetatableType
|
||||||
// * ClassType
|
// * ExternType
|
||||||
// * UnionType
|
// * UnionType
|
||||||
// * IntersectionType
|
// * IntersectionType
|
||||||
// * NegationType
|
// * NegationType
|
||||||
|
@ -476,13 +476,13 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ClassType>(right))
|
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto ls = get<SingletonType>(left))
|
if (auto ls = get<SingletonType>(left))
|
||||||
{
|
{
|
||||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ClassType>(right))
|
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
|
|
||||||
if (get<PrimitiveType>(right))
|
if (get<PrimitiveType>(right))
|
||||||
|
@ -551,9 +551,9 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto ct = get<ClassType>(left))
|
if (auto ct = get<ExternType>(left))
|
||||||
{
|
{
|
||||||
if (auto rct = get<ClassType>(right))
|
if (auto rct = get<ExternType>(right))
|
||||||
{
|
{
|
||||||
if (isSubclass(ct, rct))
|
if (isSubclass(ct, rct))
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
|
@ -873,7 +873,7 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) co
|
||||||
if (is<NeverType, ErrorType>(target))
|
if (is<NeverType, ErrorType>(target))
|
||||||
return target;
|
return target;
|
||||||
|
|
||||||
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||||
return target;
|
return target;
|
||||||
|
|
||||||
if (auto pt = get<PrimitiveType>(target))
|
if (auto pt = get<PrimitiveType>(target))
|
||||||
|
@ -909,7 +909,7 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) con
|
||||||
if (is<UnknownType>(target))
|
if (is<UnknownType>(target))
|
||||||
return builtinTypes->falsyType;
|
return builtinTypes->falsyType;
|
||||||
|
|
||||||
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||||
return builtinTypes->neverType;
|
return builtinTypes->neverType;
|
||||||
|
|
||||||
if (auto pt = get<PrimitiveType>(target))
|
if (auto pt = get<PrimitiveType>(target))
|
||||||
|
|
|
@ -136,9 +136,9 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
||||||
clone.parts = a.parts;
|
clone.parts = a.parts;
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, ClassType>)
|
else if constexpr (std::is_same_v<T, ExternType>)
|
||||||
{
|
{
|
||||||
ClassType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
ExternType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, NegationType>)
|
else if constexpr (std::is_same_v<T, NegationType>)
|
||||||
|
@ -252,21 +252,21 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
||||||
for (TypePackId a : tfit->packArguments)
|
for (TypePackId a : tfit->packArguments)
|
||||||
visitChild(a);
|
visitChild(a);
|
||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(ty))
|
else if (const ExternType* etv = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
for (const auto& [name, prop] : ctv->props)
|
for (const auto& [name, prop] : etv->props)
|
||||||
visitChild(prop.type());
|
visitChild(prop.type());
|
||||||
|
|
||||||
if (ctv->parent)
|
if (etv->parent)
|
||||||
visitChild(*ctv->parent);
|
visitChild(*etv->parent);
|
||||||
|
|
||||||
if (ctv->metatable)
|
if (etv->metatable)
|
||||||
visitChild(*ctv->metatable);
|
visitChild(*etv->metatable);
|
||||||
|
|
||||||
if (ctv->indexer)
|
if (etv->indexer)
|
||||||
{
|
{
|
||||||
visitChild(ctv->indexer->indexType);
|
visitChild(etv->indexer->indexType);
|
||||||
visitChild(ctv->indexer->indexResultType);
|
visitChild(etv->indexer->indexResultType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const NegationType* ntv = get<NegationType>(ty))
|
else if (const NegationType* ntv = get<NegationType>(ty))
|
||||||
|
@ -838,21 +838,21 @@ void Substitution::replaceChildren(TypeId ty)
|
||||||
for (TypePackId& a : tfit->packArguments)
|
for (TypePackId& a : tfit->packArguments)
|
||||||
a = replace(a);
|
a = replace(a);
|
||||||
}
|
}
|
||||||
else if (ClassType* ctv = getMutable<ClassType>(ty))
|
else if (ExternType* etv = getMutable<ExternType>(ty))
|
||||||
{
|
{
|
||||||
for (auto& [name, prop] : ctv->props)
|
for (auto& [name, prop] : etv->props)
|
||||||
prop.setType(replace(prop.type()));
|
prop.setType(replace(prop.type()));
|
||||||
|
|
||||||
if (ctv->parent)
|
if (etv->parent)
|
||||||
ctv->parent = replace(*ctv->parent);
|
etv->parent = replace(*etv->parent);
|
||||||
|
|
||||||
if (ctv->metatable)
|
if (etv->metatable)
|
||||||
ctv->metatable = replace(*ctv->metatable);
|
etv->metatable = replace(*etv->metatable);
|
||||||
|
|
||||||
if (ctv->indexer)
|
if (etv->indexer)
|
||||||
{
|
{
|
||||||
ctv->indexer->indexType = replace(ctv->indexer->indexType);
|
etv->indexer->indexType = replace(etv->indexer->indexType);
|
||||||
ctv->indexer->indexResultType = replace(ctv->indexer->indexResultType);
|
etv->indexer->indexResultType = replace(etv->indexer->indexResultType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (NegationType* ntv = getMutable<NegationType>(ty))
|
else if (NegationType* ntv = getMutable<NegationType>(ty))
|
||||||
|
|
|
@ -315,7 +315,7 @@ struct ApplyMappedGenerics : Substitution
|
||||||
|
|
||||||
bool ignoreChildren(TypeId ty) override
|
bool ignoreChildren(TypeId ty) override
|
||||||
{
|
{
|
||||||
if (get<ClassType>(ty))
|
if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return ty->persistent;
|
return ty->persistent;
|
||||||
|
@ -744,9 +744,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
result = isCovariantWith(env, p, scope);
|
result = isCovariantWith(env, p, scope);
|
||||||
else if (auto p = get2<MetatableType, TableType>(subTy, superTy))
|
else if (auto p = get2<MetatableType, TableType>(subTy, superTy))
|
||||||
result = isCovariantWith(env, p, scope);
|
result = isCovariantWith(env, p, scope);
|
||||||
else if (auto p = get2<ClassType, ClassType>(subTy, superTy))
|
else if (auto p = get2<ExternType, ExternType>(subTy, superTy))
|
||||||
result = isCovariantWith(env, p, scope);
|
result = isCovariantWith(env, p, scope);
|
||||||
else if (auto p = get2<ClassType, TableType>(subTy, superTy))
|
else if (auto p = get2<ExternType, TableType>(subTy, superTy))
|
||||||
result = isCovariantWith(env, subTy, p.first, superTy, p.second, scope);
|
result = isCovariantWith(env, subTy, p.first, superTy, p.second, scope);
|
||||||
else if (auto p = get2<TableType, PrimitiveType>(subTy, superTy))
|
else if (auto p = get2<TableType, PrimitiveType>(subTy, superTy))
|
||||||
result = isCovariantWith(env, p, scope);
|
result = isCovariantWith(env, p, scope);
|
||||||
|
@ -1336,7 +1336,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||||
}
|
}
|
||||||
// the top class type is not actually a primitive type, so the negation of
|
// the top class type is not actually a primitive type, so the negation of
|
||||||
// any one of them includes the top class type.
|
// any one of them includes the top class type.
|
||||||
else if (auto p = get2<ClassType, PrimitiveType>(subTy, negatedTy))
|
else if (auto p = get2<ExternType, PrimitiveType>(subTy, negatedTy))
|
||||||
result = {true};
|
result = {true};
|
||||||
else if (auto p = get<PrimitiveType>(negatedTy); p && is<TableType, MetatableType>(subTy))
|
else if (auto p = get<PrimitiveType>(negatedTy); p && is<TableType, MetatableType>(subTy))
|
||||||
result = {p->type != PrimitiveType::Table};
|
result = {p->type != PrimitiveType::Table};
|
||||||
|
@ -1344,9 +1344,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||||
result = {p.second->type != PrimitiveType::Function};
|
result = {p.second->type != PrimitiveType::Function};
|
||||||
else if (auto p = get2<SingletonType, SingletonType>(subTy, negatedTy))
|
else if (auto p = get2<SingletonType, SingletonType>(subTy, negatedTy))
|
||||||
result = {*p.first != *p.second};
|
result = {*p.first != *p.second};
|
||||||
else if (auto p = get2<ClassType, ClassType>(subTy, negatedTy))
|
else if (auto p = get2<ExternType, ExternType>(subTy, negatedTy))
|
||||||
result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second, scope));
|
result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second, scope));
|
||||||
else if (get2<FunctionType, ClassType>(subTy, negatedTy))
|
else if (get2<FunctionType, ExternType>(subTy, negatedTy))
|
||||||
result = {true};
|
result = {true};
|
||||||
else if (is<ErrorType, FunctionType, TableType, MetatableType>(negatedTy))
|
else if (is<ErrorType, FunctionType, TableType, MetatableType>(negatedTy))
|
||||||
iceReporter->ice("attempting to negate a non-testable type");
|
iceReporter->ice("attempting to negate a non-testable type");
|
||||||
|
@ -1471,15 +1471,15 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope)
|
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope)
|
||||||
{
|
{
|
||||||
return {isSubclass(subClass, superClass)};
|
return {isSubclass(subExternType, superExternType)};
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(
|
SubtypingResult Subtyping::isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
TypeId subTy,
|
TypeId subTy,
|
||||||
const ClassType* subClass,
|
const ExternType* subExternType,
|
||||||
TypeId superTy,
|
TypeId superTy,
|
||||||
const TableType* superTable,
|
const TableType* superTable,
|
||||||
NotNull<Scope> scope
|
NotNull<Scope> scope
|
||||||
|
@ -1491,7 +1491,7 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
|
|
||||||
for (const auto& [name, prop] : superTable->props)
|
for (const auto& [name, prop] : superTable->props)
|
||||||
{
|
{
|
||||||
if (auto classProp = lookupClassProp(subClass, name))
|
if (auto classProp = lookupExternTypeProp(subExternType, name))
|
||||||
{
|
{
|
||||||
result.andAlso(isCovariantWith(env, *classProp, prop, name, scope));
|
result.andAlso(isCovariantWith(env, *classProp, prop, name, scope));
|
||||||
}
|
}
|
||||||
|
@ -1661,7 +1661,7 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
||||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
||||||
result.andAlso(
|
result.andAlso(
|
||||||
isCovariantWith(env, subNorm->classes, superNorm->classes, scope).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables, scope))
|
isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope).orElse(isCovariantWith(env, subNorm->externTypes, superNorm->tables, scope))
|
||||||
);
|
);
|
||||||
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
||||||
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
||||||
|
@ -1678,24 +1678,24 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(
|
SubtypingResult Subtyping::isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const NormalizedClassType& subClass,
|
const NormalizedExternType& subExternType,
|
||||||
const NormalizedClassType& superClass,
|
const NormalizedExternType& superExternType,
|
||||||
NotNull<Scope> scope
|
NotNull<Scope> scope
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (const auto& [subClassTy, _] : subClass.classes)
|
for (const auto& [subExternTypeTy, _] : subExternType.externTypes)
|
||||||
{
|
{
|
||||||
SubtypingResult result;
|
SubtypingResult result;
|
||||||
|
|
||||||
for (const auto& [superClassTy, superNegations] : superClass.classes)
|
for (const auto& [superExternTypeTy, superNegations] : superExternType.externTypes)
|
||||||
{
|
{
|
||||||
result.orElse(isCovariantWith(env, subClassTy, superClassTy, scope));
|
result.orElse(isCovariantWith(env, subExternTypeTy, superExternTypeTy, scope));
|
||||||
if (!result.isSubtype)
|
if (!result.isSubtype)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (TypeId negation : superNegations)
|
for (TypeId negation : superNegations)
|
||||||
{
|
{
|
||||||
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation, scope)));
|
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subExternTypeTy, negation, scope)));
|
||||||
if (result.isSubtype)
|
if (result.isSubtype)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1710,17 +1710,17 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(
|
SubtypingResult Subtyping::isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const NormalizedClassType& subClass,
|
const NormalizedExternType& subExternType,
|
||||||
const TypeIds& superTables,
|
const TypeIds& superTables,
|
||||||
NotNull<Scope> scope
|
NotNull<Scope> scope
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (const auto& [subClassTy, _] : subClass.classes)
|
for (const auto& [subExternTypeTy, _] : subExternType.externTypes)
|
||||||
{
|
{
|
||||||
SubtypingResult result;
|
SubtypingResult result;
|
||||||
|
|
||||||
for (TypeId superTableTy : superTables)
|
for (TypeId superTableTy : superTables)
|
||||||
result.orElse(isCovariantWith(env, subClassTy, superTableTy, scope));
|
result.orElse(isCovariantWith(env, subExternTypeTy, superTableTy, scope));
|
||||||
|
|
||||||
if (!result.isSubtype)
|
if (!result.isSubtype)
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -299,9 +299,9 @@ void StateDot::visitChildren(TypeId ty, int index)
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, ClassType>)
|
else if constexpr (std::is_same_v<T, ExternType>)
|
||||||
{
|
{
|
||||||
formatAppend(result, "ClassType %s", t.name.c_str());
|
formatAppend(result, "ExternType %s", t.name.c_str());
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
||||||
|
@ -121,7 +123,7 @@ struct FindCyclicTypes final : TypeVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -720,7 +722,13 @@ struct TypeStringifier
|
||||||
if (ttv.boundTo)
|
if (ttv.boundTo)
|
||||||
return stringify(*ttv.boundTo);
|
return stringify(*ttv.boundTo);
|
||||||
|
|
||||||
if (!state.exhaustive)
|
bool showName = !state.exhaustive;
|
||||||
|
if (FFlag::LuauEnableDenseTableAlias)
|
||||||
|
{
|
||||||
|
// if hide table alias expansions are enabled and there is a name found for the table, use it
|
||||||
|
showName = !state.exhaustive || state.opts.hideTableAliasExpansions;
|
||||||
|
}
|
||||||
|
if (showName)
|
||||||
{
|
{
|
||||||
if (ttv.name)
|
if (ttv.name)
|
||||||
{
|
{
|
||||||
|
@ -743,6 +751,10 @@ struct TypeStringifier
|
||||||
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
|
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.exhaustive)
|
||||||
|
{
|
||||||
if (ttv.syntheticName)
|
if (ttv.syntheticName)
|
||||||
{
|
{
|
||||||
state.result.invalid = true;
|
state.result.invalid = true;
|
||||||
|
@ -881,9 +893,9 @@ struct TypeStringifier
|
||||||
state.emit(" }");
|
state.emit(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypeId, const ClassType& ctv)
|
void operator()(TypeId, const ExternType& etv)
|
||||||
{
|
{
|
||||||
state.emit(ctv.name);
|
state.emit(etv.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypeId, const AnyType&)
|
void operator()(TypeId, const AnyType&)
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -297,6 +299,11 @@ struct ArcCollector : public AstVisitor
|
||||||
add(*name);
|
add(*name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypePack* node) override
|
||||||
|
{
|
||||||
|
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContainsFunctionCall : public AstVisitor
|
struct ContainsFunctionCall : public AstVisitor
|
||||||
|
|
|
@ -15,6 +15,7 @@ LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -331,7 +332,7 @@ struct Printer_DEPRECATED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg)
|
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true)
|
||||||
{
|
{
|
||||||
advance(annotation.location.begin);
|
advance(annotation.location.begin);
|
||||||
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||||
|
@ -349,7 +350,7 @@ struct Printer_DEPRECATED
|
||||||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!forVarArg);
|
LUAU_ASSERT(!forVarArg);
|
||||||
visualizeTypeList(explicitTp->typeList, true);
|
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1065,12 +1066,15 @@ struct Printer_DEPRECATED
|
||||||
|
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
|
|
||||||
if (writeTypes && func.returnAnnotation)
|
if (writeTypes && (FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value()))
|
||||||
{
|
{
|
||||||
writer.symbol(":");
|
writer.symbol(":");
|
||||||
writer.space();
|
writer.space();
|
||||||
|
|
||||||
visualizeTypeList(*func.returnAnnotation, false);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
||||||
|
else
|
||||||
|
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
visualizeBlock(*func.body);
|
visualizeBlock(*func.body);
|
||||||
|
@ -1174,7 +1178,10 @@ struct Printer_DEPRECATED
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.symbol("->");
|
writer.symbol("->");
|
||||||
visualizeTypeList(a->returnTypes, true);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visualizeTypePackAnnotation(*a->returnTypes, false);
|
||||||
|
else
|
||||||
|
visualizeTypeList(a->returnTypes_DEPRECATED, true);
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
|
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
|
||||||
{
|
{
|
||||||
|
@ -1368,7 +1375,7 @@ struct Printer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void visualizeTypePackAnnotation(AstTypePack& annotation, bool forVarArg)
|
void visualizeTypePackAnnotation(AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true)
|
||||||
{
|
{
|
||||||
advance(annotation.location.begin);
|
advance(annotation.location.begin);
|
||||||
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||||
|
@ -1390,10 +1397,10 @@ struct Printer
|
||||||
LUAU_ASSERT(!forVarArg);
|
LUAU_ASSERT(!forVarArg);
|
||||||
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
||||||
visualizeTypeList(
|
visualizeTypeList(
|
||||||
explicitTp->typeList, true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
explicitTp->typeList, FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
visualizeTypeList(explicitTp->typeList, true);
|
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2383,14 +2390,17 @@ struct Printer
|
||||||
advanceBefore(func.argLocation->end, 1);
|
advanceBefore(func.argLocation->end, 1);
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
|
|
||||||
if (writeTypes && func.returnAnnotation)
|
if (writeTypes && FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value())
|
||||||
{
|
{
|
||||||
if (cstNode)
|
if (cstNode)
|
||||||
advance(cstNode->returnSpecifierPosition);
|
advance(cstNode->returnSpecifierPosition);
|
||||||
writer.symbol(":");
|
writer.symbol(":");
|
||||||
writer.space();
|
writer.space();
|
||||||
|
|
||||||
visualizeTypeList(*func.returnAnnotation, false);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
||||||
|
else
|
||||||
|
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
visualizeBlock(*func.body);
|
visualizeBlock(*func.body);
|
||||||
|
@ -2573,7 +2583,10 @@ struct Printer
|
||||||
if (cstNode)
|
if (cstNode)
|
||||||
advance(cstNode->returnArrowPosition);
|
advance(cstNode->returnArrowPosition);
|
||||||
writer.symbol("->");
|
writer.symbol("->");
|
||||||
visualizeTypeList(a->returnTypes, true);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
visualizeTypePackAnnotation(*a->returnTypes, false);
|
||||||
|
else
|
||||||
|
visualizeTypeList(a->returnTypes_DEPRECATED, true);
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
|
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
|
||||||
{
|
{
|
||||||
|
|
|
@ -282,8 +282,8 @@ std::optional<TypeId> getMetatable(TypeId type, NotNull<BuiltinTypes> builtinTyp
|
||||||
|
|
||||||
if (const MetatableType* mtType = get<MetatableType>(type))
|
if (const MetatableType* mtType = get<MetatableType>(type))
|
||||||
return mtType->metatable;
|
return mtType->metatable;
|
||||||
else if (const ClassType* classType = get<ClassType>(type))
|
else if (const ExternType* externType = get<ExternType>(type))
|
||||||
return classType->metatable;
|
return externType->metatable;
|
||||||
else if (isString(type))
|
else if (isString(type))
|
||||||
{
|
{
|
||||||
auto ptv = get<PrimitiveType>(builtinTypes->stringType);
|
auto ptv = get<PrimitiveType>(builtinTypes->stringType);
|
||||||
|
@ -346,10 +346,10 @@ std::optional<ModuleName> getDefinitionModuleName(TypeId type)
|
||||||
if (ftv->definition)
|
if (ftv->definition)
|
||||||
return ftv->definition->definitionModuleName;
|
return ftv->definition->definitionModuleName;
|
||||||
}
|
}
|
||||||
else if (auto ctv = get<ClassType>(type))
|
else if (auto etv = get<ExternType>(type))
|
||||||
{
|
{
|
||||||
if (!ctv->definitionModuleName.empty())
|
if (!etv->definitionModuleName.empty())
|
||||||
return ctv->definitionModuleName;
|
return etv->definitionModuleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -1014,7 +1014,7 @@ BuiltinTypes::BuiltinTypes()
|
||||||
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
||||||
, bufferType(arena->addType(Type{PrimitiveType{PrimitiveType::Buffer}, /*persistent*/ true}))
|
, bufferType(arena->addType(Type{PrimitiveType{PrimitiveType::Buffer}, /*persistent*/ true}))
|
||||||
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
||||||
, classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}, {}}, /*persistent*/ true}))
|
, externType(arena->addType(Type{ExternType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}, {}}, /*persistent*/ true}))
|
||||||
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
||||||
, emptyTableType(arena->addType(Type{TableType{TableState::Sealed, TypeLevel{}, nullptr}, /*persistent*/ true}))
|
, emptyTableType(arena->addType(Type{TableType{TableState::Sealed, TypeLevel{}, nullptr}, /*persistent*/ true}))
|
||||||
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
||||||
|
@ -1026,6 +1026,7 @@ BuiltinTypes::BuiltinTypes()
|
||||||
, noRefineType(arena->addType(Type{NoRefineType{}, /*persistent*/ true}))
|
, noRefineType(arena->addType(Type{NoRefineType{}, /*persistent*/ true}))
|
||||||
, falsyType(arena->addType(Type{UnionType{{falseType, nilType}}, /*persistent*/ true}))
|
, falsyType(arena->addType(Type{UnionType{{falseType, nilType}}, /*persistent*/ true}))
|
||||||
, truthyType(arena->addType(Type{NegationType{falsyType}, /*persistent*/ true}))
|
, truthyType(arena->addType(Type{NegationType{falsyType}, /*persistent*/ true}))
|
||||||
|
, notNilType(arena->addType(Type{NegationType{nilType}, /*persistent*/ true}))
|
||||||
, optionalNumberType(arena->addType(Type{UnionType{{numberType, nilType}}, /*persistent*/ true}))
|
, optionalNumberType(arena->addType(Type{UnionType{{numberType, nilType}}, /*persistent*/ true}))
|
||||||
, optionalStringType(arena->addType(Type{UnionType{{stringType, nilType}}, /*persistent*/ true}))
|
, optionalStringType(arena->addType(Type{UnionType{{stringType, nilType}}, /*persistent*/ true}))
|
||||||
, emptyTypePack(arena->addTypePack(TypePackVar{TypePack{{}}, /*persistent*/ true}))
|
, emptyTypePack(arena->addTypePack(TypePackVar{TypePack{{}}, /*persistent*/ true}))
|
||||||
|
@ -1104,9 +1105,9 @@ void persist(TypeId ty)
|
||||||
queue.push_back(ttv->indexer->indexResultType);
|
queue.push_back(ttv->indexer->indexResultType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto ctv = get<ClassType>(t))
|
else if (auto etv= get<ExternType>(t))
|
||||||
{
|
{
|
||||||
for (const auto& [_name, prop] : ctv->props)
|
for (const auto& [_name, prop] : etv->props)
|
||||||
queue.push_back(prop.type());
|
queue.push_back(prop.type());
|
||||||
}
|
}
|
||||||
else if (auto utv = get<UnionType>(t))
|
else if (auto utv = get<UnionType>(t))
|
||||||
|
@ -1206,7 +1207,7 @@ std::optional<TypeLevel> getLevel(TypePackId tp)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Property* lookupClassProp(const ClassType* cls, const Name& name)
|
const Property* lookupExternTypeProp(const ExternType* cls, const Name& name)
|
||||||
{
|
{
|
||||||
while (cls)
|
while (cls)
|
||||||
{
|
{
|
||||||
|
@ -1215,7 +1216,7 @@ const Property* lookupClassProp(const ClassType* cls, const Name& name)
|
||||||
return &it->second;
|
return &it->second;
|
||||||
|
|
||||||
if (cls->parent)
|
if (cls->parent)
|
||||||
cls = get<ClassType>(*cls->parent);
|
cls = get<ExternType>(*cls->parent);
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -1225,7 +1226,7 @@ const Property* lookupClassProp(const ClassType* cls, const Name& name)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSubclass(const ClassType* cls, const ClassType* parent)
|
bool isSubclass(const ExternType* cls, const ExternType* parent)
|
||||||
{
|
{
|
||||||
while (cls)
|
while (cls)
|
||||||
{
|
{
|
||||||
|
@ -1234,7 +1235,7 @@ bool isSubclass(const ClassType* cls, const ClassType* parent)
|
||||||
else if (!cls->parent)
|
else if (!cls->parent)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cls = get<ClassType>(*cls->parent);
|
cls = get<ExternType>(*cls->parent);
|
||||||
LUAU_ASSERT(cls);
|
LUAU_ASSERT(cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1303,8 +1304,8 @@ static Tags* getTags(TypeId ty)
|
||||||
return &ftv->tags;
|
return &ftv->tags;
|
||||||
else if (auto ttv = getMutable<TableType>(ty))
|
else if (auto ttv = getMutable<TableType>(ty))
|
||||||
return &ttv->tags;
|
return &ttv->tags;
|
||||||
else if (auto ctv = getMutable<ClassType>(ty))
|
else if (auto etv = getMutable<ExternType>(ty))
|
||||||
return &ctv->tags;
|
return &etv->tags;
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1334,19 +1335,19 @@ bool hasTag(TypeId ty, const std::string& tagName)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
// We special case classes because getTags only returns a pointer to one vector of tags.
|
// We special case extern types because getTags only returns a pointer to one vector of tags.
|
||||||
// But classes has multiple vector of tags, represented throughout the hierarchy.
|
// But extern types has multiple vector of tags, represented throughout the hierarchy.
|
||||||
if (auto ctv = get<ClassType>(ty))
|
if (auto etv = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
while (ctv)
|
while (etv)
|
||||||
{
|
{
|
||||||
if (hasTag(ctv->tags, tagName))
|
if (hasTag(etv->tags, tagName))
|
||||||
return true;
|
return true;
|
||||||
else if (!ctv->parent)
|
else if (!etv->parent)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ctv = get<ClassType>(*ctv->parent);
|
etv = get<ExternType>(*etv->parent);
|
||||||
LUAU_ASSERT(ctv);
|
LUAU_ASSERT(etv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto tags = getTags(ty))
|
else if (auto tags = getTags(ty))
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||||
{
|
{
|
||||||
|
@ -219,21 +220,21 @@ public:
|
||||||
return Luau::visit(*this, mtv.table->ty);
|
return Luau::visit(*this, mtv.table->ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstType* operator()(const ClassType& ctv)
|
AstType* operator()(const ExternType& etv)
|
||||||
{
|
{
|
||||||
RecursionCounter counter(&count);
|
RecursionCounter counter(&count);
|
||||||
|
|
||||||
char* name = allocateString(*allocator, ctv.name);
|
char* name = allocateString(*allocator, etv.name);
|
||||||
|
|
||||||
if (!options.expandClassProps || hasSeen(&ctv) || count > 1)
|
if (!options.expandExternTypeProps || hasSeen(&etv) || count > 1)
|
||||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{name}, std::nullopt, Location());
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{name}, std::nullopt, Location());
|
||||||
|
|
||||||
AstArray<AstTableProp> props;
|
AstArray<AstTableProp> props;
|
||||||
props.size = ctv.props.size();
|
props.size = etv.props.size();
|
||||||
props.data = static_cast<AstTableProp*>(allocator->allocate(sizeof(AstTableProp) * props.size));
|
props.data = static_cast<AstTableProp*>(allocator->allocate(sizeof(AstTableProp) * props.size));
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (const auto& [propName, prop] : ctv.props)
|
for (const auto& [propName, prop] : etv.props)
|
||||||
{
|
{
|
||||||
char* name = allocateString(*allocator, propName);
|
char* name = allocateString(*allocator, propName);
|
||||||
|
|
||||||
|
@ -244,13 +245,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
AstTableIndexer* indexer = nullptr;
|
AstTableIndexer* indexer = nullptr;
|
||||||
if (ctv.indexer)
|
if (etv.indexer)
|
||||||
{
|
{
|
||||||
RecursionCounter counter(&count);
|
RecursionCounter counter(&count);
|
||||||
|
|
||||||
indexer = allocator->alloc<AstTableIndexer>();
|
indexer = allocator->alloc<AstTableIndexer>();
|
||||||
indexer->indexType = Luau::visit(*this, ctv.indexer->indexType->ty);
|
indexer->indexType = Luau::visit(*this, etv.indexer->indexType->ty);
|
||||||
indexer->resultType = Luau::visit(*this, ctv.indexer->indexResultType->ty);
|
indexer->resultType = Luau::visit(*this, etv.indexer->indexResultType->ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocator->alloc<AstTypeTable>(Location(), props, indexer);
|
return allocator->alloc<AstTypeTable>(Location(), props, indexer);
|
||||||
|
@ -328,10 +329,20 @@ public:
|
||||||
if (retTail)
|
if (retTail)
|
||||||
retTailAnnotation = rehydrate(*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>(
|
return allocator->alloc<AstTypeFunction>(
|
||||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
|
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
AstType* operator()(const ErrorType&)
|
AstType* operator()(const ErrorType&)
|
||||||
{
|
{
|
||||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("Unifiable<Error>"), std::nullopt, Location());
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("Unifiable<Error>"), std::nullopt, Location());
|
||||||
|
@ -585,6 +596,8 @@ public:
|
||||||
visitLocal(arg);
|
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))
|
||||||
|
@ -597,7 +610,26 @@ public:
|
||||||
if (tail)
|
if (tail)
|
||||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||||
|
|
||||||
fn->returnAnnotation = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
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};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -661,7 +663,7 @@ void TypeChecker2::visit(AstStat* stat)
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
else if (auto s = stat->as<AstStatError>())
|
else if (auto s = stat->as<AstStatError>())
|
||||||
return visit(s);
|
return visit(s);
|
||||||
|
@ -1221,7 +1223,10 @@ void TypeChecker2::visit(AstStatDeclareFunction* stat)
|
||||||
{
|
{
|
||||||
visitGenerics(stat->generics, stat->genericPacks);
|
visitGenerics(stat->generics, stat->genericPacks);
|
||||||
visit(stat->params);
|
visit(stat->params);
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
visit(stat->retTypes);
|
visit(stat->retTypes);
|
||||||
|
else
|
||||||
|
visit(stat->retTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
||||||
|
@ -1229,9 +1234,9 @@ void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
||||||
visit(stat->type);
|
visit(stat->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatDeclareClass* stat)
|
void TypeChecker2::visit(AstStatDeclareExternType* stat)
|
||||||
{
|
{
|
||||||
for (const AstDeclaredClassProp& prop : stat->props)
|
for (const AstDeclaredExternTypeProperty& prop : stat->props)
|
||||||
visit(prop.ty);
|
visit(prop.ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1675,12 +1680,12 @@ void TypeChecker2::visit(AstExprIndexExpr* indexExpr, ValueContext context)
|
||||||
{
|
{
|
||||||
return indexExprMetatableHelper(indexExpr, mt, exprType, indexType);
|
return indexExprMetatableHelper(indexExpr, mt, exprType, indexType);
|
||||||
}
|
}
|
||||||
else if (auto cls = get<ClassType>(exprType))
|
else if (auto cls = get<ExternType>(exprType))
|
||||||
{
|
{
|
||||||
if (cls->indexer)
|
if (cls->indexer)
|
||||||
testIsSubtype(indexType, cls->indexer->indexType, indexExpr->index->location);
|
testIsSubtype(indexType, cls->indexer->indexType, indexExpr->index->location);
|
||||||
else
|
else
|
||||||
reportError(DynamicPropertyLookupOnClassesUnsafe{exprType}, indexExpr->location);
|
reportError(DynamicPropertyLookupOnExternTypesUnsafe{exprType}, indexExpr->location);
|
||||||
}
|
}
|
||||||
else if (get<UnionType>(exprType) && isOptional(exprType))
|
else if (get<UnionType>(exprType) && isOptional(exprType))
|
||||||
{
|
{
|
||||||
|
@ -1821,8 +1826,16 @@ void TypeChecker2::visit(AstExprFunction* fn)
|
||||||
visit(fn->body);
|
visit(fn->body);
|
||||||
|
|
||||||
// we need to typecheck the return annotation itself, if it exists.
|
// we need to typecheck the return annotation itself, if it exists.
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (fn->returnAnnotation)
|
if (fn->returnAnnotation)
|
||||||
visit(*fn->returnAnnotation);
|
visit(fn->returnAnnotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fn->returnAnnotation_DEPRECATED)
|
||||||
|
visit(*fn->returnAnnotation_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
||||||
|
@ -2036,7 +2049,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
|
||||||
// If we're working with things that are not tables, the metatable comparisons above are a little excessive
|
// If we're working with things that are not tables, the metatable comparisons above are a little excessive
|
||||||
// It's ok for one type to have a meta table and the other to not. In that case, we should fall back on
|
// It's ok for one type to have a meta table and the other to not. In that case, we should fall back on
|
||||||
// checking if the intersection of the types is inhabited. If `typesHaveIntersection` failed due to limits,
|
// checking if the intersection of the types is inhabited. If `typesHaveIntersection` failed due to limits,
|
||||||
// TODO: Maybe add more checks here (e.g. for functions, classes, etc)
|
// TODO: Maybe add more checks here (e.g. for functions, extern types, etc)
|
||||||
if (!(get<TableType>(leftType) || get<TableType>(rightType)))
|
if (!(get<TableType>(leftType) || get<TableType>(rightType)))
|
||||||
if (!leftMt.has_value() || !rightMt.has_value())
|
if (!leftMt.has_value() || !rightMt.has_value())
|
||||||
matches = matches || typesHaveIntersection != NormalizationResult::False;
|
matches = matches || typesHaveIntersection != NormalizationResult::False;
|
||||||
|
@ -2613,7 +2626,10 @@ void TypeChecker2::visit(AstTypeFunction* ty)
|
||||||
{
|
{
|
||||||
visitGenerics(ty->generics, ty->genericPacks);
|
visitGenerics(ty->generics, ty->genericPacks);
|
||||||
visit(ty->argTypes);
|
visit(ty->argTypes);
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
visit(ty->returnTypes);
|
visit(ty->returnTypes);
|
||||||
|
else
|
||||||
|
visit(ty->returnTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstTypeTypeof* ty)
|
void TypeChecker2::visit(AstTypeTypeof* ty)
|
||||||
|
@ -2937,7 +2953,7 @@ PropertyTypes TypeChecker2::lookupProp(
|
||||||
|
|
||||||
if (normValid)
|
if (normValid)
|
||||||
{
|
{
|
||||||
for (const auto& [ty, _negations] : norm->classes.classes)
|
for (const auto& [ty, _negations] : norm->externTypes.externTypes)
|
||||||
{
|
{
|
||||||
fetch(ty);
|
fetch(ty);
|
||||||
|
|
||||||
|
@ -3032,10 +3048,10 @@ void TypeChecker2::checkIndexTypeFromType(
|
||||||
if (propTypes.foundOneProp())
|
if (propTypes.foundOneProp())
|
||||||
reportError(MissingUnionProperty{tableTy, propTypes.missingProp, prop}, location);
|
reportError(MissingUnionProperty{tableTy, propTypes.missingProp, prop}, location);
|
||||||
// For class LValues, we don't want to report an extension error,
|
// For class LValues, we don't want to report an extension error,
|
||||||
// because classes come into being with full knowledge of their
|
// because extern types come into being with full knowledge of their
|
||||||
// shape. We instead want to report the unknown property error of
|
// shape. We instead want to report the unknown property error of
|
||||||
// the `else` branch.
|
// the `else` branch.
|
||||||
else if (context == ValueContext::LValue && !get<ClassType>(tableTy))
|
else if (context == ValueContext::LValue && !get<ExternType>(tableTy))
|
||||||
{
|
{
|
||||||
const auto lvPropTypes = lookupProp(norm.get(), prop, ValueContext::RValue, location, astIndexExprType, dummy);
|
const auto lvPropTypes = lookupProp(norm.get(), prop, ValueContext::RValue, location, astIndexExprType, dummy);
|
||||||
if (lvPropTypes.foundOneProp() && lvPropTypes.noneMissingProp())
|
if (lvPropTypes.foundOneProp() && lvPropTypes.noneMissingProp())
|
||||||
|
@ -3045,7 +3061,7 @@ void TypeChecker2::checkIndexTypeFromType(
|
||||||
else
|
else
|
||||||
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
||||||
}
|
}
|
||||||
else if (context == ValueContext::RValue && !get<ClassType>(tableTy))
|
else if (context == ValueContext::RValue && !get<ExternType>(tableTy))
|
||||||
{
|
{
|
||||||
const auto rvPropTypes = lookupProp(norm.get(), prop, ValueContext::LValue, location, astIndexExprType, dummy);
|
const auto rvPropTypes = lookupProp(norm.get(), prop, ValueContext::LValue, location, astIndexExprType, dummy);
|
||||||
if (rvPropTypes.foundOneProp() && rvPropTypes.noneMissingProp())
|
if (rvPropTypes.foundOneProp() && rvPropTypes.noneMissingProp())
|
||||||
|
@ -3098,19 +3114,25 @@ PropertyType TypeChecker2::hasIndexTypeFromType(
|
||||||
return {NormalizationResult::True, {tt->indexer->indexResultType}};
|
return {NormalizationResult::True, {tt->indexer->indexResultType}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauTypeCheckerStricterIndexCheck)
|
||||||
|
{
|
||||||
|
return {NormalizationResult::False, {builtinTypes->unknownType}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// if we are in a conditional context, we treat the property as present and `unknown` because
|
// if we are in a conditional context, we treat the property as present and `unknown` because
|
||||||
// we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit
|
// we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit
|
||||||
// in the future once luau has support for exact tables since this only applies when inexact.
|
// in the future once luau has support for exact tables since this only applies when inexact.
|
||||||
return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}};
|
return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}};
|
||||||
}
|
}
|
||||||
else if (const ClassType* cls = get<ClassType>(ty))
|
}
|
||||||
|
else if (const ExternType* cls = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
// If the property doesn't exist on the class, we consult the indexer
|
// If the property doesn't exist on the class, we consult the indexer
|
||||||
// We need to check if the type of the index expression foo (x[foo])
|
// We need to check if the type of the index expression foo (x[foo])
|
||||||
// is compatible with the indexer's indexType
|
// is compatible with the indexer's indexType
|
||||||
// Construct the intersection and test inhabitedness!
|
// Construct the intersection and test inhabitedness!
|
||||||
if (auto property = lookupClassProp(cls, prop))
|
if (auto property = lookupExternTypeProp(cls, prop))
|
||||||
return {NormalizationResult::True, context == ValueContext::LValue ? property->writeTy : property->readTy};
|
return {NormalizationResult::True, context == ValueContext::LValue ? property->writeTy : property->readTy};
|
||||||
if (cls->indexer)
|
if (cls->indexer)
|
||||||
{
|
{
|
||||||
|
@ -3183,17 +3205,17 @@ void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData&
|
||||||
|
|
||||||
if (auto ttv = getTableType(utk->table))
|
if (auto ttv = getTableType(utk->table))
|
||||||
accumulate(ttv->props);
|
accumulate(ttv->props);
|
||||||
else if (auto ctv = get<ClassType>(follow(utk->table)))
|
else if (auto etv = get<ExternType>(follow(utk->table)))
|
||||||
{
|
{
|
||||||
while (ctv)
|
while (etv)
|
||||||
{
|
{
|
||||||
accumulate(ctv->props);
|
accumulate(etv->props);
|
||||||
|
|
||||||
if (!ctv->parent)
|
if (!etv->parent)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ctv = get<ClassType>(*ctv->parent);
|
etv = get<ExternType>(*etv->parent);
|
||||||
LUAU_ASSERT(ctv);
|
LUAU_ASSERT(etv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,6 @@ LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDontForgetToReduceUnionFunc)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSearchForRefineableType)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
||||||
|
@ -68,6 +66,7 @@ LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -106,7 +105,7 @@ struct InstanceCollector_DEPRECATED : TypeOnceVisitor
|
||||||
cyclicInstance.push_back(t);
|
cyclicInstance.push_back(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +181,7 @@ struct InstanceCollector : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -270,7 +269,7 @@ struct UnscopedGenericFinder : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -844,7 +843,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
||||||
{
|
{
|
||||||
arguments[unionIndex] = option;
|
arguments[unionIndex] = option;
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...);
|
TypeFunctionReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...); // NOLINT
|
||||||
blockedTypes.insert(blockedTypes.end(), result.blockedTypes.begin(), result.blockedTypes.end());
|
blockedTypes.insert(blockedTypes.end(), result.blockedTypes.begin(), result.blockedTypes.end());
|
||||||
if (result.reductionStatus != Reduction::MaybeOk)
|
if (result.reductionStatus != Reduction::MaybeOk)
|
||||||
reductionStatus = result.reductionStatus;
|
reductionStatus = result.reductionStatus;
|
||||||
|
@ -869,7 +868,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
||||||
{},
|
{},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (FFlag::LuauDontForgetToReduceUnionFunc && ctx->solver)
|
if (ctx->solver)
|
||||||
ctx->pushConstraint(ReduceConstraint{resultTy});
|
ctx->pushConstraint(ReduceConstraint{resultTy});
|
||||||
|
|
||||||
return {{resultTy, Reduction::MaybeOk, {}, {}}};
|
return {{resultTy, Reduction::MaybeOk, {}, {}}};
|
||||||
|
@ -908,7 +907,7 @@ struct FindUserTypeFunctionBlockers : TypeOnceVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2130,7 +2129,7 @@ struct FindRefinementBlockers : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2252,6 +2251,18 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
return {std::nullopt, Reduction::MaybeOk, {t}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {t}, {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauRefineWaitForBlockedTypesInTarget)
|
||||||
|
{
|
||||||
|
// If we have a blocked type in the target, we *could* potentially
|
||||||
|
// refine it, but more likely we end up with some type explosion in
|
||||||
|
// normalization.
|
||||||
|
FindRefinementBlockers frb;
|
||||||
|
frb.traverse(targetTy);
|
||||||
|
if (!frb.found.empty())
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {frb.found.begin(), frb.found.end()}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
// Refine a target type and a discriminant one at a time.
|
// Refine a target type and a discriminant one at a time.
|
||||||
// Returns result : TypeId, toBlockOn : vector<TypeId>
|
// Returns result : TypeId, toBlockOn : vector<TypeId>
|
||||||
auto stepRefine = [&ctx](TypeId target, TypeId discriminant) -> std::pair<TypeId, std::vector<TypeId>>
|
auto stepRefine = [&ctx](TypeId target, TypeId discriminant) -> std::pair<TypeId, std::vector<TypeId>>
|
||||||
|
@ -2281,8 +2292,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
return {nullptr, {}};
|
return {nullptr, {}};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (FFlag::LuauSearchForRefineableType)
|
|
||||||
{
|
{
|
||||||
// If the discriminant type is only:
|
// If the discriminant type is only:
|
||||||
// - The `*no-refine*` type or,
|
// - The `*no-refine*` type or,
|
||||||
|
@ -2292,18 +2301,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
crt.traverse(discriminant);
|
crt.traverse(discriminant);
|
||||||
if (!crt.found)
|
if (!crt.found)
|
||||||
return {target, {}};
|
return {target, {}};
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FFlag::LuauSkipNoRefineDuringRefinement)
|
|
||||||
if (get<NoRefineType>(discriminant))
|
|
||||||
return {target, {}};
|
|
||||||
if (auto nt = get<NegationType>(discriminant))
|
|
||||||
{
|
|
||||||
if (get<NoRefineType>(follow(nt->ty)))
|
|
||||||
return {target, {}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FFlag::LuauSimplyRefineNotNil)
|
if (FFlag::LuauSimplyRefineNotNil)
|
||||||
{
|
{
|
||||||
|
@ -2684,7 +2681,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto classTy = get<ClassType>(ty))
|
if (auto classTy = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
for (auto [key, _] : classTy->props)
|
for (auto [key, _] : classTy->props)
|
||||||
result.insert(key);
|
result.insert(key);
|
||||||
|
@ -2707,7 +2704,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should not be reachable since the type should be a valid tables or classes part from normalization.
|
// this should not be reachable since the type should be a valid tables or extern types part from normalization.
|
||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2733,9 +2730,9 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
if (!normTy)
|
if (!normTy)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
// if we don't have either just tables or just classes, we've got nothing to get keys of (at least until a future version perhaps adds classes
|
// if we don't have either just tables or just extern types, we've got nothing to get keys of (at least until a future version perhaps adds extern types
|
||||||
// as well)
|
// as well)
|
||||||
if (normTy->hasTables() == normTy->hasClasses())
|
if (normTy->hasTables() == normTy->hasExternTypes())
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// this is sort of atrocious, but we're trying to reject any type that has not normalized to a table or a union of tables.
|
// this is sort of atrocious, but we're trying to reject any type that has not normalized to a table or a union of tables.
|
||||||
|
@ -2746,31 +2743,31 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
// we're going to collect the keys in here
|
// we're going to collect the keys in here
|
||||||
Set<std::string> keys{{}};
|
Set<std::string> keys{{}};
|
||||||
|
|
||||||
// computing the keys for classes
|
// computing the keys for extern types
|
||||||
if (normTy->hasClasses())
|
if (normTy->hasExternTypes())
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!normTy->hasTables());
|
LUAU_ASSERT(!normTy->hasTables());
|
||||||
|
|
||||||
// seen set for key computation for classes
|
// seen set for key computation for extern types
|
||||||
DenseHashSet<TypeId> seen{{}};
|
DenseHashSet<TypeId> seen{{}};
|
||||||
|
|
||||||
auto classesIter = normTy->classes.ordering.begin();
|
auto externTypeIter = normTy->externTypes.ordering.begin();
|
||||||
auto classesIterEnd = normTy->classes.ordering.end();
|
auto externTypeIterEnd = normTy->externTypes.ordering.end();
|
||||||
LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check earlier
|
LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier
|
||||||
|
|
||||||
// collect all the properties from the first class type
|
// collect all the properties from the first class type
|
||||||
if (!computeKeysOf(*classesIter, keys, seen, isRaw, ctx))
|
if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx))
|
||||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type!
|
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
|
// we need to look at each class to remove any keys that are not common amongst them all
|
||||||
while (++classesIter != classesIterEnd)
|
while (++externTypeIter != externTypeIterEnd)
|
||||||
{
|
{
|
||||||
seen.clear(); // we'll reuse the same seen set
|
seen.clear(); // we'll reuse the same seen set
|
||||||
|
|
||||||
Set<std::string> localKeys{{}};
|
Set<std::string> localKeys{{}};
|
||||||
|
|
||||||
// we can skip to the next class if this one is a top type
|
// we can skip to the next class if this one is a top type
|
||||||
if (!computeKeysOf(*classesIter, localKeys, seen, isRaw, ctx))
|
if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (auto& key : keys)
|
for (auto& key : keys)
|
||||||
|
@ -2785,7 +2782,7 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
// computing the keys for tables
|
// computing the keys for tables
|
||||||
if (normTy->hasTables())
|
if (normTy->hasTables())
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!normTy->hasClasses());
|
LUAU_ASSERT(!normTy->hasExternTypes());
|
||||||
|
|
||||||
// seen set for key computation for tables
|
// seen set for key computation for tables
|
||||||
DenseHashSet<TypeId> seen{{}};
|
DenseHashSet<TypeId> seen{{}};
|
||||||
|
@ -2947,7 +2944,7 @@ bool searchPropsAndIndexer(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handles recursion / metamethods of tables/classes
|
/* Handles recursion / metamethods of tables and extern types
|
||||||
`isRaw` parameter indicates whether or not we should follow __index metamethods
|
`isRaw` parameter indicates whether or not we should follow __index metamethods
|
||||||
returns false if property of `ty` could not be found */
|
returns false if property of `ty` could not be found */
|
||||||
bool tblIndexInto_DEPRECATED(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
bool tblIndexInto_DEPRECATED(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
||||||
|
@ -3122,11 +3119,11 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
return {ctx->builtins->anyType, Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->anyType, Reduction::MaybeOk, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we don't have either just tables or just classes, we've got nothing to index into
|
// if we don't have either just tables or just extern types, we've got nothing to index into
|
||||||
if (indexeeNormTy->hasTables() == indexeeNormTy->hasClasses())
|
if (indexeeNormTy->hasTables() == indexeeNormTy->hasExternTypes())
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// we're trying to reject any type that has not normalized to a table/class or a union of tables/classes.
|
// we're trying to reject any type that has not normalized to a table or extern type or a union of tables or extern types.
|
||||||
if (indexeeNormTy->hasTops() || indexeeNormTy->hasBooleans() || indexeeNormTy->hasErrors() || indexeeNormTy->hasNils() ||
|
if (indexeeNormTy->hasTops() || indexeeNormTy->hasBooleans() || indexeeNormTy->hasErrors() || indexeeNormTy->hasNils() ||
|
||||||
indexeeNormTy->hasNumbers() || indexeeNormTy->hasStrings() || indexeeNormTy->hasThreads() || indexeeNormTy->hasBuffers() ||
|
indexeeNormTy->hasNumbers() || indexeeNormTy->hasStrings() || indexeeNormTy->hasThreads() || indexeeNormTy->hasBuffers() ||
|
||||||
indexeeNormTy->hasFunctions() || indexeeNormTy->hasTyvars())
|
indexeeNormTy->hasFunctions() || indexeeNormTy->hasTyvars())
|
||||||
|
@ -3157,18 +3154,18 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
|
|
||||||
DenseHashSet<TypeId> properties{{}}; // vector of types that will be returned
|
DenseHashSet<TypeId> properties{{}}; // vector of types that will be returned
|
||||||
|
|
||||||
if (indexeeNormTy->hasClasses())
|
if (indexeeNormTy->hasExternTypes())
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!indexeeNormTy->hasTables());
|
LUAU_ASSERT(!indexeeNormTy->hasTables());
|
||||||
|
|
||||||
if (isRaw) // rawget should never reduce for classes (to match the behavior of the rawget global function)
|
if (isRaw) // rawget should never reduce for extern types (to match the behavior of the rawget global function)
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// at least one class is guaranteed to be in the iterator by .hasClasses()
|
// at least one class is guaranteed to be in the iterator by .hasExternTypes()
|
||||||
for (auto classesIter = indexeeNormTy->classes.ordering.begin(); classesIter != indexeeNormTy->classes.ordering.end(); ++classesIter)
|
for (auto externTypeIter = indexeeNormTy->externTypes.ordering.begin(); externTypeIter != indexeeNormTy->externTypes.ordering.end(); ++externTypeIter)
|
||||||
{
|
{
|
||||||
auto classTy = get<ClassType>(*classesIter);
|
auto externTy = get<ExternType>(*externTypeIter);
|
||||||
if (!classTy)
|
if (!externTy)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(false); // this should not be possible according to normalization's spec
|
LUAU_ASSERT(false); // this should not be possible according to normalization's spec
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
@ -3177,16 +3174,16 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
for (TypeId ty : *typesToFind)
|
for (TypeId ty : *typesToFind)
|
||||||
{
|
{
|
||||||
// Search for all instances of indexer in class->props and class->indexer
|
// Search for all instances of indexer in class->props and class->indexer
|
||||||
if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx))
|
if (searchPropsAndIndexer(ty, externTy->props, externTy->indexer, properties, ctx))
|
||||||
continue; // Indexer was found in this class, so we can move on to the next
|
continue; // Indexer was found in this class, so we can move on to the next
|
||||||
|
|
||||||
auto parent = classTy->parent;
|
auto parent = externTy->parent;
|
||||||
bool foundInParent = false;
|
bool foundInParent = false;
|
||||||
while (parent && !foundInParent)
|
while (parent && !foundInParent)
|
||||||
{
|
{
|
||||||
auto parentClass = get<ClassType>(follow(*parent));
|
auto parentExternType = get<ExternType>(follow(*parent));
|
||||||
foundInParent = searchPropsAndIndexer(ty, parentClass->props, parentClass->indexer, properties, ctx);
|
foundInParent = searchPropsAndIndexer(ty, parentExternType->props, parentExternType->indexer, properties, ctx);
|
||||||
parent = parentClass->parent;
|
parent = parentExternType->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we move on to the next type if any of the parents we went through had the property.
|
// we move on to the next type if any of the parents we went through had the property.
|
||||||
|
@ -3198,7 +3195,7 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
// 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.
|
// the necessary state to do that, even if we intend to just eat the errors.
|
||||||
ErrorVec dummy;
|
ErrorVec dummy;
|
||||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, *classesIter, "__index", Location{});
|
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, *externTypeIter, "__index", Location{});
|
||||||
if (!mmType) // if a metatable does not exist, there is no where else to look
|
if (!mmType) // if a metatable does not exist, there is no where else to look
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
|
@ -3210,7 +3207,7 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
|
|
||||||
if (indexeeNormTy->hasTables())
|
if (indexeeNormTy->hasTables())
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!indexeeNormTy->hasClasses());
|
LUAU_ASSERT(!indexeeNormTy->hasExternTypes());
|
||||||
|
|
||||||
// at least one table is guaranteed to be in the iterator by .hasTables()
|
// at least one table is guaranteed to be in the iterator by .hasTables()
|
||||||
for (auto tablesIter = indexeeNormTy->tables.begin(); tablesIter != indexeeNormTy->tables.end(); ++tablesIter)
|
for (auto tablesIter = indexeeNormTy->tables.begin(); tablesIter != indexeeNormTy->tables.end(); ++tablesIter)
|
||||||
|
@ -3305,7 +3302,7 @@ TypeFunctionReductionResult<TypeId> setmetatableTypeFunction(
|
||||||
// we're trying to reject any type that has not normalized to a table or a union/intersection of tables.
|
// we're trying to reject any type that has not normalized to a table or a union/intersection of tables.
|
||||||
if (targetNorm->hasTops() || targetNorm->hasBooleans() || targetNorm->hasErrors() || targetNorm->hasNils() || targetNorm->hasNumbers() ||
|
if (targetNorm->hasTops() || targetNorm->hasBooleans() || targetNorm->hasErrors() || targetNorm->hasNils() || targetNorm->hasNumbers() ||
|
||||||
targetNorm->hasStrings() || targetNorm->hasThreads() || targetNorm->hasBuffers() || targetNorm->hasFunctions() || targetNorm->hasTyvars() ||
|
targetNorm->hasStrings() || targetNorm->hasThreads() || targetNorm->hasBuffers() || targetNorm->hasFunctions() || targetNorm->hasTyvars() ||
|
||||||
targetNorm->hasClasses())
|
targetNorm->hasExternTypes())
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// if the supposed metatable is not a table, we will fail to reduce.
|
// if the supposed metatable is not a table, we will fail to reduce.
|
||||||
|
@ -3379,7 +3376,7 @@ static TypeFunctionReductionResult<TypeId> getmetatableHelper(TypeId targetTy, c
|
||||||
erroneous = false;
|
erroneous = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto clazz = get<ClassType>(targetTy))
|
if (auto clazz = get<ExternType>(targetTy))
|
||||||
{
|
{
|
||||||
metatable = clazz->metatable;
|
metatable = clazz->metatable;
|
||||||
erroneous = false;
|
erroneous = false;
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct InstanceCollector2 : TypeOnceVisitor
|
||||||
cyclicInstance.insert(t);
|
cyclicInstance.insert(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ static std::string getTag(lua_State* L, TypeFunctionTypeId ty)
|
||||||
return "table";
|
return "table";
|
||||||
else if (get<TypeFunctionFunctionType>(ty))
|
else if (get<TypeFunctionFunctionType>(ty))
|
||||||
return "function";
|
return "function";
|
||||||
else if (get<TypeFunctionClassType>(ty))
|
else if (get<TypeFunctionExternType>(ty))
|
||||||
return "class";
|
return "class";
|
||||||
else if (get<TypeFunctionGenericType>(ty))
|
else if (get<TypeFunctionGenericType>(ty))
|
||||||
return "generic";
|
return "generic";
|
||||||
|
@ -1114,7 +1114,7 @@ static int getClassParent_DEPRECATED(lua_State* L)
|
||||||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
auto tfct = get<TypeFunctionClassType>(self);
|
auto tfct = get<TypeFunctionExternType>(self);
|
||||||
if (!tfct)
|
if (!tfct)
|
||||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
@ -1136,7 +1136,7 @@ static int getReadParent(lua_State* L)
|
||||||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
auto tfct = get<TypeFunctionClassType>(self);
|
auto tfct = get<TypeFunctionExternType>(self);
|
||||||
if (!tfct)
|
if (!tfct)
|
||||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
@ -1158,7 +1158,7 @@ static int getWriteParent(lua_State* L)
|
||||||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
auto tfct = get<TypeFunctionClassType>(self);
|
auto tfct = get<TypeFunctionExternType>(self);
|
||||||
if (!tfct)
|
if (!tfct)
|
||||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
@ -1242,7 +1242,7 @@ static int getProps(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||||
{
|
{
|
||||||
lua_createtable(L, int(tfct->props.size()), 0);
|
lua_createtable(L, int(tfct->props.size()), 0);
|
||||||
for (auto& [name, prop] : tfct->props)
|
for (auto& [name, prop] : tfct->props)
|
||||||
|
@ -1305,7 +1305,7 @@ static int getIndexer(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||||
{
|
{
|
||||||
// if the indexer does not exist, we should return nil
|
// if the indexer does not exist, we should return nil
|
||||||
if (!tfct->indexer.has_value())
|
if (!tfct->indexer.has_value())
|
||||||
|
@ -1353,7 +1353,7 @@ static int getReadIndexer(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||||
{
|
{
|
||||||
// if the indexer does not exist, we should return nil
|
// if the indexer does not exist, we should return nil
|
||||||
if (!tfct->indexer.has_value())
|
if (!tfct->indexer.has_value())
|
||||||
|
@ -1399,7 +1399,7 @@ static int getWriteIndexer(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||||
{
|
{
|
||||||
// if the indexer does not exist, we should return nil
|
// if the indexer does not exist, we should return nil
|
||||||
if (!tfct->indexer.has_value())
|
if (!tfct->indexer.has_value())
|
||||||
|
@ -1439,7 +1439,7 @@ static int getMetatable(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||||
{
|
{
|
||||||
// if the metatable does not exist, we should return nil
|
// if the metatable does not exist, we should return nil
|
||||||
if (!tfct->metatable.has_value())
|
if (!tfct->metatable.has_value())
|
||||||
|
@ -1593,7 +1593,7 @@ void registerTypeUserData(lua_State* L)
|
||||||
// Union and Intersection type methods
|
// Union and Intersection type methods
|
||||||
{"components", getComponents},
|
{"components", getComponents},
|
||||||
|
|
||||||
// Class type methods
|
// Extern type methods
|
||||||
{FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED},
|
{FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED},
|
||||||
|
|
||||||
// Function type methods (cont.)
|
// Function type methods (cont.)
|
||||||
|
@ -1604,7 +1604,7 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"name", getGenericName},
|
{"name", getGenericName},
|
||||||
{"ispack", getGenericIsPack},
|
{"ispack", getGenericIsPack},
|
||||||
|
|
||||||
// move this under Class type methods when removing FFlagLuauTypeFunReadWriteParents
|
// move this under extern type methods when removing FFlagLuauTypeFunReadWriteParents
|
||||||
{FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr},
|
{FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr},
|
||||||
|
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
|
@ -1903,12 +1903,12 @@ bool areEqual(SeenSet& seen, const TypeFunctionFunctionType& lhs, const TypeFunc
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypeFunctionClassType& lhs, const TypeFunctionClassType& rhs)
|
bool areEqual(SeenSet& seen, const TypeFunctionExternType& lhs, const TypeFunctionExternType& rhs)
|
||||||
{
|
{
|
||||||
if (seenSetContains(seen, &lhs, &rhs))
|
if (seenSetContains(seen, &lhs, &rhs))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return lhs.classTy == rhs.classTy;
|
return lhs.externTy == rhs.externTy;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
||||||
|
@ -1976,8 +1976,8 @@ bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const TypeFunctionClassType* lf = get<TypeFunctionClassType>(&lhs);
|
const TypeFunctionExternType* lf = get<TypeFunctionExternType>(&lhs);
|
||||||
const TypeFunctionClassType* rf = get<TypeFunctionClassType>(&rhs);
|
const TypeFunctionExternType* rf = get<TypeFunctionExternType>(&rhs);
|
||||||
if (lf && rf)
|
if (lf && rf)
|
||||||
return areEqual(seen, *lf, *rf);
|
return areEqual(seen, *lf, *rf);
|
||||||
}
|
}
|
||||||
|
@ -2266,7 +2266,7 @@ private:
|
||||||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||||
}
|
}
|
||||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
else if (auto c = get<TypeFunctionExternType>(ty))
|
||||||
target = ty; // Don't copy a class since they are immutable
|
target = ty; // Don't copy a class since they are immutable
|
||||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->isNamed, g->isPack, g->name});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->isNamed, g->isPack, g->name});
|
||||||
|
@ -2321,7 +2321,7 @@ private:
|
||||||
cloneChildren(t1, t2);
|
cloneChildren(t1, t2);
|
||||||
else if (auto [f1, f2] = std::tuple{getMutable<TypeFunctionFunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
else if (auto [f1, f2] = std::tuple{getMutable<TypeFunctionFunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||||
cloneChildren(f1, f2);
|
cloneChildren(f1, f2);
|
||||||
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||||
cloneChildren(c1, c2);
|
cloneChildren(c1, c2);
|
||||||
else if (auto [g1, g2] = std::tuple{getMutable<TypeFunctionGenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
else if (auto [g1, g2] = std::tuple{getMutable<TypeFunctionGenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||||
cloneChildren(g1, g2);
|
cloneChildren(g1, g2);
|
||||||
|
@ -2431,7 +2431,7 @@ private:
|
||||||
f2->retTypes = shallowClone(f1->retTypes);
|
f2->retTypes = shallowClone(f1->retTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cloneChildren(TypeFunctionClassType* c1, TypeFunctionClassType* c2)
|
void cloneChildren(TypeFunctionExternType* c1, TypeFunctionExternType* c2)
|
||||||
{
|
{
|
||||||
// noop.
|
// noop.
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,12 +206,12 @@ private:
|
||||||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||||
}
|
}
|
||||||
else if (auto c = get<ClassType>(ty))
|
else if (auto c = get<ExternType>(ty))
|
||||||
{
|
{
|
||||||
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the original
|
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the original
|
||||||
// class
|
// class
|
||||||
target = typeFunctionRuntime->typeArena.allocate(
|
target = typeFunctionRuntime->typeArena.allocate(
|
||||||
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (auto g = get<GenericType>(ty))
|
else if (auto g = get<GenericType>(ty))
|
||||||
|
@ -291,7 +291,7 @@ private:
|
||||||
serializeChildren(m1, m2);
|
serializeChildren(m1, m2);
|
||||||
else if (auto [f1, f2] = std::tuple{get<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
else if (auto [f1, f2] = std::tuple{get<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||||
serializeChildren(f1, f2);
|
serializeChildren(f1, f2);
|
||||||
else if (auto [c1, c2] = std::tuple{get<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{get<ExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||||
serializeChildren(c1, c2);
|
serializeChildren(c1, c2);
|
||||||
else if (auto [g1, g2] = std::tuple{get<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
else if (auto [g1, g2] = std::tuple{get<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||||
serializeChildren(g1, g2);
|
serializeChildren(g1, g2);
|
||||||
|
@ -411,7 +411,7 @@ private:
|
||||||
f2->retTypes = shallowSerialize(f1->retTypes);
|
f2->retTypes = shallowSerialize(f1->retTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serializeChildren(const ClassType* c1, TypeFunctionClassType* c2)
|
void serializeChildren(const ExternType* c1, TypeFunctionExternType* c2)
|
||||||
{
|
{
|
||||||
for (const auto& [k, p] : c1->props)
|
for (const auto& [k, p] : c1->props)
|
||||||
{
|
{
|
||||||
|
@ -702,9 +702,9 @@ private:
|
||||||
TypePackId emptyTypePack = state->ctx->arena->addTypePack(TypePack{});
|
TypePackId emptyTypePack = state->ctx->arena->addTypePack(TypePack{});
|
||||||
target = state->ctx->arena->addType(FunctionType{emptyTypePack, emptyTypePack, {}, false});
|
target = state->ctx->arena->addType(FunctionType{emptyTypePack, emptyTypePack, {}, false});
|
||||||
}
|
}
|
||||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
else if (auto c = get<TypeFunctionExternType>(ty))
|
||||||
{
|
{
|
||||||
target = c->classTy;
|
target = c->externTy;
|
||||||
}
|
}
|
||||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||||
{
|
{
|
||||||
|
@ -811,7 +811,7 @@ private:
|
||||||
deserializeChildren(m2, m1);
|
deserializeChildren(m2, m1);
|
||||||
else if (auto [f1, f2] = std::tuple{getMutable<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
else if (auto [f1, f2] = std::tuple{getMutable<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||||
deserializeChildren(f2, f1);
|
deserializeChildren(f2, f1);
|
||||||
else if (auto [c1, c2] = std::tuple{getMutable<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{getMutable<ExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||||
deserializeChildren(c2, c1);
|
deserializeChildren(c2, c1);
|
||||||
else if (auto [g1, g2] = std::tuple{getMutable<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
else if (auto [g1, g2] = std::tuple{getMutable<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||||
deserializeChildren(g2, g1);
|
deserializeChildren(g2, g1);
|
||||||
|
@ -972,7 +972,7 @@ private:
|
||||||
f1->retTypes = shallowDeserialize(f2->retTypes);
|
f1->retTypes = shallowDeserialize(f2->retTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserializeChildren(TypeFunctionClassType* c2, ClassType* c1)
|
void deserializeChildren(TypeFunctionExternType* c2, ExternType* c1)
|
||||||
{
|
{
|
||||||
// noop.
|
// noop.
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,9 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
||||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||||
|
@ -44,7 +45,7 @@ namespace Luau
|
||||||
|
|
||||||
static bool typeCouldHaveMetatable(TypeId ty)
|
static bool typeCouldHaveMetatable(TypeId ty)
|
||||||
{
|
{
|
||||||
return get<TableType>(follow(ty)) || get<ClassType>(follow(ty)) || get<MetatableType>(follow(ty));
|
return get<TableType>(follow(ty)) || get<ExternType>(follow(ty)) || get<MetatableType>(follow(ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void defaultLuauPrintLine(const std::string& s)
|
static void defaultLuauPrintLine(const std::string& s)
|
||||||
|
@ -318,7 +319,7 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
||||||
unifierState.skipCacheForType.clear();
|
unifierState.skipCacheForType.clear();
|
||||||
|
|
||||||
duplicateTypeAliases.clear();
|
duplicateTypeAliases.clear();
|
||||||
incorrectClassDefinitions.clear();
|
incorrectExternTypeDefinitions.clear();
|
||||||
|
|
||||||
return std::move(currentModule);
|
return std::move(currentModule);
|
||||||
}
|
}
|
||||||
|
@ -383,7 +384,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStat& program)
|
||||||
}
|
}
|
||||||
else if (auto global = program.as<AstStatDeclareFunction>())
|
else if (auto global = program.as<AstStatDeclareFunction>())
|
||||||
return check(scope, *global);
|
return check(scope, *global);
|
||||||
else if (auto global = program.as<AstStatDeclareClass>())
|
else if (auto global = program.as<AstStatDeclareExternType>())
|
||||||
return check(scope, *global);
|
return check(scope, *global);
|
||||||
else if (auto errorStatement = program.as<AstStatError>())
|
else if (auto errorStatement = program.as<AstStatError>())
|
||||||
{
|
{
|
||||||
|
@ -498,9 +499,9 @@ ControlFlow TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope,
|
||||||
prototype(scope, *typealias, subLevel);
|
prototype(scope, *typealias, subLevel);
|
||||||
++subLevel;
|
++subLevel;
|
||||||
}
|
}
|
||||||
else if (const auto& declaredClass = stat->as<AstStatDeclareClass>())
|
else if (const auto& declaredExternType = stat->as<AstStatDeclareExternType>())
|
||||||
{
|
{
|
||||||
prototype(scope, *declaredClass);
|
prototype(scope, *declaredExternType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -788,7 +789,7 @@ struct Demoter : Substitution
|
||||||
|
|
||||||
bool ignoreChildren(TypeId ty) override
|
bool ignoreChildren(TypeId ty) override
|
||||||
{
|
{
|
||||||
if (get<ClassType>(ty))
|
if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1686,82 +1687,82 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||||
{
|
{
|
||||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
std::optional<TypeId> superTy = std::make_optional(builtinTypes->externType);
|
||||||
if (declaredClass.superName)
|
if (declaredExternType.superName)
|
||||||
{
|
{
|
||||||
Name superName = Name(declaredClass.superName->value);
|
Name superName = Name(declaredExternType.superName->value);
|
||||||
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
||||||
|
|
||||||
if (!lookupType)
|
if (!lookupType)
|
||||||
{
|
{
|
||||||
reportError(declaredClass.location, UnknownSymbol{superName, UnknownSymbol::Type});
|
reportError(declaredExternType.location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||||
incorrectClassDefinitions.insert(&declaredClass);
|
incorrectExternTypeDefinitions.insert(&declaredExternType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
// We don't have generic extern types, so this assertion _should_ never be hit.
|
||||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||||
superTy = lookupType->type;
|
superTy = lookupType->type;
|
||||||
|
|
||||||
if (!get<ClassType>(follow(*superTy)))
|
if (!get<ExternType>(follow(*superTy)))
|
||||||
{
|
{
|
||||||
reportError(
|
reportError(
|
||||||
declaredClass.location,
|
declaredExternType.location,
|
||||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass.name.value)}
|
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType.name.value)}
|
||||||
);
|
);
|
||||||
incorrectClassDefinitions.insert(&declaredClass);
|
incorrectExternTypeDefinitions.insert(&declaredExternType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Name className(declaredClass.name.value);
|
Name className(declaredExternType.name.value);
|
||||||
|
|
||||||
TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name, declaredClass.location));
|
TypeId classTy = addType(ExternType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name, declaredExternType.location));
|
||||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
ExternType* etv = getMutable<ExternType>(classTy);
|
||||||
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||||
|
|
||||||
ctv->metatable = metaTy;
|
etv->metatable = metaTy;
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredClass.location};
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||||
else
|
else
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||||
{
|
{
|
||||||
Name className(declaredClass.name.value);
|
Name className(declaredExternType.name.value);
|
||||||
|
|
||||||
// Don't bother checking if the class definition was incorrect
|
// Don't bother checking if the class definition was incorrect
|
||||||
if (incorrectClassDefinitions.find(&declaredClass))
|
if (incorrectExternTypeDefinitions.find(&declaredExternType))
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
|
|
||||||
std::optional<TypeFun> binding;
|
std::optional<TypeFun> binding;
|
||||||
if (auto it = scope->exportedTypeBindings.find(className); it != scope->exportedTypeBindings.end())
|
if (auto it = scope->exportedTypeBindings.find(className); it != scope->exportedTypeBindings.end())
|
||||||
binding = it->second;
|
binding = it->second;
|
||||||
|
|
||||||
// This class definition must have been `prototype()`d first.
|
// This extern type definition must have been `prototype()`d first.
|
||||||
if (!binding)
|
if (!binding)
|
||||||
ice("Class not predeclared");
|
ice("Extern type not predeclared");
|
||||||
|
|
||||||
TypeId classTy = binding->type;
|
TypeId externTy = binding->type;
|
||||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
ExternType* etv = getMutable<ExternType>(externTy);
|
||||||
|
|
||||||
if (!ctv->metatable)
|
if (!etv->metatable)
|
||||||
ice("No metatable for declared class");
|
ice("No metatable for declared extern type");
|
||||||
|
|
||||||
if (const auto& indexer = declaredClass.indexer)
|
if (const auto& indexer = declaredExternType.indexer)
|
||||||
ctv->indexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType));
|
etv->indexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType));
|
||||||
|
|
||||||
TableType* metatable = getMutable<TableType>(*ctv->metatable);
|
TableType* metatable = getMutable<TableType>(*etv->metatable);
|
||||||
for (const AstDeclaredClassProp& prop : declaredClass.props)
|
for (const AstDeclaredExternTypeProperty& prop : declaredExternType.props)
|
||||||
{
|
{
|
||||||
Name propName(prop.name.value);
|
Name propName(prop.name.value);
|
||||||
TypeId propTy = resolveType(scope, *prop.ty);
|
TypeId propTy = resolveType(scope, *prop.ty);
|
||||||
|
|
||||||
bool assignToMetatable = isMetamethod(propName);
|
bool assignToMetatable = isMetamethod(propName);
|
||||||
Luau::ClassType::Props& assignTo = assignToMetatable ? metatable->props : ctv->props;
|
Luau::ExternType::Props& assignTo = assignToMetatable ? metatable->props : etv->props;
|
||||||
|
|
||||||
// Function types always take 'self', but this isn't reflected in the
|
// Function types always take 'self', but this isn't reflected in the
|
||||||
// parsed annotation. Add it here.
|
// parsed annotation. Add it here.
|
||||||
|
@ -1770,7 +1771,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
|
||||||
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
||||||
{
|
{
|
||||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
ftv->argTypes = addTypePack(TypePack{{externTy}, ftv->argTypes});
|
||||||
ftv->hasSelf = true;
|
ftv->hasSelf = true;
|
||||||
|
|
||||||
FunctionDefinition defn;
|
FunctionDefinition defn;
|
||||||
|
@ -1813,7 +1814,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
reportError(declaredExternType.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1852,7 +1853,8 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti
|
||||||
);
|
);
|
||||||
|
|
||||||
TypePackId argPack = resolveTypePack(funScope, global.params);
|
TypePackId argPack = resolveTypePack(funScope, global.params);
|
||||||
TypePackId retPack = resolveTypePack(funScope, global.retTypes);
|
TypePackId retPack =
|
||||||
|
FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, *global.retTypes) : resolveTypePack(funScope, global.retTypes_DEPRECATED);
|
||||||
|
|
||||||
FunctionDefinition defn;
|
FunctionDefinition defn;
|
||||||
|
|
||||||
|
@ -2137,9 +2139,9 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
||||||
if (auto found = findTablePropertyRespectingMeta(type, name, location, addErrors))
|
if (auto found = findTablePropertyRespectingMeta(type, name, location, addErrors))
|
||||||
return *found;
|
return *found;
|
||||||
}
|
}
|
||||||
else if (const ClassType* cls = get<ClassType>(type))
|
else if (const ExternType* cls = get<ExternType>(type))
|
||||||
{
|
{
|
||||||
const Property* prop = lookupClassProp(cls, name);
|
const Property* prop = lookupExternTypeProp(cls, name);
|
||||||
if (prop)
|
if (prop)
|
||||||
return prop->type();
|
return prop->type();
|
||||||
|
|
||||||
|
@ -3462,14 +3464,14 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
||||||
return errorRecoveryType(scope);
|
return errorRecoveryType(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const ClassType* lhsClass = get<ClassType>(lhs))
|
else if (const ExternType* lhsExternType = get<ExternType>(lhs))
|
||||||
{
|
{
|
||||||
if (const Property* prop = lookupClassProp(lhsClass, name))
|
if (const Property* prop = lookupExternTypeProp(lhsExternType, name))
|
||||||
{
|
{
|
||||||
return prop->type();
|
return prop->type();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto indexer = lhsClass->indexer)
|
if (auto indexer = lhsExternType->indexer)
|
||||||
{
|
{
|
||||||
Unifier state = mkUnifier(scope, expr.location);
|
Unifier state = mkUnifier(scope, expr.location);
|
||||||
state.tryUnify(stringType, indexer->indexType);
|
state.tryUnify(stringType, indexer->indexType);
|
||||||
|
@ -3521,14 +3523,14 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||||
{
|
{
|
||||||
if (const Property* prop = lookupClassProp(exprClass, value->value.data))
|
if (const Property* prop = lookupExternTypeProp(exprExternType, value->value.data))
|
||||||
{
|
{
|
||||||
return prop->type();
|
return prop->type();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto indexer = exprClass->indexer)
|
if (auto indexer = exprExternType->indexer)
|
||||||
{
|
{
|
||||||
unify(stringType, indexer->indexType, scope, expr.index->location);
|
unify(stringType, indexer->indexType, scope, expr.index->location);
|
||||||
return indexer->indexResultType;
|
return indexer->indexResultType;
|
||||||
|
@ -3554,20 +3556,20 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||||
{
|
{
|
||||||
if (auto indexer = exprClass->indexer)
|
if (auto indexer = exprExternType->indexer)
|
||||||
{
|
{
|
||||||
unify(indexType, indexer->indexType, scope, expr.index->location);
|
unify(indexType, indexer->indexType, scope, expr.index->location);
|
||||||
return indexer->indexResultType;
|
return indexer->indexResultType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||||
{
|
{
|
||||||
if (isNonstrictMode())
|
if (isNonstrictMode())
|
||||||
return unknownType;
|
return unknownType;
|
||||||
reportError(TypeError{expr.location, DynamicPropertyLookupOnClassesUnsafe{exprType}});
|
reportError(TypeError{expr.location, DynamicPropertyLookupOnExternTypesUnsafe{exprType}});
|
||||||
return errorRecoveryType(scope);
|
return errorRecoveryType(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3848,8 +3850,10 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
||||||
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
||||||
|
|
||||||
TypePackId retPack;
|
TypePackId retPack;
|
||||||
if (expr.returnAnnotation)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation)
|
||||||
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
|
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
|
||||||
|
else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation_DEPRECATED)
|
||||||
|
retPack = resolveTypePack(funScope, *expr.returnAnnotation_DEPRECATED);
|
||||||
else if (isNonstrictMode())
|
else if (isNonstrictMode())
|
||||||
retPack = anyTypePack;
|
retPack = anyTypePack;
|
||||||
else if (expectedFunctionType && expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())
|
else if (expectedFunctionType && expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())
|
||||||
|
@ -4056,7 +4060,8 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
||||||
// If we're in nonstrict mode we want to only report this missing return
|
// 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
|
// statement if there are type annotations on the function. In strict mode
|
||||||
// we report it regardless.
|
// we report it regardless.
|
||||||
if (!isNonstrictMode() || function.returnAnnotation)
|
if (!isNonstrictMode() ||
|
||||||
|
(FFlag::LuauStoreReturnTypesAsPackOnAst ? function.returnAnnotation != nullptr : function.returnAnnotation_DEPRECATED.has_value()))
|
||||||
{
|
{
|
||||||
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
||||||
}
|
}
|
||||||
|
@ -4611,9 +4616,9 @@ std::unique_ptr<WithPredicate<TypePackId>> TypeChecker::checkCallOverload(
|
||||||
{
|
{
|
||||||
callTy = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
callTy = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(fn); ctv && ctv->metatable)
|
else if (const ExternType* etv = get<ExternType>(fn); etv && etv->metatable)
|
||||||
{
|
{
|
||||||
callTy = getIndexTypeFromType(scope, *ctv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
callTy = getIndexTypeFromType(scope, *etv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callTy)
|
if (callTy)
|
||||||
|
@ -5316,17 +5321,17 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d
|
||||||
|
|
||||||
if (auto ttv = getTableType(utk->table))
|
if (auto ttv = getTableType(utk->table))
|
||||||
accumulate(ttv->props);
|
accumulate(ttv->props);
|
||||||
else if (auto ctv = get<ClassType>(follow(utk->table)))
|
else if (auto etv = get<ExternType>(follow(utk->table)))
|
||||||
{
|
{
|
||||||
while (ctv)
|
while (etv)
|
||||||
{
|
{
|
||||||
accumulate(ctv->props);
|
accumulate(etv->props);
|
||||||
|
|
||||||
if (!ctv->parent)
|
if (!etv->parent)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ctv = get<ClassType>(*ctv->parent);
|
etv = get<ExternType>(*etv->parent);
|
||||||
LUAU_ASSERT(ctv);
|
LUAU_ASSERT(etv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5803,7 +5808,8 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
||||||
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
||||||
|
|
||||||
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
||||||
TypePackId retTypes = resolveTypePack(funcScope, func->returnTypes);
|
TypePackId retTypes = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funcScope, *func->returnTypes)
|
||||||
|
: resolveTypePack(funcScope, func->returnTypes_DEPRECATED);
|
||||||
|
|
||||||
std::vector<TypeId> genericTys;
|
std::vector<TypeId> genericTys;
|
||||||
genericTys.reserve(generics.size());
|
genericTys.reserve(generics.size());
|
||||||
|
@ -5854,13 +5860,9 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
||||||
return builtinTypes->nilType;
|
return builtinTypes->nilType;
|
||||||
}
|
}
|
||||||
else if (const auto& un = annotation.as<AstTypeUnion>())
|
else if (const auto& un = annotation.as<AstTypeUnion>())
|
||||||
{
|
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
{
|
{
|
||||||
if (un->types.size == 1)
|
if (un->types.size == 1)
|
||||||
return resolveType(scope, *un->types.data[0]);
|
return resolveType(scope, *un->types.data[0]);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TypeId> types;
|
std::vector<TypeId> types;
|
||||||
for (AstType* ann : un->types)
|
for (AstType* ann : un->types)
|
||||||
types.push_back(resolveType(scope, *ann));
|
types.push_back(resolveType(scope, *ann));
|
||||||
|
@ -5868,13 +5870,9 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
||||||
return addType(UnionType{types});
|
return addType(UnionType{types});
|
||||||
}
|
}
|
||||||
else if (const auto& un = annotation.as<AstTypeIntersection>())
|
else if (const auto& un = annotation.as<AstTypeIntersection>())
|
||||||
{
|
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
{
|
{
|
||||||
if (un->types.size == 1)
|
if (un->types.size == 1)
|
||||||
return resolveType(scope, *un->types.data[0]);
|
return resolveType(scope, *un->types.data[0]);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TypeId> types;
|
std::vector<TypeId> types;
|
||||||
for (AstType* ann : un->types)
|
for (AstType* ann : un->types)
|
||||||
types.push_back(resolveType(scope, *ann));
|
types.push_back(resolveType(scope, *ann));
|
||||||
|
@ -6481,7 +6479,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
||||||
return refine(
|
return refine(
|
||||||
[](TypeId ty) -> bool
|
[](TypeId ty) -> bool
|
||||||
{
|
{
|
||||||
return get<ClassType>(ty);
|
return get<ExternType>(ty);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6496,13 +6494,13 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
||||||
TypeId type = follow(typeFun->type);
|
TypeId type = follow(typeFun->type);
|
||||||
|
|
||||||
// You cannot refine to the top class type.
|
// You cannot refine to the top class type.
|
||||||
if (type == builtinTypes->classType)
|
if (type == builtinTypes->externType)
|
||||||
{
|
{
|
||||||
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're only interested in the root class of any classes.
|
// We're only interested in the root type of any extern type.
|
||||||
if (auto ctv = get<ClassType>(type); !ctv || (ctv->parent != builtinTypes->classType && !hasTag(type, kTypeofRootTag)))
|
if (auto etv = get<ExternType>(type); !etv || (etv->parent != builtinTypes->externType && !hasTag(type, kTypeofRootTag)))
|
||||||
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
||||||
|
|
||||||
// This probably hints at breaking out type filtering functions from the predicate solver so that typeof is not tightly coupled with IsA.
|
// This probably hints at breaking out type filtering functions from the predicate solver so that typeof is not tightly coupled with IsA.
|
||||||
|
|
|
@ -307,9 +307,9 @@ struct TraversalState
|
||||||
prop = &it->second;
|
prop = &it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto c = get<ClassType>(*currentType))
|
else if (auto c = get<ExternType>(*currentType))
|
||||||
{
|
{
|
||||||
prop = lookupClassProp(c, property.name);
|
prop = lookupExternTypeProp(c, property.name);
|
||||||
}
|
}
|
||||||
// For a metatable type, the table takes priority; check that before
|
// For a metatable type, the table takes priority; check that before
|
||||||
// falling through to the metatable entry below.
|
// falling through to the metatable entry below.
|
||||||
|
@ -461,7 +461,7 @@ struct TraversalState
|
||||||
indexer = &(*mtMt->indexer);
|
indexer = &(*mtMt->indexer);
|
||||||
}
|
}
|
||||||
// Note: we don't appear to walk the class hierarchy for indexers
|
// Note: we don't appear to walk the class hierarchy for indexers
|
||||||
else if (auto ct = get<ClassType>(current); ct && ct->indexer)
|
else if (auto ct = get<ExternType>(current); ct && ct->indexer)
|
||||||
indexer = &(*ct->indexer);
|
indexer = &(*ct->indexer);
|
||||||
|
|
||||||
if (indexer)
|
if (indexer)
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
@ -327,7 +325,6 @@ TypePack extendTypePack(
|
||||||
{
|
{
|
||||||
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType, ftp->polarity};
|
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType, ftp->polarity};
|
||||||
t = arena.addType(ft);
|
t = arena.addType(ft);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
trackInteriorFreeType(ftp->scope, t);
|
trackInteriorFreeType(ftp->scope, t);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -438,7 +435,6 @@ TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty)
|
||||||
|
|
||||||
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSolverV2 || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
|
||||||
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
||||||
|
|
||||||
if (!normType)
|
if (!normType)
|
||||||
|
@ -556,10 +552,8 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
||||||
|
|
||||||
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||||
LUAU_ASSERT(FFlag::LuauTrackInteriorFreeTypesOnScope);
|
LUAU_ASSERT(FFlag::LuauSolverV2);
|
||||||
else
|
|
||||||
LUAU_ASSERT(FFlag::LuauSolverV2 && FFlag::LuauTrackInteriorFreeTypesOnScope);
|
|
||||||
for (; scope; scope = scope->parent.get())
|
for (; scope; scope = scope->parent.get())
|
||||||
{
|
{
|
||||||
if (scope->interiorFreeTypes)
|
if (scope->interiorFreeTypes)
|
||||||
|
|
|
@ -292,7 +292,7 @@ TypePackId Widen::clean(TypePackId)
|
||||||
|
|
||||||
bool Widen::ignoreChildren(TypeId ty)
|
bool Widen::ignoreChildren(TypeId ty)
|
||||||
{
|
{
|
||||||
if (get<ClassType>(ty))
|
if (get<ExternType>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return !log->is<UnionType>(ty);
|
return !log->is<UnionType>(ty);
|
||||||
|
@ -693,13 +693,13 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||||
else if (log.getMutable<MetatableType>(subTy))
|
else if (log.getMutable<MetatableType>(subTy))
|
||||||
tryUnifyWithMetatable(superTy, subTy, /*reversed*/ true);
|
tryUnifyWithMetatable(superTy, subTy, /*reversed*/ true);
|
||||||
|
|
||||||
else if (log.getMutable<ClassType>(superTy))
|
else if (log.getMutable<ExternType>(superTy))
|
||||||
tryUnifyWithClass(subTy, superTy, /*reversed*/ false);
|
tryUnifyWithExternType(subTy, superTy, /*reversed*/ false);
|
||||||
|
|
||||||
// Unification of nonclasses with classes is almost, but not quite symmetrical.
|
// Unification of Luau types with extern types is almost, but not quite symmetrical.
|
||||||
// The order in which we perform this test is significant in the case that both types are classes.
|
// The order in which we perform this test is significant in the case that both types are extern types.
|
||||||
else if (log.getMutable<ClassType>(subTy))
|
else if (log.getMutable<ExternType>(subTy))
|
||||||
tryUnifyWithClass(subTy, superTy, /*reversed*/ true);
|
tryUnifyWithExternType(subTy, superTy, /*reversed*/ true);
|
||||||
|
|
||||||
else if (log.get<NegationType>(superTy) || log.get<NegationType>(subTy))
|
else if (log.get<NegationType>(superTy) || log.get<NegationType>(subTy))
|
||||||
tryUnifyNegations(subTy, superTy);
|
tryUnifyNegations(subTy, superTy);
|
||||||
|
@ -1107,15 +1107,15 @@ void Unifier::tryUnifyNormalizedTypes(
|
||||||
if (!get<PrimitiveType>(superNorm.errors))
|
if (!get<PrimitiveType>(superNorm.errors))
|
||||||
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
|
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
|
||||||
|
|
||||||
for (const auto& [subClass, _] : subNorm.classes.classes)
|
for (const auto& [subExternType, _] : subNorm.externTypes.externTypes)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
const ClassType* subCtv = get<ClassType>(subClass);
|
const ExternType* subCtv = get<ExternType>(subExternType);
|
||||||
LUAU_ASSERT(subCtv);
|
LUAU_ASSERT(subCtv);
|
||||||
|
|
||||||
for (const auto& [superClass, superNegations] : superNorm.classes.classes)
|
for (const auto& [superExternType, superNegations] : superNorm.externTypes.externTypes)
|
||||||
{
|
{
|
||||||
const ClassType* superCtv = get<ClassType>(superClass);
|
const ExternType* superCtv = get<ExternType>(superExternType);
|
||||||
LUAU_ASSERT(superCtv);
|
LUAU_ASSERT(superCtv);
|
||||||
|
|
||||||
if (isSubclass(subCtv, superCtv))
|
if (isSubclass(subCtv, superCtv))
|
||||||
|
@ -1124,7 +1124,7 @@ void Unifier::tryUnifyNormalizedTypes(
|
||||||
|
|
||||||
for (TypeId negation : superNegations)
|
for (TypeId negation : superNegations)
|
||||||
{
|
{
|
||||||
const ClassType* negationCtv = get<ClassType>(negation);
|
const ExternType* negationCtv = get<ExternType>(negation);
|
||||||
LUAU_ASSERT(negationCtv);
|
LUAU_ASSERT(negationCtv);
|
||||||
|
|
||||||
if (isSubclass(subCtv, negationCtv))
|
if (isSubclass(subCtv, negationCtv))
|
||||||
|
@ -2382,8 +2382,8 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class unification is almost, but not quite symmetrical. We use the 'reversed' boolean to indicate which scenario we are evaluating.
|
// Extern type unification is almost, but not quite symmetrical. We use the 'reversed' boolean to indicate which scenario we are evaluating.
|
||||||
void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
{
|
{
|
||||||
if (reversed)
|
if (reversed)
|
||||||
std::swap(superTy, subTy);
|
std::swap(superTy, subTy);
|
||||||
|
@ -2396,20 +2396,20 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
reportError(location, TypeMismatch{subTy, superTy, mismatchContext()});
|
reportError(location, TypeMismatch{subTy, superTy, mismatchContext()});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClassType* superClass = get<ClassType>(superTy);
|
const ExternType* superExternType = get<ExternType>(superTy);
|
||||||
if (!superClass)
|
if (!superExternType)
|
||||||
ice("tryUnifyClass invoked with non-class Type");
|
ice("tryUnifyExternType invoked with non-class Type");
|
||||||
|
|
||||||
if (const ClassType* subClass = get<ClassType>(subTy))
|
if (const ExternType* subExternType = get<ExternType>(subTy))
|
||||||
{
|
{
|
||||||
switch (variance)
|
switch (variance)
|
||||||
{
|
{
|
||||||
case Covariant:
|
case Covariant:
|
||||||
if (!isSubclass(subClass, superClass))
|
if (!isSubclass(subExternType, superExternType))
|
||||||
return fail();
|
return fail();
|
||||||
return;
|
return;
|
||||||
case Invariant:
|
case Invariant:
|
||||||
if (subClass != superClass)
|
if (subExternType != superExternType)
|
||||||
return fail();
|
return fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2434,7 +2434,7 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
|
|
||||||
for (const auto& [propName, prop] : subTable->props)
|
for (const auto& [propName, prop] : subTable->props)
|
||||||
{
|
{
|
||||||
const Property* classProp = lookupClassProp(superClass, propName);
|
const Property* classProp = lookupExternTypeProp(superExternType, propName);
|
||||||
if (!classProp)
|
if (!classProp)
|
||||||
{
|
{
|
||||||
ok = false;
|
ok = false;
|
||||||
|
@ -2462,7 +2462,7 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
if (subTable->indexer)
|
if (subTable->indexer)
|
||||||
{
|
{
|
||||||
ok = false;
|
ok = false;
|
||||||
std::string msg = "Class " + superClass->name + " does not have an indexer";
|
std::string msg = "Extern type " + superExternType->name + " does not have an indexer";
|
||||||
reportError(location, GenericError{msg});
|
reportError(location, GenericError{msg});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2635,9 +2635,9 @@ static void tryUnifyWithAny(
|
||||||
queue.push_back(mt->table);
|
queue.push_back(mt->table);
|
||||||
queue.push_back(mt->metatable);
|
queue.push_back(mt->metatable);
|
||||||
}
|
}
|
||||||
else if (state.log.getMutable<ClassType>(ty))
|
else if (state.log.getMutable<ExternType>(ty))
|
||||||
{
|
{
|
||||||
// ClassTypes never contain free types.
|
// ExternTypes never contain free types.
|
||||||
}
|
}
|
||||||
else if (auto union_ = state.log.getMutable<UnionType>(ty))
|
else if (auto union_ = state.log.getMutable<UnionType>(ty))
|
||||||
queue.insert(queue.end(), union_->options.begin(), union_->options.end());
|
queue.insert(queue.end(), union_->options.begin(), union_->options.end());
|
||||||
|
@ -2654,7 +2654,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
||||||
LUAU_ASSERT(get<AnyType>(anyTy) || get<ErrorType>(anyTy) || get<UnknownType>(anyTy) || get<NeverType>(anyTy));
|
LUAU_ASSERT(get<AnyType>(anyTy) || get<ErrorType>(anyTy) || get<UnknownType>(anyTy) || get<NeverType>(anyTy));
|
||||||
|
|
||||||
// These types are not visited in general loop below
|
// These types are not visited in general loop below
|
||||||
if (log.get<PrimitiveType>(subTy) || log.get<AnyType>(subTy) || log.get<ClassType>(subTy))
|
if (log.get<PrimitiveType>(subTy) || log.get<AnyType>(subTy) || log.get<ExternType>(subTy))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
||||||
|
|
|
@ -321,8 +321,8 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
{
|
{
|
||||||
for (TypeId generic : subFn->generics)
|
for (TypeId generic : subFn->generics)
|
||||||
{
|
{
|
||||||
const GenericType* gen = get<GenericType>(generic);
|
const GenericType* gen = get<GenericType>(follow(generic));
|
||||||
LUAU_ASSERT(gen);
|
if (gen)
|
||||||
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
if (FFlag::LuauNonReentrantGeneralization2)
|
if (FFlag::LuauNonReentrantGeneralization2)
|
||||||
{
|
{
|
||||||
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||||
LUAU_ASSERT(gen);
|
if (gen)
|
||||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -446,7 +446,25 @@ public:
|
||||||
AstStatBlock* body,
|
AstStatBlock* body,
|
||||||
size_t functionDepth,
|
size_t functionDepth,
|
||||||
const AstName& debugname,
|
const AstName& debugname,
|
||||||
const std::optional<AstTypeList>& returnAnnotation = {},
|
AstTypePack* returnAnnotation,
|
||||||
|
AstTypePack* varargAnnotation = nullptr,
|
||||||
|
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,
|
AstTypePack* varargAnnotation = nullptr,
|
||||||
const std::optional<Location>& argLocation = std::nullopt
|
const std::optional<Location>& argLocation = std::nullopt
|
||||||
);
|
);
|
||||||
|
@ -461,7 +479,9 @@ public:
|
||||||
AstArray<AstGenericTypePack*> genericPacks;
|
AstArray<AstGenericTypePack*> genericPacks;
|
||||||
AstLocal* self;
|
AstLocal* self;
|
||||||
AstArray<AstLocal*> args;
|
AstArray<AstLocal*> args;
|
||||||
std::optional<AstTypeList> returnAnnotation;
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
|
std::optional<AstTypeList> returnAnnotation_DEPRECATED;
|
||||||
|
AstTypePack* returnAnnotation = nullptr;
|
||||||
bool vararg = false;
|
bool vararg = false;
|
||||||
Location varargLocation;
|
Location varargLocation;
|
||||||
AstTypePack* varargAnnotation;
|
AstTypePack* varargAnnotation;
|
||||||
|
@ -929,6 +949,36 @@ class AstStatDeclareFunction : public AstStat
|
||||||
public:
|
public:
|
||||||
LUAU_RTTI(AstStatDeclareFunction)
|
LUAU_RTTI(AstStatDeclareFunction)
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
AstTypePack* 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,
|
||||||
|
AstTypePack* retTypes
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
AstStatDeclareFunction(
|
AstStatDeclareFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstName& name,
|
const AstName& name,
|
||||||
|
@ -942,6 +992,7 @@ public:
|
||||||
const AstTypeList& retTypes
|
const AstTypeList& retTypes
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
AstStatDeclareFunction(
|
AstStatDeclareFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstArray<AstAttr*>& attributes,
|
const AstArray<AstAttr*>& attributes,
|
||||||
|
@ -971,10 +1022,12 @@ public:
|
||||||
AstArray<AstArgumentName> paramNames;
|
AstArray<AstArgumentName> paramNames;
|
||||||
bool vararg = false;
|
bool vararg = false;
|
||||||
Location varargLocation;
|
Location varargLocation;
|
||||||
AstTypeList retTypes;
|
AstTypePack* retTypes;
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
|
AstTypeList retTypes_DEPRECATED;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstDeclaredClassProp
|
struct AstDeclaredExternTypeProperty
|
||||||
{
|
{
|
||||||
AstName name;
|
AstName name;
|
||||||
Location nameLocation;
|
Location nameLocation;
|
||||||
|
@ -1000,16 +1053,16 @@ struct AstTableIndexer
|
||||||
std::optional<Location> accessLocation;
|
std::optional<Location> accessLocation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AstStatDeclareClass : public AstStat
|
class AstStatDeclareExternType : public AstStat
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LUAU_RTTI(AstStatDeclareClass)
|
LUAU_RTTI(AstStatDeclareExternType)
|
||||||
|
|
||||||
AstStatDeclareClass(
|
AstStatDeclareExternType(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstName& name,
|
const AstName& name,
|
||||||
std::optional<AstName> superName,
|
std::optional<AstName> superName,
|
||||||
const AstArray<AstDeclaredClassProp>& props,
|
const AstArray<AstDeclaredExternTypeProperty>& props,
|
||||||
AstTableIndexer* indexer = nullptr
|
AstTableIndexer* indexer = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1018,7 +1071,7 @@ public:
|
||||||
AstName name;
|
AstName name;
|
||||||
std::optional<AstName> superName;
|
std::optional<AstName> superName;
|
||||||
|
|
||||||
AstArray<AstDeclaredClassProp> props;
|
AstArray<AstDeclaredExternTypeProperty> props;
|
||||||
AstTableIndexer* indexer;
|
AstTableIndexer* indexer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1095,6 +1148,26 @@ class AstTypeFunction : public AstType
|
||||||
public:
|
public:
|
||||||
LUAU_RTTI(AstTypeFunction)
|
LUAU_RTTI(AstTypeFunction)
|
||||||
|
|
||||||
|
AstTypeFunction(
|
||||||
|
const Location& location,
|
||||||
|
const AstArray<AstGenericType*>& generics,
|
||||||
|
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||||
|
const AstTypeList& argTypes,
|
||||||
|
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||||
|
AstTypePack* returnTypes
|
||||||
|
);
|
||||||
|
|
||||||
|
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,
|
||||||
|
AstTypePack* returnTypes
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
AstTypeFunction(
|
AstTypeFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstArray<AstGenericType*>& generics,
|
const AstArray<AstGenericType*>& generics,
|
||||||
|
@ -1104,6 +1177,7 @@ public:
|
||||||
const AstTypeList& returnTypes
|
const AstTypeList& returnTypes
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
AstTypeFunction(
|
AstTypeFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstArray<AstAttr*>& attributes,
|
const AstArray<AstAttr*>& attributes,
|
||||||
|
@ -1124,7 +1198,9 @@ public:
|
||||||
AstArray<AstGenericTypePack*> genericPacks;
|
AstArray<AstGenericTypePack*> genericPacks;
|
||||||
AstTypeList argTypes;
|
AstTypeList argTypes;
|
||||||
AstArray<std::optional<AstArgumentName>> argNames;
|
AstArray<std::optional<AstArgumentName>> argNames;
|
||||||
AstTypeList returnTypes;
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
|
AstTypeList returnTypes_DEPRECATED;
|
||||||
|
AstTypePack* returnTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AstTypeTypeof : public AstType
|
class AstTypeTypeof : public AstType
|
||||||
|
@ -1483,7 +1559,7 @@ public:
|
||||||
{
|
{
|
||||||
return visit(static_cast<AstStat*>(node));
|
return visit(static_cast<AstStat*>(node));
|
||||||
}
|
}
|
||||||
virtual bool visit(class AstStatDeclareClass* node)
|
virtual bool visit(class AstStatDeclareExternType* node)
|
||||||
{
|
{
|
||||||
return visit(static_cast<AstStat*>(node));
|
return visit(static_cast<AstStat*>(node));
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,8 +473,10 @@ class CstTypePackExplicit : public CstNode
|
||||||
public:
|
public:
|
||||||
LUAU_CST_RTTI(CstTypePackExplicit)
|
LUAU_CST_RTTI(CstTypePackExplicit)
|
||||||
|
|
||||||
CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions);
|
explicit CstTypePackExplicit();
|
||||||
|
explicit CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions);
|
||||||
|
|
||||||
|
bool hasParentheses;
|
||||||
Position openParenthesesPosition;
|
Position openParenthesesPosition;
|
||||||
Position closeParenthesesPosition;
|
Position closeParenthesesPosition;
|
||||||
AstArray<Position> commaPositions;
|
AstArray<Position> commaPositions;
|
||||||
|
|
|
@ -157,8 +157,8 @@ private:
|
||||||
// type function Name ... end
|
// type function Name ... end
|
||||||
AstStat* parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition);
|
AstStat* parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition);
|
||||||
|
|
||||||
AstDeclaredClassProp parseDeclaredClassMethod(const AstArray<AstAttr*>& attributes);
|
AstDeclaredExternTypeProperty parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes);
|
||||||
AstDeclaredClassProp parseDeclaredClassMethod_DEPRECATED();
|
AstDeclaredExternTypeProperty parseDeclaredExternTypeMethod_DEPRECATED();
|
||||||
|
|
||||||
|
|
||||||
// `declare global' Name: Type |
|
// `declare global' Name: Type |
|
||||||
|
@ -182,6 +182,14 @@ private:
|
||||||
const Name* localName,
|
const Name* localName,
|
||||||
const AstArray<AstAttr*>& attributes
|
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
|
// explist ::= {exp `,'} exp
|
||||||
void parseExprList(TempVector<AstExpr*>& result, TempVector<Position>* commaPositions = nullptr);
|
void parseExprList(TempVector<AstExpr*>& result, TempVector<Position>* commaPositions = nullptr);
|
||||||
|
@ -219,8 +227,12 @@ private:
|
||||||
TempVector<std::optional<Position>>* nameColonPositions = nullptr
|
TempVector<std::optional<Position>>* nameColonPositions = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
std::optional<AstTypeList> parseOptionalReturnType(Position* returnSpecifierPosition = nullptr);
|
AstTypePack* parseOptionalReturnType(Position* returnSpecifierPosition = nullptr);
|
||||||
std::pair<Location, AstTypeList> parseReturnType();
|
AstTypePack* parseReturnType();
|
||||||
|
|
||||||
|
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||||
|
std::optional<AstTypeList> parseOptionalReturnType_DEPRECATED(Position* returnSpecifierPosition = nullptr);
|
||||||
|
std::pair<Location, AstTypeList> parseReturnType_DEPRECATED();
|
||||||
|
|
||||||
struct TableIndexerResult
|
struct TableIndexerResult
|
||||||
{
|
{
|
||||||
|
@ -491,7 +503,7 @@ private:
|
||||||
std::vector<CstTypeTable::Item> scratchCstTableTypeProps;
|
std::vector<CstTypeTable::Item> scratchCstTableTypeProps;
|
||||||
std::vector<AstType*> scratchType;
|
std::vector<AstType*> scratchType;
|
||||||
std::vector<AstTypeOrPack> scratchTypeOrPack;
|
std::vector<AstTypeOrPack> scratchTypeOrPack;
|
||||||
std::vector<AstDeclaredClassProp> scratchDeclaredClassProps;
|
std::vector<AstDeclaredExternTypeProperty> scratchDeclaredClassProps;
|
||||||
std::vector<AstExprTable::Item> scratchItem;
|
std::vector<AstExprTable::Item> scratchItem;
|
||||||
std::vector<CstExprTable::Item> scratchCstItem;
|
std::vector<CstExprTable::Item> scratchCstItem;
|
||||||
std::vector<AstArgumentName> scratchArgName;
|
std::vector<AstArgumentName> scratchArgName;
|
||||||
|
|
178
Ast/src/Ast.cpp
178
Ast/src/Ast.cpp
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -241,7 +242,7 @@ AstExprFunction::AstExprFunction(
|
||||||
AstStatBlock* body,
|
AstStatBlock* body,
|
||||||
size_t functionDepth,
|
size_t functionDepth,
|
||||||
const AstName& debugname,
|
const AstName& debugname,
|
||||||
const std::optional<AstTypeList>& returnAnnotation,
|
AstTypePack* returnAnnotation,
|
||||||
AstTypePack* varargAnnotation,
|
AstTypePack* varargAnnotation,
|
||||||
const std::optional<Location>& argLocation
|
const std::optional<Location>& argLocation
|
||||||
)
|
)
|
||||||
|
@ -260,6 +261,41 @@ AstExprFunction::AstExprFunction(
|
||||||
, debugname(debugname)
|
, debugname(debugname)
|
||||||
, argLocation(argLocation)
|
, 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)
|
void AstExprFunction::visit(AstVisitor* visitor)
|
||||||
|
@ -275,8 +311,16 @@ void AstExprFunction::visit(AstVisitor* visitor)
|
||||||
if (varargAnnotation)
|
if (varargAnnotation)
|
||||||
varargAnnotation->visit(visitor);
|
varargAnnotation->visit(visitor);
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
if (returnAnnotation)
|
if (returnAnnotation)
|
||||||
visitTypeList(visitor, *returnAnnotation);
|
returnAnnotation->visit(visitor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (returnAnnotation_DEPRECATED)
|
||||||
|
visitTypeList(visitor, *returnAnnotation_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
body->visit(visitor);
|
body->visit(visitor);
|
||||||
}
|
}
|
||||||
|
@ -855,7 +899,7 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
||||||
const AstArray<AstArgumentName>& paramNames,
|
const AstArray<AstArgumentName>& paramNames,
|
||||||
bool vararg,
|
bool vararg,
|
||||||
const Location& varargLocation,
|
const Location& varargLocation,
|
||||||
const AstTypeList& retTypes
|
AstTypePack* retTypes
|
||||||
)
|
)
|
||||||
: AstStat(ClassIndex(), location)
|
: AstStat(ClassIndex(), location)
|
||||||
, attributes()
|
, attributes()
|
||||||
|
@ -869,8 +913,66 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
||||||
, varargLocation(varargLocation)
|
, varargLocation(varargLocation)
|
||||||
, retTypes(retTypes)
|
, retTypes(retTypes)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
AstTypePack* retTypes
|
||||||
|
)
|
||||||
|
: AstStat(ClassIndex(), location)
|
||||||
|
, attributes(attributes)
|
||||||
|
, name(name)
|
||||||
|
, nameLocation(nameLocation)
|
||||||
|
, generics(generics)
|
||||||
|
, genericPacks(genericPacks)
|
||||||
|
, params(params)
|
||||||
|
, paramNames(paramNames)
|
||||||
|
, vararg(vararg)
|
||||||
|
, 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(
|
AstStatDeclareFunction::AstStatDeclareFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstArray<AstAttr*>& attributes,
|
const AstArray<AstAttr*>& attributes,
|
||||||
|
@ -894,8 +996,9 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
||||||
, paramNames(paramNames)
|
, paramNames(paramNames)
|
||||||
, vararg(vararg)
|
, vararg(vararg)
|
||||||
, varargLocation(varargLocation)
|
, varargLocation(varargLocation)
|
||||||
, retTypes(retTypes)
|
, retTypes_DEPRECATED(retTypes)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
||||||
|
@ -903,7 +1006,10 @@ void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
||||||
if (visitor->visit(this))
|
if (visitor->visit(this))
|
||||||
{
|
{
|
||||||
visitTypeList(visitor, params);
|
visitTypeList(visitor, params);
|
||||||
visitTypeList(visitor, retTypes);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
retTypes->visit(visitor);
|
||||||
|
else
|
||||||
|
visitTypeList(visitor, retTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,11 +1031,11 @@ bool AstStatDeclareFunction::hasAttribute(AstAttr::Type attributeType) const
|
||||||
return hasAttributeInArray(attributes, attributeType);
|
return hasAttributeInArray(attributes, attributeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstStatDeclareClass::AstStatDeclareClass(
|
AstStatDeclareExternType::AstStatDeclareExternType(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstName& name,
|
const AstName& name,
|
||||||
std::optional<AstName> superName,
|
std::optional<AstName> superName,
|
||||||
const AstArray<AstDeclaredClassProp>& props,
|
const AstArray<AstDeclaredExternTypeProperty>& props,
|
||||||
AstTableIndexer* indexer
|
AstTableIndexer* indexer
|
||||||
)
|
)
|
||||||
: AstStat(ClassIndex(), location)
|
: AstStat(ClassIndex(), location)
|
||||||
|
@ -940,11 +1046,11 @@ AstStatDeclareClass::AstStatDeclareClass(
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstStatDeclareClass::visit(AstVisitor* visitor)
|
void AstStatDeclareExternType::visit(AstVisitor* visitor)
|
||||||
{
|
{
|
||||||
if (visitor->visit(this))
|
if (visitor->visit(this))
|
||||||
{
|
{
|
||||||
for (const AstDeclaredClassProp& prop : props)
|
for (const AstDeclaredExternTypeProperty& prop : props)
|
||||||
prop.ty->visit(visitor);
|
prop.ty->visit(visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1035,7 +1141,7 @@ AstTypeFunction::AstTypeFunction(
|
||||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||||
const AstTypeList& argTypes,
|
const AstTypeList& argTypes,
|
||||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||||
const AstTypeList& returnTypes
|
AstTypePack* returnTypes
|
||||||
)
|
)
|
||||||
: AstType(ClassIndex(), location)
|
: AstType(ClassIndex(), location)
|
||||||
, attributes()
|
, attributes()
|
||||||
|
@ -1045,9 +1151,53 @@ AstTypeFunction::AstTypeFunction(
|
||||||
, argNames(argNames)
|
, argNames(argNames)
|
||||||
, returnTypes(returnTypes)
|
, returnTypes(returnTypes)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
AstTypePack* returnTypes
|
||||||
|
)
|
||||||
|
: AstType(ClassIndex(), location)
|
||||||
|
, attributes(attributes)
|
||||||
|
, generics(generics)
|
||||||
|
, genericPacks(genericPacks)
|
||||||
|
, argTypes(argTypes)
|
||||||
|
, 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(
|
AstTypeFunction::AstTypeFunction(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstArray<AstAttr*>& attributes,
|
const AstArray<AstAttr*>& attributes,
|
||||||
|
@ -1063,8 +1213,9 @@ AstTypeFunction::AstTypeFunction(
|
||||||
, genericPacks(genericPacks)
|
, genericPacks(genericPacks)
|
||||||
, argTypes(argTypes)
|
, argTypes(argTypes)
|
||||||
, argNames(argNames)
|
, argNames(argNames)
|
||||||
, returnTypes(returnTypes)
|
, returnTypes_DEPRECATED(returnTypes)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,7 +1224,10 @@ void AstTypeFunction::visit(AstVisitor* visitor)
|
||||||
if (visitor->visit(this))
|
if (visitor->visit(this))
|
||||||
{
|
{
|
||||||
visitTypeList(visitor, argTypes);
|
visitTypeList(visitor, argTypes);
|
||||||
visitTypeList(visitor, returnTypes);
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
returnTypes->visit(visitor);
|
||||||
|
else
|
||||||
|
visitTypeList(visitor, returnTypes_DEPRECATED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -252,8 +252,18 @@ CstTypeSingletonString::CstTypeSingletonString(AstArray<char> sourceString, CstE
|
||||||
LUAU_ASSERT(quoteStyle != CstExprConstantString::QuotedInterp);
|
LUAU_ASSERT(quoteStyle != CstExprConstantString::QuotedInterp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CstTypePackExplicit::CstTypePackExplicit()
|
||||||
|
: CstNode(CstClassIndex())
|
||||||
|
, hasParentheses(false)
|
||||||
|
, openParenthesesPosition(Position{0, 0})
|
||||||
|
, closeParenthesesPosition(Position{0, 0})
|
||||||
|
, commaPositions({})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CstTypePackExplicit::CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions)
|
CstTypePackExplicit::CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions)
|
||||||
: CstNode(CstClassIndex())
|
: CstNode(CstClassIndex())
|
||||||
|
, hasParentheses(true)
|
||||||
, openParenthesesPosition(openParenthesesPosition)
|
, openParenthesesPosition(openParenthesesPosition)
|
||||||
, closeParenthesesPosition(closeParenthesesPosition)
|
, closeParenthesesPosition(closeParenthesesPosition)
|
||||||
, commaPositions(commaPositions)
|
, commaPositions(commaPositions)
|
||||||
|
|
|
@ -19,14 +19,14 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
// See docs/SyntaxChanges.md for an explanation.
|
// See docs/SyntaxChanges.md for an explanation.
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunResultInAutocomplete)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunResultInAutocomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDeprecatedAttribute)
|
LUAU_FASTFLAGVARIABLE(LuauDeprecatedAttribute)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixFunctionWithAttributesStartLocation)
|
LUAU_FASTFLAGVARIABLE(LuauFixFunctionWithAttributesStartLocation)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
||||||
|
|
||||||
|
@ -788,7 +788,9 @@ AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
||||||
|
|
||||||
AstExprFunction* body = parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first;
|
AstExprFunction* body = FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||||
|
? parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first
|
||||||
|
: parseFunctionBody_DEPRECATED(hasself, matchFunction, debugname, nullptr, attributes).first;
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
||||||
|
|
||||||
|
@ -941,7 +943,8 @@ AstStat* Parser::parseLocal(const AstArray<AstAttr*>& attributes)
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
||||||
|
|
||||||
auto [body, var] = parseFunctionBody(false, matchFunction, name.name, &name, attributes);
|
auto [body, var] = FFlag::LuauStoreReturnTypesAsPackOnAst ? parseFunctionBody(false, matchFunction, name.name, &name, attributes)
|
||||||
|
: parseFunctionBody_DEPRECATED(false, matchFunction, name.name, &name, attributes);
|
||||||
|
|
||||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
||||||
|
|
||||||
|
@ -1111,7 +1114,10 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio
|
||||||
size_t oldTypeFunctionDepth = typeFunctionDepth;
|
size_t oldTypeFunctionDepth = typeFunctionDepth;
|
||||||
typeFunctionDepth = functionStack.size();
|
typeFunctionDepth = functionStack.size();
|
||||||
|
|
||||||
AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
|
AstExprFunction* body =
|
||||||
|
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||||
|
? parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first
|
||||||
|
: parseFunctionBody_DEPRECATED(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
|
||||||
|
|
||||||
typeFunctionDepth = oldTypeFunctionDepth;
|
typeFunctionDepth = oldTypeFunctionDepth;
|
||||||
|
|
||||||
|
@ -1133,7 +1139,7 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod(const AstArray<AstAttr*>& attributes)
|
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||||
|
|
||||||
|
@ -1164,7 +1170,18 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod(const AstArray<AstAttr*>&
|
||||||
|
|
||||||
expectMatchAndConsume(')', matchParen);
|
expectMatchAndConsume(')', matchParen);
|
||||||
|
|
||||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
AstTypePack* retTypes;
|
||||||
|
AstTypeList retTypes_DEPRECATED;
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
retTypes = parseOptionalReturnType();
|
||||||
|
if (!retTypes)
|
||||||
|
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||||
|
}
|
||||||
Location end = lexer.previousLocation();
|
Location end = lexer.previousLocation();
|
||||||
|
|
||||||
TempVector<AstType*> vars(scratchType);
|
TempVector<AstType*> vars(scratchType);
|
||||||
|
@ -1172,7 +1189,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod(const AstArray<AstAttr*>&
|
||||||
|
|
||||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||||
{
|
{
|
||||||
return AstDeclaredClassProp{
|
return AstDeclaredExternTypeProperty{
|
||||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1191,14 +1208,25 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod(const AstArray<AstAttr*>&
|
||||||
if (vararg && !varargAnnotation)
|
if (vararg && !varargAnnotation)
|
||||||
report(start, "All declaration parameters aside from 'self' must be annotated");
|
report(start, "All declaration parameters aside from 'self' must be annotated");
|
||||||
|
|
||||||
AstType* fnType = allocator.alloc<AstTypeFunction>(
|
AstType* fnType =
|
||||||
|
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||||
|
? allocator.alloc<AstTypeFunction>(
|
||||||
Location(start, end), attributes, generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
Location(start, end), attributes, generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
||||||
|
)
|
||||||
|
: allocator.alloc<AstTypeFunction>(
|
||||||
|
Location(start, end),
|
||||||
|
attributes,
|
||||||
|
generics,
|
||||||
|
genericPacks,
|
||||||
|
AstTypeList{copy(vars), varargAnnotation},
|
||||||
|
copy(varNames),
|
||||||
|
retTypes_DEPRECATED
|
||||||
);
|
);
|
||||||
|
|
||||||
return AstDeclaredClassProp{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||||
}
|
}
|
||||||
|
|
||||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod_DEPRECATED()
|
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
||||||
{
|
{
|
||||||
Location start = lexer.current().location;
|
Location start = lexer.current().location;
|
||||||
|
|
||||||
|
@ -1227,7 +1255,18 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod_DEPRECATED()
|
||||||
|
|
||||||
expectMatchAndConsume(')', matchParen);
|
expectMatchAndConsume(')', matchParen);
|
||||||
|
|
||||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
AstTypePack* retTypes;
|
||||||
|
AstTypeList retTypes_DEPRECATED;
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
retTypes = parseOptionalReturnType();
|
||||||
|
if (!retTypes)
|
||||||
|
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||||
|
}
|
||||||
Location end = lexer.previousLocation();
|
Location end = lexer.previousLocation();
|
||||||
|
|
||||||
TempVector<AstType*> vars(scratchType);
|
TempVector<AstType*> vars(scratchType);
|
||||||
|
@ -1235,7 +1274,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod_DEPRECATED()
|
||||||
|
|
||||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||||
{
|
{
|
||||||
return AstDeclaredClassProp{
|
return AstDeclaredExternTypeProperty{
|
||||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1254,11 +1293,16 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod_DEPRECATED()
|
||||||
if (vararg && !varargAnnotation)
|
if (vararg && !varargAnnotation)
|
||||||
report(start, "All declaration parameters aside from 'self' must be annotated");
|
report(start, "All declaration parameters aside from 'self' must be annotated");
|
||||||
|
|
||||||
AstType* fnType = allocator.alloc<AstTypeFunction>(
|
AstType* fnType =
|
||||||
|
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||||
|
? allocator.alloc<AstTypeFunction>(
|
||||||
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
||||||
|
)
|
||||||
|
: allocator.alloc<AstTypeFunction>(
|
||||||
|
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes_DEPRECATED
|
||||||
);
|
);
|
||||||
|
|
||||||
return AstDeclaredClassProp{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||||
}
|
}
|
||||||
|
|
||||||
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
|
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
|
||||||
|
@ -1296,7 +1340,18 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
|
|
||||||
expectMatchAndConsume(')', matchParen);
|
expectMatchAndConsume(')', matchParen);
|
||||||
|
|
||||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0)});
|
AstTypePack* retTypes;
|
||||||
|
AstTypeList retTypes_DEPRECATED;
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
retTypes = parseOptionalReturnType();
|
||||||
|
if (!retTypes)
|
||||||
|
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy<AstType*>(nullptr, 0)});
|
||||||
|
}
|
||||||
Location end = lexer.current().location;
|
Location end = lexer.current().location;
|
||||||
|
|
||||||
TempVector<AstType*> vars(scratchType);
|
TempVector<AstType*> vars(scratchType);
|
||||||
|
@ -1314,6 +1369,8 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
if (vararg && !varargAnnotation)
|
if (vararg && !varargAnnotation)
|
||||||
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
|
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
return allocator.alloc<AstStatDeclareFunction>(
|
return allocator.alloc<AstStatDeclareFunction>(
|
||||||
Location(start, end),
|
Location(start, end),
|
||||||
attributes,
|
attributes,
|
||||||
|
@ -1328,20 +1385,61 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
retTypes
|
retTypes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (AstName(lexer.current().name) == "class")
|
else
|
||||||
{
|
{
|
||||||
|
return allocator.alloc<AstStatDeclareFunction>(
|
||||||
|
Location(start, end),
|
||||||
|
attributes,
|
||||||
|
globalName.name,
|
||||||
|
globalName.location,
|
||||||
|
generics,
|
||||||
|
genericPacks,
|
||||||
|
AstTypeList{copy(vars), varargAnnotation},
|
||||||
|
copy(varNames),
|
||||||
|
vararg,
|
||||||
|
varargLocation,
|
||||||
|
retTypes_DEPRECATED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (AstName(lexer.current().name) == "class" || (FFlag::LuauDeclareExternType && AstName(lexer.current().name) == "extern"))
|
||||||
|
{
|
||||||
|
bool foundExtern = false;
|
||||||
|
if (FFlag::LuauDeclareExternType)
|
||||||
|
{
|
||||||
|
if (AstName(lexer.current().name) == "extern")
|
||||||
|
{
|
||||||
|
foundExtern = true;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
if (AstName(lexer.current().name) != "type")
|
||||||
|
return reportStatError(lexer.current().location, {}, {}, "Expected `type` keyword after `extern`, but got %s instead", lexer.current().name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextLexeme();
|
||||||
|
|
||||||
Location classStart = lexer.current().location;
|
Location classStart = lexer.current().location;
|
||||||
Name className = parseName("class name");
|
Name className = parseName(FFlag::LuauDeclareExternType ? "type name" : "class name");
|
||||||
std::optional<AstName> superName = std::nullopt;
|
std::optional<AstName> superName = std::nullopt;
|
||||||
|
|
||||||
if (AstName(lexer.current().name) == "extends")
|
if (AstName(lexer.current().name) == "extends")
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
superName = parseName("superclass name").name;
|
superName = parseName(FFlag::LuauDeclareExternType ? "supertype name" : "superclass name").name;
|
||||||
}
|
}
|
||||||
|
|
||||||
TempVector<AstDeclaredClassProp> props(scratchDeclaredClassProps);
|
if (FFlag::LuauDeclareExternType)
|
||||||
|
{
|
||||||
|
if (foundExtern)
|
||||||
|
{
|
||||||
|
if (AstName(lexer.current().name) != "with")
|
||||||
|
report(lexer.current().location, "Expected `with` keyword before listing properties of the external type, but got %s instead", lexer.current().name);
|
||||||
|
else
|
||||||
|
nextLexeme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TempVector<AstDeclaredExternTypeProperty> props(scratchDeclaredClassProps);
|
||||||
AstTableIndexer* indexer = nullptr;
|
AstTableIndexer* indexer = nullptr;
|
||||||
|
|
||||||
while (lexer.current().type != Lexeme::ReservedEnd)
|
while (lexer.current().type != Lexeme::ReservedEnd)
|
||||||
|
@ -1368,9 +1466,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
if (FFlag::LuauDeprecatedAttribute)
|
||||||
props.push_back(parseDeclaredClassMethod(attributes));
|
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||||
else
|
else
|
||||||
props.push_back(parseDeclaredClassMethod_DEPRECATED());
|
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '[')
|
else if (lexer.current().type == '[')
|
||||||
{
|
{
|
||||||
|
@ -1393,7 +1491,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
|
|
||||||
if (chars && !containsNull)
|
if (chars && !containsNull)
|
||||||
{
|
{
|
||||||
props.push_back(AstDeclaredClassProp{
|
props.push_back(AstDeclaredExternTypeProperty{
|
||||||
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1413,6 +1511,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, begin);
|
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, begin);
|
||||||
|
|
||||||
// we lose all additional indexer expressions from the AST after error recovery here
|
// we lose all additional indexer expressions from the AST after error recovery here
|
||||||
|
if (FFlag::LuauDeclareExternType)
|
||||||
|
report(badIndexer->location, "Cannot have more than one indexer on an extern type");
|
||||||
|
else
|
||||||
report(badIndexer->location, "Cannot have more than one class indexer");
|
report(badIndexer->location, "Cannot have more than one class indexer");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1434,7 +1535,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
expectAndConsume(':', "property type annotation");
|
expectAndConsume(':', "property type annotation");
|
||||||
AstType* propType = parseType();
|
AstType* propType = parseType();
|
||||||
props.push_back(
|
props.push_back(
|
||||||
AstDeclaredClassProp{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1444,9 +1545,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
if (FFlag::LuauDeprecatedAttribute)
|
||||||
props.push_back(parseDeclaredClassMethod(attributes));
|
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||||
else
|
else
|
||||||
props.push_back(parseDeclaredClassMethod_DEPRECATED());
|
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '[' &&
|
else if (lexer.current().type == '[' &&
|
||||||
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
|
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
|
||||||
|
@ -1468,7 +1569,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
|
|
||||||
if (chars && !containsNull)
|
if (chars && !containsNull)
|
||||||
{
|
{
|
||||||
props.push_back(AstDeclaredClassProp{
|
props.push_back(AstDeclaredExternTypeProperty{
|
||||||
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1492,6 +1593,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, lexer.current());
|
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, lexer.current());
|
||||||
|
|
||||||
// we lose all additional indexer expressions from the AST after error recovery here
|
// we lose all additional indexer expressions from the AST after error recovery here
|
||||||
|
if (FFlag::LuauDeclareExternType)
|
||||||
|
report(badIndexer->location, "Cannot have more than one indexer on an extern type");
|
||||||
|
else
|
||||||
report(badIndexer->location, "Cannot have more than one class indexer");
|
report(badIndexer->location, "Cannot have more than one class indexer");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1515,7 +1619,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
expectAndConsume(':', "property type annotation");
|
expectAndConsume(':', "property type annotation");
|
||||||
AstType* propType = parseType();
|
AstType* propType = parseType();
|
||||||
props.push_back(
|
props.push_back(
|
||||||
AstDeclaredClassProp{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1524,7 +1628,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
Location classEnd = lexer.current().location;
|
Location classEnd = lexer.current().location;
|
||||||
nextLexeme(); // skip past `end`
|
nextLexeme(); // skip past `end`
|
||||||
|
|
||||||
return allocator.alloc<AstStatDeclareClass>(Location(classStart, classEnd), className.name, superName, copy(props), indexer);
|
return allocator.alloc<AstStatDeclareExternType>(Location(classStart, classEnd), className.name, superName, copy(props), indexer);
|
||||||
}
|
}
|
||||||
else if (std::optional<Name> globalName = parseNameOpt("global variable name"))
|
else if (std::optional<Name> globalName = parseNameOpt("global variable name"))
|
||||||
{
|
{
|
||||||
|
@ -1533,6 +1637,10 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
AstType* type = parseType(/* in declaration context */ true);
|
AstType* type = parseType(/* in declaration context */ true);
|
||||||
return allocator.alloc<AstStatDeclareGlobal>(Location(start, type->location), globalName->name, globalName->location, type);
|
return allocator.alloc<AstStatDeclareGlobal>(Location(start, type->location), globalName->name, globalName->location, type);
|
||||||
}
|
}
|
||||||
|
else if (FFlag::LuauDeclareExternType)
|
||||||
|
{
|
||||||
|
return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'extern type'");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'class'");
|
return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'class'");
|
||||||
|
@ -1638,6 +1746,8 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
||||||
const AstArray<AstAttr*>& attributes
|
const AstArray<AstAttr*>& attributes
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
|
|
||||||
Location start = matchFunction.location;
|
Location start = matchFunction.location;
|
||||||
|
|
||||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||||
|
@ -1689,7 +1799,145 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
||||||
|
|
||||||
matchRecoveryStopOnToken[')']--;
|
matchRecoveryStopOnToken[')']--;
|
||||||
|
|
||||||
std::optional<AstTypeList> typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
AstTypePack* typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
||||||
|
|
||||||
|
AstLocal* funLocal = nullptr;
|
||||||
|
|
||||||
|
if (localName)
|
||||||
|
funLocal = pushLocal(Binding(*localName, nullptr));
|
||||||
|
|
||||||
|
unsigned int localsBegin = saveLocals();
|
||||||
|
|
||||||
|
Function fun;
|
||||||
|
fun.vararg = vararg;
|
||||||
|
|
||||||
|
functionStack.emplace_back(fun);
|
||||||
|
|
||||||
|
auto [self, vars] = prepareFunctionArguments(start, hasself, args);
|
||||||
|
|
||||||
|
AstStatBlock* body = parseBlock();
|
||||||
|
|
||||||
|
functionStack.pop_back();
|
||||||
|
|
||||||
|
restoreLocals(localsBegin);
|
||||||
|
|
||||||
|
Location end = lexer.current().location;
|
||||||
|
|
||||||
|
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction);
|
||||||
|
body->hasEnd = hasEnd;
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreCSTData2)
|
||||||
|
{
|
||||||
|
AstExprFunction* node = allocator.alloc<AstExprFunction>(
|
||||||
|
Location(start, end),
|
||||||
|
attributes,
|
||||||
|
generics,
|
||||||
|
genericPacks,
|
||||||
|
self,
|
||||||
|
vars,
|
||||||
|
vararg,
|
||||||
|
varargLocation,
|
||||||
|
body,
|
||||||
|
functionStack.size(),
|
||||||
|
debugname,
|
||||||
|
typelist,
|
||||||
|
varargAnnotation,
|
||||||
|
argLocation
|
||||||
|
);
|
||||||
|
if (options.storeCstData)
|
||||||
|
{
|
||||||
|
cstNode->functionKeywordPosition = matchFunction.location.begin;
|
||||||
|
cstNodeMap[node] = cstNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {node, funLocal};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
allocator.alloc<AstExprFunction>(
|
||||||
|
Location(start, end),
|
||||||
|
attributes,
|
||||||
|
generics,
|
||||||
|
genericPacks,
|
||||||
|
self,
|
||||||
|
vars,
|
||||||
|
vararg,
|
||||||
|
varargLocation,
|
||||||
|
body,
|
||||||
|
functionStack.size(),
|
||||||
|
debugname,
|
||||||
|
typelist,
|
||||||
|
varargAnnotation,
|
||||||
|
argLocation
|
||||||
|
),
|
||||||
|
funLocal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody_DEPRECATED(
|
||||||
|
bool hasself,
|
||||||
|
const Lexeme& matchFunction,
|
||||||
|
const AstName& debugname,
|
||||||
|
const Name* localName,
|
||||||
|
const AstArray<AstAttr*>& attributes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
|
|
||||||
|
Location start = matchFunction.location;
|
||||||
|
|
||||||
|
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||||
|
{
|
||||||
|
if (attributes.size > 0)
|
||||||
|
start = attributes.data[0]->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* cstNode = FFlag::LuauStoreCSTData2 && options.storeCstData ? allocator.alloc<CstExprFunction>() : nullptr;
|
||||||
|
|
||||||
|
auto [generics, genericPacks] =
|
||||||
|
FFlag::LuauStoreCSTData2 && cstNode
|
||||||
|
? parseGenericTypeList(
|
||||||
|
/* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition
|
||||||
|
)
|
||||||
|
: parseGenericTypeList(/* withDefaultValues= */ false);
|
||||||
|
|
||||||
|
MatchLexeme matchParen = lexer.current();
|
||||||
|
expectAndConsume('(', "function");
|
||||||
|
|
||||||
|
// NOTE: This was added in conjunction with passing `searchForMissing` to
|
||||||
|
// `expectMatchAndConsume` inside `parseTableType` so that the behavior of
|
||||||
|
// parsing code like below (note the missing `}`):
|
||||||
|
//
|
||||||
|
// function (t: { a: number ) end
|
||||||
|
//
|
||||||
|
// ... will still parse as (roughly):
|
||||||
|
//
|
||||||
|
// function (t: { a: number }) end
|
||||||
|
//
|
||||||
|
matchRecoveryStopOnToken[')']++;
|
||||||
|
|
||||||
|
TempVector<Binding> args(scratchBinding);
|
||||||
|
|
||||||
|
bool vararg = false;
|
||||||
|
Location varargLocation;
|
||||||
|
AstTypePack* varargAnnotation = nullptr;
|
||||||
|
|
||||||
|
if (lexer.current().type != ')')
|
||||||
|
std::tie(vararg, varargLocation, varargAnnotation) =
|
||||||
|
parseBindingList(args, /* allowDot3= */ true, cstNode ? &cstNode->argsCommaPositions : nullptr);
|
||||||
|
|
||||||
|
std::optional<Location> argLocation;
|
||||||
|
|
||||||
|
if (matchParen.type == Lexeme::Type('(') && lexer.current().type == Lexeme::Type(')'))
|
||||||
|
argLocation = Location(matchParen.position, lexer.current().location.end);
|
||||||
|
|
||||||
|
expectMatchAndConsume(')', matchParen, true);
|
||||||
|
|
||||||
|
matchRecoveryStopOnToken[')']--;
|
||||||
|
|
||||||
|
std::optional<AstTypeList> typelist = parseOptionalReturnType_DEPRECATED(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
||||||
|
|
||||||
AstLocal* funLocal = nullptr;
|
AstLocal* funLocal = nullptr;
|
||||||
|
|
||||||
|
@ -1916,8 +2164,9 @@ AstTypePack* Parser::parseTypeList(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<AstTypeList> Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
|
AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow)
|
if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow)
|
||||||
{
|
{
|
||||||
if (lexer.current().type == Lexeme::SkinnyArrow)
|
if (lexer.current().type == Lexeme::SkinnyArrow)
|
||||||
|
@ -1929,7 +2178,41 @@ std::optional<AstTypeList> Parser::parseOptionalReturnType(Position* returnSpeci
|
||||||
|
|
||||||
unsigned int oldRecursionCount = recursionCounter;
|
unsigned int oldRecursionCount = recursionCounter;
|
||||||
|
|
||||||
auto [_location, result] = parseReturnType();
|
auto result = parseReturnType();
|
||||||
|
LUAU_ASSERT(result);
|
||||||
|
|
||||||
|
// At this point, if we find a , character, it indicates that there are multiple return types
|
||||||
|
// in this type annotation, but the list wasn't wrapped in parentheses.
|
||||||
|
if (lexer.current().type == ',')
|
||||||
|
{
|
||||||
|
report(lexer.current().location, "Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?");
|
||||||
|
|
||||||
|
nextLexeme();
|
||||||
|
}
|
||||||
|
|
||||||
|
recursionCounter = oldRecursionCount;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AstTypeList> Parser::parseOptionalReturnType_DEPRECATED(Luau::Position* returnSpecifierPosition)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
|
if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow)
|
||||||
|
{
|
||||||
|
if (lexer.current().type == Lexeme::SkinnyArrow)
|
||||||
|
report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'");
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreCSTData2 && returnSpecifierPosition)
|
||||||
|
*returnSpecifierPosition = lexer.current().location.begin;
|
||||||
|
nextLexeme();
|
||||||
|
|
||||||
|
unsigned int oldRecursionCount = recursionCounter;
|
||||||
|
|
||||||
|
auto [_location, result] = parseReturnType_DEPRECATED();
|
||||||
|
|
||||||
// At this point, if we find a , character, it indicates that there are multiple return types
|
// At this point, if we find a , character, it indicates that there are multiple return types
|
||||||
// in this type annotation, but the list wasn't wrapped in parentheses.
|
// in this type annotation, but the list wasn't wrapped in parentheses.
|
||||||
|
@ -1949,8 +2232,119 @@ std::optional<AstTypeList> Parser::parseOptionalReturnType(Position* returnSpeci
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReturnType ::= Type | `(' TypeList `)'
|
// ReturnType ::= Type | `(' TypeList `)'
|
||||||
std::pair<Location, AstTypeList> Parser::parseReturnType()
|
AstTypePack* Parser::parseReturnType()
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
|
incrementRecursionCounter("type annotation");
|
||||||
|
|
||||||
|
Lexeme begin = lexer.current();
|
||||||
|
|
||||||
|
if (lexer.current().type != '(')
|
||||||
|
{
|
||||||
|
if (shouldParseTypePack(lexer))
|
||||||
|
{
|
||||||
|
return parseTypePack();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AstType* type = parseType();
|
||||||
|
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(type->location, AstTypeList{copy(&type, 1), nullptr});
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextLexeme();
|
||||||
|
|
||||||
|
Location innerBegin = lexer.current().location;
|
||||||
|
|
||||||
|
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
||||||
|
|
||||||
|
TempVector<AstType*> result(scratchType);
|
||||||
|
TempVector<std::optional<AstArgumentName>> resultNames(scratchOptArgName);
|
||||||
|
TempVector<Position> commaPositions(scratchPosition);
|
||||||
|
AstTypePack* varargAnnotation = nullptr;
|
||||||
|
|
||||||
|
// possibly () -> ReturnType
|
||||||
|
if (lexer.current().type != ')')
|
||||||
|
{
|
||||||
|
if (options.storeCstData)
|
||||||
|
varargAnnotation = parseTypeList(result, resultNames, &commaPositions);
|
||||||
|
else
|
||||||
|
varargAnnotation = parseTypeList(result, resultNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Location location{begin.location, lexer.current().location};
|
||||||
|
Position closeParenthesesPosition = lexer.current().location.begin;
|
||||||
|
expectMatchAndConsume(')', begin, true);
|
||||||
|
|
||||||
|
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
|
||||||
|
|
||||||
|
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
|
||||||
|
{
|
||||||
|
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
|
||||||
|
if (FFlag::LuauAstTypeGroup3)
|
||||||
|
{
|
||||||
|
if (result.size() == 1)
|
||||||
|
{
|
||||||
|
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
|
||||||
|
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
|
||||||
|
AstType* returnType = parseTypeSuffix(inner, begin.location);
|
||||||
|
|
||||||
|
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||||
|
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||||
|
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||||
|
|
||||||
|
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||||
|
// type to successfully parse. We need the span of the whole annotation.
|
||||||
|
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||||
|
|
||||||
|
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (result.size() == 1)
|
||||||
|
{
|
||||||
|
AstType* returnType = parseTypeSuffix(result[0], innerBegin);
|
||||||
|
|
||||||
|
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||||
|
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||||
|
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||||
|
|
||||||
|
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||||
|
// type to successfully parse. We need the span of the whole annotation.
|
||||||
|
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||||
|
|
||||||
|
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(location, AstTypeList{copy(result), varargAnnotation});
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>(location.begin, closeParenthesesPosition, copy(commaPositions));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstType* tail = parseFunctionTypeTail(begin, {nullptr, 0}, {}, {}, copy(result), copy(resultNames), varargAnnotation);
|
||||||
|
|
||||||
|
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location, tail->location}, AstTypeList{copy(&tail, 1), nullptr});
|
||||||
|
if (options.storeCstData)
|
||||||
|
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnType ::= Type | `(' TypeList `)'
|
||||||
|
std::pair<Location, AstTypeList> Parser::parseReturnType_DEPRECATED()
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||||
incrementRecursionCounter("type annotation");
|
incrementRecursionCounter("type annotation");
|
||||||
|
|
||||||
Lexeme begin = lexer.current();
|
Lexeme begin = lexer.current();
|
||||||
|
@ -2574,12 +2968,25 @@ AstType* Parser::parseFunctionTypeTail(
|
||||||
expectAndConsume(Lexeme::SkinnyArrow, "function type");
|
expectAndConsume(Lexeme::SkinnyArrow, "function type");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [endLocation, returnTypeList] = parseReturnType();
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
auto returnType = parseReturnType();
|
||||||
|
LUAU_ASSERT(returnType);
|
||||||
|
|
||||||
|
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
||||||
|
return allocator.alloc<AstTypeFunction>(
|
||||||
|
Location(begin.location, returnType->location), attributes, generics, genericPacks, paramTypes, paramNames, returnType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto [endLocation, returnTypeList] = parseReturnType_DEPRECATED();
|
||||||
|
|
||||||
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
||||||
return allocator.alloc<AstTypeFunction>(
|
return allocator.alloc<AstTypeFunction>(
|
||||||
Location(begin.location, endLocation), attributes, generics, genericPacks, paramTypes, paramNames, returnTypeList
|
Location(begin.location, endLocation), attributes, generics, genericPacks, paramTypes, paramNames, returnTypeList
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isTypeFollow(Lexeme::Type c)
|
static bool isTypeFollow(Lexeme::Type c)
|
||||||
|
@ -2693,17 +3100,8 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
|
||||||
{
|
|
||||||
if (parts.size() == 1 && !isUnion && !isIntersection)
|
if (parts.size() == 1 && !isUnion && !isIntersection)
|
||||||
return parts[0];
|
return parts[0];
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (parts.size() == 1)
|
|
||||||
return parts[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUnion && isIntersection)
|
if (isUnion && isIntersection)
|
||||||
{
|
{
|
||||||
return reportTypeError(
|
return reportTypeError(
|
||||||
|
@ -3536,7 +3934,10 @@ AstExpr* Parser::parseSimpleExpr()
|
||||||
Lexeme matchFunction = lexer.current();
|
Lexeme matchFunction = lexer.current();
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
return parseFunctionBody(false, matchFunction, AstName(), nullptr, attributes).first;
|
return parseFunctionBody(false, matchFunction, AstName(), nullptr, attributes).first;
|
||||||
|
else
|
||||||
|
return parseFunctionBody_DEPRECATED(false, matchFunction, AstName(), nullptr, attributes).first;
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::Number)
|
else if (lexer.current().type == Lexeme::Number)
|
||||||
{
|
{
|
||||||
|
@ -4666,7 +5067,7 @@ void Parser::report(const Location& location, const char* format, va_list args)
|
||||||
|
|
||||||
parseErrors.emplace_back(location, message);
|
parseErrors.emplace_back(location, message);
|
||||||
|
|
||||||
if (parseErrors.size() >= unsigned(FInt::LuauParseErrorLimit) && (!FFlag::ParserNoErrorLimit || !options.noErrorLimit))
|
if (parseErrors.size() >= unsigned(FInt::LuauParseErrorLimit) && !options.noErrorLimit)
|
||||||
ParseError::raise(location, "Reached error limit (%d)", int(FInt::LuauParseErrorLimit));
|
ParseError::raise(location, "Reached error limit (%d)", int(FInt::LuauParseErrorLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer,
|
||||||
return write(getFileContents(req->absPath, "/.luaurc"), buffer, buffer_size, size_out);
|
return write(getFileContents(req->absPath, "/.luaurc"), buffer, buffer_size, size_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load(lua_State* L, void* ctx, const char* chunkname, const char* contents)
|
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents)
|
||||||
{
|
{
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -4317,6 +4319,8 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
||||||
mainFlags |= LPF_NATIVE_FUNCTION;
|
mainFlags |= LPF_NATIVE_FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
AstExprFunction main(
|
AstExprFunction main(
|
||||||
root->location,
|
root->location,
|
||||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||||
|
@ -4328,7 +4332,8 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
||||||
/* varargLocation= */ Luau::Location(),
|
/* varargLocation= */ Luau::Location(),
|
||||||
root,
|
root,
|
||||||
/* functionDepth= */ 0,
|
/* functionDepth= */ 0,
|
||||||
/* debugname= */ AstName()
|
/* debugname= */ AstName(),
|
||||||
|
/* returnAnnotation= */ nullptr
|
||||||
);
|
);
|
||||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||||
|
|
||||||
|
@ -4337,6 +4342,31 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
||||||
|
|
||||||
bytecode.setMainFunction(mainid);
|
bytecode.setMainFunction(mainid);
|
||||||
bytecode.finalize();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions)
|
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/Config.h"
|
#include "Luau/Config.h"
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
|
@ -107,7 +107,7 @@ struct luarequire_Configuration
|
||||||
|
|
||||||
// Executes the module and places the result on the stack. Returns the
|
// Executes the module and places the result on the stack. Returns the
|
||||||
// number of results placed on the stack.
|
// number of results placed on the stack.
|
||||||
int (*load)(lua_State* L, void* ctx, const char* chunkname, const char* contents);
|
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Populates function pointers in the given luarequire_Configuration.
|
// Populates function pointers in the given luarequire_Configuration.
|
||||||
|
@ -115,7 +115,18 @@ typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);
|
||||||
|
|
||||||
// Initializes and pushes the require closure onto the stack without
|
// Initializes and pushes the require closure onto the stack without
|
||||||
// registration.
|
// registration.
|
||||||
LUALIB_API int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
LUALIB_API int luarequire_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||||
|
|
||||||
// Initializes the require library and registers it globally.
|
// Initializes the require library and registers it globally.
|
||||||
LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||||
|
|
||||||
|
// Initializes and pushes a "proxyrequire" closure onto the stack. This function
|
||||||
|
// takes two parameters: the string path to resolve and the chunkname of an
|
||||||
|
// existing module. The path is resolved as if it were being required from the
|
||||||
|
// module that the chunkname represents.
|
||||||
|
LUALIB_API int luarequire_pushproxyrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||||
|
|
||||||
|
// Registers an aliased require path to a result. After registration, the given
|
||||||
|
// result will always be immediately returned when the given path is required.
|
||||||
|
// Expects the path and table to be passed as arguments on the stack.
|
||||||
|
LUALIB_API int luarequire_registermodule(lua_State* L);
|
||||||
|
|
|
@ -35,7 +35,13 @@ static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
||||||
luaL_error(L, "require configuration is missing required function pointer: load");
|
luaL_error(L, "require configuration is missing required function pointer: load");
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
static int pushrequireclosureinternal(
|
||||||
|
lua_State* L,
|
||||||
|
luarequire_Configuration_init config_init,
|
||||||
|
void* ctx,
|
||||||
|
lua_CFunction requirelikefunc,
|
||||||
|
const char* debugname
|
||||||
|
)
|
||||||
{
|
{
|
||||||
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
||||||
if (!config)
|
if (!config)
|
||||||
|
@ -46,13 +52,28 @@ int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, voi
|
||||||
|
|
||||||
lua_pushlightuserdata(L, ctx);
|
lua_pushlightuserdata(L, ctx);
|
||||||
|
|
||||||
// "require" captures config and ctx as upvalues
|
// require-like closure captures config and ctx as upvalues
|
||||||
lua_pushcclosure(L, Luau::Require::lua_require, "require", 2);
|
lua_pushcclosure(L, requirelikefunc, debugname, 2);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luarequire_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||||
|
{
|
||||||
|
return pushrequireclosureinternal(L, config_init, ctx, Luau::Require::lua_require, "require");
|
||||||
|
}
|
||||||
|
|
||||||
void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||||
{
|
{
|
||||||
lua_pushrequire(L, config_init, ctx);
|
luarequire_pushrequire(L, config_init, ctx);
|
||||||
lua_setglobal(L, "require");
|
lua_setglobal(L, "require");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luarequire_pushproxyrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||||
|
{
|
||||||
|
return pushrequireclosureinternal(L, config_init, ctx, Luau::Require::lua_proxyrequire, "proxyrequire");
|
||||||
|
}
|
||||||
|
|
||||||
|
int luarequire_registermodule(lua_State* L)
|
||||||
|
{
|
||||||
|
return Luau::Require::registerModuleImpl(L);
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,11 @@
|
||||||
namespace Luau::Require
|
namespace Luau::Require
|
||||||
{
|
{
|
||||||
|
|
||||||
static const char* cacheTableKey = "_MODULES";
|
// Stores explicitly registered modules.
|
||||||
|
static const char* registeredCacheTableKey = "_REGISTEREDMODULES";
|
||||||
|
|
||||||
|
// Stores the results of require calls.
|
||||||
|
static const char* requiredCacheTableKey = "_MODULES";
|
||||||
|
|
||||||
struct ResolvedRequire
|
struct ResolvedRequire
|
||||||
{
|
{
|
||||||
|
@ -32,7 +36,7 @@ struct ResolvedRequire
|
||||||
|
|
||||||
static bool isCached(lua_State* L, const std::string& key)
|
static bool isCached(lua_State* L, const std::string& key)
|
||||||
{
|
{
|
||||||
luaL_findtable(L, LUA_REGISTRYINDEX, cacheTableKey, 1);
|
luaL_findtable(L, LUA_REGISTRYINDEX, requiredCacheTableKey, 1);
|
||||||
lua_getfield(L, -1, key.c_str());
|
lua_getfield(L, -1, key.c_str());
|
||||||
bool cached = !lua_isnil(L, -1);
|
bool cached = !lua_isnil(L, -1);
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
|
@ -40,15 +44,12 @@ static bool isCached(lua_State* L, const std::string& key)
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, std::string path)
|
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, const char* requirerChunkname, std::string path)
|
||||||
{
|
{
|
||||||
lua_Debug ar;
|
if (!lrc->is_require_allowed(L, ctx, requirerChunkname))
|
||||||
lua_getinfo(L, 1, "s", &ar);
|
|
||||||
|
|
||||||
if (!lrc->is_require_allowed(L, ctx, ar.source))
|
|
||||||
luaL_error(L, "require is not supported in this context");
|
luaL_error(L, "require is not supported in this context");
|
||||||
|
|
||||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, ar.source};
|
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
||||||
RuntimeErrorHandler errorHandler{L}; // Errors reported directly to lua_State.
|
RuntimeErrorHandler errorHandler{L}; // Errors reported directly to lua_State.
|
||||||
|
|
||||||
Navigator navigator(navigationContext, errorHandler);
|
Navigator navigator(navigationContext, errorHandler);
|
||||||
|
@ -74,7 +75,7 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
||||||
if (isCached(L, *cacheKey))
|
if (isCached(L, *cacheKey))
|
||||||
{
|
{
|
||||||
// Put cached result on top of stack before returning.
|
// Put cached result on top of stack before returning.
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
lua_getfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||||
lua_getfield(L, -1, cacheKey->c_str());
|
lua_getfield(L, -1, cacheKey->c_str());
|
||||||
lua_remove(L, -2);
|
lua_remove(L, -2);
|
||||||
|
|
||||||
|
@ -103,7 +104,21 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_require(lua_State* L)
|
static int checkRegisteredModules(lua_State* L, const char* path)
|
||||||
|
{
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
|
||||||
|
lua_getfield(L, -1, path);
|
||||||
|
if (lua_isnil(L, -1))
|
||||||
|
{
|
||||||
|
lua_pop(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_remove(L, -2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
{
|
{
|
||||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||||
if (!lrc)
|
if (!lrc)
|
||||||
|
@ -113,11 +128,14 @@ int lua_require(lua_State* L)
|
||||||
|
|
||||||
const char* path = luaL_checkstring(L, 1);
|
const char* path = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, path);
|
if (checkRegisteredModules(L, path) == 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
||||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
int numResults = lrc->load(L, ctx, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
||||||
if (numResults > 1)
|
if (numResults > 1)
|
||||||
luaL_error(L, "module must return a single value");
|
luaL_error(L, "module must return a single value");
|
||||||
|
|
||||||
|
@ -127,7 +145,7 @@ int lua_require(lua_State* L)
|
||||||
// Initial stack state
|
// Initial stack state
|
||||||
// (-1) result
|
// (-1) result
|
||||||
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
lua_getfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||||
// (-2) result, (-1) cache table
|
// (-2) result, (-1) cache table
|
||||||
|
|
||||||
lua_pushvalue(L, -2);
|
lua_pushvalue(L, -2);
|
||||||
|
@ -143,4 +161,42 @@ int lua_require(lua_State* L)
|
||||||
return numResults;
|
return numResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lua_proxyrequire(lua_State* L)
|
||||||
|
{
|
||||||
|
const char* requirerChunkname = luaL_checkstring(L, 2);
|
||||||
|
return lua_requireinternal(L, requirerChunkname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_require(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_Debug ar;
|
||||||
|
lua_getinfo(L, 1, "s", &ar);
|
||||||
|
return lua_requireinternal(L, ar.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
int registerModuleImpl(lua_State* L)
|
||||||
|
{
|
||||||
|
if (lua_gettop(L) != 2)
|
||||||
|
luaL_error(L, "expected 2 arguments: aliased require path and desired result");
|
||||||
|
|
||||||
|
size_t len;
|
||||||
|
const char* path = luaL_checklstring(L, 1, &len);
|
||||||
|
std::string_view pathView(path, len);
|
||||||
|
if (pathView.empty() || pathView[0] != '@')
|
||||||
|
luaL_argerrorL(L, 1, "path must begin with '@'");
|
||||||
|
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
|
||||||
|
// (1) path, (2) result, (3) cache table
|
||||||
|
|
||||||
|
lua_insert(L, 1);
|
||||||
|
// (1) cache table, (2) path, (3) result
|
||||||
|
|
||||||
|
lua_settable(L, 1);
|
||||||
|
// (1) cache table
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -7,5 +7,8 @@ namespace Luau::Require
|
||||||
{
|
{
|
||||||
|
|
||||||
int lua_require(lua_State* L);
|
int lua_require(lua_State* L);
|
||||||
|
int lua_proxyrequire(lua_State* L);
|
||||||
|
|
||||||
|
int registerModuleImpl(lua_State* L);
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -179,6 +179,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/Clone.h
|
Analysis/include/Luau/Clone.h
|
||||||
Analysis/include/Luau/Constraint.h
|
Analysis/include/Luau/Constraint.h
|
||||||
Analysis/include/Luau/ConstraintGenerator.h
|
Analysis/include/Luau/ConstraintGenerator.h
|
||||||
|
Analysis/include/Luau/ConstraintSet.h
|
||||||
Analysis/include/Luau/ConstraintSolver.h
|
Analysis/include/Luau/ConstraintSolver.h
|
||||||
Analysis/include/Luau/ControlFlow.h
|
Analysis/include/Luau/ControlFlow.h
|
||||||
Analysis/include/Luau/DataFlowGraph.h
|
Analysis/include/Luau/DataFlowGraph.h
|
||||||
|
|
|
@ -58,6 +58,9 @@ LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname,
|
||||||
|
|
||||||
LUALIB_API const char* luaL_typename(lua_State* L, int idx);
|
LUALIB_API const char* luaL_typename(lua_State* L, int idx);
|
||||||
|
|
||||||
|
// wrapper for making calls from yieldable C functions
|
||||||
|
LUALIB_API int luaL_callyieldable(lua_State* L, int nargs, int nresults);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** ===============================================================
|
** ===============================================================
|
||||||
** some useful macros
|
** some useful macros
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLibWhereErrorAutoreserve)
|
LUAU_FASTFLAGVARIABLE(LuauLibWhereErrorAutoreserve)
|
||||||
|
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||||
|
|
||||||
// convert a stack index to positive
|
// convert a stack index to positive
|
||||||
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||||
|
@ -355,6 +356,22 @@ const char* luaL_typename(lua_State* L, int idx)
|
||||||
return obj ? luaT_objtypename(L, obj) : "no value";
|
return obj ? luaT_objtypename(L, obj) : "no value";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luaL_callyieldable(lua_State* L, int nargs, int nresults)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauYieldableContinuations);
|
||||||
|
|
||||||
|
api_check(L, iscfunction(L->ci->func));
|
||||||
|
Closure* cl = clvalue(L->ci->func);
|
||||||
|
api_check(L, cl->c.cont);
|
||||||
|
|
||||||
|
lua_call(L, nargs, nresults);
|
||||||
|
|
||||||
|
if (L->status == LUA_YIELD || L->status == LUA_BREAK)
|
||||||
|
return -1; // -1 is a marker for yielding from C
|
||||||
|
|
||||||
|
return cl->c.cont(L, LUA_OK);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** {======================================================
|
** {======================================================
|
||||||
** Generic Buffer manipulation
|
** Generic Buffer manipulation
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||||
|
|
||||||
static void writestring(const char* s, size_t l)
|
static void writestring(const char* s, size_t l)
|
||||||
{
|
{
|
||||||
fwrite(s, 1, l, stdout);
|
fwrite(s, 1, l, stdout);
|
||||||
|
@ -294,10 +296,18 @@ static int luaB_pcally(lua_State* L)
|
||||||
// any errors from this point on are handled by continuation
|
// any errors from this point on are handled by continuation
|
||||||
L->ci->flags |= LUA_CALLINFO_HANDLE;
|
L->ci->flags |= LUA_CALLINFO_HANDLE;
|
||||||
|
|
||||||
|
if (!FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
||||||
L->baseCcalls++;
|
L->baseCcalls++;
|
||||||
|
}
|
||||||
|
|
||||||
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), 0);
|
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), 0);
|
||||||
|
|
||||||
|
if (!FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
L->baseCcalls--;
|
L->baseCcalls--;
|
||||||
|
}
|
||||||
|
|
||||||
// necessary to accomodate functions that return lots of values
|
// necessary to accomodate functions that return lots of values
|
||||||
expandstacklimit(L, L->top);
|
expandstacklimit(L, L->top);
|
||||||
|
@ -348,12 +358,20 @@ static int luaB_xpcally(lua_State* L)
|
||||||
StkId errf = L->base;
|
StkId errf = L->base;
|
||||||
StkId func = L->base + 1;
|
StkId func = L->base + 1;
|
||||||
|
|
||||||
|
if (!FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
||||||
L->baseCcalls++;
|
L->baseCcalls++;
|
||||||
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), savestack(L, errf));
|
}
|
||||||
L->baseCcalls--;
|
|
||||||
|
|
||||||
// necessary to accomodate functions that return lots of values
|
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), savestack(L, errf));
|
||||||
|
|
||||||
|
if (!FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
|
L->baseCcalls--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// necessary to accommodate functions that return lots of values
|
||||||
expandstacklimit(L, L->top);
|
expandstacklimit(L, L->top);
|
||||||
|
|
||||||
// yielding means we need to propagate yield; resume will call continuation function later
|
// yielding means we need to propagate yield; resume will call continuation function later
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauYieldableContinuations)
|
||||||
|
|
||||||
// keep max stack allocation request under 1GB
|
// keep max stack allocation request under 1GB
|
||||||
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
||||||
|
|
||||||
|
@ -260,6 +262,60 @@ void luaD_call(lua_State* L, StkId func, int nresults)
|
||||||
if (++L->nCcalls >= LUAI_MAXCCALLS)
|
if (++L->nCcalls >= LUAI_MAXCCALLS)
|
||||||
luaD_checkCstack(L);
|
luaD_checkCstack(L);
|
||||||
|
|
||||||
|
if (FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
|
// when called from a yieldable C function, maintain yieldable invariant (baseCcalls <= nCcalls)
|
||||||
|
bool fromyieldableccall = false;
|
||||||
|
|
||||||
|
if (L->ci != L->base_ci)
|
||||||
|
{
|
||||||
|
Closure* ccl = clvalue(L->ci->func);
|
||||||
|
|
||||||
|
if (ccl->isC && ccl->c.cont)
|
||||||
|
{
|
||||||
|
fromyieldableccall = true;
|
||||||
|
L->baseCcalls++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t funcoffset = savestack(L, func);
|
||||||
|
ptrdiff_t cioffset = saveci(L, L->ci);
|
||||||
|
|
||||||
|
if (luau_precall(L, func, nresults) == PCRLUA)
|
||||||
|
{ // is a Lua function?
|
||||||
|
L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame
|
||||||
|
|
||||||
|
bool oldactive = L->isactive;
|
||||||
|
L->isactive = true;
|
||||||
|
luaC_threadbarrier(L);
|
||||||
|
|
||||||
|
luau_execute(L); // call it
|
||||||
|
|
||||||
|
if (!oldactive)
|
||||||
|
L->isactive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool yielded = L->status == LUA_YIELD || L->status == LUA_BREAK;
|
||||||
|
|
||||||
|
if (fromyieldableccall)
|
||||||
|
{
|
||||||
|
// restore original yieldable invariant
|
||||||
|
// in case of an error, this would either be restored by luaD_pcall or the thread would no longer be resumable
|
||||||
|
L->baseCcalls--;
|
||||||
|
|
||||||
|
// on yield, we have to set the CallInfo top of the C function including slots for expected results, to restore later
|
||||||
|
if (yielded)
|
||||||
|
{
|
||||||
|
CallInfo* callerci = restoreci(L, cioffset);
|
||||||
|
callerci->top = restorestack(L, funcoffset) + (nresults != LUA_MULTRET ? nresults : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nresults != LUA_MULTRET && !yielded)
|
||||||
|
L->top = restorestack(L, funcoffset) + nresults;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ptrdiff_t old_func = savestack(L, func);
|
ptrdiff_t old_func = savestack(L, func);
|
||||||
|
|
||||||
if (luau_precall(L, func, nresults) == PCRLUA)
|
if (luau_precall(L, func, nresults) == PCRLUA)
|
||||||
|
@ -278,6 +334,7 @@ void luaD_call(lua_State* L, StkId func, int nresults)
|
||||||
|
|
||||||
if (nresults != LUA_MULTRET)
|
if (nresults != LUA_MULTRET)
|
||||||
L->top = restorestack(L, old_func) + nresults;
|
L->top = restorestack(L, old_func) + nresults;
|
||||||
|
}
|
||||||
|
|
||||||
L->nCcalls--;
|
L->nCcalls--;
|
||||||
luaC_checkGC(L);
|
luaC_checkGC(L);
|
||||||
|
@ -323,9 +380,18 @@ static void resume_continue(lua_State* L)
|
||||||
// C continuation; we expect this to be followed by Lua continuations
|
// C continuation; we expect this to be followed by Lua continuations
|
||||||
int n = cl->c.cont(L, 0);
|
int n = cl->c.cont(L, 0);
|
||||||
|
|
||||||
|
if (FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
|
// continuation can break or yield again
|
||||||
|
if (L->status == LUA_BREAK || L->status == LUA_YIELD)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Continuation can break again
|
// Continuation can break again
|
||||||
if (L->status == LUA_BREAK)
|
if (L->status == LUA_BREAK)
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
luau_poscall(L, L->top - n);
|
luau_poscall(L, L->top - n);
|
||||||
}
|
}
|
||||||
|
@ -370,6 +436,11 @@ static void resume(lua_State* L, void* ud)
|
||||||
// finish interrupted execution of `OP_CALL'
|
// finish interrupted execution of `OP_CALL'
|
||||||
luau_poscall(L, firstArg);
|
luau_poscall(L, firstArg);
|
||||||
}
|
}
|
||||||
|
else if (FFlag::LuauYieldableContinuations)
|
||||||
|
{
|
||||||
|
// restore arguments we have protected for C continuation
|
||||||
|
L->base = L->ci->base;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -576,6 +647,7 @@ static void restore_stack_limit(lua_State* L)
|
||||||
int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t ef)
|
int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t ef)
|
||||||
{
|
{
|
||||||
unsigned short oldnCcalls = L->nCcalls;
|
unsigned short oldnCcalls = L->nCcalls;
|
||||||
|
unsigned short oldbaseCcalls = FFlag::LuauYieldableContinuations ? L->baseCcalls : 0;
|
||||||
ptrdiff_t old_ci = saveci(L, L->ci);
|
ptrdiff_t old_ci = saveci(L, L->ci);
|
||||||
bool oldactive = L->isactive;
|
bool oldactive = L->isactive;
|
||||||
int status = luaD_rawrunprotected(L, func, u);
|
int status = luaD_rawrunprotected(L, func, u);
|
||||||
|
@ -612,6 +684,9 @@ int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t e
|
||||||
// restore nCcalls before calling the debugprotectederror callback which may rely on the proper value to have been restored.
|
// restore nCcalls before calling the debugprotectederror callback which may rely on the proper value to have been restored.
|
||||||
L->nCcalls = oldnCcalls;
|
L->nCcalls = oldnCcalls;
|
||||||
|
|
||||||
|
if (FFlag::LuauYieldableContinuations)
|
||||||
|
L->baseCcalls = oldbaseCcalls;
|
||||||
|
|
||||||
// an error occurred, check if we have a protected error callback
|
// an error occurred, check if we have a protected error callback
|
||||||
if (yieldable && L->global->cb.debugprotectederror)
|
if (yieldable && L->global->cb.debugprotectederror)
|
||||||
{
|
{
|
||||||
|
|
|
@ -124,8 +124,8 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
// Vector3 stub
|
// Vector3 stub
|
||||||
TypeId vector3MetaType = arena.addType(TableType{});
|
TypeId vector3MetaType = arena.addType(TableType{});
|
||||||
|
|
||||||
TypeId vector3InstanceType = arena.addType(ClassType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test", {}});
|
TypeId vector3InstanceType = arena.addType(ExternType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector3InstanceType)->props = {
|
getMutable<ExternType>(vector3InstanceType)->props = {
|
||||||
{"X", {builtinTypes.numberType}},
|
{"X", {builtinTypes.numberType}},
|
||||||
{"Y", {builtinTypes.numberType}},
|
{"Y", {builtinTypes.numberType}},
|
||||||
{"Z", {builtinTypes.numberType}},
|
{"Z", {builtinTypes.numberType}},
|
||||||
|
@ -139,16 +139,16 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
||||||
|
|
||||||
// Instance stub
|
// Instance stub
|
||||||
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
TypeId instanceType = arena.addType(ExternType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(instanceType)->props = {
|
getMutable<ExternType>(instanceType)->props = {
|
||||||
{"Name", {builtinTypes.stringType}},
|
{"Name", {builtinTypes.stringType}},
|
||||||
};
|
};
|
||||||
|
|
||||||
globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
||||||
|
|
||||||
// Part stub
|
// Part stub
|
||||||
TypeId partType = arena.addType(ClassType{"Part", {}, instanceType, nullopt, {}, {}, "Test", {}});
|
TypeId partType = arena.addType(ExternType{"Part", {}, instanceType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(partType)->props = {
|
getMutable<ExternType>(partType)->props = {
|
||||||
{"Position", {vector3InstanceType}},
|
{"Position", {vector3InstanceType}},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ static const std::string kTypes[] = {
|
||||||
"vector",
|
"vector",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::string kClasses[] = {
|
static const std::string kExternTypes[] = {
|
||||||
"Vector3",
|
"Vector3",
|
||||||
"Instance",
|
"Instance",
|
||||||
"Part",
|
"Part",
|
||||||
|
@ -902,8 +902,8 @@ struct ProtoToLuau
|
||||||
|
|
||||||
void print(const luau::TypeClass& type)
|
void print(const luau::TypeClass& type)
|
||||||
{
|
{
|
||||||
size_t index = size_t(type.kind()) % std::size(kClasses);
|
size_t index = size_t(type.kind()) % std::size(kExternTypes);
|
||||||
source += kClasses[index];
|
source += kExternTypes[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(const luau::TypeRef& type)
|
void print(const luau::TypeRef& type)
|
||||||
|
|
|
@ -13,6 +13,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||||
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
struct JsonEncoderFixture
|
struct JsonEncoderFixture
|
||||||
{
|
{
|
||||||
|
@ -420,20 +421,39 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
|
||||||
{
|
{
|
||||||
AstStat* statement = expectParseStatement("declare function foo(x: number): string");
|
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 =
|
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":[]})";
|
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);
|
CHECK(toJson(statement) == expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
||||||
{
|
{
|
||||||
AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string");
|
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":[]})";
|
||||||
|
|
||||||
|
CHECK(toJson(statement) == expected);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::string_view expected =
|
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":[]})";
|
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")
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr")
|
||||||
|
@ -463,9 +483,18 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
|
||||||
|
|
||||||
REQUIRE(2 == root->body.size);
|
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 =
|
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})";
|
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);
|
CHECK(toJson(root->body.data[0]) == expected1);
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view expected2 =
|
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})";
|
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})";
|
||||||
|
@ -476,7 +505,19 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
||||||
{
|
{
|
||||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||||
|
|
||||||
if (FFlag::LuauAstTypeGroup3)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauAstTypeGroup3)
|
||||||
|
{
|
||||||
|
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 if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"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":"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":"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 if (FFlag::LuauAstTypeGroup3)
|
||||||
{
|
{
|
||||||
std::string_view 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":"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})";
|
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})";
|
||||||
|
@ -516,10 +557,18 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction")
|
||||||
{
|
{
|
||||||
AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())");
|
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 =
|
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})";
|
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);
|
CHECK(toJson(statement) == expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError")
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError")
|
||||||
|
|
|
@ -20,14 +20,13 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +158,7 @@ struct ACBuiltinsFixture : ACFixtureImpl<BuiltinsFixture>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ACClassFixture : ACFixtureImpl<ClassFixture>
|
struct ACExternTypeFixture : ACFixtureImpl<ExternTypeFixture>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3754,7 +3753,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback")
|
||||||
bool isCorrect = false;
|
bool isCorrect = false;
|
||||||
auto ac1 = autocomplete(
|
auto ac1 = autocomplete(
|
||||||
'1',
|
'1',
|
||||||
[&isCorrect](std::string, std::optional<const ClassType*>, std::optional<std::string> contents) -> std::optional<AutocompleteEntryMap>
|
[&isCorrect](std::string, std::optional<const ExternType*>, std::optional<std::string> contents) -> std::optional<AutocompleteEntryMap>
|
||||||
{
|
{
|
||||||
isCorrect = contents && *contents == "testing/";
|
isCorrect = contents && *contents == "testing/";
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -3766,8 +3765,6 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "require_by_string")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "require_by_string")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauExposeRequireByStringAutocomplete, true};
|
|
||||||
|
|
||||||
fileResolver.source["MainModule"] = R"(
|
fileResolver.source["MainModule"] = R"(
|
||||||
local info = "MainModule serves as the root directory"
|
local info = "MainModule serves as the root directory"
|
||||||
)";
|
)";
|
||||||
|
@ -3960,7 +3957,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes")
|
||||||
local x = require(@1"@2"@3)
|
local x = require(@1"@2"@3)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
StringCompletionCallback callback = [](std::string, std::optional<const ClassType*>, std::optional<std::string> contents
|
StringCompletionCallback callback = [](std::string, std::optional<const ExternType*>, std::optional<std::string> contents
|
||||||
) -> std::optional<AutocompleteEntryMap>
|
) -> std::optional<AutocompleteEntryMap>
|
||||||
{
|
{
|
||||||
Luau::AutocompleteEntryMap results = {{"test", Luau::AutocompleteEntry{Luau::AutocompleteEntryKind::String, std::nullopt, false, false}}};
|
Luau::AutocompleteEntryMap results = {{"test", Luau::AutocompleteEntry{Luau::AutocompleteEntryKind::String, std::nullopt, false, false}}};
|
||||||
|
@ -4432,7 +4429,7 @@ local x = 1 + result.
|
||||||
CHECK(ac.entryMap.count("x"));
|
CHECK(ac.entryMap.count("x"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACClassFixture, "ac_dont_overflow_on_recursive_union")
|
TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauAutocompleteUnionCopyPreviousSeen, true};
|
ScopedFastFlag _{FFlag::LuauAutocompleteUnionCopyPreviousSeen, true};
|
||||||
check(R"(
|
check(R"(
|
||||||
|
|
|
@ -28,9 +28,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "lib_documentation_symbols")
|
||||||
{
|
{
|
||||||
props = &ttv->props;
|
props = &ttv->props;
|
||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(binding.typeId))
|
else if (const ExternType* etv = get<ExternType>(binding.typeId))
|
||||||
{
|
{
|
||||||
props = &ctv->props;
|
props = &etv->props;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props)
|
if (props)
|
||||||
|
|
|
@ -9,7 +9,7 @@ using std::nullopt;
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
ClassFixture::ClassFixture(bool prepareAutocomplete)
|
ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
||||||
: BuiltinsFixture(prepareAutocomplete)
|
: BuiltinsFixture(prepareAutocomplete)
|
||||||
{
|
{
|
||||||
GlobalTypes& globals = frontend.globals;
|
GlobalTypes& globals = frontend.globals;
|
||||||
|
@ -19,22 +19,22 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
|
|
||||||
unfreeze(arena);
|
unfreeze(arena);
|
||||||
|
|
||||||
TypeId connectionType = arena.addType(ClassType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection", {}});
|
TypeId connectionType = arena.addType(ExternType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection", {}});
|
||||||
|
|
||||||
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
TypeId baseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(baseClassInstanceType)->props = {
|
getMutable<ExternType>(baseClassInstanceType)->props = {
|
||||||
{"BaseMethod", Property::readonly(makeFunction(arena, baseClassInstanceType, {numberType}, {}))},
|
{"BaseMethod", Property::readonly(makeFunction(arena, baseClassInstanceType, {numberType}, {}))},
|
||||||
{"BaseField", {numberType}},
|
{"BaseField", {numberType}},
|
||||||
|
|
||||||
{"Touched", Property::readonly(connectionType)},
|
{"Touched", Property::readonly(connectionType)},
|
||||||
};
|
};
|
||||||
|
|
||||||
getMutable<ClassType>(connectionType)->props = {
|
getMutable<ExternType>(connectionType)->props = {
|
||||||
{"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}
|
{"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId baseClassType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
TypeId baseClassType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(baseClassType)->props = {
|
getMutable<ExternType>(baseClassType)->props = {
|
||||||
{"StaticMethod", {makeFunction(arena, nullopt, {}, {numberType})}},
|
{"StaticMethod", {makeFunction(arena, nullopt, {}, {numberType})}},
|
||||||
{"Clone", {makeFunction(arena, nullopt, {baseClassInstanceType}, {baseClassInstanceType})}},
|
{"Clone", {makeFunction(arena, nullopt, {baseClassInstanceType}, {baseClassInstanceType})}},
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {baseClassInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {baseClassInstanceType})}},
|
||||||
|
@ -42,49 +42,49 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||||
addGlobalBinding(globals, "BaseClass", baseClassType, "@test");
|
addGlobalBinding(globals, "BaseClass", baseClassType, "@test");
|
||||||
|
|
||||||
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
TypeId childClassInstanceType = arena.addType(ExternType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(childClassInstanceType)->props = {
|
getMutable<ExternType>(childClassInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, childClassInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, childClassInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId childClassType = arena.addType(ClassType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
TypeId childClassType = arena.addType(ExternType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(childClassType)->props = {
|
getMutable<ExternType>(childClassType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
||||||
addGlobalBinding(globals, "ChildClass", childClassType, "@test");
|
addGlobalBinding(globals, "ChildClass", childClassType, "@test");
|
||||||
|
|
||||||
TypeId grandChildInstanceType = arena.addType(ClassType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test", {}});
|
TypeId grandChildInstanceType = arena.addType(ExternType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(grandChildInstanceType)->props = {
|
getMutable<ExternType>(grandChildInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, grandChildInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, grandChildInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId grandChildType = arena.addType(ClassType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
TypeId grandChildType = arena.addType(ExternType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(grandChildType)->props = {
|
getMutable<ExternType>(grandChildType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
globals.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
||||||
addGlobalBinding(globals, "GrandChild", childClassType, "@test");
|
addGlobalBinding(globals, "GrandChild", childClassType, "@test");
|
||||||
|
|
||||||
TypeId anotherChildInstanceType = arena.addType(ClassType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
TypeId anotherChildInstanceType = arena.addType(ExternType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(anotherChildInstanceType)->props = {
|
getMutable<ExternType>(anotherChildInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, anotherChildInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, anotherChildInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
TypeId anotherChildType = arena.addType(ExternType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(anotherChildType)->props = {
|
getMutable<ExternType>(anotherChildType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
globals.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
||||||
addGlobalBinding(globals, "AnotherChild", childClassType, "@test");
|
addGlobalBinding(globals, "AnotherChild", childClassType, "@test");
|
||||||
|
|
||||||
TypeId unrelatedClassInstanceType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
TypeId unrelatedClassInstanceType = arena.addType(ExternType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
TypeId unrelatedClassType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
TypeId unrelatedClassType = arena.addType(ExternType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(unrelatedClassType)->props = {
|
getMutable<ExternType>(unrelatedClassType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {unrelatedClassInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {unrelatedClassInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["UnrelatedClass"] = TypeFun{{}, unrelatedClassInstanceType};
|
globals.globalScope->exportedTypeBindings["UnrelatedClass"] = TypeFun{{}, unrelatedClassInstanceType};
|
||||||
|
@ -92,14 +92,14 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
|
|
||||||
TypeId vector2MetaType = arena.addType(TableType{});
|
TypeId vector2MetaType = arena.addType(TableType{});
|
||||||
|
|
||||||
vector2InstanceType = arena.addType(ClassType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test", {}});
|
vector2InstanceType = arena.addType(ExternType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector2InstanceType)->props = {
|
getMutable<ExternType>(vector2InstanceType)->props = {
|
||||||
{"X", {numberType}},
|
{"X", {numberType}},
|
||||||
{"Y", {numberType}},
|
{"Y", {numberType}},
|
||||||
};
|
};
|
||||||
|
|
||||||
vector2Type = arena.addType(ClassType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
vector2Type = arena.addType(ExternType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector2Type)->props = {
|
getMutable<ExternType>(vector2Type)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {numberType, numberType}, {vector2InstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {numberType, numberType}, {vector2InstanceType})}},
|
||||||
};
|
};
|
||||||
getMutable<TableType>(vector2MetaType)->props = {
|
getMutable<TableType>(vector2MetaType)->props = {
|
||||||
|
@ -114,7 +114,7 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
addGlobalBinding(globals, "Vector2", vector2Type, "@test");
|
addGlobalBinding(globals, "Vector2", vector2Type, "@test");
|
||||||
|
|
||||||
TypeId callableClassMetaType = arena.addType(TableType{});
|
TypeId callableClassMetaType = arena.addType(TableType{});
|
||||||
TypeId callableClassType = arena.addType(ClassType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test", {}});
|
TypeId callableClassType = arena.addType(ExternType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test", {}});
|
||||||
getMutable<TableType>(callableClassMetaType)->props = {
|
getMutable<TableType>(callableClassMetaType)->props = {
|
||||||
{"__call", {makeFunction(arena, nullopt, {callableClassType, stringType}, {numberType})}},
|
{"__call", {makeFunction(arena, nullopt, {callableClassType, stringType}, {numberType})}},
|
||||||
};
|
};
|
||||||
|
@ -124,7 +124,7 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
{
|
{
|
||||||
TypeId indexableClassMetaType = arena.addType(TableType{});
|
TypeId indexableClassMetaType = arena.addType(TableType{});
|
||||||
TypeId indexableClassType =
|
TypeId indexableClassType =
|
||||||
arena.addType(ClassType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", {}, TableIndexer{keyType, returnType}});
|
arena.addType(ExternType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", {}, TableIndexer{keyType, returnType}});
|
||||||
globals.globalScope->exportedTypeBindings[className] = TypeFun{{}, indexableClassType};
|
globals.globalScope->exportedTypeBindings[className] = TypeFun{{}, indexableClassType};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -134,9 +134,9 @@ ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||||
addIndexableClass("IndexableNumericKeyClass", numberType, numberType);
|
addIndexableClass("IndexableNumericKeyClass", numberType, numberType);
|
||||||
|
|
||||||
// Add a confusing derived class which shares the same name internally, but has a unique alias
|
// Add a confusing derived class which shares the same name internally, but has a unique alias
|
||||||
TypeId duplicateBaseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
TypeId duplicateBaseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(duplicateBaseClassInstanceType)->props = {
|
getMutable<ExternType>(duplicateBaseClassInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, duplicateBaseClassInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, duplicateBaseClassInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct ClassFixture : BuiltinsFixture
|
struct ExternTypeFixture : BuiltinsFixture
|
||||||
{
|
{
|
||||||
explicit ClassFixture(bool prepareAutocomplete = false);
|
explicit ExternTypeFixture(bool prepareAutocomplete = false);
|
||||||
|
|
||||||
TypeId vector2Type;
|
TypeId vector2Type;
|
||||||
TypeId vector2InstanceType;
|
TypeId vector2InstanceType;
|
||||||
|
|
|
@ -35,6 +35,7 @@ LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||||
|
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
{
|
{
|
||||||
|
@ -823,6 +824,236 @@ TEST_CASE("Pack")
|
||||||
runConformance("tpack.luau");
|
runConformance("tpack.luau");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int singleYield(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, 2);
|
||||||
|
|
||||||
|
return lua_yield(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int singleYieldContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, 4);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int multipleYields(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_settop(L, 1); // Only 1 argument expected
|
||||||
|
int base = luaL_checkinteger(L, 1);
|
||||||
|
|
||||||
|
luaL_checkstack(L, 2, "cmultiyield");
|
||||||
|
|
||||||
|
// current state
|
||||||
|
int pos = 1;
|
||||||
|
lua_pushinteger(L, pos);
|
||||||
|
|
||||||
|
// return value
|
||||||
|
lua_pushinteger(L, base + pos);
|
||||||
|
return lua_yield(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int multipleYieldsContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
// function arguments are still alive
|
||||||
|
int base = luaL_checkinteger(L, 1);
|
||||||
|
|
||||||
|
// function state is still alive
|
||||||
|
int pos = luaL_checkinteger(L, 2) + 1;
|
||||||
|
luaL_checkstack(L, 1, "cmultiyieldcont");
|
||||||
|
lua_pushinteger(L, pos);
|
||||||
|
lua_replace(L, 2);
|
||||||
|
|
||||||
|
luaL_checkstack(L, 1, "cmultiyieldcont");
|
||||||
|
|
||||||
|
if (pos < 4)
|
||||||
|
{
|
||||||
|
lua_pushinteger(L, base + pos);
|
||||||
|
return lua_yield(L, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lua_pushinteger(L, base + pos);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nestedMultipleYieldHelper(lua_State* L)
|
||||||
|
{
|
||||||
|
int context = luaL_checkinteger(L, lua_upvalueindex(1));
|
||||||
|
|
||||||
|
lua_pushinteger(L, 100 + context);
|
||||||
|
return lua_yield(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nestedMultipleYieldHelperContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
int context = luaL_checkinteger(L, lua_upvalueindex(1));
|
||||||
|
lua_pushinteger(L, 110 + context);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nestedMultipleYieldHelperNonYielding(lua_State* L)
|
||||||
|
{
|
||||||
|
int context = luaL_checkinteger(L, lua_upvalueindex(1));
|
||||||
|
lua_pushinteger(L, 105 + context);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int multipleYieldsWithNestedCall(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_settop(L, 2); // Only 2 arguments expected
|
||||||
|
bool nestedShouldYield = luaL_checkboolean(L, 2);
|
||||||
|
|
||||||
|
lua_pushinteger(L, 0); // state
|
||||||
|
|
||||||
|
lua_pushnumber(L, 5);
|
||||||
|
if (nestedShouldYield)
|
||||||
|
lua_pushcclosurek(L, nestedMultipleYieldHelper, nullptr, 1, nestedMultipleYieldHelperContinuation);
|
||||||
|
else
|
||||||
|
lua_pushcclosurek(L, nestedMultipleYieldHelperNonYielding, nullptr, 1, nullptr);
|
||||||
|
|
||||||
|
return luaL_callyieldable(L, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int multipleYieldsWithNestedCallContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
int state = luaL_checkinteger(L, 3);
|
||||||
|
luaL_checkstack(L, 1, "cnestedmultiyieldcont");
|
||||||
|
lua_pushinteger(L, state + 1);
|
||||||
|
lua_replace(L, 3);
|
||||||
|
|
||||||
|
if (state == 0)
|
||||||
|
{
|
||||||
|
return lua_yield(L, lua_gettop(L) - 3);
|
||||||
|
}
|
||||||
|
else if (state == 1)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, luaL_checkinteger(L, 1) + 200);
|
||||||
|
return lua_yield(L, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, luaL_checkinteger(L, 1) + 210);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCall(lua_State* L)
|
||||||
|
{
|
||||||
|
luaL_checkstack(L, 3, "cpass");
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_pushvalue(L, 3);
|
||||||
|
return luaL_callyieldable(L, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(lua_gettop(L) == 4); // 3 original arguments and the return value
|
||||||
|
LUAU_ASSERT(lua_tonumber(L, -1) == 0.5);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallMoreResults(lua_State* L)
|
||||||
|
{
|
||||||
|
luaL_checkstack(L, 3, "cpass");
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_pushvalue(L, 3);
|
||||||
|
return luaL_callyieldable(L, 2, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallMoreResultsContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(lua_gettop(L) == 13); // 3 original arguments and 10 requested return values
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(lua_isnil(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
LUAU_ASSERT(lua_tonumber(L, -1) == 0.5);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallArgReuse(lua_State* L)
|
||||||
|
{
|
||||||
|
return luaL_callyieldable(L, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallArgReuseContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(lua_gettop(L) == 1); // Original arguments were consumed, only return remains
|
||||||
|
LUAU_ASSERT(lua_tonumber(L, -1) == 0.5);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallVaradic(lua_State* L)
|
||||||
|
{
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
return luaL_callyieldable(L, lua_gettop(L) - 1, LUA_MULTRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallVaradicContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
return lua_gettop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallWithState(lua_State* L)
|
||||||
|
{
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
int args = lua_gettop(L) - 1;
|
||||||
|
|
||||||
|
lua_pushnumber(L, 42);
|
||||||
|
lua_insert(L, 1);
|
||||||
|
|
||||||
|
return luaL_callyieldable(L, args, LUA_MULTRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
int passthroughCallWithStateContinuation(lua_State* L, int status)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(luaL_checkinteger(L, 1) == 42);
|
||||||
|
|
||||||
|
return lua_gettop(L) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CYield")
|
||||||
|
{
|
||||||
|
ScopedFastFlag luauYieldableContinuations{FFlag::LuauYieldableContinuations, true};
|
||||||
|
|
||||||
|
runConformance(
|
||||||
|
"cyield.luau",
|
||||||
|
[](lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushcclosurek(L, singleYield, "singleYield", 0, singleYieldContinuation);
|
||||||
|
lua_setglobal(L, "singleYield");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, multipleYields, "multipleYields", 0, multipleYieldsContinuation);
|
||||||
|
lua_setglobal(L, "multipleYields");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, multipleYieldsWithNestedCall, "multipleYieldsWithNestedCall", 0, multipleYieldsWithNestedCallContinuation);
|
||||||
|
lua_setglobal(L, "multipleYieldsWithNestedCall");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, passthroughCall, "passthroughCall", 0, passthroughCallContinuation);
|
||||||
|
lua_setglobal(L, "passthroughCall");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, passthroughCallMoreResults, "passthroughCallMoreResults", 0, passthroughCallMoreResultsContinuation);
|
||||||
|
lua_setglobal(L, "passthroughCallMoreResults");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, passthroughCallArgReuse, "passthroughCallArgReuse", 0, passthroughCallArgReuseContinuation);
|
||||||
|
lua_setglobal(L, "passthroughCallArgReuse");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, passthroughCallVaradic, "passthroughCallVaradic", 0, passthroughCallVaradicContinuation);
|
||||||
|
lua_setglobal(L, "passthroughCallVaradic");
|
||||||
|
|
||||||
|
lua_pushcclosurek(L, passthroughCallWithState, "passthroughCallWithState", 0, passthroughCallWithStateContinuation);
|
||||||
|
lua_setglobal(L, "passthroughCallWithState");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Vector")
|
TEST_CASE("Vector")
|
||||||
{
|
{
|
||||||
lua_CompileOptions copts = defaultOptions();
|
lua_CompileOptions copts = defaultOptions();
|
||||||
|
@ -957,7 +1188,7 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
|
|
||||||
lua_pushstring(L, "function");
|
lua_pushstring(L, "function");
|
||||||
}
|
}
|
||||||
else if (auto c = Luau::get<Luau::ClassType>(type))
|
else if (auto c = Luau::get<Luau::ExternType>(type))
|
||||||
{
|
{
|
||||||
lua_pushstring(L, c->name.c_str());
|
lua_pushstring(L, c->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1583,7 +1583,7 @@ TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_metamissing_right")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DifferFixtureGeneric<ClassFixture>, "equal_class")
|
TEST_CASE_FIXTURE(DifferFixtureGeneric<ExternTypeFixture>, "equal_class")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local foo = BaseClass
|
local foo = BaseClass
|
||||||
|
@ -1594,7 +1594,7 @@ TEST_CASE_FIXTURE(DifferFixtureGeneric<ClassFixture>, "equal_class")
|
||||||
compareTypesEq("foo", "almostFoo");
|
compareTypesEq("foo", "almostFoo");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DifferFixtureGeneric<ClassFixture>, "class_normal")
|
TEST_CASE_FIXTURE(DifferFixtureGeneric<ExternTypeFixture>, "class_normal")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local foo = BaseClass
|
local foo = BaseClass
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct ESFixture : Fixture
|
||||||
ESFixture()
|
ESFixture()
|
||||||
: simplifier(newSimplifier(arena, builtinTypes))
|
: simplifier(newSimplifier(arena, builtinTypes))
|
||||||
{
|
{
|
||||||
createSomeClasses(&frontend);
|
createSomeExternTypes(&frontend);
|
||||||
|
|
||||||
ScopePtr moduleScope = frontend.globals.globalScope;
|
ScopePtr moduleScope = frontend.globals.globalScope;
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | functio
|
||||||
builtinTypes->threadType,
|
builtinTypes->threadType,
|
||||||
builtinTypes->functionType,
|
builtinTypes->functionType,
|
||||||
builtinTypes->tableType,
|
builtinTypes->tableType,
|
||||||
builtinTypes->classType,
|
builtinTypes->externType,
|
||||||
builtinTypes->bufferType,
|
builtinTypes->bufferType,
|
||||||
}}))
|
}}))
|
||||||
);
|
);
|
||||||
|
@ -262,12 +262,12 @@ TEST_CASE_FIXTURE(ESFixture, "Child | Parent")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "class | Child")
|
TEST_CASE_FIXTURE(ESFixture, "class | Child")
|
||||||
{
|
{
|
||||||
CHECK("class" == simplifyStr(arena->addType(UnionType{{builtinTypes->classType, childClass}})));
|
CHECK("class" == simplifyStr(arena->addType(UnionType{{builtinTypes->externType, childClass}})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Parent | class | Child")
|
TEST_CASE_FIXTURE(ESFixture, "Parent | class | Child")
|
||||||
{
|
{
|
||||||
CHECK("class" == simplifyStr(arena->addType(UnionType{{parentClass, builtinTypes->classType, childClass}})));
|
CHECK("class" == simplifyStr(arena->addType(UnionType{{parentClass, builtinTypes->externType, childClass}})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
|
TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
|
||||||
|
@ -311,7 +311,7 @@ TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | funct
|
||||||
builtinTypes->threadType,
|
builtinTypes->threadType,
|
||||||
builtinTypes->functionType,
|
builtinTypes->functionType,
|
||||||
builtinTypes->tableType,
|
builtinTypes->tableType,
|
||||||
builtinTypes->classType,
|
builtinTypes->externType,
|
||||||
builtinTypes->bufferType,
|
builtinTypes->bufferType,
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
|
|
@ -847,14 +847,14 @@ void registerHiddenTypes(Frontend* frontend)
|
||||||
globalScope->exportedTypeBindings["Not"] = TypeFun{{genericT}, globals.globalTypes.addType(NegationType{t})};
|
globalScope->exportedTypeBindings["Not"] = TypeFun{{genericT}, globals.globalTypes.addType(NegationType{t})};
|
||||||
globalScope->exportedTypeBindings["Mt"] = TypeFun{{genericT, genericU}, globals.globalTypes.addType(MetatableType{t, u})};
|
globalScope->exportedTypeBindings["Mt"] = TypeFun{{genericT, genericU}, globals.globalTypes.addType(MetatableType{t, u})};
|
||||||
globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend->builtinTypes->functionType};
|
globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend->builtinTypes->functionType};
|
||||||
globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend->builtinTypes->classType};
|
globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend->builtinTypes->externType};
|
||||||
globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend->builtinTypes->errorType};
|
globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend->builtinTypes->errorType};
|
||||||
globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend->builtinTypes->tableType};
|
globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend->builtinTypes->tableType};
|
||||||
|
|
||||||
freeze(globals.globalTypes);
|
freeze(globals.globalTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createSomeClasses(Frontend* frontend)
|
void createSomeExternTypes(Frontend* frontend)
|
||||||
{
|
{
|
||||||
GlobalTypes& globals = frontend->globals;
|
GlobalTypes& globals = frontend->globals;
|
||||||
|
|
||||||
|
@ -863,27 +863,27 @@ void createSomeClasses(Frontend* frontend)
|
||||||
|
|
||||||
ScopePtr moduleScope = globals.globalScope;
|
ScopePtr moduleScope = globals.globalScope;
|
||||||
|
|
||||||
TypeId parentType = arena.addType(ClassType{"Parent", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test", {}});
|
TypeId parentType = arena.addType(ExternType{"Parent", {}, frontend->builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
ClassType* parentClass = getMutable<ClassType>(parentType);
|
ExternType* parentExternType = getMutable<ExternType>(parentType);
|
||||||
parentClass->props["method"] = {makeFunction(arena, parentType, {}, {})};
|
parentExternType->props["method"] = {makeFunction(arena, parentType, {}, {})};
|
||||||
|
|
||||||
parentClass->props["virtual_method"] = {makeFunction(arena, parentType, {}, {})};
|
parentExternType->props["virtual_method"] = {makeFunction(arena, parentType, {}, {})};
|
||||||
|
|
||||||
addGlobalBinding(globals, "Parent", {parentType});
|
addGlobalBinding(globals, "Parent", {parentType});
|
||||||
moduleScope->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
moduleScope->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
||||||
|
|
||||||
TypeId childType = arena.addType(ClassType{"Child", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
TypeId childType = arena.addType(ExternType{"Child", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "Child", {childType});
|
addGlobalBinding(globals, "Child", {childType});
|
||||||
moduleScope->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
moduleScope->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
||||||
|
|
||||||
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
TypeId anotherChildType = arena.addType(ExternType{"AnotherChild", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "AnotherChild", {anotherChildType});
|
addGlobalBinding(globals, "AnotherChild", {anotherChildType});
|
||||||
moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildType};
|
moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildType};
|
||||||
|
|
||||||
TypeId unrelatedType = arena.addType(ClassType{"Unrelated", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test", {}});
|
TypeId unrelatedType = arena.addType(ExternType{"Unrelated", {}, frontend->builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "Unrelated", {unrelatedType});
|
addGlobalBinding(globals, "Unrelated", {unrelatedType});
|
||||||
moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
||||||
|
|
|
@ -213,7 +213,7 @@ std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name); // Wa
|
||||||
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
|
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
|
||||||
|
|
||||||
void registerHiddenTypes(Frontend* frontend);
|
void registerHiddenTypes(Frontend* frontend);
|
||||||
void createSomeClasses(Frontend* frontend);
|
void createSomeExternTypes(Frontend* frontend);
|
||||||
|
|
||||||
template<typename E>
|
template<typename E>
|
||||||
const E* findError(const CheckResult& result)
|
const E* findError(const CheckResult& result)
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
|
||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauCloneIncrementalModule)
|
LUAU_FASTFLAG(LuauCloneIncrementalModule)
|
||||||
|
|
||||||
|
@ -34,7 +33,6 @@ LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
|
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
LUAU_FASTFLAG(LuauBetterCursorInCommentDetection)
|
LUAU_FASTFLAG(LuauBetterCursorInCommentDetection)
|
||||||
LUAU_FASTFLAG(LuauAllFreeTypesHaveScopes)
|
LUAU_FASTFLAG(LuauAllFreeTypesHaveScopes)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
||||||
|
@ -46,7 +44,7 @@ LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +72,6 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
||||||
{
|
{
|
||||||
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
|
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
|
||||||
|
|
||||||
ScopedFastFlag luauAutocompleteRefactorsForIncrementalAutocomplete{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true};
|
|
||||||
ScopedFastFlag luauFreeTypesMustHaveBounds{FFlag::LuauFreeTypesMustHaveBounds, true};
|
ScopedFastFlag luauFreeTypesMustHaveBounds{FFlag::LuauFreeTypesMustHaveBounds, true};
|
||||||
ScopedFastFlag luauCloneIncrementalModule{FFlag::LuauCloneIncrementalModule, true};
|
ScopedFastFlag luauCloneIncrementalModule{FFlag::LuauCloneIncrementalModule, true};
|
||||||
ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true};
|
ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true};
|
||||||
|
@ -3027,7 +3024,6 @@ z:a
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "interior_free_types_assertion_caused_by_free_type_inheriting_null_scope_from_table")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "interior_free_types_assertion_caused_by_free_type_inheriting_null_scope_from_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauTrackInteriorFreeTypesOnScope, true};
|
|
||||||
const std::string source = R"(--!strict
|
const std::string source = R"(--!strict
|
||||||
local foo
|
local foo
|
||||||
local a = foo()
|
local a = foo()
|
||||||
|
|
|
@ -17,7 +17,6 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("Generalization");
|
TEST_SUITE_BEGIN("Generalization");
|
||||||
|
@ -116,12 +115,12 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "dont_traverse_into_class_types_when_ge
|
||||||
{
|
{
|
||||||
auto [propTy, _] = freshType();
|
auto [propTy, _] = freshType();
|
||||||
|
|
||||||
TypeId cursedClass = arena.addType(ClassType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, "", {}});
|
TypeId cursedExternType = arena.addType(ExternType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, "", {}});
|
||||||
|
|
||||||
auto genClass = generalize(cursedClass);
|
auto genExternType = generalize(cursedExternType);
|
||||||
REQUIRE(genClass);
|
REQUIRE(genExternType);
|
||||||
|
|
||||||
auto genPropTy = get<ClassType>(*genClass)->props.at("oh_no").readTy;
|
auto genPropTy = get<ExternType>(*genExternType)->props.at("oh_no").readTy;
|
||||||
CHECK(is<FreeType>(*genPropTy));
|
CHECK(is<FreeType>(*genPropTy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +343,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::DebugLuauForbidInternalTypes, true},
|
{FFlag::DebugLuauForbidInternalTypes, true},
|
||||||
{FFlag::LuauTrackInteriorFreeTypesOnScope, true},
|
|
||||||
{FFlag::LuauTrackInferredFunctionTypeFromCall, true}
|
{FFlag::LuauTrackInferredFunctionTypeFromCall, true}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1253,15 +1253,19 @@ _ = {
|
||||||
[2] = 2,
|
[2] = 2,
|
||||||
[1] = 3,
|
[1] = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _foo(): { first: number, second: string, first: boolean }
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
REQUIRE(6 == result.warnings.size());
|
REQUIRE(7 == result.warnings.size());
|
||||||
CHECK_EQ(result.warnings[0].text, "Table field 'first' is a duplicate; previously defined at line 3");
|
CHECK_EQ(result.warnings[0].text, "Table field 'first' is a duplicate; previously defined at line 3");
|
||||||
CHECK_EQ(result.warnings[1].text, "Table field 'first' is a duplicate; previously defined at line 9");
|
CHECK_EQ(result.warnings[1].text, "Table field 'first' is a duplicate; previously defined at line 9");
|
||||||
CHECK_EQ(result.warnings[2].text, "Table index 1 is a duplicate; previously defined as a list entry");
|
CHECK_EQ(result.warnings[2].text, "Table index 1 is a duplicate; previously defined as a list entry");
|
||||||
CHECK_EQ(result.warnings[3].text, "Table index 3 is a duplicate; previously defined as a list entry");
|
CHECK_EQ(result.warnings[3].text, "Table index 3 is a duplicate; previously defined as a list entry");
|
||||||
CHECK_EQ(result.warnings[4].text, "Table type field 'first' is a duplicate; previously defined at line 24");
|
CHECK_EQ(result.warnings[4].text, "Table type field 'first' is a duplicate; previously defined at line 24");
|
||||||
CHECK_EQ(result.warnings[5].text, "Table index 1 is a duplicate; previously defined at line 36");
|
CHECK_EQ(result.warnings[5].text, "Table index 1 is a duplicate; previously defined at line 36");
|
||||||
|
CHECK_EQ(result.warnings[6].text, "Table type field 'first' is a duplicate; previously defined at line 41");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "read_write_table_props")
|
TEST_CASE_FIXTURE(Fixture, "read_write_table_props")
|
||||||
|
@ -1300,6 +1304,19 @@ TEST_CASE_FIXTURE(Fixture, "ImportOnlyUsedInTypeAnnotation")
|
||||||
CHECK_EQ(result.warnings[0].text, "Variable 'x' is never used; prefix with '_' to silence");
|
CHECK_EQ(result.warnings[0].text, "Variable 'x' is never used; prefix with '_' to silence");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "ImportOnlyUsedInReturnType")
|
||||||
|
{
|
||||||
|
LintResult result = lint(R"(
|
||||||
|
local Foo = require(script.Parent.Foo)
|
||||||
|
|
||||||
|
function foo(): Foo.Y
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(1 == result.warnings.size());
|
||||||
|
CHECK_EQ(result.warnings[0].text, "Function 'foo' is never used; prefix with '_' to silence");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "DisableUnknownGlobalWithTypeChecking")
|
TEST_CASE_FIXTURE(Fixture, "DisableUnknownGlobalWithTypeChecking")
|
||||||
{
|
{
|
||||||
LintResult result = lint(R"(
|
LintResult result = lint(R"(
|
||||||
|
@ -1505,11 +1522,11 @@ TEST_CASE_FIXTURE(Fixture, "LintHygieneUAF")
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped")
|
||||||
{
|
{
|
||||||
unfreeze(frontend.globals.globalTypes);
|
unfreeze(frontend.globals.globalTypes);
|
||||||
TypeId instanceType = frontend.globals.globalTypes.addType(ClassType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}});
|
TypeId instanceType = frontend.globals.globalTypes.addType(ExternType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}});
|
||||||
persist(instanceType);
|
persist(instanceType);
|
||||||
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
||||||
|
|
||||||
getMutable<ClassType>(instanceType)->props = {
|
getMutable<ExternType>(instanceType)->props = {
|
||||||
{"Name", {builtinTypes->stringType}},
|
{"Name", {builtinTypes->stringType}},
|
||||||
{"DataCost", {builtinTypes->numberType, /* deprecated= */ true}},
|
{"DataCost", {builtinTypes->numberType, /* deprecated= */ true}},
|
||||||
{"Wait", {builtinTypes->anyType, /* deprecated= */ true}},
|
{"Wait", {builtinTypes->anyType, /* deprecated= */ true}},
|
||||||
|
|
|
@ -237,7 +237,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_intersection")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "clone_class")
|
TEST_CASE_FIXTURE(Fixture, "clone_class")
|
||||||
{
|
{
|
||||||
Type exampleMetaClass{ClassType{
|
Type exampleMetaClass{ExternType{
|
||||||
"ExampleClassMeta",
|
"ExampleClassMeta",
|
||||||
{
|
{
|
||||||
{"__add", {builtinTypes->anyType}},
|
{"__add", {builtinTypes->anyType}},
|
||||||
|
@ -249,7 +249,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
|
||||||
"Test",
|
"Test",
|
||||||
{}
|
{}
|
||||||
}};
|
}};
|
||||||
Type exampleClass{ClassType{
|
Type exampleClass{ExternType{
|
||||||
"ExampleClass",
|
"ExampleClass",
|
||||||
{
|
{
|
||||||
{"PropOne", {builtinTypes->numberType}},
|
{"PropOne", {builtinTypes->numberType}},
|
||||||
|
@ -267,14 +267,14 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
|
||||||
CloneState cloneState{builtinTypes};
|
CloneState cloneState{builtinTypes};
|
||||||
|
|
||||||
TypeId cloned = clone(&exampleClass, dest, cloneState);
|
TypeId cloned = clone(&exampleClass, dest, cloneState);
|
||||||
const ClassType* ctv = get<ClassType>(cloned);
|
const ExternType* etv = get<ExternType>(cloned);
|
||||||
REQUIRE(ctv != nullptr);
|
REQUIRE(etv != nullptr);
|
||||||
|
|
||||||
REQUIRE(ctv->metatable);
|
REQUIRE(etv->metatable);
|
||||||
const ClassType* metatable = get<ClassType>(*ctv->metatable);
|
const ExternType* metatable = get<ExternType>(*etv->metatable);
|
||||||
REQUIRE(metatable);
|
REQUIRE(metatable);
|
||||||
|
|
||||||
CHECK_EQ("ExampleClass", ctv->name);
|
CHECK_EQ("ExampleClass", etv->name);
|
||||||
CHECK_EQ("ExampleClassMeta", metatable->name);
|
CHECK_EQ("ExampleClassMeta", metatable->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue