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.664" have entirely different histories.
238 changed files with 8152 additions and 20296 deletions
148
Analysis/include/Luau/AnyTypeSummary.h
Normal file
148
Analysis/include/Luau/AnyTypeSummary.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/AstQuery.h"
|
||||
#include "Luau/Config.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
class AstStat;
|
||||
class ParseError;
|
||||
struct TypeError;
|
||||
struct LintWarning;
|
||||
struct GlobalTypes;
|
||||
struct ModuleResolver;
|
||||
struct ParseResult;
|
||||
struct DcrLogger;
|
||||
|
||||
struct TelemetryTypePair
|
||||
{
|
||||
std::string annotatedType;
|
||||
std::string inferredType;
|
||||
};
|
||||
|
||||
struct AnyTypeSummary
|
||||
{
|
||||
TypeArena arena;
|
||||
|
||||
AstStatBlock* rootSrc = nullptr;
|
||||
DenseHashSet<TypeId> seenTypeFamilyInstances{nullptr};
|
||||
|
||||
int recursionCount = 0;
|
||||
|
||||
std::string root;
|
||||
int strictCount = 0;
|
||||
|
||||
DenseHashMap<const void*, bool> seen{nullptr};
|
||||
|
||||
AnyTypeSummary();
|
||||
|
||||
void traverse(const Module* module, AstStat* src, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
std::pair<bool, TypeId> checkForAnyCast(const Scope* scope, AstExprTypeAssertion* expr);
|
||||
|
||||
bool containsAny(TypePackId typ);
|
||||
bool containsAny(TypeId typ);
|
||||
|
||||
bool isAnyCast(const Scope* scope, AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
bool isAnyCall(const Scope* scope, AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
bool hasVariadicAnys(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
bool hasArgAnys(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
bool hasAnyReturns(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
TypeId checkForFamilyInhabitance(const TypeId instance, Location location);
|
||||
TypeId lookupType(const AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
TypePackId reconstructTypePack(const AstArray<AstExpr*> exprs, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};
|
||||
TypeId lookupAnnotation(AstType* annotation, const Module* module, NotNull<BuiltinTypes> builtintypes);
|
||||
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation, const Module* module);
|
||||
TypeId checkForTypeFunctionInhabitance(const TypeId instance, const Location location);
|
||||
|
||||
enum Pattern : uint64_t
|
||||
{
|
||||
Casts,
|
||||
FuncArg,
|
||||
FuncRet,
|
||||
FuncApp,
|
||||
VarAnnot,
|
||||
VarAny,
|
||||
TableProp,
|
||||
Alias,
|
||||
Assign,
|
||||
TypePk
|
||||
};
|
||||
|
||||
struct TypeInfo
|
||||
{
|
||||
Pattern code;
|
||||
std::string node;
|
||||
TelemetryTypePair type;
|
||||
|
||||
explicit TypeInfo(Pattern code, std::string node, TelemetryTypePair type);
|
||||
};
|
||||
|
||||
struct FindReturnAncestry final : public AstVisitor
|
||||
{
|
||||
AstNode* currNode{nullptr};
|
||||
AstNode* stat{nullptr};
|
||||
Position rootEnd;
|
||||
bool found = false;
|
||||
|
||||
explicit FindReturnAncestry(AstNode* stat, Position rootEnd);
|
||||
|
||||
bool visit(AstType* node) override;
|
||||
bool visit(AstNode* node) override;
|
||||
bool visit(AstStatFunction* node) override;
|
||||
bool visit(AstStatLocalFunction* node) override;
|
||||
};
|
||||
|
||||
std::vector<TypeInfo> typeInfo;
|
||||
|
||||
/**
|
||||
* Fabricates a scope that is a child of another scope.
|
||||
* @param node the lexical node that the scope belongs to.
|
||||
* @param parent the parent scope of the new scope. Must not be null.
|
||||
*/
|
||||
const Scope* childScope(const AstNode* node, const Scope* parent);
|
||||
|
||||
std::optional<AstExpr*> matchRequire(const AstExprCall& call);
|
||||
AstNode* getNode(AstStatBlock* root, AstNode* node);
|
||||
const Scope* findInnerMostScope(const Location location, const Module* module);
|
||||
const AstNode* findAstAncestryAtLocation(const AstStatBlock* root, AstNode* node);
|
||||
|
||||
void visit(const Scope* scope, AstStat* stat, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatBlock* block, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatIf* ifStatement, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatWhile* while_, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatRepeat* repeat, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatReturn* ret, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatLocal* local, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatFor* for_, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatForIn* forIn, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatAssign* assign, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatCompoundAssign* assign, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatFunction* function, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatLocalFunction* function, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatTypeAlias* alias, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatDeclareGlobal* declareGlobal, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatDeclareClass* declareClass, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatDeclareFunction* declareFunction, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
void visit(const Scope* scope, AstStatError* error, const Module* module, NotNull<BuiltinTypes> builtinTypes);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
|
@ -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)";
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol
|
|||
void assignPropDocumentationSymbols(TableType::Props& props, const std::string& baseName);
|
||||
|
||||
std::string getBuiltinDefinitionSource();
|
||||
std::string getTypeFunctionDefinitionSource();
|
||||
|
||||
void addGlobalBinding(GlobalTypes& globals, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(GlobalTypes& globals, const std::string& name, Binding binding);
|
||||
|
|
|
@ -50,7 +50,6 @@ struct GeneralizationConstraint
|
|||
TypeId sourceType;
|
||||
|
||||
std::vector<TypeId> interiorTypes;
|
||||
bool hasDeprecatedAttribute = false;
|
||||
};
|
||||
|
||||
// variables ~ iterate iterator
|
||||
|
|
|
@ -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"
|
||||
|
@ -12,14 +11,15 @@
|
|||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/Symbol.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -92,8 +92,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};
|
||||
|
@ -116,23 +117,18 @@ struct ConstraintGenerator
|
|||
|
||||
// Needed to register all available type functions for execution at later stages.
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
DenseHashMap<const AstStatTypeFunction*, ScopePtr> astTypeFunctionEnvironmentScopes{nullptr};
|
||||
|
||||
// Needed to resolve modules to make 'require' import types properly.
|
||||
NotNull<ModuleResolver> moduleResolver;
|
||||
// Occasionally constraint generation needs to produce an ICE.
|
||||
const NotNull<InternalErrorReporter> ice;
|
||||
|
||||
ScopePtr globalScope;
|
||||
ScopePtr typeFunctionScope;
|
||||
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
|
||||
std::vector<RequireCycle> requireCycles;
|
||||
|
||||
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};
|
||||
|
||||
DenseHashMap<AstExpr*, Inference> inferredExprCache{nullptr};
|
||||
|
||||
DcrLogger* logger;
|
||||
|
||||
ConstraintGenerator(
|
||||
|
@ -144,16 +140,12 @@ struct ConstraintGenerator
|
|||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<InternalErrorReporter> ice,
|
||||
const ScopePtr& globalScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
DcrLogger* logger,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
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.
|
||||
|
@ -164,26 +156,19 @@ struct ConstraintGenerator
|
|||
void visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block);
|
||||
|
||||
private:
|
||||
struct InteriorFreeTypes
|
||||
{
|
||||
std::vector<TypeId> types;
|
||||
std::vector<TypePackId> typePacks;
|
||||
};
|
||||
|
||||
std::vector<std::vector<TypeId>> DEPRECATED_interiorTypes;
|
||||
std::vector<InteriorFreeTypes> interiorFreeTypes;
|
||||
std::vector<std::vector<TypeId>> interiorTypes;
|
||||
|
||||
/**
|
||||
* Fabricates a new free type belonging to a given scope.
|
||||
* @param scope the scope the free type belongs to.
|
||||
*/
|
||||
TypeId freshType(const ScopePtr& scope, Polarity polarity = Polarity::Unknown);
|
||||
TypeId freshType(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* Fabricates a new free type pack belonging to a given scope.
|
||||
* @param scope the scope the free type pack belongs to.
|
||||
*/
|
||||
TypePackId freshTypePack(const ScopePtr& scope, Polarity polarity = Polarity::Unknown);
|
||||
TypePackId freshTypePack(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* Allocate a new TypePack with the given head and tail.
|
||||
|
@ -272,7 +257,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);
|
||||
|
||||
|
@ -304,7 +289,7 @@ private:
|
|||
);
|
||||
|
||||
Inference check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton);
|
||||
Inference check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton);
|
||||
Inference check(const ScopePtr& scope, AstExprConstantBool* bool_, std::optional<TypeId> expectedType, bool forceSingleton);
|
||||
Inference check(const ScopePtr& scope, AstExprLocal* local);
|
||||
Inference check(const ScopePtr& scope, AstExprGlobal* global);
|
||||
Inference checkIndexName(const ScopePtr& scope, const RefinementKey* key, AstExpr* indexee, const std::string& index, Location indexLocation);
|
||||
|
@ -380,11 +365,6 @@ private:
|
|||
**/
|
||||
TypeId resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
||||
|
||||
// resolveType() is recursive, but we only want to invoke
|
||||
// inferGenericPolarities() once at the very end. We thus isolate the
|
||||
// recursive part of the algorithm to this internal helper.
|
||||
TypeId resolveType_(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
||||
|
||||
/**
|
||||
* Resolves a type pack from its AST annotation.
|
||||
* @param scope the scope that the type annotation appears within.
|
||||
|
@ -394,9 +374,6 @@ private:
|
|||
**/
|
||||
TypePackId resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
||||
|
||||
// Inner hepler for resolveTypePack
|
||||
TypePackId resolveTypePack_(const ScopePtr& scope, AstTypePack* tp, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
||||
|
||||
/**
|
||||
* Resolves a type pack from its AST annotation.
|
||||
* @param scope the scope that the type annotation appears within.
|
||||
|
@ -435,7 +412,7 @@ private:
|
|||
**/
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(
|
||||
const ScopePtr& scope,
|
||||
AstArray<AstGenericTypePack*> generics,
|
||||
AstArray<AstGenericTypePack*> packs,
|
||||
bool useCache = false,
|
||||
bool addTypes = true
|
||||
);
|
||||
|
@ -484,4 +461,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);
|
||||
|
||||
/**
|
||||
|
@ -383,7 +365,7 @@ public:
|
|||
* @returns a non-free type that generalizes the argument, or `std::nullopt` if one
|
||||
* does not exist
|
||||
*/
|
||||
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type);
|
||||
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables = false);
|
||||
|
||||
/**
|
||||
* Checks the existing set of constraints to see if there exist any that contain
|
||||
|
@ -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
|
||||
|
|
|
@ -38,6 +38,8 @@ struct DataFlowGraph
|
|||
DefId getDef(const AstExpr* expr) const;
|
||||
// Look up the definition optionally, knowing it may not be present.
|
||||
std::optional<DefId> getDefOptional(const AstExpr* expr) const;
|
||||
// Look up for the rvalue def for a compound assignment.
|
||||
std::optional<DefId> getRValueDefForCompoundAssign(const AstExpr* expr) const;
|
||||
|
||||
DefId getDef(const AstLocal* local) const;
|
||||
|
||||
|
@ -64,6 +66,10 @@ private:
|
|||
// All keys in this maps are really only statements that ambiently declares a symbol.
|
||||
DenseHashMap<const AstStat*, const Def*> declaredDefs{nullptr};
|
||||
|
||||
// Compound assignments are in a weird situation where the local being assigned to is also being used at its
|
||||
// previous type implicitly in an rvalue position. This map provides the previous binding.
|
||||
DenseHashMap<const AstExpr*, const Def*> compoundAssignDefs{nullptr};
|
||||
|
||||
DenseHashMap<const AstExpr*, const RefinementKey*> astRefinementKeys{nullptr};
|
||||
friend struct DataFlowGraphBuilder;
|
||||
};
|
||||
|
@ -129,8 +135,8 @@ private:
|
|||
|
||||
/// A stack of scopes used by the visitor to see where we are.
|
||||
ScopeStack scopeStack;
|
||||
NotNull<DfgScope> currentScope();
|
||||
DfgScope* currentScope_DEPRECATED();
|
||||
|
||||
DfgScope* currentScope();
|
||||
|
||||
struct FunctionCapture
|
||||
{
|
||||
|
@ -148,8 +154,8 @@ private:
|
|||
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
||||
void joinProps(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
||||
|
||||
DefId lookup(Symbol symbol, Location location);
|
||||
DefId lookup(DefId def, const std::string& key, Location location);
|
||||
DefId lookup(Symbol symbol);
|
||||
DefId lookup(DefId def, const std::string& key);
|
||||
|
||||
ControlFlow visit(AstStatBlock* b);
|
||||
ControlFlow visitBlockWithoutChildScope(AstStatBlock* b);
|
||||
|
@ -173,7 +179,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);
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypedAllocator.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/Symbol.h"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
|
@ -14,7 +13,6 @@ namespace Luau
|
|||
|
||||
struct Def;
|
||||
using DefId = NotNull<const Def>;
|
||||
struct AstLocal;
|
||||
|
||||
/**
|
||||
* A cell is a "single-object" value.
|
||||
|
@ -66,8 +64,6 @@ struct Def
|
|||
using V = Variant<struct Cell, struct Phi>;
|
||||
|
||||
V v;
|
||||
Symbol name;
|
||||
Location location;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -83,7 +79,7 @@ struct DefArena
|
|||
{
|
||||
TypedAllocator<Def> allocator;
|
||||
|
||||
DefId freshCell(Symbol sym, Location location, bool subscripted = false);
|
||||
DefId freshCell(bool subscripted = false);
|
||||
DefId phi(DefId a, DefId b);
|
||||
DefId phi(const std::vector<DefId>& defs);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
@ -455,13 +455,6 @@ struct UserDefinedTypeFunctionError
|
|||
bool operator==(const UserDefinedTypeFunctionError& rhs) const;
|
||||
};
|
||||
|
||||
struct ReservedIdentifier
|
||||
{
|
||||
std::string name;
|
||||
|
||||
bool operator==(const ReservedIdentifier& rhs) const;
|
||||
};
|
||||
|
||||
using TypeErrorData = Variant<
|
||||
TypeMismatch,
|
||||
UnknownSymbol,
|
||||
|
@ -499,7 +492,7 @@ using TypeErrorData = Variant<
|
|||
TypesAreUnrelated,
|
||||
NormalizationTooComplex,
|
||||
TypePackMismatch,
|
||||
DynamicPropertyLookupOnExternTypesUnsafe,
|
||||
DynamicPropertyLookupOnClassesUnsafe,
|
||||
UninhabitedTypeFunction,
|
||||
UninhabitedTypePackFunction,
|
||||
WhereClauseNeeded,
|
||||
|
@ -511,8 +504,7 @@ using TypeErrorData = Variant<
|
|||
UnexpectedTypeInSubtyping,
|
||||
UnexpectedTypePackInSubtyping,
|
||||
ExplicitFunctionAnnotationRecommended,
|
||||
UserDefinedTypeFunctionError,
|
||||
ReservedIdentifier>;
|
||||
UserDefinedTypeFunctionError>;
|
||||
|
||||
struct TypeErrorSummary
|
||||
{
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Luau
|
||||
|
@ -20,7 +19,7 @@ struct SourceCode
|
|||
None,
|
||||
Module,
|
||||
Script,
|
||||
Local_DEPRECATED
|
||||
Local
|
||||
};
|
||||
|
||||
std::string source;
|
||||
|
@ -33,71 +32,15 @@ struct ModuleInfo
|
|||
bool optional = false;
|
||||
};
|
||||
|
||||
struct RequireAlias
|
||||
{
|
||||
std::string alias; // Unprefixed alias name (no leading `@`).
|
||||
std::vector<std::string> tags = {};
|
||||
};
|
||||
|
||||
struct RequireNode
|
||||
{
|
||||
virtual ~RequireNode() {}
|
||||
|
||||
// Get the path component representing this node.
|
||||
virtual std::string getPathComponent() const = 0;
|
||||
|
||||
// Get the displayed user-facing label for this node, defaults to getPathComponent()
|
||||
virtual std::string getLabel() const
|
||||
{
|
||||
return getPathComponent();
|
||||
}
|
||||
|
||||
// Get tags to attach to this node's RequireSuggestion (defaults to none).
|
||||
virtual std::vector<std::string> getTags() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO: resolvePathToNode() can ultimately be replaced with a call into
|
||||
// require-by-string's path resolution algorithm. This will first require
|
||||
// generalizing that algorithm to work with a virtual file system.
|
||||
virtual std::unique_ptr<RequireNode> resolvePathToNode(const std::string& path) const = 0;
|
||||
|
||||
// Get children of this node, if any (if this node represents a directory).
|
||||
virtual std::vector<std::unique_ptr<RequireNode>> getChildren() const = 0;
|
||||
|
||||
// A list of the aliases available to this node.
|
||||
virtual std::vector<RequireAlias> getAvailableAliases() const = 0;
|
||||
};
|
||||
|
||||
struct RequireSuggestion
|
||||
{
|
||||
std::string label;
|
||||
std::string fullPath;
|
||||
std::vector<std::string> tags;
|
||||
};
|
||||
using RequireSuggestions = std::vector<RequireSuggestion>;
|
||||
|
||||
struct RequireSuggester
|
||||
{
|
||||
virtual ~RequireSuggester() {}
|
||||
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<RequireNode> getNode(const ModuleName& name) const = 0;
|
||||
|
||||
private:
|
||||
std::optional<RequireSuggestions> getRequireSuggestionsImpl(const ModuleName& requirer, const std::optional<std::string>& path) const;
|
||||
};
|
||||
|
||||
struct FileResolver
|
||||
{
|
||||
FileResolver() = default;
|
||||
FileResolver(std::shared_ptr<RequireSuggester> requireSuggester)
|
||||
: requireSuggester(std::move(requireSuggester))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FileResolver() {}
|
||||
|
||||
virtual std::optional<SourceCode> readSource(const ModuleName& name) = 0;
|
||||
|
@ -117,9 +60,10 @@ struct FileResolver
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||
|
||||
std::shared_ptr<RequireSuggester> requireSuggester;
|
||||
virtual std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
struct NullFileResolver : FileResolver
|
||||
|
|
|
@ -15,28 +15,6 @@ namespace Luau
|
|||
{
|
||||
struct FrontendOptions;
|
||||
|
||||
enum class FragmentAutocompleteWaypoint
|
||||
{
|
||||
ParseFragmentEnd,
|
||||
CloneModuleStart,
|
||||
CloneModuleEnd,
|
||||
DfgBuildEnd,
|
||||
CloneAndSquashScopeStart,
|
||||
CloneAndSquashScopeEnd,
|
||||
ConstraintSolverStart,
|
||||
ConstraintSolverEnd,
|
||||
TypecheckFragmentEnd,
|
||||
AutocompleteEnd,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
class IFragmentAutocompleteReporter
|
||||
{
|
||||
public:
|
||||
virtual void reportWaypoint(FragmentAutocompleteWaypoint) = 0;
|
||||
virtual void reportFragmentString(std::string_view) = 0;
|
||||
};
|
||||
|
||||
enum class FragmentTypeCheckStatus
|
||||
{
|
||||
SkipAutocomplete,
|
||||
|
@ -49,8 +27,6 @@ struct FragmentAutocompleteAncestryResult
|
|||
std::vector<AstLocal*> localStack;
|
||||
std::vector<AstNode*> ancestry;
|
||||
AstStat* nearestStatement = nullptr;
|
||||
AstStatBlock* parentBlock = nullptr;
|
||||
Location fragmentSelectionRegion;
|
||||
};
|
||||
|
||||
struct FragmentParseResult
|
||||
|
@ -61,7 +37,6 @@ struct FragmentParseResult
|
|||
AstStat* nearestStatement = nullptr;
|
||||
std::vector<Comment> commentLocations;
|
||||
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
|
||||
Position scopePos{0, 0};
|
||||
};
|
||||
|
||||
struct FragmentTypeCheckResult
|
||||
|
@ -75,33 +50,14 @@ struct FragmentAutocompleteResult
|
|||
{
|
||||
ModulePtr incrementalModule;
|
||||
Scope* freshScope;
|
||||
TypeArena arenaForAutocomplete_DEPRECATED;
|
||||
TypeArena arenaForAutocomplete;
|
||||
AutocompleteResult acResults;
|
||||
};
|
||||
|
||||
struct FragmentRegion
|
||||
{
|
||||
Location fragmentLocation;
|
||||
AstStat* nearestStatement = nullptr; // used for tests
|
||||
AstStatBlock* parentBlock = nullptr; // used for scope detection
|
||||
};
|
||||
|
||||
std::optional<Position> blockDiffStart(AstStatBlock* blockOld, AstStatBlock* blockNew, AstStat* nearestStatementNewAst);
|
||||
FragmentRegion getFragmentRegion(AstStatBlock* root, const Position& cursorPosition);
|
||||
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* stale, const Position& cursorPos, AstStatBlock* lastGoodParse);
|
||||
FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstStatBlock* root, const Position& cursorPos);
|
||||
|
||||
std::optional<FragmentParseResult> parseFragment_DEPRECATED(
|
||||
AstStatBlock* root,
|
||||
AstNameTable* names,
|
||||
std::string_view src,
|
||||
const Position& cursorPos,
|
||||
std::optional<Position> fragmentEndPosition
|
||||
);
|
||||
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* root, const Position& cursorPos);
|
||||
|
||||
std::optional<FragmentParseResult> parseFragment(
|
||||
AstStatBlock* stale,
|
||||
AstStatBlock* mostRecentParse,
|
||||
AstStatBlock* root,
|
||||
AstNameTable* names,
|
||||
std::string_view src,
|
||||
const Position& cursorPos,
|
||||
|
@ -114,9 +70,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
|||
const Position& cursorPos,
|
||||
std::optional<FrontendOptions> opts,
|
||||
std::string_view src,
|
||||
std::optional<Position> fragmentEndPosition,
|
||||
AstStatBlock* recentParse = nullptr,
|
||||
IFragmentAutocompleteReporter* reporter = nullptr
|
||||
std::optional<Position> fragmentEndPosition
|
||||
);
|
||||
|
||||
FragmentAutocompleteResult fragmentAutocomplete(
|
||||
|
@ -126,9 +80,7 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
|||
Position cursorPosition,
|
||||
std::optional<FrontendOptions> opts,
|
||||
StringCompletionCallback callback,
|
||||
std::optional<Position> fragmentEndPosition = std::nullopt,
|
||||
AstStatBlock* recentParse = nullptr,
|
||||
IFragmentAutocompleteReporter* reporter = nullptr
|
||||
std::optional<Position> fragmentEndPosition = std::nullopt
|
||||
);
|
||||
|
||||
enum class FragmentAutocompleteStatus
|
||||
|
@ -150,7 +102,6 @@ struct FragmentContext
|
|||
const ParseResult& freshParse;
|
||||
std::optional<FrontendOptions> opts;
|
||||
std::optional<Position> DEPRECATED_fragmentEndPosition;
|
||||
IFragmentAutocompleteReporter* reporter = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/AnyTypeSummary.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
@ -33,6 +34,7 @@ struct HotComment;
|
|||
struct BuildQueueItem;
|
||||
struct BuildQueueWorkState;
|
||||
struct FrontendCancellationToken;
|
||||
struct AnyTypeSummary;
|
||||
|
||||
struct LoadDefinitionFileResult
|
||||
{
|
||||
|
@ -215,6 +217,11 @@ struct Frontend
|
|||
std::function<void(std::function<void()> task)> executeTask = {},
|
||||
std::function<bool(size_t done, size_t total)> progress = {}
|
||||
);
|
||||
std::vector<ModuleName> checkQueuedModules_DEPRECATED(
|
||||
std::optional<FrontendOptions> optionOverride = {},
|
||||
std::function<void(std::function<void()> task)> executeTask = {},
|
||||
std::function<bool(size_t done, size_t total)> progress = {}
|
||||
);
|
||||
|
||||
std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);
|
||||
std::vector<ModuleName> getRequiredScripts(const ModuleName& name);
|
||||
|
@ -298,7 +305,6 @@ ModulePtr check(
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<FileResolver> fileResolver,
|
||||
const ScopePtr& globalScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
FrontendOptions options,
|
||||
TypeCheckLimits limits
|
||||
|
@ -313,7 +319,6 @@ ModulePtr check(
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<FileResolver> fileResolver,
|
||||
const ScopePtr& globalScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
FrontendOptions options,
|
||||
TypeCheckLimits limits,
|
||||
|
|
|
@ -8,75 +8,12 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
template<typename TID>
|
||||
struct GeneralizationParams
|
||||
{
|
||||
bool foundOutsideFunctions = false;
|
||||
size_t useCount = 0;
|
||||
Polarity polarity = Polarity::None;
|
||||
};
|
||||
|
||||
template<typename TID>
|
||||
struct GeneralizationResult
|
||||
{
|
||||
std::optional<TID> result;
|
||||
|
||||
// True if the provided type was replaced with a generic.
|
||||
bool wasReplacedByGeneric = false;
|
||||
|
||||
bool resourceLimitsExceeded = false;
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return bool(result);
|
||||
}
|
||||
};
|
||||
|
||||
// Replace a single free type by its bounds according to the polarity provided.
|
||||
GeneralizationResult<TypeId> generalizeType(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> scope,
|
||||
TypeId freeTy,
|
||||
const GeneralizationParams<TypeId>& params
|
||||
);
|
||||
|
||||
// Generalize one type pack
|
||||
GeneralizationResult<TypePackId> generalizeTypePack(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> scope,
|
||||
TypePackId tp,
|
||||
const GeneralizationParams<TypePackId>& params
|
||||
);
|
||||
|
||||
void sealTable(NotNull<Scope> scope, TypeId ty);
|
||||
|
||||
/** Attempt to generalize a type.
|
||||
*
|
||||
* If generalizationTarget is set, then only that type will be replaced by its
|
||||
* bounds. The way this is intended to be used is that ty is some function that
|
||||
* is not fully generalized, and generalizationTarget is a type within its
|
||||
* signature. There should be no further constraints that could affect the
|
||||
* bounds of generalizationTarget.
|
||||
*
|
||||
* Returns nullopt if generalization failed due to resources limits.
|
||||
*/
|
||||
std::optional<TypeId> generalize(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||
NotNull<DenseHashSet<TypeId>> bakedTypes,
|
||||
TypeId ty,
|
||||
std::optional<TypeId> generalizationTarget = {}
|
||||
/* avoid sealing tables*/ bool avoidSealingTables = false
|
||||
);
|
||||
|
||||
void pruneUnnecessaryGenerics(
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||
TypeId ty
|
||||
);
|
||||
|
||||
} // namespace Luau
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ struct GlobalTypes
|
|||
|
||||
TypeArena globalTypes;
|
||||
SourceModule globalNames; // names for symbols entered into globalScope
|
||||
|
||||
ScopePtr globalScope; // shared by all modules
|
||||
ScopePtr globalTypeFunctionScope; // shared by all modules
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,16 +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/NotNull.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct Scope;
|
||||
struct TypeArena;
|
||||
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypeId ty);
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypePackId tp);
|
||||
|
||||
} // namespace Luau
|
|
@ -67,19 +67,6 @@ public:
|
|||
return &pairs.at(it->second).second;
|
||||
}
|
||||
|
||||
V& operator[](const K& k)
|
||||
{
|
||||
auto it = indices.find(k);
|
||||
if (it == indices.end())
|
||||
{
|
||||
pairs.push_back(std::make_pair(k, V()));
|
||||
indices[k] = pairs.size() - 1;
|
||||
return pairs.back().second;
|
||||
}
|
||||
else
|
||||
return pairs.at(it->second).second;
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return pairs.begin();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "Luau/ParseResult.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/AnyTypeSummary.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
|
||||
#include <memory>
|
||||
|
@ -15,16 +16,19 @@
|
|||
#include <unordered_map>
|
||||
#include <optional>
|
||||
|
||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteCommentDetection)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
using LogLuauProc = void (*)(std::string_view, std::string_view);
|
||||
using LogLuauProc = void (*)(std::string_view);
|
||||
extern LogLuauProc logLuau;
|
||||
|
||||
void setLogLuau(LogLuauProc ll);
|
||||
void resetLogLuauProc();
|
||||
|
||||
struct Module;
|
||||
struct AnyTypeSummary;
|
||||
|
||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||
using ModulePtr = std::shared_ptr<Module>;
|
||||
|
@ -82,6 +86,10 @@ struct Module
|
|||
TypeArena interfaceTypes;
|
||||
TypeArena internalTypes;
|
||||
|
||||
// Summary of Ast Nodes that either contain
|
||||
// user annotated anys or typechecker inferred anys
|
||||
AnyTypeSummary ats{};
|
||||
|
||||
// Scopes and AST types refer to parse data, so we need to keep that alive
|
||||
std::shared_ptr<Allocator> allocator;
|
||||
std::shared_ptr<AstNameTable> names;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,68 +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 <cstdint>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
enum struct Polarity : uint8_t
|
||||
{
|
||||
None = 0b000,
|
||||
Positive = 0b001,
|
||||
Negative = 0b010,
|
||||
Mixed = 0b011,
|
||||
Unknown = 0b100,
|
||||
};
|
||||
|
||||
inline Polarity operator|(Polarity lhs, Polarity rhs)
|
||||
{
|
||||
return Polarity(uint8_t(lhs) | uint8_t(rhs));
|
||||
}
|
||||
|
||||
inline Polarity& operator|=(Polarity& lhs, Polarity rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline Polarity operator&(Polarity lhs, Polarity rhs)
|
||||
{
|
||||
return Polarity(uint8_t(lhs) & uint8_t(rhs));
|
||||
}
|
||||
|
||||
inline Polarity& operator&=(Polarity& lhs, Polarity rhs)
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline bool isPositive(Polarity p)
|
||||
{
|
||||
return bool(p & Polarity::Positive);
|
||||
}
|
||||
|
||||
inline bool isNegative(Polarity p)
|
||||
{
|
||||
return bool(p & Polarity::Negative);
|
||||
}
|
||||
|
||||
inline bool isKnown(Polarity p)
|
||||
{
|
||||
return p != Polarity::Unknown;
|
||||
}
|
||||
|
||||
inline Polarity invert(Polarity p)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case Polarity::Positive:
|
||||
return Polarity::Negative;
|
||||
case Polarity::Negative:
|
||||
return Polarity::Positive;
|
||||
default:
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Luau
|
|
@ -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
|
||||
|
@ -31,4 +31,13 @@ struct OrderedMap
|
|||
}
|
||||
};
|
||||
|
||||
struct QuantifierResult
|
||||
{
|
||||
TypeId result;
|
||||
OrderedMap<TypeId, TypeId> insertedGenerics;
|
||||
OrderedMap<TypePackId, TypePackId> insertedGenericPacks;
|
||||
};
|
||||
|
||||
std::optional<QuantifierResult> quantify(TypeArena* arena, TypeId ty, Scope* scope);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -35,12 +35,12 @@ struct Scope
|
|||
explicit Scope(TypePackId returnType); // root scope
|
||||
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
|
||||
|
||||
ScopePtr parent; // null for the root
|
||||
const ScopePtr parent; // null for the root
|
||||
|
||||
// All the children of this scope.
|
||||
std::vector<NotNull<Scope>> children;
|
||||
std::unordered_map<Symbol, Binding> bindings;
|
||||
TypePackId returnType = nullptr;
|
||||
TypePackId returnType;
|
||||
std::optional<TypePackId> varargPack;
|
||||
|
||||
TypeLevel level;
|
||||
|
@ -59,8 +59,6 @@ struct Scope
|
|||
|
||||
std::optional<TypeId> lookup(Symbol sym) const;
|
||||
std::optional<TypeId> lookupUnrefinedType(DefId def) const;
|
||||
|
||||
std::optional<TypeId> lookupRValueRefinementType(DefId def) const;
|
||||
std::optional<TypeId> lookup(DefId def) const;
|
||||
std::optional<std::pair<TypeId, Scope*>> lookupEx(DefId def);
|
||||
std::optional<std::pair<Binding*, Scope*>> lookupEx(Symbol sym);
|
||||
|
@ -73,7 +71,6 @@ struct Scope
|
|||
|
||||
// WARNING: This function linearly scans for a string key of equal value! It is thus O(n**2)
|
||||
std::optional<Binding> linearSearchForBinding(const std::string& name, bool traverseScopeChain = true) const;
|
||||
std::optional<std::pair<Symbol, Binding>> linearSearchForBindingPair(const std::string& name, bool traverseScopeChain) const;
|
||||
|
||||
RefinementMap refinements;
|
||||
|
||||
|
@ -100,7 +97,6 @@ struct Scope
|
|||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||
|
||||
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
||||
std::optional<std::vector<TypePackId>> interiorFreeTypePacks;
|
||||
};
|
||||
|
||||
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
||||
|
|
|
@ -24,9 +24,6 @@ SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<
|
|||
|
||||
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
|
||||
|
||||
SimplifyResult simplifyIntersectWithTruthy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||
SimplifyResult simplifyIntersectWithFalsy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
|
||||
|
||||
enum class Relation
|
||||
{
|
||||
Disjoint, // No A is a B or vice versa
|
||||
|
|
|
@ -86,7 +86,6 @@ struct TarjanNode
|
|||
struct Tarjan
|
||||
{
|
||||
Tarjan();
|
||||
virtual ~Tarjan() = default;
|
||||
|
||||
// Vertices (types and type packs) are indexed, using pre-order traversal.
|
||||
DenseHashMap<TypeId, int> typeToIndex{nullptr};
|
||||
|
@ -122,7 +121,7 @@ struct Tarjan
|
|||
void visitChildren(TypePackId tp, int index);
|
||||
|
||||
void visitChild(TypeId ty);
|
||||
void visitChild(TypePackId tp);
|
||||
void visitChild(TypePackId ty);
|
||||
|
||||
template<typename Ty>
|
||||
void visitChild(std::optional<Ty> ty)
|
||||
|
@ -133,7 +132,7 @@ struct Tarjan
|
|||
|
||||
// Visit the root vertex.
|
||||
TarjanResult visitRoot(TypeId ty);
|
||||
TarjanResult visitRoot(TypePackId tp);
|
||||
TarjanResult visitRoot(TypePackId ty);
|
||||
|
||||
// Used to reuse the object for a new operation
|
||||
void clearTarjan(const TxnLog* log);
|
||||
|
@ -151,12 +150,26 @@ struct Tarjan
|
|||
void visitSCC(int index);
|
||||
|
||||
// Each subclass can decide to ignore some nodes.
|
||||
virtual bool ignoreChildren(TypeId ty);
|
||||
virtual bool ignoreChildren(TypePackId ty);
|
||||
virtual bool ignoreChildren(TypeId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ignoreChildren(TypePackId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
||||
virtual bool ignoreChildrenVisit(TypeId ty);
|
||||
virtual bool ignoreChildrenVisit(TypePackId ty);
|
||||
virtual bool ignoreChildrenVisit(TypeId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
virtual bool ignoreChildrenVisit(TypePackId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
// Subclasses should say which vertices are dirty,
|
||||
// and what to do with dirty vertices.
|
||||
|
@ -171,7 +184,6 @@ struct Tarjan
|
|||
struct Substitution : Tarjan
|
||||
{
|
||||
protected:
|
||||
explicit Substitution(TypeArena* arena);
|
||||
Substitution(const TxnLog* log_, TypeArena* arena);
|
||||
|
||||
/*
|
||||
|
@ -220,23 +232,28 @@ public:
|
|||
virtual TypeId clean(TypeId ty) = 0;
|
||||
virtual TypePackId clean(TypePackId tp) = 0;
|
||||
|
||||
protected:
|
||||
// Helper functions to create new types (used by subclasses)
|
||||
template<typename T>
|
||||
TypeId addType(T tv)
|
||||
TypeId addType(const T& tv)
|
||||
{
|
||||
return arena->addType(std::move(tv));
|
||||
return arena->addType(tv);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TypePackId addTypePack(T tp)
|
||||
TypePackId addTypePack(const T& tp)
|
||||
{
|
||||
return arena->addTypePack(TypePackVar{std::move(tp)});
|
||||
return arena->addTypePack(TypePackVar{tp});
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Ty>
|
||||
std::optional<Ty> replace(std::optional<Ty> ty);
|
||||
std::optional<Ty> replace(std::optional<Ty> ty)
|
||||
{
|
||||
if (ty)
|
||||
return replace(*ty);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -192,6 +192,16 @@ struct TxnLog
|
|||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingTypePack* changeLevel(TypePackId tp, TypeLevel newLevel);
|
||||
|
||||
// Queues the replacement of a type's scope with the provided scope.
|
||||
//
|
||||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingType* changeScope(TypeId ty, NotNull<Scope> scope);
|
||||
|
||||
// Queues the replacement of a type pack's scope with the provided scope.
|
||||
//
|
||||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingTypePack* changeScope(TypePackId tp, NotNull<Scope> scope);
|
||||
|
||||
// Queues a replacement of a table type with another table type with a new
|
||||
// indexer.
|
||||
//
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Predicate.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/Unifiable.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/VecDeque.h"
|
||||
|
@ -20,6 +19,7 @@
|
|||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
||||
|
@ -72,8 +72,12 @@ struct FreeType
|
|||
// New constructors
|
||||
explicit FreeType(TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||
// This one got promoted to explicit
|
||||
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity = Polarity::Unknown);
|
||||
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound);
|
||||
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;
|
||||
|
@ -87,8 +91,6 @@ struct FreeType
|
|||
// Only used under local type inference
|
||||
TypeId lowerBound = nullptr;
|
||||
TypeId upperBound = nullptr;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
struct GenericType
|
||||
|
@ -97,8 +99,8 @@ struct GenericType
|
|||
GenericType();
|
||||
|
||||
explicit GenericType(TypeLevel level);
|
||||
explicit GenericType(const Name& name, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericType(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericType(const Name& name);
|
||||
explicit GenericType(Scope* scope);
|
||||
|
||||
GenericType(TypeLevel level, const Name& name);
|
||||
GenericType(Scope* scope, const Name& name);
|
||||
|
@ -108,8 +110,6 @@ struct GenericType
|
|||
Scope* scope = nullptr;
|
||||
Name name;
|
||||
bool explicitName = false;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
// When an equality constraint is found, it is then "bound" to that type,
|
||||
|
@ -287,7 +287,7 @@ struct MagicFunctionCallContext
|
|||
{
|
||||
NotNull<struct ConstraintSolver> solver;
|
||||
NotNull<const Constraint> constraint;
|
||||
NotNull<const AstExprCall> callSite;
|
||||
const class AstExprCall* callSite;
|
||||
TypePackId arguments;
|
||||
TypePackId result;
|
||||
};
|
||||
|
@ -348,8 +348,10 @@ struct FunctionType
|
|||
);
|
||||
|
||||
// Local monomorphic function
|
||||
FunctionType(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
FunctionType(
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {},
|
||||
|
@ -366,6 +368,16 @@ struct FunctionType
|
|||
std::optional<FunctionDefinition> defn = {},
|
||||
bool hasSelf = false
|
||||
);
|
||||
FunctionType(
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
std::vector<TypeId> generics,
|
||||
std::vector<TypePackId> genericPacks,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {},
|
||||
bool hasSelf = false
|
||||
);
|
||||
|
||||
std::optional<FunctionDefinition> definition;
|
||||
/// These should all be generic
|
||||
|
@ -374,6 +386,7 @@ struct FunctionType
|
|||
std::vector<std::optional<FunctionArgument>> argNames;
|
||||
Tags tags;
|
||||
TypeLevel level;
|
||||
Scope* scope = nullptr;
|
||||
TypePackId argTypes;
|
||||
TypePackId retTypes;
|
||||
std::shared_ptr<MagicFunction> magic = nullptr;
|
||||
|
@ -383,7 +396,6 @@ struct FunctionType
|
|||
// this flag is used as an optimization to exit early from procedures that manipulate free or generic types.
|
||||
bool hasNoFreeOrGenericTypes = false;
|
||||
bool isCheckedFunction = false;
|
||||
bool isDeprecatedFunction = false;
|
||||
};
|
||||
|
||||
enum class TableState
|
||||
|
@ -460,9 +472,7 @@ struct Property
|
|||
TypeId type() const;
|
||||
void setType(TypeId ty);
|
||||
|
||||
// If this property has a present `writeTy`, set it equal to the `readTy`.
|
||||
// This is to ensure that if we normalize a property that has divergent
|
||||
// read and write types, we make them converge (for now).
|
||||
// Sets the write type of this property to the read type.
|
||||
void makeShared();
|
||||
|
||||
bool isShared() const;
|
||||
|
@ -507,6 +517,9 @@ struct TableType
|
|||
std::optional<TypeId> boundTo;
|
||||
Tags tags;
|
||||
|
||||
// Methods of this table that have an untyped self will use the same shared self type.
|
||||
std::optional<TypeId> selfTy;
|
||||
|
||||
// We track the number of as-yet-unadded properties to unsealed tables.
|
||||
// Some constraints will use this information to decide whether or not they
|
||||
// are able to dispatch.
|
||||
|
@ -532,15 +545,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 +567,7 @@ struct ExternType
|
|||
std::optional<Location> definitionLocation;
|
||||
std::optional<TableIndexer> indexer;
|
||||
|
||||
ExternType(
|
||||
ClassType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
|
@ -575,7 +588,7 @@ struct ExternType
|
|||
{
|
||||
}
|
||||
|
||||
ExternType(
|
||||
ClassType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
|
@ -609,6 +622,7 @@ struct UserDefinedFunctionData
|
|||
AstStatTypeFunction* definition = nullptr;
|
||||
|
||||
DenseHashMap<Name, std::pair<AstStatTypeFunction*, size_t>> environment{""};
|
||||
DenseHashMap<Name, AstStatTypeFunction*> environment_DEPRECATED{""};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -775,7 +789,7 @@ using TypeVariant = Unifiable::Variant<
|
|||
FunctionType,
|
||||
TableType,
|
||||
MetatableType,
|
||||
ExternType,
|
||||
ClassType,
|
||||
AnyType,
|
||||
UnionType,
|
||||
IntersectionType,
|
||||
|
@ -868,9 +882,6 @@ struct TypeFun
|
|||
*/
|
||||
TypeId type;
|
||||
|
||||
// The location of where this TypeFun was defined, if available
|
||||
std::optional<Location> definitionLocation;
|
||||
|
||||
TypeFun() = default;
|
||||
|
||||
explicit TypeFun(TypeId ty)
|
||||
|
@ -878,23 +889,16 @@ struct TypeFun
|
|||
{
|
||||
}
|
||||
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type, std::optional<Location> definitionLocation = std::nullopt)
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type)
|
||||
: typeParams(std::move(typeParams))
|
||||
, type(type)
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
||||
TypeFun(
|
||||
std::vector<GenericTypeDefinition> typeParams,
|
||||
std::vector<GenericTypePackDefinition> typePackParams,
|
||||
TypeId type,
|
||||
std::optional<Location> definitionLocation = std::nullopt
|
||||
)
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, std::vector<GenericTypePackDefinition> typePackParams, TypeId type)
|
||||
: typeParams(std::move(typeParams))
|
||||
, typePackParams(std::move(typePackParams))
|
||||
, type(type)
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1199,7 +1202,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope);
|
||||
|
||||
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
|
||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/TypedAllocator.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypePack.h"
|
||||
|
@ -37,7 +36,11 @@ struct TypeArena
|
|||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope);
|
||||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLevel level);
|
||||
|
||||
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
TypeId freshType_DEPRECATED(TypeLevel level);
|
||||
TypeId freshType_DEPRECATED(Scope* scope);
|
||||
TypeId freshType_DEPRECATED(Scope* scope, TypeLevel level);
|
||||
|
||||
TypePackId freshTypePack(Scope* scope);
|
||||
|
||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "Luau/TypeOrPack.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -40,29 +38,18 @@ struct Reasonings
|
|||
|
||||
std::string toString()
|
||||
{
|
||||
if (FFlag::LuauImproveTypePathsInErrors && reasons.empty())
|
||||
return "";
|
||||
|
||||
// DenseHashSet ordering is entirely undefined, so we want to
|
||||
// sort the reasons here to achieve a stable error
|
||||
// stringification.
|
||||
std::sort(reasons.begin(), reasons.end());
|
||||
std::string allReasons = FFlag::LuauImproveTypePathsInErrors ? "\nthis is because " : "";
|
||||
std::string allReasons;
|
||||
bool first = true;
|
||||
for (const std::string& reason : reasons)
|
||||
{
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
if (reasons.size() > 1)
|
||||
allReasons += "\n\t * ";
|
||||
}
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
allReasons += "\n\t";
|
||||
}
|
||||
allReasons += "\n\t";
|
||||
|
||||
allReasons += reason;
|
||||
}
|
||||
|
@ -160,7 +147,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);
|
||||
|
|
|
@ -48,9 +48,6 @@ struct TypeFunctionRuntime
|
|||
// Evaluation of type functions should only be performed in the absence of parse errors in the source module
|
||||
bool allowEvaluation = true;
|
||||
|
||||
// Root scope in which the type function operates in, set up by ConstraintGenerator
|
||||
ScopePtr rootScope;
|
||||
|
||||
// Output created by 'print' function
|
||||
std::vector<std::string> messages;
|
||||
|
||||
|
@ -155,9 +152,6 @@ struct TypeFunction
|
|||
|
||||
/// The reducer function for the type function.
|
||||
ReducerFunction<TypeId> reducer;
|
||||
|
||||
/// If true, this type function can reduce even if it is parameterized on a generic.
|
||||
bool canReduceGenerics = false;
|
||||
};
|
||||
|
||||
/// Represents a type function that may be applied to map a series of types and
|
||||
|
@ -170,9 +164,6 @@ struct TypePackFunction
|
|||
|
||||
/// The reducer function for the type pack function.
|
||||
ReducerFunction<TypePackId> reducer;
|
||||
|
||||
/// If true, this type function can reduce even if it is parameterized on a generic.
|
||||
bool canReduceGenerics = false;
|
||||
};
|
||||
|
||||
struct FunctionGraphReductionResult
|
||||
|
@ -183,7 +174,6 @@ struct FunctionGraphReductionResult
|
|||
DenseHashSet<TypePackId> blockedPacks{nullptr};
|
||||
DenseHashSet<TypeId> reducedTypes{nullptr};
|
||||
DenseHashSet<TypePackId> reducedPacks{nullptr};
|
||||
DenseHashSet<TypeId> irreducibleTypes{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -254,8 +244,6 @@ struct BuiltinTypeFunctions
|
|||
TypeFunction setmetatableFunc;
|
||||
TypeFunction getmetatableFunc;
|
||||
|
||||
TypeFunction weakoptionalFunc;
|
||||
|
||||
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -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,9 @@ struct TypeFunctionExternType
|
|||
std::optional<TypeFunctionTypeId> readParent;
|
||||
std::optional<TypeFunctionTypeId> writeParent;
|
||||
|
||||
TypeId externTy;
|
||||
TypeId classTy;
|
||||
|
||||
std::string name_DEPRECATED;
|
||||
};
|
||||
|
||||
struct TypeFunctionGenericType
|
||||
|
@ -244,7 +246,7 @@ using TypeFunctionTypeVariant = Luau::Variant<
|
|||
TypeFunctionNegationType,
|
||||
TypeFunctionFunctionType,
|
||||
TypeFunctionTableType,
|
||||
TypeFunctionExternType,
|
||||
TypeFunctionClassType,
|
||||
TypeFunctionGenericType>;
|
||||
|
||||
struct TypeFunctionType
|
||||
|
|
|
@ -28,8 +28,14 @@ struct TypeFunctionRuntimeBuilderState
|
|||
{
|
||||
NotNull<TypeFunctionContext> ctx;
|
||||
|
||||
// Mapping of class name to ClassType
|
||||
// Invariant: users can not create a new class types -> any class types that get deserialized must have been an argument to the type function
|
||||
// Using this invariant, whenever a ClassType is serialized, we can put it into this map
|
||||
// whenever a ClassType is deserialized, we can use this map to return the corresponding value
|
||||
DenseHashMap<std::string, TypeId> classesSerialized_DEPRECATED{{}};
|
||||
|
||||
// List of errors that occur during serialization/deserialization
|
||||
// At every iteration of serialization/deserialization, if this list.size() != 0, we halt the process
|
||||
// At every iteration of serialization/deserialzation, if this list.size() != 0, we halt the process
|
||||
std::vector<std::string> errors{};
|
||||
|
||||
TypeFunctionRuntimeBuilderState(NotNull<TypeFunctionContext> ctx)
|
||||
|
|
|
@ -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);
|
||||
|
@ -130,7 +130,6 @@ struct TypeChecker
|
|||
const PredicateVec& predicates = {}
|
||||
);
|
||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||
WithPredicate<TypeId> checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
|
||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
|
||||
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
|
||||
|
@ -487,7 +486,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;
|
||||
};
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/Unifiable.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
@ -27,14 +26,12 @@ struct TypeFunctionInstanceTypePack;
|
|||
struct FreeTypePack
|
||||
{
|
||||
explicit FreeTypePack(TypeLevel level);
|
||||
explicit FreeTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit FreeTypePack(Scope* scope);
|
||||
FreeTypePack(Scope* scope, TypeLevel level);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
Scope* scope = nullptr;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
struct GenericTypePack
|
||||
|
@ -43,7 +40,7 @@ struct GenericTypePack
|
|||
GenericTypePack();
|
||||
explicit GenericTypePack(TypeLevel level);
|
||||
explicit GenericTypePack(const Name& name);
|
||||
explicit GenericTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericTypePack(Scope* scope);
|
||||
GenericTypePack(TypeLevel level, const Name& name);
|
||||
GenericTypePack(Scope* scope, const Name& name);
|
||||
|
||||
|
@ -52,8 +49,6 @@ struct GenericTypePack
|
|||
Scope* scope = nullptr;
|
||||
Name name;
|
||||
bool explicitName = false;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
using BoundTypePack = Unifiable::Bound<TypePackId>;
|
||||
|
@ -105,9 +100,9 @@ struct TypeFunctionInstanceTypePack
|
|||
|
||||
struct TypePackVar
|
||||
{
|
||||
explicit TypePackVar(const TypePackVariant& tp);
|
||||
explicit TypePackVar(TypePackVariant&& tp);
|
||||
TypePackVar(TypePackVariant&& tp, bool persistent);
|
||||
explicit TypePackVar(const TypePackVariant& ty);
|
||||
explicit TypePackVar(TypePackVariant&& ty);
|
||||
TypePackVar(TypePackVariant&& ty, bool persistent);
|
||||
|
||||
bool operator==(const TypePackVar& rhs) const;
|
||||
|
||||
|
@ -174,7 +169,6 @@ struct TypePackIterator
|
|||
|
||||
private:
|
||||
TypePackId currentTypePack = nullptr;
|
||||
TypePackId tailCycleCheck = nullptr;
|
||||
const TypePack* tp = nullptr;
|
||||
size_t currentIndex = 0;
|
||||
|
||||
|
@ -185,8 +179,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);
|
||||
|
|
|
@ -42,19 +42,9 @@ struct Property
|
|||
/// element.
|
||||
struct Index
|
||||
{
|
||||
enum class Variant
|
||||
{
|
||||
Pack,
|
||||
Union,
|
||||
Intersection
|
||||
};
|
||||
|
||||
/// The 0-based index to use for the lookup.
|
||||
size_t index;
|
||||
|
||||
/// The sort of thing we're indexing from, this is used in stringifying the type path for errors.
|
||||
Variant variant;
|
||||
|
||||
bool operator==(const Index& other) const;
|
||||
};
|
||||
|
||||
|
@ -215,9 +205,6 @@ using Path = TypePath::Path;
|
|||
/// terribly clear to end users of the Luau type system.
|
||||
std::string toString(const TypePath::Path& path, bool prefixDot = false);
|
||||
|
||||
/// Converts a Path to a human readable string for error reporting.
|
||||
std::string toStringHuman(const TypePath::Path& path);
|
||||
|
||||
std::optional<TypeOrPack> traverse(TypeId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
|
||||
std::optional<TypeOrPack> traverse(TypePackId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
|
|
|
@ -289,6 +289,4 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
|||
*/
|
||||
void trackInteriorFreeType(Scope* scope, TypeId ty);
|
||||
|
||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -44,12 +44,6 @@ struct Unifier2
|
|||
// Mapping from generic type packs to `TypePack`s of free types to be used in instantiation.
|
||||
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr};
|
||||
|
||||
// Unification sometimes results in the creation of new free types.
|
||||
// We collect them here so that other systems can perform necessary
|
||||
// bookkeeping.
|
||||
std::vector<TypeId> newFreshTypes;
|
||||
std::vector<TypePackId> newFreshTypePacks;
|
||||
|
||||
int recursionCount = 0;
|
||||
int recursionLimit = 0;
|
||||
|
||||
|
@ -119,9 +113,6 @@ private:
|
|||
// Returns true if needle occurs within haystack already. ie if we bound
|
||||
// needle to haystack, would a cyclic TypePack result?
|
||||
OccursCheckResult occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
|
||||
|
||||
TypeId freshType(NotNull<Scope> scope, Polarity polarity);
|
||||
TypePackId freshTypePack(NotNull<Scope> scope, Polarity polarity);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -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))
|
||||
|
|
902
Analysis/src/AnyTypeSummary.cpp
Normal file
902
Analysis/src/AnyTypeSummary.cpp
Normal file
|
@ -0,0 +1,902 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/AnyTypeSummary.h"
|
||||
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Config.h"
|
||||
#include "Luau/ConstraintGenerator.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/Parser.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/TimeTrace.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Transpiler.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/NonStrictTypeChecker.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/VisitType.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeOrPack.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2);
|
||||
LUAU_FASTINTVARIABLE(LuauAnySummaryRecursionLimit, 300);
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
void AnyTypeSummary::traverse(const Module* module, AstStat* src, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
visit(findInnerMostScope(src->location, module), src, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStat* stat, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
RecursionLimiter limiter{&recursionCount, FInt::LuauAnySummaryRecursionLimit};
|
||||
|
||||
if (auto s = stat->as<AstStatBlock>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto i = stat->as<AstStatIf>())
|
||||
return visit(scope, i, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatWhile>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatRepeat>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto r = stat->as<AstStatReturn>())
|
||||
return visit(scope, r, module, builtinTypes);
|
||||
else if (auto e = stat->as<AstStatExpr>())
|
||||
return visit(scope, e, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatLocal>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatFor>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatForIn>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto a = stat->as<AstStatAssign>())
|
||||
return visit(scope, a, module, builtinTypes);
|
||||
else if (auto a = stat->as<AstStatCompoundAssign>())
|
||||
return visit(scope, a, module, builtinTypes);
|
||||
else if (auto f = stat->as<AstStatFunction>())
|
||||
return visit(scope, f, module, builtinTypes);
|
||||
else if (auto f = stat->as<AstStatLocalFunction>())
|
||||
return visit(scope, f, module, builtinTypes);
|
||||
else if (auto a = stat->as<AstStatTypeAlias>())
|
||||
return visit(scope, a, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatDeclareFunction>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatDeclareClass>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
else if (auto s = stat->as<AstStatError>())
|
||||
return visit(scope, s, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatBlock* block, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
RecursionCounter counter{&recursionCount};
|
||||
|
||||
if (recursionCount >= FInt::LuauAnySummaryRecursionLimit)
|
||||
return; // don't report
|
||||
|
||||
for (AstStat* stat : block->body)
|
||||
visit(scope, stat, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatIf* ifStatement, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (ifStatement->thenbody)
|
||||
{
|
||||
const Scope* thenScope = findInnerMostScope(ifStatement->thenbody->location, module);
|
||||
visit(thenScope, ifStatement->thenbody, module, builtinTypes);
|
||||
}
|
||||
|
||||
if (ifStatement->elsebody)
|
||||
{
|
||||
const Scope* elseScope = findInnerMostScope(ifStatement->elsebody->location, module);
|
||||
visit(elseScope, ifStatement->elsebody, module, builtinTypes);
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatWhile* while_, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const Scope* whileScope = findInnerMostScope(while_->location, module);
|
||||
visit(whileScope, while_->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatRepeat* repeat, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const Scope* repeatScope = findInnerMostScope(repeat->location, module);
|
||||
visit(repeatScope, repeat->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatReturn* ret, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const Scope* retScope = findInnerMostScope(ret->location, module);
|
||||
|
||||
auto ctxNode = getNode(rootSrc, ret);
|
||||
bool seenTP = false;
|
||||
|
||||
for (auto val : ret->list)
|
||||
{
|
||||
if (isAnyCall(retScope, val, module, builtinTypes))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
types.inferredType = toString(lookupType(val, module, builtinTypes));
|
||||
TypeInfo ti{Pattern::FuncApp, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (isAnyCast(retScope, val, module, builtinTypes))
|
||||
{
|
||||
if (auto cast = val->as<AstExprTypeAssertion>())
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(lookupAnnotation(cast->annotation, module, builtinTypes));
|
||||
types.inferredType = toString(lookupType(cast->expr, module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret->list.size > 1 && !seenTP)
|
||||
{
|
||||
if (containsAny(retScope->returnType))
|
||||
{
|
||||
seenTP = true;
|
||||
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(retScope->returnType);
|
||||
|
||||
TypeInfo ti{Pattern::TypePk, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
auto ctxNode = getNode(rootSrc, local);
|
||||
|
||||
TypePackId values = reconstructTypePack(local->values, module, builtinTypes);
|
||||
auto [head, tail] = flatten(values);
|
||||
|
||||
size_t posn = 0;
|
||||
for (AstLocal* loc : local->vars)
|
||||
{
|
||||
if (local->vars.data[0] == loc && posn < local->values.size)
|
||||
{
|
||||
if (loc->annotation)
|
||||
{
|
||||
auto annot = lookupAnnotation(loc->annotation, module, builtinTypes);
|
||||
if (containsAny(annot))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(annot);
|
||||
types.inferredType = toString(lookupType(local->values.data[posn], module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::VarAnnot, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
|
||||
const AstExprTypeAssertion* maybeRequire = local->values.data[posn]->as<AstExprTypeAssertion>();
|
||||
if (!maybeRequire)
|
||||
continue;
|
||||
|
||||
if (std::min(local->values.size - 1, posn) < head.size())
|
||||
{
|
||||
if (isAnyCast(scope, local->values.data[posn], module, builtinTypes))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(head[std::min(local->values.size - 1, posn)]);
|
||||
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (std::min(local->values.size - 1, posn) < head.size())
|
||||
{
|
||||
if (loc->annotation)
|
||||
{
|
||||
auto annot = lookupAnnotation(loc->annotation, module, builtinTypes);
|
||||
if (containsAny(annot))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(annot);
|
||||
types.inferredType = toString(head[std::min(local->values.size - 1, posn)]);
|
||||
|
||||
TypeInfo ti{Pattern::VarAnnot, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tail)
|
||||
{
|
||||
if (containsAny(*tail))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(*tail);
|
||||
|
||||
TypeInfo ti{Pattern::VarAny, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++posn;
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatFor* for_, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const Scope* forScope = findInnerMostScope(for_->location, module);
|
||||
visit(forScope, for_->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatForIn* forIn, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const Scope* loopScope = findInnerMostScope(forIn->location, module);
|
||||
visit(loopScope, forIn->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatAssign* assign, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
auto ctxNode = getNode(rootSrc, assign);
|
||||
|
||||
TypePackId values = reconstructTypePack(assign->values, module, builtinTypes);
|
||||
auto [head, tail] = flatten(values);
|
||||
|
||||
size_t posn = 0;
|
||||
for (AstExpr* var : assign->vars)
|
||||
{
|
||||
TypeId tp = lookupType(var, module, builtinTypes);
|
||||
if (containsAny(tp))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(tp);
|
||||
|
||||
auto loc = std::min(assign->vars.size - 1, posn);
|
||||
if (head.size() >= assign->vars.size && posn < head.size())
|
||||
{
|
||||
types.inferredType = toString(head[posn]);
|
||||
}
|
||||
else if (loc < head.size())
|
||||
types.inferredType = toString(head[loc]);
|
||||
else
|
||||
types.inferredType = toString(builtinTypes->nilType);
|
||||
|
||||
TypeInfo ti{Pattern::Assign, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
++posn;
|
||||
}
|
||||
|
||||
for (AstExpr* val : assign->values)
|
||||
{
|
||||
if (isAnyCall(scope, val, module, builtinTypes))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(lookupType(val, module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::FuncApp, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (isAnyCast(scope, val, module, builtinTypes))
|
||||
{
|
||||
if (auto cast = val->as<AstExprTypeAssertion>())
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(lookupAnnotation(cast->annotation, module, builtinTypes));
|
||||
types.inferredType = toString(lookupType(val, module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tail)
|
||||
{
|
||||
if (containsAny(*tail))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(*tail);
|
||||
|
||||
TypeInfo ti{Pattern::Assign, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatCompoundAssign* assign, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
auto ctxNode = getNode(rootSrc, assign);
|
||||
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(lookupType(assign->value, module, builtinTypes));
|
||||
types.annotatedType = toString(lookupType(assign->var, module, builtinTypes));
|
||||
|
||||
if (module->astTypes.contains(assign->var))
|
||||
{
|
||||
if (containsAny(*module->astTypes.find(assign->var)))
|
||||
{
|
||||
TypeInfo ti{Pattern::Assign, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
else if (module->astTypePacks.contains(assign->var))
|
||||
{
|
||||
if (containsAny(*module->astTypePacks.find(assign->var)))
|
||||
{
|
||||
TypeInfo ti{Pattern::Assign, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAnyCall(scope, assign->value, module, builtinTypes))
|
||||
{
|
||||
TypeInfo ti{Pattern::FuncApp, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (isAnyCast(scope, assign->value, module, builtinTypes))
|
||||
{
|
||||
if (auto cast = assign->value->as<AstExprTypeAssertion>())
|
||||
{
|
||||
types.annotatedType = toString(lookupAnnotation(cast->annotation, module, builtinTypes));
|
||||
types.inferredType = toString(lookupType(cast->expr, module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatFunction* function, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
types.inferredType = toString(lookupType(function->func, module, builtinTypes));
|
||||
|
||||
if (hasVariadicAnys(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
TypeInfo ti{Pattern::VarAny, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (hasArgAnys(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
TypeInfo ti{Pattern::FuncArg, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (hasAnyReturns(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
TypeInfo ti{Pattern::FuncRet, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (function->func->body->body.size > 0)
|
||||
visit(scope, function->func->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatLocalFunction* function, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
if (hasVariadicAnys(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
types.inferredType = toString(lookupType(function->func, module, builtinTypes));
|
||||
TypeInfo ti{Pattern::VarAny, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (hasArgAnys(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
types.inferredType = toString(lookupType(function->func, module, builtinTypes));
|
||||
TypeInfo ti{Pattern::FuncArg, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (hasAnyReturns(scope, function->func, module, builtinTypes))
|
||||
{
|
||||
types.inferredType = toString(lookupType(function->func, module, builtinTypes));
|
||||
TypeInfo ti{Pattern::FuncRet, toString(function), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
|
||||
if (function->func->body->body.size > 0)
|
||||
visit(scope, function->func->body, module, builtinTypes);
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatTypeAlias* alias, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
auto ctxNode = getNode(rootSrc, alias);
|
||||
|
||||
auto annot = lookupAnnotation(alias->type, module, builtinTypes);
|
||||
if (containsAny(annot))
|
||||
{
|
||||
// no expr => no inference for aliases
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.annotatedType = toString(annot);
|
||||
TypeInfo ti{Pattern::Alias, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
auto ctxNode = getNode(rootSrc, expr);
|
||||
|
||||
if (isAnyCall(scope, expr->expr, module, builtinTypes))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
types.inferredType = toString(lookupType(expr->expr, module, builtinTypes));
|
||||
|
||||
TypeInfo ti{Pattern::FuncApp, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatDeclareGlobal* declareGlobal, const Module* module, NotNull<BuiltinTypes> builtinTypes) {}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatDeclareClass* declareClass, const Module* module, NotNull<BuiltinTypes> builtinTypes) {}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatDeclareFunction* declareFunction, const Module* module, NotNull<BuiltinTypes> builtinTypes) {}
|
||||
|
||||
void AnyTypeSummary::visit(const Scope* scope, AstStatError* error, const Module* module, NotNull<BuiltinTypes> builtinTypes) {}
|
||||
|
||||
TypeId AnyTypeSummary::checkForFamilyInhabitance(const TypeId instance, const Location location)
|
||||
{
|
||||
if (seenTypeFamilyInstances.find(instance))
|
||||
return instance;
|
||||
|
||||
seenTypeFamilyInstances.insert(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
TypeId AnyTypeSummary::lookupType(const AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
const TypeId* ty = module->astTypes.find(expr);
|
||||
if (ty)
|
||||
return checkForFamilyInhabitance(follow(*ty), expr->location);
|
||||
|
||||
const TypePackId* tp = module->astTypePacks.find(expr);
|
||||
if (tp)
|
||||
{
|
||||
if (auto fst = first(*tp, /*ignoreHiddenVariadics*/ false))
|
||||
return checkForFamilyInhabitance(*fst, expr->location);
|
||||
else if (finite(*tp) && size(*tp) == 0)
|
||||
return checkForFamilyInhabitance(builtinTypes->nilType, expr->location);
|
||||
}
|
||||
|
||||
return builtinTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
TypePackId AnyTypeSummary::reconstructTypePack(AstArray<AstExpr*> exprs, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (exprs.size == 0)
|
||||
return arena.addTypePack(TypePack{{}, std::nullopt});
|
||||
|
||||
std::vector<TypeId> head;
|
||||
|
||||
for (size_t i = 0; i < exprs.size - 1; ++i)
|
||||
{
|
||||
head.push_back(lookupType(exprs.data[i], module, builtinTypes));
|
||||
}
|
||||
|
||||
const TypePackId* tail = module->astTypePacks.find(exprs.data[exprs.size - 1]);
|
||||
if (tail)
|
||||
return arena.addTypePack(TypePack{std::move(head), follow(*tail)});
|
||||
else
|
||||
return arena.addTypePack(TypePack{std::move(head), builtinTypes->errorRecoveryTypePack()});
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::isAnyCall(const Scope* scope, AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (auto call = expr->as<AstExprCall>())
|
||||
{
|
||||
TypePackId args = reconstructTypePack(call->args, module, builtinTypes);
|
||||
if (containsAny(args))
|
||||
return true;
|
||||
|
||||
TypeId func = lookupType(call->func, module, builtinTypes);
|
||||
if (containsAny(func))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::hasVariadicAnys(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (expr->vararg && expr->varargAnnotation)
|
||||
{
|
||||
auto annot = lookupPackAnnotation(expr->varargAnnotation, module);
|
||||
if (annot && containsAny(*annot))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::hasArgAnys(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (expr->args.size > 0)
|
||||
{
|
||||
for (const AstLocal* arg : expr->args)
|
||||
{
|
||||
if (arg->annotation)
|
||||
{
|
||||
auto annot = lookupAnnotation(arg->annotation, module, builtinTypes);
|
||||
if (containsAny(annot))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::hasAnyReturns(const Scope* scope, AstExprFunction* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (!expr->returnAnnotation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AstType* ret : expr->returnAnnotation->types)
|
||||
{
|
||||
if (containsAny(lookupAnnotation(ret, module, builtinTypes)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->returnAnnotation->tailType)
|
||||
{
|
||||
auto annot = lookupPackAnnotation(expr->returnAnnotation->tailType, module);
|
||||
if (annot && containsAny(*annot))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::isAnyCast(const Scope* scope, AstExpr* expr, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||
{
|
||||
if (auto cast = expr->as<AstExprTypeAssertion>())
|
||||
{
|
||||
auto annot = lookupAnnotation(cast->annotation, module, builtinTypes);
|
||||
if (containsAny(annot))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeId AnyTypeSummary::lookupAnnotation(AstType* annotation, const Module* module, NotNull<BuiltinTypes> builtintypes)
|
||||
{
|
||||
if (FFlag::DebugLuauMagicTypes)
|
||||
{
|
||||
if (auto ref = annotation->as<AstTypeReference>(); ref && ref->parameters.size > 0)
|
||||
{
|
||||
if (auto ann = ref->parameters.data[0].type)
|
||||
{
|
||||
TypeId argTy = lookupAnnotation(ref->parameters.data[0].type, module, builtintypes);
|
||||
return follow(argTy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TypeId* ty = module->astResolvedTypes.find(annotation);
|
||||
if (ty)
|
||||
return checkForTypeFunctionInhabitance(follow(*ty), annotation->location);
|
||||
else
|
||||
return checkForTypeFunctionInhabitance(builtintypes->errorRecoveryType(), annotation->location);
|
||||
}
|
||||
|
||||
TypeId AnyTypeSummary::checkForTypeFunctionInhabitance(const TypeId instance, const Location location)
|
||||
{
|
||||
if (seenTypeFunctionInstances.find(instance))
|
||||
return instance;
|
||||
seenTypeFunctionInstances.insert(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::optional<TypePackId> AnyTypeSummary::lookupPackAnnotation(AstTypePack* annotation, const Module* module)
|
||||
{
|
||||
const TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
||||
if (tp != nullptr)
|
||||
return {follow(*tp)};
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::containsAny(TypeId typ)
|
||||
{
|
||||
typ = follow(typ);
|
||||
|
||||
if (auto t = seen.find(typ); t && !*t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
seen[typ] = false;
|
||||
|
||||
RecursionCounter counter{&recursionCount};
|
||||
if (recursionCount >= FInt::LuauAnySummaryRecursionLimit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (auto ty = get<AnyType>(typ))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (auto ty = get<UnknownType>(typ))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else if (auto ty = get<TableType>(typ))
|
||||
{
|
||||
for (auto& [_name, prop] : ty->props)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
if (auto newT = follow(prop.readTy))
|
||||
{
|
||||
if (containsAny(*newT))
|
||||
found = true;
|
||||
}
|
||||
else if (auto newT = follow(prop.writeTy))
|
||||
{
|
||||
if (containsAny(*newT))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (containsAny(prop.type()))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto ty = get<IntersectionType>(typ))
|
||||
{
|
||||
for (auto part : ty->parts)
|
||||
{
|
||||
if (containsAny(part))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto ty = get<UnionType>(typ))
|
||||
{
|
||||
for (auto option : ty->options)
|
||||
{
|
||||
if (containsAny(option))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto ty = get<FunctionType>(typ))
|
||||
{
|
||||
if (containsAny(ty->argTypes))
|
||||
found = true;
|
||||
else if (containsAny(ty->retTypes))
|
||||
found = true;
|
||||
}
|
||||
|
||||
seen[typ] = found;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::containsAny(TypePackId typ)
|
||||
{
|
||||
typ = follow(typ);
|
||||
|
||||
if (auto t = seen.find(typ); t && !*t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
seen[typ] = false;
|
||||
|
||||
auto [head, tail] = flatten(typ);
|
||||
bool found = false;
|
||||
|
||||
for (auto tp : head)
|
||||
{
|
||||
if (containsAny(tp))
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (tail)
|
||||
{
|
||||
if (auto vtp = get<VariadicTypePack>(tail))
|
||||
{
|
||||
if (auto ty = get<AnyType>(follow(vtp->ty)))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else if (auto tftp = get<TypeFunctionInstanceTypePack>(tail))
|
||||
{
|
||||
|
||||
for (TypePackId tp : tftp->packArguments)
|
||||
{
|
||||
if (containsAny(tp))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (TypeId t : tftp->typeArguments)
|
||||
{
|
||||
if (containsAny(t))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seen[typ] = found;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
const Scope* AnyTypeSummary::findInnerMostScope(const Location location, const Module* module)
|
||||
{
|
||||
const Scope* bestScope = module->getModuleScope().get();
|
||||
|
||||
bool didNarrow = false;
|
||||
do
|
||||
{
|
||||
didNarrow = false;
|
||||
for (auto scope : bestScope->children)
|
||||
{
|
||||
if (scope->location.encloses(location))
|
||||
{
|
||||
bestScope = scope.get();
|
||||
didNarrow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (didNarrow && bestScope->children.size() > 0);
|
||||
|
||||
return bestScope;
|
||||
}
|
||||
|
||||
std::optional<AstExpr*> AnyTypeSummary::matchRequire(const AstExprCall& call)
|
||||
{
|
||||
const char* require = "require";
|
||||
|
||||
if (call.args.size != 1)
|
||||
return std::nullopt;
|
||||
|
||||
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
|
||||
if (!funcAsGlobal || funcAsGlobal->name != require)
|
||||
return std::nullopt;
|
||||
|
||||
if (call.args.size != 1)
|
||||
return std::nullopt;
|
||||
|
||||
return call.args.data[0];
|
||||
}
|
||||
|
||||
AstNode* AnyTypeSummary::getNode(AstStatBlock* root, AstNode* node)
|
||||
{
|
||||
FindReturnAncestry finder(node, root->location.end);
|
||||
root->visit(&finder);
|
||||
|
||||
if (!finder.currNode)
|
||||
finder.currNode = node;
|
||||
|
||||
LUAU_ASSERT(finder.found && finder.currNode);
|
||||
return finder.currNode;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::FindReturnAncestry::visit(AstStatLocalFunction* node)
|
||||
{
|
||||
currNode = node;
|
||||
return !found;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::FindReturnAncestry::visit(AstStatFunction* node)
|
||||
{
|
||||
currNode = node;
|
||||
return !found;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::FindReturnAncestry::visit(AstType* node)
|
||||
{
|
||||
return !found;
|
||||
}
|
||||
|
||||
bool AnyTypeSummary::FindReturnAncestry::visit(AstNode* node)
|
||||
{
|
||||
if (node == stat)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (node->location.end == rootEnd && stat->location.end >= rootEnd)
|
||||
{
|
||||
currNode = node;
|
||||
found = true;
|
||||
}
|
||||
|
||||
return !found;
|
||||
}
|
||||
|
||||
|
||||
AnyTypeSummary::TypeInfo::TypeInfo(Pattern code, std::string node, TelemetryTypePair type)
|
||||
: code(code)
|
||||
, node(node)
|
||||
, type(type)
|
||||
{
|
||||
}
|
||||
|
||||
AnyTypeSummary::FindReturnAncestry::FindReturnAncestry(AstNode* stat, Position rootEnd)
|
||||
: stat(stat)
|
||||
, rootEnd(rootEnd)
|
||||
{
|
||||
}
|
||||
|
||||
AnyTypeSummary::AnyTypeSummary() {}
|
||||
|
||||
} // namespace Luau
|
|
@ -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);
|
||||
}
|
||||
if (node->returnAnnotation)
|
||||
PROP(returnAnnotation);
|
||||
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(retTypes);
|
||||
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);
|
||||
PROP(returnTypes);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1081,11 +1065,6 @@ struct AstJsonEncoder : public AstVisitor
|
|||
);
|
||||
}
|
||||
|
||||
void write(class AstTypeOptional* node)
|
||||
{
|
||||
writeNode(node, "AstTypeOptional", [&]() {});
|
||||
}
|
||||
|
||||
void write(class AstTypeUnion* node)
|
||||
{
|
||||
writeNode(
|
||||
|
@ -1167,8 +1146,6 @@ struct AstJsonEncoder : public AstVisitor
|
|||
return writeString("checked");
|
||||
case AstAttr::Type::Native:
|
||||
return writeString("native");
|
||||
case AstAttr::Type::Deprecated:
|
||||
return writeString("deprecated");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1445,7 +1422,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(class AstStatDeclareExternType* node) override
|
||||
bool visit(class AstStatDeclareClass* node) override
|
||||
{
|
||||
write(node);
|
||||
return false;
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -41,13 +43,24 @@ struct AutocompleteNodeFinder : public AstVisitor
|
|||
|
||||
bool visit(AstStat* stat) override
|
||||
{
|
||||
// Consider 'local myLocal = 4;|' and 'local myLocal = 4', where '|' is the cursor position. In both cases, the cursor position is equal
|
||||
// to `AstStatLocal.location.end`. However, in the first case (semicolon), we are starting a new statement, whilst in the second case
|
||||
// (no semicolon) we are still part of the AstStatLocal, hence the different comparison check.
|
||||
if (stat->location.begin < pos && (stat->hasSemicolon ? pos < stat->location.end : pos <= stat->location.end))
|
||||
if (FFlag::LuauExtendStatEndPosWithSemicolon)
|
||||
{
|
||||
ancestry.push_back(stat);
|
||||
return true;
|
||||
// Consider 'local myLocal = 4;|' and 'local myLocal = 4', where '|' is the cursor position. In both cases, the cursor position is equal
|
||||
// to `AstStatLocal.location.end`. However, in the first case (semicolon), we are starting a new statement, whilst in the second case
|
||||
// (no semicolon) we are still part of the AstStatLocal, hence the different comparison check.
|
||||
if (stat->location.begin < pos && (stat->hasSemicolon ? pos < stat->location.end : pos <= stat->location.end))
|
||||
{
|
||||
ancestry.push_back(stat);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stat->location.begin < pos && pos <= stat->location.end)
|
||||
{
|
||||
ancestry.push_back(stat);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -574,11 +587,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 +603,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,10 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
|
||||
|
||||
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"};
|
||||
|
@ -80,8 +80,6 @@ static ParenthesesRecommendation getParenRecommendationForIntersect(const Inters
|
|||
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
|
||||
for (Luau::TypeId partId : intersect->parts)
|
||||
{
|
||||
if (FFlag::LuauAutocompleteMissingFollows)
|
||||
partId = follow(partId);
|
||||
if (auto partFunc = Luau::get<FunctionType>(partId))
|
||||
{
|
||||
rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes));
|
||||
|
@ -307,7 +305,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 +328,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 +362,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 +395,7 @@ static void autocompleteProps(
|
|||
prop.deprecated,
|
||||
isWrongIndexer(type),
|
||||
typeCorrect,
|
||||
containingExternType,
|
||||
containingClass,
|
||||
&prop,
|
||||
prop.documentationSymbol,
|
||||
{},
|
||||
|
@ -428,12 +426,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);
|
||||
|
@ -484,21 +482,6 @@ static void autocompleteProps(
|
|||
AutocompleteEntryMap inner;
|
||||
std::unordered_set<TypeId> innerSeen;
|
||||
|
||||
// If we don't do this, and we have the misfortune of receiving a
|
||||
// recursive union like:
|
||||
//
|
||||
// t1 where t1 = t1 | ExternType
|
||||
//
|
||||
// Then we are on a one way journey to a stack overflow.
|
||||
if (FFlag::LuauAutocompleteUnionCopyPreviousSeen)
|
||||
{
|
||||
for (auto ty : seen)
|
||||
{
|
||||
if (is<UnionType, IntersectionType>(ty))
|
||||
innerSeen.insert(ty);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNil(*iter))
|
||||
{
|
||||
++iter;
|
||||
|
@ -587,7 +570,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 +682,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,16 +702,8 @@ static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Posi
|
|||
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
|
||||
return element;
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto element = findTypeElementAt(type->returnTypes_DEPRECATED, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
|
||||
return element;
|
||||
}
|
||||
|
||||
// It's possible to walk through other types like intrsection and unions if we find value in doing that
|
||||
|
@ -761,7 +712,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 +854,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,46 +1033,29 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
if (!node->returnAnnotation)
|
||||
return result;
|
||||
|
||||
for (size_t i = 0; i < node->returnAnnotation->types.size; i++)
|
||||
{
|
||||
if (!node->returnAnnotation)
|
||||
return result;
|
||||
AstType* ret = node->returnAnnotation->types.data[i];
|
||||
|
||||
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
|
||||
if (ret->location.containsClosed(position))
|
||||
{
|
||||
for (size_t i = 0; i < typePack->typeList.types.size; i++)
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
AstType* ret = typePack->typeList.types.data[i];
|
||||
|
||||
if (ret->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, i))
|
||||
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
|
||||
}
|
||||
|
||||
// TODO: with additional type information, we could suggest inferred return type here
|
||||
break;
|
||||
}
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, i))
|
||||
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
|
||||
}
|
||||
|
||||
if (AstTypePack* retTp = typePack->typeList.tailType)
|
||||
{
|
||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||
{
|
||||
if (variadic->location.containsClosed(position))
|
||||
{
|
||||
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
|
||||
{
|
||||
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
|
||||
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: with additional type information, we could suggest inferred return type here
|
||||
break;
|
||||
}
|
||||
else if (auto variadic = node->returnAnnotation->as<AstTypePackVariadic>())
|
||||
}
|
||||
|
||||
if (AstTypePack* retTp = node->returnAnnotation->tailType)
|
||||
{
|
||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||
{
|
||||
if (variadic->location.containsClosed(position))
|
||||
{
|
||||
|
@ -1133,43 +1067,6 @@ AutocompleteEntryMap autocompleteTypeNames(
|
|||
}
|
||||
}
|
||||
}
|
||||
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 +1187,7 @@ static AutocompleteEntryMap autocompleteStatement(
|
|||
)
|
||||
{
|
||||
// This is inefficient. :(
|
||||
ScopePtr scope = scopeAtPosition;
|
||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
|
||||
AutocompleteEntryMap result;
|
||||
|
||||
|
@ -1468,7 +1365,7 @@ static AutocompleteContext autocompleteExpression(
|
|||
else
|
||||
{
|
||||
// This is inefficient. :(
|
||||
ScopePtr scope = scopeAtPosition;
|
||||
ScopePtr scope = FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete ? scopeAtPosition : findScopeAtPosition(module, position);
|
||||
|
||||
while (scope)
|
||||
{
|
||||
|
@ -1537,7 +1434,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 +1458,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;
|
||||
|
@ -1622,11 +1519,10 @@ static std::optional<AutocompleteEntryMap> convertRequireSuggestionsToAutocomple
|
|||
return std::nullopt;
|
||||
|
||||
AutocompleteEntryMap result;
|
||||
for (RequireSuggestion& suggestion : *suggestions)
|
||||
for (const RequireSuggestion& suggestion : *suggestions)
|
||||
{
|
||||
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
|
||||
entry.insertText = std::move(suggestion.fullPath);
|
||||
entry.tags = std::move(suggestion.tags);
|
||||
result[std::move(suggestion.label)] = std::move(entry);
|
||||
}
|
||||
return result;
|
||||
|
@ -1687,7 +1583,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;
|
||||
}
|
||||
|
@ -1705,8 +1601,6 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
|
|||
{
|
||||
for (TypeId part : intersect->parts)
|
||||
{
|
||||
if (FFlag::LuauAutocompleteMissingFollows)
|
||||
part = follow(part);
|
||||
if (auto candidateFunctionType = Luau::get<FunctionType>(part))
|
||||
{
|
||||
if (std::optional<AutocompleteEntryMap> ret = performCallback(candidateFunctionType))
|
||||
|
@ -1855,7 +1749,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;
|
||||
|
||||
|
|
|
@ -3,23 +3,22 @@
|
|||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/ConstraintGenerator.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Frontend.h"
|
||||
#include "Luau/InferPolarity.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
#include "Luau/Symbol.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/ConstraintGenerator.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -30,11 +29,11 @@
|
|||
*/
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFreezeIgnorePersistent)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -248,7 +247,6 @@ void addGlobalBinding(GlobalTypes& globals, const ScopePtr& scope, const std::st
|
|||
|
||||
void addGlobalBinding(GlobalTypes& globals, const ScopePtr& scope, const std::string& name, Binding binding)
|
||||
{
|
||||
inferGenericPolarities(NotNull{&globals.globalTypes}, NotNull{scope.get()}, binding.typeId);
|
||||
scope->bindings[globals.globalNames.names->getOrAdd(name.c_str())] = binding;
|
||||
}
|
||||
|
||||
|
@ -290,22 +288,6 @@ void assignPropDocumentationSymbols(TableType::Props& props, const std::string&
|
|||
}
|
||||
}
|
||||
|
||||
static void finalizeGlobalBindings(ScopePtr scope)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUserTypeFunTypecheck);
|
||||
|
||||
for (const auto& pair : scope->bindings)
|
||||
{
|
||||
persist(pair.second.typeId);
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(pair.second.typeId))
|
||||
{
|
||||
if (!ttv->name)
|
||||
ttv->name = "typeof(" + toString(pair.first) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeCheckForAutocomplete)
|
||||
{
|
||||
LUAU_ASSERT(!globals.globalTypes.types.isFrozen());
|
||||
|
@ -313,9 +295,6 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
|
||||
TypeArena& arena = globals.globalTypes;
|
||||
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization2
|
||||
if (FFlag::LuauNonReentrantGeneralization2)
|
||||
globalScope = globals.globalScope.get();
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()});
|
||||
|
@ -325,8 +304,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
);
|
||||
LUAU_ASSERT(loadResult.success);
|
||||
|
||||
TypeId genericK = arena.addType(GenericType{globalScope, "K"});
|
||||
TypeId genericV = arena.addType(GenericType{globalScope, "V"});
|
||||
TypeId genericK = arena.addType(GenericType{"K"});
|
||||
TypeId genericV = arena.addType(GenericType{"V"});
|
||||
TypeId mapOfKtoV = arena.addType(TableType{{}, TableIndexer(genericK, genericV), globals.globalScope->level, TableState::Generic});
|
||||
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(builtinTypes->stringType, builtinTypes);
|
||||
|
@ -343,7 +322,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);
|
||||
|
@ -374,7 +353,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
// pairs<K, V>(t: Table<K, V>) -> ((Table<K, V>, K?) -> (K, V), Table<K, V>, nil)
|
||||
addGlobalBinding(globals, "pairs", arena.addType(FunctionType{{genericK, genericV}, {}, pairsArgsTypePack, pairsReturnTypePack}), "@luau");
|
||||
|
||||
TypeId genericMT = arena.addType(GenericType{globalScope, "MT"});
|
||||
TypeId genericMT = arena.addType(GenericType{"MT"});
|
||||
|
||||
TableType tab{TableState::Generic, globals.globalScope->level};
|
||||
TypeId tabTy = arena.addType(tab);
|
||||
|
@ -386,7 +365,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
||||
TypeId genericT = arena.addType(GenericType{"T"});
|
||||
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
||||
|
||||
// clang-format off
|
||||
|
@ -420,21 +399,14 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
// clang-format on
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunTypecheck)
|
||||
for (const auto& pair : globals.globalScope->bindings)
|
||||
{
|
||||
finalizeGlobalBindings(globals.globalScope);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& pair : globals.globalScope->bindings)
|
||||
{
|
||||
persist(pair.second.typeId);
|
||||
persist(pair.second.typeId);
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(pair.second.typeId))
|
||||
{
|
||||
if (!ttv->name)
|
||||
ttv->name = "typeof(" + toString(pair.first) + ")";
|
||||
}
|
||||
if (TableType* ttv = getMutable<TableType>(pair.second.typeId))
|
||||
{
|
||||
if (!ttv->name)
|
||||
ttv->name = "typeof(" + toString(pair.first) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,7 +415,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// declare function assert<T>(value: T, errorMessage: string?): intersect<T, ~(false?)>
|
||||
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
||||
TypeId genericT = arena.addType(GenericType{"T"});
|
||||
TypeId refinedTy = arena.addType(TypeFunctionInstanceType{
|
||||
NotNull{&builtinTypeFunctions().intersectFunc}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}
|
||||
});
|
||||
|
@ -466,16 +438,12 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
// the top table type. We do the best we can by modelling these
|
||||
// functions using unconstrained generics. It's not quite right,
|
||||
// but it'll be ok for now.
|
||||
TypeId genericTy = arena.addType(GenericType{globalScope, "T"});
|
||||
TypeId genericTy = arena.addType(GenericType{"T"});
|
||||
TypePackId thePack = arena.addTypePack({genericTy});
|
||||
TypeId idTyWithMagic = arena.addType(FunctionType{{genericTy}, {}, thePack, thePack});
|
||||
ttv->props["freeze"] = makeProperty(idTyWithMagic, "@luau/global/table.freeze");
|
||||
if (globalScope)
|
||||
inferGenericPolarities(NotNull{&globals.globalTypes}, NotNull{globalScope}, idTyWithMagic);
|
||||
|
||||
TypeId idTy = arena.addType(FunctionType{{genericTy}, {}, thePack, thePack});
|
||||
if (globalScope)
|
||||
inferGenericPolarities(NotNull{&globals.globalTypes}, NotNull{globalScope}, idTy);
|
||||
ttv->props["clone"] = makeProperty(idTy, "@luau/global/table.clone");
|
||||
}
|
||||
else
|
||||
|
@ -499,59 +467,6 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
TypeId requireTy = getGlobalBinding(globals, "require");
|
||||
attachTag(requireTy, kRequireTagName);
|
||||
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
|
||||
|
||||
if (FFlag::LuauUserTypeFunTypecheck)
|
||||
{
|
||||
// Global scope cannot be the parent of the type checking environment because it can be changed by the embedder
|
||||
globals.globalTypeFunctionScope->exportedTypeBindings = globals.globalScope->exportedTypeBindings;
|
||||
globals.globalTypeFunctionScope->builtinTypeNames = globals.globalScope->builtinTypeNames;
|
||||
|
||||
// Type function runtime also removes a few standard libraries and globals, so we will take only the ones that are defined
|
||||
static const char* typeFunctionRuntimeBindings[] = {
|
||||
// Libraries
|
||||
"math",
|
||||
"table",
|
||||
"string",
|
||||
"bit32",
|
||||
"utf8",
|
||||
"buffer",
|
||||
|
||||
// Globals
|
||||
"assert",
|
||||
"error",
|
||||
"print",
|
||||
"next",
|
||||
"ipairs",
|
||||
"pairs",
|
||||
"select",
|
||||
"unpack",
|
||||
"getmetatable",
|
||||
"setmetatable",
|
||||
"rawget",
|
||||
"rawset",
|
||||
"rawlen",
|
||||
"rawequal",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"type",
|
||||
"typeof",
|
||||
};
|
||||
|
||||
for (auto& name : typeFunctionRuntimeBindings)
|
||||
{
|
||||
AstName astName = globals.globalNames.names->get(name);
|
||||
LUAU_ASSERT(astName.value);
|
||||
|
||||
globals.globalTypeFunctionScope->bindings[astName] = globals.globalScope->bindings[astName];
|
||||
}
|
||||
|
||||
LoadDefinitionFileResult typeFunctionLoadResult = frontend.loadDefinitionFile(
|
||||
globals, globals.globalTypeFunctionScope, getTypeFunctionDefinitionSource(), "@luau", /* captureComments */ false, false
|
||||
);
|
||||
LUAU_ASSERT(typeFunctionLoadResult.success);
|
||||
|
||||
finalizeGlobalBindings(globals.globalTypeFunctionScope);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<TypeId> parseFormatString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
|
||||
|
@ -704,14 +619,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,25 +630,31 @@ 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);
|
||||
|
||||
if (!result.isSubtype)
|
||||
{
|
||||
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
||||
if (FFlag::LuauStringFormatErrorSuppression)
|
||||
{
|
||||
case ErrorSuppression::Suppress:
|
||||
break;
|
||||
case ErrorSuppression::NormalizationFailed:
|
||||
break;
|
||||
case ErrorSuppression::DoNotSuppress:
|
||||
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
||||
{
|
||||
case ErrorSuppression::Suppress:
|
||||
break;
|
||||
case ErrorSuppression::NormalizationFailed:
|
||||
break;
|
||||
case ErrorSuppression::DoNotSuppress:
|
||||
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||
|
||||
if (!reasonings.suppressed)
|
||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||
if (!reasonings.suppressed)
|
||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1531,14 +1444,15 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
|||
return false;
|
||||
|
||||
CloneState cloneState{context.solver->builtinTypes};
|
||||
TypeId resultType = shallowClone(inputType, *arena, cloneState, /* ignorePersistent */ true);
|
||||
TypeId resultType = shallowClone(inputType, *arena, cloneState, /* ignorePersistent */ FFlag::LuauFreezeIgnorePersistent);
|
||||
|
||||
if (auto tableType = getMutable<TableType>(resultType))
|
||||
{
|
||||
tableType->scope = context.constraint->scope.get();
|
||||
}
|
||||
|
||||
trackInteriorFreeType(context.constraint->scope.get(), resultType);
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||
trackInteriorFreeType(context.constraint->scope.get(), resultType);
|
||||
|
||||
TypePackId clonedTypePack = arena->addTypePack({resultType});
|
||||
asMutable(context.result)->ty.emplace<BoundTypePack>(clonedTypePack);
|
||||
|
@ -1549,7 +1463,8 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
|||
static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCallContext& context)
|
||||
{
|
||||
TypeArena* arena = context.solver->arena;
|
||||
inputType = follow(inputType);
|
||||
if (FFlag::LuauFollowTableFreeze)
|
||||
inputType = follow(inputType);
|
||||
if (auto mt = get<MetatableType>(inputType))
|
||||
{
|
||||
std::optional<TypeId> frozenTable = freezeTable(mt->table, context);
|
||||
|
@ -1566,7 +1481,7 @@ static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCa
|
|||
{
|
||||
// Clone the input type, this will become our final result type after we mutate it.
|
||||
CloneState cloneState{context.solver->builtinTypes};
|
||||
TypeId resultType = shallowClone(inputType, *arena, cloneState, /* ignorePersistent */ true);
|
||||
TypeId resultType = shallowClone(inputType, *arena, cloneState, /* ignorePersistent */ FFlag::LuauFreezeIgnorePersistent);
|
||||
auto tableTy = getMutable<TableType>(resultType);
|
||||
// `clone` should not break this.
|
||||
LUAU_ASSERT(tableTy);
|
||||
|
@ -1616,17 +1531,6 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
|
|||
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
|
||||
std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
|
||||
|
||||
if (FFlag::LuauMagicFreezeCheckBlocked)
|
||||
{
|
||||
if (resultTy && !get<BlockedType>(resultTy))
|
||||
{
|
||||
// If there's an existing result type but it's _not_ blocked, then
|
||||
// we aren't type stating this builtin and should fall back to
|
||||
// regular inference.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TypeId> frozenType = freezeTable(inputType, context);
|
||||
|
||||
if (!frozenType)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/Clone.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypePack.h"
|
||||
|
@ -9,12 +8,11 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauFreezeIgnorePersistent)
|
||||
|
||||
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
|
||||
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings)
|
||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -133,7 +131,7 @@ protected:
|
|||
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
|
||||
if (auto it = types->find(ty); it != types->end())
|
||||
return it->second;
|
||||
else if (ty->persistent && ty != forceTy)
|
||||
else if (ty->persistent && (!FFlag::LuauFreezeIgnorePersistent || ty != forceTy))
|
||||
return ty;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -143,7 +141,7 @@ protected:
|
|||
tp = follow(tp);
|
||||
if (auto it = packs->find(tp); it != packs->end())
|
||||
return it->second;
|
||||
else if (tp->persistent && tp != forceTp)
|
||||
else if (tp->persistent && (!FFlag::LuauFreezeIgnorePersistent || tp != forceTp))
|
||||
return tp;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -169,7 +167,7 @@ public:
|
|||
|
||||
if (auto clone = find(ty))
|
||||
return *clone;
|
||||
else if (ty->persistent && ty != forceTy)
|
||||
else if (ty->persistent && (!FFlag::LuauFreezeIgnorePersistent || ty != forceTy))
|
||||
return ty;
|
||||
|
||||
TypeId target = arena->addType(ty->ty);
|
||||
|
@ -179,6 +177,8 @@ public:
|
|||
generic->scope = nullptr;
|
||||
else if (auto free = getMutable<FreeType>(target))
|
||||
free->scope = nullptr;
|
||||
else if (auto fn = getMutable<FunctionType>(target))
|
||||
fn->scope = nullptr;
|
||||
else if (auto table = getMutable<TableType>(target))
|
||||
table->scope = nullptr;
|
||||
|
||||
|
@ -193,7 +193,7 @@ public:
|
|||
|
||||
if (auto clone = find(tp))
|
||||
return *clone;
|
||||
else if (tp->persistent && tp != forceTp)
|
||||
else if (tp->persistent && (!FFlag::LuauFreezeIgnorePersistent || tp != forceTp))
|
||||
return tp;
|
||||
|
||||
TypePackId target = arena->addTypePack(tp->ty);
|
||||
|
@ -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);
|
||||
|
@ -395,7 +395,7 @@ private:
|
|||
ty = shallowClone(ty);
|
||||
}
|
||||
|
||||
virtual void cloneChildren(LazyType* t)
|
||||
void cloneChildren(LazyType* t)
|
||||
{
|
||||
if (auto unwrapped = t->unwrapped.load())
|
||||
t->unwrapped.store(shallowClone(unwrapped));
|
||||
|
@ -477,7 +477,7 @@ private:
|
|||
|
||||
class FragmentAutocompleteTypeCloner final : public TypeCloner
|
||||
{
|
||||
Scope* replacementForNullScope = nullptr;
|
||||
Scope* freeTypeReplacementScope = nullptr;
|
||||
|
||||
public:
|
||||
FragmentAutocompleteTypeCloner(
|
||||
|
@ -487,12 +487,12 @@ public:
|
|||
NotNull<SeenTypePacks> packs,
|
||||
TypeId forceTy,
|
||||
TypePackId forceTp,
|
||||
Scope* replacementForNullScope
|
||||
Scope* freeTypeReplacementScope
|
||||
)
|
||||
: TypeCloner(arena, builtinTypes, types, packs, forceTy, forceTp)
|
||||
, replacementForNullScope(replacementForNullScope)
|
||||
, freeTypeReplacementScope(freeTypeReplacementScope)
|
||||
{
|
||||
LUAU_ASSERT(replacementForNullScope);
|
||||
LUAU_ASSERT(freeTypeReplacementScope);
|
||||
}
|
||||
|
||||
TypeId shallowClone(TypeId ty) override
|
||||
|
@ -502,7 +502,7 @@ public:
|
|||
|
||||
if (auto clone = find(ty))
|
||||
return *clone;
|
||||
else if (ty->persistent && ty != forceTy)
|
||||
else if (ty->persistent && (!FFlag::LuauFreezeIgnorePersistent || ty != forceTy))
|
||||
return ty;
|
||||
|
||||
TypeId target = arena->addType(ty->ty);
|
||||
|
@ -512,13 +512,12 @@ public:
|
|||
generic->scope = nullptr;
|
||||
else if (auto free = getMutable<FreeType>(target))
|
||||
{
|
||||
free->scope = replacementForNullScope;
|
||||
}
|
||||
else if (auto tt = getMutable<TableType>(target))
|
||||
{
|
||||
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||
tt->scope = replacementForNullScope;
|
||||
free->scope = freeTypeReplacementScope;
|
||||
}
|
||||
else if (auto fn = getMutable<FunctionType>(target))
|
||||
fn->scope = nullptr;
|
||||
else if (auto table = getMutable<TableType>(target))
|
||||
table->scope = nullptr;
|
||||
|
||||
(*types)[ty] = target;
|
||||
queue.emplace_back(target);
|
||||
|
@ -531,7 +530,7 @@ public:
|
|||
|
||||
if (auto clone = find(tp))
|
||||
return *clone;
|
||||
else if (tp->persistent && tp != forceTp)
|
||||
else if (tp->persistent && (!FFlag::LuauFreezeIgnorePersistent || tp != forceTp))
|
||||
return tp;
|
||||
|
||||
TypePackId target = arena->addTypePack(tp->ty);
|
||||
|
@ -539,22 +538,12 @@ public:
|
|||
if (auto generic = getMutable<GenericTypePack>(target))
|
||||
generic->scope = nullptr;
|
||||
else if (auto free = getMutable<FreeTypePack>(target))
|
||||
free->scope = replacementForNullScope;
|
||||
free->scope = freeTypeReplacementScope;
|
||||
|
||||
(*packs)[tp] = target;
|
||||
queue.emplace_back(target);
|
||||
return target;
|
||||
}
|
||||
|
||||
void cloneChildren(LazyType* t) override
|
||||
{
|
||||
// Do not clone lazy types
|
||||
if (!FFlag::LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
{
|
||||
if (auto unwrapped = t->unwrapped.load())
|
||||
t->unwrapped.store(shallowClone(unwrapped));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -562,7 +551,7 @@ public:
|
|||
|
||||
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
|
||||
{
|
||||
if (tp->persistent && !ignorePersistent)
|
||||
if (tp->persistent && (!FFlag::LuauFreezeIgnorePersistent || !ignorePersistent))
|
||||
return tp;
|
||||
|
||||
TypeCloner cloner{
|
||||
|
@ -571,7 +560,7 @@ TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState,
|
|||
NotNull{&cloneState.seenTypes},
|
||||
NotNull{&cloneState.seenTypePacks},
|
||||
nullptr,
|
||||
ignorePersistent ? tp : nullptr
|
||||
FFlag::LuauFreezeIgnorePersistent && ignorePersistent ? tp : nullptr
|
||||
};
|
||||
|
||||
return cloner.shallowClone(tp);
|
||||
|
@ -579,7 +568,7 @@ TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState,
|
|||
|
||||
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
|
||||
{
|
||||
if (typeId->persistent && !ignorePersistent)
|
||||
if (typeId->persistent && (!FFlag::LuauFreezeIgnorePersistent || !ignorePersistent))
|
||||
return typeId;
|
||||
|
||||
TypeCloner cloner{
|
||||
|
@ -587,7 +576,7 @@ TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool
|
|||
cloneState.builtinTypes,
|
||||
NotNull{&cloneState.seenTypes},
|
||||
NotNull{&cloneState.seenTypePacks},
|
||||
ignorePersistent ? typeId : nullptr,
|
||||
FFlag::LuauFreezeIgnorePersistent && ignorePersistent ? typeId : nullptr,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
@ -739,7 +728,7 @@ Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cl
|
|||
b.deprecatedSuggestion = binding.deprecatedSuggestion;
|
||||
b.documentationSymbol = binding.documentationSymbol;
|
||||
b.location = binding.location;
|
||||
b.typeId = FFlag::LuauDoNotClonePersistentBindings && binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
|
||||
b.typeId = cloner.clone(binding.typeId);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -13,25 +13,32 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
bool doesCallError(const AstExprCall* call); // TypeInfer.cpp
|
||||
|
||||
struct ReferencedDefFinder : public AstVisitor
|
||||
{
|
||||
bool visit(AstExprLocal* local) override
|
||||
{
|
||||
referencedLocalDefs.push_back(local->local);
|
||||
return true;
|
||||
}
|
||||
// ast defs is just a mapping from expr -> def in general
|
||||
// will get built up by the dfg builder
|
||||
|
||||
// localDefs, we need to copy over
|
||||
std::vector<AstLocal*> referencedLocalDefs;
|
||||
};
|
||||
|
||||
struct PushScope
|
||||
{
|
||||
ScopeStack& stack;
|
||||
size_t previousSize;
|
||||
|
||||
PushScope(ScopeStack& stack, DfgScope* scope)
|
||||
: stack(stack)
|
||||
, previousSize(stack.size())
|
||||
{
|
||||
// `scope` should never be `nullptr` here.
|
||||
LUAU_ASSERT(scope);
|
||||
|
@ -40,18 +47,7 @@ struct PushScope
|
|||
|
||||
~PushScope()
|
||||
{
|
||||
if (FFlag::LuauDfgScopeStackTrueReset)
|
||||
{
|
||||
// If somehow this stack has _shrunk_ to be smaller than we expect,
|
||||
// something very strange has happened.
|
||||
LUAU_ASSERT(stack.size() > previousSize);
|
||||
while (stack.size() > previousSize)
|
||||
stack.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.pop_back();
|
||||
}
|
||||
stack.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -86,6 +82,12 @@ std::optional<DefId> DataFlowGraph::getDefOptional(const AstExpr* expr) const
|
|||
return NotNull{*def};
|
||||
}
|
||||
|
||||
std::optional<DefId> DataFlowGraph::getRValueDefForCompoundAssign(const AstExpr* expr) const
|
||||
{
|
||||
auto def = compoundAssignDefs.find(expr);
|
||||
return def ? std::optional<DefId>(*def) : std::nullopt;
|
||||
}
|
||||
|
||||
DefId DataFlowGraph::getDef(const AstLocal* local) const
|
||||
{
|
||||
auto def = localDefs.find(local);
|
||||
|
@ -199,15 +201,7 @@ DataFlowGraph DataFlowGraphBuilder::build(
|
|||
|
||||
DataFlowGraphBuilder builder(defArena, keyArena);
|
||||
builder.handle = handle;
|
||||
|
||||
DfgScope* moduleScope;
|
||||
// We're not explicitly calling makeChildScope here because that function relies on currentScope
|
||||
// which guarantees that the scope being returned is NotNull
|
||||
// This means that while the scope stack is empty, we'll have to manually initialize the global scope
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get();
|
||||
else
|
||||
moduleScope = builder.makeChildScope();
|
||||
DfgScope* moduleScope = builder.makeChildScope();
|
||||
PushScope ps{builder.scopeStack, moduleScope};
|
||||
builder.visitBlockWithoutChildScope(block);
|
||||
builder.resolveCaptures();
|
||||
|
@ -239,13 +233,7 @@ void DataFlowGraphBuilder::resolveCaptures()
|
|||
}
|
||||
}
|
||||
|
||||
NotNull<DfgScope> DataFlowGraphBuilder::currentScope()
|
||||
{
|
||||
LUAU_ASSERT(!scopeStack.empty());
|
||||
return NotNull{scopeStack.back()};
|
||||
}
|
||||
|
||||
DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED()
|
||||
DfgScope* DataFlowGraphBuilder::currentScope()
|
||||
{
|
||||
if (scopeStack.empty())
|
||||
return nullptr; // nullptr is the root DFG scope.
|
||||
|
@ -254,10 +242,7 @@ DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED()
|
|||
|
||||
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
|
||||
{
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
|
||||
else
|
||||
return scopes.emplace_back(new DfgScope{currentScope_DEPRECATED(), scopeType}).get();
|
||||
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
|
||||
}
|
||||
|
||||
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
|
||||
|
@ -332,9 +317,9 @@ void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const
|
|||
}
|
||||
}
|
||||
|
||||
DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
|
||||
DefId DataFlowGraphBuilder::lookup(Symbol symbol)
|
||||
{
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
|
||||
// true if any of the considered scopes are a loop.
|
||||
bool outsideLoopScope = false;
|
||||
|
@ -359,15 +344,15 @@ DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
|
|||
}
|
||||
}
|
||||
|
||||
DefId result = defArena->freshCell(symbol, location);
|
||||
DefId result = defArena->freshCell();
|
||||
scope->bindings[symbol] = result;
|
||||
captures[symbol].allVersions.push_back(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location location)
|
||||
DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key)
|
||||
{
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
for (DfgScope* current = scope; current; current = current->parent)
|
||||
{
|
||||
if (auto props = current->props.find(def))
|
||||
|
@ -377,7 +362,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location l
|
|||
}
|
||||
else if (auto phi = get<Phi>(def); phi && phi->operands.empty()) // Unresolved phi nodes
|
||||
{
|
||||
DefId result = defArena->freshCell(def->name, location);
|
||||
DefId result = defArena->freshCell();
|
||||
scope->props[def][key] = result;
|
||||
return result;
|
||||
}
|
||||
|
@ -387,7 +372,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location l
|
|||
{
|
||||
std::vector<DefId> defs;
|
||||
for (DefId operand : phi->operands)
|
||||
defs.push_back(lookup(operand, key, location));
|
||||
defs.push_back(lookup(operand, key));
|
||||
|
||||
DefId result = defArena->phi(defs);
|
||||
scope->props[def][key] = result;
|
||||
|
@ -395,7 +380,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location l
|
|||
}
|
||||
else if (get<Cell>(def))
|
||||
{
|
||||
DefId result = defArena->freshCell(def->name, location);
|
||||
DefId result = defArena->freshCell();
|
||||
scope->props[def][key] = result;
|
||||
return result;
|
||||
}
|
||||
|
@ -413,10 +398,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
|
|||
cf = visitBlockWithoutChildScope(b);
|
||||
}
|
||||
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->inherit(child);
|
||||
else
|
||||
currentScope_DEPRECATED()->inherit(child);
|
||||
currentScope()->inherit(child);
|
||||
return cf;
|
||||
}
|
||||
|
||||
|
@ -473,7 +455,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);
|
||||
|
@ -501,27 +483,13 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
|
|||
elsecf = visit(i->elsebody);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
DfgScope* scope = currentScope();
|
||||
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;
|
||||
|
@ -542,10 +510,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
|
|||
visit(w->body);
|
||||
}
|
||||
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->inherit(whileScope);
|
||||
else
|
||||
currentScope_DEPRECATED()->inherit(whileScope);
|
||||
currentScope()->inherit(whileScope);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
@ -561,10 +526,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
|
|||
visitExpr(r->condition);
|
||||
}
|
||||
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->inherit(repeatScope);
|
||||
else
|
||||
currentScope_DEPRECATED()->inherit(repeatScope);
|
||||
currentScope()->inherit(repeatScope);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
@ -613,7 +575,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
|
|||
// We need to create a new def to intentionally avoid alias tracking, but we'd like to
|
||||
// make sure that the non-aliased defs are also marked as a subscript for refinements.
|
||||
bool subscripted = i < defs.size() && containsSubscriptedDefinition(defs[i]);
|
||||
DefId def = defArena->freshCell(local, local->location, subscripted);
|
||||
DefId def = defArena->freshCell(subscripted);
|
||||
if (i < l->values.size)
|
||||
{
|
||||
AstExpr* e = l->values.data[i];
|
||||
|
@ -623,10 +585,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
|
|||
}
|
||||
}
|
||||
graph.localDefs[local] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[local] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[local] = def;
|
||||
currentScope()->bindings[local] = def;
|
||||
captures[local].allVersions.push_back(def);
|
||||
}
|
||||
|
||||
|
@ -648,22 +607,16 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
|||
if (f->var->annotation)
|
||||
visitType(f->var->annotation);
|
||||
|
||||
DefId def = defArena->freshCell(f->var, f->var->location);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.localDefs[f->var] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[f->var] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[f->var] = def;
|
||||
currentScope()->bindings[f->var] = def;
|
||||
captures[f->var].allVersions.push_back(def);
|
||||
|
||||
// TODO(controlflow): entry point has a back edge from exit point
|
||||
visit(f->body);
|
||||
}
|
||||
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->inherit(forScope);
|
||||
else
|
||||
currentScope_DEPRECATED()->inherit(forScope);
|
||||
currentScope()->inherit(forScope);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
@ -680,12 +633,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
|||
if (local->annotation)
|
||||
visitType(local->annotation);
|
||||
|
||||
DefId def = defArena->freshCell(local, local->location);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.localDefs[local] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[local] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[local] = def;
|
||||
currentScope()->bindings[local] = def;
|
||||
captures[local].allVersions.push_back(def);
|
||||
}
|
||||
|
||||
|
@ -696,10 +646,8 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
|||
|
||||
visit(f->body);
|
||||
}
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->inherit(forScope);
|
||||
else
|
||||
currentScope_DEPRECATED()->inherit(forScope);
|
||||
|
||||
currentScope()->inherit(forScope);
|
||||
|
||||
return ControlFlow::None;
|
||||
}
|
||||
|
@ -714,7 +662,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)
|
|||
for (size_t i = 0; i < a->vars.size; ++i)
|
||||
{
|
||||
AstExpr* v = a->vars.data[i];
|
||||
visitLValue(v, i < defs.size() ? defs[i] : defArena->freshCell(Symbol{}, v->location));
|
||||
visitLValue(v, i < defs.size() ? defs[i] : defArena->freshCell());
|
||||
}
|
||||
|
||||
return ControlFlow::None;
|
||||
|
@ -740,7 +688,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFunction* f)
|
|||
//
|
||||
// which is evidence that references to variables must be a phi node of all possible definitions,
|
||||
// but for bug compatibility, we'll assume the same thing here.
|
||||
visitLValue(f->name, defArena->freshCell(Symbol{}, f->name->location));
|
||||
visitLValue(f->name, defArena->freshCell());
|
||||
visitExpr(f->func);
|
||||
|
||||
if (auto local = f->name->as<AstExprLocal>())
|
||||
|
@ -760,12 +708,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFunction* f)
|
|||
|
||||
ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
|
||||
{
|
||||
DefId def = defArena->freshCell(l->name, l->location);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.localDefs[l->name] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[l->name] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[l->name] = def;
|
||||
currentScope()->bindings[l->name] = def;
|
||||
captures[l->name].allVersions.push_back(def);
|
||||
visitExpr(l->func);
|
||||
|
||||
|
@ -796,12 +741,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatTypeFunction* f)
|
|||
|
||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
|
||||
{
|
||||
DefId def = defArena->freshCell(d->name, d->nameLocation);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.declaredDefs[d] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[d->name] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[d->name] = def;
|
||||
currentScope()->bindings[d->name] = def;
|
||||
captures[d->name].allVersions.push_back(def);
|
||||
|
||||
visitType(d->type);
|
||||
|
@ -811,12 +753,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
|
|||
|
||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
||||
{
|
||||
DefId def = defArena->freshCell(d->name, d->nameLocation);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.declaredDefs[d] = def;
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->bindings[d->name] = def;
|
||||
else
|
||||
currentScope_DEPRECATED()->bindings[d->name] = def;
|
||||
currentScope()->bindings[d->name] = def;
|
||||
captures[d->name].allVersions.push_back(def);
|
||||
|
||||
DfgScope* unreachable = makeChildScope();
|
||||
|
@ -825,15 +764,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 +777,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;
|
||||
|
@ -874,19 +810,19 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExpr* e)
|
|||
if (auto g = e->as<AstExprGroup>())
|
||||
return visitExpr(g);
|
||||
else if (auto c = e->as<AstExprConstantNil>())
|
||||
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
|
||||
return {defArena->freshCell(), nullptr}; // ok
|
||||
else if (auto c = e->as<AstExprConstantBool>())
|
||||
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
|
||||
return {defArena->freshCell(), nullptr}; // ok
|
||||
else if (auto c = e->as<AstExprConstantNumber>())
|
||||
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
|
||||
return {defArena->freshCell(), nullptr}; // ok
|
||||
else if (auto c = e->as<AstExprConstantString>())
|
||||
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
|
||||
return {defArena->freshCell(), nullptr}; // ok
|
||||
else if (auto l = e->as<AstExprLocal>())
|
||||
return visitExpr(l);
|
||||
else if (auto g = e->as<AstExprGlobal>())
|
||||
return visitExpr(g);
|
||||
else if (auto v = e->as<AstExprVarargs>())
|
||||
return {defArena->freshCell(Symbol{}, v->location), nullptr}; // ok
|
||||
return {defArena->freshCell(), nullptr}; // ok
|
||||
else if (auto c = e->as<AstExprCall>())
|
||||
return visitExpr(c);
|
||||
else if (auto i = e->as<AstExprIndexName>())
|
||||
|
@ -927,14 +863,14 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGroup* group)
|
|||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprLocal* l)
|
||||
{
|
||||
DefId def = lookup(l->local, l->local->location);
|
||||
DefId def = lookup(l->local);
|
||||
const RefinementKey* key = keyArena->leaf(def);
|
||||
return {def, key};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGlobal* g)
|
||||
{
|
||||
DefId def = lookup(g->name, g->location);
|
||||
DefId def = lookup(g->name);
|
||||
return {def, keyArena->leaf(def)};
|
||||
}
|
||||
|
||||
|
@ -942,12 +878,6 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
|||
{
|
||||
visitExpr(c->func);
|
||||
|
||||
if (FFlag::LuauPreprocessTypestatedArgument)
|
||||
{
|
||||
for (AstExpr* arg : c->args)
|
||||
visitExpr(arg);
|
||||
}
|
||||
|
||||
if (shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
|
||||
{
|
||||
AstExpr* firstArg = *c->args.begin();
|
||||
|
@ -978,11 +908,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
|||
visitLValue(firstArg, def);
|
||||
}
|
||||
|
||||
if (!FFlag::LuauPreprocessTypestatedArgument)
|
||||
{
|
||||
for (AstExpr* arg : c->args)
|
||||
visitExpr(arg);
|
||||
}
|
||||
for (AstExpr* arg : c->args)
|
||||
visitExpr(arg);
|
||||
|
||||
// We treat function calls as "subscripted" as they could potentially
|
||||
// return a subscripted value, consider:
|
||||
|
@ -994,7 +921,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
|||
// local v = foo({})
|
||||
//
|
||||
// We want to consider `v` to be subscripted here.
|
||||
return {defArena->freshCell(Symbol{}, c->location, /*subscripted=*/true)};
|
||||
return {defArena->freshCell(/*subscripted=*/true)};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexName* i)
|
||||
|
@ -1002,7 +929,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexName* i)
|
|||
auto [parentDef, parentKey] = visitExpr(i->expr);
|
||||
std::string index = i->index.value;
|
||||
|
||||
DefId def = lookup(parentDef, index, i->location);
|
||||
DefId def = lookup(parentDef, index);
|
||||
return {def, keyArena->node(parentKey, def, index)};
|
||||
}
|
||||
|
||||
|
@ -1015,11 +942,11 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexExpr* i)
|
|||
{
|
||||
std::string index{string->value.data, string->value.size};
|
||||
|
||||
DefId def = lookup(parentDef, index, i->location);
|
||||
DefId def = lookup(parentDef, index);
|
||||
return {def, keyArena->node(parentKey, def, index)};
|
||||
}
|
||||
|
||||
return {defArena->freshCell(Symbol{}, i->location, /* subscripted= */ true), nullptr};
|
||||
return {defArena->freshCell(/* subscripted= */ true), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||
|
@ -1032,7 +959,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
// There's no syntax for `self` to have an annotation if using `function t:m()`
|
||||
LUAU_ASSERT(!self->annotation);
|
||||
|
||||
DefId def = defArena->freshCell(f->debugname, f->location);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.localDefs[self] = def;
|
||||
signatureScope->bindings[self] = def;
|
||||
captures[self].allVersions.push_back(def);
|
||||
|
@ -1043,7 +970,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
if (param->annotation)
|
||||
visitType(param->annotation);
|
||||
|
||||
DefId def = defArena->freshCell(param, param->location);
|
||||
DefId def = defArena->freshCell();
|
||||
graph.localDefs[param] = def;
|
||||
signatureScope->bindings[param] = def;
|
||||
captures[param].allVersions.push_back(def);
|
||||
|
@ -1052,16 +979,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
if (f->varargAnnotation)
|
||||
visitTypePack(f->varargAnnotation);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (f->returnAnnotation)
|
||||
visitTypePack(f->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f->returnAnnotation_DEPRECATED)
|
||||
visitTypeList(*f->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (f->returnAnnotation)
|
||||
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
|
||||
|
@ -1073,16 +992,13 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
|||
// g() --> 5
|
||||
visit(f->body);
|
||||
|
||||
return {defArena->freshCell(f->debugname, f->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
|
||||
{
|
||||
DefId tableCell = defArena->freshCell(Symbol{}, t->location);
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->props[tableCell] = {};
|
||||
else
|
||||
currentScope_DEPRECATED()->props[tableCell] = {};
|
||||
DefId tableCell = defArena->freshCell();
|
||||
currentScope()->props[tableCell] = {};
|
||||
for (AstExprTable::Item item : t->items)
|
||||
{
|
||||
DataFlowResult result = visitExpr(item.value);
|
||||
|
@ -1090,12 +1006,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
|
|||
{
|
||||
visitExpr(item.key);
|
||||
if (auto string = item.key->as<AstExprConstantString>())
|
||||
{
|
||||
if (FFlag::LuauDfgScopeStackNotNull)
|
||||
currentScope()->props[tableCell][string->value.data] = result.def;
|
||||
else
|
||||
currentScope_DEPRECATED()->props[tableCell][string->value.data] = result.def;
|
||||
}
|
||||
currentScope()->props[tableCell][string->value.data] = result.def;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1106,7 +1017,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u)
|
|||
{
|
||||
visitExpr(u->expr);
|
||||
|
||||
return {defArena->freshCell(Symbol{}, u->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
||||
|
@ -1114,7 +1025,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
|||
visitExpr(b->left);
|
||||
visitExpr(b->right);
|
||||
|
||||
return {defArena->freshCell(Symbol{}, b->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
||||
|
@ -1131,7 +1042,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
|
|||
visitExpr(i->trueExpr);
|
||||
visitExpr(i->falseExpr);
|
||||
|
||||
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
|
||||
|
@ -1139,7 +1050,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
|
|||
for (AstExpr* e : i->expressions)
|
||||
visitExpr(e);
|
||||
|
||||
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
|
||||
|
@ -1150,7 +1061,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
|
|||
for (AstExpr* e : error->expressions)
|
||||
visitExpr(e);
|
||||
|
||||
return {defArena->freshCell(Symbol{}, error->location), nullptr};
|
||||
return {defArena->freshCell(), nullptr};
|
||||
}
|
||||
|
||||
void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
|
||||
|
@ -1176,12 +1087,12 @@ void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
|
|||
|
||||
DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
|
||||
{
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
|
||||
// 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));
|
||||
DefId updated = defArena->freshCell(containsSubscriptedDefinition(incomingDef));
|
||||
scope->bindings[l->local] = updated;
|
||||
captures[l->local].allVersions.push_back(updated);
|
||||
return updated;
|
||||
|
@ -1192,12 +1103,12 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
|
|||
|
||||
DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, DefId incomingDef)
|
||||
{
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
|
||||
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
||||
if (scope->canUpdateDefinition(g->name))
|
||||
{
|
||||
DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef));
|
||||
DefId updated = defArena->freshCell(containsSubscriptedDefinition(incomingDef));
|
||||
scope->bindings[g->name] = updated;
|
||||
captures[g->name].allVersions.push_back(updated);
|
||||
return updated;
|
||||
|
@ -1210,10 +1121,10 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef)
|
|||
{
|
||||
DefId parentDef = visitExpr(i->expr).def;
|
||||
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
if (scope->canUpdateDefinition(parentDef, i->index.value))
|
||||
{
|
||||
DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef));
|
||||
DefId updated = defArena->freshCell(containsSubscriptedDefinition(incomingDef));
|
||||
scope->props[parentDef][i->index.value] = updated;
|
||||
return updated;
|
||||
}
|
||||
|
@ -1226,12 +1137,12 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
|
|||
DefId parentDef = visitExpr(i->expr).def;
|
||||
visitExpr(i->index);
|
||||
|
||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||
DfgScope* scope = currentScope();
|
||||
if (auto string = i->index->as<AstExprConstantString>())
|
||||
{
|
||||
if (scope->canUpdateDefinition(parentDef, string->value.data))
|
||||
{
|
||||
DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef));
|
||||
DefId updated = defArena->freshCell(containsSubscriptedDefinition(incomingDef));
|
||||
scope->props[parentDef][string->value.data] = updated;
|
||||
return updated;
|
||||
}
|
||||
|
@ -1239,7 +1150,7 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
|
|||
return visitExpr(static_cast<AstExpr*>(i)).def;
|
||||
}
|
||||
else
|
||||
return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true);
|
||||
return defArena->freshCell(/*subscripted=*/true);
|
||||
}
|
||||
|
||||
DefId DataFlowGraphBuilder::visitLValue(AstExprError* error, DefId incomingDef)
|
||||
|
@ -1257,8 +1168,6 @@ void DataFlowGraphBuilder::visitType(AstType* t)
|
|||
return visitType(f);
|
||||
else if (auto tyof = t->as<AstTypeTypeof>())
|
||||
return visitType(tyof);
|
||||
else if (auto o = t->as<AstTypeOptional>())
|
||||
return;
|
||||
else if (auto u = t->as<AstTypeUnion>())
|
||||
return visitType(u);
|
||||
else if (auto i = t->as<AstTypeIntersection>())
|
||||
|
@ -1303,10 +1212,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)
|
||||
|
|
|
@ -36,9 +36,9 @@ void collectOperands(DefId def, std::vector<DefId>* operands)
|
|||
}
|
||||
}
|
||||
|
||||
DefId DefArena::freshCell(Symbol sym, Location location, bool subscripted)
|
||||
DefId DefArena::freshCell(bool subscripted)
|
||||
{
|
||||
return NotNull{allocator.allocate(Def{Cell{subscripted}, sym, location})};
|
||||
return NotNull{allocator.allocate(Def{Cell{subscripted}})};
|
||||
}
|
||||
|
||||
DefId DefArena::phi(DefId a, DefId b)
|
||||
|
|
|
@ -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,8 +1,7 @@
|
|||
// 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)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -216,6 +215,15 @@ declare debug: {
|
|||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionDebugSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||
|
||||
declare debug: {
|
||||
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
||||
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
||||
|
||||
declare utf8: {
|
||||
|
@ -262,37 +270,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
|
||||
|
@ -331,7 +309,7 @@ std::string getBuiltinDefinitionSource()
|
|||
result += kBuiltinDefinitionOsSrc;
|
||||
result += kBuiltinDefinitionCoroutineSrc;
|
||||
result += kBuiltinDefinitionTableSrc;
|
||||
result += kBuiltinDefinitionDebugSrc;
|
||||
result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED;
|
||||
result += kBuiltinDefinitionUtf8Src;
|
||||
result += kBuiltinDefinitionBufferSrc;
|
||||
result += kBuiltinDefinitionVectorSrc;
|
||||
|
@ -339,119 +317,4 @@ std::string getBuiltinDefinitionSource()
|
|||
return result;
|
||||
}
|
||||
|
||||
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
||||
static const std::string kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
||||
|
||||
export type type = {
|
||||
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
||||
"singleton" | "negation" | "union" | "intersection" | "table" | "function" | "class" | "generic",
|
||||
|
||||
is: (self: type, arg: string) -> boolean,
|
||||
|
||||
-- for singleton type
|
||||
value: (self: type) -> (string | boolean | nil),
|
||||
|
||||
-- for negation type
|
||||
inner: (self: type) -> type,
|
||||
|
||||
-- for union and intersection types
|
||||
components: (self: type) -> {type},
|
||||
|
||||
-- for table type
|
||||
setproperty: (self: type, key: type, value: type?) -> (),
|
||||
setreadproperty: (self: type, key: type, value: type?) -> (),
|
||||
setwriteproperty: (self: type, key: type, value: type?) -> (),
|
||||
readproperty: (self: type, key: type) -> type?,
|
||||
writeproperty: (self: type, key: type) -> type?,
|
||||
properties: (self: type) -> { [type]: { read: type?, write: type? } },
|
||||
setindexer: (self: type, index: type, result: type) -> (),
|
||||
setreadindexer: (self: type, index: type, result: type) -> (),
|
||||
setwriteindexer: (self: type, index: type, result: type) -> (),
|
||||
indexer: (self: type) -> { index: type, readresult: type, writeresult: type }?,
|
||||
readindexer: (self: type) -> { index: type, result: type }?,
|
||||
writeindexer: (self: type) -> { index: type, result: type }?,
|
||||
setmetatable: (self: type, arg: type) -> (),
|
||||
metatable: (self: type) -> type?,
|
||||
|
||||
-- for function type
|
||||
setparameters: (self: type, head: {type}?, tail: type?) -> (),
|
||||
parameters: (self: type) -> { head: {type}?, tail: type? },
|
||||
setreturns: (self: type, head: {type}?, tail: type? ) -> (),
|
||||
returns: (self: type) -> { head: {type}?, tail: type? },
|
||||
setgenerics: (self: type, {type}?) -> (),
|
||||
generics: (self: type) -> {type},
|
||||
|
||||
-- for class type
|
||||
-- 'properties', 'metatable', 'indexer', 'readindexer' and 'writeindexer' are shared with table type
|
||||
readparent: (self: type) -> type?,
|
||||
writeparent: (self: type) -> type?,
|
||||
|
||||
-- for generic type
|
||||
name: (self: type) -> string?,
|
||||
ispack: (self: type) -> boolean,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTypesLibSrc = 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,
|
||||
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";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // 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;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
|
||||
#include <optional>
|
||||
|
@ -18,7 +17,6 @@
|
|||
#include <unordered_set>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
|
||||
static std::string wrongNumberOfArgsString(
|
||||
size_t expectedCount,
|
||||
|
@ -70,7 +68,7 @@ namespace Luau
|
|||
{
|
||||
|
||||
// this list of binary operator type functions is used for better stringification of type functions errors
|
||||
static const std::unordered_map<std::string, const char*> DEPRECATED_kBinaryOps{
|
||||
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||
{"add", "+"},
|
||||
{"sub", "-"},
|
||||
{"mul", "*"},
|
||||
|
@ -86,27 +84,12 @@ static const std::unordered_map<std::string, const char*> DEPRECATED_kBinaryOps{
|
|||
{"eq", "== or ~="}
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||
{"add", "+"},
|
||||
{"sub", "-"},
|
||||
{"mul", "*"},
|
||||
{"div", "/"},
|
||||
{"idiv", "//"},
|
||||
{"pow", "^"},
|
||||
{"mod", "%"},
|
||||
{"concat", ".."},
|
||||
{"lt", "< or >="},
|
||||
{"le", "<= or >"},
|
||||
{"eq", "== or ~="}
|
||||
};
|
||||
|
||||
// this list of unary operator type functions is used for better stringification of type functions errors
|
||||
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};
|
||||
|
||||
// this list of type functions will receive a special error indicating that the user should file a bug on the GitHub repository
|
||||
// putting a type function in this list indicates that it is expected to _always_ reduce
|
||||
static const std::unordered_set<std::string> DEPRECATED_kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
||||
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect", "and", "or"};
|
||||
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
||||
|
||||
struct ErrorConverter
|
||||
{
|
||||
|
@ -133,10 +116,7 @@ struct ErrorConverter
|
|||
size_t luauIndentTypeMismatchMaxTypeLength = size_t(FInt::LuauIndentTypeMismatchMaxTypeLength);
|
||||
if (givenType.length() <= luauIndentTypeMismatchMaxTypeLength || wantedType.length() <= luauIndentTypeMismatchMaxTypeLength)
|
||||
return "Type " + given + " could not be converted into " + wanted;
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
return "Type\n\t" + given + "\ncould not be converted into\n\t" + wanted;
|
||||
else
|
||||
return "Type\n " + given + "\ncould not be converted into\n " + wanted;
|
||||
return "Type\n " + given + "\ncould not be converted into\n " + wanted;
|
||||
};
|
||||
|
||||
if (givenTypeName == wantedTypeName)
|
||||
|
@ -203,7 +183,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 +351,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 +382,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 +591,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";
|
||||
}
|
||||
|
@ -621,7 +601,7 @@ struct ErrorConverter
|
|||
auto tfit = get<TypeFunctionInstanceType>(e.ty);
|
||||
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type function.
|
||||
if (!tfit)
|
||||
return "Internal error: Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
|
||||
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
|
||||
|
||||
// unary operators
|
||||
if (auto unaryString = kUnaryOps.find(tfit->function->name); unaryString != kUnaryOps.end())
|
||||
|
@ -658,8 +638,7 @@ struct ErrorConverter
|
|||
}
|
||||
|
||||
// binary operators
|
||||
const auto binaryOps = FFlag::DebugLuauGreedyGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps;
|
||||
if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end())
|
||||
if (auto binaryString = kBinaryOps.find(tfit->function->name); binaryString != kBinaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||
|
||||
|
@ -713,10 +692,10 @@ struct ErrorConverter
|
|||
"'";
|
||||
}
|
||||
|
||||
if ((FFlag::DebugLuauGreedyGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
|
||||
if (kUnreachableTypeFunctions.count(tfit->function->name))
|
||||
{
|
||||
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
}
|
||||
|
||||
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
|
||||
|
@ -772,15 +751,8 @@ struct ErrorConverter
|
|||
|
||||
std::string operator()(const NonStrictFunctionDefinitionError& e) const
|
||||
{
|
||||
if (e.functionName.empty())
|
||||
{
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' is used in a way that will run time error";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' in function '" + e.functionName +
|
||||
"' is used in a way that will run time error";
|
||||
}
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' in function '" + e.functionName +
|
||||
"' is used in a way that will run time error";
|
||||
}
|
||||
|
||||
std::string operator()(const PropertyAccessViolation& e) const
|
||||
|
@ -819,11 +791,6 @@ struct ErrorConverter
|
|||
return e.message;
|
||||
}
|
||||
|
||||
std::string operator()(const ReservedIdentifier& e) const
|
||||
{
|
||||
return e.name + " cannot be used as an identifier for a type function or alias";
|
||||
}
|
||||
|
||||
std::string operator()(const CannotAssignToNever& e) const
|
||||
{
|
||||
std::string result = "Cannot assign a value of type " + toString(e.rhsType) + " to a field of type never";
|
||||
|
@ -1149,7 +1116,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;
|
||||
}
|
||||
|
@ -1211,11 +1178,6 @@ bool UserDefinedTypeFunctionError::operator==(const UserDefinedTypeFunctionError
|
|||
return message == rhs.message;
|
||||
}
|
||||
|
||||
bool ReservedIdentifier::operator==(const ReservedIdentifier& rhs) const
|
||||
{
|
||||
return name == rhs.name;
|
||||
}
|
||||
|
||||
bool CannotAssignToNever::operator==(const CannotAssignToNever& rhs) const
|
||||
{
|
||||
if (cause.size() != rhs.cause.size())
|
||||
|
@ -1391,7 +1353,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);
|
||||
|
@ -1435,9 +1397,6 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
for (auto& ty : e.cause)
|
||||
ty = clone(ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||
{
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||
}
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/FileResolver.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static std::optional<RequireSuggestions> processRequireSuggestions(std::optional<RequireSuggestions> suggestions)
|
||||
{
|
||||
if (!suggestions)
|
||||
return suggestions;
|
||||
|
||||
for (RequireSuggestion& suggestion : *suggestions)
|
||||
{
|
||||
suggestion.fullPath = escape(suggestion.fullPath);
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsFromAliases(std::vector<RequireAlias> aliases)
|
||||
{
|
||||
RequireSuggestions result;
|
||||
for (RequireAlias& alias : aliases)
|
||||
{
|
||||
RequireSuggestion suggestion;
|
||||
suggestion.label = "@" + std::move(alias.alias);
|
||||
suggestion.fullPath = suggestion.label;
|
||||
suggestion.tags = std::move(alias.tags);
|
||||
result.push_back(std::move(suggestion));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsForFirstComponent(std::unique_ptr<RequireNode> node)
|
||||
{
|
||||
RequireSuggestions result = makeSuggestionsFromAliases(node->getAvailableAliases());
|
||||
result.push_back(RequireSuggestion{"./", "./", {}});
|
||||
result.push_back(RequireSuggestion{"../", "../", {}});
|
||||
return result;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsFromNode(std::unique_ptr<RequireNode> node, const std::string_view path, bool isPartialPath)
|
||||
{
|
||||
LUAU_ASSERT(!path.empty());
|
||||
|
||||
RequireSuggestions result;
|
||||
|
||||
const size_t lastSlashInPath = path.find_last_of('/');
|
||||
|
||||
if (lastSlashInPath != std::string_view::npos)
|
||||
{
|
||||
// Add a suggestion for the parent directory
|
||||
RequireSuggestion parentSuggestion;
|
||||
parentSuggestion.label = "..";
|
||||
|
||||
// TODO: after exposing require-by-string's path normalization API, this
|
||||
// if-else can be replaced. Instead, we can simply normalize the result
|
||||
// of inserting ".." at the end of the current path.
|
||||
if (lastSlashInPath >= 2 && path.substr(lastSlashInPath - 2, 3) == "../")
|
||||
{
|
||||
parentSuggestion.fullPath = path.substr(0, lastSlashInPath + 1);
|
||||
parentSuggestion.fullPath += "..";
|
||||
}
|
||||
else
|
||||
{
|
||||
parentSuggestion.fullPath = path.substr(0, lastSlashInPath);
|
||||
}
|
||||
|
||||
result.push_back(std::move(parentSuggestion));
|
||||
}
|
||||
|
||||
std::string fullPathPrefix;
|
||||
if (isPartialPath)
|
||||
{
|
||||
// ./path/to/chi -> ./path/to/
|
||||
fullPathPrefix += path.substr(0, lastSlashInPath + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.back() == '/')
|
||||
{
|
||||
// ./path/to/ -> ./path/to/
|
||||
fullPathPrefix += path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ./path/to -> ./path/to/
|
||||
fullPathPrefix += path;
|
||||
fullPathPrefix += "/";
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::unique_ptr<RequireNode>& child : node->getChildren())
|
||||
{
|
||||
if (!child)
|
||||
continue;
|
||||
|
||||
std::string pathComponent = child->getPathComponent();
|
||||
|
||||
// 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();
|
||||
suggestion.fullPath = fullPathPrefix + std::move(pathComponent);
|
||||
suggestion.tags = child->getTags();
|
||||
result.push_back(std::move(suggestion));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestionsImpl(const ModuleName& requirer, const std::optional<std::string>& path)
|
||||
const
|
||||
{
|
||||
if (!path)
|
||||
return std::nullopt;
|
||||
|
||||
std::unique_ptr<RequireNode> requirerNode = getNode(requirer);
|
||||
if (!requirerNode)
|
||||
return std::nullopt;
|
||||
|
||||
const size_t slashPos = path->find_last_of('/');
|
||||
|
||||
if (slashPos == std::string::npos)
|
||||
return makeSuggestionsForFirstComponent(std::move(requirerNode));
|
||||
|
||||
// If path already points at a Node, return the Node's children as paths.
|
||||
if (std::unique_ptr<RequireNode> node = requirerNode->resolvePathToNode(*path))
|
||||
return makeSuggestionsFromNode(std::move(node), *path, /* isPartialPath = */ false);
|
||||
|
||||
// Otherwise, recover a partial path and use this to generate suggestions.
|
||||
if (std::unique_ptr<RequireNode> partialNode = requirerNode->resolvePathToNode(path->substr(0, slashPos)))
|
||||
return makeSuggestionsFromNode(std::move(partialNode), *path, /* isPartialPath = */ true);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
||||
{
|
||||
return processRequireSuggestions(getRequireSuggestionsImpl(requirer, path));
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> FileResolver::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
||||
{
|
||||
return requireSuggester ? requireSuggester->getRequireSuggestions(requirer, path) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/Frontend.h"
|
||||
|
||||
#include "Luau/AnyTypeSummary.h"
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
|
@ -39,15 +40,21 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
|||
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)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauModuleHoldsAstRoot)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterReverseDependencyTracking)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixMultithreadTypecheck)
|
||||
|
||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -129,9 +136,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;
|
||||
}
|
||||
|
@ -455,6 +462,20 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
|
|||
|
||||
if (item.name == name)
|
||||
checkResult.lintResult = item.module->lintResult;
|
||||
|
||||
if (FFlag::StudioReportLuauAny2 && item.options.retainFullTypeGraphs)
|
||||
{
|
||||
if (item.module)
|
||||
{
|
||||
const SourceModule& sourceModule = *item.sourceModule;
|
||||
if (sourceModule.mode == Luau::Mode::Strict)
|
||||
{
|
||||
item.module->ats.root = toString(sourceModule.root);
|
||||
}
|
||||
item.module->ats.rootSrc = sourceModule.root;
|
||||
item.module->ats.traverse(item.module.get(), sourceModule.root, NotNull{&builtinTypes_});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkResult;
|
||||
|
@ -476,6 +497,11 @@ std::vector<ModuleName> Frontend::checkQueuedModules(
|
|||
std::function<bool(size_t done, size_t total)> progress
|
||||
)
|
||||
{
|
||||
if (!FFlag::LuauFixMultithreadTypecheck)
|
||||
{
|
||||
return checkQueuedModules_DEPRECATED(optionOverride, executeTask, progress);
|
||||
}
|
||||
|
||||
FrontendOptions frontendOptions = optionOverride.value_or(options);
|
||||
if (FFlag::LuauSolverV2)
|
||||
frontendOptions.forAutocomplete = false;
|
||||
|
@ -660,6 +686,247 @@ std::vector<ModuleName> Frontend::checkQueuedModules(
|
|||
return checkedModules;
|
||||
}
|
||||
|
||||
std::vector<ModuleName> Frontend::checkQueuedModules_DEPRECATED(
|
||||
std::optional<FrontendOptions> optionOverride,
|
||||
std::function<void(std::function<void()> task)> executeTask,
|
||||
std::function<bool(size_t done, size_t total)> progress
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauFixMultithreadTypecheck);
|
||||
|
||||
FrontendOptions frontendOptions = optionOverride.value_or(options);
|
||||
if (FFlag::LuauSolverV2)
|
||||
frontendOptions.forAutocomplete = false;
|
||||
|
||||
// By taking data into locals, we make sure queue is cleared at the end, even if an ICE or a different exception is thrown
|
||||
std::vector<ModuleName> currModuleQueue;
|
||||
std::swap(currModuleQueue, moduleQueue);
|
||||
|
||||
DenseHashSet<Luau::ModuleName> seen{{}};
|
||||
std::vector<BuildQueueItem> buildQueueItems;
|
||||
|
||||
for (const ModuleName& name : currModuleQueue)
|
||||
{
|
||||
if (seen.contains(name))
|
||||
continue;
|
||||
|
||||
if (!isDirty(name, frontendOptions.forAutocomplete))
|
||||
{
|
||||
seen.insert(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<ModuleName> queue;
|
||||
bool cycleDetected = parseGraph(
|
||||
queue,
|
||||
name,
|
||||
frontendOptions.forAutocomplete,
|
||||
[&seen](const ModuleName& name)
|
||||
{
|
||||
return seen.contains(name);
|
||||
}
|
||||
);
|
||||
|
||||
addBuildQueueItems(buildQueueItems, queue, cycleDetected, seen, frontendOptions);
|
||||
}
|
||||
|
||||
if (buildQueueItems.empty())
|
||||
return {};
|
||||
|
||||
// We need a mapping from modules to build queue slots
|
||||
std::unordered_map<ModuleName, size_t> moduleNameToQueue;
|
||||
|
||||
for (size_t i = 0; i < buildQueueItems.size(); i++)
|
||||
{
|
||||
BuildQueueItem& item = buildQueueItems[i];
|
||||
moduleNameToQueue[item.name] = i;
|
||||
}
|
||||
|
||||
// Default task execution is single-threaded and immediate
|
||||
if (!executeTask)
|
||||
{
|
||||
executeTask = [](std::function<void()> task)
|
||||
{
|
||||
task();
|
||||
};
|
||||
}
|
||||
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
std::vector<size_t> readyQueueItems;
|
||||
|
||||
size_t processing = 0;
|
||||
size_t remaining = buildQueueItems.size();
|
||||
|
||||
auto itemTask = [&](size_t i)
|
||||
{
|
||||
BuildQueueItem& item = buildQueueItems[i];
|
||||
|
||||
try
|
||||
{
|
||||
checkBuildQueueItem(item);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
item.exception = std::current_exception();
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock guard(mtx);
|
||||
readyQueueItems.push_back(i);
|
||||
}
|
||||
|
||||
cv.notify_one();
|
||||
};
|
||||
|
||||
auto sendItemTask = [&](size_t i)
|
||||
{
|
||||
BuildQueueItem& item = buildQueueItems[i];
|
||||
|
||||
item.processing = true;
|
||||
processing++;
|
||||
|
||||
executeTask(
|
||||
[&itemTask, i]()
|
||||
{
|
||||
itemTask(i);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
auto sendCycleItemTask = [&]
|
||||
{
|
||||
for (size_t i = 0; i < buildQueueItems.size(); i++)
|
||||
{
|
||||
BuildQueueItem& item = buildQueueItems[i];
|
||||
|
||||
if (!item.processing)
|
||||
{
|
||||
sendItemTask(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// In a first pass, check modules that have no dependencies and record info of those modules that wait
|
||||
for (size_t i = 0; i < buildQueueItems.size(); i++)
|
||||
{
|
||||
BuildQueueItem& item = buildQueueItems[i];
|
||||
|
||||
for (const ModuleName& dep : item.sourceNode->requireSet)
|
||||
{
|
||||
if (auto it = sourceNodes.find(dep); it != sourceNodes.end())
|
||||
{
|
||||
if (it->second->hasDirtyModule(frontendOptions.forAutocomplete))
|
||||
{
|
||||
item.dirtyDependencies++;
|
||||
|
||||
buildQueueItems[moduleNameToQueue[dep]].reverseDeps.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.dirtyDependencies == 0)
|
||||
sendItemTask(i);
|
||||
}
|
||||
|
||||
// Not a single item was found, a cycle in the graph was hit
|
||||
if (processing == 0)
|
||||
sendCycleItemTask();
|
||||
|
||||
std::vector<size_t> nextItems;
|
||||
std::optional<size_t> itemWithException;
|
||||
bool cancelled = false;
|
||||
|
||||
while (remaining != 0)
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(mtx);
|
||||
|
||||
// If nothing is ready yet, wait
|
||||
cv.wait(
|
||||
guard,
|
||||
[&readyQueueItems]
|
||||
{
|
||||
return !readyQueueItems.empty();
|
||||
}
|
||||
);
|
||||
|
||||
// Handle checked items
|
||||
for (size_t i : readyQueueItems)
|
||||
{
|
||||
const BuildQueueItem& item = buildQueueItems[i];
|
||||
|
||||
// If exception was thrown, stop adding new items and wait for processing items to complete
|
||||
if (item.exception)
|
||||
itemWithException = i;
|
||||
|
||||
if (item.module && item.module->cancelled)
|
||||
cancelled = true;
|
||||
|
||||
if (itemWithException || cancelled)
|
||||
break;
|
||||
|
||||
recordItemResult(item);
|
||||
|
||||
// Notify items that were waiting for this dependency
|
||||
for (size_t reverseDep : item.reverseDeps)
|
||||
{
|
||||
BuildQueueItem& reverseDepItem = buildQueueItems[reverseDep];
|
||||
|
||||
LUAU_ASSERT(reverseDepItem.dirtyDependencies != 0);
|
||||
reverseDepItem.dirtyDependencies--;
|
||||
|
||||
// In case of a module cycle earlier, check if unlocked an item that was already processed
|
||||
if (!reverseDepItem.processing && reverseDepItem.dirtyDependencies == 0)
|
||||
nextItems.push_back(reverseDep);
|
||||
}
|
||||
}
|
||||
|
||||
LUAU_ASSERT(processing >= readyQueueItems.size());
|
||||
processing -= readyQueueItems.size();
|
||||
|
||||
LUAU_ASSERT(remaining >= readyQueueItems.size());
|
||||
remaining -= readyQueueItems.size();
|
||||
readyQueueItems.clear();
|
||||
}
|
||||
|
||||
if (progress)
|
||||
{
|
||||
if (!progress(buildQueueItems.size() - remaining, buildQueueItems.size()))
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
// Items cannot be submitted while holding the lock
|
||||
for (size_t i : nextItems)
|
||||
sendItemTask(i);
|
||||
nextItems.clear();
|
||||
|
||||
if (processing == 0)
|
||||
{
|
||||
// Typechecking might have been cancelled by user, don't return partial results
|
||||
if (cancelled)
|
||||
return {};
|
||||
|
||||
// We might have stopped because of a pending exception
|
||||
if (itemWithException)
|
||||
recordItemResult(buildQueueItems[*itemWithException]);
|
||||
}
|
||||
|
||||
// If we aren't done, but don't have anything processing, we hit a cycle
|
||||
if (remaining != 0 && processing == 0)
|
||||
sendCycleItemTask();
|
||||
}
|
||||
|
||||
std::vector<ModuleName> checkedModules;
|
||||
checkedModules.reserve(buildQueueItems.size());
|
||||
|
||||
for (size_t i = 0; i < buildQueueItems.size(); i++)
|
||||
checkedModules.push_back(std::move(buildQueueItems[i].name));
|
||||
|
||||
return checkedModules;
|
||||
}
|
||||
|
||||
std::optional<CheckResult> Frontend::getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -768,11 +1035,14 @@ bool Frontend::parseGraph(
|
|||
|
||||
buildQueue.push_back(top->name);
|
||||
|
||||
// at this point we know all valid dependencies are processed into SourceNodes
|
||||
for (const ModuleName& dep : top->requireSet)
|
||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||
{
|
||||
if (auto it = sourceNodes.find(dep); it != sourceNodes.end())
|
||||
it->second->dependents.insert(top->name);
|
||||
// at this point we know all valid dependencies are processed into SourceNodes
|
||||
for (const ModuleName& dep : top->requireSet)
|
||||
{
|
||||
if (auto it = sourceNodes.find(dep); it != sourceNodes.end())
|
||||
it->second->dependents.insert(top->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -951,7 +1221,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
item.stats.timeCheck += duration;
|
||||
item.stats.filesStrict += 1;
|
||||
|
||||
if (item.options.customModuleCheck)
|
||||
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *moduleForAutocomplete);
|
||||
|
||||
item.module = moduleForAutocomplete;
|
||||
|
@ -971,7 +1241,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
item.stats.filesStrict += mode == Mode::Strict;
|
||||
item.stats.filesNonstrict += mode == Mode::Nonstrict;
|
||||
|
||||
if (item.options.customModuleCheck)
|
||||
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *module);
|
||||
|
||||
if (FFlag::LuauSolverV2 && mode == Mode::NoCheck)
|
||||
|
@ -1003,8 +1273,11 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
freeze(module->interfaceTypes);
|
||||
|
||||
module->internalTypes.clear();
|
||||
module->defArena.allocator.clear();
|
||||
module->keyArena.allocator.clear();
|
||||
if (FFlag::LuauSelectivelyRetainDFGArena)
|
||||
{
|
||||
module->defArena.allocator.clear();
|
||||
module->keyArena.allocator.clear();
|
||||
}
|
||||
|
||||
module->astTypes.clear();
|
||||
module->astTypePacks.clear();
|
||||
|
@ -1058,35 +1331,51 @@ void Frontend::recordItemResult(const BuildQueueItem& item)
|
|||
if (item.exception)
|
||||
std::rethrow_exception(item.exception);
|
||||
|
||||
bool replacedModule = false;
|
||||
if (item.options.forAutocomplete)
|
||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||
{
|
||||
replacedModule = moduleResolverForAutocomplete.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModuleForAutocomplete = false;
|
||||
bool replacedModule = false;
|
||||
if (item.options.forAutocomplete)
|
||||
{
|
||||
replacedModule = moduleResolverForAutocomplete.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModuleForAutocomplete = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
replacedModule = moduleResolver.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModule = false;
|
||||
}
|
||||
|
||||
if (replacedModule)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Frontend::invalidateDependentModules", "Frontend");
|
||||
LUAU_TIMETRACE_ARGUMENT("name", item.name.c_str());
|
||||
traverseDependents(
|
||||
item.name,
|
||||
[forAutocomplete = item.options.forAutocomplete](SourceNode& sourceNode)
|
||||
{
|
||||
bool traverseSubtree = !sourceNode.hasInvalidModuleDependency(forAutocomplete);
|
||||
sourceNode.setInvalidModuleDependency(true, forAutocomplete);
|
||||
return traverseSubtree;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
item.sourceNode->setInvalidModuleDependency(false, item.options.forAutocomplete);
|
||||
}
|
||||
else
|
||||
{
|
||||
replacedModule = moduleResolver.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModule = false;
|
||||
if (item.options.forAutocomplete)
|
||||
{
|
||||
moduleResolverForAutocomplete.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModuleForAutocomplete = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleResolver.setModule(item.name, item.module);
|
||||
item.sourceNode->dirtyModule = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacedModule)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Frontend::invalidateDependentModules", "Frontend");
|
||||
LUAU_TIMETRACE_ARGUMENT("name", item.name.c_str());
|
||||
traverseDependents(
|
||||
item.name,
|
||||
[forAutocomplete = item.options.forAutocomplete](SourceNode& sourceNode)
|
||||
{
|
||||
bool traverseSubtree = !sourceNode.hasInvalidModuleDependency(forAutocomplete);
|
||||
sourceNode.setInvalidModuleDependency(true, forAutocomplete);
|
||||
return traverseSubtree;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
item.sourceNode->setInvalidModuleDependency(false, item.options.forAutocomplete);
|
||||
|
||||
stats.timeCheck += item.stats.timeCheck;
|
||||
stats.timeLint += item.stats.timeLint;
|
||||
|
||||
|
@ -1098,27 +1387,13 @@ void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state,
|
|||
{
|
||||
BuildQueueItem& item = state->buildQueueItems[itemPos];
|
||||
|
||||
if (DFFlag::LuauRethrowKnownExceptions)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
checkBuildQueueItem(item);
|
||||
}
|
||||
catch (const Luau::InternalCompilerError&)
|
||||
{
|
||||
item.exception = std::current_exception();
|
||||
}
|
||||
checkBuildQueueItem(item);
|
||||
}
|
||||
else
|
||||
catch (...)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkBuildQueueItem(item);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
item.exception = std::current_exception();
|
||||
}
|
||||
item.exception = std::current_exception();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1189,6 +1464,7 @@ ScopePtr Frontend::getModuleEnvironment(const SourceModule& module, const Config
|
|||
|
||||
bool Frontend::allModuleDependenciesValid(const ModuleName& name, bool forAutocomplete) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauBetterReverseDependencyTracking);
|
||||
auto it = sourceNodes.find(name);
|
||||
return it != sourceNodes.end() && !it->second->hasInvalidModuleDependency(forAutocomplete);
|
||||
}
|
||||
|
@ -1210,27 +1486,72 @@ void Frontend::markDirty(const ModuleName& name, std::vector<ModuleName>* marked
|
|||
LUAU_TIMETRACE_SCOPE("Frontend::markDirty", "Frontend");
|
||||
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
|
||||
|
||||
traverseDependents(
|
||||
name,
|
||||
[markedDirty](SourceNode& sourceNode)
|
||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||
{
|
||||
traverseDependents(
|
||||
name,
|
||||
[markedDirty](SourceNode& sourceNode)
|
||||
{
|
||||
if (markedDirty)
|
||||
markedDirty->push_back(sourceNode.name);
|
||||
|
||||
if (sourceNode.dirtySourceModule && sourceNode.dirtyModule && sourceNode.dirtyModuleForAutocomplete)
|
||||
return false;
|
||||
|
||||
sourceNode.dirtySourceModule = true;
|
||||
sourceNode.dirtyModule = true;
|
||||
sourceNode.dirtyModuleForAutocomplete = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sourceNodes.count(name) == 0)
|
||||
return;
|
||||
|
||||
std::unordered_map<ModuleName, std::vector<ModuleName>> reverseDeps;
|
||||
for (const auto& module : sourceNodes)
|
||||
{
|
||||
for (const auto& dep : module.second->requireSet)
|
||||
reverseDeps[dep].push_back(module.first);
|
||||
}
|
||||
|
||||
std::vector<ModuleName> queue{name};
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
ModuleName next = std::move(queue.back());
|
||||
queue.pop_back();
|
||||
|
||||
LUAU_ASSERT(sourceNodes.count(next) > 0);
|
||||
SourceNode& sourceNode = *sourceNodes[next];
|
||||
|
||||
if (markedDirty)
|
||||
markedDirty->push_back(sourceNode.name);
|
||||
markedDirty->push_back(next);
|
||||
|
||||
if (sourceNode.dirtySourceModule && sourceNode.dirtyModule && sourceNode.dirtyModuleForAutocomplete)
|
||||
return false;
|
||||
continue;
|
||||
|
||||
sourceNode.dirtySourceModule = true;
|
||||
sourceNode.dirtyModule = true;
|
||||
sourceNode.dirtyModuleForAutocomplete = true;
|
||||
|
||||
return true;
|
||||
if (0 == reverseDeps.count(next))
|
||||
continue;
|
||||
|
||||
sourceModules.erase(next);
|
||||
|
||||
const std::vector<ModuleName>& dependents = reverseDeps[next];
|
||||
queue.insert(queue.end(), dependents.begin(), dependents.end());
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::traverseDependents(const ModuleName& name, std::function<bool(SourceNode&)> processSubtree)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauBetterReverseDependencyTracking);
|
||||
LUAU_TIMETRACE_SCOPE("Frontend::traverseDependents", "Frontend");
|
||||
|
||||
if (sourceNodes.count(name) == 0)
|
||||
|
@ -1277,7 +1598,6 @@ ModulePtr check(
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<FileResolver> fileResolver,
|
||||
const ScopePtr& parentScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
FrontendOptions options,
|
||||
TypeCheckLimits limits,
|
||||
|
@ -1294,7 +1614,6 @@ ModulePtr check(
|
|||
moduleResolver,
|
||||
fileResolver,
|
||||
parentScope,
|
||||
typeFunctionScope,
|
||||
std::move(prepareModuleScope),
|
||||
options,
|
||||
limits,
|
||||
|
@ -1305,7 +1624,7 @@ ModulePtr check(
|
|||
|
||||
struct InternalTypeFinder : TypeOnceVisitor
|
||||
{
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
bool visit(TypeId, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1356,7 +1675,6 @@ ModulePtr check(
|
|||
NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<FileResolver> fileResolver,
|
||||
const ScopePtr& parentScope,
|
||||
const ScopePtr& typeFunctionScope,
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
FrontendOptions options,
|
||||
TypeCheckLimits limits,
|
||||
|
@ -1377,7 +1695,8 @@ ModulePtr check(
|
|||
result->interfaceTypes.owningModule = result.get();
|
||||
result->allocator = sourceModule.allocator;
|
||||
result->names = sourceModule.names;
|
||||
result->root = sourceModule.root;
|
||||
if (FFlag::LuauModuleHoldsAstRoot)
|
||||
result->root = sourceModule.root;
|
||||
|
||||
iceHandler->moduleName = sourceModule.name;
|
||||
|
||||
|
@ -1402,7 +1721,7 @@ ModulePtr check(
|
|||
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
|
||||
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
||||
|
||||
typeFunctionRuntime.allowEvaluation = FFlag::LuauTypeFunResultInAutocomplete || sourceModule.parseErrors.empty();
|
||||
typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty();
|
||||
|
||||
ConstraintGenerator cg{
|
||||
result,
|
||||
|
@ -1413,66 +1732,36 @@ ModulePtr check(
|
|||
builtinTypes,
|
||||
iceHandler,
|
||||
parentScope,
|
||||
typeFunctionScope,
|
||||
std::move(prepareModuleScope),
|
||||
logger.get(),
|
||||
NotNull{&dfg},
|
||||
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;
|
||||
cg.visitModuleRoot(sourceModule.root);
|
||||
result->errors = std::move(cg.errors);
|
||||
|
||||
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(
|
||||
NotNull{&normalizer},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull(cg.rootScope),
|
||||
borrowConstraints(cg.constraints),
|
||||
NotNull{&cg.scopeToFunction},
|
||||
result->name,
|
||||
moduleResolver,
|
||||
requireCycles,
|
||||
logger.get(),
|
||||
NotNull{&dfg},
|
||||
limits
|
||||
);
|
||||
}
|
||||
|
||||
LUAU_ASSERT(bool(cs));
|
||||
ConstraintSolver cs{
|
||||
NotNull{&normalizer},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull(cg.rootScope),
|
||||
borrowConstraints(cg.constraints),
|
||||
NotNull{&cg.scopeToFunction},
|
||||
result->name,
|
||||
moduleResolver,
|
||||
requireCycles,
|
||||
logger.get(),
|
||||
NotNull{&dfg},
|
||||
limits
|
||||
};
|
||||
|
||||
if (options.randomizeConstraintResolutionSeed)
|
||||
cs->randomize(*options.randomizeConstraintResolutionSeed);
|
||||
cs.randomize(*options.randomizeConstraintResolutionSeed);
|
||||
|
||||
try
|
||||
{
|
||||
cs->run();
|
||||
cs.run();
|
||||
}
|
||||
catch (const TimeLimitError&)
|
||||
{
|
||||
|
@ -1492,12 +1781,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)
|
||||
{
|
||||
|
@ -1625,7 +1914,6 @@ ModulePtr Frontend::check(
|
|||
NotNull{forAutocomplete ? &moduleResolverForAutocomplete : &moduleResolver},
|
||||
NotNull{fileResolver},
|
||||
environmentScope ? *environmentScope : globals.globalScope,
|
||||
globals.globalTypeFunctionScope,
|
||||
prepareModuleScopeWrap,
|
||||
options,
|
||||
typeCheckLimits,
|
||||
|
@ -1724,11 +2012,14 @@ std::pair<SourceNode*, SourceModule*> Frontend::getSourceNode(const ModuleName&
|
|||
sourceNode->name = sourceModule->name;
|
||||
sourceNode->humanReadableName = sourceModule->humanReadableName;
|
||||
|
||||
// clear all prior dependents. we will re-add them after parsing the rest of the graph
|
||||
for (const auto& [moduleName, _] : sourceNode->requireLocations)
|
||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||
{
|
||||
if (auto depIt = sourceNodes.find(moduleName); depIt != sourceNodes.end())
|
||||
depIt->second->dependents.erase(sourceNode->name);
|
||||
// clear all prior dependents. we will re-add them after parsing the rest of the graph
|
||||
for (const auto& [moduleName, _] : sourceNode->requireLocations)
|
||||
{
|
||||
if (auto depIt = sourceNodes.find(moduleName); depIt != sourceNodes.end())
|
||||
depIt->second->dependents.erase(sourceNode->name);
|
||||
}
|
||||
}
|
||||
|
||||
sourceNode->requireSet.clear();
|
||||
|
@ -1856,9 +2147,17 @@ bool FrontendModuleResolver::setModule(const ModuleName& moduleName, ModulePtr m
|
|||
{
|
||||
std::scoped_lock lock(moduleMutex);
|
||||
|
||||
bool replaced = modules.count(moduleName) > 0;
|
||||
modules[moduleName] = std::move(module);
|
||||
return replaced;
|
||||
if (FFlag::LuauBetterReverseDependencyTracking)
|
||||
{
|
||||
bool replaced = modules.count(moduleName) > 0;
|
||||
modules[moduleName] = std::move(module);
|
||||
return replaced;
|
||||
}
|
||||
else
|
||||
{
|
||||
modules[moduleName] = std::move(module);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FrontendModuleResolver::clearModules()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,7 +9,6 @@ GlobalTypes::GlobalTypes(NotNull<BuiltinTypes> builtinTypes)
|
|||
: builtinTypes(builtinTypes)
|
||||
{
|
||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
globalTypeFunctionScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
|
||||
globalScope->addBuiltinTypeBinding("any", TypeFun{{}, builtinTypes->anyType});
|
||||
globalScope->addBuiltinTypeBinding("nil", TypeFun{{}, builtinTypes->nilType});
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct InferPolarity : TypeVisitor
|
||||
{
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<Scope> scope;
|
||||
|
||||
DenseHashMap<TypeId, Polarity> types{nullptr};
|
||||
DenseHashMap<TypePackId, Polarity> packs{nullptr};
|
||||
|
||||
Polarity polarity = Polarity::Positive;
|
||||
|
||||
explicit InferPolarity(NotNull<TypeArena> arena, NotNull<Scope> scope)
|
||||
: arena(arena)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
polarity = invert(polarity);
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const GenericType& gt) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
if (subsumes(scope, gt.scope))
|
||||
types[ty] |= polarity;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const TableType& tt) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
const Polarity p = polarity;
|
||||
for (const auto& [name, prop] : tt.props)
|
||||
{
|
||||
if (prop.isShared())
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
}
|
||||
else if (prop.isReadOnly())
|
||||
{
|
||||
polarity = p;
|
||||
traverse(*prop.readTy);
|
||||
}
|
||||
else if (prop.isWriteOnly())
|
||||
{
|
||||
polarity = invert(p);
|
||||
traverse(*prop.writeTy);
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unreachable");
|
||||
}
|
||||
|
||||
if (tt.indexer)
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(tt.indexer->indexType);
|
||||
traverse(tt.indexer->indexResultType);
|
||||
}
|
||||
|
||||
polarity = p;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const FunctionType& ft) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
const Polarity p = polarity;
|
||||
|
||||
polarity = Polarity::Positive;
|
||||
|
||||
// If these types actually occur within the function signature, their
|
||||
// polarity will be overwritten. If not, we infer that they are phantom
|
||||
// types.
|
||||
for (TypeId generic : ft.generics)
|
||||
{
|
||||
generic = follow(generic);
|
||||
const auto gen = get<GenericType>(generic);
|
||||
if (gen && 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))
|
||||
packs[genericPack] = Polarity::None;
|
||||
}
|
||||
|
||||
flip();
|
||||
traverse(ft.argTypes);
|
||||
flip();
|
||||
traverse(ft.retTypes);
|
||||
|
||||
polarity = p;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypePackId tp, const GenericTypePack& gtp) override
|
||||
{
|
||||
packs[tp] |= polarity;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TID>
|
||||
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
||||
{
|
||||
if (!FFlag::LuauNonReentrantGeneralization2)
|
||||
return;
|
||||
|
||||
InferPolarity infer{arena, scope};
|
||||
infer.traverse(ty);
|
||||
|
||||
for (const auto& [ty, polarity] : infer.types)
|
||||
{
|
||||
auto gt = getMutable<GenericType>(ty);
|
||||
LUAU_ASSERT(gt);
|
||||
gt->polarity = polarity;
|
||||
}
|
||||
|
||||
for (const auto& [tp, polarity] : infer.packs)
|
||||
{
|
||||
if (tp->owningArena != arena)
|
||||
continue;
|
||||
auto gp = getMutable<GenericTypePack>(tp);
|
||||
LUAU_ASSERT(gp);
|
||||
gp->polarity = polarity;
|
||||
}
|
||||
}
|
||||
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypeId ty)
|
||||
{
|
||||
inferGenericPolarities_(arena, scope, ty);
|
||||
}
|
||||
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypePackId tp)
|
||||
{
|
||||
inferGenericPolarities_(arena, scope, tp);
|
||||
}
|
||||
|
||||
} // namespace Luau
|
|
@ -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;
|
||||
|
@ -60,7 +61,7 @@ TypeId Instantiation::clean(TypeId ty)
|
|||
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
|
||||
FunctionType clone = FunctionType{level, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
|
||||
FunctionType clone = FunctionType{level, scope, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
|
||||
clone.magic = ftv->magic;
|
||||
clone.tags = ftv->tags;
|
||||
clone.argNames = ftv->argNames;
|
||||
|
@ -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>)
|
||||
|
@ -229,8 +229,6 @@ static void errorToString(std::ostream& stream, const T& err)
|
|||
stream << "UnexpectedTypePackInSubtyping { tp = '" + toString(err.tp) + "' }";
|
||||
else if constexpr (std::is_same_v<T, UserDefinedTypeFunctionError>)
|
||||
stream << "UserDefinedTypeFunctionError { " << err.message << " }";
|
||||
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||
stream << "ReservedIdentifier { " << err.name << " }";
|
||||
else if constexpr (std::is_same_v<T, CannotAssignToNever>)
|
||||
{
|
||||
stream << "CannotAssignToNever { rvalueType = '" << toString(err.rhsType) << "', reason = '" << err.reason << "', cause = { ";
|
||||
|
|
|
@ -19,9 +19,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTFLAG(LuauAttribute)
|
||||
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -909,11 +906,6 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
bool visit(AstTypeReference* node) override
|
||||
{
|
||||
if (!node->prefix)
|
||||
|
@ -1976,11 +1968,6 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypePack* node) override
|
||||
{
|
||||
return FFlag::LuauStoreReturnTypesAsPackOnAst;
|
||||
}
|
||||
|
||||
bool visit(AstTypeTable* node) override
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -2293,57 +2280,6 @@ private:
|
|||
{
|
||||
}
|
||||
|
||||
bool visit(AstExprLocal* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->local->name.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstExprGlobal* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->name.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatLocalFunction* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatFunction* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstExprIndexName* node) override
|
||||
{
|
||||
if (std::optional<TypeId> ty = context->getType(node->expr))
|
||||
|
@ -2383,65 +2319,24 @@ 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);
|
||||
else if (FFlag::LuauDeprecatedAttribute && prop)
|
||||
{
|
||||
if (std::optional<TypeId> ty = prop->readTy)
|
||||
{
|
||||
const FunctionType* fty = get<FunctionType>(follow(ty));
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
{
|
||||
const char* className = nullptr;
|
||||
if (AstExprGlobal* global = node->expr->as<AstExprGlobal>())
|
||||
className = global->name.value;
|
||||
|
||||
const char* functionName = node->index.value;
|
||||
|
||||
report(node->location, className, functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const TableType* tty = get<TableType>(ty))
|
||||
{
|
||||
auto prop = tty->props.find(node->index.value);
|
||||
|
||||
if (prop != tty->props.end())
|
||||
if (prop != tty->props.end() && prop->second.deprecated)
|
||||
{
|
||||
if (prop->second.deprecated)
|
||||
{
|
||||
// strip synthetic typeof() for builtin tables
|
||||
if (tty->name && tty->name->compare(0, 7, "typeof(") == 0 && tty->name->back() == ')')
|
||||
report(node->location, prop->second, tty->name->substr(7, tty->name->length() - 8).c_str(), node->index.value);
|
||||
else
|
||||
report(node->location, prop->second, tty->name ? tty->name->c_str() : nullptr, node->index.value);
|
||||
}
|
||||
else if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
if (std::optional<TypeId> ty = prop->second.readTy)
|
||||
{
|
||||
const FunctionType* fty = get<FunctionType>(follow(ty));
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
{
|
||||
const char* className = nullptr;
|
||||
if (AstExprGlobal* global = node->expr->as<AstExprGlobal>())
|
||||
className = global->name.value;
|
||||
|
||||
const char* functionName = node->index.value;
|
||||
|
||||
report(node->location, className, functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// strip synthetic typeof() for builtin tables
|
||||
if (tty->name && tty->name->compare(0, 7, "typeof(") == 0 && tty->name->back() == ')')
|
||||
report(node->location, prop->second, tty->name->substr(7, tty->name->length() - 8).c_str(), node->index.value);
|
||||
else
|
||||
report(node->location, prop->second, tty->name ? tty->name->c_str() : nullptr, node->index.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2460,26 +2355,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void check(AstExprFunction* func)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(func);
|
||||
|
||||
const FunctionType* fty = getFunctionType(func);
|
||||
bool isDeprecated = fty && fty->isDeprecatedFunction;
|
||||
|
||||
// If a function is deprecated, we don't want to flag its recursive uses.
|
||||
// So we push it on a stack while its body is being analyzed.
|
||||
// When a deprecated function is used, we check the stack to ensure that we are not inside that function.
|
||||
if (isDeprecated)
|
||||
pushScope(fty);
|
||||
|
||||
func->visit(this);
|
||||
|
||||
if (isDeprecated)
|
||||
popScope(fty);
|
||||
}
|
||||
|
||||
void report(const Location& location, const Property& prop, const char* container, const char* field)
|
||||
{
|
||||
std::string suggestion = prop.deprecatedSuggestion.empty() ? "" : format(", use '%s' instead", prop.deprecatedSuggestion.c_str());
|
||||
|
@ -2489,63 +2364,6 @@ private:
|
|||
else
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s' is deprecated%s", field, suggestion.c_str());
|
||||
}
|
||||
|
||||
void report(const Location& location, const char* tableName, const char* functionName)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
if (tableName)
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s.%s' is deprecated", tableName, functionName);
|
||||
else
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s' is deprecated", functionName);
|
||||
}
|
||||
|
||||
void report(const Location& location, const char* functionName)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Function '%s' is deprecated", functionName);
|
||||
}
|
||||
|
||||
std::vector<const FunctionType*> functionTypeScopeStack;
|
||||
|
||||
void pushScope(const FunctionType* fty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
functionTypeScopeStack.push_back(fty);
|
||||
}
|
||||
|
||||
void popScope(const FunctionType* fty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
LUAU_ASSERT(fty == functionTypeScopeStack.back());
|
||||
functionTypeScopeStack.pop_back();
|
||||
}
|
||||
|
||||
bool inScope(const FunctionType* fty) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
return std::find(functionTypeScopeStack.begin(), functionTypeScopeStack.end(), fty) != functionTypeScopeStack.end();
|
||||
}
|
||||
|
||||
const FunctionType* getFunctionType(AstExpr* node)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
std::optional<TypeId> ty = context->getType(node);
|
||||
if (!ty)
|
||||
return nullptr;
|
||||
|
||||
const FunctionType* fty = get<FunctionType>(follow(ty));
|
||||
|
||||
return fty;
|
||||
}
|
||||
};
|
||||
|
||||
class LintTableOperations : AstVisitor
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteCommentDetection)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static void defaultLogLuau(std::string_view context, std::string_view input)
|
||||
static void defaultLogLuau(std::string_view input)
|
||||
{
|
||||
// The default is to do nothing because we don't want to mess with
|
||||
// the xml parsing done by the dcr script.
|
||||
|
@ -38,6 +38,21 @@ void resetLogLuauProc()
|
|||
logLuau = &defaultLogLuau;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool contains_DEPRECATED(Position pos, Comment comment)
|
||||
{
|
||||
if (comment.location.contains(pos))
|
||||
return true;
|
||||
else if (comment.type == Lexeme::BrokenComment && comment.location.begin <= pos) // Broken comments are broken specifically because they don't
|
||||
// have an end
|
||||
return true;
|
||||
else if (comment.type == Lexeme::Comment && comment.location.end == pos)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool contains(Position pos, Comment comment)
|
||||
{
|
||||
if (comment.location.contains(pos))
|
||||
|
@ -61,8 +76,11 @@ bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
|||
Comment{Lexeme::Comment, Location{pos, pos}},
|
||||
[](const Comment& a, const Comment& b)
|
||||
{
|
||||
if (a.type == Lexeme::Comment)
|
||||
return a.location.end.line < b.location.end.line;
|
||||
if (FFlag::LuauIncrementalAutocompleteCommentDetection)
|
||||
{
|
||||
if (a.type == Lexeme::Comment)
|
||||
return a.location.end.line < b.location.end.line;
|
||||
}
|
||||
return a.location.end < b.location.end;
|
||||
}
|
||||
);
|
||||
|
@ -70,7 +88,7 @@ bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
|||
if (iter == commentLocations.end())
|
||||
return false;
|
||||
|
||||
if (contains(pos, *iter))
|
||||
if (FFlag::LuauIncrementalAutocompleteCommentDetection ? contains(pos, *iter) : contains_DEPRECATED(pos, *iter))
|
||||
return true;
|
||||
|
||||
// Due to the nature of std::lower_bound, it is possible that iter points at a comment that ends
|
||||
|
@ -154,6 +172,8 @@ struct ClonePublicInterface : Substitution
|
|||
}
|
||||
|
||||
ftv->level = TypeLevel{0, 0};
|
||||
if (FFlag::LuauSolverV2)
|
||||
ftv->scope = nullptr;
|
||||
}
|
||||
else if (TableType* ttv = getMutable<TableType>(result))
|
||||
{
|
||||
|
@ -265,10 +285,7 @@ struct ClonePublicInterface : Substitution
|
|||
|
||||
TypeId type = cloneType(tf.type);
|
||||
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
||||
else
|
||||
return TypeFun{typeParams, typePackParams, type};
|
||||
return TypeFun{typeParams, typePackParams, type};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "Luau/NonStrictTypeChecker.h"
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/AstQuery.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Simplify.h"
|
||||
#include "Luau/Type.h"
|
||||
|
@ -20,12 +19,9 @@
|
|||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -217,7 +213,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 +307,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);
|
||||
|
@ -337,12 +333,7 @@ struct NonStrictTypeChecker
|
|||
// local x ; B generates the context of B without x
|
||||
visit(local);
|
||||
for (auto local : local->vars)
|
||||
{
|
||||
ctx.remove(dfg->getDef(local));
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
visit(local->annotation);
|
||||
}
|
||||
}
|
||||
else
|
||||
ctx = NonStrictContext::disjunction(builtinTypes, arena, visit(stat), ctx);
|
||||
|
@ -427,9 +418,6 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatFor* forStatement)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
visit(forStatement->var->annotation);
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
{
|
||||
// TODO: throwing out context based on same principle as existing code?
|
||||
|
@ -449,12 +437,6 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatForIn* forInStatement)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
{
|
||||
for (auto var : forInStatement->vars)
|
||||
visit(var->annotation);
|
||||
}
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
{
|
||||
for (AstExpr* rhs : forInStatement->values)
|
||||
|
@ -503,12 +485,6 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
{
|
||||
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
|
||||
visit(typeAlias->type);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -519,38 +495,16 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
{
|
||||
visitGenerics(declFn->generics, declFn->genericPacks);
|
||||
visit(declFn->params);
|
||||
visit(declFn->retTypes);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
visit(declGlobal->type);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
NonStrictContext visit(AstStatDeclareExternType* declClass)
|
||||
NonStrictContext visit(AstStatDeclareClass* declClass)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
{
|
||||
if (declClass->indexer)
|
||||
{
|
||||
visit(declClass->indexer->indexType);
|
||||
visit(declClass->indexer->resultType);
|
||||
}
|
||||
|
||||
for (auto prop : declClass->props)
|
||||
visit(prop.ty);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -809,32 +763,9 @@ struct NonStrictTypeChecker
|
|||
for (AstLocal* local : exprFn->args)
|
||||
{
|
||||
if (std::optional<TypeId> ty = willRunTimeErrorFunctionDefinition(local, remainder))
|
||||
{
|
||||
const char* debugname = exprFn->debugname.value;
|
||||
reportError(NonStrictFunctionDefinitionError{debugname ? debugname : "", local->name.value, *ty}, local->location);
|
||||
}
|
||||
reportError(NonStrictFunctionDefinitionError{exprFn->debugname.value, local->name.value, *ty}, local->location);
|
||||
remainder.remove(dfg->getDef(local));
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
visit(local->annotation);
|
||||
}
|
||||
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
{
|
||||
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(exprFn->returnAnnotation);
|
||||
else
|
||||
{
|
||||
if (exprFn->returnAnnotation_DEPRECATED)
|
||||
visit(*exprFn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
|
||||
if (exprFn->varargAnnotation)
|
||||
visit(exprFn->varargAnnotation);
|
||||
}
|
||||
|
||||
return remainder;
|
||||
}
|
||||
|
||||
|
@ -875,9 +806,6 @@ struct NonStrictTypeChecker
|
|||
|
||||
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
||||
{
|
||||
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||
visit(typeAssertion->annotation);
|
||||
|
||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||
return visit(typeAssertion->expr, ValueContext::RValue);
|
||||
else
|
||||
|
@ -914,331 +842,6 @@ struct NonStrictTypeChecker
|
|||
return {};
|
||||
}
|
||||
|
||||
void visit(AstType* ty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||
|
||||
// If this node is `nullptr`, early exit.
|
||||
if (!ty)
|
||||
return;
|
||||
|
||||
if (auto t = ty->as<AstTypeReference>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeTable>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeFunction>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeTypeof>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeUnion>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeIntersection>())
|
||||
return visit(t);
|
||||
else if (auto t = ty->as<AstTypeGroup>())
|
||||
return visit(t->type);
|
||||
}
|
||||
|
||||
void visit(AstTypeReference* ty)
|
||||
{
|
||||
// No further validation is necessary in this case. The main logic for
|
||||
// _luau_print is contained in lookupAnnotation.
|
||||
if (FFlag::DebugLuauMagicTypes && ty->name == "_luau_print")
|
||||
return;
|
||||
|
||||
for (const AstTypeOrPack& param : ty->parameters)
|
||||
{
|
||||
if (param.type)
|
||||
visit(param.type);
|
||||
else
|
||||
visit(param.typePack);
|
||||
}
|
||||
|
||||
Scope* scope = findInnermostScope(ty->location);
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
std::optional<TypeFun> alias = ty->prefix ? scope->lookupImportedType(ty->prefix->value, ty->name.value) : scope->lookupType(ty->name.value);
|
||||
|
||||
if (alias.has_value())
|
||||
{
|
||||
size_t typesRequired = alias->typeParams.size();
|
||||
size_t packsRequired = alias->typePackParams.size();
|
||||
|
||||
bool hasDefaultTypes = std::any_of(
|
||||
alias->typeParams.begin(),
|
||||
alias->typeParams.end(),
|
||||
[](auto&& el)
|
||||
{
|
||||
return el.defaultValue.has_value();
|
||||
}
|
||||
);
|
||||
|
||||
bool hasDefaultPacks = std::any_of(
|
||||
alias->typePackParams.begin(),
|
||||
alias->typePackParams.end(),
|
||||
[](auto&& el)
|
||||
{
|
||||
return el.defaultValue.has_value();
|
||||
}
|
||||
);
|
||||
|
||||
if (!ty->hasParameterList)
|
||||
{
|
||||
if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks))
|
||||
reportError(GenericError{"Type parameter list is required"}, ty->location);
|
||||
}
|
||||
|
||||
size_t typesProvided = 0;
|
||||
size_t extraTypes = 0;
|
||||
size_t packsProvided = 0;
|
||||
|
||||
for (const AstTypeOrPack& p : ty->parameters)
|
||||
{
|
||||
if (p.type)
|
||||
{
|
||||
if (packsProvided != 0)
|
||||
{
|
||||
reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typesProvided < typesRequired)
|
||||
typesProvided += 1;
|
||||
else
|
||||
extraTypes += 1;
|
||||
}
|
||||
else if (p.typePack)
|
||||
{
|
||||
std::optional<TypePackId> tp = lookupPackAnnotation(p.typePack);
|
||||
if (!tp.has_value())
|
||||
continue;
|
||||
|
||||
if (typesProvided < typesRequired && size(*tp) == 1 && finite(*tp) && first(*tp))
|
||||
typesProvided += 1;
|
||||
else
|
||||
packsProvided += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (extraTypes != 0 && packsProvided == 0)
|
||||
{
|
||||
// Extra types are only collected into a pack if a pack is expected
|
||||
if (packsRequired != 0)
|
||||
packsProvided += 1;
|
||||
else
|
||||
typesProvided += extraTypes;
|
||||
}
|
||||
|
||||
for (size_t i = typesProvided; i < typesRequired; ++i)
|
||||
{
|
||||
if (alias->typeParams[i].defaultValue)
|
||||
typesProvided += 1;
|
||||
}
|
||||
|
||||
for (size_t i = packsProvided; i < packsRequired; ++i)
|
||||
{
|
||||
if (alias->typePackParams[i].defaultValue)
|
||||
packsProvided += 1;
|
||||
}
|
||||
|
||||
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
|
||||
packsProvided += 1;
|
||||
|
||||
|
||||
if (typesProvided != typesRequired || packsProvided != packsRequired)
|
||||
{
|
||||
reportError(
|
||||
IncorrectGenericParameterCount{
|
||||
/* name */ ty->name.value,
|
||||
/* typeFun */ *alias,
|
||||
/* actualParameters */ typesProvided,
|
||||
/* actualPackParameters */ packsProvided,
|
||||
},
|
||||
ty->location
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scope->lookupPack(ty->name.value))
|
||||
{
|
||||
reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
ty->name.value,
|
||||
SwappedGenericTypeParameter::Kind::Type,
|
||||
},
|
||||
ty->location
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string symbol = "";
|
||||
if (ty->prefix)
|
||||
{
|
||||
symbol += (*(ty->prefix)).value;
|
||||
symbol += ".";
|
||||
}
|
||||
symbol += ty->name.value;
|
||||
|
||||
reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstTypeTable* table)
|
||||
{
|
||||
if (table->indexer)
|
||||
{
|
||||
visit(table->indexer->indexType);
|
||||
visit(table->indexer->resultType);
|
||||
}
|
||||
|
||||
for (auto prop : table->props)
|
||||
visit(prop.type);
|
||||
}
|
||||
|
||||
void visit(AstTypeFunction* function)
|
||||
{
|
||||
visit(function->argTypes);
|
||||
visit(function->returnTypes);
|
||||
}
|
||||
|
||||
void visit(AstTypeTypeof* typeOf)
|
||||
{
|
||||
visit(typeOf->expr, ValueContext::RValue);
|
||||
}
|
||||
|
||||
void visit(AstTypeUnion* unionType)
|
||||
{
|
||||
for (auto typ : unionType->types)
|
||||
visit(typ);
|
||||
}
|
||||
|
||||
void visit(AstTypeIntersection* intersectionType)
|
||||
{
|
||||
for (auto typ : intersectionType->types)
|
||||
visit(typ);
|
||||
}
|
||||
|
||||
void visit(AstTypeList& list)
|
||||
{
|
||||
for (auto typ : list.types)
|
||||
visit(typ);
|
||||
if (list.tailType)
|
||||
visit(list.tailType);
|
||||
}
|
||||
|
||||
void visit(AstTypePack* pack)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||
|
||||
// If there is no pack node, early exit.
|
||||
if (!pack)
|
||||
return;
|
||||
|
||||
if (auto p = pack->as<AstTypePackExplicit>())
|
||||
return visit(p);
|
||||
else if (auto p = pack->as<AstTypePackVariadic>())
|
||||
return visit(p);
|
||||
else if (auto p = pack->as<AstTypePackGeneric>())
|
||||
return visit(p);
|
||||
}
|
||||
|
||||
void visit(AstTypePackExplicit* tp)
|
||||
{
|
||||
for (AstType* type : tp->typeList.types)
|
||||
visit(type);
|
||||
|
||||
if (tp->typeList.tailType)
|
||||
visit(tp->typeList.tailType);
|
||||
}
|
||||
|
||||
void visit(AstTypePackVariadic* tp)
|
||||
{
|
||||
visit(tp->variadicType);
|
||||
}
|
||||
|
||||
void visit(AstTypePackGeneric* tp)
|
||||
{
|
||||
Scope* scope = findInnermostScope(tp->location);
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||
if (!alias.has_value())
|
||||
{
|
||||
if (scope->lookupType(tp->genericName.value))
|
||||
{
|
||||
reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
tp->genericName.value,
|
||||
SwappedGenericTypeParameter::Kind::Pack,
|
||||
},
|
||||
tp->location
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
}
|
||||
}
|
||||
|
||||
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks)
|
||||
{
|
||||
DenseHashSet<AstName> seen{AstName{}};
|
||||
|
||||
for (const auto* g : generics)
|
||||
{
|
||||
if (seen.contains(g->name))
|
||||
reportError(DuplicateGenericParameter{g->name.value}, g->location);
|
||||
else
|
||||
seen.insert(g->name);
|
||||
|
||||
if (g->defaultValue)
|
||||
visit(g->defaultValue);
|
||||
}
|
||||
|
||||
for (const auto* g : genericPacks)
|
||||
{
|
||||
if (seen.contains(g->name))
|
||||
reportError(DuplicateGenericParameter{g->name.value}, g->location);
|
||||
else
|
||||
seen.insert(g->name);
|
||||
|
||||
if (g->defaultValue)
|
||||
visit(g->defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
Scope* findInnermostScope(Location location) const
|
||||
{
|
||||
Scope* bestScope = module->getModuleScope().get();
|
||||
|
||||
bool didNarrow;
|
||||
do
|
||||
{
|
||||
didNarrow = false;
|
||||
for (auto scope : bestScope->children)
|
||||
{
|
||||
if (scope->location.encloses(location))
|
||||
{
|
||||
bestScope = scope.get();
|
||||
didNarrow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (didNarrow && bestScope->children.size() > 0);
|
||||
|
||||
return bestScope;
|
||||
}
|
||||
|
||||
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation) const
|
||||
{
|
||||
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
||||
if (tp != nullptr)
|
||||
return {follow(*tp)};
|
||||
return {};
|
||||
}
|
||||
|
||||
void reportError(TypeErrorData data, const Location& location)
|
||||
{
|
||||
module->errors.emplace_back(location, module->name, std::move(data));
|
||||
|
|
|
@ -19,10 +19,10 @@ LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
|||
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeNegationFix)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixInfiniteRecursionInNormalization)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationCatchMetatableCycles)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixNormalizedIntersectionOfNegatedClass)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -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()
|
||||
|
@ -304,17 +304,17 @@ bool NormalizedType::isUnknown() const
|
|||
|
||||
// Otherwise, we can still be unknown!
|
||||
bool hasAllPrimitives = isPrim(booleans, PrimitiveType::Boolean) && isPrim(nils, PrimitiveType::NilType) && isNumber(numbers) &&
|
||||
strings.isString() && isThread(threads) && isBuffer(buffers);
|
||||
strings.isString() && isPrim(threads, PrimitiveType::Thread) && isThread(threads);
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -579,7 +579,7 @@ NormalizationResult Normalizer::isIntersectionInhabited(TypeId left, TypeId righ
|
|||
{
|
||||
left = follow(left);
|
||||
right = follow(right);
|
||||
// We're asking if intersection is inhabited between left and right but we've already seen them ....
|
||||
// We're asking if intersection is inahbited between left and right but we've already seen them ....
|
||||
|
||||
if (cacheInhabitance)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1685,12 +1685,8 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
|||
return res;
|
||||
}
|
||||
|
||||
// Limit based on worst-case expansion of the function unions
|
||||
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeUnionLimit))
|
||||
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);
|
||||
|
@ -1700,7 +1696,6 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
|||
here.buffers = (get<NeverType>(there.buffers) ? here.buffers : there.buffers);
|
||||
unionFunctions(here.functions, there.functions);
|
||||
unionTables(here.tables, there.tables);
|
||||
|
||||
return NormalizationResult::True;
|
||||
}
|
||||
|
||||
|
@ -1740,7 +1735,7 @@ NormalizationResult Normalizer::intersectNormalWithNegationTy(TypeId toNegate, N
|
|||
return NormalizationResult::True;
|
||||
}
|
||||
|
||||
// See above for an explanation of `ignoreSmallerTyvars`.
|
||||
// See above for an explaination of `ignoreSmallerTyvars`.
|
||||
NormalizationResult Normalizer::unionNormalWithTy(
|
||||
NormalizedType& here,
|
||||
TypeId there,
|
||||
|
@ -1830,8 +1825,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 +1939,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 +2139,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 +2173,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 +2201,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 +2229,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 +2246,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
|
||||
|
@ -2294,7 +2289,7 @@ void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres,
|
|||
|
||||
for (auto nIt = negations.begin(); nIt != negations.end();)
|
||||
{
|
||||
if (isSubclass(there, *nIt))
|
||||
if (FFlag::LuauFixNormalizedIntersectionOfNegatedClass && isSubclass(there, *nIt))
|
||||
{
|
||||
// Hitting this block means that the incoming class is a
|
||||
// subclass of this type, _and_ one of its negations is a
|
||||
|
@ -2319,7 +2314,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 +2330,7 @@ void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres,
|
|||
else
|
||||
{
|
||||
it = heres.ordering.erase(it);
|
||||
heres.externTypes.erase(hereTy);
|
||||
heres.classes.erase(hereTy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3055,7 +3050,7 @@ NormalizationResult Normalizer::intersectTyvarsWithTy(
|
|||
return NormalizationResult::True;
|
||||
}
|
||||
|
||||
// See above for an explanation of `ignoreSmallerTyvars`.
|
||||
// See above for an explaination of `ignoreSmallerTyvars`.
|
||||
NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
|
||||
{
|
||||
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
||||
|
@ -3073,17 +3068,14 @@ NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const Nor
|
|||
return unionNormals(here, there, ignoreSmallerTyvars);
|
||||
}
|
||||
|
||||
// Limit based on worst-case expansion of the table/function intersections
|
||||
// Limit based on worst-case expansion of the table intersection
|
||||
// This restriction can be relaxed when table intersection simplification is improved
|
||||
if (here.tables.size() * there.tables.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
||||
return NormalizationResult::HitLimits;
|
||||
|
||||
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
||||
return NormalizationResult::HitLimits;
|
||||
|
||||
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,18 +3197,18 @@ 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))
|
||||
{
|
||||
TypeId errors = here.errors;
|
||||
clearNormal(here);
|
||||
here.errors = get<ErrorType>(errors) ? errors : there;
|
||||
here.errors = errors;
|
||||
}
|
||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
||||
{
|
||||
|
@ -3274,7 +3266,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))
|
||||
|
@ -3313,16 +3305,11 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
clearNormal(here);
|
||||
return NormalizationResult::True;
|
||||
}
|
||||
else if (get<ErrorType>(t))
|
||||
{
|
||||
// ~error is still an error, so intersecting with the negation is the same as intersecting with a type
|
||||
TypeId errors = here.errors;
|
||||
clearNormal(here);
|
||||
here.errors = get<ErrorType>(errors) ? errors : t;
|
||||
}
|
||||
else if (auto nt = get<NegationType>(t))
|
||||
{
|
||||
here.tyvars = std::move(tyvars);
|
||||
if (FFlag::LuauNormalizeNegationFix)
|
||||
here.tyvars = std::move(tyvars);
|
||||
|
||||
return intersectNormalWithTy(here, nt->ty, seenTablePropPairs, seenSetTypes);
|
||||
}
|
||||
else
|
||||
|
@ -3334,7 +3321,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
}
|
||||
else if (get<NeverType>(there))
|
||||
{
|
||||
here.externTypes.resetToNever();
|
||||
here.classes.resetToNever();
|
||||
}
|
||||
else if (get<NoRefineType>(there))
|
||||
{
|
||||
|
@ -3352,43 +3339,19 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
|||
return NormalizationResult::True;
|
||||
}
|
||||
|
||||
void makeTableShared_DEPRECATED(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (auto tableTy = getMutable<TableType>(ty))
|
||||
{
|
||||
for (auto& [_, prop] : tableTy->props)
|
||||
prop.makeShared();
|
||||
}
|
||||
else if (auto metatableTy = get<MetatableType>(ty))
|
||||
{
|
||||
makeTableShared_DEPRECATED(metatableTy->metatable);
|
||||
makeTableShared_DEPRECATED(metatableTy->table);
|
||||
}
|
||||
}
|
||||
|
||||
void makeTableShared(TypeId ty, DenseHashSet<TypeId>& seen)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (seen.contains(ty))
|
||||
return;
|
||||
seen.insert(ty);
|
||||
if (auto tableTy = getMutable<TableType>(ty))
|
||||
{
|
||||
for (auto& [_, prop] : tableTy->props)
|
||||
prop.makeShared();
|
||||
}
|
||||
else if (auto metatableTy = get<MetatableType>(ty))
|
||||
{
|
||||
makeTableShared(metatableTy->metatable, seen);
|
||||
makeTableShared(metatableTy->table, seen);
|
||||
}
|
||||
}
|
||||
|
||||
void makeTableShared(TypeId ty)
|
||||
{
|
||||
DenseHashSet<TypeId> seen{nullptr};
|
||||
makeTableShared(ty, seen);
|
||||
ty = follow(ty);
|
||||
if (auto tableTy = getMutable<TableType>(ty))
|
||||
{
|
||||
for (auto& [_, prop] : tableTy->props)
|
||||
prop.makeShared();
|
||||
}
|
||||
else if (auto metatableTy = get<MetatableType>(ty))
|
||||
{
|
||||
makeTableShared(metatableTy->metatable);
|
||||
makeTableShared(metatableTy->table);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Convert back from a normalized type to a type
|
||||
|
@ -3403,18 +3366,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())
|
||||
{
|
||||
|
@ -3490,10 +3453,7 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
|||
result.reserve(result.size() + norm.tables.size());
|
||||
for (auto table : norm.tables)
|
||||
{
|
||||
if (FFlag::LuauNormalizationCatchMetatableCycles)
|
||||
makeTableShared(table);
|
||||
else
|
||||
makeTableShared_DEPRECATED(table);
|
||||
makeTableShared(table);
|
||||
result.push_back(table);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Unifier2.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -256,32 +254,15 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
|||
}
|
||||
|
||||
// If any of the unsatisfied arguments are not supertypes of
|
||||
// nil or are `unknown`, then this overload does not match.
|
||||
// nil, then this overload does not match.
|
||||
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
|
||||
{
|
||||
if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||
{
|
||||
if (get<UnknownType>(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||
{
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
for (auto arg : fn->argTypes)
|
||||
if (get<UnknownType>(follow(arg)))
|
||||
minParams += 1;
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
||||
return {Analysis::ArityMismatch, {error}};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||
{
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
||||
return {Analysis::ArityMismatch, {error}};
|
||||
}
|
||||
return {Analysis::ArityMismatch, {error}};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +454,7 @@ SolveResult solveFunctionCall(
|
|||
|
||||
TypePackId resultPack = arena->freshTypePack(scope);
|
||||
|
||||
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, resultPack});
|
||||
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, scope.get(), argsPack, resultPack});
|
||||
Unifier2 u2{NotNull{arena}, builtinTypes, scope, iceReporter};
|
||||
|
||||
const bool occursCheckPassed = u2.unify(*overloadToUse, inferredTy);
|
||||
|
|
|
@ -107,4 +107,134 @@ void quantify(TypeId ty, TypeLevel level)
|
|||
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
|
||||
}
|
||||
|
||||
struct PureQuantifier : Substitution
|
||||
{
|
||||
Scope* scope;
|
||||
OrderedMap<TypeId, TypeId> insertedGenerics;
|
||||
OrderedMap<TypePackId, TypePackId> insertedGenericPacks;
|
||||
bool seenMutableType = false;
|
||||
bool seenGenericType = false;
|
||||
|
||||
PureQuantifier(TypeArena* arena, Scope* scope)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
bool isDirty(TypeId ty) override
|
||||
{
|
||||
LUAU_ASSERT(ty == follow(ty));
|
||||
|
||||
if (auto ftv = get<FreeType>(ty))
|
||||
{
|
||||
bool result = subsumes(scope, ftv->scope);
|
||||
seenMutableType |= result;
|
||||
return result;
|
||||
}
|
||||
else if (auto ttv = get<TableType>(ty))
|
||||
{
|
||||
if (ttv->state == TableState::Free)
|
||||
seenMutableType = true;
|
||||
else if (ttv->state == TableState::Generic)
|
||||
seenGenericType = true;
|
||||
|
||||
return (ttv->state == TableState::Unsealed || ttv->state == TableState::Free) && subsumes(scope, ttv->scope);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDirty(TypePackId tp) override
|
||||
{
|
||||
if (auto ftp = get<FreeTypePack>(tp))
|
||||
{
|
||||
return subsumes(scope, ftp->scope);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeId clean(TypeId ty) override
|
||||
{
|
||||
if (auto ftv = get<FreeType>(ty))
|
||||
{
|
||||
TypeId result = arena->addType(GenericType{scope});
|
||||
insertedGenerics.push(ty, result);
|
||||
return result;
|
||||
}
|
||||
else if (auto ttv = get<TableType>(ty))
|
||||
{
|
||||
TypeId result = arena->addType(TableType{});
|
||||
TableType* resultTable = getMutable<TableType>(result);
|
||||
LUAU_ASSERT(resultTable);
|
||||
|
||||
*resultTable = *ttv;
|
||||
resultTable->level = TypeLevel{};
|
||||
resultTable->scope = scope;
|
||||
|
||||
if (ttv->state == TableState::Free)
|
||||
{
|
||||
resultTable->state = TableState::Generic;
|
||||
insertedGenerics.push(ty, result);
|
||||
}
|
||||
else if (ttv->state == TableState::Unsealed)
|
||||
resultTable->state = TableState::Sealed;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return ty;
|
||||
}
|
||||
|
||||
TypePackId clean(TypePackId tp) override
|
||||
{
|
||||
if (auto ftp = get<FreeTypePack>(tp))
|
||||
{
|
||||
TypePackId result = arena->addTypePack(TypePackVar{GenericTypePack{scope}});
|
||||
insertedGenericPacks.push(tp, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
||||
bool ignoreChildren(TypeId ty) override
|
||||
{
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return ty->persistent;
|
||||
}
|
||||
bool ignoreChildren(TypePackId ty) override
|
||||
{
|
||||
return ty->persistent;
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<QuantifierResult> quantify(TypeArena* arena, TypeId ty, Scope* scope)
|
||||
{
|
||||
PureQuantifier quantifier{arena, scope};
|
||||
std::optional<TypeId> result = quantifier.substitute(ty);
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
|
||||
FunctionType* ftv = getMutable<FunctionType>(*result);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->scope = scope;
|
||||
|
||||
for (auto k : quantifier.insertedGenerics.keys)
|
||||
{
|
||||
TypeId g = quantifier.insertedGenerics.pairings[k];
|
||||
if (get<GenericType>(g))
|
||||
ftv->generics.push_back(g);
|
||||
}
|
||||
|
||||
for (auto k : quantifier.insertedGenericPacks.keys)
|
||||
ftv->genericPacks.push_back(quantifier.insertedGenericPacks.pairings[k]);
|
||||
|
||||
ftv->hasNoFreeOrGenericTypes = ftv->generics.empty() && ftv->genericPacks.empty() && !quantifier.seenGenericType && !quantifier.seenMutableType;
|
||||
|
||||
return std::optional<QuantifierResult>({*result, std::move(quantifier.insertedGenerics), std::move(quantifier.insertedGenericPacks)});
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "Luau/Ast.h"
|
||||
#include "Luau/Module.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExtendedSimpleRequire)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -67,12 +67,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>())
|
||||
|
@ -112,50 +106,96 @@ struct RequireTracer : AstVisitor
|
|||
{
|
||||
ModuleInfo moduleContext{currentModuleName};
|
||||
|
||||
// seed worklist with require arguments
|
||||
work.reserve(requireCalls.size());
|
||||
|
||||
for (AstExprCall* require : requireCalls)
|
||||
work.push_back(require->args.data[0]);
|
||||
|
||||
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
||||
for (size_t i = 0; i < work.size(); ++i)
|
||||
if (FFlag::LuauExtendedSimpleRequire)
|
||||
{
|
||||
if (AstNode* dep = getDependent(work[i]))
|
||||
work.push_back(dep);
|
||||
}
|
||||
// seed worklist with require arguments
|
||||
work.reserve(requireCalls.size());
|
||||
|
||||
// resolve all expressions to a module info
|
||||
for (size_t i = work.size(); i > 0; --i)
|
||||
{
|
||||
AstNode* expr = work[i - 1];
|
||||
for (AstExprCall* require : requireCalls)
|
||||
work.push_back(require->args.data[0]);
|
||||
|
||||
// when multiple expressions depend on the same one we push it to work queue multiple times
|
||||
if (result.exprs.contains(expr))
|
||||
continue;
|
||||
|
||||
std::optional<ModuleInfo> info;
|
||||
|
||||
if (AstNode* dep = getDependent(expr))
|
||||
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
||||
for (size_t i = 0; i < work.size(); ++i)
|
||||
{
|
||||
const ModuleInfo* context = result.exprs.find(dep);
|
||||
if (AstNode* dep = getDependent(work[i]))
|
||||
work.push_back(dep);
|
||||
}
|
||||
|
||||
if (context && expr->is<AstExprLocal>())
|
||||
info = *context; // locals just inherit their dependent context, no resolution required
|
||||
else if (context && (expr->is<AstExprGroup>() || expr->is<AstTypeGroup>()))
|
||||
info = *context; // simple group nodes propagate their value
|
||||
else if (context && (expr->is<AstTypeTypeof>() || expr->is<AstExprTypeAssertion>()))
|
||||
info = *context; // typeof type annotations will resolve to the typeof content
|
||||
// resolve all expressions to a module info
|
||||
for (size_t i = work.size(); i > 0; --i)
|
||||
{
|
||||
AstNode* expr = work[i - 1];
|
||||
|
||||
// when multiple expressions depend on the same one we push it to work queue multiple times
|
||||
if (result.exprs.contains(expr))
|
||||
continue;
|
||||
|
||||
std::optional<ModuleInfo> info;
|
||||
|
||||
if (AstNode* dep = getDependent(expr))
|
||||
{
|
||||
const ModuleInfo* context = result.exprs.find(dep);
|
||||
|
||||
if (context && expr->is<AstExprLocal>())
|
||||
info = *context; // locals just inherit their dependent context, no resolution required
|
||||
else if (context && (expr->is<AstExprGroup>() || expr->is<AstTypeGroup>()))
|
||||
info = *context; // simple group nodes propagate their value
|
||||
else if (context && (expr->is<AstTypeTypeof>() || expr->is<AstExprTypeAssertion>()))
|
||||
info = *context; // typeof type annotations will resolve to the typeof content
|
||||
else if (AstExpr* asExpr = expr->asExpr())
|
||||
info = fileResolver->resolveModule(context, asExpr);
|
||||
}
|
||||
else if (AstExpr* asExpr = expr->asExpr())
|
||||
info = fileResolver->resolveModule(context, asExpr);
|
||||
}
|
||||
else if (AstExpr* asExpr = expr->asExpr())
|
||||
{
|
||||
info = fileResolver->resolveModule(&moduleContext, asExpr);
|
||||
}
|
||||
{
|
||||
info = fileResolver->resolveModule(&moduleContext, asExpr);
|
||||
}
|
||||
|
||||
if (info)
|
||||
result.exprs[expr] = std::move(*info);
|
||||
if (info)
|
||||
result.exprs[expr] = std::move(*info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// seed worklist with require arguments
|
||||
work_DEPRECATED.reserve(requireCalls.size());
|
||||
|
||||
for (AstExprCall* require : requireCalls)
|
||||
work_DEPRECATED.push_back(require->args.data[0]);
|
||||
|
||||
// push all dependent expressions to the work stack; note that the vector is modified during traversal
|
||||
for (size_t i = 0; i < work_DEPRECATED.size(); ++i)
|
||||
if (AstExpr* dep = getDependent_DEPRECATED(work_DEPRECATED[i]))
|
||||
work_DEPRECATED.push_back(dep);
|
||||
|
||||
// resolve all expressions to a module info
|
||||
for (size_t i = work_DEPRECATED.size(); i > 0; --i)
|
||||
{
|
||||
AstExpr* expr = work_DEPRECATED[i - 1];
|
||||
|
||||
// when multiple expressions depend on the same one we push it to work queue multiple times
|
||||
if (result.exprs.contains(expr))
|
||||
continue;
|
||||
|
||||
std::optional<ModuleInfo> info;
|
||||
|
||||
if (AstExpr* dep = getDependent_DEPRECATED(expr))
|
||||
{
|
||||
const ModuleInfo* context = result.exprs.find(dep);
|
||||
|
||||
// locals just inherit their dependent context, no resolution required
|
||||
if (expr->is<AstExprLocal>())
|
||||
info = context ? std::optional<ModuleInfo>(*context) : std::nullopt;
|
||||
else
|
||||
info = fileResolver->resolveModule(context, expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
info = fileResolver->resolveModule(&moduleContext, expr);
|
||||
}
|
||||
|
||||
if (info)
|
||||
result.exprs[expr] = std::move(*info);
|
||||
}
|
||||
}
|
||||
|
||||
// resolve all requires according to their argument
|
||||
|
@ -184,6 +224,7 @@ struct RequireTracer : AstVisitor
|
|||
ModuleName currentModuleName;
|
||||
|
||||
DenseHashMap<AstLocal*, AstExpr*> locals;
|
||||
std::vector<AstExpr*> work_DEPRECATED;
|
||||
std::vector<AstNode*> work;
|
||||
std::vector<AstExprCall*> requireCalls;
|
||||
};
|
||||
|
|
|
@ -84,17 +84,6 @@ std::optional<TypeId> Scope::lookupUnrefinedType(DefId def) const
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope::lookupRValueRefinementType(DefId def) const
|
||||
{
|
||||
for (const Scope* current = this; current; current = current->parent.get())
|
||||
{
|
||||
if (auto ty = current->rvalueRefinements.find(def))
|
||||
return *ty;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope::lookup(DefId def) const
|
||||
{
|
||||
for (const Scope* current = this; current; current = current->parent.get())
|
||||
|
@ -192,29 +181,6 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::pair<Symbol, Binding>> Scope::linearSearchForBindingPair(const std::string& name, bool traverseScopeChain) const
|
||||
{
|
||||
const Scope* scope = this;
|
||||
|
||||
while (scope)
|
||||
{
|
||||
for (auto& [n, binding] : scope->bindings)
|
||||
{
|
||||
if (n.local && n.local->name == name.c_str())
|
||||
return {{n, binding}};
|
||||
else if (n.global.value && n.global == name.c_str())
|
||||
return {{n, binding}};
|
||||
}
|
||||
|
||||
scope = scope->parent.get();
|
||||
|
||||
if (!traverseScopeChain)
|
||||
break;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Updates the `this` scope with the assignments from the `childScope` including ones that doesn't exist in `this`.
|
||||
void Scope::inheritAssignments(const ScopePtr& childScope)
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
@ -15,10 +14,8 @@
|
|||
|
||||
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationRecheckAssumption)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType)
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8);
|
||||
LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -50,8 +47,6 @@ struct TypeSimplifier
|
|||
// Attempt to intersect the two types. Does not recurse. Does not handle
|
||||
// unions, intersections, or negations.
|
||||
std::optional<TypeId> basicIntersect(TypeId left, TypeId right);
|
||||
std::optional<TypeId> basicIntersectWithTruthy(TypeId target) const;
|
||||
std::optional<TypeId> basicIntersectWithFalsy(TypeId target) const;
|
||||
|
||||
TypeId intersect(TypeId left, TypeId right);
|
||||
TypeId union_(TypeId left, TypeId right);
|
||||
|
@ -318,14 +313,12 @@ 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;
|
||||
|
||||
return Relation::Superset;
|
||||
else
|
||||
return Relation::Superset;
|
||||
}
|
||||
|
||||
if (get<UnknownType>(right))
|
||||
|
@ -335,8 +328,8 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (get<AnyType>(right))
|
||||
return Relation::Coincident;
|
||||
|
||||
return Relation::Superset;
|
||||
else
|
||||
return Relation::Superset;
|
||||
}
|
||||
|
||||
if (get<AnyType>(right))
|
||||
|
@ -360,7 +353,7 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
// * FunctionType
|
||||
// * TableType
|
||||
// * MetatableType
|
||||
// * ExternType
|
||||
// * ClassType
|
||||
// * UnionType
|
||||
// * IntersectionType
|
||||
// * NegationType
|
||||
|
@ -368,33 +361,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;
|
||||
|
||||
return Relation::Disjoint;
|
||||
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;
|
||||
|
||||
return Relation::Subset;
|
||||
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,54 +444,52 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (lp->type == rp->type)
|
||||
return Relation::Coincident;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (auto rs = get<SingletonType>(right))
|
||||
{
|
||||
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;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
if (lp->type == PrimitiveType::Function)
|
||||
{
|
||||
if (get<FunctionType>(right))
|
||||
return Relation::Superset;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
if (lp->type == PrimitiveType::Table)
|
||||
{
|
||||
if (get<TableType>(right))
|
||||
return Relation::Superset;
|
||||
|
||||
return Relation::Disjoint;
|
||||
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;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,11 +499,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (rp->type == PrimitiveType::Function)
|
||||
return Relation::Subset;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
return Relation::Intersects;
|
||||
else
|
||||
return Relation::Intersects;
|
||||
}
|
||||
|
||||
if (auto lt = get<TableType>(left))
|
||||
|
@ -528,11 +512,10 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
{
|
||||
if (rp->type == PrimitiveType::Table)
|
||||
return Relation::Subset;
|
||||
|
||||
return Relation::Disjoint;
|
||||
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,58 +535,29 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
|||
*/
|
||||
if (lt->props.size() > 1 && r == Relation::Superset)
|
||||
return Relation::Intersects;
|
||||
|
||||
return r;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
|
||||
if (1 == lt->props.size())
|
||||
else if (1 == lt->props.size())
|
||||
return flip(relate(right, left, seen));
|
||||
|
||||
return Relation::Intersects;
|
||||
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;
|
||||
|
||||
return Relation::Disjoint;
|
||||
else
|
||||
return Relation::Disjoint;
|
||||
}
|
||||
|
||||
return Relation::Disjoint;
|
||||
|
@ -753,9 +707,7 @@ TypeId TypeSimplifier::intersectUnionWithType(TypeId left, TypeId right)
|
|||
bool changed = false;
|
||||
std::set<TypeId> newParts;
|
||||
|
||||
size_t maxSize = DFInt::LuauSimplificationComplexityLimit;
|
||||
|
||||
if (leftUnion->options.size() > maxSize)
|
||||
if (leftUnion->options.size() > (size_t)DFInt::LuauSimplificationComplexityLimit)
|
||||
return arena->addType(IntersectionType{{left, right}});
|
||||
|
||||
for (TypeId part : leftUnion)
|
||||
|
@ -770,13 +722,6 @@ TypeId TypeSimplifier::intersectUnionWithType(TypeId left, TypeId right)
|
|||
}
|
||||
|
||||
newParts.insert(simplified);
|
||||
|
||||
if (FFlag::LuauSimplificationRecheckAssumption)
|
||||
{
|
||||
// Initial combination size check could not predict nested union iteration
|
||||
if (newParts.size() > maxSize)
|
||||
return arena->addType(IntersectionType{{left, right}});
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
|
@ -817,13 +762,6 @@ TypeId TypeSimplifier::intersectUnions(TypeId left, TypeId right)
|
|||
continue;
|
||||
|
||||
newParts.insert(simplified);
|
||||
|
||||
if (FFlag::LuauSimplificationRecheckAssumption)
|
||||
{
|
||||
// Initial combination size check could not predict nested union iteration
|
||||
if (newParts.size() > maxSize)
|
||||
return arena->addType(IntersectionType{{left, right}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -902,78 +840,6 @@ TypeId TypeSimplifier::intersectNegatedUnion(TypeId left, TypeId right)
|
|||
return intersectFromParts(std::move(newParts));
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) const
|
||||
{
|
||||
target = follow(target);
|
||||
|
||||
if (is<UnknownType>(target))
|
||||
return builtinTypes->truthyType;
|
||||
|
||||
if (is<AnyType>(target))
|
||||
// any = *error-type* | unknown, so truthy & any = *error-type* | truthy
|
||||
return arena->addType(UnionType{{builtinTypes->truthyType, builtinTypes->errorType}});
|
||||
|
||||
if (is<NeverType, ErrorType>(target))
|
||||
return target;
|
||||
|
||||
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||
return target;
|
||||
|
||||
if (auto pt = get<PrimitiveType>(target))
|
||||
{
|
||||
switch (pt->type)
|
||||
{
|
||||
case PrimitiveType::NilType:
|
||||
return builtinTypes->neverType;
|
||||
case PrimitiveType::Boolean:
|
||||
return builtinTypes->trueType;
|
||||
default:
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto st = get<SingletonType>(target))
|
||||
return st->variant == BooleanSingleton{false} ? builtinTypes->neverType : target;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) const
|
||||
{
|
||||
target = follow(target);
|
||||
|
||||
if (is<NeverType, ErrorType>(target))
|
||||
return target;
|
||||
|
||||
if (is<AnyType>(target))
|
||||
// any = *error-type* | unknown, so falsy & any = *error-type* | falsy
|
||||
return arena->addType(UnionType{{builtinTypes->falsyType, builtinTypes->errorType}});
|
||||
|
||||
if (is<UnknownType>(target))
|
||||
return builtinTypes->falsyType;
|
||||
|
||||
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
|
||||
return builtinTypes->neverType;
|
||||
|
||||
if (auto pt = get<PrimitiveType>(target))
|
||||
{
|
||||
switch (pt->type)
|
||||
{
|
||||
case PrimitiveType::NilType:
|
||||
return builtinTypes->nilType;
|
||||
case PrimitiveType::Boolean:
|
||||
return builtinTypes->falseType;
|
||||
default:
|
||||
return builtinTypes->neverType;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto st = get<SingletonType>(target))
|
||||
return st->variant == BooleanSingleton{false} ? builtinTypes->falseType : builtinTypes->neverType;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
|
||||
{
|
||||
const NegationType* leftNegation = get<NegationType>(left);
|
||||
|
@ -1200,8 +1066,11 @@ TypeId TypeSimplifier::intersectIntersectionWithType(TypeId left, TypeId right)
|
|||
|
||||
std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
||||
{
|
||||
left = follow(left);
|
||||
right = follow(right);
|
||||
if (FFlag::LuauFlagBasicIntersectFollows)
|
||||
{
|
||||
left = follow(left);
|
||||
right = follow(right);
|
||||
}
|
||||
|
||||
if (get<AnyType>(left) && get<ErrorType>(right))
|
||||
return right;
|
||||
|
@ -1310,25 +1179,6 @@ std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (FFlag::LuauOptimizeFalsyAndTruthyIntersect)
|
||||
{
|
||||
if (isTruthyType(left))
|
||||
if (auto res = basicIntersectWithTruthy(right))
|
||||
return res;
|
||||
|
||||
if (isTruthyType(right))
|
||||
if (auto res = basicIntersectWithTruthy(left))
|
||||
return res;
|
||||
|
||||
if (isFalsyType(left))
|
||||
if (auto res = basicIntersectWithFalsy(right))
|
||||
return res;
|
||||
|
||||
if (isFalsyType(right))
|
||||
if (auto res = basicIntersectWithFalsy(left))
|
||||
return res;
|
||||
}
|
||||
|
||||
Relation relation = relate(left, right);
|
||||
if (left == right || Relation::Coincident == relation)
|
||||
return left;
|
||||
|
|
|
@ -2,23 +2,24 @@
|
|||
#include "Luau/Substitution.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/Type.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
||||
LUAU_FASTFLAG(LuauSyntheticErrors)
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
||||
static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool alwaysClone)
|
||||
{
|
||||
auto go = [ty, &dest](auto&& a)
|
||||
auto go = [ty, &dest, alwaysClone](auto&& a)
|
||||
{
|
||||
using T = std::decay_t<decltype(a)>;
|
||||
|
||||
|
@ -94,15 +95,13 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
|||
return dest.addType(a);
|
||||
else if constexpr (std::is_same_v<T, FunctionType>)
|
||||
{
|
||||
FunctionType clone = FunctionType{a.level, a.argTypes, a.retTypes, a.definition, a.hasSelf};
|
||||
FunctionType clone = FunctionType{a.level, a.scope, a.argTypes, a.retTypes, a.definition, a.hasSelf};
|
||||
clone.generics = a.generics;
|
||||
clone.genericPacks = a.genericPacks;
|
||||
clone.magic = a.magic;
|
||||
clone.tags = a.tags;
|
||||
clone.argNames = a.argNames;
|
||||
clone.isCheckedFunction = a.isCheckedFunction;
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
clone.isDeprecatedFunction = a.isDeprecatedFunction;
|
||||
return dest.addType(std::move(clone));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TableType>)
|
||||
|
@ -136,10 +135,15 @@ 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};
|
||||
return dest.addType(std::move(clone));
|
||||
if (alwaysClone)
|
||||
{
|
||||
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
|
||||
return ty;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, NegationType>)
|
||||
return dest.addType(NegationType{a.ty});
|
||||
|
@ -252,21 +256,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))
|
||||
|
@ -540,27 +544,6 @@ void Tarjan::visitSCC(int index)
|
|||
}
|
||||
}
|
||||
|
||||
bool Tarjan::ignoreChildren(TypeId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tarjan::ignoreChildren(TypePackId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
||||
bool Tarjan::ignoreChildrenVisit(TypeId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
bool Tarjan::ignoreChildrenVisit(TypePackId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
TarjanResult Tarjan::findDirty(TypeId ty)
|
||||
{
|
||||
return visitRoot(ty);
|
||||
|
@ -571,11 +554,6 @@ TarjanResult Tarjan::findDirty(TypePackId tp)
|
|||
return visitRoot(tp);
|
||||
}
|
||||
|
||||
Substitution::Substitution(TypeArena* arena)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
{
|
||||
}
|
||||
|
||||
Substitution::Substitution(const TxnLog* log_, TypeArena* arena)
|
||||
: arena(arena)
|
||||
{
|
||||
|
@ -676,7 +654,7 @@ void Substitution::resetState(const TxnLog* log, TypeArena* arena)
|
|||
|
||||
TypeId Substitution::clone(TypeId ty)
|
||||
{
|
||||
return shallowClone(ty, *arena, log);
|
||||
return shallowClone(ty, *arena, log, /* alwaysClone */ true);
|
||||
}
|
||||
|
||||
TypePackId Substitution::clone(TypePackId tp)
|
||||
|
@ -838,21 +816,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))
|
||||
|
@ -892,13 +870,4 @@ void Substitution::replaceChildren(TypePackId tp)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Ty>
|
||||
std::optional<Ty> Substitution::replace(std::optional<Ty> ty)
|
||||
{
|
||||
if (ty)
|
||||
return replace(*ty);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -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"
|
||||
|
@ -20,8 +22,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingFixTailPack)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -31,7 +32,7 @@ struct VarianceFlipper
|
|||
Subtyping::Variance* variance;
|
||||
Subtyping::Variance oldValue;
|
||||
|
||||
explicit VarianceFlipper(Subtyping::Variance* v)
|
||||
VarianceFlipper(Subtyping::Variance* v)
|
||||
: variance(v)
|
||||
, oldValue(*v)
|
||||
{
|
||||
|
@ -99,9 +100,6 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
|||
else
|
||||
result.insert(r);
|
||||
}
|
||||
|
||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const SubtypingReasoning& r : b)
|
||||
|
@ -118,9 +116,6 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
|||
else
|
||||
result.insert(r);
|
||||
}
|
||||
|
||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -313,7 +308,7 @@ struct ApplyMappedGenerics : Substitution
|
|||
|
||||
bool ignoreChildren(TypeId ty) override
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return ty->persistent;
|
||||
|
@ -421,14 +416,6 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope
|
|||
|
||||
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
||||
|
||||
if (result.normalizationTooComplex)
|
||||
{
|
||||
if (result.isCacheable)
|
||||
resultCache[{subTy, superTy}] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const auto& [subTy, bounds] : env.mappedGenerics)
|
||||
{
|
||||
const auto& lb = bounds.lowerBound;
|
||||
|
@ -606,12 +593,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
|
||||
if (semantic.normalizationTooComplex)
|
||||
{
|
||||
result = semantic;
|
||||
}
|
||||
else if (semantic.isSubtype)
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
semantic.reasoning.clear();
|
||||
result = semantic;
|
||||
|
@ -626,12 +608,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
|
||||
if (semantic.normalizationTooComplex)
|
||||
{
|
||||
result = semantic;
|
||||
}
|
||||
else if (semantic.isSubtype)
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
// Clear the semantic reasoning, as any reasonings within
|
||||
// potentially contain invalid paths.
|
||||
|
@ -742,9 +719,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);
|
||||
|
@ -777,8 +754,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||
// Match head types pairwise
|
||||
|
||||
for (size_t i = 0; i < headSize; ++i)
|
||||
results.push_back(isCovariantWith(env, subHead[i], superHead[i], scope).withBothComponent(TypePath::Index{i, TypePath::Index::Variant::Pack})
|
||||
);
|
||||
results.push_back(isCovariantWith(env, subHead[i], superHead[i], scope).withBothComponent(TypePath::Index{i}));
|
||||
|
||||
// Handle mismatched head sizes
|
||||
|
||||
|
@ -791,7 +767,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||
for (size_t i = headSize; i < superHead.size(); ++i)
|
||||
results.push_back(isCovariantWith(env, vt->ty, superHead[i], scope)
|
||||
.withSubPath(TypePath::PathBuilder().tail().variadic().build())
|
||||
.withSuperComponent(TypePath::Index{i, TypePath::Index::Variant::Pack}));
|
||||
.withSuperComponent(TypePath::Index{i}));
|
||||
}
|
||||
else if (auto gt = get<GenericTypePack>(*subTail))
|
||||
{
|
||||
|
@ -845,7 +821,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||
{
|
||||
for (size_t i = headSize; i < subHead.size(); ++i)
|
||||
results.push_back(isCovariantWith(env, subHead[i], vt->ty, scope)
|
||||
.withSubComponent(TypePath::Index{i, TypePath::Index::Variant::Pack})
|
||||
.withSubComponent(TypePath::Index{i})
|
||||
.withSuperPath(TypePath::PathBuilder().tail().variadic().build()));
|
||||
}
|
||||
else if (auto gt = get<GenericTypePack>(*superTail))
|
||||
|
@ -883,7 +859,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||
else
|
||||
return SubtypingResult{false}
|
||||
.withSuperComponent(TypePath::PackField::Tail)
|
||||
.withError({scope->location, UnexpectedTypePackInSubtyping{*superTail}});
|
||||
.withError({scope->location, UnexpectedTypePackInSubtyping{FFlag::LuauSubtypingFixTailPack ? *superTail : *subTail}});
|
||||
}
|
||||
else
|
||||
return {false};
|
||||
|
@ -1106,10 +1082,6 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
for (TypeId ty : superUnion)
|
||||
{
|
||||
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
||||
|
||||
if (next.normalizationTooComplex)
|
||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||
|
||||
if (next.isSubtype)
|
||||
return SubtypingResult{true};
|
||||
}
|
||||
|
@ -1128,13 +1100,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Unio
|
|||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : subUnion)
|
||||
{
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Union}));
|
||||
|
||||
if (subtypings.back().normalizationTooComplex)
|
||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||
}
|
||||
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::all(subtypings);
|
||||
}
|
||||
|
||||
|
@ -1144,13 +1110,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : superIntersection)
|
||||
{
|
||||
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||
|
||||
if (subtypings.back().normalizationTooComplex)
|
||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||
}
|
||||
|
||||
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::all(subtypings);
|
||||
}
|
||||
|
||||
|
@ -1160,13 +1120,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Inte
|
|||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : subIntersection)
|
||||
{
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||
|
||||
if (subtypings.back().normalizationTooComplex)
|
||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||
}
|
||||
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::any(subtypings);
|
||||
}
|
||||
|
||||
|
@ -1334,7 +1288,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 +1296,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");
|
||||
|
@ -1456,7 +1410,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
|||
// of the supertype table.
|
||||
//
|
||||
// There's a flaw here in that if the __index metamethod contributes a new
|
||||
// field that would satisfy the subtyping relationship, we'll erroneously say
|
||||
// field that would satisfy the subtyping relationship, we'll erronously say
|
||||
// that the metatable isn't a subtype of the table, even though they have
|
||||
// compatible properties/shapes. We'll revisit this later when we have a
|
||||
// better understanding of how important this is.
|
||||
|
@ -1469,15 +1423,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 +1443,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 +1613,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 +1630,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 +1662,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;
|
||||
|
@ -1806,12 +1760,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
|||
{
|
||||
results.emplace_back();
|
||||
for (TypeId superTy : superTypes)
|
||||
{
|
||||
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
||||
|
||||
if (results.back().normalizationTooComplex)
|
||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
||||
}
|
||||
}
|
||||
|
||||
return SubtypingResult::all(results);
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Unifier2.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalFailsafe)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDontInPlaceMutateTableType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowNonSharedTableTypesInLiteral)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceUpcast)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -138,33 +138,27 @@ TypeId matchLiteralType(
|
|||
* things like replace explicit named properties with indexers as required
|
||||
* by the expected type.
|
||||
*/
|
||||
|
||||
if (!isLiteral(expr))
|
||||
{
|
||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
||||
return result.isSubtype ? expectedType : exprType;
|
||||
if (FFlag::LuauBidirectionalInferenceUpcast)
|
||||
{
|
||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
||||
return result.isSubtype
|
||||
? expectedType
|
||||
: exprType;
|
||||
}
|
||||
else
|
||||
return exprType;
|
||||
}
|
||||
|
||||
expectedType = follow(expectedType);
|
||||
exprType = follow(exprType);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
if (get<AnyType>(expectedType) || get<UnknownType>(expectedType))
|
||||
{
|
||||
// The intent of `matchLiteralType` is to upcast values when it's safe
|
||||
// to do so. it's always safe to upcast to `any` or `unknown`, so we
|
||||
// can unconditionally do so here.
|
||||
if (is<AnyType, UnknownType>(expectedType))
|
||||
return expectedType;
|
||||
// "Narrowing" to unknown or any is not going to do anything useful.
|
||||
return exprType;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<AnyType>(expectedType) || get<UnknownType>(expectedType))
|
||||
{
|
||||
// "Narrowing" to unknown or any is not going to do anything useful.
|
||||
return exprType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (expr->is<AstExprConstantString>())
|
||||
{
|
||||
|
@ -233,26 +227,19 @@ TypeId matchLiteralType(
|
|||
}
|
||||
|
||||
|
||||
if (expr->is<AstExprFunction>())
|
||||
if (FFlag::LuauBidirectionalInferenceUpcast && expr->is<AstExprFunction>())
|
||||
{
|
||||
// TODO: Push argument / return types into the lambda. For now, just do
|
||||
// TODO: Push argument / return types into the lambda. For now, just do
|
||||
// the non-literal thing: check for a subtype and upcast if valid.
|
||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
||||
return result.isSubtype ? expectedType : exprType;
|
||||
return result.isSubtype
|
||||
? expectedType
|
||||
: exprType;
|
||||
}
|
||||
|
||||
if (auto exprTable = expr->as<AstExprTable>())
|
||||
{
|
||||
TableType* const tableTy = getMutable<TableType>(exprType);
|
||||
|
||||
// This can occur if we have an expression like:
|
||||
//
|
||||
// { x = {}, x = 42 }
|
||||
//
|
||||
// The type of this will be `{ x: number }`
|
||||
if (FFlag::LuauBidirectionalFailsafe && !tableTy)
|
||||
return exprType;
|
||||
|
||||
LUAU_ASSERT(tableTy);
|
||||
|
||||
const TableType* expectedTableTy = get<TableType>(expectedType);
|
||||
|
@ -279,9 +266,6 @@ TypeId matchLiteralType(
|
|||
|
||||
DenseHashSet<AstExprConstantString*> keysToDelete{nullptr};
|
||||
|
||||
DenseHashSet<TypeId> indexerKeyTypes{nullptr};
|
||||
DenseHashSet<TypeId> indexerValueTypes{nullptr};
|
||||
|
||||
for (const AstExprTable::Item& item : exprTable->items)
|
||||
{
|
||||
if (isRecord(item))
|
||||
|
@ -289,20 +273,23 @@ TypeId matchLiteralType(
|
|||
const AstArray<char>& s = item.key->as<AstExprConstantString>()->value;
|
||||
std::string keyStr{s.data, s.data + s.size};
|
||||
auto it = tableTy->props.find(keyStr);
|
||||
|
||||
// This can occur, potentially, if we are re-entrant.
|
||||
if (FFlag::LuauBidirectionalFailsafe && it == tableTy->props.end())
|
||||
continue;
|
||||
|
||||
LUAU_ASSERT(it != tableTy->props.end());
|
||||
|
||||
Property& prop = it->second;
|
||||
|
||||
// If we encounter a duplcate property, we may have already
|
||||
// set it to be read-only. If that's the case, the only thing
|
||||
// that will definitely crash is trying to access a write
|
||||
// only property.
|
||||
LUAU_ASSERT(!prop.isWriteOnly());
|
||||
if (FFlag::LuauAllowNonSharedTableTypesInLiteral)
|
||||
{
|
||||
// If we encounter a duplcate property, we may have already
|
||||
// set it to be read-only. If that's the case, the only thing
|
||||
// that will definitely crash is trying to access a write
|
||||
// only property.
|
||||
LUAU_ASSERT(!prop.isWriteOnly());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Table literals always initially result in shared read-write types
|
||||
LUAU_ASSERT(prop.isShared());
|
||||
}
|
||||
TypeId propTy = *prop.readTy;
|
||||
|
||||
auto it2 = expectedTableTy->props.find(keyStr);
|
||||
|
@ -330,20 +317,16 @@ TypeId matchLiteralType(
|
|||
toBlock
|
||||
);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(arena->addType(SingletonType{StringSingleton{keyStr}}));
|
||||
indexerValueTypes.insert(matchedType);
|
||||
}
|
||||
if (tableTy->indexer)
|
||||
unifier->unify(matchedType, tableTy->indexer->indexResultType);
|
||||
else
|
||||
{
|
||||
if (tableTy->indexer)
|
||||
unifier->unify(matchedType, tableTy->indexer->indexResultType);
|
||||
else
|
||||
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
||||
}
|
||||
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
||||
|
||||
if (FFlag::LuauDontInPlaceMutateTableType)
|
||||
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
||||
else
|
||||
tableTy->props.erase(keyStr);
|
||||
|
||||
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
||||
}
|
||||
|
||||
// If it's just an extra property and the expected type
|
||||
|
@ -366,25 +349,22 @@ TypeId matchLiteralType(
|
|||
// quadratic in a hurry.
|
||||
if (expectedProp.isShared())
|
||||
{
|
||||
matchedType = matchLiteralType(
|
||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock
|
||||
);
|
||||
matchedType =
|
||||
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock);
|
||||
prop.readTy = matchedType;
|
||||
prop.writeTy = matchedType;
|
||||
}
|
||||
else if (expectedReadTy)
|
||||
{
|
||||
matchedType = matchLiteralType(
|
||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock
|
||||
);
|
||||
matchedType =
|
||||
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock);
|
||||
prop.readTy = matchedType;
|
||||
prop.writeTy.reset();
|
||||
}
|
||||
else if (expectedWriteTy)
|
||||
{
|
||||
matchedType = matchLiteralType(
|
||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedWriteTy, propTy, item.value, toBlock
|
||||
);
|
||||
matchedType =
|
||||
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedWriteTy, propTy, item.value, toBlock);
|
||||
prop.readTy.reset();
|
||||
prop.writeTy = matchedType;
|
||||
}
|
||||
|
@ -401,16 +381,10 @@ TypeId matchLiteralType(
|
|||
LUAU_ASSERT(matchedType);
|
||||
|
||||
(*astExpectedTypes)[item.value] = matchedType;
|
||||
// NOTE: We do *not* add to the potential indexer types here.
|
||||
// I think this is correct to support something like:
|
||||
//
|
||||
// { [string]: number, foo: boolean }
|
||||
//
|
||||
}
|
||||
else if (item.kind == AstExprTable::Item::List)
|
||||
{
|
||||
if (!FFlag::LuauBidirectionalInferenceCollectIndexerTypes || !FFlag::LuauBidirectionalInferenceElideAssert)
|
||||
LUAU_ASSERT(tableTy->indexer);
|
||||
LUAU_ASSERT(tableTy->indexer);
|
||||
|
||||
if (expectedTableTy->indexer)
|
||||
{
|
||||
|
@ -431,17 +405,9 @@ TypeId matchLiteralType(
|
|||
toBlock
|
||||
);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(builtinTypes->numberType);
|
||||
indexerValueTypes.insert(matchedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the index result type is the prop type, we can replace it with the matched type here.
|
||||
if (tableTy->indexer->indexResultType == *propTy)
|
||||
tableTy->indexer->indexResultType = matchedType;
|
||||
}
|
||||
// if the index result type is the prop type, we can replace it with the matched type here.
|
||||
if (tableTy->indexer->indexResultType == *propTy)
|
||||
tableTy->indexer->indexResultType = matchedType;
|
||||
}
|
||||
}
|
||||
else if (item.kind == AstExprTable::Item::General)
|
||||
|
@ -463,22 +429,19 @@ TypeId matchLiteralType(
|
|||
// Populate expected types for non-string keys declared with [] (the code below will handle the case where they are strings)
|
||||
if (!item.key->as<AstExprConstantString>() && expectedTableTy->indexer)
|
||||
(*astExpectedTypes)[item.key] = expectedTableTy->indexer->indexType;
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(tKey);
|
||||
indexerValueTypes.insert(tProp);
|
||||
}
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unexpected");
|
||||
}
|
||||
|
||||
for (const auto& key : keysToDelete)
|
||||
if (FFlag::LuauDontInPlaceMutateTableType)
|
||||
{
|
||||
const AstArray<char>& s = key->value;
|
||||
std::string keyStr{s.data, s.data + s.size};
|
||||
tableTy->props.erase(keyStr);
|
||||
for (const auto& key : keysToDelete)
|
||||
{
|
||||
const AstArray<char>& s = key->value;
|
||||
std::string keyStr{s.data, s.data + s.size};
|
||||
tableTy->props.erase(keyStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Keys that the expectedType says we should have, but that aren't
|
||||
|
@ -530,39 +493,9 @@ TypeId matchLiteralType(
|
|||
// have one too.
|
||||
// TODO: If the expected table also has an indexer, we might want to
|
||||
// push the expected indexer's types into it.
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes && expectedTableTy->indexer)
|
||||
if (expectedTableTy->indexer && !tableTy->indexer)
|
||||
{
|
||||
if (indexerValueTypes.size() > 0 && indexerKeyTypes.size() > 0)
|
||||
{
|
||||
TypeId inferredKeyType = builtinTypes->neverType;
|
||||
TypeId inferredValueType = builtinTypes->neverType;
|
||||
for (auto kt : indexerKeyTypes)
|
||||
{
|
||||
auto simplified = simplifyUnion(builtinTypes, arena, inferredKeyType, kt);
|
||||
inferredKeyType = simplified.result;
|
||||
}
|
||||
for (auto vt : indexerValueTypes)
|
||||
{
|
||||
auto simplified = simplifyUnion(builtinTypes, arena, inferredValueType, vt);
|
||||
inferredValueType = simplified.result;
|
||||
}
|
||||
tableTy->indexer = TableIndexer{inferredKeyType, inferredValueType};
|
||||
auto keyCheck = subtyping->isSubtype(inferredKeyType, expectedTableTy->indexer->indexType, unifier->scope);
|
||||
if (keyCheck.isSubtype)
|
||||
tableTy->indexer->indexType = expectedTableTy->indexer->indexType;
|
||||
auto valueCheck = subtyping->isSubtype(inferredValueType, expectedTableTy->indexer->indexResultType, unifier->scope);
|
||||
if (valueCheck.isSubtype)
|
||||
tableTy->indexer->indexResultType = expectedTableTy->indexer->indexResultType;
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(indexerKeyTypes.empty() && indexerValueTypes.empty());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expectedTableTy->indexer && !tableTy->indexer)
|
||||
{
|
||||
tableTy->indexer = expectedTableTy->indexer;
|
||||
}
|
||||
tableTy->indexer = expectedTableTy->indexer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,11 +19,8 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias)
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
|
||||
|
||||
/*
|
||||
* Enables increasing levels of verbosity for Luau type names when stringifying.
|
||||
|
@ -123,7 +120,7 @@ struct FindCyclicTypes final : TypeVisitor
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -304,28 +301,6 @@ struct StringifierState
|
|||
emit(std::to_string(i).c_str());
|
||||
}
|
||||
|
||||
void emit(Polarity p)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case Polarity::None:
|
||||
emit(" ");
|
||||
break;
|
||||
case Polarity::Negative:
|
||||
emit(" -");
|
||||
break;
|
||||
case Polarity::Positive:
|
||||
emit("+ ");
|
||||
break;
|
||||
case Polarity::Mixed:
|
||||
emit("+-");
|
||||
break;
|
||||
default:
|
||||
emit("!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void indent()
|
||||
{
|
||||
indentation += 4;
|
||||
|
@ -507,8 +482,6 @@ struct TypeStringifier
|
|||
{
|
||||
state.emit("'");
|
||||
state.emit(state.getName(ty));
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(ftv.polarity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -521,9 +494,6 @@ struct TypeStringifier
|
|||
state.emit("'");
|
||||
state.emit(state.getName(ty));
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(ftv.polarity);
|
||||
|
||||
if (!get<UnknownType>(upperBound))
|
||||
{
|
||||
state.emit(" <: ");
|
||||
|
@ -539,9 +509,6 @@ struct TypeStringifier
|
|||
|
||||
state.emit(state.getName(ty));
|
||||
|
||||
if (FFlag::LuauSolverV2 && FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(ftv.polarity);
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||
{
|
||||
state.emit("-");
|
||||
|
@ -571,9 +538,6 @@ struct TypeStringifier
|
|||
else
|
||||
state.emit(state.getName(ty));
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(gtv.polarity);
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||
{
|
||||
state.emit("-");
|
||||
|
@ -722,13 +686,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 +709,6 @@ struct TypeStringifier
|
|||
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.exhaustive)
|
||||
{
|
||||
if (ttv.syntheticName)
|
||||
{
|
||||
state.result.invalid = true;
|
||||
|
@ -893,9 +847,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&)
|
||||
|
@ -923,9 +877,6 @@ struct TypeStringifier
|
|||
bool hasNonNilDisjunct = false;
|
||||
|
||||
std::vector<std::string> results = {};
|
||||
size_t resultsLength = 0;
|
||||
bool lengthLimitHit = false;
|
||||
|
||||
for (auto el : &uv)
|
||||
{
|
||||
el = follow(el);
|
||||
|
@ -952,34 +903,14 @@ struct TypeStringifier
|
|||
if (needParens)
|
||||
state.emit(")");
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
resultsLength += state.result.name.length();
|
||||
|
||||
results.push_back(std::move(state.result.name));
|
||||
|
||||
state.result.name = std::move(saved);
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
{
|
||||
lengthLimitHit = state.opts.maxTypeLength > 0 && resultsLength > state.opts.maxTypeLength;
|
||||
|
||||
if (lengthLimitHit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.unsee(&uv);
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
{
|
||||
if (!lengthLimitHit && !FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
}
|
||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
|
||||
if (optional && results.size() > 1)
|
||||
state.emit("(");
|
||||
|
@ -1023,9 +954,6 @@ struct TypeStringifier
|
|||
}
|
||||
|
||||
std::vector<std::string> results = {};
|
||||
size_t resultsLength = 0;
|
||||
bool lengthLimitHit = false;
|
||||
|
||||
for (auto el : uv.parts)
|
||||
{
|
||||
el = follow(el);
|
||||
|
@ -1042,34 +970,14 @@ struct TypeStringifier
|
|||
if (needParens)
|
||||
state.emit(")");
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
resultsLength += state.result.name.length();
|
||||
|
||||
results.push_back(std::move(state.result.name));
|
||||
|
||||
state.result.name = std::move(saved);
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
{
|
||||
lengthLimitHit = state.opts.maxTypeLength > 0 && resultsLength > state.opts.maxTypeLength;
|
||||
|
||||
if (lengthLimitHit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.unsee(&uv);
|
||||
|
||||
if (FFlag::LuauStringPartLengthLimit)
|
||||
{
|
||||
if (!lengthLimitHit && !FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
}
|
||||
if (!FFlag::DebugLuauToStringNoLexicalSort)
|
||||
std::sort(results.begin(), results.end());
|
||||
|
||||
bool first = true;
|
||||
bool shouldPlaceOnNewlines = results.size() > state.opts.compositeTypesSingleLineLimit || isOverloadedFunction(ty);
|
||||
|
@ -1314,9 +1222,6 @@ struct TypePackStringifier
|
|||
state.emit(state.getName(tp));
|
||||
}
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(pack.polarity);
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||
{
|
||||
state.emit("-");
|
||||
|
@ -1336,9 +1241,6 @@ struct TypePackStringifier
|
|||
state.emit("free-");
|
||||
state.emit(state.getName(tp));
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
||||
state.emit(pack.polarity);
|
||||
|
||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||
{
|
||||
state.emit("-");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
#include <limits>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup2)
|
||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -167,7 +166,7 @@ struct StringWriter : Writer
|
|||
|
||||
void symbol(std::string_view s) override
|
||||
{
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
if (FFlag::LuauStoreCSTData)
|
||||
{
|
||||
write(s);
|
||||
}
|
||||
|
@ -257,7 +256,7 @@ public:
|
|||
first = !first;
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauStoreCSTData2 && commaPosition)
|
||||
if (FFlag::LuauStoreCSTData && commaPosition)
|
||||
{
|
||||
writer.advance(*commaPosition);
|
||||
commaPosition++;
|
||||
|
@ -272,43 +271,6 @@ private:
|
|||
const Position* commaPosition;
|
||||
};
|
||||
|
||||
class ArgNameInserter
|
||||
{
|
||||
public:
|
||||
ArgNameInserter(Writer& w, AstArray<std::optional<AstArgumentName>> names, AstArray<std::optional<Position>> colonPositions)
|
||||
: writer(w)
|
||||
, names(names)
|
||||
, colonPositions(colonPositions)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
if (idx < names.size)
|
||||
{
|
||||
const auto name = names.data[idx];
|
||||
if (name.has_value())
|
||||
{
|
||||
writer.advance(name->second.begin);
|
||||
writer.identifier(name->first.value);
|
||||
if (idx < colonPositions.size)
|
||||
{
|
||||
LUAU_ASSERT(colonPositions.data[idx].has_value());
|
||||
writer.advance(*colonPositions.data[idx]);
|
||||
}
|
||||
writer.symbol(":");
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
private:
|
||||
Writer& writer;
|
||||
AstArray<std::optional<AstArgumentName>> names;
|
||||
AstArray<std::optional<Position>> colonPositions;
|
||||
size_t idx = 0;
|
||||
};
|
||||
|
||||
struct Printer_DEPRECATED
|
||||
{
|
||||
explicit Printer_DEPRECATED(Writer& writer)
|
||||
|
@ -331,7 +293,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 +311,7 @@ struct Printer_DEPRECATED
|
|||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||
{
|
||||
LUAU_ASSERT(!forVarArg);
|
||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||
visualizeTypeList(explicitTp->typeList, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -368,7 +330,7 @@ struct Printer_DEPRECATED
|
|||
else if (typeCount == 1)
|
||||
{
|
||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
writer.symbol("(");
|
||||
|
||||
// Only variadic tail
|
||||
|
@ -381,7 +343,7 @@ struct Printer_DEPRECATED
|
|||
visualizeTypeAnnotation(*list.types.data[0]);
|
||||
}
|
||||
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
writer.symbol(")");
|
||||
}
|
||||
else
|
||||
|
@ -705,6 +667,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 +1027,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 +1136,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>())
|
||||
{
|
||||
|
@ -1233,18 +1191,9 @@ struct Printer_DEPRECATED
|
|||
AstType* l = a->types.data[0];
|
||||
AstType* r = a->types.data[1];
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
}
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
|
||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||
auto rta = r->as<AstTypeReference>();
|
||||
|
@ -1267,15 +1216,6 @@ struct Printer_DEPRECATED
|
|||
|
||||
for (size_t i = 0; i < a->types.size; ++i)
|
||||
{
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
if (a->types.data[i]->is<AstTypeOptional>())
|
||||
{
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
writer.maybeSpace(a->types.data[i]->location.begin, 2);
|
||||
|
@ -1372,7 +1312,7 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
void visualizeTypePackAnnotation(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>())
|
||||
|
@ -1382,22 +1322,15 @@ struct Printer
|
|||
|
||||
visualizeTypeAnnotation(*variadicTp->variadicType);
|
||||
}
|
||||
else if (AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
|
||||
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
|
||||
{
|
||||
writer.symbol(genericTp->genericName.value);
|
||||
if (const auto cstNode = lookupCstNode<CstTypePackGeneric>(genericTp))
|
||||
advance(cstNode->ellipsisPosition);
|
||||
writer.symbol("...");
|
||||
}
|
||||
else if (AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
|
||||
{
|
||||
LUAU_ASSERT(!forVarArg);
|
||||
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
||||
visualizeTypeList(
|
||||
explicitTp->typeList, FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
||||
);
|
||||
else
|
||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||
visualizeTypeList(explicitTp->typeList, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1405,37 +1338,19 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
void visualizeNamedTypeList(
|
||||
const AstTypeList& list,
|
||||
bool unconditionallyParenthesize,
|
||||
std::optional<Position> openParenthesesPosition,
|
||||
std::optional<Position> closeParenthesesPosition,
|
||||
AstArray<Position> commaPositions,
|
||||
AstArray<std::optional<AstArgumentName>> argNames,
|
||||
AstArray<std::optional<Position>> argNamesColonPositions
|
||||
)
|
||||
void visualizeTypeList(const AstTypeList& list, bool unconditionallyParenthesize)
|
||||
{
|
||||
size_t typeCount = list.types.size + (list.tailType != nullptr ? 1 : 0);
|
||||
if (typeCount == 0)
|
||||
{
|
||||
if (openParenthesesPosition)
|
||||
advance(*openParenthesesPosition);
|
||||
writer.symbol("(");
|
||||
if (closeParenthesesPosition)
|
||||
advance(*closeParenthesesPosition);
|
||||
writer.symbol(")");
|
||||
}
|
||||
else if (typeCount == 1)
|
||||
{
|
||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
{
|
||||
if (openParenthesesPosition)
|
||||
advance(*openParenthesesPosition);
|
||||
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
writer.symbol("(");
|
||||
}
|
||||
|
||||
ArgNameInserter(writer, argNames, argNamesColonPositions)();
|
||||
|
||||
// Only variadic tail
|
||||
if (list.types.size == 0)
|
||||
|
@ -1447,51 +1362,34 @@ struct Printer
|
|||
visualizeTypeAnnotation(*list.types.data[0]);
|
||||
}
|
||||
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
{
|
||||
if (closeParenthesesPosition)
|
||||
advance(*closeParenthesesPosition);
|
||||
if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
writer.symbol(")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (openParenthesesPosition)
|
||||
advance(*openParenthesesPosition);
|
||||
writer.symbol("(");
|
||||
|
||||
CommaSeparatorInserter comma(writer, commaPositions.size > 0 ? commaPositions.begin() : nullptr);
|
||||
ArgNameInserter argName(writer, argNames, argNamesColonPositions);
|
||||
bool first = true;
|
||||
for (const auto& el : list.types)
|
||||
{
|
||||
comma();
|
||||
argName();
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
writer.symbol(",");
|
||||
|
||||
visualizeTypeAnnotation(*el);
|
||||
}
|
||||
|
||||
if (list.tailType)
|
||||
{
|
||||
comma();
|
||||
writer.symbol(",");
|
||||
visualizeTypePackAnnotation(*list.tailType, false);
|
||||
}
|
||||
|
||||
if (closeParenthesesPosition)
|
||||
advance(*closeParenthesesPosition);
|
||||
writer.symbol(")");
|
||||
}
|
||||
}
|
||||
|
||||
void visualizeTypeList(
|
||||
const AstTypeList& list,
|
||||
bool unconditionallyParenthesize,
|
||||
std::optional<Position> openParenthesesPosition = std::nullopt,
|
||||
std::optional<Position> closeParenthesesPosition = std::nullopt,
|
||||
AstArray<Position> commaPositions = {}
|
||||
)
|
||||
{
|
||||
visualizeNamedTypeList(list, unconditionallyParenthesize, openParenthesesPosition, closeParenthesesPosition, commaPositions, {}, {});
|
||||
}
|
||||
|
||||
bool isIntegerish(double d)
|
||||
{
|
||||
if (d <= std::numeric_limits<int>::max() && d >= std::numeric_limits<int>::min())
|
||||
|
@ -1502,14 +1400,13 @@ struct Printer
|
|||
|
||||
void visualize(AstExpr& expr)
|
||||
{
|
||||
if (!expr.is<AstExprFunction>() || FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
advance(expr.location.begin);
|
||||
advance(expr.location.begin);
|
||||
|
||||
if (const auto& a = expr.as<AstExprGroup>())
|
||||
{
|
||||
writer.symbol("(");
|
||||
visualize(*a->expr);
|
||||
advanceBefore(a->location.end, 1);
|
||||
advance(Position{a->location.end.line, a->location.end.column - 1});
|
||||
writer.symbol(")");
|
||||
}
|
||||
else if (expr.is<AstExprConstantNil>())
|
||||
|
@ -1637,17 +1534,6 @@ struct Printer
|
|||
}
|
||||
else if (const auto& a = expr.as<AstExprFunction>())
|
||||
{
|
||||
for (const auto& attribute : a->attributes)
|
||||
visualizeAttribute(*attribute);
|
||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
{
|
||||
if (const auto cstNode = lookupCstNode<CstExprFunction>(a))
|
||||
advance(cstNode->functionKeywordPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance(a->location.begin);
|
||||
}
|
||||
writer.keyword("function");
|
||||
visualizeFunctionBody(*a);
|
||||
}
|
||||
|
@ -1889,18 +1775,9 @@ struct Printer
|
|||
writer.advance(newPos);
|
||||
}
|
||||
|
||||
void advanceBefore(const Position& newPos, unsigned int tokenLength)
|
||||
{
|
||||
if (newPos.column >= tokenLength)
|
||||
advance(Position{newPos.line, newPos.column - tokenLength});
|
||||
else
|
||||
advance(newPos);
|
||||
}
|
||||
|
||||
void visualize(AstStat& program)
|
||||
{
|
||||
if ((!program.is<AstStatLocalFunction>() && !program.is<AstStatFunction>()) || FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
advance(program.location.begin);
|
||||
advance(program.location.begin);
|
||||
|
||||
if (const auto& block = program.as<AstStatBlock>())
|
||||
{
|
||||
|
@ -1940,8 +1817,8 @@ struct Printer
|
|||
visualizeBlock(*a->body);
|
||||
if (const auto cstNode = lookupCstNode<CstStatRepeat>(a))
|
||||
writer.advance(cstNode->untilPosition);
|
||||
else
|
||||
advanceBefore(a->condition->location.begin, 6);
|
||||
else if (a->condition->location.begin.column > 5)
|
||||
writer.advance(Position{a->condition->location.begin.line, a->condition->location.begin.column - 6});
|
||||
writer.keyword("until");
|
||||
visualize(*a->condition);
|
||||
}
|
||||
|
@ -2137,36 +2014,13 @@ struct Printer
|
|||
}
|
||||
else if (const auto& a = program.as<AstStatFunction>())
|
||||
{
|
||||
for (const auto& attribute : a->func->attributes)
|
||||
visualizeAttribute(*attribute);
|
||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
{
|
||||
if (const auto cstNode = lookupCstNode<CstStatFunction>(a))
|
||||
advance(cstNode->functionKeywordPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance(a->location.begin);
|
||||
}
|
||||
writer.keyword("function");
|
||||
visualize(*a->name);
|
||||
visualizeFunctionBody(*a->func);
|
||||
}
|
||||
else if (const auto& a = program.as<AstStatLocalFunction>())
|
||||
{
|
||||
for (const auto& attribute : a->func->attributes)
|
||||
visualizeAttribute(*attribute);
|
||||
|
||||
const auto cstNode = lookupCstNode<CstStatLocalFunction>(a);
|
||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
||||
{
|
||||
if (cstNode)
|
||||
advance(cstNode->localKeywordPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance(a->location.begin);
|
||||
}
|
||||
|
||||
writer.keyword("local");
|
||||
|
||||
|
@ -2267,20 +2121,7 @@ struct Printer
|
|||
{
|
||||
if (writeTypes)
|
||||
{
|
||||
const auto cstNode = lookupCstNode<CstStatTypeFunction>(t);
|
||||
if (t->exported)
|
||||
writer.keyword("export");
|
||||
if (cstNode)
|
||||
advance(cstNode->typeKeywordPosition);
|
||||
else
|
||||
writer.space();
|
||||
writer.keyword("type");
|
||||
if (cstNode)
|
||||
advance(cstNode->functionKeywordPosition);
|
||||
else
|
||||
writer.space();
|
||||
writer.keyword("function");
|
||||
advance(t->nameLocation.begin);
|
||||
writer.keyword("type function");
|
||||
writer.identifier(t->name.value);
|
||||
visualizeFunctionBody(*t->body);
|
||||
}
|
||||
|
@ -2310,21 +2151,17 @@ struct Printer
|
|||
|
||||
if (program.hasSemicolon)
|
||||
{
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
advanceBefore(program.location.end, 1);
|
||||
if (FFlag::LuauStoreCSTData)
|
||||
advance(Position{program.location.end.line, program.location.end.column - 1});
|
||||
writer.symbol(";");
|
||||
}
|
||||
}
|
||||
|
||||
void visualizeFunctionBody(AstExprFunction& func)
|
||||
{
|
||||
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
||||
|
||||
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
||||
if (cstNode)
|
||||
advance(cstNode->openGenericsPosition);
|
||||
CommaSeparatorInserter comma(writer);
|
||||
writer.symbol("<");
|
||||
for (const auto& o : func.generics)
|
||||
{
|
||||
|
@ -2339,19 +2176,13 @@ struct Printer
|
|||
|
||||
writer.advance(o->location.begin);
|
||||
writer.identifier(o->name.value);
|
||||
if (const auto* genericTypePackCstNode = lookupCstNode<CstGenericTypePack>(o))
|
||||
advance(genericTypePackCstNode->ellipsisPosition);
|
||||
writer.symbol("...");
|
||||
}
|
||||
if (cstNode)
|
||||
advance(cstNode->closeGenericsPosition);
|
||||
writer.symbol(">");
|
||||
}
|
||||
|
||||
if (func.argLocation)
|
||||
advance(func.argLocation->begin);
|
||||
writer.symbol("(");
|
||||
CommaSeparatorInserter comma(writer, cstNode ? cstNode->argsCommaPositions.begin() : nullptr);
|
||||
CommaSeparatorInserter comma(writer);
|
||||
|
||||
for (size_t i = 0; i < func.args.size; ++i)
|
||||
{
|
||||
|
@ -2381,27 +2212,14 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
if (func.argLocation)
|
||||
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);
|
||||
|
@ -2483,23 +2301,6 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
void visualizeAttribute(AstAttr& attribute)
|
||||
{
|
||||
advance(attribute.location.begin);
|
||||
switch (attribute.type)
|
||||
{
|
||||
case AstAttr::Checked:
|
||||
writer.keyword("@checked");
|
||||
break;
|
||||
case AstAttr::Native:
|
||||
writer.keyword("@native");
|
||||
break;
|
||||
case AstAttr::Deprecated:
|
||||
writer.keyword("@deprecated");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void visualizeTypeAnnotation(AstType& typeAnnotation)
|
||||
{
|
||||
advance(typeAnnotation.location.begin);
|
||||
|
@ -2539,13 +2340,9 @@ struct Printer
|
|||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeFunction>())
|
||||
{
|
||||
const auto cstNode = lookupCstNode<CstTypeFunction>(a);
|
||||
|
||||
if (a->generics.size > 0 || a->genericPacks.size > 0)
|
||||
{
|
||||
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
||||
if (cstNode)
|
||||
advance(cstNode->openGenericsPosition);
|
||||
CommaSeparatorInserter comma(writer);
|
||||
writer.symbol("<");
|
||||
for (const auto& o : a->generics)
|
||||
{
|
||||
|
@ -2560,34 +2357,17 @@ struct Printer
|
|||
|
||||
writer.advance(o->location.begin);
|
||||
writer.identifier(o->name.value);
|
||||
if (const auto* genericTypePackCstNode = lookupCstNode<CstGenericTypePack>(o))
|
||||
advance(genericTypePackCstNode->ellipsisPosition);
|
||||
writer.symbol("...");
|
||||
}
|
||||
if (cstNode)
|
||||
advance(cstNode->closeGenericsPosition);
|
||||
writer.symbol(">");
|
||||
}
|
||||
|
||||
{
|
||||
visualizeNamedTypeList(
|
||||
a->argTypes,
|
||||
true,
|
||||
cstNode ? std::make_optional(cstNode->openArgsPosition) : std::nullopt,
|
||||
cstNode ? std::make_optional(cstNode->closeArgsPosition) : std::nullopt,
|
||||
cstNode ? cstNode->argumentsCommaPositions : Luau::AstArray<Position>{},
|
||||
a->argNames,
|
||||
cstNode ? cstNode->argumentNameColonPositions : Luau::AstArray<std::optional<Position>>{}
|
||||
);
|
||||
visualizeTypeList(a->argTypes, true);
|
||||
}
|
||||
|
||||
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>())
|
||||
{
|
||||
|
@ -2660,7 +2440,6 @@ struct Printer
|
|||
{
|
||||
advance(item.indexerOpenPosition);
|
||||
writer.symbol("[");
|
||||
advance(item.stringPosition);
|
||||
writer.sourceString(
|
||||
std::string_view(item.stringInfo->sourceString.data, item.stringInfo->sourceString.size),
|
||||
item.stringInfo->quoteStyle,
|
||||
|
@ -2748,25 +2527,14 @@ struct Printer
|
|||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeUnion>())
|
||||
{
|
||||
const auto cstNode = lookupCstNode<CstTypeUnion>(a);
|
||||
|
||||
if (!cstNode && a->types.size == 2)
|
||||
if (a->types.size == 2)
|
||||
{
|
||||
AstType* l = a->types.data[0];
|
||||
AstType* r = a->types.data[1];
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
}
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
|
||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||
auto rta = r->as<AstTypeReference>();
|
||||
|
@ -2787,39 +2555,15 @@ struct Printer
|
|||
}
|
||||
}
|
||||
|
||||
if (cstNode && cstNode->leadingPosition)
|
||||
{
|
||||
advance(*cstNode->leadingPosition);
|
||||
writer.symbol("|");
|
||||
}
|
||||
|
||||
size_t separatorIndex = 0;
|
||||
for (size_t i = 0; i < a->types.size; ++i)
|
||||
{
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
if (const auto optional = a->types.data[i]->as<AstTypeOptional>())
|
||||
{
|
||||
advance(optional->location.begin);
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (cstNode && FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
// separatorIndex is only valid if `?` is handled as an AstTypeOptional
|
||||
advance(cstNode->separatorPositions.data[separatorIndex]);
|
||||
separatorIndex++;
|
||||
}
|
||||
else
|
||||
writer.maybeSpace(a->types.data[i]->location.begin, 2);
|
||||
writer.maybeSpace(a->types.data[i]->location.begin, 2);
|
||||
writer.symbol("|");
|
||||
}
|
||||
|
||||
bool wrap = !cstNode && (a->types.data[i]->as<AstTypeIntersection>() || a->types.data[i]->as<AstTypeFunction>());
|
||||
bool wrap = a->types.data[i]->as<AstTypeIntersection>() || a->types.data[i]->as<AstTypeFunction>();
|
||||
|
||||
if (wrap)
|
||||
writer.symbol("(");
|
||||
|
@ -2832,27 +2576,15 @@ struct Printer
|
|||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
|
||||
{
|
||||
const auto cstNode = lookupCstNode<CstTypeIntersection>(a);
|
||||
|
||||
// If the sizes are equal, we know there is a leading & token
|
||||
if (cstNode && cstNode->leadingPosition)
|
||||
{
|
||||
advance(*cstNode->leadingPosition);
|
||||
writer.symbol("&");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < a->types.size; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
if (cstNode)
|
||||
advance(cstNode->separatorPositions.data[i - 1]);
|
||||
else
|
||||
writer.maybeSpace(a->types.data[i]->location.begin, 2);
|
||||
writer.maybeSpace(a->types.data[i]->location.begin, 2);
|
||||
writer.symbol("&");
|
||||
}
|
||||
|
||||
bool wrap = !cstNode && (a->types.data[i]->as<AstTypeUnion>() || a->types.data[i]->as<AstTypeFunction>());
|
||||
bool wrap = a->types.data[i]->as<AstTypeUnion>() || a->types.data[i]->as<AstTypeFunction>();
|
||||
|
||||
if (wrap)
|
||||
writer.symbol("(");
|
||||
|
@ -2867,7 +2599,7 @@ struct Printer
|
|||
{
|
||||
writer.symbol("(");
|
||||
visualizeTypeAnnotation(*a->type);
|
||||
advanceBefore(a->location.end, 1);
|
||||
advance(Position{a->location.end.line, a->location.end.column - 1});
|
||||
writer.symbol(")");
|
||||
}
|
||||
else if (const auto& a = typeAnnotation.as<AstTypeSingletonBool>())
|
||||
|
@ -2901,7 +2633,7 @@ std::string toString(AstNode* node)
|
|||
StringWriter writer;
|
||||
writer.pos = node->location.begin;
|
||||
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
if (FFlag::LuauStoreCSTData)
|
||||
{
|
||||
Printer printer(writer, CstNodeMap{nullptr});
|
||||
printer.writeTypes = true;
|
||||
|
@ -2937,7 +2669,7 @@ void dump(AstNode* node)
|
|||
std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
||||
{
|
||||
StringWriter writer;
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
if (FFlag::LuauStoreCSTData)
|
||||
{
|
||||
Printer(writer, cstNodeMap).visualizeBlock(block);
|
||||
}
|
||||
|
@ -2951,7 +2683,7 @@ std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
|||
std::string transpileWithTypes(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
||||
{
|
||||
StringWriter writer;
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
if (FFlag::LuauStoreCSTData)
|
||||
{
|
||||
Printer printer(writer, cstNodeMap);
|
||||
printer.writeTypes = true;
|
||||
|
|
|
@ -407,6 +407,41 @@ PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
|
|||
return newTp;
|
||||
}
|
||||
|
||||
PendingType* TxnLog::changeScope(TypeId ty, NotNull<Scope> newScope)
|
||||
{
|
||||
LUAU_ASSERT(get<FreeType>(ty) || get<TableType>(ty) || get<FunctionType>(ty));
|
||||
|
||||
PendingType* newTy = queue(ty);
|
||||
if (FreeType* ftv = Luau::getMutable<FreeType>(newTy))
|
||||
{
|
||||
ftv->scope = newScope;
|
||||
}
|
||||
else if (TableType* ttv = Luau::getMutable<TableType>(newTy))
|
||||
{
|
||||
LUAU_ASSERT(ttv->state == TableState::Free || ttv->state == TableState::Generic);
|
||||
ttv->scope = newScope;
|
||||
}
|
||||
else if (FunctionType* ftv = Luau::getMutable<FunctionType>(newTy))
|
||||
{
|
||||
ftv->scope = newScope;
|
||||
}
|
||||
|
||||
return newTy;
|
||||
}
|
||||
|
||||
PendingTypePack* TxnLog::changeScope(TypePackId tp, NotNull<Scope> newScope)
|
||||
{
|
||||
LUAU_ASSERT(get<FreeTypePack>(tp));
|
||||
|
||||
PendingTypePack* newTp = queue(tp);
|
||||
if (FreeTypePack* ftp = Luau::getMutable<FreeTypePack>(newTp))
|
||||
{
|
||||
ftp->scope = newScope;
|
||||
}
|
||||
|
||||
return newTp;
|
||||
}
|
||||
|
||||
PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexer)
|
||||
{
|
||||
LUAU_ASSERT(get<TableType>(ty));
|
||||
|
|
|
@ -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;
|
||||
|
@ -488,12 +488,11 @@ FreeType::FreeType(TypeLevel level, TypeId lowerBound, TypeId upperBound)
|
|||
{
|
||||
}
|
||||
|
||||
FreeType::FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity)
|
||||
FreeType::FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound)
|
||||
: index(Unifiable::freshIndex())
|
||||
, scope(scope)
|
||||
, lowerBound(lowerBound)
|
||||
, upperBound(upperBound)
|
||||
, polarity(polarity)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -506,6 +505,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))
|
||||
|
@ -519,18 +543,16 @@ GenericType::GenericType(TypeLevel level)
|
|||
{
|
||||
}
|
||||
|
||||
GenericType::GenericType(const Name& name, Polarity polarity)
|
||||
GenericType::GenericType(const Name& name)
|
||||
: index(Unifiable::freshIndex())
|
||||
, name(name)
|
||||
, explicitName(true)
|
||||
, polarity(polarity)
|
||||
{
|
||||
}
|
||||
|
||||
GenericType::GenericType(Scope* scope, Polarity polarity)
|
||||
GenericType::GenericType(Scope* scope)
|
||||
: index(Unifiable::freshIndex())
|
||||
, scope(scope)
|
||||
, polarity(polarity)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -608,6 +630,23 @@ FunctionType::FunctionType(TypeLevel level, TypePackId argTypes, TypePackId retT
|
|||
{
|
||||
}
|
||||
|
||||
FunctionType::FunctionType(
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn,
|
||||
bool hasSelf
|
||||
)
|
||||
: definition(std::move(defn))
|
||||
, level(level)
|
||||
, scope(scope)
|
||||
, argTypes(argTypes)
|
||||
, retTypes(retTypes)
|
||||
, hasSelf(hasSelf)
|
||||
{
|
||||
}
|
||||
|
||||
FunctionType::FunctionType(
|
||||
std::vector<TypeId> generics,
|
||||
std::vector<TypePackId> genericPacks,
|
||||
|
@ -644,6 +683,27 @@ FunctionType::FunctionType(
|
|||
{
|
||||
}
|
||||
|
||||
FunctionType::FunctionType(
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
std::vector<TypeId> generics,
|
||||
std::vector<TypePackId> genericPacks,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn,
|
||||
bool hasSelf
|
||||
)
|
||||
: definition(std::move(defn))
|
||||
, generics(generics)
|
||||
, genericPacks(genericPacks)
|
||||
, level(level)
|
||||
, scope(scope)
|
||||
, argTypes(argTypes)
|
||||
, retTypes(retTypes)
|
||||
, hasSelf(hasSelf)
|
||||
{
|
||||
}
|
||||
|
||||
Property::Property() {}
|
||||
|
||||
Property::Property(
|
||||
|
@ -989,7 +1049,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 +1061,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 +1139,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 +1241,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 +1250,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 +1260,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 +1269,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);
|
||||
}
|
||||
|
||||
|
@ -1247,9 +1306,9 @@ IntersectionTypeIterator end(const IntersectionType* itv)
|
|||
return IntersectionTypeIterator{};
|
||||
}
|
||||
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope, Polarity polarity)
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope)
|
||||
{
|
||||
return arena->addType(FreeType{scope, builtinTypes->neverType, builtinTypes->unknownType, polarity});
|
||||
return arena->addType(FreeType{scope, builtinTypes->neverType, builtinTypes->unknownType});
|
||||
}
|
||||
|
||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
||||
|
@ -1279,8 +1338,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 +1369,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,9 +50,36 @@ TypeId TypeArena::freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLe
|
|||
return allocated;
|
||||
}
|
||||
|
||||
TypePackId TypeArena::freshTypePack(Scope* scope, Polarity polarity)
|
||||
TypeId TypeArena::freshType_DEPRECATED(TypeLevel level)
|
||||
{
|
||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope, polarity});
|
||||
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)
|
||||
{
|
||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope});
|
||||
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||
{
|
||||
char* result = (char*)allocator.allocate(contents.size() + 1);
|
||||
|
@ -220,21 +217,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 +242,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);
|
||||
|
@ -308,8 +305,7 @@ public:
|
|||
std::optional<AstArgumentName>* arg = &argNames.data[i++];
|
||||
|
||||
if (el)
|
||||
new (arg)
|
||||
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location));
|
||||
new (arg) std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), el->location));
|
||||
else
|
||||
new (arg) std::optional<AstArgumentName>();
|
||||
}
|
||||
|
@ -329,19 +325,9 @@ 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}
|
||||
);
|
||||
}
|
||||
return allocator->alloc<AstTypeFunction>(
|
||||
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
|
||||
);
|
||||
}
|
||||
AstType* operator()(const ErrorType&)
|
||||
{
|
||||
|
@ -596,40 +582,19 @@ public:
|
|||
visitLocal(arg);
|
||||
}
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
if (!fn->returnAnnotation)
|
||||
{
|
||||
if (!fn->returnAnnotation)
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
TypePackId ret = result->returnType;
|
||||
TypePackId ret = result->returnType;
|
||||
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation = allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fn->returnAnnotation_DEPRECATED)
|
||||
{
|
||||
if (auto result = getScope(fn->body->location))
|
||||
{
|
||||
TypePackId ret = result->returnType;
|
||||
|
||||
AstTypePack* variadicAnnotation = nullptr;
|
||||
const auto& [v, tail] = flatten(ret);
|
||||
|
||||
if (tail)
|
||||
variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail);
|
||||
|
||||
fn->returnAnnotation_DEPRECATED = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
fn->returnAnnotation = AstTypeList{typeAstPack(ret), variadicAnnotation};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,15 +26,10 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -662,7 +657,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);
|
||||
|
@ -1206,8 +1201,7 @@ void TypeChecker2::visit(AstStatTypeAlias* stat)
|
|||
|
||||
void TypeChecker2::visit(AstStatTypeFunction* stat)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunTypecheck)
|
||||
visit(stat->body);
|
||||
// TODO: add type checking for user-defined type functions
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstTypeList types)
|
||||
|
@ -1222,10 +1216,7 @@ void TypeChecker2::visit(AstStatDeclareFunction* stat)
|
|||
{
|
||||
visitGenerics(stat->generics, stat->genericPacks);
|
||||
visit(stat->params);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(stat->retTypes);
|
||||
else
|
||||
visit(stat->retTypes_DEPRECATED);
|
||||
visit(stat->retTypes);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstStatDeclareGlobal* stat)
|
||||
|
@ -1233,9 +1224,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 +1670,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 +1816,8 @@ void TypeChecker2::visit(AstExprFunction* fn)
|
|||
visit(fn->body);
|
||||
|
||||
// we need to typecheck the return annotation itself, if it exists.
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
if (fn->returnAnnotation)
|
||||
visit(fn->returnAnnotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fn->returnAnnotation_DEPRECATED)
|
||||
visit(*fn->returnAnnotation_DEPRECATED);
|
||||
}
|
||||
if (fn->returnAnnotation)
|
||||
visit(*fn->returnAnnotation);
|
||||
|
||||
|
||||
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
||||
|
@ -2048,7 +2031,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 +2096,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));
|
||||
|
@ -2239,21 +2225,10 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
|
|||
|
||||
return builtinTypes->numberType;
|
||||
case AstExprBinary::Op::Concat:
|
||||
{
|
||||
if (FFlag::LuauTypeCheckerAcceptNumberConcats)
|
||||
{
|
||||
const TypeId numberOrString = module->internalTypes.addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}});
|
||||
testIsSubtype(leftType, numberOrString, expr->left->location);
|
||||
testIsSubtype(rightType, numberOrString, expr->right->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
testIsSubtype(leftType, builtinTypes->stringType, expr->left->location);
|
||||
testIsSubtype(rightType, builtinTypes->stringType, expr->right->location);
|
||||
}
|
||||
testIsSubtype(leftType, builtinTypes->stringType, expr->left->location);
|
||||
testIsSubtype(rightType, builtinTypes->stringType, expr->right->location);
|
||||
|
||||
return builtinTypes->stringType;
|
||||
}
|
||||
case AstExprBinary::Op::CompareGe:
|
||||
case AstExprBinary::Op::CompareGt:
|
||||
case AstExprBinary::Op::CompareLe:
|
||||
|
@ -2376,7 +2351,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 +2597,7 @@ void TypeChecker2::visit(AstTypeFunction* ty)
|
|||
{
|
||||
visitGenerics(ty->generics, ty->genericPacks);
|
||||
visit(ty->argTypes);
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
visit(ty->returnTypes);
|
||||
else
|
||||
visit(ty->returnTypes_DEPRECATED);
|
||||
visit(ty->returnTypes);
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstTypeTypeof* ty)
|
||||
|
@ -2728,61 +2701,20 @@ Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location loc
|
|||
if (!subLeafTy && !superLeafTy && !subLeafTp && !superLeafTp)
|
||||
ice->ice("Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack.", location);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
|
||||
std::string subLeafAsString = toString(subLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (subLeafAsString.empty())
|
||||
subLeafAsString = "()";
|
||||
|
||||
std::string superLeafAsString = toString(superLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (superLeafAsString.empty())
|
||||
superLeafAsString = "()";
|
||||
|
||||
std::stringstream baseReasonBuilder;
|
||||
baseReasonBuilder << "`" << subLeafAsString << "` is not " << relation << " `" << superLeafAsString << "`";
|
||||
std::string baseReason = baseReasonBuilder.str();
|
||||
|
||||
std::stringstream reason;
|
||||
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` in the former type and `" << superLeafAsString
|
||||
<< "` in the latter type, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty() && !reasoning.superPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` and " << toStringHuman(reasoning.superPath) << "`"
|
||||
<< superLeafAsString << "`, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "`, which is not " << relation << " `" << superLeafAsString
|
||||
<< "`";
|
||||
else
|
||||
reason << toStringHuman(reasoning.superPath) << "`" << superLeafAsString << "`, and " << baseReason;
|
||||
|
||||
reasons.push_back(reason.str());
|
||||
}
|
||||
std::string reason;
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason = "at " + toString(reasoning.subPath) + ", " + toString(subLeaf) + " is not " + relation + " " + toString(superLeaf);
|
||||
else
|
||||
{
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
reason = "type " + toString(subTy) + toString(reasoning.subPath, /* prefixDot */ true) + " (" + toString(subLeaf) + ") is not " +
|
||||
relation + " " + toString(superTy) + toString(reasoning.superPath, /* prefixDot */ true) + " (" + toString(superLeaf) + ")";
|
||||
|
||||
std::string reason;
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason = "at " + toString(reasoning.subPath) + ", " + toString(subLeaf) + " is not " + relation + " " + toString(superLeaf);
|
||||
else
|
||||
reason = "type " + toString(subTy) + toString(reasoning.subPath, /* prefixDot */ true) + " (" + toString(subLeaf) + ") is not " +
|
||||
relation + " " + toString(superTy) + toString(reasoning.superPath, /* prefixDot */ true) + " (" + toString(superLeaf) + ")";
|
||||
|
||||
reasons.push_back(reason);
|
||||
}
|
||||
reasons.push_back(reason);
|
||||
|
||||
// if we haven't already proved this isn't suppressing, we have to keep checking.
|
||||
if (suppressed)
|
||||
|
@ -2948,7 +2880,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 +2975,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 +2988,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 +3041,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}};
|
||||
}
|
||||
|
||||
// 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 +3126,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunFixHydratedClasses)
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunSingletonEquality)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -155,7 +158,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 +319,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 +1118,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 +1140,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 +1162,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 +1246,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 +1309,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 +1357,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 +1403,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 +1443,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 +1528,6 @@ void registerTypesLibrary(lua_State* L)
|
|||
{"copy", deepCopy},
|
||||
{"generic", createGeneric},
|
||||
|
||||
{(FFlag::LuauTypeFunOptional) ? "optional" : nullptr, (FFlag::LuauTypeFunOptional) ? createOptional : nullptr},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
@ -1627,7 +1597,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 +1608,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}
|
||||
|
@ -1647,8 +1617,11 @@ void registerTypeUserData(lua_State* L)
|
|||
// Create and register metatable for type userdata
|
||||
luaL_newmetatable(L, "type");
|
||||
|
||||
lua_pushstring(L, "type");
|
||||
lua_setfield(L, -2, "__type");
|
||||
if (FFlag::LuauUserTypeFunTypeofReturnsType)
|
||||
{
|
||||
lua_pushstring(L, "type");
|
||||
lua_setfield(L, -2, "__type");
|
||||
}
|
||||
|
||||
// Protect metatable from being changed
|
||||
lua_pushstring(L, "The metatable is locked");
|
||||
|
@ -1689,7 +1662,10 @@ static int print(lua_State* L)
|
|||
const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
|
||||
if (i > 1)
|
||||
{
|
||||
result.append(1, '\t');
|
||||
if (FFlag::LuauTypeFunPrintFix)
|
||||
result.append(1, '\t');
|
||||
else
|
||||
result.append('\t', 1);
|
||||
}
|
||||
result.append(s, l);
|
||||
lua_pop(L, 1);
|
||||
|
@ -1782,14 +1758,14 @@ bool areEqual(SeenSet& seen, const TypeFunctionSingletonType& lhs, const TypeFun
|
|||
|
||||
{
|
||||
const TypeFunctionBooleanSingleton* lp = get<TypeFunctionBooleanSingleton>(&lhs);
|
||||
const TypeFunctionBooleanSingleton* rp = get<TypeFunctionBooleanSingleton>(&rhs);
|
||||
const TypeFunctionBooleanSingleton* rp = get<TypeFunctionBooleanSingleton>(FFlag::LuauTypeFunSingletonEquality ? &rhs : &lhs);
|
||||
if (lp && rp)
|
||||
return lp->value == rp->value;
|
||||
}
|
||||
|
||||
{
|
||||
const TypeFunctionStringSingleton* lp = get<TypeFunctionStringSingleton>(&lhs);
|
||||
const TypeFunctionStringSingleton* rp = get<TypeFunctionStringSingleton>(&rhs);
|
||||
const TypeFunctionStringSingleton* rp = get<TypeFunctionStringSingleton>(FFlag::LuauTypeFunSingletonEquality ? &rhs : &lhs);
|
||||
if (lp && rp)
|
||||
return lp->value == rp->value;
|
||||
}
|
||||
|
@ -1937,12 +1913,15 @@ 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;
|
||||
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||
return lhs.classTy == rhs.classTy;
|
||||
else
|
||||
return lhs.name_DEPRECATED == rhs.name_DEPRECATED;
|
||||
}
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType& rhs)
|
||||
|
@ -2010,8 +1989,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 +2279,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 +2334,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 +2444,7 @@ private:
|
|||
f2->retTypes = shallowClone(f1->retTypes);
|
||||
}
|
||||
|
||||
void cloneChildren(TypeFunctionExternType* c1, TypeFunctionExternType* c2)
|
||||
void cloneChildren(TypeFunctionClassType* c1, TypeFunctionClassType* c2)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
// used to control the recursion limit of any operations done by user-defined type functions
|
||||
// currently, controls serialization, deserialization, and `type.copy`
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||
LUAU_FASTFLAG(LuauTypeFunFixHydratedClasses)
|
||||
LUAU_FASTFLAG(LuauTypeFunReadWriteParents)
|
||||
|
||||
namespace Luau
|
||||
|
@ -206,13 +207,21 @@ 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}
|
||||
);
|
||||
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||
{
|
||||
// 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(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty});
|
||||
}
|
||||
else
|
||||
{
|
||||
state->classesSerialized_DEPRECATED[c->name] = ty;
|
||||
target = typeFunctionRuntime->typeArena.allocate(
|
||||
TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name}
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (auto g = get<GenericType>(ty))
|
||||
{
|
||||
|
@ -291,7 +300,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 +420,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 +711,19 @@ 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;
|
||||
if (FFlag::LuauTypeFunFixHydratedClasses)
|
||||
{
|
||||
target = c->classTy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto result = state->classesSerialized_DEPRECATED.find(c->name_DEPRECATED))
|
||||
target = *result;
|
||||
else
|
||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious class type is being deserialized");
|
||||
}
|
||||
}
|
||||
else if (auto g = get<TypeFunctionGenericType>(ty))
|
||||
{
|
||||
|
@ -811,7 +830,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 +991,7 @@ private:
|
|||
f1->retTypes = shallowDeserialize(f2->retTypes);
|
||||
}
|
||||
|
||||
void deserializeChildren(TypeFunctionExternType* c2, ExternType* c1)
|
||||
void deserializeChildren(TypeFunctionClassType* c2, ClassType* c1)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
|
|
@ -32,19 +32,17 @@ 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)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLimitIterationWhenCheckingArgumentCounts)
|
||||
LUAU_FASTFLAG(LuauModuleHoldsAstRoot)
|
||||
|
||||
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)
|
||||
|
@ -258,7 +256,8 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
|||
currentModule->type = module.type;
|
||||
currentModule->allocator = module.allocator;
|
||||
currentModule->names = module.names;
|
||||
currentModule->root = module.root;
|
||||
if (FFlag::LuauModuleHoldsAstRoot)
|
||||
currentModule->root = module.root;
|
||||
|
||||
iceHandler->moduleName = module.name;
|
||||
normalizer.arena = ¤tModule->internalTypes;
|
||||
|
@ -318,7 +317,7 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
|||
unifierState.skipCacheForType.clear();
|
||||
|
||||
duplicateTypeAliases.clear();
|
||||
incorrectExternTypeDefinitions.clear();
|
||||
incorrectClassDefinitions.clear();
|
||||
|
||||
return std::move(currentModule);
|
||||
}
|
||||
|
@ -383,7 +382,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 +497,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 +787,7 @@ struct Demoter : Substitution
|
|||
|
||||
bool ignoreChildren(TypeId ty) override
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -798,7 +797,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
|
||||
|
@ -1319,24 +1319,8 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
|||
// Extract the remaining return values of the call
|
||||
// and check them against the parameter types of the iterator function.
|
||||
auto [types, tail] = flatten(callRetPack);
|
||||
|
||||
if (FFlag::LuauStatForInFix)
|
||||
{
|
||||
if (!types.empty())
|
||||
{
|
||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
||||
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
||||
}
|
||||
else
|
||||
{
|
||||
argPack = addTypePack(TypePack{});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
||||
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
||||
}
|
||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
||||
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1674,10 +1658,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
|||
FreeType* ftv = getMutable<FreeType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty, typealias.location};
|
||||
else
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
|
||||
scope->typeAliasLocations[name] = typealias.location;
|
||||
scope->typeAliasNameLocations[name] = typealias.nameLocation;
|
||||
|
@ -1685,82 +1666,79 @@ 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;
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||
else
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||
ctv->metatable = metaTy;
|
||||
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 +1747,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 +1790,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 +1829,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;
|
||||
|
||||
|
@ -1926,7 +1903,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
else if (auto a = expr.as<AstExprUnary>())
|
||||
result = checkExpr(scope, *a);
|
||||
else if (auto a = expr.as<AstExprBinary>())
|
||||
result = FFlag::LuauReduceCheckBinaryExprStackPressure ? checkExpr(scope, *a, expectedType) : checkExpr_DEPRECATED(scope, *a, expectedType);
|
||||
result = checkExpr(scope, *a, expectedType);
|
||||
else if (auto a = expr.as<AstExprTypeAssertion>())
|
||||
result = checkExpr(scope, *a);
|
||||
else if (auto a = expr.as<AstExprError>())
|
||||
|
@ -2137,9 +2114,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();
|
||||
|
||||
|
@ -3188,82 +3165,20 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||
}
|
||||
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
||||
{
|
||||
// Defer the stack allocation of lhs, predicate etc until this lambda is called.
|
||||
auto checkExprOr = [&]() -> WithPredicate<TypeId>
|
||||
{
|
||||
// For these, passing expectedType is worse than simply forcing them, because their implementation
|
||||
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
|
||||
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
|
||||
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
|
||||
|
||||
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
||||
return {booleanType, {std::move(*predicate)}};
|
||||
|
||||
PredicateVec predicates;
|
||||
|
||||
if (auto lvalue = tryGetLValue(*expr.left))
|
||||
predicates.emplace_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
|
||||
|
||||
if (auto lvalue = tryGetLValue(*expr.right))
|
||||
predicates.emplace_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
|
||||
|
||||
if (!predicates.empty() && expr.op == AstExprBinary::CompareNe)
|
||||
predicates = {NotPredicate{std::move(predicates)}};
|
||||
|
||||
return {checkBinaryOperation(scope, expr, lhs.type, rhs.type), std::move(predicates)};
|
||||
};
|
||||
return checkExprOr();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expected types are not useful for other binary operators.
|
||||
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left);
|
||||
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right);
|
||||
|
||||
// Intentionally discarding predicates with other operators.
|
||||
return WithPredicate{checkBinaryOperation(scope, expr, lhs.type, rhs.type, lhs.predicates)};
|
||||
}
|
||||
}
|
||||
|
||||
WithPredicate<TypeId> TypeChecker::checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType)
|
||||
{
|
||||
if (expr.op == AstExprBinary::And)
|
||||
{
|
||||
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
|
||||
|
||||
ScopePtr innerScope = childScope(scope, expr.location);
|
||||
resolve(lhsPredicates, innerScope, true);
|
||||
|
||||
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
|
||||
|
||||
return {checkBinaryOperation(scope, expr, lhsTy, rhsTy), {AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||
}
|
||||
else if (expr.op == AstExprBinary::Or)
|
||||
{
|
||||
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType);
|
||||
|
||||
ScopePtr innerScope = childScope(scope, expr.location);
|
||||
resolve(lhsPredicates, innerScope, false);
|
||||
|
||||
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType);
|
||||
|
||||
// Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation.
|
||||
TypeId result = checkBinaryOperation(scope, expr, lhsTy, rhsTy, lhsPredicates);
|
||||
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||
}
|
||||
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
||||
{
|
||||
// For these, passing expectedType is worse than simply forcing them, because their implementation
|
||||
// may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first.
|
||||
WithPredicate<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true);
|
||||
WithPredicate<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true);
|
||||
|
||||
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
||||
return {booleanType, {std::move(*predicate)}};
|
||||
|
||||
PredicateVec predicates;
|
||||
|
||||
if (auto lvalue = tryGetLValue(*expr.left))
|
||||
predicates.push_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location});
|
||||
|
||||
if (auto lvalue = tryGetLValue(*expr.right))
|
||||
predicates.push_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location});
|
||||
|
||||
|
@ -3462,14 +3377,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 +3436,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 +3469,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 +3763,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 +3971,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});
|
||||
}
|
||||
|
@ -4117,23 +4029,6 @@ void TypeChecker::checkArgumentList(
|
|||
|
||||
size_t paramIndex = 0;
|
||||
|
||||
int loopCount = 0;
|
||||
auto exceedsLoopCount = [&]()
|
||||
{
|
||||
if (FFlag::LuauLimitIterationWhenCheckingArgumentCounts)
|
||||
{
|
||||
++loopCount;
|
||||
if (loopCount > FInt::LuauTypeInferTypePackLoopLimit)
|
||||
{
|
||||
state.reportError(TypeError{state.location, CodeTooComplex{}});
|
||||
reportErrorCodeTooComplex(state.location);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto reportCountMismatchError = [&state, &argLocations, paramPack, argPack, &funName]()
|
||||
{
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
|
@ -4208,17 +4103,12 @@ void TypeChecker::checkArgumentList(
|
|||
}
|
||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||
{
|
||||
loopCount = 0;
|
||||
|
||||
// Function is variadic and requires that all subsequent parameters
|
||||
// be compatible with a type.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(vtp->ty, *paramIter);
|
||||
++paramIter;
|
||||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -4227,16 +4117,10 @@ void TypeChecker::checkArgumentList(
|
|||
{
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(paramIter, endIter));
|
||||
|
||||
loopCount = 0;
|
||||
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
rest.push_back(*paramIter);
|
||||
++paramIter;
|
||||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||
|
@ -4280,17 +4164,12 @@ void TypeChecker::checkArgumentList(
|
|||
// too many parameters passed
|
||||
if (!paramIter.tail())
|
||||
{
|
||||
loopCount = 0;
|
||||
|
||||
while (argIter != endIter)
|
||||
{
|
||||
// The use of unify here is deliberate. We don't want this unification
|
||||
// to be undoable.
|
||||
unify(errorRecoveryType(scope), *argIter, scope, state.location);
|
||||
++argIter;
|
||||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
reportCountMismatchError();
|
||||
return;
|
||||
|
@ -4304,8 +4183,6 @@ void TypeChecker::checkArgumentList(
|
|||
}
|
||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||
{
|
||||
loopCount = 0;
|
||||
|
||||
// Function is variadic and requires that all subsequent parameters
|
||||
// be compatible with a type.
|
||||
size_t argIndex = paramIndex;
|
||||
|
@ -4321,17 +4198,12 @@ void TypeChecker::checkArgumentList(
|
|||
|
||||
++argIter;
|
||||
++argIndex;
|
||||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
loopCount = 0;
|
||||
|
||||
// Create a type pack out of the remaining argument types
|
||||
// and unify it with the tail.
|
||||
std::vector<TypeId> rest;
|
||||
|
@ -4340,10 +4212,7 @@ void TypeChecker::checkArgumentList(
|
|||
{
|
||||
rest.push_back(*argIter);
|
||||
++argIter;
|
||||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
|
@ -4614,9 +4483,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 +5188,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 +5277,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 +5675,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());
|
||||
|
@ -5852,14 +5721,14 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
TypeId ty = checkExpr(scope, *typeOf->expr).type;
|
||||
return ty;
|
||||
}
|
||||
else if (annotation.is<AstTypeOptional>())
|
||||
{
|
||||
return builtinTypes->nilType;
|
||||
}
|
||||
else if (const auto& un = annotation.as<AstTypeUnion>())
|
||||
{
|
||||
if (un->types.size == 1)
|
||||
return resolveType(scope, *un->types.data[0]);
|
||||
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));
|
||||
|
@ -5868,8 +5737,12 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
}
|
||||
else if (const auto& un = annotation.as<AstTypeIntersection>())
|
||||
{
|
||||
if (un->types.size == 1)
|
||||
return resolveType(scope, *un->types.data[0]);
|
||||
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 +6349,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
|||
return refine(
|
||||
[](TypeId ty) -> bool
|
||||
{
|
||||
return get<ExternType>(ty);
|
||||
return get<ClassType>(ty);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -6491,13 +6364,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.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypePackDetectCycles)
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -18,11 +18,10 @@ FreeTypePack::FreeTypePack(TypeLevel level)
|
|||
{
|
||||
}
|
||||
|
||||
FreeTypePack::FreeTypePack(Scope* scope, Polarity polarity)
|
||||
FreeTypePack::FreeTypePack(Scope* scope)
|
||||
: index(Unifiable::freshIndex())
|
||||
, level{}
|
||||
, scope(scope)
|
||||
, polarity(polarity)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -53,10 +52,9 @@ GenericTypePack::GenericTypePack(const Name& name)
|
|||
{
|
||||
}
|
||||
|
||||
GenericTypePack::GenericTypePack(Scope* scope, Polarity polarity)
|
||||
GenericTypePack::GenericTypePack(Scope* scope)
|
||||
: index(Unifiable::freshIndex())
|
||||
, scope(scope)
|
||||
, polarity(polarity)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -149,15 +147,6 @@ TypePackIterator& TypePackIterator::operator++()
|
|||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||
|
||||
if (FFlag::LuauTypePackDetectCycles && tp)
|
||||
{
|
||||
// Step twice on each iteration to detect cycles
|
||||
tailCycleCheck = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
|
||||
if (currentTypePack == tailCycleCheck)
|
||||
throw InternalCompilerError("TypePackIterator detected a type pack cycle");
|
||||
}
|
||||
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
|
@ -208,26 +197,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);
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
#include <type_traits>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAGVARIABLE(LuauDisableNewSolverAssertsInMixedMode)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauDisableNewSolverAssertsInMixedMode);
|
||||
// Maximum number of steps to follow when traversing a path. May not always
|
||||
// equate to the number of components in a path, depending on the traversal
|
||||
// logic.
|
||||
|
@ -307,9 +306,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 +460,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)
|
||||
|
@ -639,247 +638,6 @@ std::string toString(const TypePath::Path& path, bool prefixDot)
|
|||
return result.str();
|
||||
}
|
||||
|
||||
std::string toStringHuman(const TypePath::Path& path)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauSolverV2);
|
||||
|
||||
enum class State
|
||||
{
|
||||
Initial,
|
||||
Normal,
|
||||
Property,
|
||||
PendingIs,
|
||||
PendingAs,
|
||||
PendingWhich,
|
||||
};
|
||||
|
||||
std::stringstream result;
|
||||
State state = State::Initial;
|
||||
bool last = false;
|
||||
|
||||
auto strComponent = [&](auto&& c)
|
||||
{
|
||||
using T = std::decay_t<decltype(c)>;
|
||||
if constexpr (std::is_same_v<T, TypePath::Property>)
|
||||
{
|
||||
if (state == State::PendingIs)
|
||||
result << ", ";
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case State::Initial:
|
||||
case State::PendingIs:
|
||||
if (c.isRead)
|
||||
result << "accessing `";
|
||||
else
|
||||
result << "writing to `";
|
||||
break;
|
||||
case State::Property:
|
||||
// if the previous state was a property, then we're doing a sequence of indexing
|
||||
result << '.';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
result << c.name;
|
||||
|
||||
state = State::Property;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypePath::Index>)
|
||||
{
|
||||
size_t humanIndex = c.index + 1;
|
||||
|
||||
if (state == State::Initial && !last)
|
||||
result << "in" << ' ';
|
||||
else if (state == State::PendingIs)
|
||||
result << ' ' << "has" << ' ';
|
||||
else if (state == State::Property)
|
||||
result << '`' << ' ' << "has" << ' ';
|
||||
|
||||
result << "the " << humanIndex;
|
||||
switch (humanIndex)
|
||||
{
|
||||
case 1:
|
||||
result << "st";
|
||||
break;
|
||||
case 2:
|
||||
result << "nd";
|
||||
break;
|
||||
case 3:
|
||||
result << "rd";
|
||||
break;
|
||||
default:
|
||||
result << "th";
|
||||
}
|
||||
|
||||
switch (c.variant)
|
||||
{
|
||||
case TypePath::Index::Variant::Pack:
|
||||
result << ' ' << "entry in the type pack";
|
||||
break;
|
||||
case TypePath::Index::Variant::Union:
|
||||
result << ' ' << "component of the union";
|
||||
break;
|
||||
case TypePath::Index::Variant::Intersection:
|
||||
result << ' ' << "component of the intersection";
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == State::PendingWhich)
|
||||
result << ' ' << "which";
|
||||
|
||||
if (state == State::PendingIs || state == State::Property)
|
||||
state = State::PendingAs;
|
||||
else
|
||||
state = State::PendingIs;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypePath::TypeField>)
|
||||
{
|
||||
if (state == State::Initial && !last)
|
||||
result << "in" << ' ';
|
||||
else if (state == State::PendingIs)
|
||||
result << ", ";
|
||||
else if (state == State::Property)
|
||||
result << '`' << ' ' << "has" << ' ';
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case TypePath::TypeField::Table:
|
||||
result << "the table portion";
|
||||
if (state == State::Property)
|
||||
state = State::PendingAs;
|
||||
else
|
||||
state = State::PendingIs;
|
||||
break;
|
||||
case TypePath::TypeField::Metatable:
|
||||
result << "the metatable portion";
|
||||
if (state == State::Property)
|
||||
state = State::PendingAs;
|
||||
else
|
||||
state = State::PendingIs;
|
||||
break;
|
||||
case TypePath::TypeField::LowerBound:
|
||||
result << "the lower bound of" << ' ';
|
||||
state = State::Normal;
|
||||
break;
|
||||
case TypePath::TypeField::UpperBound:
|
||||
result << "the upper bound of" << ' ';
|
||||
state = State::Normal;
|
||||
break;
|
||||
case TypePath::TypeField::IndexLookup:
|
||||
result << "the index type";
|
||||
if (state == State::Property)
|
||||
state = State::PendingAs;
|
||||
else
|
||||
state = State::PendingIs;
|
||||
break;
|
||||
case TypePath::TypeField::IndexResult:
|
||||
result << "the result of indexing";
|
||||
if (state == State::Property)
|
||||
state = State::PendingAs;
|
||||
else
|
||||
state = State::PendingIs;
|
||||
break;
|
||||
case TypePath::TypeField::Negated:
|
||||
result << "the negation" << ' ';
|
||||
state = State::Normal;
|
||||
break;
|
||||
case TypePath::TypeField::Variadic:
|
||||
result << "the variadic" << ' ';
|
||||
state = State::Normal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypePath::PackField>)
|
||||
{
|
||||
if (state == State::PendingIs)
|
||||
result << ", ";
|
||||
else if (state == State::Property)
|
||||
result << "`, ";
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case TypePath::PackField::Arguments:
|
||||
if (state == State::Initial)
|
||||
result << "it" << ' ';
|
||||
else if (state == State::PendingIs)
|
||||
result << "the function" << ' ';
|
||||
|
||||
result << "takes";
|
||||
break;
|
||||
case TypePath::PackField::Returns:
|
||||
if (state == State::Initial)
|
||||
result << "it" << ' ';
|
||||
else if (state == State::PendingIs)
|
||||
result << "the function" << ' ';
|
||||
|
||||
result << "returns";
|
||||
break;
|
||||
case TypePath::PackField::Tail:
|
||||
if (state == State::Initial)
|
||||
result << "it has" << ' ';
|
||||
result << "a tail of";
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == State::PendingIs)
|
||||
{
|
||||
result << ' ';
|
||||
state = State::PendingWhich;
|
||||
}
|
||||
else
|
||||
{
|
||||
result << ' ';
|
||||
state = State::Normal;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TypePath::Reduction>)
|
||||
{
|
||||
if (state == State::Initial)
|
||||
result << "it" << ' ';
|
||||
result << "reduces to" << ' ';
|
||||
state = State::Normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(always_false_v<T>, "Unhandled Component variant");
|
||||
}
|
||||
};
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
for (const TypePath::Component& component : path.components)
|
||||
{
|
||||
count++;
|
||||
if (count == path.components.size())
|
||||
last = true;
|
||||
|
||||
Luau::visit(strComponent, component);
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case State::Property:
|
||||
result << "` results in ";
|
||||
break;
|
||||
case State::PendingWhich:
|
||||
// pending `which` becomes `is` if it's at the end
|
||||
result << "is" << ' ';
|
||||
break;
|
||||
case State::PendingIs:
|
||||
result << ' ' << "is" << ' ';
|
||||
break;
|
||||
case State::PendingAs:
|
||||
result << ' ' << "as" << ' ';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
static bool traverse(TraversalState& state, const Path& path)
|
||||
{
|
||||
auto step = [&state](auto&& c)
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -303,11 +304,7 @@ TypePack extendTypePack(
|
|||
// also have to create a new tail.
|
||||
|
||||
TypePack newPack;
|
||||
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
||||
|
||||
if (FFlag::LuauNonReentrantGeneralization2)
|
||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
||||
|
||||
newPack.tail = arena.freshTypePack(ftp->scope);
|
||||
if (FFlag::LuauSolverV2)
|
||||
result.tail = newPack.tail;
|
||||
size_t overridesIndex = 0;
|
||||
|
@ -322,12 +319,13 @@ TypePack extendTypePack(
|
|||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType, ftp->polarity};
|
||||
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType};
|
||||
t = arena.addType(ft);
|
||||
trackInteriorFreeType(ftp->scope, t);
|
||||
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 +432,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 +550,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)
|
||||
|
@ -567,24 +568,4 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
|
|||
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypes` member.");
|
||||
}
|
||||
|
||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
||||
{
|
||||
LUAU_ASSERT(tp);
|
||||
if (!FFlag::LuauNonReentrantGeneralization2)
|
||||
return;
|
||||
|
||||
for (; scope; scope = scope->parent.get())
|
||||
{
|
||||
if (scope->interiorFreeTypePacks)
|
||||
{
|
||||
scope->interiorFreeTypePacks->push_back(tp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// There should at least be *one* generalization constraint per module
|
||||
// where `interiorFreeTypes` is present, which would be the one made
|
||||
// by ConstraintGenerator::visitModuleRoot.
|
||||
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypePacks` member.");
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -24,7 +24,6 @@ const size_t kPageSize = sysconf(_SC_PAGESIZE);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue