mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Compare commits
No commits in common. "master" and "0.670" have entirely different histories.
142 changed files with 1993 additions and 5390 deletions
|
@ -57,7 +57,7 @@ struct AutocompleteEntry
|
|||
// Set if this suggestion matches the type expected in the context
|
||||
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
|
||||
|
||||
std::optional<const ExternType*> containingExternType = std::nullopt;
|
||||
std::optional<const ClassType*> containingClass = std::nullopt;
|
||||
std::optional<const Property*> prop = std::nullopt;
|
||||
std::optional<std::string> documentationSymbol = std::nullopt;
|
||||
Tags tags;
|
||||
|
@ -85,7 +85,7 @@ struct AutocompleteResult
|
|||
};
|
||||
|
||||
using StringCompletionCallback =
|
||||
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ExternType*> ctx, std::optional<std::string> contents)>;
|
||||
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ClassType*> ctx, std::optional<std::string> contents)>;
|
||||
|
||||
constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/ConstraintSet.h"
|
||||
#include "Luau/ControlFlow.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
|
@ -92,8 +91,9 @@ struct ConstraintGenerator
|
|||
// Constraints that go straight to the solver.
|
||||
std::vector<ConstraintPtr> constraints;
|
||||
|
||||
// The set of all free types introduced during constraint generation.
|
||||
DenseHashSet<TypeId> freeTypes{nullptr};
|
||||
// Constraints that do not go to the solver right away. Other constraints
|
||||
// will enqueue them during solving.
|
||||
std::vector<ConstraintPtr> unqueuedConstraints;
|
||||
|
||||
// Map a function's signature scope back to its signature type.
|
||||
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||
|
@ -151,9 +151,6 @@ struct ConstraintGenerator
|
|||
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
|
||||
* of scopes, constraints, and free types that can be solved later.
|
||||
|
@ -272,7 +269,7 @@ private:
|
|||
ControlFlow visit(const ScopePtr& scope, AstStatTypeAlias* alias);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatTypeFunction* function);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareExternType* declareExternType);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareClass* declareClass);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
|
||||
ControlFlow visit(const ScopePtr& scope, AstStatError* error);
|
||||
|
||||
|
@ -484,4 +481,9 @@ private:
|
|||
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
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// 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,7 +3,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/ConstraintSet.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
|
@ -88,7 +87,6 @@ struct ConstraintSolver
|
|||
NotNull<Simplifier> simplifier;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
// The entire set of constraints that the solver is trying to resolve.
|
||||
ConstraintSet constraintSet;
|
||||
std::vector<NotNull<Constraint>> constraints;
|
||||
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
|
||||
NotNull<Scope> rootScope;
|
||||
|
@ -142,19 +140,6 @@ struct ConstraintSolver
|
|||
|
||||
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(
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<Simplifier> simplifier,
|
||||
|
@ -189,9 +174,6 @@ struct ConstraintSolver
|
|||
bool isDone() const;
|
||||
|
||||
private:
|
||||
/// A helper that does most of the setup work that is shared between the two constructors.
|
||||
void initFreeTypeTracking();
|
||||
|
||||
void generalizeOneType(TypeId ty);
|
||||
|
||||
/**
|
||||
|
@ -450,10 +432,6 @@ public:
|
|||
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);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -173,7 +173,7 @@ private:
|
|||
ControlFlow visit(AstStatTypeFunction* f);
|
||||
ControlFlow visit(AstStatDeclareGlobal* d);
|
||||
ControlFlow visit(AstStatDeclareFunction* d);
|
||||
ControlFlow visit(AstStatDeclareExternType* d);
|
||||
ControlFlow visit(AstStatDeclareClass* d);
|
||||
ControlFlow visit(AstStatError* error);
|
||||
|
||||
DataFlowResult visitExpr(AstExpr* e);
|
||||
|
|
|
@ -332,11 +332,11 @@ struct TypePackMismatch
|
|||
bool operator==(const TypePackMismatch& rhs) const;
|
||||
};
|
||||
|
||||
struct DynamicPropertyLookupOnExternTypesUnsafe
|
||||
struct DynamicPropertyLookupOnClassesUnsafe
|
||||
{
|
||||
TypeId ty;
|
||||
|
||||
bool operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const;
|
||||
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
|
||||
};
|
||||
|
||||
struct UninhabitedTypeFunction
|
||||
|
@ -499,7 +499,7 @@ using TypeErrorData = Variant<
|
|||
TypesAreUnrelated,
|
||||
NormalizationTooComplex,
|
||||
TypePackMismatch,
|
||||
DynamicPropertyLookupOnExternTypesUnsafe,
|
||||
DynamicPropertyLookupOnClassesUnsafe,
|
||||
UninhabitedTypeFunction,
|
||||
UninhabitedTypePackFunction,
|
||||
WhereClauseNeeded,
|
||||
|
|
|
@ -117,7 +117,8 @@ struct FileResolver
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||
// Make non-virtual when removing FFlagLuauImproveRequireByStringAutocomplete.
|
||||
virtual std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||
|
||||
std::shared_ptr<RequireSuggester> requireSuggester;
|
||||
};
|
||||
|
|
|
@ -133,9 +133,9 @@ struct GenericTypeFinder : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const Luau::ExternType&) override
|
||||
bool visit(TypeId ty, const Luau::ClassType&) override
|
||||
{
|
||||
// During function instantiation, extern types are not traversed even if they have generics
|
||||
// During function instantiation, classes are not traversed even if they have generics
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -181,7 +181,7 @@ struct NormalizedStringType
|
|||
|
||||
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
|
||||
|
||||
struct NormalizedExternType
|
||||
struct NormalizedClassType
|
||||
{
|
||||
/** Has the following structure:
|
||||
*
|
||||
|
@ -192,7 +192,7 @@ struct NormalizedExternType
|
|||
*
|
||||
* Each TypeId is a class type.
|
||||
*/
|
||||
std::unordered_map<TypeId, TypeIds> externTypes;
|
||||
std::unordered_map<TypeId, TypeIds> classes;
|
||||
|
||||
/**
|
||||
* 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
|
||||
// * P is a union of primitive types (including singletons, extern types and the error type)
|
||||
// * P is a union of primitive types (including singletons, classes and the error type)
|
||||
// * T is a union of table 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
|
||||
|
@ -260,7 +260,7 @@ struct NormalizedType
|
|||
// This type is either never, boolean type, or a boolean singleton.
|
||||
TypeId booleans;
|
||||
|
||||
NormalizedExternType externTypes;
|
||||
NormalizedClassType classes;
|
||||
|
||||
// The error part of the 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)
|
||||
bool hasTops() const;
|
||||
bool hasBooleans() const;
|
||||
bool hasExternTypes() const;
|
||||
bool hasClasses() const;
|
||||
bool hasErrors() const;
|
||||
bool hasNils() const;
|
||||
bool hasNumbers() const;
|
||||
|
@ -391,10 +391,10 @@ public:
|
|||
void unionTysWithTy(TypeIds& here, TypeId there);
|
||||
TypeId unionOfTops(TypeId here, TypeId there);
|
||||
TypeId unionOfBools(TypeId here, TypeId there);
|
||||
void unionExternTypesWithExternType(TypeIds& heres, TypeId there);
|
||||
void unionExternTypes(TypeIds& heres, const TypeIds& theres);
|
||||
void unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
|
||||
void unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
|
||||
void unionClassesWithClass(TypeIds& heres, TypeId there);
|
||||
void unionClasses(TypeIds& heres, const TypeIds& theres);
|
||||
void unionClassesWithClass(NormalizedClassType& heres, TypeId there);
|
||||
void unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
||||
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
|
||||
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
|
||||
|
@ -423,8 +423,8 @@ public:
|
|||
// ------- Normalizing intersections
|
||||
TypeId intersectionOfTops(TypeId here, TypeId there);
|
||||
TypeId intersectionOfBools(TypeId here, TypeId there);
|
||||
void intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
|
||||
void intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
|
||||
void intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
||||
void intersectClassesWithClass(NormalizedClassType& heres, TypeId there);
|
||||
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
|
||||
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);
|
||||
|
||||
// TODO: This is eerily similar to the pattern that NormalizedExternType
|
||||
// TODO: This is eerily similar to the pattern that NormalizedClassType
|
||||
// implements. We could, and perhaps should, merge them together.
|
||||
template<typename K, typename V>
|
||||
struct OrderedMap
|
||||
|
|
|
@ -22,7 +22,7 @@ struct InternalErrorReporter;
|
|||
|
||||
class TypeIds;
|
||||
class Normalizer;
|
||||
struct NormalizedExternType;
|
||||
struct NormalizedClassType;
|
||||
struct NormalizedFunctionType;
|
||||
struct NormalizedStringType;
|
||||
struct NormalizedType;
|
||||
|
@ -121,7 +121,7 @@ struct SubtypingEnvironment
|
|||
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};
|
||||
|
||||
/*
|
||||
* See the test cyclic_tables_are_assumed_to_be_compatible_with_extern_types for
|
||||
* See the test cyclic_tables_are_assumed_to_be_compatible_with_classes for
|
||||
* details.
|
||||
*
|
||||
* An empty value is equivalent to a nonexistent key.
|
||||
|
@ -229,8 +229,9 @@ private:
|
|||
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 TableType* superTable, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope);
|
||||
SubtypingResult
|
||||
isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const FunctionType* subFunction,
|
||||
|
@ -258,11 +259,11 @@ private:
|
|||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedExternType& subExternType,
|
||||
const NormalizedExternType& superExternType,
|
||||
const NormalizedClassType& subClass,
|
||||
const NormalizedClassType& superClass,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedExternType& subExternType, const TypeIds& superTables, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
|
|
|
@ -44,7 +44,6 @@ struct ToStringOptions
|
|||
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 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.
|
||||
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypes
|
||||
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
|
||||
|
|
|
@ -74,6 +74,10 @@ struct FreeType
|
|||
// This one got promoted to explicit
|
||||
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity = Polarity::Unknown);
|
||||
explicit FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||
// Old constructors
|
||||
explicit FreeType(TypeLevel level);
|
||||
explicit FreeType(Scope* scope);
|
||||
FreeType(Scope* scope, TypeLevel level);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
|
@ -287,7 +291,7 @@ struct MagicFunctionCallContext
|
|||
{
|
||||
NotNull<struct ConstraintSolver> solver;
|
||||
NotNull<const Constraint> constraint;
|
||||
NotNull<const AstExprCall> callSite;
|
||||
const class AstExprCall* callSite;
|
||||
TypePackId arguments;
|
||||
TypePackId result;
|
||||
};
|
||||
|
@ -532,15 +536,15 @@ struct ClassUserData
|
|||
virtual ~ClassUserData() {}
|
||||
};
|
||||
|
||||
/** The type of an external userdata exposed to Luau.
|
||||
/** The type of a class.
|
||||
*
|
||||
* Extern types behave like tables in many ways, but there are some important differences:
|
||||
* Classes behave like tables in many ways, but there are some important differences:
|
||||
*
|
||||
* The properties of a class are always exactly known.
|
||||
* Extern types optionally have a parent type.
|
||||
* Two different extern types that share the same properties are nevertheless distinct and mutually incompatible.
|
||||
* Classes optionally have a parent class.
|
||||
* Two different classes that share the same properties are nevertheless distinct and mutually incompatible.
|
||||
*/
|
||||
struct ExternType
|
||||
struct ClassType
|
||||
{
|
||||
using Props = TableType::Props;
|
||||
|
||||
|
@ -554,7 +558,7 @@ struct ExternType
|
|||
std::optional<Location> definitionLocation;
|
||||
std::optional<TableIndexer> indexer;
|
||||
|
||||
ExternType(
|
||||
ClassType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
|
@ -575,7 +579,7 @@ struct ExternType
|
|||
{
|
||||
}
|
||||
|
||||
ExternType(
|
||||
ClassType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
|
@ -775,7 +779,7 @@ using TypeVariant = Unifiable::Variant<
|
|||
FunctionType,
|
||||
TableType,
|
||||
MetatableType,
|
||||
ExternType,
|
||||
ClassType,
|
||||
AnyType,
|
||||
UnionType,
|
||||
IntersectionType,
|
||||
|
@ -986,7 +990,7 @@ public:
|
|||
const TypeId threadType;
|
||||
const TypeId bufferType;
|
||||
const TypeId functionType;
|
||||
const TypeId externType;
|
||||
const TypeId classType;
|
||||
const TypeId tableType;
|
||||
const TypeId emptyTableType;
|
||||
const TypeId trueType;
|
||||
|
@ -998,7 +1002,6 @@ public:
|
|||
const TypeId noRefineType;
|
||||
const TypeId falsyType;
|
||||
const TypeId truthyType;
|
||||
const TypeId notNilType;
|
||||
|
||||
const TypeId optionalNumberType;
|
||||
const TypeId optionalStringType;
|
||||
|
@ -1019,10 +1022,10 @@ TypeLevel* getMutableLevel(TypeId ty);
|
|||
|
||||
std::optional<TypeLevel> getLevel(TypePackId tp);
|
||||
|
||||
const Property* lookupExternTypeProp(const ExternType* cls, const Name& name);
|
||||
const Property* lookupClassProp(const ClassType* cls, const Name& name);
|
||||
|
||||
// Whether `cls` is a subclass of `parent`
|
||||
bool isSubclass(const ExternType* cls, const ExternType* parent);
|
||||
bool isSubclass(const ClassType* cls, const ClassType* parent);
|
||||
|
||||
Type* asMutable(TypeId ty);
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ struct TypeArena
|
|||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope);
|
||||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLevel level);
|
||||
|
||||
TypeId freshType_DEPRECATED(TypeLevel level);
|
||||
TypeId freshType_DEPRECATED(Scope* scope);
|
||||
TypeId freshType_DEPRECATED(Scope* scope, TypeLevel level);
|
||||
|
||||
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
|
||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Luau
|
|||
struct TypeRehydrationOptions
|
||||
{
|
||||
std::unordered_set<std::string> bannedNames;
|
||||
bool expandExternTypeProps = false;
|
||||
bool expandClassProps = false;
|
||||
};
|
||||
|
||||
void attachTypeData(SourceModule& source, Module& result);
|
||||
|
|
|
@ -160,7 +160,7 @@ private:
|
|||
void visit(AstTypeList types);
|
||||
void visit(AstStatDeclareFunction* stat);
|
||||
void visit(AstStatDeclareGlobal* stat);
|
||||
void visit(AstStatDeclareExternType* stat);
|
||||
void visit(AstStatDeclareClass* stat);
|
||||
void visit(AstStatError* stat);
|
||||
void visit(AstExpr* expr, ValueContext context);
|
||||
void visit(AstExprGroup* expr, ValueContext context);
|
||||
|
|
|
@ -205,7 +205,7 @@ struct TypeFunctionTableType
|
|||
std::optional<TypeFunctionTypeId> metatable;
|
||||
};
|
||||
|
||||
struct TypeFunctionExternType
|
||||
struct TypeFunctionClassType
|
||||
{
|
||||
using Name = std::string;
|
||||
using Props = std::map<Name, TypeFunctionProperty>;
|
||||
|
@ -222,7 +222,7 @@ struct TypeFunctionExternType
|
|||
std::optional<TypeFunctionTypeId> readParent;
|
||||
std::optional<TypeFunctionTypeId> writeParent;
|
||||
|
||||
TypeId externTy;
|
||||
TypeId classTy;
|
||||
};
|
||||
|
||||
struct TypeFunctionGenericType
|
||||
|
@ -244,7 +244,7 @@ using TypeFunctionTypeVariant = Luau::Variant<
|
|||
TypeFunctionNegationType,
|
||||
TypeFunctionFunctionType,
|
||||
TypeFunctionTableType,
|
||||
TypeFunctionExternType,
|
||||
TypeFunctionClassType,
|
||||
TypeFunctionGenericType>;
|
||||
|
||||
struct TypeFunctionType
|
||||
|
|
|
@ -29,7 +29,7 @@ struct SingletonType;
|
|||
struct FunctionType;
|
||||
struct TableType;
|
||||
struct MetatableType;
|
||||
struct ExternType;
|
||||
struct ClassType;
|
||||
struct AnyType;
|
||||
struct UnionType;
|
||||
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, const AstStatTypeAlias& typealias);
|
||||
ControlFlow check(const ScopePtr& scope, const AstStatTypeFunction& typefunction);
|
||||
ControlFlow check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
|
||||
ControlFlow check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
|
||||
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 AstStatDeclareExternType& declaredExternType);
|
||||
void prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
|
||||
|
||||
ControlFlow checkBlock(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.
|
||||
*/
|
||||
DenseHashSet<const AstStatDeclareExternType*> incorrectExternTypeDefinitions{nullptr};
|
||||
DenseHashSet<const AstStatDeclareClass*> incorrectClassDefinitions{nullptr};
|
||||
|
||||
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
|
||||
};
|
||||
|
|
|
@ -185,8 +185,6 @@ TypePackIterator begin(TypePackId tp);
|
|||
TypePackIterator begin(TypePackId tp, const TxnLog* log);
|
||||
TypePackIterator end(TypePackId tp);
|
||||
|
||||
TypePackId getTail(TypePackId tp);
|
||||
|
||||
using SeenSet = std::set<std::pair<const void*, const void*>>;
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
|
||||
|
|
|
@ -140,7 +140,7 @@ private:
|
|||
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false, const LiteralProperties* aliasableMap = nullptr);
|
||||
void tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyNegations(TypeId subTy, TypeId superTy);
|
||||
|
||||
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
|
||||
|
|
|
@ -126,7 +126,7 @@ struct GenericTypeVisitor
|
|||
{
|
||||
return visit(ty);
|
||||
}
|
||||
virtual bool visit(TypeId ty, const ExternType& etv)
|
||||
virtual bool visit(TypeId ty, const ClassType& ctv)
|
||||
{
|
||||
return visit(ty);
|
||||
}
|
||||
|
@ -313,11 +313,11 @@ struct GenericTypeVisitor
|
|||
traverse(mtv->metatable);
|
||||
}
|
||||
}
|
||||
else if (auto etv = get<ExternType>(ty))
|
||||
else if (auto ctv = get<ClassType>(ty))
|
||||
{
|
||||
if (visit(ty, *etv))
|
||||
if (visit(ty, *ctv))
|
||||
{
|
||||
for (const auto& [name, prop] : etv->props)
|
||||
for (const auto& [name, prop] : ctv->props)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
|
@ -335,16 +335,16 @@ struct GenericTypeVisitor
|
|||
traverse(prop.type());
|
||||
}
|
||||
|
||||
if (etv->parent)
|
||||
traverse(*etv->parent);
|
||||
if (ctv->parent)
|
||||
traverse(*ctv->parent);
|
||||
|
||||
if (etv->metatable)
|
||||
traverse(*etv->metatable);
|
||||
if (ctv->metatable)
|
||||
traverse(*ctv->metatable);
|
||||
|
||||
if (etv->indexer)
|
||||
if (ctv->indexer)
|
||||
{
|
||||
traverse(etv->indexer->indexType);
|
||||
traverse(etv->indexer->indexResultType);
|
||||
traverse(ctv->indexer->indexType);
|
||||
traverse(ctv->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +396,7 @@ struct GenericTypeVisitor
|
|||
traverse(unwrapped);
|
||||
|
||||
// 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 ExternType
|
||||
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType
|
||||
// that doesn't need to be expanded.
|
||||
}
|
||||
else if (auto stv = get<SingletonType>(ty))
|
||||
|
|
|
@ -88,7 +88,7 @@ TypePackId Anyification::clean(TypePackId tp)
|
|||
|
||||
bool Anyification::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return ty->persistent;
|
||||
|
|
|
@ -31,7 +31,7 @@ bool ApplyTypeFunction::ignoreChildren(TypeId ty)
|
|||
{
|
||||
if (get<GenericType>(ty))
|
||||
return true;
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -433,16 +431,8 @@ struct AstJsonEncoder : public AstVisitor
|
|||
if (node->self)
|
||||
PROP(self);
|
||||
PROP(args);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (node->returnAnnotation)
|
||||
PROP(returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node->returnAnnotation_DEPRECATED)
|
||||
write("returnAnnotation", node->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
PROP(vararg);
|
||||
PROP(varargLocation);
|
||||
if (node->varargAnnotation)
|
||||
|
@ -475,26 +465,26 @@ struct AstJsonEncoder : public AstVisitor
|
|||
writeRaw("}");
|
||||
}
|
||||
|
||||
void write(class AstGenericType* genericType)
|
||||
void write(const AstGenericType& genericType)
|
||||
{
|
||||
writeRaw("{");
|
||||
bool c = pushComma();
|
||||
writeType("AstGenericType");
|
||||
write("name", genericType->name);
|
||||
if (genericType->defaultValue)
|
||||
write("luauType", genericType->defaultValue);
|
||||
write("name", genericType.name);
|
||||
if (genericType.defaultValue)
|
||||
write("luauType", genericType.defaultValue);
|
||||
popComma(c);
|
||||
writeRaw("}");
|
||||
}
|
||||
|
||||
void write(class AstGenericTypePack* genericTypePack)
|
||||
void write(const AstGenericTypePack& genericTypePack)
|
||||
{
|
||||
writeRaw("{");
|
||||
bool c = pushComma();
|
||||
writeType("AstGenericTypePack");
|
||||
write("name", genericTypePack->name);
|
||||
if (genericTypePack->defaultValue)
|
||||
write("luauType", genericTypePack->defaultValue);
|
||||
write("name", genericTypePack.name);
|
||||
if (genericTypePack.defaultValue)
|
||||
write("luauType", genericTypePack.defaultValue);
|
||||
popComma(c);
|
||||
writeRaw("}");
|
||||
}
|
||||
|
@ -912,10 +902,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
PROP(paramNames);
|
||||
PROP(vararg);
|
||||
PROP(varargLocation);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
PROP(retTypes);
|
||||
else
|
||||
write("retTypes", node->retTypes_DEPRECATED);
|
||||
PROP(generics);
|
||||
PROP(genericPacks);
|
||||
}
|
||||
|
@ -936,7 +923,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
);
|
||||
}
|
||||
|
||||
void write(const AstDeclaredExternTypeProperty& prop)
|
||||
void write(const AstDeclaredClassProp& prop)
|
||||
{
|
||||
writeRaw("{");
|
||||
bool c = pushComma();
|
||||
|
@ -949,7 +936,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
writeRaw("}");
|
||||
}
|
||||
|
||||
void write(class AstStatDeclareExternType* node)
|
||||
void write(class AstStatDeclareClass* node)
|
||||
{
|
||||
writeNode(
|
||||
node,
|
||||
|
@ -1061,10 +1048,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
PROP(genericPacks);
|
||||
PROP(argTypes);
|
||||
PROP(argNames);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
PROP(returnTypes);
|
||||
else
|
||||
write("returnTypes", node->returnTypes_DEPRECATED);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1445,7 +1429,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(class AstStatDeclareExternType* node) override
|
||||
bool visit(class AstStatDeclareClass* node) override
|
||||
{
|
||||
write(node);
|
||||
return false;
|
||||
|
|
|
@ -574,11 +574,11 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(parentTy))
|
||||
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
||||
{
|
||||
while (etv)
|
||||
while (ctv)
|
||||
{
|
||||
if (auto propIt = etv->props.find(indexName->index.value); propIt != etv->props.end())
|
||||
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
|
@ -590,7 +590,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
||||
);
|
||||
}
|
||||
etv = etv->parent ? Luau::get<Luau::ExternType>(*etv->parent) : nullptr;
|
||||
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
|
||||
}
|
||||
}
|
||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
||||
|
|
|
@ -24,10 +24,14 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||
|
||||
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUnionCopyPreviousSeen)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
@ -307,7 +311,7 @@ static void autocompleteProps(
|
|||
const std::vector<AstNode*>& nodes,
|
||||
AutocompleteEntryMap& result,
|
||||
std::unordered_set<TypeId>& seen,
|
||||
std::optional<const ExternType*> containingExternType = std::nullopt
|
||||
std::optional<const ClassType*> containingClass = std::nullopt
|
||||
)
|
||||
{
|
||||
rootTy = follow(rootTy);
|
||||
|
@ -330,8 +334,8 @@ static void autocompleteProps(
|
|||
if (calledWithSelf == ftv->hasSelf)
|
||||
return true;
|
||||
|
||||
// Calls on extern types require strict match between how function is declared and how it's called
|
||||
if (get<ExternType>(rootTy))
|
||||
// Calls on classes require strict match between how function is declared and how it's called
|
||||
if (get<ClassType>(rootTy))
|
||||
return false;
|
||||
|
||||
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
|
||||
|
@ -364,7 +368,7 @@ static void autocompleteProps(
|
|||
return calledWithSelf;
|
||||
};
|
||||
|
||||
auto fillProps = [&](const ExternType::Props& props)
|
||||
auto fillProps = [&](const ClassType::Props& props)
|
||||
{
|
||||
for (const auto& [name, prop] : props)
|
||||
{
|
||||
|
@ -397,7 +401,7 @@ static void autocompleteProps(
|
|||
prop.deprecated,
|
||||
isWrongIndexer(type),
|
||||
typeCorrect,
|
||||
containingExternType,
|
||||
containingClass,
|
||||
&prop,
|
||||
prop.documentationSymbol,
|
||||
{},
|
||||
|
@ -428,12 +432,12 @@ static void autocompleteProps(
|
|||
}
|
||||
};
|
||||
|
||||
if (auto cls = get<ExternType>(ty))
|
||||
if (auto cls = get<ClassType>(ty))
|
||||
{
|
||||
containingExternType = containingExternType.value_or(cls);
|
||||
containingClass = containingClass.value_or(cls);
|
||||
fillProps(cls->props);
|
||||
if (cls->parent)
|
||||
autocompleteProps(module, typeArena, builtinTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingExternType);
|
||||
autocompleteProps(module, typeArena, builtinTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingClass);
|
||||
}
|
||||
else if (auto tbl = get<TableType>(ty))
|
||||
fillProps(tbl->props);
|
||||
|
@ -487,7 +491,7 @@ static void autocompleteProps(
|
|||
// If we don't do this, and we have the misfortune of receiving a
|
||||
// recursive union like:
|
||||
//
|
||||
// t1 where t1 = t1 | ExternType
|
||||
// t1 where t1 = t1 | Class
|
||||
//
|
||||
// Then we are on a one way journey to a stack overflow.
|
||||
if (FFlag::LuauAutocompleteUnionCopyPreviousSeen)
|
||||
|
@ -587,7 +591,7 @@ AutocompleteEntryMap autocompleteProps(
|
|||
AutocompleteEntryMap autocompleteModuleTypes(const Module& module, const ScopePtr& scopeAtPosition, Position position, std::string_view moduleName)
|
||||
{
|
||||
AutocompleteEntryMap result;
|
||||
ScopePtr startScope = scopeAtPosition;
|
||||
ScopePtr startScope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
for (ScopePtr& scope = startScope; scope; scope = scope->parent)
|
||||
{
|
||||
if (auto it = scope->importedTypeBindings.find(std::string(moduleName)); it != scope->importedTypeBindings.end())
|
||||
|
@ -699,30 +703,6 @@ static std::optional<TypeId> findTypeElementAt(const AstTypeList& astTypeList, T
|
|||
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)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
@ -743,17 +723,9 @@ static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Posi
|
|||
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
|
||||
return element;
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes_DEPRECATED, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible to walk through other types like intrsection and unions if we find value in doing that
|
||||
return {};
|
||||
|
@ -761,7 +733,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)
|
||||
{
|
||||
if (ScopePtr scope = scopeAtPosition)
|
||||
if (ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position))
|
||||
{
|
||||
for (const auto& [name, binding] : scope->bindings)
|
||||
{
|
||||
|
@ -903,7 +875,7 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
{
|
||||
AutocompleteEntryMap result;
|
||||
|
||||
ScopePtr startScope = scopeAtPosition;
|
||||
ScopePtr startScope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
|
||||
for (ScopePtr scope = startScope; scope; scope = scope->parent)
|
||||
{
|
||||
|
@ -1082,16 +1054,12 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (!node->returnAnnotation)
|
||||
return result;
|
||||
|
||||
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
|
||||
for (size_t i = 0; i < node->returnAnnotation->types.size; i++)
|
||||
{
|
||||
for (size_t i = 0; i < typePack->typeList.types.size; i++)
|
||||
{
|
||||
AstType* ret = typePack->typeList.types.data[i];
|
||||
AstType* ret = node->returnAnnotation->types.data[i];
|
||||
|
||||
if (ret->location.containsClosed(position))
|
||||
{
|
||||
|
@ -1106,7 +1074,7 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
|
||||
if (AstTypePack* retTp = typePack->typeList.tailType)
|
||||
if (AstTypePack* retTp = node->returnAnnotation->tailType)
|
||||
{
|
||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||
{
|
||||
|
@ -1121,56 +1089,6 @@ 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;
|
||||
}
|
||||
|
@ -1290,7 +1208,7 @@ static AutocompleteEntryMap autocompleteStatement(
|
|||
)
|
||||
{
|
||||
// This is inefficient. :(
|
||||
ScopePtr scope = scopeAtPosition;
|
||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
|
||||
AutocompleteEntryMap result;
|
||||
|
||||
|
@ -1468,7 +1386,7 @@ static AutocompleteContext autocompleteExpression(
|
|||
else
|
||||
{
|
||||
// This is inefficient. :(
|
||||
ScopePtr scope = scopeAtPosition;
|
||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
|
||||
while (scope)
|
||||
{
|
||||
|
@ -1537,7 +1455,7 @@ static AutocompleteResult autocompleteExpression(
|
|||
return {result, ancestry, context};
|
||||
}
|
||||
|
||||
static std::optional<const ExternType*> getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr)
|
||||
static std::optional<const ClassType*> getMethodContainingClass(const ModulePtr& module, AstExpr* funcExpr)
|
||||
{
|
||||
AstExpr* parentExpr = nullptr;
|
||||
if (auto indexName = funcExpr->as<AstExprIndexName>())
|
||||
|
@ -1561,14 +1479,14 @@ static std::optional<const ExternType*> getMethodContainingExternType(const Modu
|
|||
|
||||
Luau::TypeId parentType = Luau::follow(*parentIt);
|
||||
|
||||
if (auto parentExternType = Luau::get<ExternType>(parentType))
|
||||
if (auto parentClass = Luau::get<ClassType>(parentType))
|
||||
{
|
||||
return parentExternType;
|
||||
return parentClass;
|
||||
}
|
||||
|
||||
if (auto parentUnion = Luau::get<UnionType>(parentType))
|
||||
{
|
||||
return returnFirstNonnullOptionOfType<ExternType>(parentUnion);
|
||||
return returnFirstNonnullOptionOfType<ClassType>(parentUnion);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
@ -1626,7 +1544,10 @@ static std::optional<AutocompleteEntryMap> convertRequireSuggestionsToAutocomple
|
|||
{
|
||||
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
|
||||
entry.insertText = std::move(suggestion.fullPath);
|
||||
if (FFlag::LuauExposeRequireByStringAutocomplete)
|
||||
{
|
||||
entry.tags = std::move(suggestion.tags);
|
||||
}
|
||||
result[std::move(suggestion.label)] = std::move(entry);
|
||||
}
|
||||
return result;
|
||||
|
@ -1687,7 +1608,7 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
|
|||
{
|
||||
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
|
||||
}
|
||||
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingExternType(module, candidate->func), candidateString))
|
||||
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
@ -1855,7 +1776,7 @@ static std::optional<AutocompleteEntry> makeAnonymousAutofilled(
|
|||
if (!type)
|
||||
return std::nullopt;
|
||||
|
||||
const ScopePtr scope = scopeAtPosition;
|
||||
const ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(*module, position);
|
||||
if (!scope)
|
||||
return std::nullopt;
|
||||
|
||||
|
|
|
@ -32,9 +32,10 @@
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -343,7 +344,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
|
||||
{
|
||||
TypeId vectorTy = it->second.type;
|
||||
ExternType* vectorCls = getMutable<ExternType>(vectorTy);
|
||||
ClassType* vectorCls = getMutable<ClassType>(vectorTy);
|
||||
|
||||
vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);
|
||||
|
@ -704,14 +705,6 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
|||
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);
|
||||
const auto& [params, tail] = flatten(context.arguments);
|
||||
|
||||
|
@ -723,9 +716,7 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
|||
{
|
||||
TypeId actualTy = params[i + paramOffset];
|
||||
TypeId expectedTy = expected[i];
|
||||
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;
|
||||
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||
// use subtyping instead here
|
||||
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
||||
|
||||
|
@ -1538,6 +1529,7 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
|||
tableType->scope = context.constraint->scope.get();
|
||||
}
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(context.constraint->scope.get(), resultType);
|
||||
|
||||
TypePackId clonedTypePack = arena->addTypePack({resultType});
|
||||
|
@ -1549,6 +1541,7 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
|||
static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCallContext& context)
|
||||
{
|
||||
TypeArena* arena = context.solver->arena;
|
||||
if (FFlag::LuauFollowTableFreeze)
|
||||
inputType = follow(inputType);
|
||||
if (auto mt = get<MetatableType>(inputType))
|
||||
{
|
||||
|
|
|
@ -355,7 +355,7 @@ private:
|
|||
t->metatable = shallowClone(t->metatable);
|
||||
}
|
||||
|
||||
void cloneChildren(ExternType* t)
|
||||
void cloneChildren(ClassType* t)
|
||||
{
|
||||
for (auto& [_, p] : t->props)
|
||||
p = shallowClone(p);
|
||||
|
|
|
@ -20,7 +20,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
|||
|
||||
DenseHashSet<TypeId>* result;
|
||||
|
||||
explicit ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
||||
ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
||||
: result(result)
|
||||
{
|
||||
}
|
||||
|
@ -43,15 +43,24 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
// ExternTypes never contain free types.
|
||||
// ClassTypes never contain free types.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
||||
{
|
||||
return FFlag::DebugLuauGreedyGeneralization;
|
||||
// We do not consider reference counted types that are inside a type
|
||||
// function to be part of the reachable reference counted types.
|
||||
// Otherwise, code can be constructed in just the right way such
|
||||
// that two type functions both claim to mutate a free type, which
|
||||
// prevents either type function from trying to generalize it, so
|
||||
// we potentially get stuck.
|
||||
//
|
||||
// The default behavior here is `true` for "visit the child types"
|
||||
// of this type, hence:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,10 +130,8 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
}
|
||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||
{
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
rci.traverse(hic->subjectType);
|
||||
rci.traverse(hic->resultType);
|
||||
// `HasIndexerConstraint` should not mutate `indexType`.
|
||||
// `HasIndexerConstraint` should not mutate `subjectType` or `indexType`.
|
||||
}
|
||||
else if (auto apc = get<AssignPropConstraint>(*this))
|
||||
{
|
||||
|
@ -143,10 +150,6 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
rci.traverse(ty);
|
||||
// `UnpackConstraint` should not mutate `sourcePack`.
|
||||
}
|
||||
else if (auto rpc = get<ReduceConstraint>(*this); FFlag::DebugLuauGreedyGeneralization && rpc)
|
||||
{
|
||||
rci.traverse(rpc->ty);
|
||||
}
|
||||
else if (auto rpc = get<ReducePackConstraint>(*this))
|
||||
{
|
||||
rci.traverse(rpc->tp);
|
||||
|
|
|
@ -34,11 +34,15 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
|
|||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||
|
||||
|
@ -46,12 +50,9 @@ LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
|||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -156,7 +157,7 @@ struct HasFreeType : TypeOnceVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -217,32 +218,6 @@ ConstraintGenerator::ConstraintGenerator(
|
|||
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)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("ConstraintGenerator::visitModuleRoot", "Typechecking");
|
||||
|
@ -288,7 +263,8 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
GeneralizationConstraint{
|
||||
result,
|
||||
moduleFnTy,
|
||||
std::vector<TypeId>{},
|
||||
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
||||
: std::move(DEPRECATED_interiorTypes.back())
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -297,7 +273,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||
}
|
||||
else
|
||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
scope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||
|
||||
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
||||
|
@ -383,18 +359,18 @@ TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
|||
{
|
||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
||||
interiorFreeTypes.back().types.push_back(ft);
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
freeTypes.insert(ft);
|
||||
|
||||
return ft;
|
||||
}
|
||||
else
|
||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
{
|
||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
||||
DEPRECATED_interiorTypes.back().push_back(ft);
|
||||
return ft;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Luau::freshType(arena, builtinTypes, scope.get());
|
||||
}
|
||||
}
|
||||
|
||||
TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity polarity)
|
||||
|
@ -602,9 +578,16 @@ void ConstraintGenerator::computeRefinement(
|
|||
|
||||
// When the top-level expression is `t[x]`, we want to refine it into `nil`, not `never`.
|
||||
LUAU_ASSERT(refis->get(proposition->key->def));
|
||||
if (FFlag::LuauDoNotLeakNilInRefinement)
|
||||
{
|
||||
refis->get(proposition->key->def)->shouldAppendNilType =
|
||||
(sense || !eq) && containsSubscriptedDefinition(proposition->key->def) && !proposition->implicitFromCall;
|
||||
}
|
||||
else
|
||||
{
|
||||
refis->get(proposition->key->def)->shouldAppendNilType = (sense || !eq) && containsSubscriptedDefinition(proposition->key->def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
|
@ -650,7 +633,7 @@ struct FindSimplificationBlockers : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
bool visit(TypeId, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -875,7 +858,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
|
||||
aliasDefinitionLocations[function->name.value] = function->location;
|
||||
}
|
||||
else if (auto classDeclaration = stat->as<AstStatDeclareExternType>())
|
||||
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
|
||||
{
|
||||
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
||||
{
|
||||
|
@ -1069,7 +1052,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat)
|
|||
return visit(scope, s);
|
||||
else if (auto s = stat->as<AstStatDeclareFunction>())
|
||||
return visit(scope, s);
|
||||
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
||||
return visit(scope, s);
|
||||
else if (auto s = stat->as<AstStatError>())
|
||||
return visit(scope, s);
|
||||
|
@ -1290,8 +1273,11 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
|
|||
|
||||
for (AstLocal* var : forIn->vars)
|
||||
{
|
||||
TypeId assignee = arena->addType(BlockedType{});
|
||||
variableTypes.push_back(assignee);
|
||||
|
||||
TypeId loopVar = arena->addType(BlockedType{});
|
||||
variableTypes.push_back(loopVar);
|
||||
localTypes[loopVar].insert(assignee);
|
||||
|
||||
if (var->annotation)
|
||||
{
|
||||
|
@ -1310,23 +1296,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
|
|||
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes}
|
||||
);
|
||||
|
||||
if (FFlag::LuauAddCallConstraintForIterableFunctions)
|
||||
{
|
||||
// Add an intersection ReduceConstraint for the key variable to denote that it can't be nil
|
||||
AstLocal* keyVar = *forIn->vars.begin();
|
||||
const DefId keyDef = dfg->getDef(keyVar);
|
||||
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)
|
||||
{
|
||||
auto bt = getMutable<BlockedType>(var);
|
||||
|
@ -1409,7 +1378,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->name->location};
|
||||
|
||||
bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature);
|
||||
bool sigFullyDefined = !hasFreeType(sig.signature);
|
||||
if (sigFullyDefined)
|
||||
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
||||
|
||||
|
@ -1471,7 +1440,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||
|
||||
Checkpoint start = checkpoint(this);
|
||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||
bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature);
|
||||
bool sigFullyDefined = !hasFreeType(sig.signature);
|
||||
|
||||
DefId def = dfg->getDef(function->name);
|
||||
|
||||
|
@ -1810,7 +1779,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||
GeneralizationConstraint{
|
||||
generalizedTy,
|
||||
sig.signature,
|
||||
std::vector<TypeId>{},
|
||||
FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(DEPRECATED_interiorTypes.back())
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1819,7 +1788,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||
}
|
||||
else
|
||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||
|
||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||
|
@ -1883,71 +1852,71 @@ static bool isMetamethod(const Name& name)
|
|||
name == "__idiv";
|
||||
}
|
||||
|
||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExternType* declaredExternType)
|
||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
||||
{
|
||||
// If a class with the same name was already defined, we skip over
|
||||
auto bindingIt = scope->exportedTypeBindings.find(declaredExternType->name.value);
|
||||
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
|
||||
if (bindingIt == scope->exportedTypeBindings.end())
|
||||
return ControlFlow::None;
|
||||
|
||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->externType);
|
||||
if (declaredExternType->superName)
|
||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
||||
if (declaredClass->superName)
|
||||
{
|
||||
Name superName = Name(declaredExternType->superName->value);
|
||||
Name superName = Name(declaredClass->superName->value);
|
||||
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
||||
|
||||
if (!lookupType)
|
||||
{
|
||||
reportError(declaredExternType->location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||
reportError(declaredClass->location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
||||
// We don't have generic extern types, so this assertion _should_ never be hit.
|
||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||
superTy = follow(lookupType->type);
|
||||
|
||||
if (!get<ExternType>(follow(*superTy)))
|
||||
if (!get<ClassType>(follow(*superTy)))
|
||||
{
|
||||
reportError(
|
||||
declaredExternType->location,
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType->name.value)}
|
||||
declaredClass->location,
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass->name.value)}
|
||||
);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
}
|
||||
|
||||
Name className(declaredExternType->name.value);
|
||||
Name className(declaredClass->name.value);
|
||||
|
||||
TypeId externTy = arena->addType(ExternType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredExternType->location));
|
||||
ExternType* etv = getMutable<ExternType>(externTy);
|
||||
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredClass->location));
|
||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
||||
|
||||
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
||||
TableType* metatable = getMutable<TableType>(metaTy);
|
||||
|
||||
etv->metatable = metaTy;
|
||||
ctv->metatable = metaTy;
|
||||
|
||||
TypeId classBindTy = bindingIt->second.type;
|
||||
emplaceType<BoundType>(asMutable(classBindTy), externTy);
|
||||
emplaceType<BoundType>(asMutable(classBindTy), classTy);
|
||||
|
||||
if (declaredExternType->indexer)
|
||||
if (declaredClass->indexer)
|
||||
{
|
||||
RecursionCounter counter{&recursionCount};
|
||||
|
||||
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
||||
{
|
||||
reportCodeTooComplex(declaredExternType->indexer->location);
|
||||
reportCodeTooComplex(declaredClass->indexer->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
etv->indexer = TableIndexer{
|
||||
resolveType(scope, declaredExternType->indexer->indexType, /* inTypeArguments */ false),
|
||||
resolveType(scope, declaredExternType->indexer->resultType, /* inTypeArguments */ false),
|
||||
ctv->indexer = TableIndexer{
|
||||
resolveType(scope, declaredClass->indexer->indexType, /* inTypeArguments */ false),
|
||||
resolveType(scope, declaredClass->indexer->resultType, /* inTypeArguments */ false),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const AstDeclaredExternTypeProperty& prop : declaredExternType->props)
|
||||
for (const AstDeclaredClassProp& prop : declaredClass->props)
|
||||
{
|
||||
Name propName(prop.name.value);
|
||||
TypeId propTy = resolveType(scope, prop.ty, /* inTypeArguments */ false);
|
||||
|
@ -1961,7 +1930,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
|||
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
||||
{
|
||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||
ftv->argTypes = addTypePack({externTy}, ftv->argTypes);
|
||||
ftv->argTypes = addTypePack({classTy}, ftv->argTypes);
|
||||
|
||||
ftv->hasSelf = true;
|
||||
|
||||
|
@ -1976,7 +1945,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
|||
}
|
||||
}
|
||||
|
||||
TableType::Props& props = assignToMetatable ? metatable->props : etv->props;
|
||||
TableType::Props& props = assignToMetatable ? metatable->props : ctv->props;
|
||||
|
||||
if (props.count(propName) == 0)
|
||||
{
|
||||
|
@ -2007,7 +1976,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
|||
}
|
||||
else
|
||||
{
|
||||
reportError(declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
reportError(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2039,8 +2008,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
|||
funScope = childScope(global, scope);
|
||||
|
||||
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
|
||||
TypePackId retPack = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false)
|
||||
: resolveTypePack(funScope, global->retTypes_DEPRECATED, /* inTypeArguments */ false);
|
||||
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false);
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
|
@ -2388,14 +2356,11 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
|||
argEndCheckpoint,
|
||||
this,
|
||||
[checkConstraint, callConstraint](const ConstraintPtr& constraint)
|
||||
{
|
||||
if (!(FFlag::DebugLuauGreedyGeneralization && get<PrimitiveTypeConstraint>(*constraint)))
|
||||
{
|
||||
constraint->dependencies.emplace_back(checkConstraint);
|
||||
|
||||
callConstraint->dependencies.emplace_back(constraint.get());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
|
||||
|
@ -2499,7 +2464,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
|||
}
|
||||
else
|
||||
{
|
||||
FreeType ft = FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType};
|
||||
FreeType ft =
|
||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
||||
ft.lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
||||
ft.upperBound = builtinTypes->stringType;
|
||||
freeTy = arena->addType(ft);
|
||||
|
@ -2526,7 +2492,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
|||
}
|
||||
else
|
||||
{
|
||||
FreeType ft = FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType};
|
||||
FreeType ft =
|
||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
||||
ft.lowerBound = singletonType;
|
||||
ft.upperBound = builtinTypes->booleanType;
|
||||
freeTy = arena->addType(ft);
|
||||
|
@ -2691,7 +2658,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
|||
GeneralizationConstraint{
|
||||
generalizedTy,
|
||||
sig.signature,
|
||||
std::vector<TypeId>{},
|
||||
(FFlag::LuauNonReentrantGeneralization2 || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
||||
: std::move(DEPRECATED_interiorTypes.back())
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -2705,7 +2673,9 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
|||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||
|
||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||
DEPRECATED_interiorTypes.pop_back();
|
||||
}
|
||||
|
@ -2993,7 +2963,7 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
|
|||
else if (typeguard->type == "userdata")
|
||||
{
|
||||
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
||||
discriminantTy = builtinTypes->externType;
|
||||
discriminantTy = builtinTypes->classType;
|
||||
}
|
||||
else if (!typeguard->isTypeof && typeguard->type == "vector")
|
||||
discriminantTy = builtinTypes->neverType; // TODO: figure out a way to deal with this quirky type
|
||||
|
@ -3003,8 +2973,8 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
|
|||
{
|
||||
TypeId ty = follow(typeFun->type);
|
||||
|
||||
// We're only interested in the root type of any extern type.
|
||||
if (auto etv = get<ExternType>(ty); etv && (etv->parent == builtinTypes->externType || hasTag(ty, kTypeofRootTag)))
|
||||
// We're only interested in the root class of any classes.
|
||||
if (auto ctv = get<ClassType>(ty); ctv && (ctv->parent == builtinTypes->classType || hasTag(ty, kTypeofRootTag)))
|
||||
discriminantTy = ty;
|
||||
}
|
||||
|
||||
|
@ -3077,7 +3047,7 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
|
|||
if (ty)
|
||||
{
|
||||
TypeIds* localDomain = localTypes.find(*ty);
|
||||
if (localDomain && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && local->upvalue))
|
||||
if (localDomain)
|
||||
localDomain->insert(rhsType);
|
||||
}
|
||||
else
|
||||
|
@ -3108,8 +3078,6 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
|
|||
if (annotatedTy)
|
||||
addConstraint(scope, local->location, SubtypeConstraint{rhsType, *annotatedTy});
|
||||
|
||||
// This is vestigial.
|
||||
if (!FFlag::LuauDoNotAddUpvalueTypesToLocalType)
|
||||
if (TypeIds* localDomain = localTypes.find(*ty))
|
||||
localDomain->insert(rhsType);
|
||||
}
|
||||
|
@ -3415,36 +3383,12 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||
|
||||
LUAU_ASSERT(nullptr != varargPack);
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
// Some of the types in argTypes will eventually be generics, and some
|
||||
// will not. The ones that are not generic will be pruned when
|
||||
// GeneralizationConstraint dispatches.
|
||||
genericTypes.insert(genericTypes.begin(), argTypes.begin(), argTypes.end());
|
||||
varargPack = follow(varargPack);
|
||||
returnType = follow(returnType);
|
||||
if (varargPack == returnType)
|
||||
genericTypePacks = {varargPack};
|
||||
else
|
||||
genericTypePacks = {varargPack, returnType};
|
||||
}
|
||||
|
||||
// If there is both an annotation and an expected type, the annotation wins.
|
||||
// Type checking will sort out any discrepancies later.
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation)
|
||||
if (fn->returnAnnotation)
|
||||
{
|
||||
TypePackId annotatedRetType =
|
||||
resolveTypePack(signatureScope, fn->returnAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
||||
// 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);
|
||||
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.
|
||||
|
@ -3695,16 +3639,8 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
|||
AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes};
|
||||
TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
|
||||
TypePackId returnTypes;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes_DEPRECATED};
|
||||
returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
}
|
||||
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes};
|
||||
TypePackId returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
||||
|
||||
// TODO: FunctionType needs a pointer to the scope so that we know
|
||||
// how to quantify/instantiate it.
|
||||
|
@ -3788,9 +3724,13 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
{
|
||||
if (unionAnnotation->types.size == 1)
|
||||
return resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||
}
|
||||
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : unionAnnotation->types)
|
||||
{
|
||||
|
@ -3818,9 +3758,13 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
{
|
||||
if (intersectionAnnotation->types.size == 1)
|
||||
return resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||
}
|
||||
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : intersectionAnnotation->types)
|
||||
{
|
||||
|
@ -4350,4 +4294,15 @@ TypeId ConstraintGenerator::simplifyUnion(const ScopePtr& scope, Location locati
|
|||
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
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Luau/Location.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/OverloadResolution.h"
|
||||
#include "Luau/Quantify.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Simplify.h"
|
||||
#include "Luau/TableLiteralInference.h"
|
||||
|
@ -32,20 +33,20 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
||||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static void dump(ConstraintSolver* cs, ToStringOptions& opts);
|
||||
|
||||
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
||||
{
|
||||
size_t result = 0;
|
||||
|
@ -275,6 +276,26 @@ size_t HashInstantiationSignature::operator()(const InstantiationSignature& sign
|
|||
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
|
||||
{
|
||||
ConstraintSolver* solver;
|
||||
|
@ -300,44 +321,12 @@ struct InstantiationQueuer : TypeOnceVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType& etv) override
|
||||
bool visit(TypeId ty, const ClassType& ctv) override
|
||||
{
|
||||
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(
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<Simplifier> simplifier,
|
||||
|
@ -357,7 +346,6 @@ ConstraintSolver::ConstraintSolver(
|
|||
, normalizer(normalizer)
|
||||
, simplifier(simplifier)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, constraintSet{rootScope}
|
||||
, constraints(std::move(constraints))
|
||||
, scopeToFunction(scopeToFunction)
|
||||
, rootScope(rootScope)
|
||||
|
@ -367,9 +355,33 @@ ConstraintSolver::ConstraintSolver(
|
|||
, requireCycles(std::move(requireCycles))
|
||||
, logger(logger)
|
||||
, limits(std::move(limits))
|
||||
, opts{/*exhaustive*/ true}
|
||||
{
|
||||
initFreeTypeTracking();
|
||||
opts.exhaustive = true;
|
||||
|
||||
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)
|
||||
|
@ -414,18 +426,6 @@ void ConstraintSolver::run()
|
|||
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)
|
||||
{
|
||||
bool progress = false;
|
||||
|
@ -655,39 +655,12 @@ struct TypeSearcher : TypeVisitor
|
|||
|
||||
// }
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
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)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
@ -885,6 +858,8 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType());
|
||||
}
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
{
|
||||
// We check if this member is initialized and then access it, but
|
||||
// clang-tidy doesn't understand this is safe.
|
||||
if (constraint->scope->interiorFreeTypes)
|
||||
|
@ -932,6 +907,13 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (TypeId ty : c.interiorTypes)
|
||||
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1007,11 +989,15 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
|||
{
|
||||
TypeId keyTy = 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, valueTy);
|
||||
}
|
||||
TypeId tableTy =
|
||||
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, tableTy);
|
||||
|
||||
unify(constraint, nextTy, tableTy);
|
||||
|
@ -1336,6 +1322,8 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
|||
if (!ty)
|
||||
continue;
|
||||
|
||||
if (FFlag::LuauSearchForRefineableType)
|
||||
{
|
||||
if (isBlocked(*ty))
|
||||
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
||||
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
||||
|
@ -1343,7 +1331,20 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
|||
// We also need to unconditionally unblock these types, otherwise
|
||||
// you end up with funky looking "Blocked on *no-refine*."
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1456,9 +1457,9 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
|||
|
||||
if (ftv)
|
||||
{
|
||||
if (ftv->magic && c.callSite)
|
||||
if (ftv->magic)
|
||||
{
|
||||
usedMagic = ftv->magic->infer(MagicFunctionCallContext{NotNull{this}, constraint, NotNull{c.callSite}, c.argsPack, result});
|
||||
usedMagic = ftv->magic->infer(MagicFunctionCallContext{NotNull{this}, constraint, c.callSite, c.argsPack, result});
|
||||
ftv->magic->refine(MagicRefinementContext{constraint->scope, c.callSite, c.discriminantTypes});
|
||||
}
|
||||
}
|
||||
|
@ -1533,7 +1534,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
|||
|
||||
// This can potentially contain free types if the return type of
|
||||
// `inferredTy` is never unified elsewhere.
|
||||
if (FFlag::LuauTrackInferredFunctionTypeFromCall)
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInferredFunctionTypeFromCall)
|
||||
trackInteriorFreeType(constraint->scope, inferredTy);
|
||||
|
||||
unblock(c.result, constraint->location);
|
||||
|
@ -1902,7 +1903,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
}
|
||||
else if (auto mt = get<MetatableType>(subjectType))
|
||||
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
|
||||
else if (auto ct = get<ExternType>(subjectType))
|
||||
else if (auto ct = get<ClassType>(subjectType))
|
||||
{
|
||||
if (auto indexer = ct->indexer)
|
||||
{
|
||||
|
@ -2064,9 +2065,9 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
|||
// Important: In every codepath through this function, the type `c.propType`
|
||||
// must be bound to something, even if it's just the errorType.
|
||||
|
||||
if (auto lhsExternType = get<ExternType>(lhsType))
|
||||
if (auto lhsClass = get<ClassType>(lhsType))
|
||||
{
|
||||
const Property* prop = lookupExternTypeProp(lhsExternType, propName);
|
||||
const Property* prop = lookupClassProp(lhsClass, propName);
|
||||
if (!prop || !prop->writeTy.has_value())
|
||||
{
|
||||
bind(constraint, c.propType, builtinTypes->anyType);
|
||||
|
@ -2081,48 +2082,13 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
|||
if (auto lhsFree = getMutable<FreeType>(lhsType))
|
||||
{
|
||||
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
|
||||
if (!blocked.empty())
|
||||
{
|
||||
for (TypeId t : blocked)
|
||||
block(t, constraint);
|
||||
return false;
|
||||
}
|
||||
else if (maybeTy)
|
||||
{
|
||||
bind(constraint, c.propType, isIndex ? arena->addType(UnionType{{*maybeTy, builtinTypes->nilType}}) : *maybeTy);
|
||||
unify(constraint, rhsType, *maybeTy);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||
|
||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||
|
||||
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||
LUAU_ASSERT(upperTable);
|
||||
|
||||
upperTable->props[c.propName] = rhsType;
|
||||
|
||||
// Food for thought: Could we block if simplification encounters a blocked type?
|
||||
lhsFree->upperBound = simplifyIntersection(constraint->scope, constraint->location, lhsFreeUpperBound, newUpperBound);
|
||||
|
||||
bind(constraint, c.propType, rhsType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<TableType>(lhsFreeUpperBound) || get<MetatableType>(lhsFreeUpperBound))
|
||||
lhsType = lhsFreeUpperBound;
|
||||
else
|
||||
{
|
||||
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||
|
||||
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||
|
@ -2137,7 +2103,6 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case that lhsType is a table that already has the property or
|
||||
// a matching indexer. This also handles unions and intersections.
|
||||
|
@ -2287,20 +2252,20 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
|||
return *res;
|
||||
}
|
||||
|
||||
if (auto lhsExternType = get<ExternType>(lhsType))
|
||||
if (auto lhsClass = get<ClassType>(lhsType))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (lhsExternType->indexer)
|
||||
if (lhsClass->indexer)
|
||||
{
|
||||
unify(constraint, indexType, lhsExternType->indexer->indexType);
|
||||
unify(constraint, rhsType, lhsExternType->indexer->indexResultType);
|
||||
bind(constraint, c.propType, arena->addType(UnionType{{lhsExternType->indexer->indexResultType, builtinTypes->nilType}}));
|
||||
unify(constraint, indexType, lhsClass->indexer->indexType);
|
||||
unify(constraint, rhsType, lhsClass->indexer->indexResultType);
|
||||
bind(constraint, c.propType, arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lhsExternType->parent)
|
||||
lhsExternType = get<ExternType>(lhsExternType->parent);
|
||||
if (lhsClass->parent)
|
||||
lhsClass = get<ClassType>(lhsClass->parent);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
@ -2327,7 +2292,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
|||
parts.insert(rhsType);
|
||||
}
|
||||
}
|
||||
else if (auto cls = get<ExternType>(follow(t)))
|
||||
else if (auto cls = get<ClassType>(follow(t)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -2339,7 +2304,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
|||
}
|
||||
|
||||
if (cls->parent)
|
||||
cls = get<ExternType>(cls->parent);
|
||||
cls = get<ClassType>(cls->parent);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
@ -2390,6 +2355,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
|
|||
// constitute any meaningful constraint, so we replace it
|
||||
// with a free type.
|
||||
TypeId f = freshType(arena, builtinTypes, constraint->scope, Polarity::Positive); // FIXME? Is this the right polarity?
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, f);
|
||||
shiftReferences(resultTy, f);
|
||||
emplaceType<BoundType>(asMutable(resultTy), f);
|
||||
|
@ -2533,8 +2499,11 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
|||
{
|
||||
TypeId keyTy = 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, valueTy);
|
||||
}
|
||||
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
|
||||
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
|
||||
|
||||
|
@ -2684,31 +2653,12 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
|
|||
const FunctionType* nextFn = get<FunctionType>(nextTy);
|
||||
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.
|
||||
LUAU_ASSERT(nextFn);
|
||||
const TypePackId nextRetPack = nextFn->retTypes;
|
||||
|
||||
// the type of the `nextAstFragment` is the `nextTy`.
|
||||
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
|
||||
|
||||
if (FFlag::LuauAddCallConstraintForIterableFunctions)
|
||||
{
|
||||
// Construct a FunctionCallConstraint, to help us learn about the type of the loop variables being assigned to in this iterable
|
||||
TypePackId tableTyPack = arena->addTypePack({tableTy});
|
||||
|
||||
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);
|
||||
auto it = begin(nextRetPack);
|
||||
std::vector<TypeId> modifiedNextRetHead;
|
||||
|
||||
// The first value is never nil in the context of the loop, even if it's nil
|
||||
|
@ -2730,7 +2680,6 @@ bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy
|
|||
auto unpackConstraint = unpackAndAssign(c.variables, modifiedNextRetPack, constraint);
|
||||
|
||||
inheritBlocks(constraint, unpackConstraint);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2811,6 +2760,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
if (ttv->state == TableState::Free)
|
||||
{
|
||||
TypeId result = freshType(arena, builtinTypes, ttv->scope, Polarity::Mixed);
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(ttv->scope, result);
|
||||
switch (context)
|
||||
{
|
||||
|
@ -2885,9 +2835,9 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
else if (get<MetatableType>(mtt))
|
||||
return lookupTableProp(constraint, mtt, propName, context, inConditional, suppressSimplification, seen);
|
||||
}
|
||||
else if (auto ct = get<ExternType>(subjectType))
|
||||
else if (auto ct = get<ClassType>(subjectType))
|
||||
{
|
||||
if (auto p = lookupExternTypeProp(ct, propName))
|
||||
if (auto p = lookupClassProp(ct, propName))
|
||||
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
|
||||
if (ct->indexer)
|
||||
{
|
||||
|
@ -2909,33 +2859,23 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
{
|
||||
const TypeId upperBound = follow(ft->upperBound);
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||
{
|
||||
TablePropLookupResult res = lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
||||
// If the upper bound is a table that already has the property, we don't need to extend its bounds.
|
||||
if (res.propType || get<PrimitiveType>(upperBound))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||
return lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
||||
}
|
||||
|
||||
// TODO: The upper bound could be an intersection that contains suitable tables or extern types.
|
||||
// TODO: The upper bound could be an intersection that contains suitable tables or classes.
|
||||
|
||||
NotNull<Scope> scope{ft->scope};
|
||||
|
||||
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||
|
||||
TableType* tt = getMutable<TableType>(newUpperBound);
|
||||
LUAU_ASSERT(tt);
|
||||
TypeId propType = freshType(arena, builtinTypes, scope, Polarity::Mixed);
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(scope, propType);
|
||||
|
||||
switch (context)
|
||||
|
@ -3155,7 +3095,7 @@ struct Blocker : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -3531,35 +3471,4 @@ 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, 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
|
||||
|
|
|
@ -16,9 +16,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -473,7 +470,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStat* s)
|
|||
return visit(d);
|
||||
else if (auto d = s->as<AstStatDeclareFunction>())
|
||||
return visit(d);
|
||||
else if (auto d = s->as<AstStatDeclareExternType>())
|
||||
else if (auto d = s->as<AstStatDeclareClass>())
|
||||
return visit(d);
|
||||
else if (auto error = s->as<AstStatError>())
|
||||
return visit(error);
|
||||
|
@ -502,26 +499,12 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
|
|||
}
|
||||
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
if (FFlag::LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
{
|
||||
// If the control flow from the `if` or `else` block is non-linear,
|
||||
// then we should assume that the _other_ branch is the one taken.
|
||||
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
||||
scope->inherit(elseScope);
|
||||
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
||||
scope->inherit(thenScope);
|
||||
else if ((thencf | elsecf) == ControlFlow::None)
|
||||
join(scope, thenScope, elseScope);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
||||
join(scope, scope, elseScope);
|
||||
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
||||
join(scope, thenScope, scope);
|
||||
else if ((thencf | elsecf) == ControlFlow::None)
|
||||
join(scope, thenScope, elseScope);
|
||||
}
|
||||
|
||||
if (thencf == elsecf)
|
||||
return thencf;
|
||||
|
@ -825,15 +808,12 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
|||
visitGenerics(d->generics);
|
||||
visitGenericPacks(d->genericPacks);
|
||||
visitTypeList(d->params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visitTypePack(d->retTypes);
|
||||
else
|
||||
visitTypeList(d->retTypes_DEPRECATED);
|
||||
visitTypeList(d->retTypes);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareExternType* d)
|
||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d)
|
||||
{
|
||||
// This declaration does not "introduce" any bindings in value namespace,
|
||||
// so there's no symbolic value to begin with. We'll traverse the properties
|
||||
|
@ -841,7 +821,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareExternType* d)
|
|||
DfgScope* unreachable = makeChildScope();
|
||||
PushScope ps{scopeStack, unreachable};
|
||||
|
||||
for (AstDeclaredExternTypeProperty prop : d->props)
|
||||
for (AstDeclaredClassProp prop : d->props)
|
||||
visitType(prop.ty);
|
||||
|
||||
return ControlFlow::None;
|
||||
|
@ -1052,16 +1032,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
if (f->varargAnnotation)
|
||||
visitTypePack(f->varargAnnotation);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (f->returnAnnotation)
|
||||
visitTypePack(f->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f->returnAnnotation_DEPRECATED)
|
||||
visitTypeList(*f->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
visitTypeList(*f->returnAnnotation);
|
||||
|
||||
// TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be
|
||||
// visible to the beginning of the function, so statically speaking, the body of the function has an exit point
|
||||
|
@ -1179,7 +1151,7 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
|
|||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
|
||||
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
||||
if (scope->canUpdateDefinition(l->local) && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && l->upvalue))
|
||||
if (scope->canUpdateDefinition(l->local))
|
||||
{
|
||||
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
|
||||
scope->bindings[l->local] = updated;
|
||||
|
@ -1303,10 +1275,7 @@ void DataFlowGraphBuilder::visitType(AstTypeFunction* f)
|
|||
visitGenerics(f->generics);
|
||||
visitGenericPacks(f->genericPacks);
|
||||
visitTypeList(f->argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visitTypePack(f->returnTypes);
|
||||
else
|
||||
visitTypeList(f->returnTypes_DEPRECATED);
|
||||
visitTypeList(f->returnTypes);
|
||||
}
|
||||
|
||||
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 diffGeneric(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
struct FindSeteqCounterexampleResult
|
||||
{
|
||||
// nullopt if no counterexample found
|
||||
|
@ -481,14 +481,14 @@ static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId rig
|
|||
return differResult;
|
||||
}
|
||||
|
||||
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right)
|
||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right)
|
||||
{
|
||||
const ExternType* leftExternType = get<ExternType>(left);
|
||||
const ExternType* rightExternType = get<ExternType>(right);
|
||||
LUAU_ASSERT(leftExternType);
|
||||
LUAU_ASSERT(rightExternType);
|
||||
const ClassType* leftClass = get<ClassType>(left);
|
||||
const ClassType* rightClass = get<ClassType>(right);
|
||||
LUAU_ASSERT(leftClass);
|
||||
LUAU_ASSERT(rightClass);
|
||||
|
||||
if (leftExternType == rightExternType)
|
||||
if (leftClass == rightClass)
|
||||
{
|
||||
return DifferResult{};
|
||||
}
|
||||
|
@ -651,9 +651,9 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig
|
|||
{
|
||||
return diffNegation(env, left, right);
|
||||
}
|
||||
else if (auto lc = get<ExternType>(left))
|
||||
else if (auto lc = get<ClassType>(left))
|
||||
{
|
||||
return diffExternType(env, left, right);
|
||||
return diffClass(env, left, right);
|
||||
}
|
||||
|
||||
throw InternalCompilerError{"Unimplemented Simple TypeId variant for diffing"};
|
||||
|
@ -960,7 +960,7 @@ bool isSimple(TypeId ty)
|
|||
{
|
||||
ty = follow(ty);
|
||||
// TODO: think about GenericType, etc.
|
||||
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ExternType>(ty) ||
|
||||
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ClassType>(ty) ||
|
||||
get<UnknownType>(ty) || get<NeverType>(ty);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauDeclareExternType)
|
||||
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -262,37 +259,7 @@ declare buffer: {
|
|||
|
||||
)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(
|
||||
static const std::string kBuiltinDefinitionVectorSrc = 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
|
||||
declare class vector
|
||||
|
@ -340,11 +307,11 @@ std::string getBuiltinDefinitionSource()
|
|||
}
|
||||
|
||||
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
||||
static const std::string kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
||||
static const std::string kBuiltinDefinitionTypesSrc = R"BUILTIN_SRC(
|
||||
|
||||
export type type = {
|
||||
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
||||
"singleton" | "negation" | "union" | "intersection" | "table" | "function" | "class" | "generic",
|
||||
"singleton" | "negation" | "union" | "intesection" | "table" | "function" | "class" | "generic",
|
||||
|
||||
is: (self: type, arg: string) -> boolean,
|
||||
|
||||
|
@ -391,10 +358,6 @@ export type type = {
|
|||
ispack: (self: type) -> boolean,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare types: {
|
||||
unknown: type,
|
||||
never: type,
|
||||
|
@ -414,44 +377,12 @@ declare types: {
|
|||
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||
copy: @checked (arg: type) -> type,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare types: {
|
||||
unknown: type,
|
||||
never: type,
|
||||
any: type,
|
||||
boolean: type,
|
||||
number: type,
|
||||
string: type,
|
||||
thread: type,
|
||||
buffer: type,
|
||||
|
||||
singleton: @checked (arg: string | boolean | nil) -> type,
|
||||
optional: @checked (arg: type) -> type,
|
||||
generic: @checked (name: string, ispack: boolean?) -> type,
|
||||
negationof: @checked (arg: type) -> type,
|
||||
unionof: @checked (...type) -> type,
|
||||
intersectionof: @checked (...type) -> type,
|
||||
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
|
||||
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||
copy: @checked (arg: type) -> type,
|
||||
}
|
||||
)BUILTIN_SRC";
|
||||
|
||||
|
||||
std::string getTypeFunctionDefinitionSource()
|
||||
{
|
||||
|
||||
std::string result = kBuiltinDefinitionTypeMethodSrc;
|
||||
|
||||
if (FFlag::LuauTypeFunOptional)
|
||||
result += kBuiltinDefinitionTypesLibWithOptionalSrc;
|
||||
else
|
||||
result += kBuiltinDefinitionTypesLibSrc;
|
||||
|
||||
return result;
|
||||
return kBuiltinDefinitionTypesSrc;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -330,9 +330,9 @@ Id toId(
|
|||
return egraph.add(TOpaque{ty});
|
||||
else if (get<FunctionType>(ty))
|
||||
return egraph.add(TFunction{ty});
|
||||
else if (ty == builtinTypes->externType)
|
||||
else if (ty == builtinTypes->classType)
|
||||
return egraph.add(TTopClass{});
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return egraph.add(TClass{ty});
|
||||
else if (get<AnyType>(ty))
|
||||
return egraph.add(TAny{});
|
||||
|
@ -752,7 +752,7 @@ TypeId fromId(
|
|||
else if (node.get<TTopTable>())
|
||||
return builtinTypes->tableType;
|
||||
else if (node.get<TTopClass>())
|
||||
return builtinTypes->externType;
|
||||
return builtinTypes->classType;
|
||||
else if (node.get<TBuffer>())
|
||||
return builtinTypes->bufferType;
|
||||
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";
|
||||
else if (auto cls = node.get<TClass>())
|
||||
{
|
||||
const ExternType* ct = get<ExternType>(cls->value());
|
||||
const ClassType* ct = get<ClassType>(cls->value());
|
||||
LUAU_ASSERT(ct);
|
||||
return ct->name;
|
||||
}
|
||||
|
@ -1177,12 +1177,12 @@ enum SubclassRelationship
|
|||
|
||||
static SubclassRelationship relateClasses(const TClass* leftClass, const TClass* rightClass)
|
||||
{
|
||||
const ExternType* leftExternType = Luau::get<ExternType>(leftClass->value());
|
||||
const ExternType* rightExternType = Luau::get<ExternType>(rightClass->value());
|
||||
const ClassType* leftClassType = Luau::get<ClassType>(leftClass->value());
|
||||
const ClassType* rightClassType = Luau::get<ClassType>(rightClass->value());
|
||||
|
||||
if (isSubclass(leftExternType, rightExternType))
|
||||
if (isSubclass(leftClassType, rightClassType))
|
||||
return RightSuper;
|
||||
else if (isSubclass(rightExternType, leftExternType))
|
||||
else if (isSubclass(rightClassType, leftClassType))
|
||||
return LeftSuper;
|
||||
else
|
||||
return Unrelated;
|
||||
|
|
|
@ -203,7 +203,7 @@ struct ErrorConverter
|
|||
TypeId t = follow(e.table);
|
||||
if (get<TableType>(t))
|
||||
return "Key '" + e.key + "' not found in table '" + Luau::toString(t) + "'";
|
||||
else if (get<ExternType>(t))
|
||||
else if (get<ClassType>(t))
|
||||
return "Key '" + e.key + "' not found in class '" + Luau::toString(t) + "'";
|
||||
else
|
||||
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 ";
|
||||
|
||||
TypeId t = follow(e.table);
|
||||
if (get<ExternType>(t))
|
||||
if (get<ClassType>(t))
|
||||
s += "class";
|
||||
else
|
||||
s += "table";
|
||||
|
@ -402,8 +402,8 @@ struct ErrorConverter
|
|||
std::optional<TypeId> metatable;
|
||||
if (const MetatableType* mtType = get<MetatableType>(type))
|
||||
metatable = mtType->metatable;
|
||||
else if (const ExternType* externType = get<ExternType>(type))
|
||||
metatable = externType->metatable;
|
||||
else if (const ClassType* classType = get<ClassType>(type))
|
||||
metatable = classType->metatable;
|
||||
|
||||
if (!metatable)
|
||||
return std::nullopt;
|
||||
|
@ -611,7 +611,7 @@ struct ErrorConverter
|
|||
return ss;
|
||||
}
|
||||
|
||||
std::string operator()(const DynamicPropertyLookupOnExternTypesUnsafe& e) const
|
||||
std::string operator()(const DynamicPropertyLookupOnClassesUnsafe& e) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool DynamicPropertyLookupOnExternTypesUnsafe::operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const
|
||||
bool DynamicPropertyLookupOnClassesUnsafe::operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const
|
||||
{
|
||||
return ty == rhs.ty;
|
||||
}
|
||||
|
@ -1391,7 +1391,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
e.wantedTp = clone(e.wantedTp);
|
||||
e.givenTp = clone(e.givenTp);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
||||
e.ty = clone(e.ty);
|
||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||
e.ty = clone(e.ty);
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauExposeRequireByStringAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEscapeCharactersInRequireSuggestions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauHideImpossibleRequireSuggestions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -18,10 +22,13 @@ static std::optional<RequireSuggestions> processRequireSuggestions(std::optional
|
|||
if (!suggestions)
|
||||
return suggestions;
|
||||
|
||||
if (FFlag::LuauEscapeCharactersInRequireSuggestions)
|
||||
{
|
||||
for (RequireSuggestion& suggestion : *suggestions)
|
||||
{
|
||||
suggestion.fullPath = escape(suggestion.fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
@ -105,11 +112,13 @@ static RequireSuggestions makeSuggestionsFromNode(std::unique_ptr<RequireNode> n
|
|||
continue;
|
||||
|
||||
std::string pathComponent = child->getPathComponent();
|
||||
|
||||
if (FFlag::LuauHideImpossibleRequireSuggestions)
|
||||
{
|
||||
// If path component contains a slash, it cannot be required by string.
|
||||
// There's no point suggesting it.
|
||||
if (pathComponent.find('/') != std::string::npos)
|
||||
continue;
|
||||
}
|
||||
|
||||
RequireSuggestion suggestion;
|
||||
suggestion.label = isPartialPath || path.back() == '/' ? child->getLabel() : "/" + child->getLabel();
|
||||
|
@ -154,6 +163,9 @@ std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestions(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,10 @@
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||
|
@ -40,7 +43,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
|||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -95,11 +97,7 @@ Location getFunctionDeclarationExtents(AstExprFunction* exprFn, AstExpr* exprNam
|
|||
{
|
||||
auto fnBegin = exprFn->location.begin;
|
||||
auto fnEnd = exprFn->location.end;
|
||||
if (auto returnAnnot = exprFn->returnAnnotation; FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||
{
|
||||
fnEnd = returnAnnot->location.end;
|
||||
}
|
||||
else if (auto returnAnnot = exprFn->returnAnnotation_DEPRECATED; !FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot)
|
||||
if (auto returnAnnot = exprFn->returnAnnotation)
|
||||
{
|
||||
if (returnAnnot->tailType)
|
||||
fnEnd = returnAnnot->tailType->location.end;
|
||||
|
@ -544,11 +542,6 @@ struct UsageFinder : public AstVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
bool visit(AstStatTypeAlias* alias) override
|
||||
{
|
||||
declaredAliases.insert(std::string(alias->name.value));
|
||||
|
@ -735,7 +728,7 @@ struct MixedModeIncrementalTCDefFinder : public AstVisitor
|
|||
// requires that we find the local/global `m` and place it in the environment.
|
||||
// The default behaviour here is to return false, and have individual visitors override
|
||||
// the specific behaviour they need.
|
||||
return true;
|
||||
return FFlag::LuauMixedModeDefFinderTraversesTypeOf;
|
||||
}
|
||||
|
||||
bool visit(AstStatTypeAlias* alias) override
|
||||
|
@ -1225,6 +1218,31 @@ ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::uniq
|
|||
return incremental;
|
||||
}
|
||||
|
||||
ModulePtr copyModule(const ModulePtr& result, std::unique_ptr<Allocator> alloc)
|
||||
{
|
||||
ModulePtr incrementalModule = std::make_shared<Module>();
|
||||
incrementalModule->name = result->name;
|
||||
incrementalModule->humanReadableName = "Incremental$" + result->humanReadableName;
|
||||
incrementalModule->internalTypes.owningModule = incrementalModule.get();
|
||||
incrementalModule->interfaceTypes.owningModule = incrementalModule.get();
|
||||
incrementalModule->allocator = std::move(alloc);
|
||||
// Don't need to keep this alive (it's already on the source module)
|
||||
copyModuleVec(incrementalModule->scopes, result->scopes);
|
||||
copyModuleMap(incrementalModule->astTypes, result->astTypes);
|
||||
copyModuleMap(incrementalModule->astTypePacks, result->astTypePacks);
|
||||
copyModuleMap(incrementalModule->astExpectedTypes, result->astExpectedTypes);
|
||||
// Don't need to clone astOriginalCallTypes
|
||||
copyModuleMap(incrementalModule->astOverloadResolvedTypes, result->astOverloadResolvedTypes);
|
||||
// Don't need to clone astForInNextTypes
|
||||
copyModuleMap(incrementalModule->astForInNextTypes, result->astForInNextTypes);
|
||||
// Don't need to clone astResolvedTypes
|
||||
// Don't need to clone astResolvedTypePacks
|
||||
// Don't need to clone upperBoundContributors
|
||||
copyModuleMap(incrementalModule->astScopes, result->astScopes);
|
||||
// Don't need to clone declared Globals;
|
||||
return incrementalModule;
|
||||
}
|
||||
|
||||
void mixedModeCompatibility(
|
||||
const ScopePtr& bottomScopeStale,
|
||||
const ScopePtr& myFakeScope,
|
||||
|
@ -1288,8 +1306,10 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
|||
ModulePtr incrementalModule = nullptr;
|
||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||
incrementalModule = cloneModule(cloneState, stale, std::move(astAllocator), freshChildOfNearestScope.get());
|
||||
else
|
||||
else if (FFlag::LuauCloneIncrementalModule)
|
||||
incrementalModule = cloneModule_DEPRECATED(cloneState, stale, std::move(astAllocator));
|
||||
else
|
||||
incrementalModule = copyModule(stale, std::move(astAllocator));
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneModuleEnd);
|
||||
incrementalModule->checkedInNewSolver = true;
|
||||
|
@ -1343,6 +1363,8 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
|||
};
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
||||
if (FFlag::LuauCloneIncrementalModule)
|
||||
{
|
||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||
cg.rootScope = freshChildOfNearestScope.get();
|
||||
|
||||
|
@ -1363,7 +1385,22 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
|||
for (auto p : cg.scopes)
|
||||
incrementalModule->scopes.emplace_back(std::move(p));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any additions to the scope must occur in a fresh scope
|
||||
cg.rootScope = stale->getModuleScope().get();
|
||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||
mixedModeCompatibility(closestScope, freshChildOfNearestScope, stale, NotNull{&dfg}, root);
|
||||
// closest Scope -> children = { ...., freshChildOfNearestScope}
|
||||
// We need to trim nearestChild from the scope hierarchy
|
||||
closestScope->children.emplace_back(freshChildOfNearestScope.get());
|
||||
cg.visitFragmentRoot(freshChildOfNearestScope, root);
|
||||
// Trim nearestChild from the closestScope
|
||||
Scope* back = closestScope->children.back().get();
|
||||
LUAU_ASSERT(back == freshChildOfNearestScope.get());
|
||||
closestScope->children.pop_back();
|
||||
}
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverStart);
|
||||
|
||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||
|
@ -1669,6 +1706,7 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
|||
IFragmentAutocompleteReporter* reporter
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
||||
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
|||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRethrowKnownExceptions, false)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||
|
@ -129,9 +128,9 @@ static void generateDocumentationSymbols(TypeId ty, const std::string& rootName)
|
|||
prop.documentationSymbol = rootName + "." + name;
|
||||
}
|
||||
}
|
||||
else if (ExternType* etv = getMutable<ExternType>(ty))
|
||||
else if (ClassType* ctv = getMutable<ClassType>(ty))
|
||||
{
|
||||
for (auto& [name, prop] : etv->props)
|
||||
for (auto& [name, prop] : ctv->props)
|
||||
{
|
||||
prop.documentationSymbol = rootName + "." + name;
|
||||
}
|
||||
|
@ -1305,7 +1304,7 @@ ModulePtr check(
|
|||
|
||||
struct InternalTypeFinder : TypeOnceVisitor
|
||||
{
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
bool visit(TypeId, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1420,36 +1419,10 @@ ModulePtr check(
|
|||
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);
|
||||
result->errors = std::move(cg.errors);
|
||||
|
||||
cs.emplace(
|
||||
ConstraintSolver cs{
|
||||
NotNull{&normalizer},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
|
@ -1462,17 +1435,14 @@ ModulePtr check(
|
|||
logger.get(),
|
||||
NotNull{&dfg},
|
||||
limits
|
||||
);
|
||||
}
|
||||
|
||||
LUAU_ASSERT(bool(cs));
|
||||
};
|
||||
|
||||
if (options.randomizeConstraintResolutionSeed)
|
||||
cs->randomize(*options.randomizeConstraintResolutionSeed);
|
||||
cs.randomize(*options.randomizeConstraintResolutionSeed);
|
||||
|
||||
try
|
||||
{
|
||||
cs->run();
|
||||
cs.run();
|
||||
}
|
||||
catch (const TimeLimitError&)
|
||||
{
|
||||
|
@ -1492,12 +1462,12 @@ ModulePtr check(
|
|||
printf("%s\n", output.c_str());
|
||||
}
|
||||
|
||||
for (TypeError& e : cs->errors)
|
||||
for (TypeError& e : cs.errors)
|
||||
result->errors.emplace_back(std::move(e));
|
||||
|
||||
result->scopes = std::move(cg.scopes);
|
||||
result->type = sourceModule.type;
|
||||
result->upperBoundContributors = std::move(cs->upperBoundContributors);
|
||||
result->upperBoundContributors = std::move(cs.upperBoundContributors);
|
||||
|
||||
if (result->timeout || result->cancelled)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization2)
|
||||
|
||||
|
@ -548,7 +549,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
traverse(*prop.readTy);
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(prop.isShared());
|
||||
LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||
|
||||
Polarity p = polarity;
|
||||
polarity = Polarity::Mixed;
|
||||
|
@ -604,7 +605,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
bool visit(TypeId, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -896,7 +897,7 @@ struct TypeCacher : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
cache(ty);
|
||||
return false;
|
||||
|
@ -1403,10 +1404,7 @@ std::optional<TypeId> generalize(
|
|||
}
|
||||
|
||||
for (TypeId unsealedTableTy : fts.unsealedTables)
|
||||
{
|
||||
if (!generalizationTarget || unsealedTableTy == *generalizationTarget)
|
||||
sealTable(scope, unsealedTableTy);
|
||||
}
|
||||
|
||||
for (const auto& [freePackId, params] : fts.typePacks)
|
||||
{
|
||||
|
@ -1466,93 +1464,29 @@ std::optional<TypeId> generalize(
|
|||
|
||||
struct GenericCounter : TypeVisitor
|
||||
{
|
||||
struct CounterState
|
||||
{
|
||||
size_t count = 0;
|
||||
Polarity polarity = Polarity::None;
|
||||
};
|
||||
|
||||
NotNull<DenseHashSet<TypeId>> cachedTypes;
|
||||
DenseHashMap<TypeId, CounterState> generics{nullptr};
|
||||
DenseHashMap<TypePackId, CounterState> genericPacks{nullptr};
|
||||
|
||||
Polarity polarity = Polarity::Positive;
|
||||
DenseHashMap<TypeId, size_t> generics{nullptr};
|
||||
DenseHashMap<TypePackId, size_t> genericPacks{nullptr};
|
||||
|
||||
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
||||
: cachedTypes(cachedTypes)
|
||||
{
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const FunctionType& ft) override
|
||||
{
|
||||
if (ty->persistent)
|
||||
return false;
|
||||
|
||||
polarity = invert(polarity);
|
||||
traverse(ft.argTypes);
|
||||
polarity = invert(polarity);
|
||||
traverse(ft.retTypes);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const TableType& tt) override
|
||||
{
|
||||
if (ty->persistent)
|
||||
return false;
|
||||
|
||||
const Polarity previous = polarity;
|
||||
|
||||
for (const auto& [_name, prop] : tt.props)
|
||||
{
|
||||
if (prop.isReadOnly())
|
||||
traverse(*prop.readTy);
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(prop.isShared());
|
||||
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
polarity = previous;
|
||||
}
|
||||
}
|
||||
|
||||
if (tt.indexer)
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(tt.indexer->indexType);
|
||||
traverse(tt.indexer->indexResultType);
|
||||
polarity = previous;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const GenericType&) override
|
||||
{
|
||||
auto state = generics.find(ty);
|
||||
if (state)
|
||||
{
|
||||
++state->count;
|
||||
state->polarity |= polarity;
|
||||
}
|
||||
size_t* count = generics.find(ty);
|
||||
if (count)
|
||||
++*count;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypePackId tp, const GenericTypePack&) override
|
||||
{
|
||||
auto state = genericPacks.find(tp);
|
||||
if (state)
|
||||
{
|
||||
++state->count;
|
||||
state->polarity |= polarity;
|
||||
}
|
||||
size_t* count = genericPacks.find(tp);
|
||||
if (count)
|
||||
++*count;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1579,78 +1513,66 @@ void pruneUnnecessaryGenerics(
|
|||
if (!functionTy)
|
||||
return;
|
||||
|
||||
// If a generic has no explicit name and is only referred to in one place in
|
||||
// the function's signature, it can be replaced with unknown.
|
||||
// Types (and packs) to be removed from the generics list
|
||||
DenseHashSet<TypeId> clipTypes{nullptr};
|
||||
DenseHashSet<TypePackId> clipTypePacks{nullptr};
|
||||
|
||||
GenericCounter counter{cachedTypes};
|
||||
for (TypeId generic : functionTy->generics)
|
||||
{
|
||||
generic = follow(generic);
|
||||
auto g = get<GenericType>(generic);
|
||||
if (g && !g->explicitName)
|
||||
counter.generics[generic] = {};
|
||||
LUAU_ASSERT(g);
|
||||
if (!g)
|
||||
clipTypes.insert(generic);
|
||||
else if (!g->explicitName)
|
||||
counter.generics[generic] = 0;
|
||||
}
|
||||
|
||||
// It is sometimes the case that a pack in the generic list will become a
|
||||
// pack that (transitively) has a generic tail. If it does, we need to add
|
||||
// that generic tail to the generic pack list.
|
||||
for (size_t i = 0; i < functionTy->genericPacks.size(); ++i)
|
||||
for (TypePackId genericPack : functionTy->genericPacks)
|
||||
{
|
||||
TypePackId genericPack = follow(functionTy->genericPacks[i]);
|
||||
|
||||
TypePackId tail = getTail(genericPack);
|
||||
|
||||
if (tail != genericPack)
|
||||
functionTy->genericPacks.push_back(tail);
|
||||
|
||||
if (auto g = get<GenericTypePack>(tail); g && !g->explicitName)
|
||||
counter.genericPacks[genericPack] = {};
|
||||
auto g = get<GenericTypePack>(genericPack);
|
||||
if (!g)
|
||||
clipTypePacks.insert(genericPack);
|
||||
else if (!g->explicitName)
|
||||
counter.genericPacks[genericPack] = 0;
|
||||
}
|
||||
|
||||
counter.traverse(ty);
|
||||
|
||||
for (const auto& [generic, state] : counter.generics)
|
||||
for (const auto& [generic, count] : counter.generics)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
if (state.count == 1 && state.polarity != Polarity::Mixed)
|
||||
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(
|
||||
functionTy->generics.begin(),
|
||||
functionTy->generics.end(),
|
||||
[&](TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (seen.contains(ty))
|
||||
return true;
|
||||
seen.insert(ty);
|
||||
|
||||
return !get<GenericType>(ty);
|
||||
return clipTypes.contains(ty);
|
||||
}
|
||||
);
|
||||
|
||||
functionTy->generics.erase(it, functionTy->generics.end());
|
||||
|
||||
for (const auto& [genericPack, state] : counter.genericPacks)
|
||||
for (const auto& [genericPack, count] : counter.genericPacks)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
if (state.count == 1)
|
||||
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
||||
clipTypePacks.insert(genericPack);
|
||||
}
|
||||
}
|
||||
|
||||
DenseHashSet<TypePackId> seen2{nullptr};
|
||||
auto it2 = std::remove_if(
|
||||
functionTy->genericPacks.begin(),
|
||||
functionTy->genericPacks.end(),
|
||||
[&](TypePackId tp)
|
||||
{
|
||||
tp = follow(tp);
|
||||
if (seen2.contains(tp))
|
||||
return true;
|
||||
seen2.insert(tp);
|
||||
|
||||
return !get<GenericTypePack>(tp);
|
||||
return clipTypePacks.contains(tp);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -95,16 +95,16 @@ struct InferPolarity : TypeVisitor
|
|||
// types.
|
||||
for (TypeId generic : ft.generics)
|
||||
{
|
||||
generic = follow(generic);
|
||||
const auto gen = get<GenericType>(generic);
|
||||
if (gen && subsumes(scope, gen->scope))
|
||||
LUAU_ASSERT(gen);
|
||||
if (subsumes(scope, gen->scope))
|
||||
types[generic] = Polarity::None;
|
||||
}
|
||||
for (TypePackId genericPack : ft.genericPacks)
|
||||
{
|
||||
genericPack = follow(genericPack);
|
||||
const auto gen = get<GenericTypePack>(genericPack);
|
||||
if (gen && subsumes(scope, gen->scope))
|
||||
LUAU_ASSERT(gen);
|
||||
if (subsumes(scope, gen->scope))
|
||||
packs[genericPack] = Polarity::None;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ struct InferPolarity : TypeVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
bool visit(TypeId, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -49,7 +50,7 @@ bool Instantiation::ignoreChildren(TypeId ty)
|
|||
{
|
||||
if (log->getMutable<FunctionType>(ty))
|
||||
return true;
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
@ -119,7 +120,7 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
|||
// 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);
|
||||
}
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
|
@ -163,7 +164,7 @@ TypeId ReplaceGenerics::clean(TypeId ty)
|
|||
}
|
||||
else
|
||||
{
|
||||
return arena->freshType(builtinTypes, scope, level);
|
||||
return FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtinTypes, scope, level) : addType(FreeType{scope, level});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Luau
|
|||
|
||||
bool Instantiation2::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
if (auto ftv = get<FunctionType>(ty))
|
||||
|
|
|
@ -193,8 +193,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
|||
stream << "NormalizationTooComplex { }";
|
||||
else if constexpr (std::is_same_v<T, TypePackMismatch>)
|
||||
stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }";
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||
stream << "DynamicPropertyLookupOnExternTypesUnsafe { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
||||
stream << "DynamicPropertyLookupOnClassesUnsafe { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||
stream << "UninhabitedTypeFunction { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
|
||||
|
|
|
@ -20,7 +20,6 @@ LUAU_FASTFLAG(LuauAttribute)
|
|||
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -909,11 +908,6 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
bool visit(AstTypeReference* node) override
|
||||
{
|
||||
if (!node->prefix)
|
||||
|
@ -1976,11 +1970,6 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
bool visit(AstTypeTable* node) override
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -2383,9 +2372,9 @@ private:
|
|||
|
||||
void check(AstExprIndexName* node, TypeId ty)
|
||||
{
|
||||
if (const ExternType* cty = get<ExternType>(ty))
|
||||
if (const ClassType* cty = get<ClassType>(ty))
|
||||
{
|
||||
const Property* prop = lookupExternTypeProp(cty, node->index.value);
|
||||
const Property* prop = lookupClassProp(cty, node->index.value);
|
||||
|
||||
if (prop && prop->deprecated)
|
||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -217,7 +217,7 @@ struct NonStrictTypeChecker
|
|||
return *fst;
|
||||
else if (auto ftp = get<FreeTypePack>(pack))
|
||||
{
|
||||
TypeId result = arena->freshType(builtinTypes, ftp->scope);
|
||||
TypeId result = FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtinTypes, ftp->scope) : arena->addType(FreeType{ftp->scope});
|
||||
TypePackId freeTail = arena->addTypePack(FreeTypePack{ftp->scope});
|
||||
|
||||
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
||||
|
@ -311,7 +311,7 @@ struct NonStrictTypeChecker
|
|||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
||||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
||||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatError>())
|
||||
return visit(s);
|
||||
|
@ -340,10 +340,13 @@ struct NonStrictTypeChecker
|
|||
{
|
||||
ctx.remove(dfg->getDef(local));
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
if (local->annotation)
|
||||
visit(local->annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ctx = NonStrictContext::disjunction(builtinTypes, arena, visit(stat), ctx);
|
||||
}
|
||||
|
@ -427,7 +430,8 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatFor* forStatement)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
if (forStatement->var->annotation)
|
||||
visit(forStatement->var->annotation);
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
|
@ -449,11 +453,14 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatForIn* forInStatement)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
for (auto var : forInStatement->vars)
|
||||
{
|
||||
if (var->annotation)
|
||||
visit(var->annotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
{
|
||||
|
@ -503,7 +510,7 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
|
||||
visit(typeAlias->type);
|
||||
|
@ -519,7 +526,7 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
visitGenerics(declFn->generics, declFn->genericPacks);
|
||||
visit(declFn->params);
|
||||
|
@ -531,15 +538,15 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
visit(declGlobal->type);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
NonStrictContext visit(AstStatDeclareExternType* declClass)
|
||||
NonStrictContext visit(AstStatDeclareClass* declClass)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
if (declClass->indexer)
|
||||
{
|
||||
|
@ -815,21 +822,19 @@ struct NonStrictTypeChecker
|
|||
}
|
||||
remainder.remove(dfg->getDef(local));
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
if (local->annotation)
|
||||
visit(local->annotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
{
|
||||
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(exprFn->returnAnnotation);
|
||||
else
|
||||
{
|
||||
if (exprFn->returnAnnotation_DEPRECATED)
|
||||
visit(*exprFn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (exprFn->returnAnnotation)
|
||||
visit(*exprFn->returnAnnotation);
|
||||
|
||||
if (exprFn->varargAnnotation)
|
||||
visit(exprFn->varargAnnotation);
|
||||
|
@ -875,7 +880,7 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
||||
visit(typeAssertion->annotation);
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
|
@ -916,11 +921,7 @@ struct NonStrictTypeChecker
|
|||
|
||||
void visit(AstType* ty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||
|
||||
// If this node is `nullptr`, early exit.
|
||||
if (!ty)
|
||||
return;
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
||||
|
||||
if (auto t = ty->as<AstTypeReference>())
|
||||
return visit(t);
|
||||
|
@ -1129,11 +1130,7 @@ struct NonStrictTypeChecker
|
|||
|
||||
void visit(AstTypePack* pack)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||
|
||||
// If there is no pack node, early exit.
|
||||
if (!pack)
|
||||
return;
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
||||
|
||||
if (auto p = pack->as<AstTypePackExplicit>())
|
||||
return visit(p);
|
||||
|
|
|
@ -249,23 +249,23 @@ bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& s
|
|||
return true;
|
||||
}
|
||||
|
||||
void NormalizedExternType::pushPair(TypeId ty, TypeIds negations)
|
||||
void NormalizedClassType::pushPair(TypeId ty, TypeIds negations)
|
||||
{
|
||||
auto result = externTypes.insert(std::make_pair(ty, std::move(negations)));
|
||||
auto result = classes.insert(std::make_pair(ty, std::move(negations)));
|
||||
if (result.second)
|
||||
ordering.push_back(ty);
|
||||
LUAU_ASSERT(ordering.size() == externTypes.size());
|
||||
LUAU_ASSERT(ordering.size() == classes.size());
|
||||
}
|
||||
|
||||
void NormalizedExternType::resetToNever()
|
||||
void NormalizedClassType::resetToNever()
|
||||
{
|
||||
ordering.clear();
|
||||
externTypes.clear();
|
||||
classes.clear();
|
||||
}
|
||||
|
||||
bool NormalizedExternType::isNever() const
|
||||
bool NormalizedClassType::isNever() const
|
||||
{
|
||||
return externTypes.empty();
|
||||
return classes.empty();
|
||||
}
|
||||
|
||||
void NormalizedFunctionType::resetToTop()
|
||||
|
@ -307,14 +307,14 @@ bool NormalizedType::isUnknown() const
|
|||
strings.isString() && isThread(threads) && isBuffer(buffers);
|
||||
|
||||
// Check is class
|
||||
bool isTopExternType = false;
|
||||
for (const auto& [t, disj] : externTypes.externTypes)
|
||||
bool isTopClass = false;
|
||||
for (auto [t, disj] : classes.classes)
|
||||
{
|
||||
if (auto ct = get<ExternType>(t))
|
||||
if (auto ct = get<ClassType>(t))
|
||||
{
|
||||
if (ct->name == "class" && disj.empty())
|
||||
{
|
||||
isTopExternType = true;
|
||||
isTopClass = true;
|
||||
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
|
||||
return get<NeverType>(errors) && hasAllPrimitives && isTopExternType && isTopTable && functions.isTop;
|
||||
return get<NeverType>(errors) && hasAllPrimitives && isTopClass && isTopTable && functions.isTop;
|
||||
}
|
||||
|
||||
bool NormalizedType::isExactlyNumber() const
|
||||
{
|
||||
return hasNumbers() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
|
||||
return hasNumbers() && !hasTops() && !hasBooleans() && !hasClasses() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
|
||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||
}
|
||||
|
||||
bool NormalizedType::isSubtypeOfString() const
|
||||
{
|
||||
return hasStrings() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
|
||||
return hasStrings() && !hasTops() && !hasBooleans() && !hasClasses() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
|
||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||
}
|
||||
|
||||
bool NormalizedType::isSubtypeOfBooleans() const
|
||||
{
|
||||
return hasBooleans() && !hasTops() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||
return hasBooleans() && !hasTops() && !hasClasses() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
|
||||
}
|
||||
|
||||
|
@ -380,9 +380,9 @@ bool NormalizedType::hasBooleans() const
|
|||
return !get<NeverType>(booleans);
|
||||
}
|
||||
|
||||
bool NormalizedType::hasExternTypes() const
|
||||
bool NormalizedType::hasClasses() const
|
||||
{
|
||||
return !externTypes.isNever();
|
||||
return !classes.isNever();
|
||||
}
|
||||
|
||||
bool NormalizedType::hasErrors() const
|
||||
|
@ -440,7 +440,7 @@ bool NormalizedType::isFalsy() const
|
|||
hasAFalse = !bs->value;
|
||||
}
|
||||
|
||||
return (hasAFalse || hasNils()) && (!hasTops() && !hasExternTypes() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||
return (hasAFalse || hasNils()) && (!hasTops() && !hasClasses() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
|
||||
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars());
|
||||
}
|
||||
|
||||
|
@ -452,7 +452,7 @@ bool NormalizedType::isTruthy() const
|
|||
static bool isShallowInhabited(const NormalizedType& norm)
|
||||
{
|
||||
// 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.externTypes.isNever() || !get<NeverType>(norm.errors) ||
|
||||
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || !norm.classes.isNever() || !get<NeverType>(norm.errors) ||
|
||||
!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();
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ NormalizationResult Normalizer::isInhabited(const NormalizedType* norm, Set<Type
|
|||
return NormalizationResult::HitLimits;
|
||||
|
||||
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->externTypes.isNever() ||
|
||||
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || !get<NeverType>(norm->buffers) || !norm->classes.isNever() ||
|
||||
!norm->strings.isNever() || !norm->functions.isNever())
|
||||
return NormalizationResult::True;
|
||||
|
||||
|
@ -619,13 +619,13 @@ static int tyvarIndex(TypeId ty)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedExternType& externTypes)
|
||||
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedClassType& classes)
|
||||
{
|
||||
if (externTypes.externTypes.size() != 1)
|
||||
if (classes.classes.size() != 1)
|
||||
return false;
|
||||
|
||||
auto first = externTypes.externTypes.begin();
|
||||
if (first->first != builtinTypes->externType)
|
||||
auto first = classes.classes.begin();
|
||||
if (first->first != builtinTypes->classType)
|
||||
return false;
|
||||
|
||||
if (!first->second.empty())
|
||||
|
@ -634,11 +634,11 @@ static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedExternType
|
|||
return true;
|
||||
}
|
||||
|
||||
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedExternType& externTypes)
|
||||
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedClassType& classes)
|
||||
{
|
||||
externTypes.ordering.clear();
|
||||
externTypes.externTypes.clear();
|
||||
externTypes.pushPair(builtinTypes->externType, TypeIds{});
|
||||
classes.ordering.clear();
|
||||
classes.classes.clear();
|
||||
classes.pushPair(builtinTypes->classType, TypeIds{});
|
||||
}
|
||||
|
||||
#ifdef LUAU_ASSERTENABLED
|
||||
|
@ -762,50 +762,50 @@ static bool areNormalizedTables(const TypeIds& tys)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool areNormalizedExternTypes(const NormalizedExternType& tys)
|
||||
static bool areNormalizedClasses(const NormalizedClassType& tys)
|
||||
{
|
||||
for (const auto& [ty, negations] : tys.externTypes)
|
||||
for (const auto& [ty, negations] : tys.classes)
|
||||
{
|
||||
const ExternType* etv = get<ExternType>(ty);
|
||||
if (!etv)
|
||||
const ClassType* ctv = get<ClassType>(ty);
|
||||
if (!ctv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (TypeId negation : negations)
|
||||
{
|
||||
const ExternType* nctv = get<ExternType>(negation);
|
||||
const ClassType* nctv = get<ClassType>(negation);
|
||||
if (!nctv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isSubclass(nctv, etv))
|
||||
if (!isSubclass(nctv, ctv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [otherTy, otherNegations] : tys.externTypes)
|
||||
for (const auto& [otherTy, otherNegations] : tys.classes)
|
||||
{
|
||||
if (otherTy == ty)
|
||||
continue;
|
||||
|
||||
const ExternType* octv = get<ExternType>(otherTy);
|
||||
const ClassType* octv = get<ClassType>(otherTy);
|
||||
if (!octv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSubclass(etv, octv))
|
||||
if (isSubclass(ctv, octv))
|
||||
{
|
||||
auto iss = [etv](TypeId t)
|
||||
auto iss = [ctv](TypeId t)
|
||||
{
|
||||
const ExternType* c = get<ExternType>(t);
|
||||
const ClassType* c = get<ClassType>(t);
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
return isSubclass(etv, c);
|
||||
return isSubclass(ctv, c);
|
||||
};
|
||||
|
||||
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(isNormalizedBoolean(norm.booleans));
|
||||
LUAU_ASSERT(areNormalizedExternTypes(norm.externTypes));
|
||||
LUAU_ASSERT(areNormalizedClasses(norm.classes));
|
||||
LUAU_ASSERT(isNormalizedError(norm.errors));
|
||||
LUAU_ASSERT(isNormalizedNil(norm.nils));
|
||||
LUAU_ASSERT(isNormalizedNumber(norm.numbers));
|
||||
|
@ -988,7 +988,7 @@ void Normalizer::clearNormal(NormalizedType& norm)
|
|||
{
|
||||
norm.tops = builtinTypes->neverType;
|
||||
norm.booleans = builtinTypes->neverType;
|
||||
norm.externTypes.resetToNever();
|
||||
norm.classes.resetToNever();
|
||||
norm.errors = builtinTypes->neverType;
|
||||
norm.nils = builtinTypes->neverType;
|
||||
norm.numbers = builtinTypes->neverType;
|
||||
|
@ -1138,17 +1138,17 @@ TypeId Normalizer::unionOfBools(TypeId here, TypeId there)
|
|||
return builtinTypes->booleanType;
|
||||
}
|
||||
|
||||
void Normalizer::unionExternTypesWithExternType(TypeIds& heres, TypeId there)
|
||||
void Normalizer::unionClassesWithClass(TypeIds& heres, TypeId there)
|
||||
{
|
||||
if (heres.count(there))
|
||||
return;
|
||||
|
||||
const ExternType* tctv = get<ExternType>(there);
|
||||
const ClassType* tctv = get<ClassType>(there);
|
||||
|
||||
for (auto it = heres.begin(); it != heres.end();)
|
||||
{
|
||||
TypeId here = *it;
|
||||
const ExternType* hctv = get<ExternType>(here);
|
||||
const ClassType* hctv = get<ClassType>(here);
|
||||
if (isSubclass(tctv, hctv))
|
||||
return;
|
||||
else if (isSubclass(hctv, tctv))
|
||||
|
@ -1160,16 +1160,16 @@ void Normalizer::unionExternTypesWithExternType(TypeIds& heres, TypeId there)
|
|||
heres.insert(there);
|
||||
}
|
||||
|
||||
void Normalizer::unionExternTypes(TypeIds& heres, const TypeIds& theres)
|
||||
void Normalizer::unionClasses(TypeIds& heres, const TypeIds& theres)
|
||||
{
|
||||
for (TypeId there : theres)
|
||||
unionExternTypesWithExternType(heres, there);
|
||||
unionClassesWithClass(heres, there);
|
||||
}
|
||||
|
||||
static bool isSubclass(TypeId test, TypeId parent)
|
||||
{
|
||||
const ExternType* testCtv = get<ExternType>(test);
|
||||
const ExternType* parentCtv = get<ExternType>(parent);
|
||||
const ClassType* testCtv = get<ClassType>(test);
|
||||
const ClassType* parentCtv = get<ClassType>(parent);
|
||||
|
||||
LUAU_ASSERT(testCtv);
|
||||
LUAU_ASSERT(parentCtv);
|
||||
|
@ -1177,12 +1177,12 @@ static bool isSubclass(TypeId test, TypeId parent)
|
|||
return isSubclass(testCtv, parentCtv);
|
||||
}
|
||||
|
||||
void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
|
||||
void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
||||
{
|
||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||
{
|
||||
TypeId hereTy = *it;
|
||||
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
||||
|
||||
// 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
|
||||
|
@ -1204,7 +1204,7 @@ void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, Typ
|
|||
}
|
||||
// If the incoming class is a superclass of one of the
|
||||
// negations, then the negation no longer applies and must be
|
||||
// removed. This is also true if they are equal. Since extern types
|
||||
// removed. This is also true if they are equal. Since classes
|
||||
// are, at this time, entirely persistent (we do not clone
|
||||
// them), a pointer identity check is sufficient.
|
||||
else if (isSubclass(hereNegation, there))
|
||||
|
@ -1231,7 +1231,7 @@ void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, Typ
|
|||
{
|
||||
TypeIds negations = std::move(hereNegations);
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
|
||||
heres.pushPair(there, std::move(negations));
|
||||
return;
|
||||
|
@ -1248,10 +1248,10 @@ void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, Typ
|
|||
heres.pushPair(there, TypeIds{});
|
||||
}
|
||||
|
||||
void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
|
||||
void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
||||
{
|
||||
// This method bears much similarity with unionExternTypesWithExternType, but is
|
||||
// solving a more general problem. In unionExternTypesWithExternType, we are dealing
|
||||
// This method bears much similarity with unionClassesWithClass, but is
|
||||
// solving a more general problem. In unionClassesWithClass, we are dealing
|
||||
// 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
|
||||
// have negations to worry about combining. The two aspects combine to make
|
||||
|
@ -1260,9 +1260,9 @@ void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedE
|
|||
|
||||
for (const TypeId thereTy : theres.ordering)
|
||||
{
|
||||
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
|
||||
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
||||
|
||||
// If it happens that there are _no_ extern types in the current map, or the
|
||||
// If it happens that there are _no_ classes in the current map, or the
|
||||
// incoming class is completely unrelated to any class in the current
|
||||
// map, we must insert the incoming pair as-is.
|
||||
bool insert = true;
|
||||
|
@ -1270,7 +1270,7 @@ void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedE
|
|||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||
{
|
||||
TypeId hereTy = *it;
|
||||
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
||||
|
||||
if (isSubclass(thereTy, hereTy))
|
||||
{
|
||||
|
@ -1294,7 +1294,7 @@ void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedE
|
|||
// If the incoming class is a superclass of one of the
|
||||
// negations, then the negation no longer applies and must
|
||||
// be removed. This is also true if they are equal. Since
|
||||
// extern types are, at this time, entirely persistent (we do not
|
||||
// classes are, at this time, entirely persistent (we do not
|
||||
// clone them), a pointer identity check is sufficient.
|
||||
else if (isSubclass(hereNegateTy, thereTy))
|
||||
{
|
||||
|
@ -1319,17 +1319,17 @@ void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedE
|
|||
else if (isSubclass(hereTy, thereTy))
|
||||
{
|
||||
TypeIds negations = std::move(hereNegations);
|
||||
unionExternTypes(negations, thereNegations);
|
||||
unionClasses(negations, thereNegations);
|
||||
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
heres.pushPair(thereTy, std::move(negations));
|
||||
insert = false;
|
||||
break;
|
||||
}
|
||||
else if (hereTy == thereTy)
|
||||
{
|
||||
unionExternTypes(hereNegations, thereNegations);
|
||||
unionClasses(hereNegations, thereNegations);
|
||||
insert = false;
|
||||
break;
|
||||
}
|
||||
|
@ -1690,7 +1690,7 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
|||
return NormalizationResult::HitLimits;
|
||||
|
||||
here.booleans = unionOfBools(here.booleans, there.booleans);
|
||||
unionExternTypes(here.externTypes, there.externTypes);
|
||||
unionClasses(here.classes, there.classes);
|
||||
|
||||
here.errors = (get<NeverType>(there.errors) ? here.errors : there.errors);
|
||||
here.nils = (get<NeverType>(there.nils) ? here.nils : there.nils);
|
||||
|
@ -1830,8 +1830,8 @@ NormalizationResult Normalizer::unionNormalWithTy(
|
|||
unionFunctionsWithFunction(here.functions, there);
|
||||
else if (get<TableType>(there) || get<MetatableType>(there))
|
||||
unionTablesWithTable(here.tables, there);
|
||||
else if (get<ExternType>(there))
|
||||
unionExternTypesWithExternType(here.externTypes, there);
|
||||
else if (get<ClassType>(there))
|
||||
unionClassesWithClass(here.classes, there);
|
||||
else if (get<ErrorType>(there))
|
||||
here.errors = 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;
|
||||
}
|
||||
|
||||
if (here.externTypes.isNever())
|
||||
if (here.classes.isNever())
|
||||
{
|
||||
resetToTop(builtinTypes, result.externTypes);
|
||||
resetToTop(builtinTypes, result.classes);
|
||||
}
|
||||
else if (isTop(builtinTypes, result.externTypes))
|
||||
else if (isTop(builtinTypes, result.classes))
|
||||
{
|
||||
result.externTypes.resetToNever();
|
||||
result.classes.resetToNever();
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeIds rootNegations{};
|
||||
|
||||
for (const auto& [hereParent, hereNegations] : here.externTypes.externTypes)
|
||||
for (const auto& [hereParent, hereNegations] : here.classes.classes)
|
||||
{
|
||||
if (hereParent != builtinTypes->externType)
|
||||
if (hereParent != builtinTypes->classType)
|
||||
rootNegations.insert(hereParent);
|
||||
|
||||
for (TypeId hereNegation : hereNegations)
|
||||
unionExternTypesWithExternType(result.externTypes, hereNegation);
|
||||
unionClassesWithClass(result.classes, hereNegation);
|
||||
}
|
||||
|
||||
if (!rootNegations.empty())
|
||||
result.externTypes.pushPair(builtinTypes->externType, rootNegations);
|
||||
result.classes.pushPair(builtinTypes->classType, rootNegations);
|
||||
}
|
||||
|
||||
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
|
||||
|
@ -2144,7 +2144,7 @@ TypeId Normalizer::intersectionOfBools(TypeId here, TypeId there)
|
|||
return there;
|
||||
}
|
||||
|
||||
void Normalizer::intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
|
||||
void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
||||
{
|
||||
if (theres.isNever())
|
||||
{
|
||||
|
@ -2178,12 +2178,12 @@ void Normalizer::intersectExternTypes(NormalizedExternType& heres, const Normali
|
|||
// declare the result of the intersection operation to be never.
|
||||
for (const TypeId thereTy : theres.ordering)
|
||||
{
|
||||
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
|
||||
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
||||
|
||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||
{
|
||||
TypeId hereTy = *it;
|
||||
TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||
TypeIds& hereNegations = heres.classes.at(hereTy);
|
||||
|
||||
if (isSubclass(thereTy, hereTy))
|
||||
{
|
||||
|
@ -2206,10 +2206,10 @@ void Normalizer::intersectExternTypes(NormalizedExternType& heres, const Normali
|
|||
}
|
||||
}
|
||||
|
||||
unionExternTypes(negations, thereNegations);
|
||||
unionClasses(negations, thereNegations);
|
||||
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
heres.pushPair(thereTy, std::move(negations));
|
||||
break;
|
||||
}
|
||||
|
@ -2234,15 +2234,15 @@ void Normalizer::intersectExternTypes(NormalizedExternType& heres, const Normali
|
|||
{
|
||||
if (isSubclass(hereTy, *nIt))
|
||||
{
|
||||
// eg SomeExternType & (class & ~SomeExternType)
|
||||
// or SomeExternType & (class & ~ParentExternType)
|
||||
heres.externTypes.erase(hereTy);
|
||||
// eg SomeClass & (class & ~SomeClass)
|
||||
// or SomeClass & (class & ~ParentClass)
|
||||
heres.classes.erase(hereTy);
|
||||
it = heres.ordering.erase(it);
|
||||
erasedHere = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// eg SomeExternType & (class & ~Unrelated)
|
||||
// eg SomeClass & (class & ~Unrelated)
|
||||
if (!isSubclass(*nIt, hereTy))
|
||||
nIt = negations.erase(nIt);
|
||||
else
|
||||
|
@ -2251,30 +2251,30 @@ void Normalizer::intersectExternTypes(NormalizedExternType& heres, const Normali
|
|||
|
||||
if (!erasedHere)
|
||||
{
|
||||
unionExternTypes(hereNegations, negations);
|
||||
unionClasses(hereNegations, negations);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else if (hereTy == thereTy)
|
||||
{
|
||||
unionExternTypes(hereNegations, thereNegations);
|
||||
unionClasses(hereNegations, thereNegations);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
|
||||
void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId there)
|
||||
{
|
||||
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
||||
{
|
||||
TypeId hereTy = *it;
|
||||
const TypeIds& hereNegations = heres.externTypes.at(hereTy);
|
||||
const TypeIds& hereNegations = heres.classes.at(hereTy);
|
||||
|
||||
// If the incoming class _is_ the current class, we skip it. Maybe
|
||||
// another entry will have a different story. We check for this first
|
||||
|
@ -2319,7 +2319,7 @@ void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres,
|
|||
}
|
||||
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
if (!emptyIntersectWithNegation)
|
||||
heres.pushPair(there, std::move(negations));
|
||||
break;
|
||||
|
@ -2335,7 +2335,7 @@ void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres,
|
|||
else
|
||||
{
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3083,7 +3083,7 @@ NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const Nor
|
|||
|
||||
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
||||
|
||||
intersectExternTypes(here.externTypes, there.externTypes);
|
||||
intersectClasses(here.classes, there.classes);
|
||||
here.errors = (get<NeverType>(there.errors) ? there.errors : here.errors);
|
||||
here.nils = (get<NeverType>(there.nils) ? there.nils : here.nils);
|
||||
here.numbers = (get<NeverType>(there.numbers) ? there.numbers : here.numbers);
|
||||
|
@ -3205,12 +3205,12 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
|
||||
here.tables = std::move(tables);
|
||||
}
|
||||
else if (get<ExternType>(there))
|
||||
else if (get<ClassType>(there))
|
||||
{
|
||||
NormalizedExternType nct = std::move(here.externTypes);
|
||||
NormalizedClassType nct = std::move(here.classes);
|
||||
clearNormal(here);
|
||||
intersectExternTypesWithExternType(nct, there);
|
||||
here.externTypes = std::move(nct);
|
||||
intersectClassesWithClass(nct, there);
|
||||
here.classes = std::move(nct);
|
||||
}
|
||||
else if (get<ErrorType>(there))
|
||||
{
|
||||
|
@ -3274,7 +3274,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
subtractPrimitive(here, ntv->ty);
|
||||
else if (const SingletonType* stv = get<SingletonType>(t))
|
||||
subtractSingleton(here, follow(ntv->ty));
|
||||
else if (get<ExternType>(t))
|
||||
else if (get<ClassType>(t))
|
||||
{
|
||||
NormalizationResult res = intersectNormalWithNegationTy(t, here);
|
||||
if (shouldEarlyExit(res))
|
||||
|
@ -3334,7 +3334,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
}
|
||||
else if (get<NeverType>(there))
|
||||
{
|
||||
here.externTypes.resetToNever();
|
||||
here.classes.resetToNever();
|
||||
}
|
||||
else if (get<NoRefineType>(there))
|
||||
{
|
||||
|
@ -3403,18 +3403,18 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
|||
if (!get<NeverType>(norm.booleans))
|
||||
result.push_back(norm.booleans);
|
||||
|
||||
if (isTop(builtinTypes, norm.externTypes))
|
||||
if (isTop(builtinTypes, norm.classes))
|
||||
{
|
||||
result.push_back(builtinTypes->externType);
|
||||
result.push_back(builtinTypes->classType);
|
||||
}
|
||||
else if (!norm.externTypes.isNever())
|
||||
else if (!norm.classes.isNever())
|
||||
{
|
||||
std::vector<TypeId> parts;
|
||||
parts.reserve(norm.externTypes.externTypes.size());
|
||||
parts.reserve(norm.classes.classes.size());
|
||||
|
||||
for (const TypeId normTy : norm.externTypes.ordering)
|
||||
for (const TypeId normTy : norm.classes.ordering)
|
||||
{
|
||||
const TypeIds& normNegations = norm.externTypes.externTypes.at(normTy);
|
||||
const TypeIds& normNegations = norm.classes.classes.at(normTy);
|
||||
|
||||
if (normNegations.empty())
|
||||
{
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "Luau/Ast.h"
|
||||
#include "Luau/Module.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -67,12 +65,6 @@ struct RequireTracer : AstVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
// allow resolving require inside `typeof` annotations
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
AstExpr* getDependent_DEPRECATED(AstExpr* node)
|
||||
{
|
||||
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypePairHash.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
@ -18,7 +17,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationRecheckAssumption)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -318,13 +316,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (get<AnyType>(right))
|
||||
return Relation::Subset;
|
||||
|
||||
if (get<UnknownType>(right))
|
||||
else if (get<UnknownType>(right))
|
||||
return Relation::Coincident;
|
||||
|
||||
if (get<ErrorType>(right))
|
||||
else if (get<ErrorType>(right))
|
||||
return Relation::Disjoint;
|
||||
|
||||
else
|
||||
return Relation::Superset;
|
||||
}
|
||||
|
||||
|
@ -335,7 +331,7 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (get<AnyType>(right))
|
||||
return Relation::Coincident;
|
||||
|
||||
else
|
||||
return Relation::Superset;
|
||||
}
|
||||
|
||||
|
@ -360,7 +356,7 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
// * FunctionType
|
||||
// * TableType
|
||||
// * MetatableType
|
||||
// * ExternType
|
||||
// * ClassType
|
||||
// * UnionType
|
||||
// * IntersectionType
|
||||
// * NegationType
|
||||
|
@ -368,33 +364,26 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
if (isTypeVariable(left) || isTypeVariable(right))
|
||||
return Relation::Intersects;
|
||||
|
||||
if (FFlag::LuauSimplificationTableExternType)
|
||||
{
|
||||
// if either type is a type function, we cannot know if they'll be related.
|
||||
if (get<TypeFunctionInstanceType>(left) || get<TypeFunctionInstanceType>(right))
|
||||
return Relation::Intersects;
|
||||
}
|
||||
|
||||
if (get<ErrorType>(left))
|
||||
{
|
||||
if (get<ErrorType>(right))
|
||||
return Relation::Coincident;
|
||||
else if (get<AnyType>(right))
|
||||
return Relation::Subset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
else if (get<ErrorType>(right))
|
||||
if (get<ErrorType>(right))
|
||||
return flip(relate(right, left, seen));
|
||||
|
||||
if (get<NeverType>(left))
|
||||
{
|
||||
if (get<NeverType>(right))
|
||||
return Relation::Coincident;
|
||||
|
||||
else
|
||||
return Relation::Subset;
|
||||
}
|
||||
else if (get<NeverType>(right))
|
||||
if (get<NeverType>(right))
|
||||
return flip(relate(right, left, seen));
|
||||
|
||||
if (auto ut = get<IntersectionType>(left))
|
||||
|
@ -458,7 +447,7 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (lp->type == rp->type)
|
||||
return Relation::Coincident;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
|
@ -466,10 +455,9 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (lp->type == PrimitiveType::String && rs->variant.get_if<StringSingleton>())
|
||||
return Relation::Superset;
|
||||
|
||||
if (lp->type == PrimitiveType::Boolean && rs->variant.get_if<BooleanSingleton>())
|
||||
else if (lp->type == PrimitiveType::Boolean && rs->variant.get_if<BooleanSingleton>())
|
||||
return Relation::Superset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
|
@ -477,34 +465,33 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (get<FunctionType>(right))
|
||||
return Relation::Superset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
if (lp->type == PrimitiveType::Table)
|
||||
{
|
||||
if (get<TableType>(right))
|
||||
return Relation::Superset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ClassType>(right))
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (auto ls = get<SingletonType>(left))
|
||||
{
|
||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ClassType>(right))
|
||||
return Relation::Disjoint;
|
||||
|
||||
if (get<PrimitiveType>(right))
|
||||
return flip(relate(right, left, seen));
|
||||
|
||||
if (auto rs = get<SingletonType>(right))
|
||||
{
|
||||
if (ls->variant == rs->variant)
|
||||
return Relation::Coincident;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
}
|
||||
|
@ -515,10 +502,10 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (rp->type == PrimitiveType::Function)
|
||||
return Relation::Subset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
else
|
||||
return Relation::Intersects;
|
||||
}
|
||||
|
||||
|
@ -528,11 +515,10 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (rp->type == PrimitiveType::Table)
|
||||
return Relation::Subset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (auto rt = get<TableType>(right))
|
||||
else if (auto rt = get<TableType>(right))
|
||||
{
|
||||
// TODO PROBABLY indexers and metatables.
|
||||
if (1 == rt->props.size())
|
||||
|
@ -552,57 +538,28 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
*/
|
||||
if (lt->props.size() > 1 && r == Relation::Superset)
|
||||
return Relation::Intersects;
|
||||
|
||||
else
|
||||
return r;
|
||||
}
|
||||
|
||||
if (1 == lt->props.size())
|
||||
else if (1 == lt->props.size())
|
||||
return flip(relate(right, left, seen));
|
||||
|
||||
else
|
||||
return Relation::Intersects;
|
||||
}
|
||||
|
||||
if (FFlag::LuauSimplificationTableExternType)
|
||||
{
|
||||
if (auto re = get<ExternType>(right))
|
||||
{
|
||||
Relation overall = Relation::Coincident;
|
||||
|
||||
for (auto& [name, prop] : lt->props)
|
||||
{
|
||||
if (auto propInExternType = re->props.find(name); propInExternType != re->props.end())
|
||||
{
|
||||
Relation propRel = relate(prop.type(), propInExternType->second.type());
|
||||
|
||||
if (propRel == Relation::Disjoint)
|
||||
return Relation::Disjoint;
|
||||
|
||||
if (propRel == Relation::Coincident)
|
||||
continue;
|
||||
|
||||
overall = Relation::Intersects;
|
||||
}
|
||||
}
|
||||
|
||||
return overall;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO metatables
|
||||
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (auto ct = get<ExternType>(left))
|
||||
if (auto ct = get<ClassType>(left))
|
||||
{
|
||||
if (auto rct = get<ExternType>(right))
|
||||
if (auto rct = get<ClassType>(right))
|
||||
{
|
||||
if (isSubclass(ct, rct))
|
||||
return Relation::Subset;
|
||||
|
||||
if (isSubclass(rct, ct))
|
||||
else if (isSubclass(rct, ct))
|
||||
return Relation::Superset;
|
||||
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
|
@ -916,7 +873,7 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) co
|
|||
if (is<NeverType, ErrorType>(target))
|
||||
return target;
|
||||
|
||||
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
||||
return target;
|
||||
|
||||
if (auto pt = get<PrimitiveType>(target))
|
||||
|
@ -952,7 +909,7 @@ std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) con
|
|||
if (is<UnknownType>(target))
|
||||
return builtinTypes->falsyType;
|
||||
|
||||
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||
if (is<FunctionType, TableType, MetatableType, ClassType>(target))
|
||||
return builtinTypes->neverType;
|
||||
|
||||
if (auto pt = get<PrimitiveType>(target))
|
||||
|
|
|
@ -136,9 +136,9 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
|||
clone.parts = a.parts;
|
||||
return dest.addType(std::move(clone));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ExternType>)
|
||||
else if constexpr (std::is_same_v<T, ClassType>)
|
||||
{
|
||||
ExternType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
||||
ClassType 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));
|
||||
}
|
||||
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)
|
||||
visitChild(a);
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(ty))
|
||||
else if (const ClassType* ctv = get<ClassType>(ty))
|
||||
{
|
||||
for (const auto& [name, prop] : etv->props)
|
||||
for (const auto& [name, prop] : ctv->props)
|
||||
visitChild(prop.type());
|
||||
|
||||
if (etv->parent)
|
||||
visitChild(*etv->parent);
|
||||
if (ctv->parent)
|
||||
visitChild(*ctv->parent);
|
||||
|
||||
if (etv->metatable)
|
||||
visitChild(*etv->metatable);
|
||||
if (ctv->metatable)
|
||||
visitChild(*ctv->metatable);
|
||||
|
||||
if (etv->indexer)
|
||||
if (ctv->indexer)
|
||||
{
|
||||
visitChild(etv->indexer->indexType);
|
||||
visitChild(etv->indexer->indexResultType);
|
||||
visitChild(ctv->indexer->indexType);
|
||||
visitChild(ctv->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
else if (const NegationType* ntv = get<NegationType>(ty))
|
||||
|
@ -838,21 +838,21 @@ void Substitution::replaceChildren(TypeId ty)
|
|||
for (TypePackId& a : tfit->packArguments)
|
||||
a = replace(a);
|
||||
}
|
||||
else if (ExternType* etv = getMutable<ExternType>(ty))
|
||||
else if (ClassType* ctv = getMutable<ClassType>(ty))
|
||||
{
|
||||
for (auto& [name, prop] : etv->props)
|
||||
for (auto& [name, prop] : ctv->props)
|
||||
prop.setType(replace(prop.type()));
|
||||
|
||||
if (etv->parent)
|
||||
etv->parent = replace(*etv->parent);
|
||||
if (ctv->parent)
|
||||
ctv->parent = replace(*ctv->parent);
|
||||
|
||||
if (etv->metatable)
|
||||
etv->metatable = replace(*etv->metatable);
|
||||
if (ctv->metatable)
|
||||
ctv->metatable = replace(*ctv->metatable);
|
||||
|
||||
if (etv->indexer)
|
||||
if (ctv->indexer)
|
||||
{
|
||||
etv->indexer->indexType = replace(etv->indexer->indexType);
|
||||
etv->indexer->indexResultType = replace(etv->indexer->indexResultType);
|
||||
ctv->indexer->indexType = replace(ctv->indexer->indexType);
|
||||
ctv->indexer->indexResultType = replace(ctv->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
else if (NegationType* ntv = getMutable<NegationType>(ty))
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
#include "Luau/Normalize.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/Substitution.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypePath.h"
|
||||
|
@ -31,7 +33,7 @@ struct VarianceFlipper
|
|||
Subtyping::Variance* variance;
|
||||
Subtyping::Variance oldValue;
|
||||
|
||||
explicit VarianceFlipper(Subtyping::Variance* v)
|
||||
VarianceFlipper(Subtyping::Variance* v)
|
||||
: variance(v)
|
||||
, oldValue(*v)
|
||||
{
|
||||
|
@ -313,7 +315,7 @@ struct ApplyMappedGenerics : Substitution
|
|||
|
||||
bool ignoreChildren(TypeId ty) override
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return ty->persistent;
|
||||
|
@ -742,9 +744,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<MetatableType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<ExternType, ExternType>(subTy, superTy))
|
||||
else if (auto p = get2<ClassType, ClassType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<ExternType, TableType>(subTy, superTy))
|
||||
else if (auto p = get2<ClassType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, subTy, p.first, superTy, p.second, scope);
|
||||
else if (auto p = get2<TableType, PrimitiveType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p, scope);
|
||||
|
@ -1334,7 +1336,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
|||
}
|
||||
// the top class type is not actually a primitive type, so the negation of
|
||||
// any one of them includes the top class type.
|
||||
else if (auto p = get2<ExternType, PrimitiveType>(subTy, negatedTy))
|
||||
else if (auto p = get2<ClassType, PrimitiveType>(subTy, negatedTy))
|
||||
result = {true};
|
||||
else if (auto p = get<PrimitiveType>(negatedTy); p && is<TableType, MetatableType>(subTy))
|
||||
result = {p->type != PrimitiveType::Table};
|
||||
|
@ -1342,9 +1344,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
|||
result = {p.second->type != PrimitiveType::Function};
|
||||
else if (auto p = get2<SingletonType, SingletonType>(subTy, negatedTy))
|
||||
result = {*p.first != *p.second};
|
||||
else if (auto p = get2<ExternType, ExternType>(subTy, negatedTy))
|
||||
else if (auto p = get2<ClassType, ClassType>(subTy, negatedTy))
|
||||
result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second, scope));
|
||||
else if (get2<FunctionType, ExternType>(subTy, negatedTy))
|
||||
else if (get2<FunctionType, ClassType>(subTy, negatedTy))
|
||||
result = {true};
|
||||
else if (is<ErrorType, FunctionType, TableType, MetatableType>(negatedTy))
|
||||
iceReporter->ice("attempting to negate a non-testable type");
|
||||
|
@ -1469,15 +1471,15 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
|||
}
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope)
|
||||
{
|
||||
return {isSubclass(subExternType, superExternType)};
|
||||
return {isSubclass(subClass, superClass)};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
TypeId subTy,
|
||||
const ExternType* subExternType,
|
||||
const ClassType* subClass,
|
||||
TypeId superTy,
|
||||
const TableType* superTable,
|
||||
NotNull<Scope> scope
|
||||
|
@ -1489,7 +1491,7 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
|
||||
for (const auto& [name, prop] : superTable->props)
|
||||
{
|
||||
if (auto classProp = lookupExternTypeProp(subExternType, name))
|
||||
if (auto classProp = lookupClassProp(subClass, name))
|
||||
{
|
||||
result.andAlso(isCovariantWith(env, *classProp, prop, name, scope));
|
||||
}
|
||||
|
@ -1659,7 +1661,7 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
||||
result.andAlso(
|
||||
isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope).orElse(isCovariantWith(env, subNorm->externTypes, superNorm->tables, scope))
|
||||
isCovariantWith(env, subNorm->classes, superNorm->classes, scope).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables, scope))
|
||||
);
|
||||
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
||||
|
@ -1676,24 +1678,24 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedExternType& subExternType,
|
||||
const NormalizedExternType& superExternType,
|
||||
const NormalizedClassType& subClass,
|
||||
const NormalizedClassType& superClass,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
for (const auto& [subExternTypeTy, _] : subExternType.externTypes)
|
||||
for (const auto& [subClassTy, _] : subClass.classes)
|
||||
{
|
||||
SubtypingResult result;
|
||||
|
||||
for (const auto& [superExternTypeTy, superNegations] : superExternType.externTypes)
|
||||
for (const auto& [superClassTy, superNegations] : superClass.classes)
|
||||
{
|
||||
result.orElse(isCovariantWith(env, subExternTypeTy, superExternTypeTy, scope));
|
||||
result.orElse(isCovariantWith(env, subClassTy, superClassTy, scope));
|
||||
if (!result.isSubtype)
|
||||
continue;
|
||||
|
||||
for (TypeId negation : superNegations)
|
||||
{
|
||||
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subExternTypeTy, negation, scope)));
|
||||
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation, scope)));
|
||||
if (result.isSubtype)
|
||||
break;
|
||||
}
|
||||
|
@ -1708,17 +1710,17 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedExternType& subExternType,
|
||||
const NormalizedClassType& subClass,
|
||||
const TypeIds& superTables,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
for (const auto& [subExternTypeTy, _] : subExternType.externTypes)
|
||||
for (const auto& [subClassTy, _] : subClass.classes)
|
||||
{
|
||||
SubtypingResult result;
|
||||
|
||||
for (TypeId superTableTy : superTables)
|
||||
result.orElse(isCovariantWith(env, subExternTypeTy, superTableTy, scope));
|
||||
result.orElse(isCovariantWith(env, subClassTy, superTableTy, scope));
|
||||
|
||||
if (!result.isSubtype)
|
||||
return result;
|
||||
|
|
|
@ -299,9 +299,9 @@ void StateDot::visitChildren(TypeId ty, int index)
|
|||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ExternType>)
|
||||
else if constexpr (std::is_same_v<T, ClassType>)
|
||||
{
|
||||
formatAppend(result, "ExternType %s", t.name.c_str());
|
||||
formatAppend(result, "ClassType %s", t.name.c_str());
|
||||
finishNodeLabel(ty);
|
||||
finishNode();
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
||||
|
@ -123,7 +121,7 @@ struct FindCyclicTypes final : TypeVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -722,13 +720,7 @@ struct TypeStringifier
|
|||
if (ttv.boundTo)
|
||||
return stringify(*ttv.boundTo);
|
||||
|
||||
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 (!state.exhaustive)
|
||||
{
|
||||
if (ttv.name)
|
||||
{
|
||||
|
@ -751,10 +743,6 @@ struct TypeStringifier
|
|||
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.exhaustive)
|
||||
{
|
||||
if (ttv.syntheticName)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
|
@ -893,9 +881,9 @@ struct TypeStringifier
|
|||
state.emit(" }");
|
||||
}
|
||||
|
||||
void operator()(TypeId, const ExternType& etv)
|
||||
void operator()(TypeId, const ClassType& ctv)
|
||||
{
|
||||
state.emit(etv.name);
|
||||
state.emit(ctv.name);
|
||||
}
|
||||
|
||||
void operator()(TypeId, const AnyType&)
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -299,11 +297,6 @@ struct ArcCollector : public AstVisitor
|
|||
add(*name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
};
|
||||
|
||||
struct ContainsFunctionCall : public AstVisitor
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -331,7 +331,7 @@ struct Printer_DEPRECATED
|
|||
}
|
||||
}
|
||||
|
||||
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true)
|
||||
void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg)
|
||||
{
|
||||
advance(annotation.location.begin);
|
||||
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||
|
@ -349,7 +349,7 @@ struct Printer_DEPRECATED
|
|||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||
{
|
||||
LUAU_ASSERT(!forVarArg);
|
||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||
visualizeTypeList(explicitTp->typeList, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -705,6 +705,8 @@ struct Printer_DEPRECATED
|
|||
writer.keyword("do");
|
||||
for (const auto& s : block->body)
|
||||
visualize(*s);
|
||||
if (!FFlag::LuauFixDoBlockEndLocation)
|
||||
writer.advance(block->location.end);
|
||||
writeEnd(program.location);
|
||||
}
|
||||
else if (const auto& a = program.as<AstStatIf>())
|
||||
|
@ -1063,15 +1065,12 @@ struct Printer_DEPRECATED
|
|||
|
||||
writer.symbol(")");
|
||||
|
||||
if (writeTypes && (FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value()))
|
||||
if (writeTypes && func.returnAnnotation)
|
||||
{
|
||||
writer.symbol(":");
|
||||
writer.space();
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
||||
else
|
||||
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
||||
visualizeTypeList(*func.returnAnnotation, false);
|
||||
}
|
||||
|
||||
visualizeBlock(*func.body);
|
||||
|
@ -1175,10 +1174,7 @@ struct Printer_DEPRECATED
|
|||
}
|
||||
|
||||
writer.symbol("->");
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visualizeTypePackAnnotation(*a->returnTypes, false);
|
||||
else
|
||||
visualizeTypeList(a->returnTypes_DEPRECATED, true);
|
||||
visualizeTypeList(a->returnTypes, true);
|
||||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
|
||||
{
|
||||
|
@ -1372,7 +1368,7 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
void visualizeTypePackAnnotation(AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true)
|
||||
void visualizeTypePackAnnotation(AstTypePack& annotation, bool forVarArg)
|
||||
{
|
||||
advance(annotation.location.begin);
|
||||
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
|
||||
|
@ -1394,10 +1390,10 @@ struct Printer
|
|||
LUAU_ASSERT(!forVarArg);
|
||||
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
||||
visualizeTypeList(
|
||||
explicitTp->typeList, FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
||||
explicitTp->typeList, true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
||||
);
|
||||
else
|
||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||
visualizeTypeList(explicitTp->typeList, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2320,6 +2316,8 @@ struct Printer
|
|||
{
|
||||
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
||||
|
||||
// TODO(CLI-139347): need to handle return type (incl. parentheses of return type)
|
||||
|
||||
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
||||
|
@ -2385,23 +2383,14 @@ struct Printer
|
|||
advanceBefore(func.argLocation->end, 1);
|
||||
writer.symbol(")");
|
||||
|
||||
if (writeTypes && FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value())
|
||||
if (writeTypes && func.returnAnnotation)
|
||||
{
|
||||
if (cstNode)
|
||||
advance(cstNode->returnSpecifierPosition);
|
||||
writer.symbol(":");
|
||||
writer.space();
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (!cstNode)
|
||||
writer.space();
|
||||
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.space();
|
||||
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
||||
}
|
||||
visualizeTypeList(*func.returnAnnotation, false);
|
||||
}
|
||||
|
||||
visualizeBlock(*func.body);
|
||||
|
@ -2584,10 +2573,7 @@ struct Printer
|
|||
if (cstNode)
|
||||
advance(cstNode->returnArrowPosition);
|
||||
writer.symbol("->");
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visualizeTypePackAnnotation(*a->returnTypes, false);
|
||||
else
|
||||
visualizeTypeList(a->returnTypes_DEPRECATED, true);
|
||||
visualizeTypeList(a->returnTypes, true);
|
||||
}
|
||||
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))
|
||||
return mtType->metatable;
|
||||
else if (const ExternType* externType = get<ExternType>(type))
|
||||
return externType->metatable;
|
||||
else if (const ClassType* classType = get<ClassType>(type))
|
||||
return classType->metatable;
|
||||
else if (isString(type))
|
||||
{
|
||||
auto ptv = get<PrimitiveType>(builtinTypes->stringType);
|
||||
|
@ -346,10 +346,10 @@ std::optional<ModuleName> getDefinitionModuleName(TypeId type)
|
|||
if (ftv->definition)
|
||||
return ftv->definition->definitionModuleName;
|
||||
}
|
||||
else if (auto etv = get<ExternType>(type))
|
||||
else if (auto ctv = get<ClassType>(type))
|
||||
{
|
||||
if (!etv->definitionModuleName.empty())
|
||||
return etv->definitionModuleName;
|
||||
if (!ctv->definitionModuleName.empty())
|
||||
return ctv->definitionModuleName;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
@ -506,6 +506,31 @@ FreeType::FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId uppe
|
|||
{
|
||||
}
|
||||
|
||||
// Old constructors
|
||||
FreeType::FreeType(TypeLevel level)
|
||||
: index(Unifiable::freshIndex())
|
||||
, level(level)
|
||||
, scope(nullptr)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
||||
}
|
||||
|
||||
FreeType::FreeType(Scope* scope)
|
||||
: index(Unifiable::freshIndex())
|
||||
, level{}
|
||||
, scope(scope)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
||||
}
|
||||
|
||||
FreeType::FreeType(Scope* scope, TypeLevel level)
|
||||
: index(Unifiable::freshIndex())
|
||||
, level(level)
|
||||
, scope(scope)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
||||
}
|
||||
|
||||
GenericType::GenericType()
|
||||
: index(Unifiable::freshIndex())
|
||||
, name("g" + std::to_string(index))
|
||||
|
@ -989,7 +1014,7 @@ BuiltinTypes::BuiltinTypes()
|
|||
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
||||
, bufferType(arena->addType(Type{PrimitiveType{PrimitiveType::Buffer}, /*persistent*/ true}))
|
||||
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
||||
, externType(arena->addType(Type{ExternType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}, {}}, /*persistent*/ true}))
|
||||
, classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}, {}}, /*persistent*/ true}))
|
||||
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
||||
, emptyTableType(arena->addType(Type{TableType{TableState::Sealed, TypeLevel{}, nullptr}, /*persistent*/ true}))
|
||||
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
||||
|
@ -1001,7 +1026,6 @@ BuiltinTypes::BuiltinTypes()
|
|||
, noRefineType(arena->addType(Type{NoRefineType{}, /*persistent*/ true}))
|
||||
, falsyType(arena->addType(Type{UnionType{{falseType, nilType}}, /*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}))
|
||||
, optionalStringType(arena->addType(Type{UnionType{{stringType, nilType}}, /*persistent*/ true}))
|
||||
, emptyTypePack(arena->addTypePack(TypePackVar{TypePack{{}}, /*persistent*/ true}))
|
||||
|
@ -1080,9 +1104,9 @@ void persist(TypeId ty)
|
|||
queue.push_back(ttv->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
else if (auto etv= get<ExternType>(t))
|
||||
else if (auto ctv = get<ClassType>(t))
|
||||
{
|
||||
for (const auto& [_name, prop] : etv->props)
|
||||
for (const auto& [_name, prop] : ctv->props)
|
||||
queue.push_back(prop.type());
|
||||
}
|
||||
else if (auto utv = get<UnionType>(t))
|
||||
|
@ -1182,7 +1206,7 @@ std::optional<TypeLevel> getLevel(TypePackId tp)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Property* lookupExternTypeProp(const ExternType* cls, const Name& name)
|
||||
const Property* lookupClassProp(const ClassType* cls, const Name& name)
|
||||
{
|
||||
while (cls)
|
||||
{
|
||||
|
@ -1191,7 +1215,7 @@ const Property* lookupExternTypeProp(const ExternType* cls, const Name& name)
|
|||
return &it->second;
|
||||
|
||||
if (cls->parent)
|
||||
cls = get<ExternType>(*cls->parent);
|
||||
cls = get<ClassType>(*cls->parent);
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
|
@ -1201,7 +1225,7 @@ const Property* lookupExternTypeProp(const ExternType* cls, const Name& name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool isSubclass(const ExternType* cls, const ExternType* parent)
|
||||
bool isSubclass(const ClassType* cls, const ClassType* parent)
|
||||
{
|
||||
while (cls)
|
||||
{
|
||||
|
@ -1210,7 +1234,7 @@ bool isSubclass(const ExternType* cls, const ExternType* parent)
|
|||
else if (!cls->parent)
|
||||
return false;
|
||||
|
||||
cls = get<ExternType>(*cls->parent);
|
||||
cls = get<ClassType>(*cls->parent);
|
||||
LUAU_ASSERT(cls);
|
||||
}
|
||||
|
||||
|
@ -1279,8 +1303,8 @@ static Tags* getTags(TypeId ty)
|
|||
return &ftv->tags;
|
||||
else if (auto ttv = getMutable<TableType>(ty))
|
||||
return &ttv->tags;
|
||||
else if (auto etv = getMutable<ExternType>(ty))
|
||||
return &etv->tags;
|
||||
else if (auto ctv = getMutable<ClassType>(ty))
|
||||
return &ctv->tags;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1310,19 +1334,19 @@ bool hasTag(TypeId ty, const std::string& tagName)
|
|||
{
|
||||
ty = follow(ty);
|
||||
|
||||
// We special case extern types because getTags only returns a pointer to one vector of tags.
|
||||
// But extern types has multiple vector of tags, represented throughout the hierarchy.
|
||||
if (auto etv = get<ExternType>(ty))
|
||||
// We special case classes because getTags only returns a pointer to one vector of tags.
|
||||
// But classes has multiple vector of tags, represented throughout the hierarchy.
|
||||
if (auto ctv = get<ClassType>(ty))
|
||||
{
|
||||
while (etv)
|
||||
while (ctv)
|
||||
{
|
||||
if (hasTag(etv->tags, tagName))
|
||||
if (hasTag(ctv->tags, tagName))
|
||||
return true;
|
||||
else if (!etv->parent)
|
||||
else if (!ctv->parent)
|
||||
return false;
|
||||
|
||||
etv = get<ExternType>(*etv->parent);
|
||||
LUAU_ASSERT(etv);
|
||||
ctv = get<ClassType>(*ctv->parent);
|
||||
LUAU_ASSERT(ctv);
|
||||
}
|
||||
}
|
||||
else if (auto tags = getTags(ty))
|
||||
|
|
|
@ -50,6 +50,33 @@ TypeId TypeArena::freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLe
|
|||
return allocated;
|
||||
}
|
||||
|
||||
TypeId TypeArena::freshType_DEPRECATED(TypeLevel level)
|
||||
{
|
||||
TypeId allocated = types.allocate(FreeType{level});
|
||||
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
TypeId TypeArena::freshType_DEPRECATED(Scope* scope)
|
||||
{
|
||||
TypeId allocated = types.allocate(FreeType{scope});
|
||||
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
TypeId TypeArena::freshType_DEPRECATED(Scope* scope, TypeLevel level)
|
||||
{
|
||||
TypeId allocated = types.allocate(FreeType{scope, level});
|
||||
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
TypePackId TypeArena::freshTypePack(Scope* scope, Polarity polarity)
|
||||
{
|
||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope, polarity});
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <string>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||
{
|
||||
|
@ -220,21 +219,21 @@ public:
|
|||
return Luau::visit(*this, mtv.table->ty);
|
||||
}
|
||||
|
||||
AstType* operator()(const ExternType& etv)
|
||||
AstType* operator()(const ClassType& ctv)
|
||||
{
|
||||
RecursionCounter counter(&count);
|
||||
|
||||
char* name = allocateString(*allocator, etv.name);
|
||||
char* name = allocateString(*allocator, ctv.name);
|
||||
|
||||
if (!options.expandExternTypeProps || hasSeen(&etv) || count > 1)
|
||||
if (!options.expandClassProps || hasSeen(&ctv) || count > 1)
|
||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{name}, std::nullopt, Location());
|
||||
|
||||
AstArray<AstTableProp> props;
|
||||
props.size = etv.props.size();
|
||||
props.size = ctv.props.size();
|
||||
props.data = static_cast<AstTableProp*>(allocator->allocate(sizeof(AstTableProp) * props.size));
|
||||
|
||||
int idx = 0;
|
||||
for (const auto& [propName, prop] : etv.props)
|
||||
for (const auto& [propName, prop] : ctv.props)
|
||||
{
|
||||
char* name = allocateString(*allocator, propName);
|
||||
|
||||
|
@ -245,13 +244,13 @@ public:
|
|||
}
|
||||
|
||||
AstTableIndexer* indexer = nullptr;
|
||||
if (etv.indexer)
|
||||
if (ctv.indexer)
|
||||
{
|
||||
RecursionCounter counter(&count);
|
||||
|
||||
indexer = allocator->alloc<AstTableIndexer>();
|
||||
indexer->indexType = Luau::visit(*this, etv.indexer->indexType->ty);
|
||||
indexer->resultType = Luau::visit(*this, etv.indexer->indexResultType->ty);
|
||||
indexer->indexType = Luau::visit(*this, ctv.indexer->indexType->ty);
|
||||
indexer->resultType = Luau::visit(*this, ctv.indexer->indexResultType->ty);
|
||||
}
|
||||
|
||||
return allocator->alloc<AstTypeTable>(Location(), props, indexer);
|
||||
|
@ -329,20 +328,10 @@ public:
|
|||
if (retTail)
|
||||
retTailAnnotation = rehydrate(*retTail);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
auto returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{returnTypes, retTailAnnotation});
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, returnAnnotation
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
|
||||
);
|
||||
}
|
||||
}
|
||||
AstType* operator()(const ErrorType&)
|
||||
{
|
||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("Unifiable<Error>"), std::nullopt, Location());
|
||||
|
@ -596,8 +585,6 @@ public:
|
|||
visitLocal(arg);
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (!fn->returnAnnotation)
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
|
@ -610,26 +597,7 @@ public:
|
|||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fn->returnAnnotation_DEPRECATED)
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
TypePackId ret = result->returnType;
|
||||
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation_DEPRECATED = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
fn->returnAnnotation = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,10 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -662,7 +661,7 @@ void TypeChecker2::visit(AstStat* stat)
|
|||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
||||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatDeclareExternType>())
|
||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
||||
return visit(s);
|
||||
else if (auto s = stat->as<AstStatError>())
|
||||
return visit(s);
|
||||
|
@ -1222,10 +1221,7 @@ void TypeChecker2::visit(AstStatDeclareFunction* stat)
|
|||
{
|
||||
visitGenerics(stat->generics, stat->genericPacks);
|
||||
visit(stat->params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(stat->retTypes);
|
||||
else
|
||||
visit(stat->retTypes_DEPRECATED);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
||||
|
@ -1233,9 +1229,9 @@ void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
|||
visit(stat->type);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstStatDeclareExternType* stat)
|
||||
void TypeChecker2::visit(AstStatDeclareClass* stat)
|
||||
{
|
||||
for (const AstDeclaredExternTypeProperty& prop : stat->props)
|
||||
for (const AstDeclaredClassProp& prop : stat->props)
|
||||
visit(prop.ty);
|
||||
}
|
||||
|
||||
|
@ -1679,12 +1675,12 @@ void TypeChecker2::visit(AstExprIndexExpr* indexExpr, ValueContext context)
|
|||
{
|
||||
return indexExprMetatableHelper(indexExpr, mt, exprType, indexType);
|
||||
}
|
||||
else if (auto cls = get<ExternType>(exprType))
|
||||
else if (auto cls = get<ClassType>(exprType))
|
||||
{
|
||||
if (cls->indexer)
|
||||
testIsSubtype(indexType, cls->indexer->indexType, indexExpr->index->location);
|
||||
else
|
||||
reportError(DynamicPropertyLookupOnExternTypesUnsafe{exprType}, indexExpr->location);
|
||||
reportError(DynamicPropertyLookupOnClassesUnsafe{exprType}, indexExpr->location);
|
||||
}
|
||||
else if (get<UnionType>(exprType) && isOptional(exprType))
|
||||
{
|
||||
|
@ -1825,16 +1821,8 @@ void TypeChecker2::visit(AstExprFunction* fn)
|
|||
visit(fn->body);
|
||||
|
||||
// we need to typecheck the return annotation itself, if it exists.
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (fn->returnAnnotation)
|
||||
visit(fn->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fn->returnAnnotation_DEPRECATED)
|
||||
visit(*fn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
visit(*fn->returnAnnotation);
|
||||
|
||||
|
||||
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
||||
|
@ -2048,7 +2036,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
|
||||
// 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,
|
||||
// TODO: Maybe add more checks here (e.g. for functions, extern types, etc)
|
||||
// TODO: Maybe add more checks here (e.g. for functions, classes, etc)
|
||||
if (!(get<TableType>(leftType) || get<TableType>(rightType)))
|
||||
if (!leftMt.has_value() || !rightMt.has_value())
|
||||
matches = matches || typesHaveIntersection != NormalizationResult::False;
|
||||
|
@ -2113,7 +2101,10 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
|
|||
}
|
||||
else
|
||||
{
|
||||
expectedRets = module->internalTypes.addTypePack({module->internalTypes.freshType(builtinTypes, scope, TypeLevel{})});
|
||||
expectedRets = module->internalTypes.addTypePack(
|
||||
{FFlag::LuauFreeTypesMustHaveBounds ? module->internalTypes.freshType(builtinTypes, scope, TypeLevel{})
|
||||
: module->internalTypes.freshType_DEPRECATED(scope, TypeLevel{})}
|
||||
);
|
||||
}
|
||||
|
||||
TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets));
|
||||
|
@ -2376,7 +2367,8 @@ TypeId TypeChecker2::flattenPack(TypePackId pack)
|
|||
return *fst;
|
||||
else if (auto ftp = get<FreeTypePack>(pack))
|
||||
{
|
||||
TypeId result = module->internalTypes.freshType(builtinTypes, ftp->scope);
|
||||
TypeId result = FFlag::LuauFreeTypesMustHaveBounds ? module->internalTypes.freshType(builtinTypes, ftp->scope)
|
||||
: module->internalTypes.addType(FreeType{ftp->scope});
|
||||
TypePackId freeTail = module->internalTypes.addTypePack(FreeTypePack{ftp->scope});
|
||||
|
||||
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
||||
|
@ -2621,10 +2613,7 @@ void TypeChecker2::visit(AstTypeFunction* ty)
|
|||
{
|
||||
visitGenerics(ty->generics, ty->genericPacks);
|
||||
visit(ty->argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(ty->returnTypes);
|
||||
else
|
||||
visit(ty->returnTypes_DEPRECATED);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstTypeTypeof* ty)
|
||||
|
@ -2948,7 +2937,7 @@ PropertyTypes TypeChecker2::lookupProp(
|
|||
|
||||
if (normValid)
|
||||
{
|
||||
for (const auto& [ty, _negations] : norm->externTypes.externTypes)
|
||||
for (const auto& [ty, _negations] : norm->classes.classes)
|
||||
{
|
||||
fetch(ty);
|
||||
|
||||
|
@ -3043,10 +3032,10 @@ void TypeChecker2::checkIndexTypeFromType(
|
|||
if (propTypes.foundOneProp())
|
||||
reportError(MissingUnionProperty{tableTy, propTypes.missingProp, prop}, location);
|
||||
// For class LValues, we don't want to report an extension error,
|
||||
// because extern types come into being with full knowledge of their
|
||||
// because classes come into being with full knowledge of their
|
||||
// shape. We instead want to report the unknown property error of
|
||||
// the `else` branch.
|
||||
else if (context == ValueContext::LValue && !get<ExternType>(tableTy))
|
||||
else if (context == ValueContext::LValue && !get<ClassType>(tableTy))
|
||||
{
|
||||
const auto lvPropTypes = lookupProp(norm.get(), prop, ValueContext::RValue, location, astIndexExprType, dummy);
|
||||
if (lvPropTypes.foundOneProp() && lvPropTypes.noneMissingProp())
|
||||
|
@ -3056,7 +3045,7 @@ void TypeChecker2::checkIndexTypeFromType(
|
|||
else
|
||||
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
||||
}
|
||||
else if (context == ValueContext::RValue && !get<ExternType>(tableTy))
|
||||
else if (context == ValueContext::RValue && !get<ClassType>(tableTy))
|
||||
{
|
||||
const auto rvPropTypes = lookupProp(norm.get(), prop, ValueContext::LValue, location, astIndexExprType, dummy);
|
||||
if (rvPropTypes.foundOneProp() && rvPropTypes.noneMissingProp())
|
||||
|
@ -3109,25 +3098,19 @@ PropertyType TypeChecker2::hasIndexTypeFromType(
|
|||
return {NormalizationResult::True, {tt->indexer->indexResultType}};
|
||||
}
|
||||
|
||||
if (FFlag::LuauTypeCheckerStricterIndexCheck)
|
||||
{
|
||||
return {NormalizationResult::False, {builtinTypes->unknownType}};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// if we are in a conditional context, we treat the property as present and `unknown` because
|
||||
// we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit
|
||||
// in the future once luau has support for exact tables since this only applies when inexact.
|
||||
return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}};
|
||||
}
|
||||
}
|
||||
else if (const ExternType* cls = get<ExternType>(ty))
|
||||
else if (const ClassType* cls = get<ClassType>(ty))
|
||||
{
|
||||
// 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])
|
||||
// is compatible with the indexer's indexType
|
||||
// Construct the intersection and test inhabitedness!
|
||||
if (auto property = lookupExternTypeProp(cls, prop))
|
||||
if (auto property = lookupClassProp(cls, prop))
|
||||
return {NormalizationResult::True, context == ValueContext::LValue ? property->writeTy : property->readTy};
|
||||
if (cls->indexer)
|
||||
{
|
||||
|
@ -3200,17 +3183,17 @@ void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData&
|
|||
|
||||
if (auto ttv = getTableType(utk->table))
|
||||
accumulate(ttv->props);
|
||||
else if (auto etv = get<ExternType>(follow(utk->table)))
|
||||
else if (auto ctv = get<ClassType>(follow(utk->table)))
|
||||
{
|
||||
while (etv)
|
||||
while (ctv)
|
||||
{
|
||||
accumulate(etv->props);
|
||||
accumulate(ctv->props);
|
||||
|
||||
if (!etv->parent)
|
||||
if (!ctv->parent)
|
||||
break;
|
||||
|
||||
etv = get<ExternType>(*etv->parent);
|
||||
LUAU_ASSERT(etv);
|
||||
ctv = get<ClassType>(*ctv->parent);
|
||||
LUAU_ASSERT(ctv);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
|||
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDontForgetToReduceUnionFunc)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSearchForRefineableType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
||||
|
@ -66,7 +68,6 @@ LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
|||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -105,7 +106,7 @@ struct InstanceCollector_DEPRECATED : TypeOnceVisitor
|
|||
cyclicInstance.push_back(t);
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -181,7 +182,7 @@ struct InstanceCollector : TypeOnceVisitor
|
|||
}
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -269,7 +270,7 @@ struct UnscopedGenericFinder : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -843,7 +844,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
|||
{
|
||||
arguments[unionIndex] = option;
|
||||
|
||||
TypeFunctionReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...); // NOLINT
|
||||
TypeFunctionReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...);
|
||||
blockedTypes.insert(blockedTypes.end(), result.blockedTypes.begin(), result.blockedTypes.end());
|
||||
if (result.reductionStatus != Reduction::MaybeOk)
|
||||
reductionStatus = result.reductionStatus;
|
||||
|
@ -868,7 +869,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
|||
{},
|
||||
});
|
||||
|
||||
if (ctx->solver)
|
||||
if (FFlag::LuauDontForgetToReduceUnionFunc && ctx->solver)
|
||||
ctx->pushConstraint(ReduceConstraint{resultTy});
|
||||
|
||||
return {{resultTy, Reduction::MaybeOk, {}, {}}};
|
||||
|
@ -907,7 +908,7 @@ struct FindUserTypeFunctionBlockers : TypeOnceVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1839,20 +1840,10 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
|
|||
return {rhsTy, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isPending(lhsTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
else if (isPending(rhsTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||
}
|
||||
|
||||
// Or evalutes to the LHS type if the LHS is truthy, and the RHS type if LHS is falsy.
|
||||
SimplifyResult filteredLhs = simplifyIntersection(ctx->builtins, ctx->arena, lhsTy, ctx->builtins->truthyType);
|
||||
|
@ -1886,20 +1877,10 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
|
|||
if (lhsTy == instance || rhsTy == instance)
|
||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isPending(lhsTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
else if (isPending(rhsTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||
}
|
||||
|
||||
// Algebra Reduction Rules for comparison type functions
|
||||
// Note that comparing to never tells you nothing about the other operand
|
||||
|
@ -2149,7 +2130,7 @@ struct FindRefinementBlockers : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2260,12 +2241,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
for (size_t i = 1; i < typeParams.size(); i++)
|
||||
discriminantTypes.push_back(follow(typeParams.at(i)));
|
||||
|
||||
const bool targetIsPending = FFlag::DebugLuauGreedyGeneralization
|
||||
? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
||||
: isPending(targetTy, ctx->solver);
|
||||
|
||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||
if (targetIsPending)
|
||||
if (isPending(targetTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {targetTy}, {}};
|
||||
else
|
||||
{
|
||||
|
@ -2275,18 +2252,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
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.
|
||||
// Returns result : TypeId, toBlockOn : vector<TypeId>
|
||||
auto stepRefine = [&ctx](TypeId target, TypeId discriminant) -> std::pair<TypeId, std::vector<TypeId>>
|
||||
|
@ -2316,6 +2281,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
return {nullptr, {}};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauSearchForRefineableType)
|
||||
{
|
||||
// If the discriminant type is only:
|
||||
// - The `*no-refine*` type or,
|
||||
|
@ -2325,6 +2292,18 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
crt.traverse(discriminant);
|
||||
if (!crt.found)
|
||||
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)
|
||||
{
|
||||
|
@ -2350,32 +2329,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
||||
{
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
// Simplification considers free and generic types to be
|
||||
// 'blocking', but that's not suitable for refine<>.
|
||||
//
|
||||
// If we are only blocked on those types, we consider
|
||||
// the simplification a success and reduce.
|
||||
if (std::all_of(
|
||||
begin(result.blockedTypes),
|
||||
end(result.blockedTypes),
|
||||
[](auto&& v)
|
||||
{
|
||||
return is<FreeType, GenericType>(follow(v));
|
||||
}
|
||||
))
|
||||
{
|
||||
return {result.result, {}};
|
||||
}
|
||||
else
|
||||
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!result.blockedTypes.empty())
|
||||
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||
}
|
||||
return {result.result, {}};
|
||||
}
|
||||
}
|
||||
|
@ -2729,7 +2684,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||
return res;
|
||||
}
|
||||
|
||||
if (auto classTy = get<ExternType>(ty))
|
||||
if (auto classTy = get<ClassType>(ty))
|
||||
{
|
||||
for (auto [key, _] : classTy->props)
|
||||
result.insert(key);
|
||||
|
@ -2752,7 +2707,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||
return res;
|
||||
}
|
||||
|
||||
// this should not be reachable since the type should be a valid tables or extern types part from normalization.
|
||||
// this should not be reachable since the type should be a valid tables or classes part from normalization.
|
||||
LUAU_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
@ -2778,9 +2733,9 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
|||
if (!normTy)
|
||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// as well)
|
||||
if (normTy->hasTables() == normTy->hasExternTypes())
|
||||
if (normTy->hasTables() == normTy->hasClasses())
|
||||
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.
|
||||
|
@ -2791,31 +2746,31 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
|||
// we're going to collect the keys in here
|
||||
Set<std::string> keys{{}};
|
||||
|
||||
// computing the keys for extern types
|
||||
if (normTy->hasExternTypes())
|
||||
// computing the keys for classes
|
||||
if (normTy->hasClasses())
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasTables());
|
||||
|
||||
// seen set for key computation for extern types
|
||||
// seen set for key computation for classes
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
auto externTypeIter = normTy->externTypes.ordering.begin();
|
||||
auto externTypeIterEnd = normTy->externTypes.ordering.end();
|
||||
LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier
|
||||
auto classesIter = normTy->classes.ordering.begin();
|
||||
auto classesIterEnd = normTy->classes.ordering.end();
|
||||
LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check earlier
|
||||
|
||||
// collect all the properties from the first class type
|
||||
if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx))
|
||||
if (!computeKeysOf(*classesIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type!
|
||||
|
||||
// we need to look at each class to remove any keys that are not common amongst them all
|
||||
while (++externTypeIter != externTypeIterEnd)
|
||||
while (++classesIter != classesIterEnd)
|
||||
{
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
|
||||
// we can skip to the next class if this one is a top type
|
||||
if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx))
|
||||
if (!computeKeysOf(*classesIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto& key : keys)
|
||||
|
@ -2830,7 +2785,7 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
|||
// computing the keys for tables
|
||||
if (normTy->hasTables())
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasExternTypes());
|
||||
LUAU_ASSERT(!normTy->hasClasses());
|
||||
|
||||
// seen set for key computation for tables
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
@ -2992,7 +2947,7 @@ bool searchPropsAndIndexer(
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Handles recursion / metamethods of tables and extern types
|
||||
/* Handles recursion / metamethods of tables/classes
|
||||
`isRaw` parameter indicates whether or not we should follow __index metamethods
|
||||
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)
|
||||
|
@ -3167,11 +3122,11 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
return {ctx->builtins->anyType, Reduction::MaybeOk, {}, {}};
|
||||
}
|
||||
|
||||
// if we don't have either just tables or just extern types, we've got nothing to index into
|
||||
if (indexeeNormTy->hasTables() == indexeeNormTy->hasExternTypes())
|
||||
// if we don't have either just tables or just classes, we've got nothing to index into
|
||||
if (indexeeNormTy->hasTables() == indexeeNormTy->hasClasses())
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
// 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.
|
||||
// we're trying to reject any type that has not normalized to a table/class or a union of tables/classes.
|
||||
if (indexeeNormTy->hasTops() || indexeeNormTy->hasBooleans() || indexeeNormTy->hasErrors() || indexeeNormTy->hasNils() ||
|
||||
indexeeNormTy->hasNumbers() || indexeeNormTy->hasStrings() || indexeeNormTy->hasThreads() || indexeeNormTy->hasBuffers() ||
|
||||
indexeeNormTy->hasFunctions() || indexeeNormTy->hasTyvars())
|
||||
|
@ -3202,18 +3157,18 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
|
||||
DenseHashSet<TypeId> properties{{}}; // vector of types that will be returned
|
||||
|
||||
if (indexeeNormTy->hasExternTypes())
|
||||
if (indexeeNormTy->hasClasses())
|
||||
{
|
||||
LUAU_ASSERT(!indexeeNormTy->hasTables());
|
||||
|
||||
if (isRaw) // rawget should never reduce for extern types (to match the behavior of the rawget global function)
|
||||
if (isRaw) // rawget should never reduce for classes (to match the behavior of the rawget global function)
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
// at least one class is guaranteed to be in the iterator by .hasExternTypes()
|
||||
for (auto externTypeIter = indexeeNormTy->externTypes.ordering.begin(); externTypeIter != indexeeNormTy->externTypes.ordering.end(); ++externTypeIter)
|
||||
// at least one class is guaranteed to be in the iterator by .hasClasses()
|
||||
for (auto classesIter = indexeeNormTy->classes.ordering.begin(); classesIter != indexeeNormTy->classes.ordering.end(); ++classesIter)
|
||||
{
|
||||
auto externTy = get<ExternType>(*externTypeIter);
|
||||
if (!externTy)
|
||||
auto classTy = get<ClassType>(*classesIter);
|
||||
if (!classTy)
|
||||
{
|
||||
LUAU_ASSERT(false); // this should not be possible according to normalization's spec
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
@ -3222,16 +3177,16 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
for (TypeId ty : *typesToFind)
|
||||
{
|
||||
// Search for all instances of indexer in class->props and class->indexer
|
||||
if (searchPropsAndIndexer(ty, externTy->props, externTy->indexer, properties, ctx))
|
||||
if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx))
|
||||
continue; // Indexer was found in this class, so we can move on to the next
|
||||
|
||||
auto parent = externTy->parent;
|
||||
auto parent = classTy->parent;
|
||||
bool foundInParent = false;
|
||||
while (parent && !foundInParent)
|
||||
{
|
||||
auto parentExternType = get<ExternType>(follow(*parent));
|
||||
foundInParent = searchPropsAndIndexer(ty, parentExternType->props, parentExternType->indexer, properties, ctx);
|
||||
parent = parentExternType->parent;
|
||||
auto parentClass = get<ClassType>(follow(*parent));
|
||||
foundInParent = searchPropsAndIndexer(ty, parentClass->props, parentClass->indexer, properties, ctx);
|
||||
parent = parentClass->parent;
|
||||
}
|
||||
|
||||
// we move on to the next type if any of the parents we went through had the property.
|
||||
|
@ -3243,7 +3198,7 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
||||
// the necessary state to do that, even if we intend to just eat the errors.
|
||||
ErrorVec dummy;
|
||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, *externTypeIter, "__index", Location{});
|
||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, *classesIter, "__index", Location{});
|
||||
if (!mmType) // if a metatable does not exist, there is no where else to look
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
|
@ -3255,7 +3210,7 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
|
||||
if (indexeeNormTy->hasTables())
|
||||
{
|
||||
LUAU_ASSERT(!indexeeNormTy->hasExternTypes());
|
||||
LUAU_ASSERT(!indexeeNormTy->hasClasses());
|
||||
|
||||
// at least one table is guaranteed to be in the iterator by .hasTables()
|
||||
for (auto tablesIter = indexeeNormTy->tables.begin(); tablesIter != indexeeNormTy->tables.end(); ++tablesIter)
|
||||
|
@ -3350,7 +3305,7 @@ TypeFunctionReductionResult<TypeId> setmetatableTypeFunction(
|
|||
// 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() ||
|
||||
targetNorm->hasStrings() || targetNorm->hasThreads() || targetNorm->hasBuffers() || targetNorm->hasFunctions() || targetNorm->hasTyvars() ||
|
||||
targetNorm->hasExternTypes())
|
||||
targetNorm->hasClasses())
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
// if the supposed metatable is not a table, we will fail to reduce.
|
||||
|
@ -3424,7 +3379,7 @@ static TypeFunctionReductionResult<TypeId> getmetatableHelper(TypeId targetTy, c
|
|||
erroneous = false;
|
||||
}
|
||||
|
||||
if (auto clazz = get<ExternType>(targetTy))
|
||||
if (auto clazz = get<ClassType>(targetTy))
|
||||
{
|
||||
metatable = clazz->metatable;
|
||||
erroneous = false;
|
||||
|
@ -3575,7 +3530,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
|||
, ltFunc{"lt", ltTypeFunction}
|
||||
, leFunc{"le", leTypeFunction}
|
||||
, eqFunc{"eq", eqTypeFunction}
|
||||
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::DebugLuauGreedyGeneralization}
|
||||
, refineFunc{"refine", refineTypeFunction}
|
||||
, singletonFunc{"singleton", singletonTypeFunction}
|
||||
, unionFunc{"union", unionTypeFunction}
|
||||
, intersectFunc{"intersect", intersectTypeFunction}
|
||||
|
|
|
@ -46,7 +46,7 @@ struct InstanceCollector2 : TypeOnceVisitor
|
|||
cyclicInstance.insert(t);
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -155,7 +154,7 @@ static std::string getTag(lua_State* L, TypeFunctionTypeId ty)
|
|||
return "table";
|
||||
else if (get<TypeFunctionFunctionType>(ty))
|
||||
return "function";
|
||||
else if (get<TypeFunctionExternType>(ty))
|
||||
else if (get<TypeFunctionClassType>(ty))
|
||||
return "class";
|
||||
else if (get<TypeFunctionGenericType>(ty))
|
||||
return "generic";
|
||||
|
@ -316,38 +315,6 @@ static int getSingletonValue(lua_State* L)
|
|||
luaL_error(L, "type.value: can't call `value` method on `%s` type", getTag(L, self).c_str());
|
||||
}
|
||||
|
||||
// Luau: `types.optional(ty: type) -> type`
|
||||
// Returns the type instance representing an optional version of `ty`.
|
||||
// If `ty` is a union, this adds `nil` to the components of the union.
|
||||
// Otherwise, makes a union of the two things.
|
||||
static int createOptional(lua_State* L)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauTypeFunOptional);
|
||||
|
||||
int argumentCount = lua_gettop(L);
|
||||
if (argumentCount != 1)
|
||||
luaL_error(L, "types.optional: expected 1 argument, but got %d", argumentCount);
|
||||
|
||||
TypeFunctionTypeId argument = getTypeUserData(L, 1);
|
||||
|
||||
std::vector<TypeFunctionTypeId> components;
|
||||
|
||||
if (auto unionTy = get<TypeFunctionUnionType>(argument))
|
||||
{
|
||||
components.reserve(unionTy->components.size() + 1);
|
||||
|
||||
components.insert(components.begin(), unionTy->components.begin(), unionTy->components.end());
|
||||
}
|
||||
else
|
||||
components.emplace_back(argument);
|
||||
|
||||
components.emplace_back(allocateTypeFunctionType(L, TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType)));
|
||||
|
||||
allocTypeUserData(L, TypeFunctionUnionType{components});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Luau: `types.unionof(...: type) -> type`
|
||||
// Returns the type instance representing union
|
||||
static int createUnion(lua_State* L)
|
||||
|
@ -1147,7 +1114,7 @@ static int getClassParent_DEPRECATED(lua_State* L)
|
|||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||
|
||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||
auto tfct = get<TypeFunctionExternType>(self);
|
||||
auto tfct = get<TypeFunctionClassType>(self);
|
||||
if (!tfct)
|
||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||
|
||||
|
@ -1169,7 +1136,7 @@ static int getReadParent(lua_State* L)
|
|||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||
|
||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||
auto tfct = get<TypeFunctionExternType>(self);
|
||||
auto tfct = get<TypeFunctionClassType>(self);
|
||||
if (!tfct)
|
||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||
|
||||
|
@ -1191,7 +1158,7 @@ static int getWriteParent(lua_State* L)
|
|||
luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount);
|
||||
|
||||
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||
auto tfct = get<TypeFunctionExternType>(self);
|
||||
auto tfct = get<TypeFunctionClassType>(self);
|
||||
if (!tfct)
|
||||
luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str());
|
||||
|
||||
|
@ -1275,7 +1242,7 @@ static int getProps(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
||||
{
|
||||
lua_createtable(L, int(tfct->props.size()), 0);
|
||||
for (auto& [name, prop] : tfct->props)
|
||||
|
@ -1338,7 +1305,7 @@ static int getIndexer(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
||||
{
|
||||
// if the indexer does not exist, we should return nil
|
||||
if (!tfct->indexer.has_value())
|
||||
|
@ -1386,7 +1353,7 @@ static int getReadIndexer(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
||||
{
|
||||
// if the indexer does not exist, we should return nil
|
||||
if (!tfct->indexer.has_value())
|
||||
|
@ -1432,7 +1399,7 @@ static int getWriteIndexer(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
||||
{
|
||||
// if the indexer does not exist, we should return nil
|
||||
if (!tfct->indexer.has_value())
|
||||
|
@ -1472,7 +1439,7 @@ static int getMetatable(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (auto tfct = get<TypeFunctionExternType>(self))
|
||||
if (auto tfct = get<TypeFunctionClassType>(self))
|
||||
{
|
||||
// if the metatable does not exist, we should return nil
|
||||
if (!tfct->metatable.has_value())
|
||||
|
@ -1557,7 +1524,6 @@ void registerTypesLibrary(lua_State* L)
|
|||
{"copy", deepCopy},
|
||||
{"generic", createGeneric},
|
||||
|
||||
{(FFlag::LuauTypeFunOptional) ? "optional" : nullptr, (FFlag::LuauTypeFunOptional) ? createOptional : nullptr},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
@ -1627,7 +1593,7 @@ void registerTypeUserData(lua_State* L)
|
|||
// Union and Intersection type methods
|
||||
{"components", getComponents},
|
||||
|
||||
// Extern type methods
|
||||
// Class type methods
|
||||
{FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED},
|
||||
|
||||
// Function type methods (cont.)
|
||||
|
@ -1638,7 +1604,7 @@ void registerTypeUserData(lua_State* L)
|
|||
{"name", getGenericName},
|
||||
{"ispack", getGenericIsPack},
|
||||
|
||||
// move this under extern type methods when removing FFlagLuauTypeFunReadWriteParents
|
||||
// move this under Class type methods when removing FFlagLuauTypeFunReadWriteParents
|
||||
{FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr},
|
||||
|
||||
{nullptr, nullptr}
|
||||
|
@ -1937,12 +1903,12 @@ bool areEqual(SeenSet& seen, const TypeFunctionFunctionType& lhs, const TypeFunc
|
|||
return true;
|
||||
}
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypeFunctionExternType& lhs, const TypeFunctionExternType& rhs)
|
||||
bool areEqual(SeenSet& seen, const TypeFunctionClassType& lhs, const TypeFunctionClassType& rhs)
|
||||
{
|
||||
if (seenSetContains(seen, &lhs, &rhs))
|
||||
return true;
|
||||
|
||||
return lhs.externTy == rhs.externTy;
|
||||
return lhs.classTy == rhs.classTy;
|
||||
}
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
||||
|
@ -2010,8 +1976,8 @@ bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType
|
|||
}
|
||||
|
||||
{
|
||||
const TypeFunctionExternType* lf = get<TypeFunctionExternType>(&lhs);
|
||||
const TypeFunctionExternType* rf = get<TypeFunctionExternType>(&rhs);
|
||||
const TypeFunctionClassType* lf = get<TypeFunctionClassType>(&lhs);
|
||||
const TypeFunctionClassType* rf = get<TypeFunctionClassType>(&rhs);
|
||||
if (lf && rf)
|
||||
return areEqual(seen, *lf, *rf);
|
||||
}
|
||||
|
@ -2300,7 +2266,7 @@ private:
|
|||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||
}
|
||||
else if (auto c = get<TypeFunctionExternType>(ty))
|
||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
||||
target = ty; // Don't copy a class since they are immutable
|
||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->isNamed, g->isPack, g->name});
|
||||
|
@ -2355,7 +2321,7 @@ private:
|
|||
cloneChildren(t1, t2);
|
||||
else if (auto [f1, f2] = std::tuple{getMutable<TypeFunctionFunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||
cloneChildren(f1, f2);
|
||||
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||
cloneChildren(c1, c2);
|
||||
else if (auto [g1, g2] = std::tuple{getMutable<TypeFunctionGenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||
cloneChildren(g1, g2);
|
||||
|
@ -2465,7 +2431,7 @@ private:
|
|||
f2->retTypes = shallowClone(f1->retTypes);
|
||||
}
|
||||
|
||||
void cloneChildren(TypeFunctionExternType* c1, TypeFunctionExternType* c2)
|
||||
void cloneChildren(TypeFunctionClassType* c1, TypeFunctionClassType* c2)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
|
|
@ -206,12 +206,12 @@ private:
|
|||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||
}
|
||||
else if (auto c = get<ExternType>(ty))
|
||||
else if (auto c = get<ClassType>(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
|
||||
// class
|
||||
target = typeFunctionRuntime->typeArena.allocate(
|
||||
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
||||
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
||||
);
|
||||
}
|
||||
else if (auto g = get<GenericType>(ty))
|
||||
|
@ -291,7 +291,7 @@ private:
|
|||
serializeChildren(m1, m2);
|
||||
else if (auto [f1, f2] = std::tuple{get<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||
serializeChildren(f1, f2);
|
||||
else if (auto [c1, c2] = std::tuple{get<ExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||
else if (auto [c1, c2] = std::tuple{get<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||
serializeChildren(c1, c2);
|
||||
else if (auto [g1, g2] = std::tuple{get<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||
serializeChildren(g1, g2);
|
||||
|
@ -411,7 +411,7 @@ private:
|
|||
f2->retTypes = shallowSerialize(f1->retTypes);
|
||||
}
|
||||
|
||||
void serializeChildren(const ExternType* c1, TypeFunctionExternType* c2)
|
||||
void serializeChildren(const ClassType* c1, TypeFunctionClassType* c2)
|
||||
{
|
||||
for (const auto& [k, p] : c1->props)
|
||||
{
|
||||
|
@ -702,9 +702,9 @@ private:
|
|||
TypePackId emptyTypePack = state->ctx->arena->addTypePack(TypePack{});
|
||||
target = state->ctx->arena->addType(FunctionType{emptyTypePack, emptyTypePack, {}, false});
|
||||
}
|
||||
else if (auto c = get<TypeFunctionExternType>(ty))
|
||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
||||
{
|
||||
target = c->externTy;
|
||||
target = c->classTy;
|
||||
}
|
||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||
{
|
||||
|
@ -811,7 +811,7 @@ private:
|
|||
deserializeChildren(m2, m1);
|
||||
else if (auto [f1, f2] = std::tuple{getMutable<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
|
||||
deserializeChildren(f2, f1);
|
||||
else if (auto [c1, c2] = std::tuple{getMutable<ExternType>(ty), getMutable<TypeFunctionExternType>(tfti)}; c1 && c2)
|
||||
else if (auto [c1, c2] = std::tuple{getMutable<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||
deserializeChildren(c2, c1);
|
||||
else if (auto [g1, g2] = std::tuple{getMutable<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)}; g1 && g2)
|
||||
deserializeChildren(g2, g1);
|
||||
|
@ -972,7 +972,7 @@ private:
|
|||
f1->retTypes = shallowDeserialize(f2->retTypes);
|
||||
}
|
||||
|
||||
void deserializeChildren(TypeFunctionExternType* c2, ExternType* c1)
|
||||
void deserializeChildren(TypeFunctionClassType* c2, ClassType* c1)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
|||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||
|
@ -44,7 +44,7 @@ namespace Luau
|
|||
|
||||
static bool typeCouldHaveMetatable(TypeId ty)
|
||||
{
|
||||
return get<TableType>(follow(ty)) || get<ExternType>(follow(ty)) || get<MetatableType>(follow(ty));
|
||||
return get<TableType>(follow(ty)) || get<ClassType>(follow(ty)) || get<MetatableType>(follow(ty));
|
||||
}
|
||||
|
||||
static void defaultLuauPrintLine(const std::string& s)
|
||||
|
@ -318,7 +318,7 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
|||
unifierState.skipCacheForType.clear();
|
||||
|
||||
duplicateTypeAliases.clear();
|
||||
incorrectExternTypeDefinitions.clear();
|
||||
incorrectClassDefinitions.clear();
|
||||
|
||||
return std::move(currentModule);
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStat& program)
|
|||
}
|
||||
else if (auto global = program.as<AstStatDeclareFunction>())
|
||||
return check(scope, *global);
|
||||
else if (auto global = program.as<AstStatDeclareExternType>())
|
||||
else if (auto global = program.as<AstStatDeclareClass>())
|
||||
return check(scope, *global);
|
||||
else if (auto errorStatement = program.as<AstStatError>())
|
||||
{
|
||||
|
@ -498,9 +498,9 @@ ControlFlow TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope,
|
|||
prototype(scope, *typealias, subLevel);
|
||||
++subLevel;
|
||||
}
|
||||
else if (const auto& declaredExternType = stat->as<AstStatDeclareExternType>())
|
||||
else if (const auto& declaredClass = stat->as<AstStatDeclareClass>())
|
||||
{
|
||||
prototype(scope, *declaredExternType);
|
||||
prototype(scope, *declaredClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,7 +788,7 @@ struct Demoter : Substitution
|
|||
|
||||
bool ignoreChildren(TypeId ty) override
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -798,7 +798,8 @@ struct Demoter : Substitution
|
|||
{
|
||||
auto ftv = get<FreeType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
return arena->freshType(builtins, demotedLevel(ftv->level));
|
||||
return FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtins, demotedLevel(ftv->level))
|
||||
: addType(FreeType{demotedLevel(ftv->level)});
|
||||
}
|
||||
|
||||
TypePackId clean(TypePackId tp) override
|
||||
|
@ -1685,82 +1686,82 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
|||
}
|
||||
}
|
||||
|
||||
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
||||
{
|
||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->externType);
|
||||
if (declaredExternType.superName)
|
||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
||||
if (declaredClass.superName)
|
||||
{
|
||||
Name superName = Name(declaredExternType.superName->value);
|
||||
Name superName = Name(declaredClass.superName->value);
|
||||
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
||||
|
||||
if (!lookupType)
|
||||
{
|
||||
reportError(declaredExternType.location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||
incorrectExternTypeDefinitions.insert(&declaredExternType);
|
||||
reportError(declaredClass.location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||
incorrectClassDefinitions.insert(&declaredClass);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have generic extern types, so this assertion _should_ never be hit.
|
||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||
superTy = lookupType->type;
|
||||
|
||||
if (!get<ExternType>(follow(*superTy)))
|
||||
if (!get<ClassType>(follow(*superTy)))
|
||||
{
|
||||
reportError(
|
||||
declaredExternType.location,
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType.name.value)}
|
||||
declaredClass.location,
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass.name.value)}
|
||||
);
|
||||
incorrectExternTypeDefinitions.insert(&declaredExternType);
|
||||
incorrectClassDefinitions.insert(&declaredClass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Name className(declaredExternType.name.value);
|
||||
Name className(declaredClass.name.value);
|
||||
|
||||
TypeId classTy = addType(ExternType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name, declaredExternType.location));
|
||||
ExternType* etv = getMutable<ExternType>(classTy);
|
||||
TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name, declaredClass.location));
|
||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
||||
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||
|
||||
etv->metatable = metaTy;
|
||||
ctv->metatable = metaTy;
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredClass.location};
|
||||
else
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||
}
|
||||
|
||||
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
||||
{
|
||||
Name className(declaredExternType.name.value);
|
||||
Name className(declaredClass.name.value);
|
||||
|
||||
// Don't bother checking if the class definition was incorrect
|
||||
if (incorrectExternTypeDefinitions.find(&declaredExternType))
|
||||
if (incorrectClassDefinitions.find(&declaredClass))
|
||||
return ControlFlow::None;
|
||||
|
||||
std::optional<TypeFun> binding;
|
||||
if (auto it = scope->exportedTypeBindings.find(className); it != scope->exportedTypeBindings.end())
|
||||
binding = it->second;
|
||||
|
||||
// This extern type definition must have been `prototype()`d first.
|
||||
// This class definition must have been `prototype()`d first.
|
||||
if (!binding)
|
||||
ice("Extern type not predeclared");
|
||||
ice("Class not predeclared");
|
||||
|
||||
TypeId externTy = binding->type;
|
||||
ExternType* etv = getMutable<ExternType>(externTy);
|
||||
TypeId classTy = binding->type;
|
||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
||||
|
||||
if (!etv->metatable)
|
||||
ice("No metatable for declared extern type");
|
||||
if (!ctv->metatable)
|
||||
ice("No metatable for declared class");
|
||||
|
||||
if (const auto& indexer = declaredExternType.indexer)
|
||||
etv->indexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType));
|
||||
if (const auto& indexer = declaredClass.indexer)
|
||||
ctv->indexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType));
|
||||
|
||||
TableType* metatable = getMutable<TableType>(*etv->metatable);
|
||||
for (const AstDeclaredExternTypeProperty& prop : declaredExternType.props)
|
||||
TableType* metatable = getMutable<TableType>(*ctv->metatable);
|
||||
for (const AstDeclaredClassProp& prop : declaredClass.props)
|
||||
{
|
||||
Name propName(prop.name.value);
|
||||
TypeId propTy = resolveType(scope, *prop.ty);
|
||||
|
||||
bool assignToMetatable = isMetamethod(propName);
|
||||
Luau::ExternType::Props& assignTo = assignToMetatable ? metatable->props : etv->props;
|
||||
Luau::ClassType::Props& assignTo = assignToMetatable ? metatable->props : ctv->props;
|
||||
|
||||
// Function types always take 'self', but this isn't reflected in the
|
||||
// parsed annotation. Add it here.
|
||||
|
@ -1769,7 +1770,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExtern
|
|||
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
||||
{
|
||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||
ftv->argTypes = addTypePack(TypePack{{externTy}, ftv->argTypes});
|
||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||
ftv->hasSelf = true;
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
@ -1812,7 +1813,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExtern
|
|||
}
|
||||
else
|
||||
{
|
||||
reportError(declaredExternType.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1851,8 +1852,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti
|
|||
);
|
||||
|
||||
TypePackId argPack = resolveTypePack(funScope, global.params);
|
||||
TypePackId retPack =
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, *global.retTypes) : resolveTypePack(funScope, global.retTypes_DEPRECATED);
|
||||
TypePackId retPack = resolveTypePack(funScope, global.retTypes);
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
|
@ -2137,9 +2137,9 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
|||
if (auto found = findTablePropertyRespectingMeta(type, name, location, addErrors))
|
||||
return *found;
|
||||
}
|
||||
else if (const ExternType* cls = get<ExternType>(type))
|
||||
else if (const ClassType* cls = get<ClassType>(type))
|
||||
{
|
||||
const Property* prop = lookupExternTypeProp(cls, name);
|
||||
const Property* prop = lookupClassProp(cls, name);
|
||||
if (prop)
|
||||
return prop->type();
|
||||
|
||||
|
@ -3462,14 +3462,14 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
return errorRecoveryType(scope);
|
||||
}
|
||||
}
|
||||
else if (const ExternType* lhsExternType = get<ExternType>(lhs))
|
||||
else if (const ClassType* lhsClass = get<ClassType>(lhs))
|
||||
{
|
||||
if (const Property* prop = lookupExternTypeProp(lhsExternType, name))
|
||||
if (const Property* prop = lookupClassProp(lhsClass, name))
|
||||
{
|
||||
return prop->type();
|
||||
}
|
||||
|
||||
if (auto indexer = lhsExternType->indexer)
|
||||
if (auto indexer = lhsClass->indexer)
|
||||
{
|
||||
Unifier state = mkUnifier(scope, expr.location);
|
||||
state.tryUnify(stringType, indexer->indexType);
|
||||
|
@ -3521,14 +3521,14 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
|
||||
if (value)
|
||||
{
|
||||
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
||||
{
|
||||
if (const Property* prop = lookupExternTypeProp(exprExternType, value->value.data))
|
||||
if (const Property* prop = lookupClassProp(exprClass, value->value.data))
|
||||
{
|
||||
return prop->type();
|
||||
}
|
||||
|
||||
if (auto indexer = exprExternType->indexer)
|
||||
if (auto indexer = exprClass->indexer)
|
||||
{
|
||||
unify(stringType, indexer->indexType, scope, expr.index->location);
|
||||
return indexer->indexResultType;
|
||||
|
@ -3554,20 +3554,20 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
}
|
||||
else
|
||||
{
|
||||
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
||||
{
|
||||
if (auto indexer = exprExternType->indexer)
|
||||
if (auto indexer = exprClass->indexer)
|
||||
{
|
||||
unify(indexType, indexer->indexType, scope, expr.index->location);
|
||||
return indexer->indexResultType;
|
||||
}
|
||||
}
|
||||
|
||||
if (const ExternType* exprExternType = get<ExternType>(exprType))
|
||||
if (const ClassType* exprClass = get<ClassType>(exprType))
|
||||
{
|
||||
if (isNonstrictMode())
|
||||
return unknownType;
|
||||
reportError(TypeError{expr.location, DynamicPropertyLookupOnExternTypesUnsafe{exprType}});
|
||||
reportError(TypeError{expr.location, DynamicPropertyLookupOnClassesUnsafe{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
}
|
||||
|
@ -3848,10 +3848,8 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
|||
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
||||
|
||||
TypePackId retPack;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation)
|
||||
if (expr.returnAnnotation)
|
||||
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
|
||||
else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation_DEPRECATED)
|
||||
retPack = resolveTypePack(funScope, *expr.returnAnnotation_DEPRECATED);
|
||||
else if (isNonstrictMode())
|
||||
retPack = anyTypePack;
|
||||
else if (expectedFunctionType && expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())
|
||||
|
@ -4058,8 +4056,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
|||
// If we're in nonstrict mode we want to only report this missing return
|
||||
// statement if there are type annotations on the function. In strict mode
|
||||
// we report it regardless.
|
||||
if (!isNonstrictMode() ||
|
||||
(FFlag::LuauStoreReturnTypesAsPackOnAst ? function.returnAnnotation != nullptr : function.returnAnnotation_DEPRECATED.has_value()))
|
||||
if (!isNonstrictMode() || function.returnAnnotation)
|
||||
{
|
||||
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
||||
}
|
||||
|
@ -4614,9 +4611,9 @@ std::unique_ptr<WithPredicate<TypePackId>> TypeChecker::checkCallOverload(
|
|||
{
|
||||
callTy = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(fn); etv && etv->metatable)
|
||||
else if (const ClassType* ctv = get<ClassType>(fn); ctv && ctv->metatable)
|
||||
{
|
||||
callTy = getIndexTypeFromType(scope, *etv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||
callTy = getIndexTypeFromType(scope, *ctv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||
}
|
||||
|
||||
if (callTy)
|
||||
|
@ -5319,17 +5316,17 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d
|
|||
|
||||
if (auto ttv = getTableType(utk->table))
|
||||
accumulate(ttv->props);
|
||||
else if (auto etv = get<ExternType>(follow(utk->table)))
|
||||
else if (auto ctv = get<ClassType>(follow(utk->table)))
|
||||
{
|
||||
while (etv)
|
||||
while (ctv)
|
||||
{
|
||||
accumulate(etv->props);
|
||||
accumulate(ctv->props);
|
||||
|
||||
if (!etv->parent)
|
||||
if (!ctv->parent)
|
||||
break;
|
||||
|
||||
etv = get<ExternType>(*etv->parent);
|
||||
LUAU_ASSERT(etv);
|
||||
ctv = get<ClassType>(*ctv->parent);
|
||||
LUAU_ASSERT(ctv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5408,7 +5405,8 @@ TypeId TypeChecker::freshType(const ScopePtr& scope)
|
|||
|
||||
TypeId TypeChecker::freshType(TypeLevel level)
|
||||
{
|
||||
return currentModule->internalTypes.freshType(builtinTypes, level);
|
||||
return FFlag::LuauFreeTypesMustHaveBounds ? currentModule->internalTypes.freshType(builtinTypes, level)
|
||||
: currentModule->internalTypes.addType(Type(FreeType(level)));
|
||||
}
|
||||
|
||||
TypeId TypeChecker::singletonType(bool value)
|
||||
|
@ -5805,8 +5803,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks);
|
||||
|
||||
TypePackId argTypes = resolveTypePack(funcScope, func->argTypes);
|
||||
TypePackId retTypes = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funcScope, *func->returnTypes)
|
||||
: resolveTypePack(funcScope, func->returnTypes_DEPRECATED);
|
||||
TypePackId retTypes = resolveTypePack(funcScope, func->returnTypes);
|
||||
|
||||
std::vector<TypeId> genericTys;
|
||||
genericTys.reserve(generics.size());
|
||||
|
@ -5857,9 +5854,13 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
return builtinTypes->nilType;
|
||||
}
|
||||
else if (const auto& un = annotation.as<AstTypeUnion>())
|
||||
{
|
||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
{
|
||||
if (un->types.size == 1)
|
||||
return resolveType(scope, *un->types.data[0]);
|
||||
}
|
||||
|
||||
std::vector<TypeId> types;
|
||||
for (AstType* ann : un->types)
|
||||
types.push_back(resolveType(scope, *ann));
|
||||
|
@ -5867,9 +5868,13 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
return addType(UnionType{types});
|
||||
}
|
||||
else if (const auto& un = annotation.as<AstTypeIntersection>())
|
||||
{
|
||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
{
|
||||
if (un->types.size == 1)
|
||||
return resolveType(scope, *un->types.data[0]);
|
||||
}
|
||||
|
||||
std::vector<TypeId> types;
|
||||
for (AstType* ann : un->types)
|
||||
types.push_back(resolveType(scope, *ann));
|
||||
|
@ -6476,7 +6481,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
|||
return refine(
|
||||
[](TypeId ty) -> bool
|
||||
{
|
||||
return get<ExternType>(ty);
|
||||
return get<ClassType>(ty);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -6491,13 +6496,13 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
|||
TypeId type = follow(typeFun->type);
|
||||
|
||||
// You cannot refine to the top class type.
|
||||
if (type == builtinTypes->externType)
|
||||
if (type == builtinTypes->classType)
|
||||
{
|
||||
return addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
||||
}
|
||||
|
||||
// We're only interested in the root type of any extern type.
|
||||
if (auto etv = get<ExternType>(type); !etv || (etv->parent != builtinTypes->externType && !hasTag(type, kTypeofRootTag)))
|
||||
// We're only interested in the root class of any classes.
|
||||
if (auto ctv = get<ClassType>(type); !ctv || (ctv->parent != builtinTypes->classType && !hasTag(type, kTypeofRootTag)))
|
||||
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.
|
||||
|
|
|
@ -208,26 +208,6 @@ TypePackIterator end(TypePackId tp)
|
|||
return TypePackIterator{};
|
||||
}
|
||||
|
||||
TypePackId getTail(TypePackId tp)
|
||||
{
|
||||
DenseHashSet<TypePackId> seen{nullptr};
|
||||
while (tp)
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (seen.contains(tp))
|
||||
break;
|
||||
seen.insert(tp);
|
||||
|
||||
if (auto pack = get<TypePack>(tp); pack && pack->tail)
|
||||
tp = *pack->tail;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return follow(tp);
|
||||
}
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
||||
{
|
||||
TypePackId lhsId = const_cast<TypePackId>(&lhs);
|
||||
|
|
|
@ -307,9 +307,9 @@ struct TraversalState
|
|||
prop = &it->second;
|
||||
}
|
||||
}
|
||||
else if (auto c = get<ExternType>(*currentType))
|
||||
else if (auto c = get<ClassType>(*currentType))
|
||||
{
|
||||
prop = lookupExternTypeProp(c, property.name);
|
||||
prop = lookupClassProp(c, property.name);
|
||||
}
|
||||
// For a metatable type, the table takes priority; check that before
|
||||
// falling through to the metatable entry below.
|
||||
|
@ -461,7 +461,7 @@ struct TraversalState
|
|||
indexer = &(*mtMt->indexer);
|
||||
}
|
||||
// Note: we don't appear to walk the class hierarchy for indexers
|
||||
else if (auto ct = get<ExternType>(current); ct && ct->indexer)
|
||||
else if (auto ct = get<ClassType>(current); ct && ct->indexer)
|
||||
indexer = &(*ct->indexer);
|
||||
|
||||
if (indexer)
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||
|
||||
|
@ -324,10 +327,11 @@ TypePack extendTypePack(
|
|||
{
|
||||
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType, ftp->polarity};
|
||||
t = arena.addType(ft);
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(ftp->scope, t);
|
||||
}
|
||||
else
|
||||
t = arena.freshType(builtinTypes, ftp->scope);
|
||||
t = FFlag::LuauFreeTypesMustHaveBounds ? arena.freshType(builtinTypes, ftp->scope) : arena.freshType_DEPRECATED(ftp->scope);
|
||||
}
|
||||
|
||||
newPack.head.push_back(t);
|
||||
|
@ -434,6 +438,7 @@ TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty)
|
|||
|
||||
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauSolverV2 || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
|
||||
|
||||
if (!normType)
|
||||
|
@ -551,8 +556,10 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
|||
|
||||
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||
{
|
||||
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||
LUAU_ASSERT(FFlag::LuauSolverV2);
|
||||
if (FFlag::LuauDisableNewSolverAssertsInMixedMode)
|
||||
LUAU_ASSERT(FFlag::LuauTrackInteriorFreeTypesOnScope);
|
||||
else
|
||||
LUAU_ASSERT(FFlag::LuauSolverV2 && FFlag::LuauTrackInteriorFreeTypesOnScope);
|
||||
for (; scope; scope = scope->parent.get())
|
||||
{
|
||||
if (scope->interiorFreeTypes)
|
||||
|
|
|
@ -22,6 +22,7 @@ LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping)
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart)
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -291,7 +292,7 @@ TypePackId Widen::clean(TypePackId)
|
|||
|
||||
bool Widen::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return !log->is<UnionType>(ty);
|
||||
|
@ -692,13 +693,13 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
else if (log.getMutable<MetatableType>(subTy))
|
||||
tryUnifyWithMetatable(superTy, subTy, /*reversed*/ true);
|
||||
|
||||
else if (log.getMutable<ExternType>(superTy))
|
||||
tryUnifyWithExternType(subTy, superTy, /*reversed*/ false);
|
||||
else if (log.getMutable<ClassType>(superTy))
|
||||
tryUnifyWithClass(subTy, superTy, /*reversed*/ false);
|
||||
|
||||
// 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 extern types.
|
||||
else if (log.getMutable<ExternType>(subTy))
|
||||
tryUnifyWithExternType(subTy, superTy, /*reversed*/ true);
|
||||
// Unification of nonclasses with classes is almost, but not quite symmetrical.
|
||||
// The order in which we perform this test is significant in the case that both types are classes.
|
||||
else if (log.getMutable<ClassType>(subTy))
|
||||
tryUnifyWithClass(subTy, superTy, /*reversed*/ true);
|
||||
|
||||
else if (log.get<NegationType>(superTy) || log.get<NegationType>(subTy))
|
||||
tryUnifyNegations(subTy, superTy);
|
||||
|
@ -1106,15 +1107,15 @@ void Unifier::tryUnifyNormalizedTypes(
|
|||
if (!get<PrimitiveType>(superNorm.errors))
|
||||
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
|
||||
|
||||
for (const auto& [subExternType, _] : subNorm.externTypes.externTypes)
|
||||
for (const auto& [subClass, _] : subNorm.classes.classes)
|
||||
{
|
||||
bool found = false;
|
||||
const ExternType* subCtv = get<ExternType>(subExternType);
|
||||
const ClassType* subCtv = get<ClassType>(subClass);
|
||||
LUAU_ASSERT(subCtv);
|
||||
|
||||
for (const auto& [superExternType, superNegations] : superNorm.externTypes.externTypes)
|
||||
for (const auto& [superClass, superNegations] : superNorm.classes.classes)
|
||||
{
|
||||
const ExternType* superCtv = get<ExternType>(superExternType);
|
||||
const ClassType* superCtv = get<ClassType>(superClass);
|
||||
LUAU_ASSERT(superCtv);
|
||||
|
||||
if (isSubclass(subCtv, superCtv))
|
||||
|
@ -1123,7 +1124,7 @@ void Unifier::tryUnifyNormalizedTypes(
|
|||
|
||||
for (TypeId negation : superNegations)
|
||||
{
|
||||
const ExternType* negationCtv = get<ExternType>(negation);
|
||||
const ClassType* negationCtv = get<ClassType>(negation);
|
||||
LUAU_ASSERT(negationCtv);
|
||||
|
||||
if (isSubclass(subCtv, negationCtv))
|
||||
|
@ -1558,7 +1559,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
|||
if (FFlag::LuauSolverV2)
|
||||
return freshType(NotNull{types}, builtinTypes, scope);
|
||||
else
|
||||
return types->freshType(builtinTypes, scope, level);
|
||||
return FFlag::LuauFreeTypesMustHaveBounds ? types->freshType(builtinTypes, scope, level) : types->freshType_DEPRECATED(scope, level);
|
||||
};
|
||||
|
||||
const TypePackId emptyTp = types->addTypePack(TypePack{{}, std::nullopt});
|
||||
|
@ -2381,8 +2382,8 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
|||
}
|
||||
}
|
||||
|
||||
// Extern type unification is almost, but not quite symmetrical. We use the 'reversed' boolean to indicate which scenario we are evaluating.
|
||||
void Unifier::tryUnifyWithExternType(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.
|
||||
void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||
{
|
||||
if (reversed)
|
||||
std::swap(superTy, subTy);
|
||||
|
@ -2395,20 +2396,20 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed
|
|||
reportError(location, TypeMismatch{subTy, superTy, mismatchContext()});
|
||||
};
|
||||
|
||||
const ExternType* superExternType = get<ExternType>(superTy);
|
||||
if (!superExternType)
|
||||
ice("tryUnifyExternType invoked with non-class Type");
|
||||
const ClassType* superClass = get<ClassType>(superTy);
|
||||
if (!superClass)
|
||||
ice("tryUnifyClass invoked with non-class Type");
|
||||
|
||||
if (const ExternType* subExternType = get<ExternType>(subTy))
|
||||
if (const ClassType* subClass = get<ClassType>(subTy))
|
||||
{
|
||||
switch (variance)
|
||||
{
|
||||
case Covariant:
|
||||
if (!isSubclass(subExternType, superExternType))
|
||||
if (!isSubclass(subClass, superClass))
|
||||
return fail();
|
||||
return;
|
||||
case Invariant:
|
||||
if (subExternType != superExternType)
|
||||
if (subClass != superClass)
|
||||
return fail();
|
||||
return;
|
||||
}
|
||||
|
@ -2433,7 +2434,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed
|
|||
|
||||
for (const auto& [propName, prop] : subTable->props)
|
||||
{
|
||||
const Property* classProp = lookupExternTypeProp(superExternType, propName);
|
||||
const Property* classProp = lookupClassProp(superClass, propName);
|
||||
if (!classProp)
|
||||
{
|
||||
ok = false;
|
||||
|
@ -2461,7 +2462,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed
|
|||
if (subTable->indexer)
|
||||
{
|
||||
ok = false;
|
||||
std::string msg = "Extern type " + superExternType->name + " does not have an indexer";
|
||||
std::string msg = "Class " + superClass->name + " does not have an indexer";
|
||||
reportError(location, GenericError{msg});
|
||||
}
|
||||
|
||||
|
@ -2634,9 +2635,9 @@ static void tryUnifyWithAny(
|
|||
queue.push_back(mt->table);
|
||||
queue.push_back(mt->metatable);
|
||||
}
|
||||
else if (state.log.getMutable<ExternType>(ty))
|
||||
else if (state.log.getMutable<ClassType>(ty))
|
||||
{
|
||||
// ExternTypes never contain free types.
|
||||
// ClassTypes never contain free types.
|
||||
}
|
||||
else if (auto union_ = state.log.getMutable<UnionType>(ty))
|
||||
queue.insert(queue.end(), union_->options.begin(), union_->options.end());
|
||||
|
@ -2653,7 +2654,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
|||
LUAU_ASSERT(get<AnyType>(anyTy) || get<ErrorType>(anyTy) || get<UnknownType>(anyTy) || get<NeverType>(anyTy));
|
||||
|
||||
// These types are not visited in general loop below
|
||||
if (log.get<PrimitiveType>(subTy) || log.get<AnyType>(subTy) || log.get<ExternType>(subTy))
|
||||
if (log.get<PrimitiveType>(subTy) || log.get<AnyType>(subTy) || log.get<ClassType>(subTy))
|
||||
return;
|
||||
|
||||
TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
||||
|
|
|
@ -321,8 +321,8 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
|||
{
|
||||
for (TypeId generic : subFn->generics)
|
||||
{
|
||||
const GenericType* gen = get<GenericType>(follow(generic));
|
||||
if (gen)
|
||||
const GenericType* gen = get<GenericType>(generic);
|
||||
LUAU_ASSERT(gen);
|
||||
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
||||
}
|
||||
|
||||
|
@ -331,7 +331,7 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
|||
if (FFlag::LuauNonReentrantGeneralization2)
|
||||
{
|
||||
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||
if (gen)
|
||||
LUAU_ASSERT(gen);
|
||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -446,25 +446,7 @@ public:
|
|||
AstStatBlock* body,
|
||||
size_t functionDepth,
|
||||
const AstName& debugname,
|
||||
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,
|
||||
const std::optional<AstTypeList>& returnAnnotation = {},
|
||||
AstTypePack* varargAnnotation = nullptr,
|
||||
const std::optional<Location>& argLocation = std::nullopt
|
||||
);
|
||||
|
@ -479,9 +461,7 @@ public:
|
|||
AstArray<AstGenericTypePack*> genericPacks;
|
||||
AstLocal* self;
|
||||
AstArray<AstLocal*> args;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::optional<AstTypeList> returnAnnotation_DEPRECATED;
|
||||
AstTypePack* returnAnnotation = nullptr;
|
||||
std::optional<AstTypeList> returnAnnotation;
|
||||
bool vararg = false;
|
||||
Location varargLocation;
|
||||
AstTypePack* varargAnnotation;
|
||||
|
@ -949,36 +929,6 @@ class AstStatDeclareFunction : public AstStat
|
|||
public:
|
||||
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(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
|
@ -992,7 +942,6 @@ public:
|
|||
const AstTypeList& retTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
|
@ -1022,12 +971,10 @@ public:
|
|||
AstArray<AstArgumentName> paramNames;
|
||||
bool vararg = false;
|
||||
Location varargLocation;
|
||||
AstTypePack* retTypes;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeList retTypes_DEPRECATED;
|
||||
AstTypeList retTypes;
|
||||
};
|
||||
|
||||
struct AstDeclaredExternTypeProperty
|
||||
struct AstDeclaredClassProp
|
||||
{
|
||||
AstName name;
|
||||
Location nameLocation;
|
||||
|
@ -1053,16 +1000,16 @@ struct AstTableIndexer
|
|||
std::optional<Location> accessLocation;
|
||||
};
|
||||
|
||||
class AstStatDeclareExternType : public AstStat
|
||||
class AstStatDeclareClass : public AstStat
|
||||
{
|
||||
public:
|
||||
LUAU_RTTI(AstStatDeclareExternType)
|
||||
LUAU_RTTI(AstStatDeclareClass)
|
||||
|
||||
AstStatDeclareExternType(
|
||||
AstStatDeclareClass(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
std::optional<AstName> superName,
|
||||
const AstArray<AstDeclaredExternTypeProperty>& props,
|
||||
const AstArray<AstDeclaredClassProp>& props,
|
||||
AstTableIndexer* indexer = nullptr
|
||||
);
|
||||
|
||||
|
@ -1071,7 +1018,7 @@ public:
|
|||
AstName name;
|
||||
std::optional<AstName> superName;
|
||||
|
||||
AstArray<AstDeclaredExternTypeProperty> props;
|
||||
AstArray<AstDeclaredClassProp> props;
|
||||
AstTableIndexer* indexer;
|
||||
};
|
||||
|
||||
|
@ -1148,26 +1095,6 @@ class AstTypeFunction : public AstType
|
|||
public:
|
||||
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(
|
||||
const Location& location,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
|
@ -1177,7 +1104,6 @@ public:
|
|||
const AstTypeList& returnTypes
|
||||
);
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
|
@ -1198,9 +1124,7 @@ public:
|
|||
AstArray<AstGenericTypePack*> genericPacks;
|
||||
AstTypeList argTypes;
|
||||
AstArray<std::optional<AstArgumentName>> argNames;
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstTypeList returnTypes_DEPRECATED;
|
||||
AstTypePack* returnTypes;
|
||||
AstTypeList returnTypes;
|
||||
};
|
||||
|
||||
class AstTypeTypeof : public AstType
|
||||
|
@ -1559,7 +1483,7 @@ public:
|
|||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
virtual bool visit(class AstStatDeclareExternType* node)
|
||||
virtual bool visit(class AstStatDeclareClass* node)
|
||||
{
|
||||
return visit(static_cast<AstStat*>(node));
|
||||
}
|
||||
|
|
|
@ -473,10 +473,8 @@ class CstTypePackExplicit : public CstNode
|
|||
public:
|
||||
LUAU_CST_RTTI(CstTypePackExplicit)
|
||||
|
||||
explicit CstTypePackExplicit();
|
||||
explicit CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions);
|
||||
CstTypePackExplicit(Position openParenthesesPosition, Position closeParenthesesPosition, AstArray<Position> commaPositions);
|
||||
|
||||
bool hasParentheses;
|
||||
Position openParenthesesPosition;
|
||||
Position closeParenthesesPosition;
|
||||
AstArray<Position> commaPositions;
|
||||
|
|
|
@ -157,8 +157,8 @@ private:
|
|||
// type function Name ... end
|
||||
AstStat* parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition);
|
||||
|
||||
AstDeclaredExternTypeProperty parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes);
|
||||
AstDeclaredExternTypeProperty parseDeclaredExternTypeMethod_DEPRECATED();
|
||||
AstDeclaredClassProp parseDeclaredClassMethod(const AstArray<AstAttr*>& attributes);
|
||||
AstDeclaredClassProp parseDeclaredClassMethod_DEPRECATED();
|
||||
|
||||
|
||||
// `declare global' Name: Type |
|
||||
|
@ -182,14 +182,6 @@ private:
|
|||
const Name* localName,
|
||||
const AstArray<AstAttr*>& attributes
|
||||
);
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::pair<AstExprFunction*, AstLocal*> parseFunctionBody_DEPRECATED(
|
||||
bool hasself,
|
||||
const Lexeme& matchFunction,
|
||||
const AstName& debugname,
|
||||
const Name* localName,
|
||||
const AstArray<AstAttr*>& attributes
|
||||
);
|
||||
|
||||
// explist ::= {exp `,'} exp
|
||||
void parseExprList(TempVector<AstExpr*>& result, TempVector<Position>* commaPositions = nullptr);
|
||||
|
@ -227,12 +219,8 @@ private:
|
|||
TempVector<std::optional<Position>>* nameColonPositions = nullptr
|
||||
);
|
||||
|
||||
AstTypePack* parseOptionalReturnType(Position* returnSpecifierPosition = nullptr);
|
||||
AstTypePack* parseReturnType();
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
std::optional<AstTypeList> parseOptionalReturnType_DEPRECATED(Position* returnSpecifierPosition = nullptr);
|
||||
std::pair<Location, AstTypeList> parseReturnType_DEPRECATED();
|
||||
std::optional<AstTypeList> parseOptionalReturnType(Position* returnSpecifierPosition = nullptr);
|
||||
std::pair<Location, AstTypeList> parseReturnType();
|
||||
|
||||
struct TableIndexerResult
|
||||
{
|
||||
|
@ -503,7 +491,7 @@ private:
|
|||
std::vector<CstTypeTable::Item> scratchCstTableTypeProps;
|
||||
std::vector<AstType*> scratchType;
|
||||
std::vector<AstTypeOrPack> scratchTypeOrPack;
|
||||
std::vector<AstDeclaredExternTypeProperty> scratchDeclaredClassProps;
|
||||
std::vector<AstDeclaredClassProp> scratchDeclaredClassProps;
|
||||
std::vector<AstExprTable::Item> scratchItem;
|
||||
std::vector<CstExprTable::Item> scratchCstItem;
|
||||
std::vector<AstArgumentName> scratchArgName;
|
||||
|
|
178
Ast/src/Ast.cpp
178
Ast/src/Ast.cpp
|
@ -4,7 +4,6 @@
|
|||
#include "Luau/Common.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -242,7 +241,7 @@ AstExprFunction::AstExprFunction(
|
|||
AstStatBlock* body,
|
||||
size_t functionDepth,
|
||||
const AstName& debugname,
|
||||
AstTypePack* returnAnnotation,
|
||||
const std::optional<AstTypeList>& returnAnnotation,
|
||||
AstTypePack* varargAnnotation,
|
||||
const std::optional<Location>& argLocation
|
||||
)
|
||||
|
@ -261,41 +260,6 @@ AstExprFunction::AstExprFunction(
|
|||
, debugname(debugname)
|
||||
, argLocation(argLocation)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
AstExprFunction::AstExprFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
AstLocal* self,
|
||||
const AstArray<AstLocal*>& args,
|
||||
bool vararg,
|
||||
const Location& varargLocation,
|
||||
AstStatBlock* body,
|
||||
size_t functionDepth,
|
||||
const AstName& debugname,
|
||||
const std::optional<AstTypeList>& returnAnnotation,
|
||||
AstTypePack* varargAnnotation,
|
||||
const std::optional<Location>& argLocation
|
||||
)
|
||||
: AstExpr(ClassIndex(), location)
|
||||
, attributes(attributes)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, self(self)
|
||||
, args(args)
|
||||
, returnAnnotation_DEPRECATED(returnAnnotation)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, varargAnnotation(varargAnnotation)
|
||||
, body(body)
|
||||
, functionDepth(functionDepth)
|
||||
, debugname(debugname)
|
||||
, argLocation(argLocation)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
void AstExprFunction::visit(AstVisitor* visitor)
|
||||
|
@ -311,16 +275,8 @@ void AstExprFunction::visit(AstVisitor* visitor)
|
|||
if (varargAnnotation)
|
||||
varargAnnotation->visit(visitor);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (returnAnnotation)
|
||||
returnAnnotation->visit(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (returnAnnotation_DEPRECATED)
|
||||
visitTypeList(visitor, *returnAnnotation_DEPRECATED);
|
||||
}
|
||||
visitTypeList(visitor, *returnAnnotation);
|
||||
|
||||
body->visit(visitor);
|
||||
}
|
||||
|
@ -889,62 +845,6 @@ void AstStatDeclareGlobal::visit(AstVisitor* visitor)
|
|||
type->visit(visitor);
|
||||
}
|
||||
|
||||
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,
|
||||
AstTypePack* retTypes
|
||||
)
|
||||
: AstStat(ClassIndex(), location)
|
||||
, attributes()
|
||||
, name(name)
|
||||
, nameLocation(nameLocation)
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, params(params)
|
||||
, paramNames(paramNames)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, 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,
|
||||
|
@ -967,12 +867,10 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
|||
, paramNames(paramNames)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, retTypes_DEPRECATED(retTypes)
|
||||
, retTypes(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
// Clip with FFlagLuauStoreReturnTypesAsPackOnAst
|
||||
AstStatDeclareFunction::AstStatDeclareFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstAttr*>& attributes,
|
||||
|
@ -996,9 +894,8 @@ AstStatDeclareFunction::AstStatDeclareFunction(
|
|||
, paramNames(paramNames)
|
||||
, vararg(vararg)
|
||||
, varargLocation(varargLocation)
|
||||
, retTypes_DEPRECATED(retTypes)
|
||||
, retTypes(retTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
}
|
||||
|
||||
void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
||||
|
@ -1006,10 +903,7 @@ void AstStatDeclareFunction::visit(AstVisitor* visitor)
|
|||
if (visitor->visit(this))
|
||||
{
|
||||
visitTypeList(visitor, params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
retTypes->visit(visitor);
|
||||
else
|
||||
visitTypeList(visitor, retTypes_DEPRECATED);
|
||||
visitTypeList(visitor, retTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1031,11 +925,11 @@ bool AstStatDeclareFunction::hasAttribute(AstAttr::Type attributeType) const
|
|||
return hasAttributeInArray(attributes, attributeType);
|
||||
}
|
||||
|
||||
AstStatDeclareExternType::AstStatDeclareExternType(
|
||||
AstStatDeclareClass::AstStatDeclareClass(
|
||||
const Location& location,
|
||||
const AstName& name,
|
||||
std::optional<AstName> superName,
|
||||
const AstArray<AstDeclaredExternTypeProperty>& props,
|
||||
const AstArray<AstDeclaredClassProp>& props,
|
||||
AstTableIndexer* indexer
|
||||
)
|
||||
: AstStat(ClassIndex(), location)
|
||||
|
@ -1046,11 +940,11 @@ AstStatDeclareExternType::AstStatDeclareExternType(
|
|||
{
|
||||
}
|
||||
|
||||
void AstStatDeclareExternType::visit(AstVisitor* visitor)
|
||||
void AstStatDeclareClass::visit(AstVisitor* visitor)
|
||||
{
|
||||
if (visitor->visit(this))
|
||||
{
|
||||
for (const AstDeclaredExternTypeProperty& prop : props)
|
||||
for (const AstDeclaredClassProp& prop : props)
|
||||
prop.ty->visit(visitor);
|
||||
}
|
||||
}
|
||||
|
@ -1135,48 +1029,6 @@ void AstTypeTable::visit(AstVisitor* visitor)
|
|||
}
|
||||
}
|
||||
|
||||
AstTypeFunction::AstTypeFunction(
|
||||
const Location& location,
|
||||
const AstArray<AstGenericType*>& generics,
|
||||
const AstArray<AstGenericTypePack*>& genericPacks,
|
||||
const AstTypeList& argTypes,
|
||||
const AstArray<std::optional<AstArgumentName>>& argNames,
|
||||
AstTypePack* returnTypes
|
||||
)
|
||||
: AstType(ClassIndex(), location)
|
||||
, 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);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -1191,13 +1043,11 @@ AstTypeFunction::AstTypeFunction(
|
|||
, genericPacks(genericPacks)
|
||||
, argTypes(argTypes)
|
||||
, argNames(argNames)
|
||||
, returnTypes_DEPRECATED(returnTypes)
|
||||
, 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<AstAttr*>& attributes,
|
||||
|
@ -1213,9 +1063,8 @@ AstTypeFunction::AstTypeFunction(
|
|||
, genericPacks(genericPacks)
|
||||
, argTypes(argTypes)
|
||||
, argNames(argNames)
|
||||
, returnTypes_DEPRECATED(returnTypes)
|
||||
, returnTypes(returnTypes)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size);
|
||||
}
|
||||
|
||||
|
@ -1224,10 +1073,7 @@ void AstTypeFunction::visit(AstVisitor* visitor)
|
|||
if (visitor->visit(this))
|
||||
{
|
||||
visitTypeList(visitor, argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
returnTypes->visit(visitor);
|
||||
else
|
||||
visitTypeList(visitor, returnTypes_DEPRECATED);
|
||||
visitTypeList(visitor, returnTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -252,18 +252,8 @@ CstTypeSingletonString::CstTypeSingletonString(AstArray<char> sourceString, CstE
|
|||
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)
|
||||
: CstNode(CstClassIndex())
|
||||
, hasParentheses(true)
|
||||
, openParenthesesPosition(openParenthesesPosition)
|
||||
, closeParenthesesPosition(closeParenthesesPosition)
|
||||
, commaPositions(commaPositions)
|
||||
|
|
|
@ -19,13 +19,14 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
|||
// See docs/SyntaxChanges.md for an explanation.
|
||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunResultInAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
||||
|
||||
|
@ -554,7 +555,7 @@ AstStat* Parser::parseDo()
|
|||
|
||||
Location endLocation = lexer.current().location;
|
||||
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
|
||||
if (body->hasEnd)
|
||||
if (FFlag::LuauFixDoBlockEndLocation && body->hasEnd)
|
||||
body->location.end = endLocation.end;
|
||||
|
||||
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
||||
|
@ -787,9 +788,7 @@ AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
|
|||
|
||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
||||
|
||||
AstExprFunction* body = FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||
? parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first
|
||||
: parseFunctionBody_DEPRECATED(hasself, matchFunction, debugname, nullptr, attributes).first;
|
||||
AstExprFunction* body = parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first;
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
||||
|
||||
|
@ -942,8 +941,7 @@ AstStat* Parser::parseLocal(const AstArray<AstAttr*>& attributes)
|
|||
|
||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
|
||||
|
||||
auto [body, var] = FFlag::LuauStoreReturnTypesAsPackOnAst ? parseFunctionBody(false, matchFunction, name.name, &name, attributes)
|
||||
: parseFunctionBody_DEPRECATED(false, matchFunction, name.name, &name, attributes);
|
||||
auto [body, var] = parseFunctionBody(false, matchFunction, name.name, &name, attributes);
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
|
||||
|
||||
|
@ -1113,10 +1111,7 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio
|
|||
size_t oldTypeFunctionDepth = typeFunctionDepth;
|
||||
typeFunctionDepth = functionStack.size();
|
||||
|
||||
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;
|
||||
AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
|
||||
|
||||
typeFunctionDepth = oldTypeFunctionDepth;
|
||||
|
||||
|
@ -1138,7 +1133,7 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio
|
|||
}
|
||||
}
|
||||
|
||||
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes)
|
||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod(const AstArray<AstAttr*>& attributes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
|
@ -1169,18 +1164,7 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr
|
|||
|
||||
expectMatchAndConsume(')', matchParen);
|
||||
|
||||
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});
|
||||
}
|
||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
TempVector<AstType*> vars(scratchType);
|
||||
|
@ -1188,7 +1172,7 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr
|
|||
|
||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||
{
|
||||
return AstDeclaredExternTypeProperty{
|
||||
return AstDeclaredClassProp{
|
||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||
};
|
||||
}
|
||||
|
@ -1207,25 +1191,14 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr
|
|||
if (vararg && !varargAnnotation)
|
||||
report(start, "All declaration parameters aside from 'self' must be annotated");
|
||||
|
||||
AstType* fnType =
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||
? allocator.alloc<AstTypeFunction>(
|
||||
AstType* fnType = allocator.alloc<AstTypeFunction>(
|
||||
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 AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
return AstDeclaredClassProp{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
}
|
||||
|
||||
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod_DEPRECATED()
|
||||
{
|
||||
Location start = lexer.current().location;
|
||||
|
||||
|
@ -1254,18 +1227,7 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
|||
|
||||
expectMatchAndConsume(')', matchParen);
|
||||
|
||||
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});
|
||||
}
|
||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
TempVector<AstType*> vars(scratchType);
|
||||
|
@ -1273,7 +1235,7 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
|||
|
||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||
{
|
||||
return AstDeclaredExternTypeProperty{
|
||||
return AstDeclaredClassProp{
|
||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||
};
|
||||
}
|
||||
|
@ -1292,16 +1254,11 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
|||
if (vararg && !varargAnnotation)
|
||||
report(start, "All declaration parameters aside from 'self' must be annotated");
|
||||
|
||||
AstType* fnType =
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||
? allocator.alloc<AstTypeFunction>(
|
||||
AstType* fnType = allocator.alloc<AstTypeFunction>(
|
||||
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 AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
return AstDeclaredClassProp{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
}
|
||||
|
||||
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
|
||||
|
@ -1339,18 +1296,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
|
||||
expectMatchAndConsume(')', matchParen);
|
||||
|
||||
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)});
|
||||
}
|
||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0)});
|
||||
Location end = lexer.current().location;
|
||||
|
||||
TempVector<AstType*> vars(scratchType);
|
||||
|
@ -1368,8 +1314,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
if (vararg && !varargAnnotation)
|
||||
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
return allocator.alloc<AstStatDeclareFunction>(
|
||||
Location(start, end),
|
||||
attributes,
|
||||
|
@ -1384,61 +1328,20 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
retTypes
|
||||
);
|
||||
}
|
||||
else
|
||||
else if (AstName(lexer.current().name) == "class")
|
||||
{
|
||||
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();
|
||||
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;
|
||||
Name className = parseName(FFlag::LuauDeclareExternType ? "type name" : "class name");
|
||||
Name className = parseName("class name");
|
||||
std::optional<AstName> superName = std::nullopt;
|
||||
|
||||
if (AstName(lexer.current().name) == "extends")
|
||||
{
|
||||
nextLexeme();
|
||||
superName = parseName(FFlag::LuauDeclareExternType ? "supertype name" : "superclass name").name;
|
||||
superName = parseName("superclass name").name;
|
||||
}
|
||||
|
||||
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);
|
||||
TempVector<AstDeclaredClassProp> props(scratchDeclaredClassProps);
|
||||
AstTableIndexer* indexer = nullptr;
|
||||
|
||||
while (lexer.current().type != Lexeme::ReservedEnd)
|
||||
|
@ -1465,9 +1368,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
props.push_back(parseDeclaredClassMethod(attributes));
|
||||
else
|
||||
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||
props.push_back(parseDeclaredClassMethod_DEPRECATED());
|
||||
}
|
||||
else if (lexer.current().type == '[')
|
||||
{
|
||||
|
@ -1490,7 +1393,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
|
||||
if (chars && !containsNull)
|
||||
{
|
||||
props.push_back(AstDeclaredExternTypeProperty{
|
||||
props.push_back(AstDeclaredClassProp{
|
||||
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
||||
});
|
||||
}
|
||||
|
@ -1510,9 +1413,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, begin);
|
||||
|
||||
// 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");
|
||||
}
|
||||
else
|
||||
|
@ -1534,7 +1434,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
expectAndConsume(':', "property type annotation");
|
||||
AstType* propType = parseType();
|
||||
props.push_back(
|
||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
AstDeclaredClassProp{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1544,9 +1444,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
props.push_back(parseDeclaredClassMethod(attributes));
|
||||
else
|
||||
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||
props.push_back(parseDeclaredClassMethod_DEPRECATED());
|
||||
}
|
||||
else if (lexer.current().type == '[' &&
|
||||
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
|
||||
|
@ -1568,7 +1468,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
|
||||
if (chars && !containsNull)
|
||||
{
|
||||
props.push_back(AstDeclaredExternTypeProperty{
|
||||
props.push_back(AstDeclaredClassProp{
|
||||
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
||||
});
|
||||
}
|
||||
|
@ -1592,9 +1492,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
badIndexer = parseTableIndexer_DEPRECATED(AstTableAccess::ReadWrite, std::nullopt, lexer.current());
|
||||
|
||||
// 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");
|
||||
}
|
||||
else
|
||||
|
@ -1618,7 +1515,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
expectAndConsume(':', "property type annotation");
|
||||
AstType* propType = parseType();
|
||||
props.push_back(
|
||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
AstDeclaredClassProp{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1627,7 +1524,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
Location classEnd = lexer.current().location;
|
||||
nextLexeme(); // skip past `end`
|
||||
|
||||
return allocator.alloc<AstStatDeclareExternType>(Location(classStart, classEnd), className.name, superName, copy(props), indexer);
|
||||
return allocator.alloc<AstStatDeclareClass>(Location(classStart, classEnd), className.name, superName, copy(props), indexer);
|
||||
}
|
||||
else if (std::optional<Name> globalName = parseNameOpt("global variable name"))
|
||||
{
|
||||
|
@ -1636,10 +1533,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
AstType* type = parseType(/* in declaration context */ true);
|
||||
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
|
||||
{
|
||||
return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'class'");
|
||||
|
@ -1745,8 +1638,6 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
|||
const AstArray<AstAttr*>& attributes
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
|
||||
Location start = matchFunction.location;
|
||||
|
||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
|
@ -1798,145 +1689,7 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
|||
|
||||
matchRecoveryStopOnToken[')']--;
|
||||
|
||||
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);
|
||||
std::optional<AstTypeList> typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
|
||||
|
||||
AstLocal* funLocal = nullptr;
|
||||
|
||||
|
@ -2163,9 +1916,8 @@ AstTypePack* Parser::parseTypeList(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
|
||||
std::optional<AstTypeList> Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst);
|
||||
if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow)
|
||||
{
|
||||
if (lexer.current().type == Lexeme::SkinnyArrow)
|
||||
|
@ -2177,41 +1929,7 @@ AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
|
|||
|
||||
unsigned int oldRecursionCount = recursionCounter;
|
||||
|
||||
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();
|
||||
auto [_location, result] = parseReturnType();
|
||||
|
||||
// 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.
|
||||
|
@ -2231,119 +1949,8 @@ std::optional<AstTypeList> Parser::parseOptionalReturnType_DEPRECATED(Luau::Posi
|
|||
}
|
||||
|
||||
// ReturnType ::= Type | `(' TypeList `)'
|
||||
AstTypePack* Parser::parseReturnType()
|
||||
std::pair<Location, AstTypeList> 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");
|
||||
|
||||
Lexeme begin = lexer.current();
|
||||
|
@ -2967,25 +2574,12 @@ AstType* Parser::parseFunctionTypeTail(
|
|||
expectAndConsume(Lexeme::SkinnyArrow, "function type");
|
||||
}
|
||||
|
||||
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();
|
||||
auto [endLocation, returnTypeList] = parseReturnType();
|
||||
|
||||
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
||||
return allocator.alloc<AstTypeFunction>(
|
||||
Location(begin.location, endLocation), attributes, generics, genericPacks, paramTypes, paramNames, returnTypeList
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isTypeFollow(Lexeme::Type c)
|
||||
|
@ -3099,8 +2693,17 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||
{
|
||||
if (parts.size() == 1 && !isUnion && !isIntersection)
|
||||
return parts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parts.size() == 1)
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
if (isUnion && isIntersection)
|
||||
{
|
||||
return reportTypeError(
|
||||
|
@ -3933,10 +3536,7 @@ AstExpr* Parser::parseSimpleExpr()
|
|||
Lexeme matchFunction = lexer.current();
|
||||
nextLexeme();
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
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)
|
||||
{
|
||||
|
@ -5066,7 +4666,7 @@ void Parser::report(const Location& location, const char* format, va_list args)
|
|||
|
||||
parseErrors.emplace_back(location, message);
|
||||
|
||||
if (parseErrors.size() >= unsigned(FInt::LuauParseErrorLimit) && !options.noErrorLimit)
|
||||
if (parseErrors.size() >= unsigned(FInt::LuauParseErrorLimit) && (!FFlag::ParserNoErrorLimit || !options.noErrorLimit))
|
||||
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);
|
||||
}
|
||||
|
||||
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents)
|
||||
static int load(lua_State* L, void* ctx, const char* chunkname, const char* contents)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo)
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -4319,8 +4317,6 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
mainFlags |= LPF_NATIVE_FUNCTION;
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
|
@ -4332,8 +4328,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
/* varargLocation= */ Luau::Location(),
|
||||
root,
|
||||
/* functionDepth= */ 0,
|
||||
/* debugname= */ AstName(),
|
||||
/* returnAnnotation= */ nullptr
|
||||
/* debugname= */ AstName()
|
||||
);
|
||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||
|
||||
|
@ -4342,31 +4337,6 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
|
||||
bytecode.setMainFunction(mainid);
|
||||
bytecode.finalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
AstExprFunction main(
|
||||
root->location,
|
||||
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
|
||||
/* generics= */ AstArray<AstGenericType*>(),
|
||||
/* genericPacks= */ AstArray<AstGenericTypePack*>(),
|
||||
/* self= */ nullptr,
|
||||
AstArray<AstLocal*>(),
|
||||
/* vararg= */ true,
|
||||
/* varargLocation= */ Luau::Location(),
|
||||
root,
|
||||
/* functionDepth= */ 0,
|
||||
/* debugname= */ AstName(),
|
||||
/* returnAnnotation= */ std::nullopt
|
||||
);
|
||||
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
|
||||
|
||||
const Compiler::Function* mainf = compiler.functions.find(&main);
|
||||
LUAU_ASSERT(mainf && mainf->upvals.empty());
|
||||
|
||||
bytecode.setMainFunction(mainid);
|
||||
bytecode.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Luau/Config.h"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -87,11 +88,6 @@ private:
|
|||
[[nodiscard]] Error navigateToAlias(const std::string& alias, const std::string& value);
|
||||
[[nodiscard]] Error navigateToAndPopulateConfig(const std::string& desiredAlias);
|
||||
|
||||
[[nodiscard]] Error resetToRequirer();
|
||||
[[nodiscard]] Error jumpToAlias(const std::string& aliasPath);
|
||||
[[nodiscard]] Error navigateToParent(std::optional<std::string> previousComponent);
|
||||
[[nodiscard]] Error navigateToChild(const std::string& component);
|
||||
|
||||
NavigationContext& navigationContext;
|
||||
ErrorHandler& errorHandler;
|
||||
Luau::Config config;
|
||||
|
|
|
@ -10,11 +10,24 @@
|
|||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
static constexpr char kRequireErrorAmbiguous[] = "require path could not be resolved to a unique file";
|
||||
static constexpr char kRequireErrorGeneric[] = "error requiring module";
|
||||
|
||||
namespace Luau::Require
|
||||
{
|
||||
|
||||
using Error = std::optional<std::string>;
|
||||
|
||||
static Error toError(NavigationContext::NavigateResult result)
|
||||
{
|
||||
if (result == NavigationContext::NavigateResult::Success)
|
||||
return std::nullopt;
|
||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||
return kRequireErrorAmbiguous;
|
||||
else
|
||||
return kRequireErrorGeneric;
|
||||
}
|
||||
|
||||
static std::string extractAlias(std::string_view path)
|
||||
{
|
||||
// To ignore the '@' alias prefix when processing the alias
|
||||
|
@ -40,7 +53,7 @@ Navigator::Status Navigator::navigate(std::string path)
|
|||
{
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
|
||||
if (Error error = resetToRequirer())
|
||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
||||
{
|
||||
errorHandler.reportError(*error);
|
||||
return Status::ErrorReported;
|
||||
|
@ -85,7 +98,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
|||
|
||||
// If the alias is "@self", we reset to the requirer's context and
|
||||
// navigate directly from there.
|
||||
if (Error error = resetToRequirer())
|
||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
||||
return error;
|
||||
if (Error error = navigateThroughPath(path))
|
||||
return error;
|
||||
|
@ -101,7 +114,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
|||
|
||||
if (pathType == PathType::RelativeToCurrent || pathType == PathType::RelativeToParent)
|
||||
{
|
||||
if (Error error = navigateToParent(std::nullopt))
|
||||
if (Error error = toError(navigationContext.toParent()))
|
||||
return error;
|
||||
if (Error error = navigateThroughPath(path))
|
||||
return error;
|
||||
|
@ -120,7 +133,6 @@ Error Navigator::navigateThroughPath(std::string_view path)
|
|||
components = splitPath(components.second);
|
||||
}
|
||||
|
||||
std::optional<std::string> previousComponent;
|
||||
while (!(components.first.empty() && components.second.empty()))
|
||||
{
|
||||
if (components.first == "." || components.first.empty())
|
||||
|
@ -130,15 +142,14 @@ Error Navigator::navigateThroughPath(std::string_view path)
|
|||
}
|
||||
else if (components.first == "..")
|
||||
{
|
||||
if (Error error = navigateToParent(previousComponent))
|
||||
if (Error error = toError(navigationContext.toParent()))
|
||||
return error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Error error = navigateToChild(std::string{components.first}))
|
||||
if (Error error = toError(navigationContext.toChild(std::string{components.first})))
|
||||
return error;
|
||||
}
|
||||
previousComponent = components.first;
|
||||
components = splitPath(components.second);
|
||||
}
|
||||
|
||||
|
@ -156,11 +167,11 @@ Error Navigator::navigateToAlias(const std::string& alias, const std::string& va
|
|||
}
|
||||
else if (pathType == PathType::Aliased)
|
||||
{
|
||||
return "alias \"@" + alias + "\" cannot point to an aliased path (\"" + value + "\")";
|
||||
return "@" + alias + " cannot point to other aliases";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Error error = jumpToAlias(value))
|
||||
if (Error error = toError(navigationContext.jumpToAlias(value)))
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -178,7 +189,7 @@ Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
|||
{
|
||||
std::optional<std::string> configContents = navigationContext.getConfig();
|
||||
if (!configContents)
|
||||
return "could not get configuration file contents to resolve alias \"" + desiredAlias + "\"";
|
||||
return "could not get configuration file contents";
|
||||
|
||||
Luau::ConfigOptions opts;
|
||||
Luau::ConfigOptions::AliasOptions aliasOpts;
|
||||
|
@ -194,56 +205,4 @@ Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
Error Navigator::resetToRequirer()
|
||||
{
|
||||
NavigationContext::NavigateResult result = navigationContext.reset(navigationContext.getRequirerIdentifier());
|
||||
if (result == NavigationContext::NavigateResult::Success)
|
||||
return std::nullopt;
|
||||
|
||||
std::string errorMessage = "could not reset to requiring context";
|
||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||
errorMessage += " (ambiguous)";
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
Error Navigator::jumpToAlias(const std::string& aliasPath)
|
||||
{
|
||||
NavigationContext::NavigateResult result = navigationContext.jumpToAlias(aliasPath);
|
||||
if (result == NavigationContext::NavigateResult::Success)
|
||||
return std::nullopt;
|
||||
|
||||
std::string errorMessage = "could not jump to alias \"" + aliasPath + "\"";
|
||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||
errorMessage += " (ambiguous)";
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
Error Navigator::navigateToParent(std::optional<std::string> previousComponent)
|
||||
{
|
||||
NavigationContext::NavigateResult result = navigationContext.toParent();
|
||||
if (result == NavigationContext::NavigateResult::Success)
|
||||
return std::nullopt;
|
||||
|
||||
std::string errorMessage;
|
||||
if (previousComponent)
|
||||
errorMessage = "could not get parent of component \"" + *previousComponent + "\"";
|
||||
else
|
||||
errorMessage = "could not get parent of requiring context";
|
||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||
errorMessage += " (ambiguous)";
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
Error Navigator::navigateToChild(const std::string& component)
|
||||
{
|
||||
NavigationContext::NavigateResult result = navigationContext.toChild(component);
|
||||
if (result == NavigationContext::NavigateResult::Success)
|
||||
return std::nullopt;
|
||||
|
||||
std::string errorMessage = "could not resolve child component \"" + component + "\"";
|
||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||
errorMessage += " (ambiguous)";
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
} // namespace Luau::Require
|
||||
|
|
|
@ -106,10 +106,8 @@ struct luarequire_Configuration
|
|||
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||
|
||||
// Executes the module and places the result on the stack. Returns the
|
||||
// number of results placed on the stack. Returning -1 directs the requiring
|
||||
// thread to yield. In this case, this thread should be resumed with the
|
||||
// module result pushed onto its stack.
|
||||
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents);
|
||||
// number of results placed on the stack.
|
||||
int (*load)(lua_State* L, void* ctx, const char* chunkname, const char* contents);
|
||||
};
|
||||
|
||||
// Populates function pointers in the given luarequire_Configuration.
|
||||
|
@ -117,25 +115,7 @@ typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);
|
|||
|
||||
// Initializes and pushes the require closure onto the stack without
|
||||
// registration.
|
||||
LUALIB_API int luarequire_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||
LUALIB_API int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
||||
|
||||
// Initializes the require library and registers it globally.
|
||||
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);
|
||||
|
||||
// Clears the entry associated with the given cache key from the require cache.
|
||||
// Expects the cache key to be passed as an argument on the stack.
|
||||
LUALIB_API int luarequire_clearcacheentry(lua_State* L);
|
||||
|
||||
// Clears all entries from the require cache.
|
||||
LUALIB_API int luarequire_clearcache(lua_State* L);
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
static constexpr size_t initalFileBufferSize = 1024;
|
||||
static constexpr size_t initalIdentifierBufferSize = 64;
|
||||
|
||||
|
@ -113,16 +111,14 @@ std::optional<std::string> RuntimeNavigationContext::getStringFromCWriter(
|
|||
}
|
||||
|
||||
|
||||
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L, std::string requiredPath)
|
||||
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L)
|
||||
: L(L)
|
||||
, errorPrefix("error requiring module \"" + std::move(requiredPath) + "\": ")
|
||||
{
|
||||
}
|
||||
|
||||
void RuntimeErrorHandler::reportError(std::string message)
|
||||
{
|
||||
std::string fullError = errorPrefix + std::move(message);
|
||||
luaL_errorL(L, "%s", fullError.c_str());
|
||||
luaL_errorL(L, "%s", message.c_str());
|
||||
}
|
||||
|
||||
} // namespace Luau::Require
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "Luau/RequireNavigator.h"
|
||||
#include "Luau/Require.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
struct lua_State;
|
||||
struct luarequire_Configuration;
|
||||
|
||||
|
@ -50,12 +48,11 @@ private:
|
|||
class RuntimeErrorHandler : public ErrorHandler
|
||||
{
|
||||
public:
|
||||
RuntimeErrorHandler(lua_State* L, std::string requiredPath);
|
||||
RuntimeErrorHandler(lua_State* L);
|
||||
void reportError(std::string message) override;
|
||||
|
||||
private:
|
||||
lua_State* L;
|
||||
std::string errorPrefix;
|
||||
};
|
||||
|
||||
} // namespace Luau::Require
|
||||
|
|
|
@ -35,13 +35,7 @@ static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
|||
luaL_error(L, "require configuration is missing required function pointer: load");
|
||||
}
|
||||
|
||||
static int pushrequireclosureinternal(
|
||||
lua_State* L,
|
||||
luarequire_Configuration_init config_init,
|
||||
void* ctx,
|
||||
lua_CFunction requirelikefunc,
|
||||
const char* debugname
|
||||
)
|
||||
int lua_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
||||
{
|
||||
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
||||
if (!config)
|
||||
|
@ -52,38 +46,13 @@ static int pushrequireclosureinternal(
|
|||
|
||||
lua_pushlightuserdata(L, ctx);
|
||||
|
||||
// require-like closure captures config and ctx as upvalues
|
||||
lua_pushcclosurek(L, requirelikefunc, debugname, 2, Luau::Require::lua_requirecont);
|
||||
// "require" captures config and ctx as upvalues
|
||||
lua_pushcclosure(L, Luau::Require::lua_require, "require", 2);
|
||||
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)
|
||||
{
|
||||
luarequire_pushrequire(L, config_init, ctx);
|
||||
lua_pushrequire(L, config_init, ctx);
|
||||
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);
|
||||
}
|
||||
|
||||
int luarequire_clearcacheentry(lua_State* L)
|
||||
{
|
||||
return Luau::Require::clearCacheEntry(L);
|
||||
}
|
||||
|
||||
int luarequire_clearcache(lua_State* L)
|
||||
{
|
||||
return Luau::Require::clearCache(L);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,7 @@
|
|||
namespace Luau::Require
|
||||
{
|
||||
|
||||
// Stores explicitly registered modules.
|
||||
static const char* registeredCacheTableKey = "_REGISTEREDMODULES";
|
||||
|
||||
// Stores the results of require calls.
|
||||
static const char* requiredCacheTableKey = "_MODULES";
|
||||
static const char* cacheTableKey = "_MODULES";
|
||||
|
||||
struct ResolvedRequire
|
||||
{
|
||||
|
@ -36,7 +32,7 @@ struct ResolvedRequire
|
|||
|
||||
static bool isCached(lua_State* L, const std::string& key)
|
||||
{
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, requiredCacheTableKey, 1);
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, cacheTableKey, 1);
|
||||
lua_getfield(L, -1, key.c_str());
|
||||
bool cached = !lua_isnil(L, -1);
|
||||
lua_pop(L, 2);
|
||||
|
@ -44,13 +40,16 @@ static bool isCached(lua_State* L, const std::string& key)
|
|||
return cached;
|
||||
}
|
||||
|
||||
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, const char* requirerChunkname, std::string path)
|
||||
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, std::string path)
|
||||
{
|
||||
if (!lrc->is_require_allowed(L, ctx, requirerChunkname))
|
||||
lua_Debug ar;
|
||||
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");
|
||||
|
||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
||||
RuntimeErrorHandler errorHandler{L, path}; // Errors reported directly to lua_State.
|
||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, ar.source};
|
||||
RuntimeErrorHandler errorHandler{L}; // Errors reported directly to lua_State.
|
||||
|
||||
Navigator navigator(navigationContext, errorHandler);
|
||||
|
||||
|
@ -61,7 +60,7 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
|||
|
||||
if (!navigationContext.isModulePresent())
|
||||
{
|
||||
errorHandler.reportError("no module present at resolved path");
|
||||
luaL_errorL(L, "no module present at resolved path");
|
||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
||||
}
|
||||
|
||||
|
@ -75,7 +74,7 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
|||
if (isCached(L, *cacheKey))
|
||||
{
|
||||
// Put cached result on top of stack before returning.
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
||||
lua_getfield(L, -1, cacheKey->c_str());
|
||||
lua_remove(L, -2);
|
||||
|
||||
|
@ -104,27 +103,21 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
|||
};
|
||||
}
|
||||
|
||||
static int checkRegisteredModules(lua_State* L, const char* path)
|
||||
int lua_require(lua_State* L)
|
||||
{
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1);
|
||||
lua_getfield(L, -1, path);
|
||||
if (lua_isnil(L, -1))
|
||||
{
|
||||
lua_pop(L, 2);
|
||||
return 0;
|
||||
}
|
||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||
if (!lrc)
|
||||
luaL_error(L, "unable to find require configuration");
|
||||
|
||||
lua_remove(L, -2);
|
||||
void* ctx = lua_tolightuserdata(L, lua_upvalueindex(2));
|
||||
|
||||
const char* path = luaL_checkstring(L, 1);
|
||||
|
||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, path);
|
||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua_requirecont(lua_State* L, int status)
|
||||
{
|
||||
// Number of stack arguments present before this continuation is called.
|
||||
const int numStackArgs = 2;
|
||||
const int numResults = lua_gettop(L) - numStackArgs;
|
||||
const char* cacheKey = luaL_checkstring(L, 2);
|
||||
|
||||
int numResults = lrc->load(L, ctx, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
||||
if (numResults > 1)
|
||||
luaL_error(L, "module must return a single value");
|
||||
|
||||
|
@ -134,13 +127,13 @@ int lua_requirecont(lua_State* L, int status)
|
|||
// Initial stack state
|
||||
// (-1) result
|
||||
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
||||
// (-2) result, (-1) cache table
|
||||
|
||||
lua_pushvalue(L, -2);
|
||||
// (-3) result, (-2) cache table, (-1) result
|
||||
|
||||
lua_setfield(L, -2, cacheKey);
|
||||
lua_setfield(L, -2, resolvedRequire.cacheKey.c_str());
|
||||
// (-2) result, (-1) cache table
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
@ -150,103 +143,4 @@ int lua_requirecont(lua_State* L, int status)
|
|||
return numResults;
|
||||
}
|
||||
|
||||
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||
{
|
||||
// If modifying the state of the stack, please update numStackArgs in the
|
||||
// lua_requirecont continuation function.
|
||||
|
||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||
if (!lrc)
|
||||
luaL_error(L, "unable to find require configuration");
|
||||
|
||||
void* ctx = lua_tolightuserdata(L, lua_upvalueindex(2));
|
||||
|
||||
// (1) path
|
||||
const char* path = luaL_checkstring(L, 1);
|
||||
|
||||
if (checkRegisteredModules(L, path) == 1)
|
||||
return 1;
|
||||
|
||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
||||
return 1;
|
||||
|
||||
// (1) path, (2) cacheKey
|
||||
lua_pushstring(L, resolvedRequire.cacheKey.c_str());
|
||||
|
||||
int numArgsBeforeLoad = lua_gettop(L);
|
||||
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
||||
if (numResults == -1)
|
||||
{
|
||||
if (lua_gettop(L) != numArgsBeforeLoad)
|
||||
luaL_error(L, "stack cannot be modified when require yields");
|
||||
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
return lua_requirecont(L, LUA_OK);
|
||||
}
|
||||
|
||||
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;
|
||||
int level = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (!lua_getinfo(L, level++, "s", &ar))
|
||||
luaL_error(L, "require is not supported in this context");
|
||||
} while (ar.what[0] == 'C');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int clearCacheEntry(lua_State* L)
|
||||
{
|
||||
const char* cacheKey = luaL_checkstring(L, 1);
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, requiredCacheTableKey, 1);
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, cacheKey);
|
||||
lua_pop(L, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clearCache(lua_State* L)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Luau::Require
|
||||
|
|
|
@ -7,12 +7,5 @@ namespace Luau::Require
|
|||
{
|
||||
|
||||
int lua_require(lua_State* L);
|
||||
int lua_proxyrequire(lua_State* L);
|
||||
int lua_requirecont(lua_State* L, int status);
|
||||
|
||||
int registerModuleImpl(lua_State* L);
|
||||
|
||||
int clearCacheEntry(lua_State* L);
|
||||
int clearCache(lua_State* L);
|
||||
|
||||
} // namespace Luau::Require
|
||||
|
|
|
@ -179,7 +179,6 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/include/Luau/Clone.h
|
||||
Analysis/include/Luau/Constraint.h
|
||||
Analysis/include/Luau/ConstraintGenerator.h
|
||||
Analysis/include/Luau/ConstraintSet.h
|
||||
Analysis/include/Luau/ConstraintSolver.h
|
||||
Analysis/include/Luau/ControlFlow.h
|
||||
Analysis/include/Luau/DataFlowGraph.h
|
||||
|
|
|
@ -58,9 +58,6 @@ 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);
|
||||
|
||||
// wrapper for making calls from yieldable C functions
|
||||
LUALIB_API int luaL_callyieldable(lua_State* L, int nargs, int nresults);
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLibWhereErrorAutoreserve)
|
||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||
|
||||
// convert a stack index to positive
|
||||
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||
|
@ -356,22 +355,6 @@ const char* luaL_typename(lua_State* L, int idx)
|
|||
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
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||
|
||||
static void writestring(const char* s, size_t l)
|
||||
{
|
||||
fwrite(s, 1, l, stdout);
|
||||
|
@ -296,18 +294,10 @@ static int luaB_pcally(lua_State* L)
|
|||
// any errors from this point on are handled by continuation
|
||||
L->ci->flags |= LUA_CALLINFO_HANDLE;
|
||||
|
||||
if (!FFlag::LuauYieldableContinuations)
|
||||
{
|
||||
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
||||
L->baseCcalls++;
|
||||
}
|
||||
|
||||
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), 0);
|
||||
|
||||
if (!FFlag::LuauYieldableContinuations)
|
||||
{
|
||||
L->baseCcalls--;
|
||||
}
|
||||
|
||||
// necessary to accomodate functions that return lots of values
|
||||
expandstacklimit(L, L->top);
|
||||
|
@ -358,20 +348,12 @@ static int luaB_xpcally(lua_State* L)
|
|||
StkId errf = L->base;
|
||||
StkId func = L->base + 1;
|
||||
|
||||
if (!FFlag::LuauYieldableContinuations)
|
||||
{
|
||||
// maintain yieldable invariant (baseCcalls <= nCcalls)
|
||||
L->baseCcalls++;
|
||||
}
|
||||
|
||||
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
|
||||
// necessary to accomodate functions that return lots of values
|
||||
expandstacklimit(L, L->top);
|
||||
|
||||
// yielding means we need to propagate yield; resume will call continuation function later
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauYieldableContinuations)
|
||||
|
||||
// keep max stack allocation request under 1GB
|
||||
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
||||
|
||||
|
@ -262,60 +260,6 @@ void luaD_call(lua_State* L, StkId func, int nresults)
|
|||
if (++L->nCcalls >= LUAI_MAXCCALLS)
|
||||
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);
|
||||
|
||||
if (luau_precall(L, func, nresults) == PCRLUA)
|
||||
|
@ -334,7 +278,6 @@ void luaD_call(lua_State* L, StkId func, int nresults)
|
|||
|
||||
if (nresults != LUA_MULTRET)
|
||||
L->top = restorestack(L, old_func) + nresults;
|
||||
}
|
||||
|
||||
L->nCcalls--;
|
||||
luaC_checkGC(L);
|
||||
|
@ -380,18 +323,9 @@ static void resume_continue(lua_State* L)
|
|||
// C continuation; we expect this to be followed by Lua continuations
|
||||
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
|
||||
if (L->status == LUA_BREAK)
|
||||
break;
|
||||
}
|
||||
|
||||
luau_poscall(L, L->top - n);
|
||||
}
|
||||
|
@ -436,11 +370,6 @@ static void resume(lua_State* L, void* ud)
|
|||
// finish interrupted execution of `OP_CALL'
|
||||
luau_poscall(L, firstArg);
|
||||
}
|
||||
else if (FFlag::LuauYieldableContinuations)
|
||||
{
|
||||
// restore arguments we have protected for C continuation
|
||||
L->base = L->ci->base;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -647,7 +576,6 @@ 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)
|
||||
{
|
||||
unsigned short oldnCcalls = L->nCcalls;
|
||||
unsigned short oldbaseCcalls = FFlag::LuauYieldableContinuations ? L->baseCcalls : 0;
|
||||
ptrdiff_t old_ci = saveci(L, L->ci);
|
||||
bool oldactive = L->isactive;
|
||||
int status = luaD_rawrunprotected(L, func, u);
|
||||
|
@ -684,9 +612,6 @@ 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.
|
||||
L->nCcalls = oldnCcalls;
|
||||
|
||||
if (FFlag::LuauYieldableContinuations)
|
||||
L->baseCcalls = oldbaseCcalls;
|
||||
|
||||
// an error occurred, check if we have a protected error callback
|
||||
if (yieldable && L->global->cb.debugprotectederror)
|
||||
{
|
||||
|
|
|
@ -124,8 +124,8 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
|||
// Vector3 stub
|
||||
TypeId vector3MetaType = arena.addType(TableType{});
|
||||
|
||||
TypeId vector3InstanceType = arena.addType(ExternType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(vector3InstanceType)->props = {
|
||||
TypeId vector3InstanceType = arena.addType(ClassType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(vector3InstanceType)->props = {
|
||||
{"X", {builtinTypes.numberType}},
|
||||
{"Y", {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};
|
||||
|
||||
// Instance stub
|
||||
TypeId instanceType = arena.addType(ExternType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(instanceType)->props = {
|
||||
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(instanceType)->props = {
|
||||
{"Name", {builtinTypes.stringType}},
|
||||
};
|
||||
|
||||
globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
||||
|
||||
// Part stub
|
||||
TypeId partType = arena.addType(ExternType{"Part", {}, instanceType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(partType)->props = {
|
||||
TypeId partType = arena.addType(ClassType{"Part", {}, instanceType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(partType)->props = {
|
||||
{"Position", {vector3InstanceType}},
|
||||
};
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ static const std::string kTypes[] = {
|
|||
"vector",
|
||||
};
|
||||
|
||||
static const std::string kExternTypes[] = {
|
||||
static const std::string kClasses[] = {
|
||||
"Vector3",
|
||||
"Instance",
|
||||
"Part",
|
||||
|
@ -902,8 +902,8 @@ struct ProtoToLuau
|
|||
|
||||
void print(const luau::TypeClass& type)
|
||||
{
|
||||
size_t index = size_t(type.kind()) % std::size(kExternTypes);
|
||||
source += kExternTypes[index];
|
||||
size_t index = size_t(type.kind()) % std::size(kClasses);
|
||||
source += kClasses[index];
|
||||
}
|
||||
|
||||
void print(const luau::TypeRef& type)
|
||||
|
|
|
@ -13,7 +13,6 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
struct JsonEncoderFixture
|
||||
{
|
||||
|
@ -421,39 +420,20 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
|
|||
{
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number): string");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypePackExplicit","location":"0,33 - 0,39","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
||||
{
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypePackExplicit","location":"0,46 - 0,52","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]}},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]},"generics":[],"genericPacks":[]})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr")
|
||||
|
@ -483,18 +463,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
|
|||
|
||||
REQUIRE(2 == root->body.size);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected1 =
|
||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypePackExplicit","location":"3,48 - 3,54","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}},"location":"3,12 - 3,54"}],"indexer":null})";
|
||||
CHECK(toJson(root->body.data[0]) == expected1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected1 =
|
||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}},"location":"3,12 - 3,54"}],"indexer":null})";
|
||||
CHECK(toJson(root->body.data[0]) == expected1);
|
||||
}
|
||||
|
||||
std::string_view 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})";
|
||||
|
@ -505,19 +476,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
|||
{
|
||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||
|
||||
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)
|
||||
if (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":"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})";
|
||||
|
@ -557,18 +516,10 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction")
|
|||
{
|
||||
AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypePackExplicit","location":"0,44 - 0,46","typeList":{"type":"AstTypeList","types":[]}}},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})";
|
||||
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError")
|
||||
|
@ -599,63 +550,4 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypePackExplicit")
|
|||
CHECK(toJson(root->body.data[1]) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericType")
|
||||
{
|
||||
AstStatBlock* root = expectParse(R"(
|
||||
a = function<b, c>()
|
||||
end
|
||||
)");
|
||||
|
||||
CHECK(1 == root->body.size);
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatAssign","location":"1,8 - 2,11","vars":[{"type":"AstExprGlobal","location":"1,8 - 1,9","global":"a"}],"values":[{"type":"AstExprFunction","location":"1,12 - 2,11","attributes":[],"generics":[{"type":"AstGenericType","name":"b"},{"type":"AstGenericType","name":"c"}],"genericPacks":[],"args":[],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"1,28 - 2,8","hasEnd":true,"body":[]},"functionDepth":1,"debugname":""}]})";
|
||||
|
||||
CHECK(toJson(root->body.data[0]) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypeWithDefault")
|
||||
{
|
||||
AstStatBlock* root = expectParse(R"(
|
||||
type Foo<X = string> = X
|
||||
)");
|
||||
|
||||
CHECK(1 == root->body.size);
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"1,8 - 1,32","name":"Foo","generics":[{"type":"AstGenericType","name":"X","luauType":{"type":"AstTypeReference","location":"1,21 - 1,27","name":"string","nameLocation":"1,21 - 1,27","parameters":[]}}],"genericPacks":[],"value":{"type":"AstTypeReference","location":"1,31 - 1,32","name":"X","nameLocation":"1,31 - 1,32","parameters":[]},"exported":false})";
|
||||
|
||||
CHECK(toJson(root->body.data[0]) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePack")
|
||||
{
|
||||
AstStatBlock* root = expectParse(R"(
|
||||
a = function<b..., c...>()
|
||||
end
|
||||
)");
|
||||
|
||||
CHECK(1 == root->body.size);
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatAssign","location":"1,8 - 2,11","vars":[{"type":"AstExprGlobal","location":"1,8 - 1,9","global":"a"}],"values":[{"type":"AstExprFunction","location":"1,12 - 2,11","attributes":[],"generics":[],"genericPacks":[{"type":"AstGenericTypePack","name":"b"},{"type":"AstGenericTypePack","name":"c"}],"args":[],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"1,34 - 2,8","hasEnd":true,"body":[]},"functionDepth":1,"debugname":""}]})";
|
||||
|
||||
CHECK(toJson(root->body.data[0]) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePackWithDefault")
|
||||
{
|
||||
AstStatBlock* root = expectParse(R"(
|
||||
type Foo<X... = ...string> = any
|
||||
)");
|
||||
|
||||
CHECK(1 == root->body.size);
|
||||
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"1,8 - 1,40","name":"Foo","generics":[],"genericPacks":[{"type":"AstGenericTypePack","name":"X","luauType":{"type":"AstTypePackVariadic","location":"1,24 - 1,33","variadicType":{"type":"AstTypeReference","location":"1,27 - 1,33","name":"string","nameLocation":"1,27 - 1,33","parameters":[]}}}],"value":{"type":"AstTypeReference","location":"1,37 - 1,40","name":"any","nameLocation":"1,37 - 1,40","parameters":[]},"exported":false})";
|
||||
|
||||
CHECK(toJson(root->body.data[0]) == expected);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
|
|
|
@ -20,13 +20,14 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
|||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
|
||||
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
||||
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ struct ACBuiltinsFixture : ACFixtureImpl<BuiltinsFixture>
|
|||
{
|
||||
};
|
||||
|
||||
struct ACExternTypeFixture : ACFixtureImpl<ExternTypeFixture>
|
||||
struct ACClassFixture : ACFixtureImpl<ClassFixture>
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -3753,7 +3754,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback")
|
|||
bool isCorrect = false;
|
||||
auto ac1 = autocomplete(
|
||||
'1',
|
||||
[&isCorrect](std::string, std::optional<const ExternType*>, std::optional<std::string> contents) -> std::optional<AutocompleteEntryMap>
|
||||
[&isCorrect](std::string, std::optional<const ClassType*>, std::optional<std::string> contents) -> std::optional<AutocompleteEntryMap>
|
||||
{
|
||||
isCorrect = contents && *contents == "testing/";
|
||||
return std::nullopt;
|
||||
|
@ -3765,6 +3766,8 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback")
|
|||
|
||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "require_by_string")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauExposeRequireByStringAutocomplete, true};
|
||||
|
||||
fileResolver.source["MainModule"] = R"(
|
||||
local info = "MainModule serves as the root directory"
|
||||
)";
|
||||
|
@ -3957,7 +3960,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes")
|
|||
local x = require(@1"@2"@3)
|
||||
)");
|
||||
|
||||
StringCompletionCallback callback = [](std::string, std::optional<const ExternType*>, std::optional<std::string> contents
|
||||
StringCompletionCallback callback = [](std::string, std::optional<const ClassType*>, std::optional<std::string> contents
|
||||
) -> std::optional<AutocompleteEntryMap>
|
||||
{
|
||||
Luau::AutocompleteEntryMap results = {{"test", Luau::AutocompleteEntry{Luau::AutocompleteEntryKind::String, std::nullopt, false, false}}};
|
||||
|
@ -4429,7 +4432,7 @@ local x = 1 + result.
|
|||
CHECK(ac.entryMap.count("x"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
||||
TEST_CASE_FIXTURE(ACClassFixture, "ac_dont_overflow_on_recursive_union")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAutocompleteUnionCopyPreviousSeen, true};
|
||||
check(R"(
|
||||
|
|
|
@ -28,9 +28,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "lib_documentation_symbols")
|
|||
{
|
||||
props = &ttv->props;
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(binding.typeId))
|
||||
else if (const ClassType* ctv = get<ClassType>(binding.typeId))
|
||||
{
|
||||
props = &etv->props;
|
||||
props = &ctv->props;
|
||||
}
|
||||
|
||||
if (props)
|
||||
|
|
|
@ -9,7 +9,7 @@ using std::nullopt;
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
||||
ClassFixture::ClassFixture(bool prepareAutocomplete)
|
||||
: BuiltinsFixture(prepareAutocomplete)
|
||||
{
|
||||
GlobalTypes& globals = frontend.globals;
|
||||
|
@ -19,22 +19,22 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
|
||||
unfreeze(arena);
|
||||
|
||||
TypeId connectionType = arena.addType(ExternType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection", {}});
|
||||
TypeId connectionType = arena.addType(ClassType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection", {}});
|
||||
|
||||
TypeId baseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(baseClassInstanceType)->props = {
|
||||
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(baseClassInstanceType)->props = {
|
||||
{"BaseMethod", Property::readonly(makeFunction(arena, baseClassInstanceType, {numberType}, {}))},
|
||||
{"BaseField", {numberType}},
|
||||
|
||||
{"Touched", Property::readonly(connectionType)},
|
||||
};
|
||||
|
||||
getMutable<ExternType>(connectionType)->props = {
|
||||
getMutable<ClassType>(connectionType)->props = {
|
||||
{"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}
|
||||
};
|
||||
|
||||
TypeId baseClassType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(baseClassType)->props = {
|
||||
TypeId baseClassType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(baseClassType)->props = {
|
||||
{"StaticMethod", {makeFunction(arena, nullopt, {}, {numberType})}},
|
||||
{"Clone", {makeFunction(arena, nullopt, {baseClassInstanceType}, {baseClassInstanceType})}},
|
||||
{"New", {makeFunction(arena, nullopt, {}, {baseClassInstanceType})}},
|
||||
|
@ -42,49 +42,49 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||
addGlobalBinding(globals, "BaseClass", baseClassType, "@test");
|
||||
|
||||
TypeId childClassInstanceType = arena.addType(ExternType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
|
||||
getMutable<ExternType>(childClassInstanceType)->props = {
|
||||
getMutable<ClassType>(childClassInstanceType)->props = {
|
||||
{"Method", {makeFunction(arena, childClassInstanceType, {}, {stringType})}},
|
||||
};
|
||||
|
||||
TypeId childClassType = arena.addType(ExternType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(childClassType)->props = {
|
||||
TypeId childClassType = arena.addType(ClassType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(childClassType)->props = {
|
||||
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
||||
};
|
||||
globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
||||
addGlobalBinding(globals, "ChildClass", childClassType, "@test");
|
||||
|
||||
TypeId grandChildInstanceType = arena.addType(ExternType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
TypeId grandChildInstanceType = arena.addType(ClassType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
|
||||
getMutable<ExternType>(grandChildInstanceType)->props = {
|
||||
getMutable<ClassType>(grandChildInstanceType)->props = {
|
||||
{"Method", {makeFunction(arena, grandChildInstanceType, {}, {stringType})}},
|
||||
};
|
||||
|
||||
TypeId grandChildType = arena.addType(ExternType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(grandChildType)->props = {
|
||||
TypeId grandChildType = arena.addType(ClassType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(grandChildType)->props = {
|
||||
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
||||
};
|
||||
globals.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
||||
addGlobalBinding(globals, "GrandChild", childClassType, "@test");
|
||||
|
||||
TypeId anotherChildInstanceType = arena.addType(ExternType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
TypeId anotherChildInstanceType = arena.addType(ClassType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
|
||||
getMutable<ExternType>(anotherChildInstanceType)->props = {
|
||||
getMutable<ClassType>(anotherChildInstanceType)->props = {
|
||||
{"Method", {makeFunction(arena, anotherChildInstanceType, {}, {stringType})}},
|
||||
};
|
||||
|
||||
TypeId anotherChildType = arena.addType(ExternType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(anotherChildType)->props = {
|
||||
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(anotherChildType)->props = {
|
||||
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
||||
};
|
||||
globals.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
||||
addGlobalBinding(globals, "AnotherChild", childClassType, "@test");
|
||||
|
||||
TypeId unrelatedClassInstanceType = arena.addType(ExternType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
TypeId unrelatedClassInstanceType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
|
||||
TypeId unrelatedClassType = arena.addType(ExternType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(unrelatedClassType)->props = {
|
||||
TypeId unrelatedClassType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(unrelatedClassType)->props = {
|
||||
{"New", {makeFunction(arena, nullopt, {}, {unrelatedClassInstanceType})}},
|
||||
};
|
||||
globals.globalScope->exportedTypeBindings["UnrelatedClass"] = TypeFun{{}, unrelatedClassInstanceType};
|
||||
|
@ -92,14 +92,14 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
|
||||
TypeId vector2MetaType = arena.addType(TableType{});
|
||||
|
||||
vector2InstanceType = arena.addType(ExternType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(vector2InstanceType)->props = {
|
||||
vector2InstanceType = arena.addType(ClassType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(vector2InstanceType)->props = {
|
||||
{"X", {numberType}},
|
||||
{"Y", {numberType}},
|
||||
};
|
||||
|
||||
vector2Type = arena.addType(ExternType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ExternType>(vector2Type)->props = {
|
||||
vector2Type = arena.addType(ClassType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||
getMutable<ClassType>(vector2Type)->props = {
|
||||
{"New", {makeFunction(arena, nullopt, {numberType, numberType}, {vector2InstanceType})}},
|
||||
};
|
||||
getMutable<TableType>(vector2MetaType)->props = {
|
||||
|
@ -114,7 +114,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
addGlobalBinding(globals, "Vector2", vector2Type, "@test");
|
||||
|
||||
TypeId callableClassMetaType = arena.addType(TableType{});
|
||||
TypeId callableClassType = arena.addType(ExternType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test", {}});
|
||||
TypeId callableClassType = arena.addType(ClassType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test", {}});
|
||||
getMutable<TableType>(callableClassMetaType)->props = {
|
||||
{"__call", {makeFunction(arena, nullopt, {callableClassType, stringType}, {numberType})}},
|
||||
};
|
||||
|
@ -124,7 +124,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
{
|
||||
TypeId indexableClassMetaType = arena.addType(TableType{});
|
||||
TypeId indexableClassType =
|
||||
arena.addType(ExternType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", {}, TableIndexer{keyType, returnType}});
|
||||
arena.addType(ClassType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", {}, TableIndexer{keyType, returnType}});
|
||||
globals.globalScope->exportedTypeBindings[className] = TypeFun{{}, indexableClassType};
|
||||
};
|
||||
|
||||
|
@ -134,9 +134,9 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
|
|||
addIndexableClass("IndexableNumericKeyClass", numberType, numberType);
|
||||
|
||||
// Add a confusing derived class which shares the same name internally, but has a unique alias
|
||||
TypeId duplicateBaseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
TypeId duplicateBaseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||
|
||||
getMutable<ExternType>(duplicateBaseClassInstanceType)->props = {
|
||||
getMutable<ClassType>(duplicateBaseClassInstanceType)->props = {
|
||||
{"Method", {makeFunction(arena, duplicateBaseClassInstanceType, {}, {stringType})}},
|
||||
};
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
struct ExternTypeFixture : BuiltinsFixture
|
||||
struct ClassFixture : BuiltinsFixture
|
||||
{
|
||||
explicit ExternTypeFixture(bool prepareAutocomplete = false);
|
||||
explicit ClassFixture(bool prepareAutocomplete = false);
|
||||
|
||||
TypeId vector2Type;
|
||||
TypeId vector2InstanceType;
|
||||
|
|
|
@ -35,7 +35,6 @@ LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
|||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||
|
||||
static lua_CompileOptions defaultOptions()
|
||||
{
|
||||
|
@ -824,236 +823,6 @@ TEST_CASE("Pack")
|
|||
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")
|
||||
{
|
||||
lua_CompileOptions copts = defaultOptions();
|
||||
|
@ -1188,7 +957,7 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
|||
|
||||
lua_pushstring(L, "function");
|
||||
}
|
||||
else if (auto c = Luau::get<Luau::ExternType>(type))
|
||||
else if (auto c = Luau::get<Luau::ClassType>(type))
|
||||
{
|
||||
lua_pushstring(L, c->name.c_str());
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
|
||||
struct DataFlowGraphFixture
|
||||
{
|
||||
|
@ -422,8 +421,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node_3")
|
|||
|
||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||
|
||||
dfg(R"(
|
||||
local x = 5
|
||||
|
||||
|
@ -442,14 +439,15 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_
|
|||
DefId x4 = getDef<AstExprLocal, 3>(); // x = "five"
|
||||
|
||||
CHECK(x1 != x2);
|
||||
CHECK(x2 == x3);
|
||||
CHECK(x2 != x3);
|
||||
CHECK(x3 != x4);
|
||||
|
||||
const Phi* phi = get<Phi>(x2);
|
||||
REQUIRE(phi);
|
||||
REQUIRE(phi->operands.size() == 2);
|
||||
REQUIRE(phi->operands.size() == 3);
|
||||
CHECK(phi->operands.at(0) == x1);
|
||||
CHECK(phi->operands.at(1) == x4);
|
||||
CHECK(phi->operands.at(1) == x3);
|
||||
CHECK(phi->operands.at(2) == x4);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions_properties")
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue