mirror of
https://github.com/luau-lang/luau.git
synced 2025-04-17 02:13:55 +01:00
Compare commits
No commits in common. "master" and "0.666" have entirely different histories.
134 changed files with 4738 additions and 8849 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
|
|
@ -50,7 +50,6 @@ struct GeneralizationConstraint
|
||||||
TypeId sourceType;
|
TypeId sourceType;
|
||||||
|
|
||||||
std::vector<TypeId> interiorTypes;
|
std::vector<TypeId> interiorTypes;
|
||||||
bool hasDeprecatedAttribute = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// variables ~ iterate iterator
|
// variables ~ iterate iterator
|
||||||
|
|
|
@ -11,14 +11,15 @@
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Polarity.h"
|
|
||||||
#include "Luau/Refinement.h"
|
#include "Luau/Refinement.h"
|
||||||
#include "Luau/Symbol.h"
|
#include "Luau/Symbol.h"
|
||||||
#include "Luau/TypeFwd.h"
|
#include "Luau/TypeFwd.h"
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
|
#include "Luau/Variant.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -131,8 +132,6 @@ struct ConstraintGenerator
|
||||||
|
|
||||||
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};
|
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};
|
||||||
|
|
||||||
DenseHashMap<AstExpr*, Inference> inferredExprCache{nullptr};
|
|
||||||
|
|
||||||
DcrLogger* logger;
|
DcrLogger* logger;
|
||||||
|
|
||||||
ConstraintGenerator(
|
ConstraintGenerator(
|
||||||
|
@ -161,26 +160,19 @@ struct ConstraintGenerator
|
||||||
void visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block);
|
void visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct InteriorFreeTypes
|
std::vector<std::vector<TypeId>> interiorTypes;
|
||||||
{
|
|
||||||
std::vector<TypeId> types;
|
|
||||||
std::vector<TypePackId> typePacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::vector<TypeId>> DEPRECATED_interiorTypes;
|
|
||||||
std::vector<InteriorFreeTypes> interiorFreeTypes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fabricates a new free type belonging to a given scope.
|
* Fabricates a new free type belonging to a given scope.
|
||||||
* @param scope the scope the free type belongs to.
|
* @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.
|
* Fabricates a new free type pack belonging to a given scope.
|
||||||
* @param scope the scope the free type pack belongs to.
|
* @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.
|
* Allocate a new TypePack with the given head and tail.
|
||||||
|
@ -301,7 +293,7 @@ private:
|
||||||
);
|
);
|
||||||
|
|
||||||
Inference check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton);
|
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, AstExprLocal* local);
|
||||||
Inference check(const ScopePtr& scope, AstExprGlobal* global);
|
Inference check(const ScopePtr& scope, AstExprGlobal* global);
|
||||||
Inference checkIndexName(const ScopePtr& scope, const RefinementKey* key, AstExpr* indexee, const std::string& index, Location indexLocation);
|
Inference checkIndexName(const ScopePtr& scope, const RefinementKey* key, AstExpr* indexee, const std::string& index, Location indexLocation);
|
||||||
|
@ -377,11 +369,6 @@ private:
|
||||||
**/
|
**/
|
||||||
TypeId resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
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.
|
* Resolves a type pack from its AST annotation.
|
||||||
* @param scope the scope that the type annotation appears within.
|
* @param scope the scope that the type annotation appears within.
|
||||||
|
@ -391,9 +378,6 @@ private:
|
||||||
**/
|
**/
|
||||||
TypePackId resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArguments, bool replaceErrorWithFresh = false);
|
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.
|
* Resolves a type pack from its AST annotation.
|
||||||
* @param scope the scope that the type annotation appears within.
|
* @param scope the scope that the type annotation appears within.
|
||||||
|
@ -432,7 +416,7 @@ private:
|
||||||
**/
|
**/
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(
|
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(
|
||||||
const ScopePtr& scope,
|
const ScopePtr& scope,
|
||||||
AstArray<AstGenericTypePack*> generics,
|
AstArray<AstGenericTypePack*> packs,
|
||||||
bool useCache = false,
|
bool useCache = false,
|
||||||
bool addTypes = true
|
bool addTypes = true
|
||||||
);
|
);
|
||||||
|
|
|
@ -365,7 +365,7 @@ public:
|
||||||
* @returns a non-free type that generalizes the argument, or `std::nullopt` if one
|
* @returns a non-free type that generalizes the argument, or `std::nullopt` if one
|
||||||
* does not exist
|
* 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
|
* Checks the existing set of constraints to see if there exist any that contain
|
||||||
|
|
|
@ -38,6 +38,8 @@ struct DataFlowGraph
|
||||||
DefId getDef(const AstExpr* expr) const;
|
DefId getDef(const AstExpr* expr) const;
|
||||||
// Look up the definition optionally, knowing it may not be present.
|
// Look up the definition optionally, knowing it may not be present.
|
||||||
std::optional<DefId> getDefOptional(const AstExpr* expr) const;
|
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;
|
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.
|
// All keys in this maps are really only statements that ambiently declares a symbol.
|
||||||
DenseHashMap<const AstStat*, const Def*> declaredDefs{nullptr};
|
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};
|
DenseHashMap<const AstExpr*, const RefinementKey*> astRefinementKeys{nullptr};
|
||||||
friend struct DataFlowGraphBuilder;
|
friend struct DataFlowGraphBuilder;
|
||||||
};
|
};
|
||||||
|
@ -129,8 +135,8 @@ private:
|
||||||
|
|
||||||
/// A stack of scopes used by the visitor to see where we are.
|
/// A stack of scopes used by the visitor to see where we are.
|
||||||
ScopeStack scopeStack;
|
ScopeStack scopeStack;
|
||||||
NotNull<DfgScope> currentScope();
|
|
||||||
DfgScope* currentScope_DEPRECATED();
|
DfgScope* currentScope();
|
||||||
|
|
||||||
struct FunctionCapture
|
struct FunctionCapture
|
||||||
{
|
{
|
||||||
|
@ -148,8 +154,8 @@ private:
|
||||||
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
||||||
void joinProps(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(Symbol symbol);
|
||||||
DefId lookup(DefId def, const std::string& key, Location location);
|
DefId lookup(DefId def, const std::string& key);
|
||||||
|
|
||||||
ControlFlow visit(AstStatBlock* b);
|
ControlFlow visit(AstStatBlock* b);
|
||||||
ControlFlow visitBlockWithoutChildScope(AstStatBlock* b);
|
ControlFlow visitBlockWithoutChildScope(AstStatBlock* b);
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/TypedAllocator.h"
|
#include "Luau/TypedAllocator.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
#include "Luau/Location.h"
|
|
||||||
#include "Luau/Symbol.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ namespace Luau
|
||||||
|
|
||||||
struct Def;
|
struct Def;
|
||||||
using DefId = NotNull<const Def>;
|
using DefId = NotNull<const Def>;
|
||||||
struct AstLocal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cell is a "single-object" value.
|
* A cell is a "single-object" value.
|
||||||
|
@ -66,8 +64,6 @@ struct Def
|
||||||
using V = Variant<struct Cell, struct Phi>;
|
using V = Variant<struct Cell, struct Phi>;
|
||||||
|
|
||||||
V v;
|
V v;
|
||||||
Symbol name;
|
|
||||||
Location location;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -83,7 +79,7 @@ struct DefArena
|
||||||
{
|
{
|
||||||
TypedAllocator<Def> allocator;
|
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(DefId a, DefId b);
|
||||||
DefId phi(const std::vector<DefId>& defs);
|
DefId phi(const std::vector<DefId>& defs);
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,8 +49,6 @@ struct FragmentAutocompleteAncestryResult
|
||||||
std::vector<AstLocal*> localStack;
|
std::vector<AstLocal*> localStack;
|
||||||
std::vector<AstNode*> ancestry;
|
std::vector<AstNode*> ancestry;
|
||||||
AstStat* nearestStatement = nullptr;
|
AstStat* nearestStatement = nullptr;
|
||||||
AstStatBlock* parentBlock = nullptr;
|
|
||||||
Location fragmentSelectionRegion;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentParseResult
|
struct FragmentParseResult
|
||||||
|
@ -61,7 +59,6 @@ struct FragmentParseResult
|
||||||
AstStat* nearestStatement = nullptr;
|
AstStat* nearestStatement = nullptr;
|
||||||
std::vector<Comment> commentLocations;
|
std::vector<Comment> commentLocations;
|
||||||
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
|
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
|
||||||
Position scopePos{0, 0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentTypeCheckResult
|
struct FragmentTypeCheckResult
|
||||||
|
@ -75,33 +72,14 @@ struct FragmentAutocompleteResult
|
||||||
{
|
{
|
||||||
ModulePtr incrementalModule;
|
ModulePtr incrementalModule;
|
||||||
Scope* freshScope;
|
Scope* freshScope;
|
||||||
TypeArena arenaForAutocomplete_DEPRECATED;
|
TypeArena arenaForAutocomplete;
|
||||||
AutocompleteResult acResults;
|
AutocompleteResult acResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentRegion
|
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* root, const Position& cursorPos);
|
||||||
{
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
std::optional<FragmentParseResult> parseFragment(
|
std::optional<FragmentParseResult> parseFragment(
|
||||||
AstStatBlock* stale,
|
AstStatBlock* root,
|
||||||
AstStatBlock* mostRecentParse,
|
|
||||||
AstNameTable* names,
|
AstNameTable* names,
|
||||||
std::string_view src,
|
std::string_view src,
|
||||||
const Position& cursorPos,
|
const Position& cursorPos,
|
||||||
|
@ -115,7 +93,6 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
std::optional<FrontendOptions> opts,
|
std::optional<FrontendOptions> opts,
|
||||||
std::string_view src,
|
std::string_view src,
|
||||||
std::optional<Position> fragmentEndPosition,
|
std::optional<Position> fragmentEndPosition,
|
||||||
AstStatBlock* recentParse = nullptr,
|
|
||||||
IFragmentAutocompleteReporter* reporter = nullptr
|
IFragmentAutocompleteReporter* reporter = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -127,7 +104,6 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
std::optional<FrontendOptions> opts,
|
std::optional<FrontendOptions> opts,
|
||||||
StringCompletionCallback callback,
|
StringCompletionCallback callback,
|
||||||
std::optional<Position> fragmentEndPosition = std::nullopt,
|
std::optional<Position> fragmentEndPosition = std::nullopt,
|
||||||
AstStatBlock* recentParse = nullptr,
|
|
||||||
IFragmentAutocompleteReporter* reporter = nullptr
|
IFragmentAutocompleteReporter* reporter = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Luau/Set.h"
|
#include "Luau/Set.h"
|
||||||
#include "Luau/TypeCheckLimits.h"
|
#include "Luau/TypeCheckLimits.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
|
#include "Luau/AnyTypeSummary.h"
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -33,6 +34,7 @@ struct HotComment;
|
||||||
struct BuildQueueItem;
|
struct BuildQueueItem;
|
||||||
struct BuildQueueWorkState;
|
struct BuildQueueWorkState;
|
||||||
struct FrontendCancellationToken;
|
struct FrontendCancellationToken;
|
||||||
|
struct AnyTypeSummary;
|
||||||
|
|
||||||
struct LoadDefinitionFileResult
|
struct LoadDefinitionFileResult
|
||||||
{
|
{
|
||||||
|
@ -215,6 +217,11 @@ struct Frontend
|
||||||
std::function<void(std::function<void()> task)> executeTask = {},
|
std::function<void(std::function<void()> task)> executeTask = {},
|
||||||
std::function<bool(size_t done, size_t total)> progress = {}
|
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::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);
|
||||||
std::vector<ModuleName> getRequiredScripts(const ModuleName& name);
|
std::vector<ModuleName> getRequiredScripts(const ModuleName& name);
|
||||||
|
|
|
@ -8,39 +8,13 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename TID>
|
|
||||||
struct GeneralizationParams
|
|
||||||
{
|
|
||||||
bool foundOutsideFunctions = false;
|
|
||||||
size_t useCount = 0;
|
|
||||||
Polarity polarity = Polarity::None;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Replace a single free type by its bounds according to the polarity provided.
|
|
||||||
std::optional<TypeId> generalizeType(
|
|
||||||
NotNull<TypeArena> arena,
|
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
|
||||||
NotNull<Scope> scope,
|
|
||||||
TypeId freeTy,
|
|
||||||
const GeneralizationParams<TypeId>& params
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generalize one type pack
|
|
||||||
std::optional<TypePackId> generalizeTypePack(
|
|
||||||
NotNull<TypeArena> arena,
|
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
|
||||||
NotNull<Scope> scope,
|
|
||||||
TypePackId tp,
|
|
||||||
const GeneralizationParams<TypePackId>& params
|
|
||||||
);
|
|
||||||
|
|
||||||
void sealTable(NotNull<Scope> scope, TypeId ty);
|
|
||||||
|
|
||||||
std::optional<TypeId> generalize(
|
std::optional<TypeId> generalize(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
TypeId ty
|
TypeId ty,
|
||||||
|
/* avoid sealing tables*/ bool avoidSealingTables = false
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
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
|
const_iterator begin() const
|
||||||
{
|
{
|
||||||
return pairs.begin();
|
return pairs.begin();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "Luau/ParseResult.h"
|
#include "Luau/ParseResult.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
|
#include "Luau/AnyTypeSummary.h"
|
||||||
#include "Luau/DataFlowGraph.h"
|
#include "Luau/DataFlowGraph.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -15,16 +16,19 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
using LogLuauProc = void (*)(std::string_view, std::string_view);
|
using LogLuauProc = void (*)(std::string_view);
|
||||||
extern LogLuauProc logLuau;
|
extern LogLuauProc logLuau;
|
||||||
|
|
||||||
void setLogLuau(LogLuauProc ll);
|
void setLogLuau(LogLuauProc ll);
|
||||||
void resetLogLuauProc();
|
void resetLogLuauProc();
|
||||||
|
|
||||||
struct Module;
|
struct Module;
|
||||||
|
struct AnyTypeSummary;
|
||||||
|
|
||||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||||
using ModulePtr = std::shared_ptr<Module>;
|
using ModulePtr = std::shared_ptr<Module>;
|
||||||
|
@ -82,6 +86,10 @@ struct Module
|
||||||
TypeArena interfaceTypes;
|
TypeArena interfaceTypes;
|
||||||
TypeArena internalTypes;
|
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
|
// Scopes and AST types refer to parse data, so we need to keep that alive
|
||||||
std::shared_ptr<Allocator> allocator;
|
std::shared_ptr<Allocator> allocator;
|
||||||
std::shared_ptr<AstNameTable> names;
|
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
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/DataFlowGraph.h"
|
|
||||||
#include "Luau/EqSatSimplification.h"
|
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
|
#include "Luau/DataFlowGraph.h"
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
|
|
@ -40,7 +40,7 @@ struct Scope
|
||||||
// All the children of this scope.
|
// All the children of this scope.
|
||||||
std::vector<NotNull<Scope>> children;
|
std::vector<NotNull<Scope>> children;
|
||||||
std::unordered_map<Symbol, Binding> bindings;
|
std::unordered_map<Symbol, Binding> bindings;
|
||||||
TypePackId returnType = nullptr;
|
TypePackId returnType;
|
||||||
std::optional<TypePackId> varargPack;
|
std::optional<TypePackId> varargPack;
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
|
@ -100,7 +100,6 @@ struct Scope
|
||||||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||||
|
|
||||||
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
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
|
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
||||||
|
|
|
@ -192,6 +192,16 @@ struct TxnLog
|
||||||
// The pointer returned lives until `commit` or `clear` is called.
|
// The pointer returned lives until `commit` or `clear` is called.
|
||||||
PendingTypePack* changeLevel(TypePackId tp, TypeLevel newLevel);
|
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
|
// Queues a replacement of a table type with another table type with a new
|
||||||
// indexer.
|
// indexer.
|
||||||
//
|
//
|
||||||
|
|
|
@ -5,11 +5,10 @@
|
||||||
|
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/Refinement.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Polarity.h"
|
|
||||||
#include "Luau/Predicate.h"
|
#include "Luau/Predicate.h"
|
||||||
#include "Luau/Refinement.h"
|
|
||||||
#include "Luau/Unifiable.h"
|
#include "Luau/Unifiable.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
#include "Luau/VecDeque.h"
|
#include "Luau/VecDeque.h"
|
||||||
|
@ -20,6 +19,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
||||||
|
@ -72,7 +72,7 @@ struct FreeType
|
||||||
// New constructors
|
// New constructors
|
||||||
explicit FreeType(TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
explicit FreeType(TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||||
// This one got promoted to explicit
|
// 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);
|
explicit FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||||
// Old constructors
|
// Old constructors
|
||||||
explicit FreeType(TypeLevel level);
|
explicit FreeType(TypeLevel level);
|
||||||
|
@ -91,8 +91,6 @@ struct FreeType
|
||||||
// Only used under local type inference
|
// Only used under local type inference
|
||||||
TypeId lowerBound = nullptr;
|
TypeId lowerBound = nullptr;
|
||||||
TypeId upperBound = nullptr;
|
TypeId upperBound = nullptr;
|
||||||
|
|
||||||
Polarity polarity = Polarity::Unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GenericType
|
struct GenericType
|
||||||
|
@ -101,8 +99,8 @@ struct GenericType
|
||||||
GenericType();
|
GenericType();
|
||||||
|
|
||||||
explicit GenericType(TypeLevel level);
|
explicit GenericType(TypeLevel level);
|
||||||
explicit GenericType(const Name& name, Polarity polarity = Polarity::Unknown);
|
explicit GenericType(const Name& name);
|
||||||
explicit GenericType(Scope* scope, Polarity polarity = Polarity::Unknown);
|
explicit GenericType(Scope* scope);
|
||||||
|
|
||||||
GenericType(TypeLevel level, const Name& name);
|
GenericType(TypeLevel level, const Name& name);
|
||||||
GenericType(Scope* scope, const Name& name);
|
GenericType(Scope* scope, const Name& name);
|
||||||
|
@ -112,8 +110,6 @@ struct GenericType
|
||||||
Scope* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
Name name;
|
Name name;
|
||||||
bool explicitName = false;
|
bool explicitName = false;
|
||||||
|
|
||||||
Polarity polarity = Polarity::Unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// When an equality constraint is found, it is then "bound" to that type,
|
// When an equality constraint is found, it is then "bound" to that type,
|
||||||
|
@ -352,8 +348,10 @@ struct FunctionType
|
||||||
);
|
);
|
||||||
|
|
||||||
// Local monomorphic function
|
// Local monomorphic function
|
||||||
|
FunctionType(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||||
FunctionType(
|
FunctionType(
|
||||||
TypeLevel level,
|
TypeLevel level,
|
||||||
|
Scope* scope,
|
||||||
TypePackId argTypes,
|
TypePackId argTypes,
|
||||||
TypePackId retTypes,
|
TypePackId retTypes,
|
||||||
std::optional<FunctionDefinition> defn = {},
|
std::optional<FunctionDefinition> defn = {},
|
||||||
|
@ -370,6 +368,16 @@ struct FunctionType
|
||||||
std::optional<FunctionDefinition> defn = {},
|
std::optional<FunctionDefinition> defn = {},
|
||||||
bool hasSelf = false
|
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;
|
std::optional<FunctionDefinition> definition;
|
||||||
/// These should all be generic
|
/// These should all be generic
|
||||||
|
@ -378,6 +386,7 @@ struct FunctionType
|
||||||
std::vector<std::optional<FunctionArgument>> argNames;
|
std::vector<std::optional<FunctionArgument>> argNames;
|
||||||
Tags tags;
|
Tags tags;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
|
Scope* scope = nullptr;
|
||||||
TypePackId argTypes;
|
TypePackId argTypes;
|
||||||
TypePackId retTypes;
|
TypePackId retTypes;
|
||||||
std::shared_ptr<MagicFunction> magic = nullptr;
|
std::shared_ptr<MagicFunction> magic = nullptr;
|
||||||
|
@ -387,7 +396,6 @@ struct FunctionType
|
||||||
// this flag is used as an optimization to exit early from procedures that manipulate free or generic types.
|
// this flag is used as an optimization to exit early from procedures that manipulate free or generic types.
|
||||||
bool hasNoFreeOrGenericTypes = false;
|
bool hasNoFreeOrGenericTypes = false;
|
||||||
bool isCheckedFunction = false;
|
bool isCheckedFunction = false;
|
||||||
bool isDeprecatedFunction = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TableState
|
enum class TableState
|
||||||
|
@ -464,9 +472,7 @@ struct Property
|
||||||
TypeId type() const;
|
TypeId type() const;
|
||||||
void setType(TypeId ty);
|
void setType(TypeId ty);
|
||||||
|
|
||||||
// If this property has a present `writeTy`, set it equal to the `readTy`.
|
// Sets the write type of this property to the read type.
|
||||||
// This is to ensure that if we normalize a property that has divergent
|
|
||||||
// read and write types, we make them converge (for now).
|
|
||||||
void makeShared();
|
void makeShared();
|
||||||
|
|
||||||
bool isShared() const;
|
bool isShared() const;
|
||||||
|
@ -511,6 +517,9 @@ struct TableType
|
||||||
std::optional<TypeId> boundTo;
|
std::optional<TypeId> boundTo;
|
||||||
Tags tags;
|
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.
|
// We track the number of as-yet-unadded properties to unsealed tables.
|
||||||
// Some constraints will use this information to decide whether or not they
|
// Some constraints will use this information to decide whether or not they
|
||||||
// are able to dispatch.
|
// are able to dispatch.
|
||||||
|
@ -872,9 +881,6 @@ struct TypeFun
|
||||||
*/
|
*/
|
||||||
TypeId type;
|
TypeId type;
|
||||||
|
|
||||||
// The location of where this TypeFun was defined, if available
|
|
||||||
std::optional<Location> definitionLocation;
|
|
||||||
|
|
||||||
TypeFun() = default;
|
TypeFun() = default;
|
||||||
|
|
||||||
explicit TypeFun(TypeId ty)
|
explicit TypeFun(TypeId ty)
|
||||||
|
@ -882,23 +888,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))
|
: typeParams(std::move(typeParams))
|
||||||
, type(type)
|
, type(type)
|
||||||
, definitionLocation(definitionLocation)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFun(
|
TypeFun(std::vector<GenericTypeDefinition> typeParams, std::vector<GenericTypePackDefinition> typePackParams, TypeId type)
|
||||||
std::vector<GenericTypeDefinition> typeParams,
|
|
||||||
std::vector<GenericTypePackDefinition> typePackParams,
|
|
||||||
TypeId type,
|
|
||||||
std::optional<Location> definitionLocation = std::nullopt
|
|
||||||
)
|
|
||||||
: typeParams(std::move(typeParams))
|
: typeParams(std::move(typeParams))
|
||||||
, typePackParams(std::move(typePackParams))
|
, typePackParams(std::move(typePackParams))
|
||||||
, type(type)
|
, type(type)
|
||||||
, definitionLocation(definitionLocation)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,7 +1201,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)>;
|
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
|
||||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
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
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Polarity.h"
|
|
||||||
#include "Luau/TypedAllocator.h"
|
#include "Luau/TypedAllocator.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
|
@ -41,7 +40,7 @@ struct TypeArena
|
||||||
TypeId freshType_DEPRECATED(Scope* scope);
|
TypeId freshType_DEPRECATED(Scope* scope);
|
||||||
TypeId freshType_DEPRECATED(Scope* scope, TypeLevel level);
|
TypeId freshType_DEPRECATED(Scope* scope, TypeLevel level);
|
||||||
|
|
||||||
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
TypePackId freshTypePack(Scope* scope);
|
||||||
|
|
||||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||||
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
|
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
|
||||||
|
|
|
@ -177,7 +177,6 @@ struct FunctionGraphReductionResult
|
||||||
DenseHashSet<TypePackId> blockedPacks{nullptr};
|
DenseHashSet<TypePackId> blockedPacks{nullptr};
|
||||||
DenseHashSet<TypeId> reducedTypes{nullptr};
|
DenseHashSet<TypeId> reducedTypes{nullptr};
|
||||||
DenseHashSet<TypePackId> reducedPacks{nullptr};
|
DenseHashSet<TypePackId> reducedPacks{nullptr};
|
||||||
DenseHashSet<TypeId> irreducibleTypes{nullptr};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,8 +247,6 @@ struct BuiltinTypeFunctions
|
||||||
TypeFunction setmetatableFunc;
|
TypeFunction setmetatableFunc;
|
||||||
TypeFunction getmetatableFunc;
|
TypeFunction getmetatableFunc;
|
||||||
|
|
||||||
TypeFunction weakoptionalFunc;
|
|
||||||
|
|
||||||
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
|
||||||
#include "Luau/NotNull.h"
|
|
||||||
#include "Luau/Polarity.h"
|
|
||||||
#include "Luau/TypeFwd.h"
|
|
||||||
#include "Luau/Unifiable.h"
|
#include "Luau/Unifiable.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
|
#include "Luau/TypeFwd.h"
|
||||||
|
#include "Luau/NotNull.h"
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -27,14 +26,12 @@ struct TypeFunctionInstanceTypePack;
|
||||||
struct FreeTypePack
|
struct FreeTypePack
|
||||||
{
|
{
|
||||||
explicit FreeTypePack(TypeLevel level);
|
explicit FreeTypePack(TypeLevel level);
|
||||||
explicit FreeTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
explicit FreeTypePack(Scope* scope);
|
||||||
FreeTypePack(Scope* scope, TypeLevel level);
|
FreeTypePack(Scope* scope, TypeLevel level);
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
|
|
||||||
Polarity polarity = Polarity::Unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GenericTypePack
|
struct GenericTypePack
|
||||||
|
@ -43,7 +40,7 @@ struct GenericTypePack
|
||||||
GenericTypePack();
|
GenericTypePack();
|
||||||
explicit GenericTypePack(TypeLevel level);
|
explicit GenericTypePack(TypeLevel level);
|
||||||
explicit GenericTypePack(const Name& name);
|
explicit GenericTypePack(const Name& name);
|
||||||
explicit GenericTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
explicit GenericTypePack(Scope* scope);
|
||||||
GenericTypePack(TypeLevel level, const Name& name);
|
GenericTypePack(TypeLevel level, const Name& name);
|
||||||
GenericTypePack(Scope* scope, const Name& name);
|
GenericTypePack(Scope* scope, const Name& name);
|
||||||
|
|
||||||
|
@ -52,8 +49,6 @@ struct GenericTypePack
|
||||||
Scope* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
Name name;
|
Name name;
|
||||||
bool explicitName = false;
|
bool explicitName = false;
|
||||||
|
|
||||||
Polarity polarity = Polarity::Unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using BoundTypePack = Unifiable::Bound<TypePackId>;
|
using BoundTypePack = Unifiable::Bound<TypePackId>;
|
||||||
|
@ -105,9 +100,9 @@ struct TypeFunctionInstanceTypePack
|
||||||
|
|
||||||
struct TypePackVar
|
struct TypePackVar
|
||||||
{
|
{
|
||||||
explicit TypePackVar(const TypePackVariant& tp);
|
explicit TypePackVar(const TypePackVariant& ty);
|
||||||
explicit TypePackVar(TypePackVariant&& tp);
|
explicit TypePackVar(TypePackVariant&& ty);
|
||||||
TypePackVar(TypePackVariant&& tp, bool persistent);
|
TypePackVar(TypePackVariant&& ty, bool persistent);
|
||||||
|
|
||||||
bool operator==(const TypePackVar& rhs) const;
|
bool operator==(const TypePackVar& rhs) const;
|
||||||
|
|
||||||
|
@ -174,7 +169,6 @@ struct TypePackIterator
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TypePackId currentTypePack = nullptr;
|
TypePackId currentTypePack = nullptr;
|
||||||
TypePackId tailCycleCheck = nullptr;
|
|
||||||
const TypePack* tp = nullptr;
|
const TypePack* tp = nullptr;
|
||||||
size_t currentIndex = 0;
|
size_t currentIndex = 0;
|
||||||
|
|
||||||
|
|
|
@ -289,6 +289,4 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
||||||
*/
|
*/
|
||||||
void trackInteriorFreeType(Scope* scope, TypeId ty);
|
void trackInteriorFreeType(Scope* scope, TypeId ty);
|
||||||
|
|
||||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp);
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -44,12 +44,6 @@ struct Unifier2
|
||||||
// Mapping from generic type packs to `TypePack`s of free types to be used in instantiation.
|
// Mapping from generic type packs to `TypePack`s of free types to be used in instantiation.
|
||||||
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr};
|
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 recursionCount = 0;
|
||||||
int recursionLimit = 0;
|
int recursionLimit = 0;
|
||||||
|
|
||||||
|
@ -119,9 +113,6 @@ private:
|
||||||
// Returns true if needle occurs within haystack already. ie if we bound
|
// Returns true if needle occurs within haystack already. ie if we bound
|
||||||
// needle to haystack, would a cyclic TypePack result?
|
// needle to haystack, would a cyclic TypePack result?
|
||||||
OccursCheckResult occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
|
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
|
} // namespace Luau
|
||||||
|
|
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
|
|
@ -1151,8 +1151,6 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
return writeString("checked");
|
return writeString("checked");
|
||||||
case AstAttr::Type::Native:
|
case AstAttr::Type::Native:
|
||||||
return writeString("native");
|
return writeString("native");
|
||||||
case AstAttr::Type::Deprecated:
|
|
||||||
return writeString("deprecated");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -40,6 +42,8 @@ struct AutocompleteNodeFinder : public AstVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(AstStat* stat) override
|
bool visit(AstStat* stat) override
|
||||||
|
{
|
||||||
|
if (FFlag::LuauExtendStatEndPosWithSemicolon)
|
||||||
{
|
{
|
||||||
// Consider 'local myLocal = 4;|' and 'local myLocal = 4', where '|' is the cursor position. In both cases, the cursor position is equal
|
// 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
|
// to `AstStatLocal.location.end`. However, in the first case (semicolon), we are starting a new statement, whilst in the second case
|
||||||
|
@ -49,6 +53,15 @@ struct AutocompleteNodeFinder : public AstVisitor
|
||||||
ancestry.push_back(stat);
|
ancestry.push_back(stat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat->location.begin < pos && pos <= stat->location.end)
|
||||||
|
{
|
||||||
|
ancestry.push_back(stat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,22 @@
|
||||||
|
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Clone.h"
|
#include "Luau/Clone.h"
|
||||||
#include "Luau/Common.h"
|
|
||||||
#include "Luau/ConstraintGenerator.h"
|
|
||||||
#include "Luau/ConstraintSolver.h"
|
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
#include "Luau/InferPolarity.h"
|
|
||||||
#include "Luau/NotNull.h"
|
|
||||||
#include "Luau/Subtyping.h"
|
|
||||||
#include "Luau/Symbol.h"
|
#include "Luau/Symbol.h"
|
||||||
|
#include "Luau/Common.h"
|
||||||
#include "Luau/ToString.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/TypeChecker2.h"
|
||||||
#include "Luau/TypeFunction.h"
|
#include "Luau/TypeFunction.h"
|
||||||
#include "Luau/TypeInfer.h"
|
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
|
#include "Luau/Subtyping.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -30,12 +29,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -249,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)
|
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;
|
scope->bindings[globals.globalNames.names->getOrAdd(name.c_str())] = binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,9 +311,6 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
|
|
||||||
TypeArena& arena = globals.globalTypes;
|
TypeArena& arena = globals.globalTypes;
|
||||||
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
||||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
globalScope = globals.globalScope.get();
|
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()});
|
builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()});
|
||||||
|
@ -326,8 +320,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
);
|
);
|
||||||
LUAU_ASSERT(loadResult.success);
|
LUAU_ASSERT(loadResult.success);
|
||||||
|
|
||||||
TypeId genericK = arena.addType(GenericType{globalScope, "K"});
|
TypeId genericK = arena.addType(GenericType{"K"});
|
||||||
TypeId genericV = arena.addType(GenericType{globalScope, "V"});
|
TypeId genericV = arena.addType(GenericType{"V"});
|
||||||
TypeId mapOfKtoV = arena.addType(TableType{{}, TableIndexer(genericK, genericV), globals.globalScope->level, TableState::Generic});
|
TypeId mapOfKtoV = arena.addType(TableType{{}, TableIndexer(genericK, genericV), globals.globalScope->level, TableState::Generic});
|
||||||
|
|
||||||
std::optional<TypeId> stringMetatableTy = getMetatable(builtinTypes->stringType, builtinTypes);
|
std::optional<TypeId> stringMetatableTy = getMetatable(builtinTypes->stringType, builtinTypes);
|
||||||
|
@ -375,7 +369,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)
|
// 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");
|
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};
|
TableType tab{TableState::Generic, globals.globalScope->level};
|
||||||
TypeId tabTy = arena.addType(tab);
|
TypeId tabTy = arena.addType(tab);
|
||||||
|
@ -387,7 +381,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
TypeId genericT = arena.addType(GenericType{"T"});
|
||||||
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -444,7 +438,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
// declare function assert<T>(value: T, errorMessage: string?): intersect<T, ~(false?)>
|
// 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{
|
TypeId refinedTy = arena.addType(TypeFunctionInstanceType{
|
||||||
NotNull{&builtinTypeFunctions().intersectFunc}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}
|
NotNull{&builtinTypeFunctions().intersectFunc}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}
|
||||||
});
|
});
|
||||||
|
@ -467,16 +461,12 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
// the top table type. We do the best we can by modelling these
|
// the top table type. We do the best we can by modelling these
|
||||||
// functions using unconstrained generics. It's not quite right,
|
// functions using unconstrained generics. It's not quite right,
|
||||||
// but it'll be ok for now.
|
// 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});
|
TypePackId thePack = arena.addTypePack({genericTy});
|
||||||
TypeId idTyWithMagic = arena.addType(FunctionType{{genericTy}, {}, thePack, thePack});
|
TypeId idTyWithMagic = arena.addType(FunctionType{{genericTy}, {}, thePack, thePack});
|
||||||
ttv->props["freeze"] = makeProperty(idTyWithMagic, "@luau/global/table.freeze");
|
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});
|
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");
|
ttv->props["clone"] = makeProperty(idTy, "@luau/global/table.clone");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -721,6 +711,8 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
||||||
|
|
||||||
if (!result.isSubtype)
|
if (!result.isSubtype)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauStringFormatErrorSuppression)
|
||||||
{
|
{
|
||||||
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
||||||
{
|
{
|
||||||
|
@ -735,6 +727,12 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1609,17 +1607,6 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
|
||||||
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
|
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
|
||||||
std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
|
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);
|
std::optional<TypeId> frozenType = freezeTable(inputType, context);
|
||||||
|
|
||||||
if (!frozenType)
|
if (!frozenType)
|
||||||
|
|
|
@ -179,6 +179,8 @@ public:
|
||||||
generic->scope = nullptr;
|
generic->scope = nullptr;
|
||||||
else if (auto free = getMutable<FreeType>(target))
|
else if (auto free = getMutable<FreeType>(target))
|
||||||
free->scope = nullptr;
|
free->scope = nullptr;
|
||||||
|
else if (auto fn = getMutable<FunctionType>(target))
|
||||||
|
fn->scope = nullptr;
|
||||||
else if (auto table = getMutable<TableType>(target))
|
else if (auto table = getMutable<TableType>(target))
|
||||||
table->scope = nullptr;
|
table->scope = nullptr;
|
||||||
|
|
||||||
|
@ -519,6 +521,11 @@ public:
|
||||||
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
|
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||||
tt->scope = replacementForNullScope;
|
tt->scope = replacementForNullScope;
|
||||||
}
|
}
|
||||||
|
else if (auto fn = getMutable<FunctionType>(target))
|
||||||
|
{
|
||||||
|
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||||
|
fn->scope = replacementForNullScope;
|
||||||
|
}
|
||||||
|
|
||||||
(*types)[ty] = target;
|
(*types)[ty] = target;
|
||||||
queue.emplace_back(target);
|
queue.emplace_back(target);
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "Luau/DcrLogger.h"
|
#include "Luau/DcrLogger.h"
|
||||||
#include "Luau/Def.h"
|
#include "Luau/Def.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/InferPolarity.h"
|
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
|
@ -33,12 +32,11 @@
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPropagateExpectedTypesForCalls)
|
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGlobalSelfAssignmentCycle)
|
LUAU_FASTFLAGVARIABLE(LuauGlobalSelfAssignmentCycle)
|
||||||
|
|
||||||
|
@ -47,12 +45,6 @@ LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauExtraFollows)
|
LUAU_FASTFLAGVARIABLE(LuauExtraFollows)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -230,10 +222,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
rootScope->location = block->location;
|
rootScope->location = block->location;
|
||||||
module->astScopes[block] = NotNull{scope.get()};
|
module->astScopes[block] = NotNull{scope.get()};
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
rootScope->returnType = freshTypePack(scope);
|
||||||
interiorFreeTypes.emplace_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
if (FFlag::LuauUserTypeFunTypecheck)
|
||||||
{
|
{
|
||||||
|
@ -243,8 +232,8 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
typeFunctionRuntime->rootScope = localTypeFunctionScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootScope->returnType = freshTypePack(scope, Polarity::Positive);
|
TypeId moduleFnTy = arena->addType(FunctionType{TypeLevel{}, rootScope, builtinTypes->anyTypePack, rootScope->returnType});
|
||||||
TypeId moduleFnTy = arena->addType(FunctionType{TypeLevel{}, builtinTypes->anyTypePack, rootScope->returnType});
|
interiorTypes.emplace_back();
|
||||||
|
|
||||||
prepopulateGlobalScope(scope, block);
|
prepopulateGlobalScope(scope, block);
|
||||||
|
|
||||||
|
@ -261,20 +250,12 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
scope,
|
scope,
|
||||||
block->location,
|
block->location,
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
result,
|
result, moduleFnTy, FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(interiorTypes.back())
|
||||||
moduleFnTy,
|
|
||||||
(FFlag::LuauNonReentrantGeneralization || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
{
|
scope->interiorFreeTypes = std::move(interiorTypes.back());
|
||||||
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
|
||||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
|
||||||
}
|
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
scope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
|
||||||
|
|
||||||
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
|
@ -287,10 +268,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.pop_back();
|
||||||
interiorFreeTypes.pop_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
|
||||||
|
|
||||||
fillInInferredBindings(scope, block);
|
fillInInferredBindings(scope, block);
|
||||||
|
|
||||||
|
@ -319,16 +297,11 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
||||||
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
||||||
// Pre
|
// Pre
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
// We need to pop the interior types,
|
||||||
interiorFreeTypes.emplace_back();
|
interiorTypes.emplace_back();
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
|
||||||
visitBlockWithoutChildScope(resumeScope, block);
|
visitBlockWithoutChildScope(resumeScope, block);
|
||||||
// Post
|
// Post
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.pop_back();
|
||||||
interiorFreeTypes.pop_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
|
||||||
|
|
||||||
fillInInferredBindings(resumeScope, block);
|
fillInInferredBindings(resumeScope, block);
|
||||||
|
|
||||||
|
@ -353,18 +326,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
TypeId ConstraintGenerator::freshType(const ScopePtr& scope)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
{
|
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
|
||||||
interiorFreeTypes.back().types.push_back(ft);
|
|
||||||
return ft;
|
|
||||||
}
|
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
{
|
{
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
||||||
DEPRECATED_interiorTypes.back().push_back(ft);
|
interiorTypes.back().push_back(ft);
|
||||||
return ft;
|
return ft;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -373,13 +340,10 @@ TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity polarity)
|
TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope)
|
||||||
{
|
{
|
||||||
FreeTypePack f{scope.get(), polarity};
|
FreeTypePack f{scope.get()};
|
||||||
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
return arena->addTypePack(TypePackVar{std::move(f)});
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
interiorFreeTypes.back().typePacks.push_back(result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGenerator::addTypePack(std::vector<TypeId> head, std::optional<TypePackId> tail)
|
TypePackId ConstraintGenerator::addTypePack(std::vector<TypeId> head, std::optional<TypePackId> tail)
|
||||||
|
@ -685,7 +649,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
||||||
if (std::optional<TypeId> defTy = lookup(scope, location, def))
|
if (std::optional<TypeId> defTy = lookup(scope, location, def))
|
||||||
{
|
{
|
||||||
TypeId ty = *defTy;
|
TypeId ty = *defTy;
|
||||||
if (!FFlag::LuauWeakNilRefinementType && partition.shouldAppendNilType)
|
if (partition.shouldAppendNilType)
|
||||||
ty = arena->addType(UnionType{{ty, builtinTypes->nilType}});
|
ty = arena->addType(UnionType{{ty, builtinTypes->nilType}});
|
||||||
// Intersect ty with every discriminant type. If either type is not
|
// Intersect ty with every discriminant type. If either type is not
|
||||||
// sufficiently solved, we queue the intersection up via an
|
// sufficiently solved, we queue the intersection up via an
|
||||||
|
@ -733,9 +697,6 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
||||||
if (kind != RefinementsOpKind::None)
|
if (kind != RefinementsOpKind::None)
|
||||||
ty = flushConstraints(kind, ty, discriminants);
|
ty = flushConstraints(kind, ty, discriminants);
|
||||||
|
|
||||||
if (FFlag::LuauWeakNilRefinementType && partition.shouldAppendNilType)
|
|
||||||
ty = createTypeFunctionInstance(builtinTypeFunctions().weakoptionalFunc, {ty}, {}, scope, location);
|
|
||||||
|
|
||||||
scope->rvalueRefinements[def] = ty;
|
scope->rvalueRefinements[def] = ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -787,9 +748,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
initialFun.typePackParams.push_back(genPack);
|
initialFun.typePackParams.push_back(genPack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
initialFun.definitionLocation = alias->location;
|
|
||||||
|
|
||||||
if (alias->exported)
|
if (alias->exported)
|
||||||
scope->exportedTypeBindings[alias->name.value] = std::move(initialFun);
|
scope->exportedTypeBindings[alias->name.value] = std::move(initialFun);
|
||||||
else
|
else
|
||||||
|
@ -847,9 +805,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
||||||
|
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
typeFunction.definitionLocation = function->location;
|
|
||||||
|
|
||||||
// Set type bindings and definition locations for this user-defined type function
|
// Set type bindings and definition locations for this user-defined type function
|
||||||
if (function->exported)
|
if (function->exported)
|
||||||
scope->exportedTypeBindings[function->name.value] = std::move(typeFunction);
|
scope->exportedTypeBindings[function->name.value] = std::move(typeFunction);
|
||||||
|
@ -877,8 +832,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
TypeId initialType = arena->addType(BlockedType{});
|
TypeId initialType = arena->addType(BlockedType{});
|
||||||
TypeFun initialFun{initialType};
|
TypeFun initialFun{initialType};
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
initialFun.definitionLocation = classDeclaration->location;
|
|
||||||
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
||||||
|
|
||||||
classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location;
|
classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location;
|
||||||
|
@ -1404,23 +1357,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatRepeat* rep
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void propagateDeprecatedAttributeToConstraint(ConstraintV& c, const AstExprFunction* func)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
if (GeneralizationConstraint* genConstraint = c.get_if<GeneralizationConstraint>())
|
|
||||||
{
|
|
||||||
genConstraint->hasDeprecatedAttribute = func->hasAttribute(AstAttr::Type::Deprecated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void propagateDeprecatedAttributeToType(TypeId signature, const AstExprFunction* func)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
FunctionType* fty = getMutable<FunctionType>(signature);
|
|
||||||
LUAU_ASSERT(fty);
|
|
||||||
fty->isDeprecatedFunction = func->hasAttribute(AstAttr::Type::Deprecated);
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
||||||
{
|
{
|
||||||
// Local
|
// Local
|
||||||
|
@ -1458,9 +1394,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
||||||
std::unique_ptr<Constraint> c =
|
std::unique_ptr<Constraint> c =
|
||||||
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
||||||
|
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
start,
|
start,
|
||||||
|
@ -1484,11 +1417,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
||||||
module->astTypes[function->func] = functionType;
|
module->astTypes[function->func] = functionType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
module->astTypes[function->func] = sig.signature;
|
module->astTypes[function->func] = sig.signature;
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
@ -1529,11 +1458,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
|
|
||||||
TypeId generalizedType = arena->addType(BlockedType{});
|
TypeId generalizedType = arena->addType(BlockedType{});
|
||||||
if (sigFullyDefined)
|
if (sigFullyDefined)
|
||||||
{
|
|
||||||
emplaceType<BoundType>(asMutable(generalizedType), sig.signature);
|
emplaceType<BoundType>(asMutable(generalizedType), sig.signature);
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const ScopePtr& constraintScope = sig.signatureScope ? sig.signatureScope : sig.bodyScope;
|
const ScopePtr& constraintScope = sig.signatureScope ? sig.signatureScope : sig.bodyScope;
|
||||||
|
@ -1541,9 +1466,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
NotNull<Constraint> c = addConstraint(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
NotNull<Constraint> c = addConstraint(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
||||||
getMutable<BlockedType>(generalizedType)->setOwner(c);
|
getMutable<BlockedType>(generalizedType)->setOwner(c);
|
||||||
|
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
start,
|
start,
|
||||||
|
@ -1817,10 +1739,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
// Place this function as a child of the non-type function scope
|
// Place this function as a child of the non-type function scope
|
||||||
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
interiorFreeTypes.emplace_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
|
||||||
checkFunctionBody(sig.bodyScope, function->body);
|
checkFunctionBody(sig.bodyScope, function->body);
|
||||||
Checkpoint endCheckpoint = checkpoint(this);
|
Checkpoint endCheckpoint = checkpoint(this);
|
||||||
|
|
||||||
|
@ -1829,25 +1748,15 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
||||||
sig.signatureScope,
|
sig.signatureScope,
|
||||||
function->location,
|
function->location,
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
generalizedTy,
|
generalizedTy, sig.signature, FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(interiorTypes.back())
|
||||||
sig.signature,
|
|
||||||
FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(DEPRECATED_interiorTypes.back())
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
{
|
sig.signatureScope->interiorFreeTypes = std::move(interiorTypes.back());
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
|
||||||
}
|
|
||||||
else if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.pop_back();
|
||||||
interiorFreeTypes.pop_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
|
@ -2069,13 +1978,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
||||||
defn.varargLocation = global->vararg ? std::make_optional(global->varargLocation) : std::nullopt;
|
defn.varargLocation = global->vararg ? std::make_optional(global->varargLocation) : std::nullopt;
|
||||||
defn.originalNameLocation = global->nameLocation;
|
defn.originalNameLocation = global->nameLocation;
|
||||||
|
|
||||||
TypeId fnType = arena->addType(FunctionType{TypeLevel{}, std::move(genericTys), std::move(genericTps), paramPack, retPack, defn});
|
TypeId fnType = arena->addType(FunctionType{TypeLevel{}, funScope.get(), std::move(genericTys), std::move(genericTps), paramPack, retPack, defn});
|
||||||
inferGenericPolarities(arena, NotNull{scope.get()}, fnType);
|
|
||||||
|
|
||||||
FunctionType* ftv = getMutable<FunctionType>(fnType);
|
FunctionType* ftv = getMutable<FunctionType>(fnType);
|
||||||
ftv->isCheckedFunction = global->isCheckedFunction();
|
ftv->isCheckedFunction = global->isCheckedFunction();
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
ftv->isDeprecatedFunction = global->hasAttribute(AstAttr::Type::Deprecated);
|
|
||||||
|
|
||||||
ftv->argNames.reserve(global->paramNames.size);
|
ftv->argNames.reserve(global->paramNames.size);
|
||||||
for (const auto& el : global->paramNames)
|
for (const auto& el : global->paramNames)
|
||||||
|
@ -2239,27 +2144,17 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
if (selfTy)
|
if (selfTy)
|
||||||
args.push_back(*selfTy);
|
args.push_back(*selfTy);
|
||||||
else
|
else
|
||||||
args.push_back(freshType(scope, Polarity::Negative));
|
args.push_back(freshType(scope));
|
||||||
}
|
}
|
||||||
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
|
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
|
||||||
{
|
{
|
||||||
std::optional<TypeId> expectedType = std::nullopt;
|
auto [ty, refinement] = check(scope, arg, /*expectedType*/ std::nullopt, /*forceSingleton*/ false, /*generalize*/ false);
|
||||||
if (FFlag::LuauPropagateExpectedTypesForCalls && i < expectedTypesForCall.size())
|
|
||||||
{
|
|
||||||
expectedType = expectedTypesForCall[i];
|
|
||||||
}
|
|
||||||
auto [ty, refinement] = check(scope, arg, expectedType, /*forceSingleton*/ false, /*generalize*/ false);
|
|
||||||
args.push_back(ty);
|
args.push_back(ty);
|
||||||
argumentRefinements.push_back(refinement);
|
argumentRefinements.push_back(refinement);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<std::optional<Luau::TypeId>> expectedTypes = {};
|
auto [tp, refis] = checkPack(scope, arg, {});
|
||||||
if (FFlag::LuauPropagateExpectedTypesForCalls && i < expectedTypesForCall.size())
|
|
||||||
{
|
|
||||||
expectedTypes.insert(expectedTypes.end(), expectedTypesForCall.begin() + int(i), expectedTypesForCall.end());
|
|
||||||
}
|
|
||||||
auto [tp, refis] = checkPack(scope, arg, expectedTypes);
|
|
||||||
argTail = tp;
|
argTail = tp;
|
||||||
argumentRefinements.insert(argumentRefinements.end(), refis.begin(), refis.end());
|
argumentRefinements.insert(argumentRefinements.end(), refis.begin(), refis.end());
|
||||||
}
|
}
|
||||||
|
@ -2361,7 +2256,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
// TODO: How do expectedTypes play into this? Do they?
|
// TODO: How do expectedTypes play into this? Do they?
|
||||||
TypePackId rets = arena->addTypePack(BlockedTypePack{});
|
TypePackId rets = arena->addTypePack(BlockedTypePack{});
|
||||||
TypePackId argPack = addTypePack(std::move(args), argTail);
|
TypePackId argPack = addTypePack(std::move(args), argTail);
|
||||||
FunctionType ftv(TypeLevel{}, argPack, rets, std::nullopt, call->self);
|
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets, std::nullopt, call->self);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To make bidirectional type checking work, we need to solve these constraints in a particular order:
|
* To make bidirectional type checking work, we need to solve these constraints in a particular order:
|
||||||
|
@ -2428,16 +2323,6 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||||
return Inference{builtinTypes->errorRecoveryType()};
|
return Inference{builtinTypes->errorRecoveryType()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// We may recurse a given expression more than once when checking compound
|
|
||||||
// assignment, so we store and cache expressions here s.t. when we generate
|
|
||||||
// constraints for something like:
|
|
||||||
//
|
|
||||||
// a[b] += c
|
|
||||||
//
|
|
||||||
// We only solve _one_ set of constraints for `b`.
|
|
||||||
if (FFlag::LuauCacheInferencePerAstExpr && inferredExprCache.contains(expr))
|
|
||||||
return inferredExprCache[expr];
|
|
||||||
|
|
||||||
Inference result;
|
Inference result;
|
||||||
|
|
||||||
if (auto group = expr->as<AstExprGroup>())
|
if (auto group = expr->as<AstExprGroup>())
|
||||||
|
@ -2490,9 +2375,6 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||||
result = Inference{freshType(scope)};
|
result = Inference{freshType(scope)};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauCacheInferencePerAstExpr)
|
|
||||||
inferredExprCache[expr] = result;
|
|
||||||
|
|
||||||
LUAU_ASSERT(result.ty);
|
LUAU_ASSERT(result.ty);
|
||||||
module->astTypes[expr] = result.ty;
|
module->astTypes[expr] = result.ty;
|
||||||
if (expectedType)
|
if (expectedType)
|
||||||
|
@ -2505,24 +2387,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
||||||
if (forceSingleton)
|
if (forceSingleton)
|
||||||
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
|
||||||
LUAU_ASSERT(ft);
|
|
||||||
ft->lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
|
||||||
ft->upperBound = builtinTypes->stringType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FreeType ft =
|
FreeType ft =
|
||||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
||||||
ft.lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
ft.lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
||||||
ft.upperBound = builtinTypes->stringType;
|
ft.upperBound = builtinTypes->stringType;
|
||||||
freeTy = arena->addType(ft);
|
const TypeId freeTy = arena->addType(ft);
|
||||||
}
|
|
||||||
|
|
||||||
addConstraint(scope, string->location, PrimitiveTypeConstraint{freeTy, expectedType, builtinTypes->stringType});
|
addConstraint(scope, string->location, PrimitiveTypeConstraint{freeTy, expectedType, builtinTypes->stringType});
|
||||||
return Inference{freeTy};
|
return Inference{freeTy};
|
||||||
}
|
}
|
||||||
|
@ -2533,24 +2402,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
if (forceSingleton)
|
if (forceSingleton)
|
||||||
return Inference{singletonType};
|
return Inference{singletonType};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
|
||||||
LUAU_ASSERT(ft);
|
|
||||||
ft->lowerBound = singletonType;
|
|
||||||
ft->upperBound = builtinTypes->booleanType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FreeType ft =
|
FreeType ft =
|
||||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
||||||
ft.lowerBound = singletonType;
|
ft.lowerBound = singletonType;
|
||||||
ft.upperBound = builtinTypes->booleanType;
|
ft.upperBound = builtinTypes->booleanType;
|
||||||
freeTy = arena->addType(ft);
|
const TypeId freeTy = arena->addType(ft);
|
||||||
}
|
|
||||||
|
|
||||||
addConstraint(scope, boolExpr->location, PrimitiveTypeConstraint{freeTy, expectedType, builtinTypes->booleanType});
|
addConstraint(scope, boolExpr->location, PrimitiveTypeConstraint{freeTy, expectedType, builtinTypes->booleanType});
|
||||||
return Inference{freeTy};
|
return Inference{freeTy};
|
||||||
}
|
}
|
||||||
|
@ -2558,7 +2414,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprLocal* local)
|
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprLocal* local)
|
||||||
{
|
{
|
||||||
const RefinementKey* key = dfg->getRefinementKey(local);
|
const RefinementKey* key = dfg->getRefinementKey(local);
|
||||||
LUAU_ASSERT(key);
|
std::optional<DefId> rvalueDef = dfg->getRValueDefForCompoundAssign(local);
|
||||||
|
LUAU_ASSERT(key || rvalueDef);
|
||||||
|
|
||||||
std::optional<TypeId> maybeTy;
|
std::optional<TypeId> maybeTy;
|
||||||
|
|
||||||
|
@ -2566,6 +2423,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprLocal* local)
|
||||||
if (key)
|
if (key)
|
||||||
maybeTy = lookup(scope, local->location, key->def);
|
maybeTy = lookup(scope, local->location, key->def);
|
||||||
|
|
||||||
|
// if the current def doesn't have a type, we might be doing a compound assignment
|
||||||
|
// and therefore might need to look at the rvalue def instead.
|
||||||
|
if (!maybeTy && rvalueDef)
|
||||||
|
maybeTy = lookup(scope, local->location, *rvalueDef);
|
||||||
|
|
||||||
if (maybeTy)
|
if (maybeTy)
|
||||||
{
|
{
|
||||||
TypeId ty = follow(*maybeTy);
|
TypeId ty = follow(*maybeTy);
|
||||||
|
@ -2581,9 +2443,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprLocal* local)
|
||||||
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprGlobal* global)
|
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprGlobal* global)
|
||||||
{
|
{
|
||||||
const RefinementKey* key = dfg->getRefinementKey(global);
|
const RefinementKey* key = dfg->getRefinementKey(global);
|
||||||
LUAU_ASSERT(key);
|
std::optional<DefId> rvalueDef = dfg->getRValueDefForCompoundAssign(global);
|
||||||
|
LUAU_ASSERT(key || rvalueDef);
|
||||||
|
|
||||||
DefId def = key->def;
|
// we'll use whichever of the two definitions we have here.
|
||||||
|
DefId def = key ? key->def : *rvalueDef;
|
||||||
|
|
||||||
/* prepopulateGlobalScope() has already added all global functions to the environment by this point, so any
|
/* prepopulateGlobalScope() has already added all global functions to the environment by this point, so any
|
||||||
* global that is not already in-scope is definitely an unknown symbol.
|
* global that is not already in-scope is definitely an unknown symbol.
|
||||||
|
@ -2696,10 +2560,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
Checkpoint startCheckpoint = checkpoint(this);
|
Checkpoint startCheckpoint = checkpoint(this);
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.push_back(std::vector<TypeId>{});
|
||||||
interiorFreeTypes.emplace_back();
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
|
||||||
checkFunctionBody(sig.bodyScope, func);
|
checkFunctionBody(sig.bodyScope, func);
|
||||||
Checkpoint endCheckpoint = checkpoint(this);
|
Checkpoint endCheckpoint = checkpoint(this);
|
||||||
|
|
||||||
|
@ -2708,29 +2569,15 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
sig.signatureScope,
|
sig.signatureScope,
|
||||||
func->location,
|
func->location,
|
||||||
GeneralizationConstraint{
|
GeneralizationConstraint{
|
||||||
generalizedTy,
|
generalizedTy, sig.signature, FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(interiorTypes.back())
|
||||||
sig.signature,
|
|
||||||
(FFlag::LuauNonReentrantGeneralization || FFlag::LuauTrackInteriorFreeTypesOnScope) ? std::vector<TypeId>{}
|
|
||||||
: std::move(DEPRECATED_interiorTypes.back())
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
|
||||||
interiorFreeTypes.pop_back();
|
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
sig.signatureScope->interiorFreeTypes = std::move(interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
interiorTypes.pop_back();
|
||||||
}
|
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
|
@ -3210,10 +3057,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
ttv->definitionLocation = expr->location;
|
ttv->definitionLocation = expr->location;
|
||||||
ttv->scope = scope.get();
|
ttv->scope = scope.get();
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
interiorTypes.back().push_back(ty);
|
||||||
interiorFreeTypes.back().types.push_back(ty);
|
|
||||||
else
|
|
||||||
DEPRECATED_interiorTypes.back().push_back(ty);
|
|
||||||
|
|
||||||
TypeIds indexKeyLowerBound;
|
TypeIds indexKeyLowerBound;
|
||||||
TypeIds indexValueLowerBound;
|
TypeIds indexValueLowerBound;
|
||||||
|
@ -3275,6 +3119,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectedType)
|
if (expectedType)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauDeferBidirectionalInferenceForTableAssignment)
|
||||||
{
|
{
|
||||||
addConstraint(
|
addConstraint(
|
||||||
scope,
|
scope,
|
||||||
|
@ -3288,6 +3134,36 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Unifier2 unifier{arena, builtinTypes, NotNull{scope.get()}, ice};
|
||||||
|
Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, ice};
|
||||||
|
std::vector<TypeId> toBlock;
|
||||||
|
// This logic is incomplete as we want to re-run this
|
||||||
|
// _after_ blocked types have resolved, but this
|
||||||
|
// allows us to do some bidirectional inference.
|
||||||
|
toBlock = findBlockedTypesIn(expr, NotNull{&module->astTypes});
|
||||||
|
if (toBlock.empty())
|
||||||
|
{
|
||||||
|
matchLiteralType(
|
||||||
|
NotNull{&module->astTypes},
|
||||||
|
NotNull{&module->astExpectedTypes},
|
||||||
|
builtinTypes,
|
||||||
|
arena,
|
||||||
|
NotNull{&unifier},
|
||||||
|
NotNull{&sp},
|
||||||
|
*expectedType,
|
||||||
|
ty,
|
||||||
|
expr,
|
||||||
|
toBlock
|
||||||
|
);
|
||||||
|
// The visitor we ran prior should ensure that there are no
|
||||||
|
// blocked types that we would encounter while matching on
|
||||||
|
// this expression.
|
||||||
|
LUAU_ASSERT(toBlock.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Inference{ty};
|
return Inference{ty};
|
||||||
}
|
}
|
||||||
|
@ -3315,7 +3191,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
|
|
||||||
// We need to assign returnType before creating bodyScope so that the
|
// We need to assign returnType before creating bodyScope so that the
|
||||||
// return type gets propagated to bodyScope.
|
// return type gets propagated to bodyScope.
|
||||||
returnType = freshTypePack(signatureScope, Polarity::Positive);
|
returnType = freshTypePack(signatureScope);
|
||||||
signatureScope->returnType = returnType;
|
signatureScope->returnType = returnType;
|
||||||
|
|
||||||
bodyScope = childScope(fn->body, signatureScope);
|
bodyScope = childScope(fn->body, signatureScope);
|
||||||
|
@ -3395,7 +3271,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
if (i < expectedArgPack.head.size())
|
if (i < expectedArgPack.head.size())
|
||||||
argTy = expectedArgPack.head[i];
|
argTy = expectedArgPack.head[i];
|
||||||
else
|
else
|
||||||
argTy = freshType(signatureScope, Polarity::Negative);
|
argTy = freshType(signatureScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
argTypes.push_back(argTy);
|
argTypes.push_back(argTy);
|
||||||
|
@ -3457,7 +3333,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
|
|
||||||
// TODO: Preserve argument names in the function's type.
|
// TODO: Preserve argument names in the function's type.
|
||||||
|
|
||||||
FunctionType actualFunction{TypeLevel{}, arena->addTypePack(argTypes, varargPack), returnType};
|
FunctionType actualFunction{TypeLevel{}, parent.get(), arena->addTypePack(argTypes, varargPack), returnType};
|
||||||
actualFunction.generics = std::move(genericTypes);
|
actualFunction.generics = std::move(genericTypes);
|
||||||
actualFunction.genericPacks = std::move(genericTypePacks);
|
actualFunction.genericPacks = std::move(genericTypePacks);
|
||||||
actualFunction.argNames = std::move(argNames);
|
actualFunction.argNames = std::move(argNames);
|
||||||
|
@ -3474,8 +3350,6 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
LUAU_ASSERT(actualFunctionType);
|
LUAU_ASSERT(actualFunctionType);
|
||||||
module->astTypes[fn] = actualFunctionType;
|
module->astTypes[fn] = actualFunctionType;
|
||||||
|
|
||||||
inferGenericPolarities(arena, NotNull{signatureScope.get()}, actualFunctionType);
|
|
||||||
|
|
||||||
if (expectedType && get<FreeType>(*expectedType))
|
if (expectedType && get<FreeType>(*expectedType))
|
||||||
bindFreeType(*expectedType, actualFunctionType);
|
bindFreeType(*expectedType, actualFunctionType);
|
||||||
|
|
||||||
|
@ -3520,7 +3394,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
|
||||||
return builtinTypes->errorRecoveryType();
|
return builtinTypes->errorRecoveryType();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments);
|
return resolveType(scope, ref->parameters.data[0].type, inTypeArguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3553,11 +3427,11 @@ TypeId ConstraintGenerator::resolveReferenceType(
|
||||||
// that is done in the parser.
|
// that is done in the parser.
|
||||||
if (p.type)
|
if (p.type)
|
||||||
{
|
{
|
||||||
parameters.push_back(resolveType_(scope, p.type, /* inTypeArguments */ true));
|
parameters.push_back(resolveType(scope, p.type, /* inTypeArguments */ true));
|
||||||
}
|
}
|
||||||
else if (p.typePack)
|
else if (p.typePack)
|
||||||
{
|
{
|
||||||
TypePackId tp = resolveTypePack_(scope, p.typePack, /*inTypeArguments*/ true);
|
TypePackId tp = resolveTypePack(scope, p.typePack, /*inTypeArguments*/ true);
|
||||||
|
|
||||||
// If we need more regular types, we can use single element type packs to fill those in
|
// If we need more regular types, we can use single element type packs to fill those in
|
||||||
if (parameters.size() < alias->typeParams.size() && size(tp) == 1 && finite(tp) && first(tp))
|
if (parameters.size() < alias->typeParams.size() && size(tp) == 1 && finite(tp) && first(tp))
|
||||||
|
@ -3586,7 +3460,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
|
||||||
{
|
{
|
||||||
result = builtinTypes->errorRecoveryType();
|
result = builtinTypes->errorRecoveryType();
|
||||||
if (replaceErrorWithFresh)
|
if (replaceErrorWithFresh)
|
||||||
result = freshType(scope, Polarity::Mixed);
|
result = freshType(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -3599,7 +3473,7 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
|
||||||
|
|
||||||
for (const AstTableProp& prop : tab->props)
|
for (const AstTableProp& prop : tab->props)
|
||||||
{
|
{
|
||||||
TypeId propTy = resolveType_(scope, prop.type, inTypeArguments);
|
TypeId propTy = resolveType(scope, prop.type, inTypeArguments);
|
||||||
|
|
||||||
Property& p = props[prop.name.value];
|
Property& p = props[prop.name.value];
|
||||||
p.typeLocation = prop.location;
|
p.typeLocation = prop.location;
|
||||||
|
@ -3691,18 +3565,13 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
||||||
signatureScope = scope;
|
signatureScope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes};
|
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes, inTypeArguments, replaceErrorWithFresh);
|
||||||
TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh);
|
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
||||||
|
|
||||||
AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes};
|
|
||||||
TypePackId returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh);
|
|
||||||
|
|
||||||
// TODO: FunctionType needs a pointer to the scope so that we know
|
// TODO: FunctionType needs a pointer to the scope so that we know
|
||||||
// how to quantify/instantiate it.
|
// how to quantify/instantiate it.
|
||||||
FunctionType ftv{TypeLevel{}, {}, {}, argTypes, returnTypes};
|
FunctionType ftv{TypeLevel{}, scope.get(), {}, {}, argTypes, returnTypes};
|
||||||
ftv.isCheckedFunction = fn->isCheckedFunction();
|
ftv.isCheckedFunction = fn->isCheckedFunction();
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
ftv.isDeprecatedFunction = fn->hasAttribute(AstAttr::Type::Deprecated);
|
|
||||||
|
|
||||||
// This replicates the behavior of the appropriate FunctionType
|
// This replicates the behavior of the appropriate FunctionType
|
||||||
// constructors.
|
// constructors.
|
||||||
|
@ -3727,13 +3596,6 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGenerator::resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh)
|
TypeId ConstraintGenerator::resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh)
|
||||||
{
|
|
||||||
TypeId result = resolveType_(scope, ty, inTypeArguments, replaceErrorWithFresh);
|
|
||||||
inferGenericPolarities(arena, NotNull{scope.get()}, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh)
|
|
||||||
{
|
{
|
||||||
TypeId result = nullptr;
|
TypeId result = nullptr;
|
||||||
|
|
||||||
|
@ -3756,34 +3618,14 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
}
|
}
|
||||||
else if (ty->is<AstTypeOptional>())
|
else if (ty->is<AstTypeOptional>())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
result = builtinTypes->nilType;
|
|
||||||
else
|
|
||||||
return builtinTypes->nilType;
|
return builtinTypes->nilType;
|
||||||
}
|
}
|
||||||
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
||||||
{
|
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
{
|
|
||||||
if (unionAnnotation->types.size == 1)
|
|
||||||
result = resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<TypeId> parts;
|
|
||||||
for (AstType* part : unionAnnotation->types)
|
|
||||||
{
|
|
||||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = arena->addType(UnionType{parts});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
{
|
{
|
||||||
if (unionAnnotation->types.size == 1)
|
if (unionAnnotation->types.size == 1)
|
||||||
return resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
return resolveType(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
|
@ -3794,30 +3636,12 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
|
|
||||||
result = arena->addType(UnionType{parts});
|
result = arena->addType(UnionType{parts});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
||||||
{
|
|
||||||
if (FFlag::LuauAlwaysResolveAstTypes)
|
|
||||||
{
|
|
||||||
if (intersectionAnnotation->types.size == 1)
|
|
||||||
result = resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<TypeId> parts;
|
|
||||||
for (AstType* part : intersectionAnnotation->types)
|
|
||||||
{
|
|
||||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = arena->addType(IntersectionType{parts});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
if (FFlag::LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
{
|
{
|
||||||
if (intersectionAnnotation->types.size == 1)
|
if (intersectionAnnotation->types.size == 1)
|
||||||
return resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
return resolveType(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
|
@ -3828,10 +3652,9 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
|
|
||||||
result = arena->addType(IntersectionType{parts});
|
result = arena->addType(IntersectionType{parts});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
|
else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
|
||||||
{
|
{
|
||||||
result = resolveType_(scope, typeGroupAnnotation->type, inTypeArguments);
|
result = resolveType(scope, typeGroupAnnotation->type, inTypeArguments);
|
||||||
}
|
}
|
||||||
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
|
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
|
||||||
{
|
{
|
||||||
|
@ -3861,13 +3684,6 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGenerator::resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArgument, bool replaceErrorWithFresh)
|
TypePackId ConstraintGenerator::resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArgument, bool replaceErrorWithFresh)
|
||||||
{
|
|
||||||
TypePackId result = resolveTypePack_(scope, tp, inTypeArgument, replaceErrorWithFresh);
|
|
||||||
inferGenericPolarities(arena, NotNull{scope.get()}, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId ConstraintGenerator::resolveTypePack_(const ScopePtr& scope, AstTypePack* tp, bool inTypeArgument, bool replaceErrorWithFresh)
|
|
||||||
{
|
{
|
||||||
TypePackId result;
|
TypePackId result;
|
||||||
if (auto expl = tp->as<AstTypePackExplicit>())
|
if (auto expl = tp->as<AstTypePackExplicit>())
|
||||||
|
@ -3876,7 +3692,7 @@ TypePackId ConstraintGenerator::resolveTypePack_(const ScopePtr& scope, AstTypeP
|
||||||
}
|
}
|
||||||
else if (auto var = tp->as<AstTypePackVariadic>())
|
else if (auto var = tp->as<AstTypePackVariadic>())
|
||||||
{
|
{
|
||||||
TypeId ty = resolveType_(scope, var->variadicType, inTypeArgument, replaceErrorWithFresh);
|
TypeId ty = resolveType(scope, var->variadicType, inTypeArgument, replaceErrorWithFresh);
|
||||||
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
|
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
|
||||||
}
|
}
|
||||||
else if (auto gen = tp->as<AstTypePackGeneric>())
|
else if (auto gen = tp->as<AstTypePackGeneric>())
|
||||||
|
@ -3907,18 +3723,16 @@ TypePackId ConstraintGenerator::resolveTypePack(const ScopePtr& scope, const Ast
|
||||||
|
|
||||||
for (AstType* headTy : list.types)
|
for (AstType* headTy : list.types)
|
||||||
{
|
{
|
||||||
head.push_back(resolveType_(scope, headTy, inTypeArguments, replaceErrorWithFresh));
|
head.push_back(resolveType(scope, headTy, inTypeArguments, replaceErrorWithFresh));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypePackId> tail = std::nullopt;
|
std::optional<TypePackId> tail = std::nullopt;
|
||||||
if (list.tailType)
|
if (list.tailType)
|
||||||
{
|
{
|
||||||
tail = resolveTypePack_(scope, list.tailType, inTypeArguments, replaceErrorWithFresh);
|
tail = resolveTypePack(scope, list.tailType, inTypeArguments, replaceErrorWithFresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId result = addTypePack(std::move(head), tail);
|
return addTypePack(std::move(head), tail);
|
||||||
inferGenericPolarities(arena, NotNull{scope.get()}, result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGenerator::createGenerics(
|
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGenerator::createGenerics(
|
||||||
|
|
|
@ -35,14 +35,9 @@ LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
LUAU_FASTFLAG(LuauSearchForRefineableType)
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -362,6 +357,8 @@ ConstraintSolver::ConstraintSolver(
|
||||||
{
|
{
|
||||||
unsolvedConstraints.emplace_back(c);
|
unsolvedConstraints.emplace_back(c);
|
||||||
|
|
||||||
|
if (FFlag::LuauPrecalculateMutatedFreeTypes2)
|
||||||
|
{
|
||||||
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes();
|
auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes();
|
||||||
for (auto ty : maybeMutatedTypesPerConstraint)
|
for (auto ty : maybeMutatedTypesPerConstraint)
|
||||||
{
|
{
|
||||||
|
@ -375,6 +372,17 @@ ConstraintSolver::ConstraintSolver(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// initialize the reference counts for the free types in this constraint.
|
||||||
|
for (auto ty : c->getMaybeMutatedFreeTypes())
|
||||||
|
{
|
||||||
|
// increment the reference count for `ty`
|
||||||
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
|
refCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (NotNull<const Constraint> dep : c->dependencies)
|
for (NotNull<const Constraint> dep : c->dependencies)
|
||||||
|
@ -465,6 +473,8 @@ void ConstraintSolver::run()
|
||||||
unblock(c);
|
unblock(c);
|
||||||
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
|
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
|
||||||
|
|
||||||
|
if (FFlag::LuauPrecalculateMutatedFreeTypes2)
|
||||||
|
{
|
||||||
const auto maybeMutated = maybeMutatedFreeTypes.find(c);
|
const auto maybeMutated = maybeMutatedFreeTypes.find(c);
|
||||||
if (maybeMutated != maybeMutatedFreeTypes.end())
|
if (maybeMutated != maybeMutatedFreeTypes.end())
|
||||||
{
|
{
|
||||||
|
@ -499,6 +509,25 @@ void ConstraintSolver::run()
|
||||||
generalizeOneType(ty);
|
generalizeOneType(ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// decrement the referenced free types for this constraint if we dispatched successfully!
|
||||||
|
for (auto ty : c->getMaybeMutatedFreeTypes())
|
||||||
|
{
|
||||||
|
size_t& refCount = unresolvedConstraints[ty];
|
||||||
|
if (refCount > 0)
|
||||||
|
refCount -= 1;
|
||||||
|
|
||||||
|
// We have two constraints that are designed to wait for the
|
||||||
|
// refCount on a free type to be equal to 1: the
|
||||||
|
// PrimitiveTypeConstraint and ReduceConstraint. We
|
||||||
|
// therefore wake any constraint waiting for a free type's
|
||||||
|
// refcount to be 1 or 0.
|
||||||
|
if (refCount <= 1)
|
||||||
|
unblock(ty, Location{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (logger)
|
if (logger)
|
||||||
{
|
{
|
||||||
|
@ -596,6 +625,14 @@ bool ConstraintSolver::isDone() const
|
||||||
|
|
||||||
struct TypeSearcher : TypeVisitor
|
struct TypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
|
enum struct Polarity: uint8_t
|
||||||
|
{
|
||||||
|
None = 0b00,
|
||||||
|
Positive = 0b01,
|
||||||
|
Negative = 0b10,
|
||||||
|
Mixed = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
TypeId needle;
|
TypeId needle;
|
||||||
Polarity current = Polarity::Positive;
|
Polarity current = Polarity::Positive;
|
||||||
|
|
||||||
|
@ -604,14 +641,12 @@ struct TypeSearcher : TypeVisitor
|
||||||
|
|
||||||
explicit TypeSearcher(TypeId needle)
|
explicit TypeSearcher(TypeId needle)
|
||||||
: TypeSearcher(needle, Polarity::Positive)
|
: TypeSearcher(needle, Polarity::Positive)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
|
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
|
||||||
: needle(needle)
|
: needle(needle)
|
||||||
, current(initialPolarity)
|
, current(initialPolarity)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override
|
||||||
{
|
{
|
||||||
|
@ -713,12 +748,12 @@ void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
|
|
||||||
switch (ts.result)
|
switch (ts.result)
|
||||||
{
|
{
|
||||||
case Polarity::None:
|
case TypeSearcher::Polarity::None:
|
||||||
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Polarity::Negative:
|
case TypeSearcher::Polarity::Negative:
|
||||||
case Polarity::Mixed:
|
case TypeSearcher::Polarity::Mixed:
|
||||||
if (get<UnknownType>(upperBound) && ts.count > 1)
|
if (get<UnknownType>(upperBound) && ts.count > 1)
|
||||||
{
|
{
|
||||||
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
||||||
|
@ -728,7 +763,7 @@ void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
asMutable(ty)->reassign(Type{BoundType{upperBound}});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Polarity::Positive:
|
case TypeSearcher::Polarity::Positive:
|
||||||
if (get<UnknownType>(lowerBound) && ts.count > 1)
|
if (get<UnknownType>(lowerBound) && ts.count > 1)
|
||||||
{
|
{
|
||||||
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
asMutable(ty)->reassign(Type{GenericType{tyScope}});
|
||||||
|
@ -737,8 +772,6 @@ void ConstraintSolver::generalizeOneType(TypeId ty)
|
||||||
else
|
else
|
||||||
asMutable(ty)->reassign(Type{BoundType{lowerBound}});
|
asMutable(ty)->reassign(Type{BoundType{lowerBound}});
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -750,16 +783,7 @@ void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, Typ
|
||||||
|
|
||||||
boundTo = follow(boundTo);
|
boundTo = follow(boundTo);
|
||||||
if (get<BlockedType>(ty) && ty == boundTo)
|
if (get<BlockedType>(ty) && ty == boundTo)
|
||||||
{
|
return emplace<FreeType>(constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType);
|
||||||
emplace<FreeType>(
|
|
||||||
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
|
|
||||||
); // FIXME? Is this the right polarity?
|
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
trackInteriorFreeType(constraint->scope, ty);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shiftReferences(ty, boundTo);
|
shiftReferences(ty, boundTo);
|
||||||
emplaceType<BoundType>(asMutable(ty), boundTo);
|
emplaceType<BoundType>(asMutable(ty), boundTo);
|
||||||
|
@ -894,15 +918,6 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
bind(constraint, generalizedType, *generalizedTy);
|
bind(constraint, generalizedType, *generalizedTy);
|
||||||
else
|
else
|
||||||
unify(constraint, generalizedType, *generalizedTy);
|
unify(constraint, generalizedType, *generalizedTy);
|
||||||
|
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
{
|
|
||||||
if (FunctionType* fty = getMutable<FunctionType>(follow(generalizedType)))
|
|
||||||
{
|
|
||||||
if (c.hasDeprecatedAttribute)
|
|
||||||
fty->isDeprecatedFunction = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -915,53 +930,13 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
// We check if this member is initialized and then access it, but
|
// We check if this member is initialized and then access it, but
|
||||||
// clang-tidy doesn't understand this is safe.
|
// clang-tidy doesn't understand this is safe.
|
||||||
if (constraint->scope->interiorFreeTypes)
|
if (constraint->scope->interiorFreeTypes)
|
||||||
{
|
|
||||||
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
||||||
{
|
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
ty = follow(ty);
|
|
||||||
if (auto freeTy = get<FreeType>(ty))
|
|
||||||
{
|
|
||||||
GeneralizationParams<TypeId> params;
|
|
||||||
params.foundOutsideFunctions = true;
|
|
||||||
params.useCount = 1;
|
|
||||||
params.polarity = freeTy->polarity;
|
|
||||||
|
|
||||||
generalizeType(arena, builtinTypes, constraint->scope, ty, params);
|
|
||||||
}
|
|
||||||
else if (get<TableType>(ty))
|
|
||||||
sealTable(constraint->scope, ty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
if (constraint->scope->interiorFreeTypePacks)
|
|
||||||
{
|
|
||||||
for (TypePackId tp : *constraint->scope->interiorFreeTypePacks) // NOLINT(bugprone-unchecked-optional-access)
|
|
||||||
{
|
|
||||||
tp = follow(tp);
|
|
||||||
if (auto freeTp = get<FreeTypePack>(tp))
|
|
||||||
{
|
|
||||||
GeneralizationParams<TypePackId> params;
|
|
||||||
params.foundOutsideFunctions = true;
|
|
||||||
params.useCount = 1;
|
|
||||||
params.polarity = freeTp->polarity;
|
|
||||||
LUAU_ASSERT(isKnown(params.polarity));
|
|
||||||
generalizeTypePack(arena, builtinTypes, constraint->scope, tp, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (TypeId ty : c.interiorTypes)
|
for (TypeId ty : c.interiorTypes)
|
||||||
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty);
|
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1037,8 +1012,8 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
||||||
TypeId nextTy = follow(iterator.head[0]);
|
TypeId nextTy = follow(iterator.head[0]);
|
||||||
if (get<FreeType>(nextTy))
|
if (get<FreeType>(nextTy))
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
{
|
{
|
||||||
trackInteriorFreeType(constraint->scope, keyTy);
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
|
@ -1497,8 +1472,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
argsPack = arena->addTypePack(TypePack{std::move(argsHead), argsTail});
|
argsPack = arena->addTypePack(TypePack{std::move(argsHead), argsTail});
|
||||||
fn = follow(*callMm);
|
fn = follow(*callMm);
|
||||||
emplace<FreeTypePack>(constraint, c.result, constraint->scope, Polarity::Positive);
|
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
||||||
trackInteriorFreeTypePack(constraint->scope, c.result);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1515,10 +1489,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!usedMagic)
|
if (!usedMagic)
|
||||||
{
|
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
||||||
emplace<FreeTypePack>(constraint, c.result, constraint->scope, Polarity::Positive);
|
|
||||||
trackInteriorFreeTypePack(constraint->scope, c.result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
|
@ -1539,19 +1510,11 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
if (status == OverloadResolver::Analysis::Ok)
|
if (status == OverloadResolver::Analysis::Ok)
|
||||||
overloadToUse = overload;
|
overloadToUse = overload;
|
||||||
|
|
||||||
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, c.result});
|
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, constraint->scope.get(), argsPack, c.result});
|
||||||
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
||||||
|
|
||||||
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
for (TypeId freeTy : u2.newFreshTypes)
|
|
||||||
trackInteriorFreeType(constraint->scope, freeTy);
|
|
||||||
for (TypePackId freeTp : u2.newFreshTypePacks)
|
|
||||||
trackInteriorFreeTypePack(constraint->scope, freeTp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
|
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
|
||||||
{
|
{
|
||||||
std::optional<TypePackId> subst = instantiate2(arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions), result);
|
std::optional<TypePackId> subst = instantiate2(arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions), result);
|
||||||
|
@ -1582,11 +1545,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
queuer.traverse(overloadToUse);
|
queuer.traverse(overloadToUse);
|
||||||
queuer.traverse(inferredTy);
|
queuer.traverse(inferredTy);
|
||||||
|
|
||||||
// This can potentially contain free types if the return type of
|
|
||||||
// `inferredTy` is never unified elsewhere.
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInferredFunctionTypeFromCall)
|
|
||||||
trackInteriorFreeType(constraint->scope, inferredTy);
|
|
||||||
|
|
||||||
unblock(c.result, constraint->location);
|
unblock(c.result, constraint->location);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1600,43 +1558,6 @@ static AstExpr* unwrapGroup(AstExpr* expr)
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContainsGenerics : public TypeOnceVisitor
|
|
||||||
{
|
|
||||||
DenseHashSet<const void*> generics{nullptr};
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const GenericType&) override
|
|
||||||
{
|
|
||||||
found |= generics.contains(ty);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp, const GenericTypePack&) override
|
|
||||||
{
|
|
||||||
found |= generics.contains(tp);
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasGeneric(TypeId ty)
|
|
||||||
{
|
|
||||||
traverse(ty);
|
|
||||||
auto ret = found;
|
|
||||||
found = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint)
|
bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint)
|
||||||
{
|
{
|
||||||
TypeId fn = follow(c.fn);
|
TypeId fn = follow(c.fn);
|
||||||
|
@ -1679,27 +1600,15 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
DenseHashMap<TypeId, TypeId> replacements{nullptr};
|
DenseHashMap<TypeId, TypeId> replacements{nullptr};
|
||||||
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
|
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
|
||||||
|
|
||||||
ContainsGenerics containsGenerics;
|
|
||||||
|
|
||||||
for (auto generic : ftv->generics)
|
for (auto generic : ftv->generics)
|
||||||
{
|
|
||||||
replacements[generic] = builtinTypes->unknownType;
|
replacements[generic] = builtinTypes->unknownType;
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
containsGenerics.generics.insert(generic);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto genericPack : ftv->genericPacks)
|
for (auto genericPack : ftv->genericPacks)
|
||||||
{
|
|
||||||
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
|
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
containsGenerics.generics.insert(genericPack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the type of the function has generics, we don't actually want to push any of the generics themselves
|
// If the type of the function has generics, we don't actually want to push any of the generics themselves
|
||||||
// into the argument types as expected types because this creates an unnecessary loop. Instead, we want to
|
// into the argument types as expected types because this creates an unnecessary loop. Instead, we want to
|
||||||
// replace these types with `unknown` (and `...unknown`) to keep any structure but not create the cycle.
|
// replace these types with `unknown` (and `...unknown`) to keep any structure but not create the cycle.
|
||||||
if (!FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
{
|
|
||||||
if (!replacements.empty() || !replacementPacks.empty())
|
if (!replacements.empty() || !replacementPacks.empty())
|
||||||
{
|
{
|
||||||
Replacer replacer{arena, std::move(replacements), std::move(replacementPacks)};
|
Replacer replacer{arena, std::move(replacements), std::move(replacementPacks)};
|
||||||
|
@ -1723,7 +1632,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
||||||
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
|
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
|
||||||
|
@ -1740,10 +1648,6 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
|
|
||||||
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
||||||
|
|
||||||
// Generic types are skipped over entirely, for now.
|
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes && containsGenerics.hasGeneric(expectedArgTy))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
||||||
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
||||||
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
||||||
|
@ -1848,16 +1752,8 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Con
|
||||||
LUAU_ASSERT(get<BlockedType>(resultType));
|
LUAU_ASSERT(get<BlockedType>(resultType));
|
||||||
LUAU_ASSERT(canMutate(resultType, constraint));
|
LUAU_ASSERT(canMutate(resultType, constraint));
|
||||||
|
|
||||||
if (FFlag::LuauHasPropProperBlock)
|
|
||||||
{
|
|
||||||
if (isBlocked(subjectType))
|
|
||||||
return block(subjectType, constraint);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isBlocked(subjectType) || get<PendingExpansionType>(subjectType) || get<TypeFunctionInstanceType>(subjectType))
|
if (isBlocked(subjectType) || get<PendingExpansionType>(subjectType) || get<TypeFunctionInstanceType>(subjectType))
|
||||||
return block(subjectType, constraint);
|
return block(subjectType, constraint);
|
||||||
}
|
|
||||||
|
|
||||||
if (const TableType* subjectTable = getTableType(subjectType))
|
if (const TableType* subjectTable = getTableType(subjectType))
|
||||||
{
|
{
|
||||||
|
@ -1919,7 +1815,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
else if (auto mt = get<MetatableType>(follow(ft->upperBound)))
|
else if (auto mt = get<MetatableType>(follow(ft->upperBound)))
|
||||||
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
|
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
|
||||||
|
|
||||||
FreeType freeResult{ft->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
FreeType freeResult{ft->scope, builtinTypes->neverType, builtinTypes->unknownType};
|
||||||
emplace<FreeType>(constraint, resultType, freeResult);
|
emplace<FreeType>(constraint, resultType, freeResult);
|
||||||
|
|
||||||
TypeId upperBound =
|
TypeId upperBound =
|
||||||
|
@ -1942,10 +1838,8 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
{
|
{
|
||||||
// FIXME this is greedy.
|
// FIXME this is greedy.
|
||||||
|
|
||||||
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType};
|
||||||
emplace<FreeType>(constraint, resultType, freeResult);
|
emplace<FreeType>(constraint, resultType, freeResult);
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
trackInteriorFreeType(constraint->scope, resultType);
|
|
||||||
|
|
||||||
tt->indexer = TableIndexer{indexType, resultType};
|
tt->indexer = TableIndexer{indexType, resultType};
|
||||||
return true;
|
return true;
|
||||||
|
@ -2167,7 +2061,11 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
if (maybeTy)
|
if (maybeTy)
|
||||||
{
|
{
|
||||||
TypeId propTy = *maybeTy;
|
TypeId propTy = *maybeTy;
|
||||||
bind(constraint, c.propType, isIndex ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy);
|
bind(
|
||||||
|
constraint,
|
||||||
|
c.propType,
|
||||||
|
isIndex ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy
|
||||||
|
);
|
||||||
unify(constraint, rhsType, propTy);
|
unify(constraint, rhsType, propTy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2261,7 +2159,11 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
{
|
{
|
||||||
unify(constraint, indexType, lhsTable->indexer->indexType);
|
unify(constraint, indexType, lhsTable->indexer->indexType);
|
||||||
unify(constraint, rhsType, lhsTable->indexer->indexResultType);
|
unify(constraint, rhsType, lhsTable->indexer->indexResultType);
|
||||||
bind(constraint, c.propType, arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}}));
|
bind(
|
||||||
|
constraint,
|
||||||
|
c.propType,
|
||||||
|
arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}})
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2310,7 +2212,11 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
|
||||||
{
|
{
|
||||||
unify(constraint, indexType, lhsClass->indexer->indexType);
|
unify(constraint, indexType, lhsClass->indexer->indexType);
|
||||||
unify(constraint, rhsType, lhsClass->indexer->indexResultType);
|
unify(constraint, rhsType, lhsClass->indexer->indexResultType);
|
||||||
bind(constraint, c.propType, arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}));
|
bind(
|
||||||
|
constraint,
|
||||||
|
c.propType,
|
||||||
|
arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}})
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2404,7 +2310,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
|
||||||
// is only blocked on itself. This doesn't actually
|
// is only blocked on itself. This doesn't actually
|
||||||
// constitute any meaningful constraint, so we replace it
|
// constitute any meaningful constraint, so we replace it
|
||||||
// with a free type.
|
// with a free type.
|
||||||
TypeId f = freshType(arena, builtinTypes, constraint->scope, Polarity::Positive); // FIXME? Is this the right polarity?
|
TypeId f = freshType(arena, builtinTypes, constraint->scope);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
trackInteriorFreeType(constraint->scope, f);
|
trackInteriorFreeType(constraint->scope, f);
|
||||||
shiftReferences(resultTy, f);
|
shiftReferences(resultTy, f);
|
||||||
|
@ -2453,18 +2359,11 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
|
||||||
for (TypePackId r : result.reducedPacks)
|
for (TypePackId r : result.reducedPacks)
|
||||||
unblock(r, constraint->location);
|
unblock(r, constraint->location);
|
||||||
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
|
||||||
for (TypeId ity : result.irreducibleTypes)
|
|
||||||
uninhabitedTypeFunctions.insert(ity);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
|
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
// If we couldn't reduce this type function, stick it in the set!
|
// If we couldn't reduce this type function, stick it in the set!
|
||||||
if (get<TypeFunctionInstanceType>(ty) && (!FFlag::LuauNewTypeFunReductionChecks2 || !result.irreducibleTypes.find(ty)))
|
if (get<TypeFunctionInstanceType>(ty))
|
||||||
typeFunctionsToFinalize[ty] = constraint;
|
typeFunctionsToFinalize[ty] = constraint;
|
||||||
|
|
||||||
if (force || reductionFinished)
|
if (force || reductionFinished)
|
||||||
|
@ -2547,8 +2446,8 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
||||||
|
|
||||||
if (get<FreeType>(iteratorTy))
|
if (get<FreeType>(iteratorTy))
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
{
|
{
|
||||||
trackInteriorFreeType(constraint->scope, keyTy);
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
|
@ -2809,7 +2708,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
|
|
||||||
if (ttv->state == TableState::Free)
|
if (ttv->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId result = freshType(arena, builtinTypes, ttv->scope, Polarity::Mixed);
|
TypeId result = freshType(arena, builtinTypes, ttv->scope);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
trackInteriorFreeType(ttv->scope, result);
|
trackInteriorFreeType(ttv->scope, result);
|
||||||
switch (context)
|
switch (context)
|
||||||
|
@ -2923,7 +2822,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
|
|
||||||
TableType* tt = getMutable<TableType>(newUpperBound);
|
TableType* tt = getMutable<TableType>(newUpperBound);
|
||||||
LUAU_ASSERT(tt);
|
LUAU_ASSERT(tt);
|
||||||
TypeId propType = freshType(arena, builtinTypes, scope, Polarity::Mixed);
|
TypeId propType = freshType(arena, builtinTypes, scope);
|
||||||
|
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
trackInteriorFreeType(scope, propType);
|
trackInteriorFreeType(scope, propType);
|
||||||
|
@ -3384,7 +3283,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type)
|
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables)
|
||||||
{
|
{
|
||||||
TypeId t = follow(type);
|
TypeId t = follow(type);
|
||||||
if (get<FreeType>(t))
|
if (get<FreeType>(t))
|
||||||
|
@ -3399,7 +3298,7 @@ std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope,
|
||||||
// that until all constraint generation is complete.
|
// that until all constraint generation is complete.
|
||||||
}
|
}
|
||||||
|
|
||||||
return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type);
|
return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type, avoidSealingTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty)
|
bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty)
|
||||||
|
|
|
@ -13,22 +13,32 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
bool doesCallError(const AstExprCall* call); // TypeInfer.cpp
|
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
|
struct PushScope
|
||||||
{
|
{
|
||||||
ScopeStack& stack;
|
ScopeStack& stack;
|
||||||
size_t previousSize;
|
|
||||||
|
|
||||||
PushScope(ScopeStack& stack, DfgScope* scope)
|
PushScope(ScopeStack& stack, DfgScope* scope)
|
||||||
: stack(stack)
|
: stack(stack)
|
||||||
, previousSize(stack.size())
|
|
||||||
{
|
{
|
||||||
// `scope` should never be `nullptr` here.
|
// `scope` should never be `nullptr` here.
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
@ -37,19 +47,8 @@ struct PushScope
|
||||||
|
|
||||||
~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();
|
stack.pop_back();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
stack.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const RefinementKey* RefinementKeyArena::leaf(DefId def)
|
const RefinementKey* RefinementKeyArena::leaf(DefId def)
|
||||||
|
@ -83,6 +82,12 @@ std::optional<DefId> DataFlowGraph::getDefOptional(const AstExpr* expr) const
|
||||||
return NotNull{*def};
|
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
|
DefId DataFlowGraph::getDef(const AstLocal* local) const
|
||||||
{
|
{
|
||||||
auto def = localDefs.find(local);
|
auto def = localDefs.find(local);
|
||||||
|
@ -196,15 +201,7 @@ DataFlowGraph DataFlowGraphBuilder::build(
|
||||||
|
|
||||||
DataFlowGraphBuilder builder(defArena, keyArena);
|
DataFlowGraphBuilder builder(defArena, keyArena);
|
||||||
builder.handle = handle;
|
builder.handle = handle;
|
||||||
|
DfgScope* moduleScope = builder.makeChildScope();
|
||||||
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();
|
|
||||||
PushScope ps{builder.scopeStack, moduleScope};
|
PushScope ps{builder.scopeStack, moduleScope};
|
||||||
builder.visitBlockWithoutChildScope(block);
|
builder.visitBlockWithoutChildScope(block);
|
||||||
builder.resolveCaptures();
|
builder.resolveCaptures();
|
||||||
|
@ -236,13 +233,7 @@ void DataFlowGraphBuilder::resolveCaptures()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull<DfgScope> DataFlowGraphBuilder::currentScope()
|
DfgScope* DataFlowGraphBuilder::currentScope()
|
||||||
{
|
|
||||||
LUAU_ASSERT(!scopeStack.empty());
|
|
||||||
return NotNull{scopeStack.back()};
|
|
||||||
}
|
|
||||||
|
|
||||||
DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED()
|
|
||||||
{
|
{
|
||||||
if (scopeStack.empty())
|
if (scopeStack.empty())
|
||||||
return nullptr; // nullptr is the root DFG scope.
|
return nullptr; // nullptr is the root DFG scope.
|
||||||
|
@ -251,10 +242,7 @@ DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED()
|
||||||
|
|
||||||
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
|
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
|
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
|
||||||
else
|
|
||||||
return scopes.emplace_back(new DfgScope{currentScope_DEPRECATED(), scopeType}).get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
|
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
|
||||||
|
@ -329,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.
|
// true if any of the considered scopes are a loop.
|
||||||
bool outsideLoopScope = false;
|
bool outsideLoopScope = false;
|
||||||
|
@ -356,15 +344,15 @@ DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DefId result = defArena->freshCell(symbol, location);
|
DefId result = defArena->freshCell();
|
||||||
scope->bindings[symbol] = result;
|
scope->bindings[symbol] = result;
|
||||||
captures[symbol].allVersions.push_back(result);
|
captures[symbol].allVersions.push_back(result);
|
||||||
return 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)
|
for (DfgScope* current = scope; current; current = current->parent)
|
||||||
{
|
{
|
||||||
if (auto props = current->props.find(def))
|
if (auto props = current->props.find(def))
|
||||||
|
@ -374,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
|
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;
|
scope->props[def][key] = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -384,7 +372,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location l
|
||||||
{
|
{
|
||||||
std::vector<DefId> defs;
|
std::vector<DefId> defs;
|
||||||
for (DefId operand : phi->operands)
|
for (DefId operand : phi->operands)
|
||||||
defs.push_back(lookup(operand, key, location));
|
defs.push_back(lookup(operand, key));
|
||||||
|
|
||||||
DefId result = defArena->phi(defs);
|
DefId result = defArena->phi(defs);
|
||||||
scope->props[def][key] = result;
|
scope->props[def][key] = result;
|
||||||
|
@ -392,7 +380,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location l
|
||||||
}
|
}
|
||||||
else if (get<Cell>(def))
|
else if (get<Cell>(def))
|
||||||
{
|
{
|
||||||
DefId result = defArena->freshCell(def->name, location);
|
DefId result = defArena->freshCell();
|
||||||
scope->props[def][key] = result;
|
scope->props[def][key] = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -410,10 +398,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
|
||||||
cf = visitBlockWithoutChildScope(b);
|
cf = visitBlockWithoutChildScope(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->inherit(child);
|
currentScope()->inherit(child);
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->inherit(child);
|
|
||||||
return cf;
|
return cf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +483,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
|
||||||
elsecf = visit(i->elsebody);
|
elsecf = visit(i->elsebody);
|
||||||
}
|
}
|
||||||
|
|
||||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
DfgScope* scope = currentScope();
|
||||||
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
||||||
join(scope, scope, elseScope);
|
join(scope, scope, elseScope);
|
||||||
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
||||||
|
@ -525,10 +510,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
|
||||||
visit(w->body);
|
visit(w->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->inherit(whileScope);
|
currentScope()->inherit(whileScope);
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->inherit(whileScope);
|
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
@ -544,10 +526,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
|
||||||
visitExpr(r->condition);
|
visitExpr(r->condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->inherit(repeatScope);
|
currentScope()->inherit(repeatScope);
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->inherit(repeatScope);
|
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
@ -596,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
|
// 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.
|
// make sure that the non-aliased defs are also marked as a subscript for refinements.
|
||||||
bool subscripted = i < defs.size() && containsSubscriptedDefinition(defs[i]);
|
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)
|
if (i < l->values.size)
|
||||||
{
|
{
|
||||||
AstExpr* e = l->values.data[i];
|
AstExpr* e = l->values.data[i];
|
||||||
|
@ -606,10 +585,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
graph.localDefs[local] = def;
|
graph.localDefs[local] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[local] = def;
|
currentScope()->bindings[local] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[local] = def;
|
|
||||||
captures[local].allVersions.push_back(def);
|
captures[local].allVersions.push_back(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,22 +607,16 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
|
||||||
if (f->var->annotation)
|
if (f->var->annotation)
|
||||||
visitType(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;
|
graph.localDefs[f->var] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[f->var] = def;
|
currentScope()->bindings[f->var] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[f->var] = def;
|
|
||||||
captures[f->var].allVersions.push_back(def);
|
captures[f->var].allVersions.push_back(def);
|
||||||
|
|
||||||
// TODO(controlflow): entry point has a back edge from exit point
|
// TODO(controlflow): entry point has a back edge from exit point
|
||||||
visit(f->body);
|
visit(f->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->inherit(forScope);
|
currentScope()->inherit(forScope);
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->inherit(forScope);
|
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
@ -663,12 +633,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
||||||
if (local->annotation)
|
if (local->annotation)
|
||||||
visitType(local->annotation);
|
visitType(local->annotation);
|
||||||
|
|
||||||
DefId def = defArena->freshCell(local, local->location);
|
DefId def = defArena->freshCell();
|
||||||
graph.localDefs[local] = def;
|
graph.localDefs[local] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[local] = def;
|
currentScope()->bindings[local] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[local] = def;
|
|
||||||
captures[local].allVersions.push_back(def);
|
captures[local].allVersions.push_back(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,10 +646,8 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
|
||||||
|
|
||||||
visit(f->body);
|
visit(f->body);
|
||||||
}
|
}
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->inherit(forScope);
|
currentScope()->inherit(forScope);
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->inherit(forScope);
|
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
|
@ -697,7 +662,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)
|
||||||
for (size_t i = 0; i < a->vars.size; ++i)
|
for (size_t i = 0; i < a->vars.size; ++i)
|
||||||
{
|
{
|
||||||
AstExpr* v = a->vars.data[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;
|
return ControlFlow::None;
|
||||||
|
@ -723,7 +688,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFunction* f)
|
||||||
//
|
//
|
||||||
// which is evidence that references to variables must be a phi node of all possible definitions,
|
// 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.
|
// 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);
|
visitExpr(f->func);
|
||||||
|
|
||||||
if (auto local = f->name->as<AstExprLocal>())
|
if (auto local = f->name->as<AstExprLocal>())
|
||||||
|
@ -743,12 +708,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFunction* f)
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
|
||||||
{
|
{
|
||||||
DefId def = defArena->freshCell(l->name, l->location);
|
DefId def = defArena->freshCell();
|
||||||
graph.localDefs[l->name] = def;
|
graph.localDefs[l->name] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[l->name] = def;
|
currentScope()->bindings[l->name] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[l->name] = def;
|
|
||||||
captures[l->name].allVersions.push_back(def);
|
captures[l->name].allVersions.push_back(def);
|
||||||
visitExpr(l->func);
|
visitExpr(l->func);
|
||||||
|
|
||||||
|
@ -779,12 +741,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatTypeFunction* f)
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
|
||||||
{
|
{
|
||||||
DefId def = defArena->freshCell(d->name, d->nameLocation);
|
DefId def = defArena->freshCell();
|
||||||
graph.declaredDefs[d] = def;
|
graph.declaredDefs[d] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[d->name] = def;
|
currentScope()->bindings[d->name] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[d->name] = def;
|
|
||||||
captures[d->name].allVersions.push_back(def);
|
captures[d->name].allVersions.push_back(def);
|
||||||
|
|
||||||
visitType(d->type);
|
visitType(d->type);
|
||||||
|
@ -794,12 +753,9 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
|
||||||
|
|
||||||
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
|
||||||
{
|
{
|
||||||
DefId def = defArena->freshCell(d->name, d->nameLocation);
|
DefId def = defArena->freshCell();
|
||||||
graph.declaredDefs[d] = def;
|
graph.declaredDefs[d] = def;
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->bindings[d->name] = def;
|
currentScope()->bindings[d->name] = def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->bindings[d->name] = def;
|
|
||||||
captures[d->name].allVersions.push_back(def);
|
captures[d->name].allVersions.push_back(def);
|
||||||
|
|
||||||
DfgScope* unreachable = makeChildScope();
|
DfgScope* unreachable = makeChildScope();
|
||||||
|
@ -854,19 +810,19 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExpr* e)
|
||||||
if (auto g = e->as<AstExprGroup>())
|
if (auto g = e->as<AstExprGroup>())
|
||||||
return visitExpr(g);
|
return visitExpr(g);
|
||||||
else if (auto c = e->as<AstExprConstantNil>())
|
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>())
|
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>())
|
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>())
|
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>())
|
else if (auto l = e->as<AstExprLocal>())
|
||||||
return visitExpr(l);
|
return visitExpr(l);
|
||||||
else if (auto g = e->as<AstExprGlobal>())
|
else if (auto g = e->as<AstExprGlobal>())
|
||||||
return visitExpr(g);
|
return visitExpr(g);
|
||||||
else if (auto v = e->as<AstExprVarargs>())
|
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>())
|
else if (auto c = e->as<AstExprCall>())
|
||||||
return visitExpr(c);
|
return visitExpr(c);
|
||||||
else if (auto i = e->as<AstExprIndexName>())
|
else if (auto i = e->as<AstExprIndexName>())
|
||||||
|
@ -907,14 +863,14 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGroup* group)
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprLocal* l)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprLocal* l)
|
||||||
{
|
{
|
||||||
DefId def = lookup(l->local, l->local->location);
|
DefId def = lookup(l->local);
|
||||||
const RefinementKey* key = keyArena->leaf(def);
|
const RefinementKey* key = keyArena->leaf(def);
|
||||||
return {def, key};
|
return {def, key};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGlobal* g)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGlobal* g)
|
||||||
{
|
{
|
||||||
DefId def = lookup(g->name, g->location);
|
DefId def = lookup(g->name);
|
||||||
return {def, keyArena->leaf(def)};
|
return {def, keyArena->leaf(def)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,12 +878,6 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
||||||
{
|
{
|
||||||
visitExpr(c->func);
|
visitExpr(c->func);
|
||||||
|
|
||||||
if (FFlag::LuauPreprocessTypestatedArgument)
|
|
||||||
{
|
|
||||||
for (AstExpr* arg : c->args)
|
|
||||||
visitExpr(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
|
if (shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
|
||||||
{
|
{
|
||||||
AstExpr* firstArg = *c->args.begin();
|
AstExpr* firstArg = *c->args.begin();
|
||||||
|
@ -958,11 +908,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
||||||
visitLValue(firstArg, def);
|
visitLValue(firstArg, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FFlag::LuauPreprocessTypestatedArgument)
|
|
||||||
{
|
|
||||||
for (AstExpr* arg : c->args)
|
for (AstExpr* arg : c->args)
|
||||||
visitExpr(arg);
|
visitExpr(arg);
|
||||||
}
|
|
||||||
|
|
||||||
// We treat function calls as "subscripted" as they could potentially
|
// We treat function calls as "subscripted" as they could potentially
|
||||||
// return a subscripted value, consider:
|
// return a subscripted value, consider:
|
||||||
|
@ -974,7 +921,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
|
||||||
// local v = foo({})
|
// local v = foo({})
|
||||||
//
|
//
|
||||||
// We want to consider `v` to be subscripted here.
|
// 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)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexName* i)
|
||||||
|
@ -982,7 +929,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexName* i)
|
||||||
auto [parentDef, parentKey] = visitExpr(i->expr);
|
auto [parentDef, parentKey] = visitExpr(i->expr);
|
||||||
std::string index = i->index.value;
|
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)};
|
return {def, keyArena->node(parentKey, def, index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,11 +942,11 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexExpr* i)
|
||||||
{
|
{
|
||||||
std::string index{string->value.data, string->value.size};
|
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 {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)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||||
|
@ -1012,7 +959,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||||
// There's no syntax for `self` to have an annotation if using `function t:m()`
|
// There's no syntax for `self` to have an annotation if using `function t:m()`
|
||||||
LUAU_ASSERT(!self->annotation);
|
LUAU_ASSERT(!self->annotation);
|
||||||
|
|
||||||
DefId def = defArena->freshCell(f->debugname, f->location);
|
DefId def = defArena->freshCell();
|
||||||
graph.localDefs[self] = def;
|
graph.localDefs[self] = def;
|
||||||
signatureScope->bindings[self] = def;
|
signatureScope->bindings[self] = def;
|
||||||
captures[self].allVersions.push_back(def);
|
captures[self].allVersions.push_back(def);
|
||||||
|
@ -1023,7 +970,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||||
if (param->annotation)
|
if (param->annotation)
|
||||||
visitType(param->annotation);
|
visitType(param->annotation);
|
||||||
|
|
||||||
DefId def = defArena->freshCell(param, param->location);
|
DefId def = defArena->freshCell();
|
||||||
graph.localDefs[param] = def;
|
graph.localDefs[param] = def;
|
||||||
signatureScope->bindings[param] = def;
|
signatureScope->bindings[param] = def;
|
||||||
captures[param].allVersions.push_back(def);
|
captures[param].allVersions.push_back(def);
|
||||||
|
@ -1045,16 +992,13 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
|
||||||
// g() --> 5
|
// g() --> 5
|
||||||
visit(f->body);
|
visit(f->body);
|
||||||
|
|
||||||
return {defArena->freshCell(f->debugname, f->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
|
||||||
{
|
{
|
||||||
DefId tableCell = defArena->freshCell(Symbol{}, t->location);
|
DefId tableCell = defArena->freshCell();
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->props[tableCell] = {};
|
currentScope()->props[tableCell] = {};
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->props[tableCell] = {};
|
|
||||||
for (AstExprTable::Item item : t->items)
|
for (AstExprTable::Item item : t->items)
|
||||||
{
|
{
|
||||||
DataFlowResult result = visitExpr(item.value);
|
DataFlowResult result = visitExpr(item.value);
|
||||||
|
@ -1062,12 +1006,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
|
||||||
{
|
{
|
||||||
visitExpr(item.key);
|
visitExpr(item.key);
|
||||||
if (auto string = item.key->as<AstExprConstantString>())
|
if (auto string = item.key->as<AstExprConstantString>())
|
||||||
{
|
|
||||||
if (FFlag::LuauDfgScopeStackNotNull)
|
|
||||||
currentScope()->props[tableCell][string->value.data] = result.def;
|
currentScope()->props[tableCell][string->value.data] = result.def;
|
||||||
else
|
|
||||||
currentScope_DEPRECATED()->props[tableCell][string->value.data] = result.def;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,7 +1017,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u)
|
||||||
{
|
{
|
||||||
visitExpr(u->expr);
|
visitExpr(u->expr);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, u->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
||||||
|
@ -1086,7 +1025,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
|
||||||
visitExpr(b->left);
|
visitExpr(b->left);
|
||||||
visitExpr(b->right);
|
visitExpr(b->right);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, b->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
|
||||||
|
@ -1103,7 +1042,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
|
||||||
visitExpr(i->trueExpr);
|
visitExpr(i->trueExpr);
|
||||||
visitExpr(i->falseExpr);
|
visitExpr(i->falseExpr);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
|
||||||
|
@ -1111,7 +1050,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
|
||||||
for (AstExpr* e : i->expressions)
|
for (AstExpr* e : i->expressions)
|
||||||
visitExpr(e);
|
visitExpr(e);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, i->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
|
||||||
|
@ -1122,7 +1061,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
|
||||||
for (AstExpr* e : error->expressions)
|
for (AstExpr* e : error->expressions)
|
||||||
visitExpr(e);
|
visitExpr(e);
|
||||||
|
|
||||||
return {defArena->freshCell(Symbol{}, error->location), nullptr};
|
return {defArena->freshCell(), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
|
void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
|
||||||
|
@ -1148,12 +1087,12 @@ void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
|
||||||
|
|
||||||
DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, 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.
|
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
||||||
if (scope->canUpdateDefinition(l->local))
|
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;
|
scope->bindings[l->local] = updated;
|
||||||
captures[l->local].allVersions.push_back(updated);
|
captures[l->local].allVersions.push_back(updated);
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -1164,12 +1103,12 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
|
||||||
|
|
||||||
DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, 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.
|
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
||||||
if (scope->canUpdateDefinition(g->name))
|
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;
|
scope->bindings[g->name] = updated;
|
||||||
captures[g->name].allVersions.push_back(updated);
|
captures[g->name].allVersions.push_back(updated);
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -1182,10 +1121,10 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef)
|
||||||
{
|
{
|
||||||
DefId parentDef = visitExpr(i->expr).def;
|
DefId parentDef = visitExpr(i->expr).def;
|
||||||
|
|
||||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
DfgScope* scope = currentScope();
|
||||||
if (scope->canUpdateDefinition(parentDef, i->index.value))
|
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;
|
scope->props[parentDef][i->index.value] = updated;
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
@ -1198,12 +1137,12 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
|
||||||
DefId parentDef = visitExpr(i->expr).def;
|
DefId parentDef = visitExpr(i->expr).def;
|
||||||
visitExpr(i->index);
|
visitExpr(i->index);
|
||||||
|
|
||||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
DfgScope* scope = currentScope();
|
||||||
if (auto string = i->index->as<AstExprConstantString>())
|
if (auto string = i->index->as<AstExprConstantString>())
|
||||||
{
|
{
|
||||||
if (scope->canUpdateDefinition(parentDef, string->value.data))
|
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;
|
scope->props[parentDef][string->value.data] = updated;
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1150,7 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
|
||||||
return visitExpr(static_cast<AstExpr*>(i)).def;
|
return visitExpr(static_cast<AstExpr*>(i)).def;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true);
|
return defArena->freshCell(/*subscripted=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefId DataFlowGraphBuilder::visitLValue(AstExprError* error, DefId incomingDef)
|
DefId DataFlowGraphBuilder::visitLValue(AstExprError* error, DefId incomingDef)
|
||||||
|
|
|
@ -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)
|
DefId DefArena::phi(DefId a, DefId b)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -213,6 +215,15 @@ declare debug: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)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(
|
static const std::string kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare utf8: {
|
declare utf8: {
|
||||||
|
@ -298,7 +309,7 @@ std::string getBuiltinDefinitionSource()
|
||||||
result += kBuiltinDefinitionOsSrc;
|
result += kBuiltinDefinitionOsSrc;
|
||||||
result += kBuiltinDefinitionCoroutineSrc;
|
result += kBuiltinDefinitionCoroutineSrc;
|
||||||
result += kBuiltinDefinitionTableSrc;
|
result += kBuiltinDefinitionTableSrc;
|
||||||
result += kBuiltinDefinitionDebugSrc;
|
result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED;
|
||||||
result += kBuiltinDefinitionUtf8Src;
|
result += kBuiltinDefinitionUtf8Src;
|
||||||
result += kBuiltinDefinitionBufferSrc;
|
result += kBuiltinDefinitionBufferSrc;
|
||||||
result += kBuiltinDefinitionVectorSrc;
|
result += kBuiltinDefinitionVectorSrc;
|
||||||
|
|
|
@ -606,7 +606,7 @@ struct ErrorConverter
|
||||||
auto tfit = get<TypeFunctionInstanceType>(e.ty);
|
auto tfit = get<TypeFunctionInstanceType>(e.ty);
|
||||||
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type function.
|
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type function.
|
||||||
if (!tfit)
|
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
|
// unary operators
|
||||||
if (auto unaryString = kUnaryOps.find(tfit->function->name); unaryString != kUnaryOps.end())
|
if (auto unaryString = kUnaryOps.find(tfit->function->name); unaryString != kUnaryOps.end())
|
||||||
|
|
|
@ -22,27 +22,26 @@
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/Clone.h"
|
#include "Luau/Clone.h"
|
||||||
#include "AutocompleteCore.h"
|
#include "AutocompleteCore.h"
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
|
LUAU_FASTFLAGVARIABLE(LogFragmentsFromAutocomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAcSupportsReporter)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
||||||
|
LUAU_FASTFLAG(LuauModuleHoldsAstRoot)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneReturnTypePack)
|
LUAU_FASTFLAGVARIABLE(LuauCloneReturnTypePack)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteDemandBasedCloning)
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -88,433 +87,6 @@ void cloneModuleMap(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const Position& startPos, const Position& endPos);
|
|
||||||
|
|
||||||
// when typing a function partially, get the span of the first line
|
|
||||||
// e.g. local function fn() : ... - typically we want to provide autocomplete results if you're
|
|
||||||
// editing type annotations in this range
|
|
||||||
Location getFunctionDeclarationExtents(AstExprFunction* exprFn, AstExpr* exprName = nullptr, AstLocal* localName = nullptr)
|
|
||||||
{
|
|
||||||
auto fnBegin = exprFn->location.begin;
|
|
||||||
auto fnEnd = exprFn->location.end;
|
|
||||||
if (auto returnAnnot = exprFn->returnAnnotation)
|
|
||||||
{
|
|
||||||
if (returnAnnot->tailType)
|
|
||||||
fnEnd = returnAnnot->tailType->location.end;
|
|
||||||
else if (returnAnnot->types.size != 0)
|
|
||||||
fnEnd = returnAnnot->types.data[returnAnnot->types.size - 1]->location.end;
|
|
||||||
}
|
|
||||||
else if (exprFn->args.size != 0)
|
|
||||||
{
|
|
||||||
auto last = exprFn->args.data[exprFn->args.size - 1];
|
|
||||||
if (last->annotation)
|
|
||||||
fnEnd = last->annotation->location.end;
|
|
||||||
else
|
|
||||||
fnEnd = last->location.end;
|
|
||||||
}
|
|
||||||
else if (exprFn->genericPacks.size != 0)
|
|
||||||
fnEnd = exprFn->genericPacks.data[exprFn->genericPacks.size - 1]->location.end;
|
|
||||||
else if (exprFn->generics.size != 0)
|
|
||||||
fnEnd = exprFn->generics.data[exprFn->generics.size - 1]->location.end;
|
|
||||||
else if (exprName)
|
|
||||||
fnEnd = exprName->location.end;
|
|
||||||
else if (localName)
|
|
||||||
fnEnd = localName->location.end;
|
|
||||||
return Location{fnBegin, fnEnd};
|
|
||||||
};
|
|
||||||
|
|
||||||
Location getAstStatForExtents(AstStatFor* forStat)
|
|
||||||
{
|
|
||||||
auto begin = forStat->location.begin;
|
|
||||||
auto end = forStat->location.end;
|
|
||||||
if (forStat->step)
|
|
||||||
end = forStat->step->location.end;
|
|
||||||
else if (forStat->to)
|
|
||||||
end = forStat->to->location.end;
|
|
||||||
else if (forStat->from)
|
|
||||||
end = forStat->from->location.end;
|
|
||||||
else if (forStat->var)
|
|
||||||
end = forStat->var->location.end;
|
|
||||||
|
|
||||||
return Location{begin, end};
|
|
||||||
}
|
|
||||||
|
|
||||||
Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPosition)
|
|
||||||
{
|
|
||||||
Location empty{cursorPosition, cursorPosition};
|
|
||||||
if (nearestStatement)
|
|
||||||
{
|
|
||||||
Location nonEmpty{nearestStatement->location.begin, cursorPosition};
|
|
||||||
// If your sibling is a do block, do nothing
|
|
||||||
if (auto doEnd = nearestStatement->as<AstStatBlock>())
|
|
||||||
return empty;
|
|
||||||
|
|
||||||
// If you're inside the body of the function and this is your sibling, empty fragment
|
|
||||||
// If you're outside the body (e.g. you're typing stuff out, non-empty)
|
|
||||||
if (auto fn = nearestStatement->as<AstStatFunction>())
|
|
||||||
{
|
|
||||||
auto loc = getFunctionDeclarationExtents(fn->func, fn->name, /* local */ nullptr);
|
|
||||||
if (loc.containsClosed(cursorPosition))
|
|
||||||
return nonEmpty;
|
|
||||||
else if (fn->func->body->location.containsClosed(cursorPosition) || fn->location.end <= cursorPosition)
|
|
||||||
return empty;
|
|
||||||
else if (fn->func->location.contains(cursorPosition))
|
|
||||||
return nonEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto fn = nearestStatement->as<AstStatLocalFunction>())
|
|
||||||
{
|
|
||||||
auto loc = getFunctionDeclarationExtents(fn->func, /* global func */ nullptr, fn->name);
|
|
||||||
if (loc.containsClosed(cursorPosition))
|
|
||||||
return nonEmpty;
|
|
||||||
else if (fn->func->body->location.containsClosed(cursorPosition) || fn->location.end <= cursorPosition)
|
|
||||||
return empty;
|
|
||||||
else if (fn->func->location.contains(cursorPosition))
|
|
||||||
return nonEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto wh = nearestStatement->as<AstStatWhile>())
|
|
||||||
{
|
|
||||||
if (!wh->hasDo)
|
|
||||||
return nonEmpty;
|
|
||||||
else
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto forStat = nearestStatement->as<AstStatFor>())
|
|
||||||
{
|
|
||||||
if (!forStat->hasDo)
|
|
||||||
return nonEmpty;
|
|
||||||
else
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto forIn = nearestStatement->as<AstStatForIn>())
|
|
||||||
{
|
|
||||||
// If we don't have a do statement
|
|
||||||
if (!forIn->hasDo)
|
|
||||||
return nonEmpty;
|
|
||||||
else
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto ifS = nearestStatement->as<AstStatIf>())
|
|
||||||
{
|
|
||||||
auto conditionExtents = Location{ifS->location.begin, ifS->condition->location.end};
|
|
||||||
if (conditionExtents.containsClosed(cursorPosition))
|
|
||||||
return nonEmpty;
|
|
||||||
else if (ifS->thenbody->location.containsClosed(cursorPosition))
|
|
||||||
return empty;
|
|
||||||
else if (auto elseS = ifS->elsebody)
|
|
||||||
{
|
|
||||||
if (auto elseIf = ifS->elsebody->as<AstStatIf>())
|
|
||||||
{
|
|
||||||
|
|
||||||
if (elseIf->thenbody->hasEnd)
|
|
||||||
return empty;
|
|
||||||
else
|
|
||||||
return {elseS->location.begin, cursorPosition};
|
|
||||||
}
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nonEmpty;
|
|
||||||
}
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NearestStatementFinder : public AstVisitor
|
|
||||||
{
|
|
||||||
explicit NearestStatementFinder(const Position& cursorPosition)
|
|
||||||
: cursor(cursorPosition)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(AstStatBlock* block) override
|
|
||||||
{
|
|
||||||
if (block->location.containsClosed(cursor))
|
|
||||||
{
|
|
||||||
parent = block;
|
|
||||||
for (auto v : block->body)
|
|
||||||
{
|
|
||||||
if (v->location.begin <= cursor)
|
|
||||||
{
|
|
||||||
nearest = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Position& cursor;
|
|
||||||
AstStat* nearest = nullptr;
|
|
||||||
AstStatBlock* parent = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This struct takes a block found in a updated AST and looks for the corresponding block in a different ast.
|
|
||||||
// This is a best effort check - we are looking for the block that is as close in location, ideally the same
|
|
||||||
// block as the one from the updated AST
|
|
||||||
struct NearestLikelyBlockFinder : public AstVisitor
|
|
||||||
{
|
|
||||||
explicit NearestLikelyBlockFinder(NotNull<AstStatBlock> stmtBlockRecentAst)
|
|
||||||
: stmtBlockRecentAst(stmtBlockRecentAst)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(AstStatBlock* block) override
|
|
||||||
{
|
|
||||||
if (block->location.begin <= stmtBlockRecentAst->location.begin)
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
if (found.value()->location.begin < block->location.begin)
|
|
||||||
found.emplace(block);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
found.emplace(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
NotNull<AstStatBlock> stmtBlockRecentAst;
|
|
||||||
std::optional<AstStatBlock*> found = std::nullopt;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Diffs two ast stat blocks. Once at the first difference, consume between that range and the end of the nearest statement
|
|
||||||
std::optional<Position> blockDiffStart(AstStatBlock* blockOld, AstStatBlock* blockNew, AstStat* nearestStatementNewAst)
|
|
||||||
{
|
|
||||||
AstArray<AstStat*> _old = blockOld->body;
|
|
||||||
AstArray<AstStat*> _new = blockNew->body;
|
|
||||||
size_t oldSize = _old.size;
|
|
||||||
size_t stIndex = 0;
|
|
||||||
|
|
||||||
// We couldn't find a nearest statement
|
|
||||||
if (nearestStatementNewAst == blockNew)
|
|
||||||
return std::nullopt;
|
|
||||||
bool found = false;
|
|
||||||
for (auto st : _new)
|
|
||||||
{
|
|
||||||
if (st == nearestStatementNewAst)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return std::nullopt;
|
|
||||||
// Take care of some easy cases!
|
|
||||||
if (oldSize == 0 && _new.size >= 0)
|
|
||||||
return {_new.data[0]->location.begin};
|
|
||||||
|
|
||||||
if (_new.size < oldSize)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < std::min(oldSize, stIndex + 1); i++)
|
|
||||||
{
|
|
||||||
AstStat* oldStat = _old.data[i];
|
|
||||||
AstStat* newStat = _new.data[i];
|
|
||||||
|
|
||||||
bool isSame = oldStat->classIndex == newStat->classIndex && oldStat->location == newStat->location;
|
|
||||||
if (!isSame)
|
|
||||||
return {oldStat->location.begin};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldSize <= stIndex)
|
|
||||||
return {_new.data[oldSize]->location.begin};
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FragmentRegion getFragmentRegion(AstStatBlock* root, const Position& cursorPosition)
|
|
||||||
{
|
|
||||||
NearestStatementFinder nsf{cursorPosition};
|
|
||||||
root->visit(&nsf);
|
|
||||||
AstStatBlock* parent = root;
|
|
||||||
if (nsf.parent)
|
|
||||||
parent = nsf.parent;
|
|
||||||
return FragmentRegion{getFragmentLocation(nsf.nearest, cursorPosition), nsf.nearest, parent};
|
|
||||||
};
|
|
||||||
|
|
||||||
FragmentRegion getFragmentRegionWithBlockDiff(AstStatBlock* stale, AstStatBlock* fresh, const Position& cursorPos)
|
|
||||||
{
|
|
||||||
// Visit the new ast
|
|
||||||
NearestStatementFinder nsf{cursorPos};
|
|
||||||
fresh->visit(&nsf);
|
|
||||||
// parent must always be non-null
|
|
||||||
NotNull<AstStatBlock> parent{nsf.parent ? nsf.parent : fresh};
|
|
||||||
NotNull<AstStat> nearest{nsf.nearest ? nsf.nearest : fresh};
|
|
||||||
// Grab the same start block in the stale ast
|
|
||||||
NearestLikelyBlockFinder lsf{parent};
|
|
||||||
stale->visit(&lsf);
|
|
||||||
|
|
||||||
if (auto sameBlock = lsf.found)
|
|
||||||
{
|
|
||||||
if (std::optional<Position> fd = blockDiffStart(*sameBlock, parent, nearest))
|
|
||||||
return FragmentRegion{Location{*fd, cursorPos}, nearest, parent};
|
|
||||||
}
|
|
||||||
return FragmentRegion{getFragmentLocation(nsf.nearest, cursorPos), nearest, parent};
|
|
||||||
}
|
|
||||||
|
|
||||||
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* stale, const Position& cursorPos, AstStatBlock* lastGoodParse)
|
|
||||||
{
|
|
||||||
// the freshest ast can sometimes be null if the parse was bad.
|
|
||||||
if (lastGoodParse == nullptr)
|
|
||||||
return {};
|
|
||||||
FragmentRegion region = FFlag::LuauBlockDiffFragmentSelection ? getFragmentRegionWithBlockDiff(stale, lastGoodParse, cursorPos)
|
|
||||||
: getFragmentRegion(lastGoodParse, cursorPos);
|
|
||||||
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(stale, cursorPos);
|
|
||||||
LUAU_ASSERT(ancestry.size() >= 1);
|
|
||||||
// We should only pick up locals that are before the region
|
|
||||||
DenseHashMap<AstName, AstLocal*> localMap{AstName()};
|
|
||||||
std::vector<AstLocal*> localStack;
|
|
||||||
|
|
||||||
for (AstNode* node : ancestry)
|
|
||||||
{
|
|
||||||
if (auto block = node->as<AstStatBlock>())
|
|
||||||
{
|
|
||||||
for (auto stat : block->body)
|
|
||||||
{
|
|
||||||
if (stat->location.begin < region.fragmentLocation.begin)
|
|
||||||
{
|
|
||||||
// This statement precedes the current one
|
|
||||||
if (auto statLoc = stat->as<AstStatLocal>())
|
|
||||||
{
|
|
||||||
for (auto v : statLoc->vars)
|
|
||||||
{
|
|
||||||
localStack.push_back(v);
|
|
||||||
localMap[v->name] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto locFun = stat->as<AstStatLocalFunction>())
|
|
||||||
{
|
|
||||||
localStack.push_back(locFun->name);
|
|
||||||
localMap[locFun->name->name] = locFun->name;
|
|
||||||
if (locFun->location.contains(cursorPos))
|
|
||||||
{
|
|
||||||
for (AstLocal* loc : locFun->func->args)
|
|
||||||
{
|
|
||||||
localStack.push_back(loc);
|
|
||||||
localMap[loc->name] = loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto globFun = stat->as<AstStatFunction>())
|
|
||||||
{
|
|
||||||
if (globFun->location.contains(cursorPos))
|
|
||||||
{
|
|
||||||
for (AstLocal* loc : globFun->func->args)
|
|
||||||
{
|
|
||||||
localStack.push_back(loc);
|
|
||||||
localMap[loc->name] = loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto typeFun = stat->as<AstStatTypeFunction>(); typeFun)
|
|
||||||
{
|
|
||||||
if (typeFun->location.contains(cursorPos))
|
|
||||||
{
|
|
||||||
for (AstLocal* loc : typeFun->body->args)
|
|
||||||
{
|
|
||||||
localStack.push_back(loc);
|
|
||||||
localMap[loc->name] = loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto forL = stat->as<AstStatFor>())
|
|
||||||
{
|
|
||||||
if (forL->var && forL->var->location.begin < region.fragmentLocation.begin)
|
|
||||||
{
|
|
||||||
localStack.push_back(forL->var);
|
|
||||||
localMap[forL->var->name] = forL->var;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto forIn = stat->as<AstStatForIn>())
|
|
||||||
{
|
|
||||||
for (auto var : forIn->vars)
|
|
||||||
{
|
|
||||||
if (var->location.begin < region.fragmentLocation.begin)
|
|
||||||
{
|
|
||||||
localStack.push_back(var);
|
|
||||||
localMap[var->name] = var;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto exprFunc = node->as<AstExprFunction>())
|
|
||||||
{
|
|
||||||
if (exprFunc->location.contains(cursorPos))
|
|
||||||
{
|
|
||||||
for (auto v : exprFunc->args)
|
|
||||||
{
|
|
||||||
localStack.push_back(v);
|
|
||||||
localMap[v->name] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {localMap, localStack, ancestry, region.nearestStatement, region.parentBlock, region.fragmentLocation};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<FragmentParseResult> parseFragment(
|
|
||||||
AstStatBlock* stale,
|
|
||||||
AstStatBlock* mostRecentParse,
|
|
||||||
AstNameTable* names,
|
|
||||||
std::string_view src,
|
|
||||||
const Position& cursorPos,
|
|
||||||
std::optional<Position> fragmentEndPosition
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (mostRecentParse == nullptr)
|
|
||||||
return std::nullopt;
|
|
||||||
FragmentAutocompleteAncestryResult result = findAncestryForFragmentParse(stale, cursorPos, mostRecentParse);
|
|
||||||
AstStat* nearestStatement = result.nearestStatement;
|
|
||||||
|
|
||||||
Position startPos = result.fragmentSelectionRegion.begin;
|
|
||||||
Position endPos = fragmentEndPosition.value_or(result.fragmentSelectionRegion.end);
|
|
||||||
|
|
||||||
auto [offsetStart, parseLength] = getDocumentOffsets(src, startPos, endPos);
|
|
||||||
const char* srcStart = src.data() + offsetStart;
|
|
||||||
std::string_view dbg = src.substr(offsetStart, parseLength);
|
|
||||||
FragmentParseResult fragmentResult;
|
|
||||||
fragmentResult.fragmentToParse = std::string(dbg);
|
|
||||||
// For the duration of the incremental parse, we want to allow the name table to re-use duplicate names
|
|
||||||
if (FFlag::DebugLogFragmentsFromAutocomplete)
|
|
||||||
logLuau("Fragment Selected", dbg);
|
|
||||||
|
|
||||||
ParseOptions opts;
|
|
||||||
opts.allowDeclarationSyntax = false;
|
|
||||||
opts.captureComments = true;
|
|
||||||
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos};
|
|
||||||
ParseResult p = Luau::Parser::parse(srcStart, parseLength, *names, *fragmentResult.alloc, opts);
|
|
||||||
// This means we threw a ParseError and we should decline to offer autocomplete here.
|
|
||||||
if (p.root == nullptr)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
std::vector<AstNode*> fabricatedAncestry = std::move(result.ancestry);
|
|
||||||
std::vector<AstNode*> fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorPos);
|
|
||||||
fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end());
|
|
||||||
if (nearestStatement == nullptr)
|
|
||||||
nearestStatement = p.root;
|
|
||||||
fragmentResult.root = p.root;
|
|
||||||
fragmentResult.ancestry = std::move(fabricatedAncestry);
|
|
||||||
fragmentResult.nearestStatement = nearestStatement;
|
|
||||||
fragmentResult.commentLocations = std::move(p.commentLocations);
|
|
||||||
fragmentResult.scopePos = result.parentBlock->location.begin;
|
|
||||||
return fragmentResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UsageFinder : public AstVisitor
|
struct UsageFinder : public AstVisitor
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -586,7 +158,6 @@ void cloneTypesFromFragment(
|
||||||
const ModulePtr& staleModule,
|
const ModulePtr& staleModule,
|
||||||
NotNull<TypeArena> destArena,
|
NotNull<TypeArena> destArena,
|
||||||
NotNull<DataFlowGraph> dfg,
|
NotNull<DataFlowGraph> dfg,
|
||||||
NotNull<BuiltinTypes> builtins,
|
|
||||||
AstStatBlock* program,
|
AstStatBlock* program,
|
||||||
Scope* destScope
|
Scope* destScope
|
||||||
)
|
)
|
||||||
|
@ -617,13 +188,6 @@ void cloneTypesFromFragment(
|
||||||
destScope->lvalueTypes[d] = Luau::cloneIncremental(pair->second.typeId, *destArena, cloneState, destScope);
|
destScope->lvalueTypes[d] = Luau::cloneIncremental(pair->second.typeId, *destArena, cloneState, destScope);
|
||||||
destScope->bindings[pair->first] = Luau::cloneIncremental(pair->second, *destArena, cloneState, destScope);
|
destScope->bindings[pair->first] = Luau::cloneIncremental(pair->second, *destArena, cloneState, destScope);
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauBetterScopeSelection && !FFlag::LuauBlockDiffFragmentSelection)
|
|
||||||
{
|
|
||||||
destScope->lvalueTypes[d] = builtins->unknownType;
|
|
||||||
Binding b;
|
|
||||||
b.typeId = builtins->unknownType;
|
|
||||||
destScope->bindings[Symbol(loc)] = b;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second - any referenced type alias bindings need to be placed in scope so type annotation can be resolved.
|
// Second - any referenced type alias bindings need to be placed in scope so type annotation can be resolved.
|
||||||
|
@ -835,11 +399,16 @@ static FrontendModuleResolver& getModuleResolver(Frontend& frontend, std::option
|
||||||
}
|
}
|
||||||
|
|
||||||
bool statIsBeforePos(const AstNode* stat, const Position& cursorPos)
|
bool statIsBeforePos(const AstNode* stat, const Position& cursorPos)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauIncrementalAutocompleteBugfixes)
|
||||||
{
|
{
|
||||||
return (stat->location.begin < cursorPos);
|
return (stat->location.begin < cursorPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstStatBlock* root, const Position& cursorPos)
|
return stat->location.begin < cursorPos && stat->location.begin.line < cursorPos.line;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* root, const Position& cursorPos)
|
||||||
{
|
{
|
||||||
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(root, cursorPos);
|
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(root, cursorPos);
|
||||||
// Should always contain the root AstStat
|
// Should always contain the root AstStat
|
||||||
|
@ -868,7 +437,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstSt
|
||||||
{
|
{
|
||||||
for (auto stat : block->body)
|
for (auto stat : block->body)
|
||||||
{
|
{
|
||||||
if (statIsBeforePos(stat, nearestStatement->location.begin))
|
if (statIsBeforePos(stat, FFlag::LuauIncrementalAutocompleteBugfixes ? nearestStatement->location.begin : cursorPos))
|
||||||
{
|
{
|
||||||
// This statement precedes the current one
|
// This statement precedes the current one
|
||||||
if (auto statLoc = stat->as<AstStatLocal>())
|
if (auto statLoc = stat->as<AstStatLocal>())
|
||||||
|
@ -917,6 +486,8 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (FFlag::LuauIncrementalAutocompleteBugfixes)
|
||||||
|
{
|
||||||
if (auto exprFunc = node->as<AstExprFunction>())
|
if (auto exprFunc = node->as<AstExprFunction>())
|
||||||
{
|
{
|
||||||
if (exprFunc->location.contains(cursorPos))
|
if (exprFunc->location.contains(cursorPos))
|
||||||
|
@ -929,6 +500,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {std::move(localMap), std::move(localStack), std::move(ancestry), std::move(nearestStatement)};
|
return {std::move(localMap), std::move(localStack), std::move(ancestry), std::move(nearestStatement)};
|
||||||
}
|
}
|
||||||
|
@ -941,7 +513,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstSt
|
||||||
* Example - your document is "foo bar baz" and getDocumentOffsets is passed (0, 4), (0, 8). This function returns the pair {3, 5}
|
* Example - your document is "foo bar baz" and getDocumentOffsets is passed (0, 4), (0, 8). This function returns the pair {3, 5}
|
||||||
* which corresponds to the string " bar "
|
* which corresponds to the string " bar "
|
||||||
*/
|
*/
|
||||||
static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const Position& startPos, const Position& endPos)
|
std::pair<size_t, size_t> getDocumentOffsets(const std::string_view& src, const Position& startPos, const Position& endPos)
|
||||||
{
|
{
|
||||||
size_t lineCount = 0;
|
size_t lineCount = 0;
|
||||||
size_t colCount = 0;
|
size_t colCount = 0;
|
||||||
|
@ -998,14 +570,14 @@ static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const
|
||||||
return {min, len};
|
return {min, len};
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopePtr findClosestScope_DEPRECATED(const ModulePtr& module, const AstStat* nearestStatement)
|
ScopePtr findClosestScope(const ModulePtr& module, const AstStat* nearestStatement)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(module->hasModuleScope());
|
LUAU_ASSERT(module->hasModuleScope());
|
||||||
|
|
||||||
ScopePtr closest = module->getModuleScope();
|
ScopePtr closest = module->getModuleScope();
|
||||||
|
|
||||||
// find the scope the nearest statement belonged to.
|
// find the scope the nearest statement belonged to.
|
||||||
for (const auto& [loc, sc] : module->scopes)
|
for (auto [loc, sc] : module->scopes)
|
||||||
{
|
{
|
||||||
if (loc.encloses(nearestStatement->location) && closest->location.begin <= loc.begin)
|
if (loc.encloses(nearestStatement->location) && closest->location.begin <= loc.begin)
|
||||||
closest = sc;
|
closest = sc;
|
||||||
|
@ -1014,38 +586,7 @@ ScopePtr findClosestScope_DEPRECATED(const ModulePtr& module, const AstStat* nea
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopePtr findClosestScope(const ModulePtr& module, const Position& scopePos)
|
std::optional<FragmentParseResult> parseFragment(
|
||||||
{
|
|
||||||
LUAU_ASSERT(module->hasModuleScope());
|
|
||||||
if (FFlag::LuauBlockDiffFragmentSelection)
|
|
||||||
{
|
|
||||||
ScopePtr closest = module->getModuleScope();
|
|
||||||
// find the scope the nearest statement belonged to.
|
|
||||||
for (const auto& [loc, sc] : module->scopes)
|
|
||||||
{
|
|
||||||
// We bias towards the later scopes because those correspond to inner scopes.
|
|
||||||
// in the case of if statements, we create two scopes at the same location for the body of the then
|
|
||||||
// and else branches, so we need to bias later. This is why the closest update condition has a <=
|
|
||||||
// instead of a <
|
|
||||||
if (sc->location.contains(scopePos) && closest->location.begin <= sc->location.begin)
|
|
||||||
closest = sc;
|
|
||||||
}
|
|
||||||
return closest;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ScopePtr closest = module->getModuleScope();
|
|
||||||
// find the scope the nearest statement belonged to.
|
|
||||||
for (const auto& [loc, sc] : module->scopes)
|
|
||||||
{
|
|
||||||
if (sc->location.contains(scopePos) && closest->location.begin < sc->location.begin)
|
|
||||||
closest = sc;
|
|
||||||
}
|
|
||||||
return closest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<FragmentParseResult> parseFragment_DEPRECATED(
|
|
||||||
AstStatBlock* root,
|
AstStatBlock* root,
|
||||||
AstNameTable* names,
|
AstNameTable* names,
|
||||||
std::string_view src,
|
std::string_view src,
|
||||||
|
@ -1053,7 +594,7 @@ std::optional<FragmentParseResult> parseFragment_DEPRECATED(
|
||||||
std::optional<Position> fragmentEndPosition
|
std::optional<Position> fragmentEndPosition
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
FragmentAutocompleteAncestryResult result = findAncestryForFragmentParse_DEPRECATED(root, cursorPos);
|
FragmentAutocompleteAncestryResult result = findAncestryForFragmentParse(root, cursorPos);
|
||||||
AstStat* nearestStatement = result.nearestStatement;
|
AstStat* nearestStatement = result.nearestStatement;
|
||||||
|
|
||||||
const Location& rootSpan = root->location;
|
const Location& rootSpan = root->location;
|
||||||
|
@ -1084,8 +625,8 @@ std::optional<FragmentParseResult> parseFragment_DEPRECATED(
|
||||||
FragmentParseResult fragmentResult;
|
FragmentParseResult fragmentResult;
|
||||||
fragmentResult.fragmentToParse = std::string(dbg.data(), parseLength);
|
fragmentResult.fragmentToParse = std::string(dbg.data(), parseLength);
|
||||||
// For the duration of the incremental parse, we want to allow the name table to re-use duplicate names
|
// For the duration of the incremental parse, we want to allow the name table to re-use duplicate names
|
||||||
if (FFlag::DebugLogFragmentsFromAutocomplete)
|
if (FFlag::LogFragmentsFromAutocomplete)
|
||||||
logLuau("Fragment Selected", dbg);
|
logLuau(dbg);
|
||||||
|
|
||||||
ParseOptions opts;
|
ParseOptions opts;
|
||||||
opts.allowDeclarationSyntax = false;
|
opts.allowDeclarationSyntax = false;
|
||||||
|
@ -1213,7 +754,7 @@ void mixedModeCompatibility(
|
||||||
|
|
||||||
static void reportWaypoint(IFragmentAutocompleteReporter* reporter, FragmentAutocompleteWaypoint type)
|
static void reportWaypoint(IFragmentAutocompleteReporter* reporter, FragmentAutocompleteWaypoint type)
|
||||||
{
|
{
|
||||||
if (!reporter)
|
if (!FFlag::LuauFragmentAcSupportsReporter || !reporter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
reporter->reportWaypoint(type);
|
reporter->reportWaypoint(type);
|
||||||
|
@ -1221,7 +762,7 @@ static void reportWaypoint(IFragmentAutocompleteReporter* reporter, FragmentAuto
|
||||||
|
|
||||||
static void reportFragmentString(IFragmentAutocompleteReporter* reporter, std::string_view fragment)
|
static void reportFragmentString(IFragmentAutocompleteReporter* reporter, std::string_view fragment)
|
||||||
{
|
{
|
||||||
if (!reporter)
|
if (!FFlag::LuauFragmentAcSupportsReporter || !reporter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
reporter->reportFragmentString(fragment);
|
reporter->reportFragmentString(fragment);
|
||||||
|
@ -1351,8 +892,6 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
||||||
{
|
{
|
||||||
if (!sc->interiorFreeTypes.has_value())
|
if (!sc->interiorFreeTypes.has_value())
|
||||||
sc->interiorFreeTypes.emplace();
|
sc->interiorFreeTypes.emplace();
|
||||||
if (!sc->interiorFreeTypePacks.has_value())
|
|
||||||
sc->interiorFreeTypePacks.emplace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1474,7 +1013,6 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
std::shared_ptr<Scope> freshChildOfNearestScope = std::make_shared<Scope>(nullptr);
|
std::shared_ptr<Scope> freshChildOfNearestScope = std::make_shared<Scope>(nullptr);
|
||||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||||
freshChildOfNearestScope->interiorFreeTypes.emplace();
|
freshChildOfNearestScope->interiorFreeTypes.emplace();
|
||||||
freshChildOfNearestScope->interiorFreeTypePacks.emplace();
|
|
||||||
cg.rootScope = freshChildOfNearestScope.get();
|
cg.rootScope = freshChildOfNearestScope.get();
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunTypecheck)
|
if (FFlag::LuauUserTypeFunTypecheck)
|
||||||
|
@ -1487,14 +1025,7 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
||||||
cloneTypesFromFragment(
|
cloneTypesFromFragment(
|
||||||
cloneState,
|
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
||||||
closestScope.get(),
|
|
||||||
stale,
|
|
||||||
NotNull{&incrementalModule->internalTypes},
|
|
||||||
NotNull{&dfg},
|
|
||||||
frontend.builtinTypes,
|
|
||||||
root,
|
|
||||||
freshChildOfNearestScope.get()
|
|
||||||
);
|
);
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeEnd);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeEnd);
|
||||||
|
|
||||||
|
@ -1555,7 +1086,6 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
std::optional<FrontendOptions> opts,
|
std::optional<FrontendOptions> opts,
|
||||||
std::string_view src,
|
std::string_view src,
|
||||||
std::optional<Position> fragmentEndPosition,
|
std::optional<Position> fragmentEndPosition,
|
||||||
AstStatBlock* recentParse,
|
|
||||||
IFragmentAutocompleteReporter* reporter
|
IFragmentAutocompleteReporter* reporter
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -1574,9 +1104,30 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<FragmentParseResult> tryParse;
|
std::optional<FragmentParseResult> tryParse;
|
||||||
tryParse = FFlag::LuauBetterScopeSelection ? parseFragment(module->root, recentParse, module->names.get(), src, cursorPos, fragmentEndPosition)
|
if (FFlag::LuauModuleHoldsAstRoot)
|
||||||
: parseFragment_DEPRECATED(module->root, module->names.get(), src, cursorPos, fragmentEndPosition);
|
{
|
||||||
|
tryParse = parseFragment(module->root, module->names.get(), src, cursorPos, fragmentEndPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
||||||
|
if (!sourceModule)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!"Expected Source Module for fragment typecheck");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauIncrementalAutocompleteBugfixes)
|
||||||
|
{
|
||||||
|
if (sourceModule->allocator.get() != module->allocator.get())
|
||||||
|
{
|
||||||
|
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tryParse = parseFragment(sourceModule->root, sourceModule->names.get(), src, cursorPos, fragmentEndPosition);
|
||||||
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ParseFragmentEnd);
|
||||||
|
}
|
||||||
|
|
||||||
if (!tryParse)
|
if (!tryParse)
|
||||||
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
||||||
|
@ -1587,8 +1138,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
||||||
|
|
||||||
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
||||||
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
|
const ScopePtr& closestScope = findClosestScope(module, parseResult.nearestStatement);
|
||||||
: findClosestScope_DEPRECATED(module, parseResult.nearestStatement);
|
|
||||||
FragmentTypeCheckResult result =
|
FragmentTypeCheckResult result =
|
||||||
FFlag::LuauIncrementalAutocompleteDemandBasedCloning
|
FFlag::LuauIncrementalAutocompleteDemandBasedCloning
|
||||||
? typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter)
|
? typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter)
|
||||||
|
@ -1624,15 +1174,14 @@ FragmentAutocompleteStatusResult tryFragmentAutocomplete(
|
||||||
context.opts,
|
context.opts,
|
||||||
std::move(stringCompletionCB),
|
std::move(stringCompletionCB),
|
||||||
context.DEPRECATED_fragmentEndPosition,
|
context.DEPRECATED_fragmentEndPosition,
|
||||||
context.freshParse.root,
|
FFlag::LuauFragmentAcSupportsReporter ? context.reporter : nullptr
|
||||||
context.reporter
|
|
||||||
);
|
);
|
||||||
return {FragmentAutocompleteStatus::Success, std::move(fragmentAutocomplete)};
|
return {FragmentAutocompleteStatus::Success, std::move(fragmentAutocomplete)};
|
||||||
}
|
}
|
||||||
catch (const Luau::InternalCompilerError& e)
|
catch (const Luau::InternalCompilerError& e)
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLogFragmentsFromAutocomplete)
|
if (FFlag::LogFragmentsFromAutocomplete)
|
||||||
logLuau("tryFragmentAutocomplete exception", e.what());
|
logLuau(e.what());
|
||||||
return {FragmentAutocompleteStatus::InternalIce, std::nullopt};
|
return {FragmentAutocompleteStatus::InternalIce, std::nullopt};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1645,7 +1194,6 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
std::optional<FrontendOptions> opts,
|
std::optional<FrontendOptions> opts,
|
||||||
StringCompletionCallback callback,
|
StringCompletionCallback callback,
|
||||||
std::optional<Position> fragmentEndPosition,
|
std::optional<Position> fragmentEndPosition,
|
||||||
AstStatBlock* recentParse,
|
|
||||||
IFragmentAutocompleteReporter* reporter
|
IFragmentAutocompleteReporter* reporter
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -1653,21 +1201,33 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete");
|
||||||
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
|
||||||
|
|
||||||
auto [tcStatus, tcResult] = typecheckFragment(frontend, moduleName, cursorPosition, opts, src, fragmentEndPosition, recentParse, reporter);
|
if (!FFlag::LuauModuleHoldsAstRoot)
|
||||||
|
{
|
||||||
|
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
|
||||||
|
if (!sourceModule)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!"Expected Source Module for fragment typecheck");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the cursor is within a comment in the stale source module we should avoid providing a recommendation
|
||||||
|
if (isWithinComment(*sourceModule, fragmentEndPosition.value_or(cursorPosition)))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [tcStatus, tcResult] = typecheckFragment(frontend, moduleName, cursorPosition, opts, src, fragmentEndPosition, reporter);
|
||||||
if (tcStatus == FragmentTypeCheckStatus::SkipAutocomplete)
|
if (tcStatus == FragmentTypeCheckStatus::SkipAutocomplete)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::TypecheckFragmentEnd);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::TypecheckFragmentEnd);
|
||||||
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();
|
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();
|
||||||
if (FFlag::DebugLogFragmentsFromAutocomplete)
|
if (FFlag::LogFragmentsFromAutocomplete)
|
||||||
logLuau("Fragment Autocomplete Source Script", src);
|
logLuau(src);
|
||||||
TypeArena arenaForAutocomplete_DEPRECATED;
|
TypeArena arenaForFragmentAutocomplete;
|
||||||
if (FFlag::LuauFragmentAcMemoryLeak)
|
|
||||||
unfreeze(tcResult.incrementalModule->internalTypes);
|
|
||||||
auto result = Luau::autocomplete_(
|
auto result = Luau::autocomplete_(
|
||||||
tcResult.incrementalModule,
|
tcResult.incrementalModule,
|
||||||
frontend.builtinTypes,
|
frontend.builtinTypes,
|
||||||
FFlag::LuauFragmentAcMemoryLeak ? &tcResult.incrementalModule->internalTypes : &arenaForAutocomplete_DEPRECATED,
|
&arenaForFragmentAutocomplete,
|
||||||
tcResult.ancestry,
|
tcResult.ancestry,
|
||||||
globalScope,
|
globalScope,
|
||||||
tcResult.freshScope,
|
tcResult.freshScope,
|
||||||
|
@ -1675,10 +1235,9 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
frontend.fileResolver,
|
frontend.fileResolver,
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
if (FFlag::LuauFragmentAcMemoryLeak)
|
|
||||||
freeze(tcResult.incrementalModule->internalTypes);
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::AutocompleteEnd);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::AutocompleteEnd);
|
||||||
return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(arenaForAutocomplete_DEPRECATED), std::move(result)};
|
return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(arenaForFragmentAutocomplete), std::move(result)};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
|
|
||||||
|
#include "Luau/AnyTypeSummary.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Clone.h"
|
#include "Luau/Clone.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
@ -39,15 +40,20 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRethrowKnownExceptions, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||||
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauModuleHoldsAstRoot)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFixMultithreadTypecheck)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -455,6 +461,20 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
|
||||||
|
|
||||||
if (item.name == name)
|
if (item.name == name)
|
||||||
checkResult.lintResult = item.module->lintResult;
|
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;
|
return checkResult;
|
||||||
|
@ -476,6 +496,11 @@ std::vector<ModuleName> Frontend::checkQueuedModules(
|
||||||
std::function<bool(size_t done, size_t total)> progress
|
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);
|
FrontendOptions frontendOptions = optionOverride.value_or(options);
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
frontendOptions.forAutocomplete = false;
|
frontendOptions.forAutocomplete = false;
|
||||||
|
@ -660,6 +685,247 @@ std::vector<ModuleName> Frontend::checkQueuedModules(
|
||||||
return checkedModules;
|
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)
|
std::optional<CheckResult> Frontend::getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -951,7 +1217,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||||
item.stats.timeCheck += duration;
|
item.stats.timeCheck += duration;
|
||||||
item.stats.filesStrict += 1;
|
item.stats.filesStrict += 1;
|
||||||
|
|
||||||
if (item.options.customModuleCheck)
|
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||||
item.options.customModuleCheck(sourceModule, *moduleForAutocomplete);
|
item.options.customModuleCheck(sourceModule, *moduleForAutocomplete);
|
||||||
|
|
||||||
item.module = moduleForAutocomplete;
|
item.module = moduleForAutocomplete;
|
||||||
|
@ -971,7 +1237,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||||
item.stats.filesStrict += mode == Mode::Strict;
|
item.stats.filesStrict += mode == Mode::Strict;
|
||||||
item.stats.filesNonstrict += mode == Mode::Nonstrict;
|
item.stats.filesNonstrict += mode == Mode::Nonstrict;
|
||||||
|
|
||||||
if (item.options.customModuleCheck)
|
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||||
item.options.customModuleCheck(sourceModule, *module);
|
item.options.customModuleCheck(sourceModule, *module);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && mode == Mode::NoCheck)
|
if (FFlag::LuauSolverV2 && mode == Mode::NoCheck)
|
||||||
|
@ -1101,19 +1367,6 @@ void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state,
|
||||||
{
|
{
|
||||||
BuildQueueItem& item = state->buildQueueItems[itemPos];
|
BuildQueueItem& item = state->buildQueueItems[itemPos];
|
||||||
|
|
||||||
if (DFFlag::LuauRethrowKnownExceptions)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
checkBuildQueueItem(item);
|
|
||||||
}
|
|
||||||
catch (const Luau::InternalCompilerError&)
|
|
||||||
{
|
|
||||||
item.exception = std::current_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
checkBuildQueueItem(item);
|
checkBuildQueueItem(item);
|
||||||
|
@ -1122,7 +1375,6 @@ void Frontend::performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state,
|
||||||
{
|
{
|
||||||
item.exception = std::current_exception();
|
item.exception = std::current_exception();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock guard(state->mtx);
|
std::unique_lock guard(state->mtx);
|
||||||
|
@ -1380,6 +1632,7 @@ ModulePtr check(
|
||||||
result->interfaceTypes.owningModule = result.get();
|
result->interfaceTypes.owningModule = result.get();
|
||||||
result->allocator = sourceModule.allocator;
|
result->allocator = sourceModule.allocator;
|
||||||
result->names = sourceModule.names;
|
result->names = sourceModule.names;
|
||||||
|
if (FFlag::LuauModuleHoldsAstRoot)
|
||||||
result->root = sourceModule.root;
|
result->root = sourceModule.root;
|
||||||
|
|
||||||
iceHandler->moduleName = sourceModule.name;
|
iceHandler->moduleName = sourceModule.name;
|
||||||
|
@ -1405,7 +1658,7 @@ ModulePtr check(
|
||||||
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
|
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
|
||||||
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
|
||||||
|
|
||||||
typeFunctionRuntime.allowEvaluation = FFlag::LuauTypeFunResultInAutocomplete || sourceModule.parseErrors.empty();
|
typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty();
|
||||||
|
|
||||||
ConstraintGenerator cg{
|
ConstraintGenerator cg{
|
||||||
result,
|
result,
|
||||||
|
|
|
@ -4,84 +4,19 @@
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/InsertionOrderedMap.h"
|
|
||||||
#include "Luau/Polarity.h"
|
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/ToString.h"
|
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauGeneralizationRemoveRecursiveUpperBound2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct OrderedSet
|
|
||||||
{
|
|
||||||
using iterator = typename std::vector<T>::iterator;
|
|
||||||
using const_iterator = typename std::vector<T>::const_iterator;
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return elements.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
return elements.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(T t)
|
|
||||||
{
|
|
||||||
if (!elementSet.contains(t))
|
|
||||||
{
|
|
||||||
elementSet.insert(t);
|
|
||||||
elements.push_back(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin()
|
|
||||||
{
|
|
||||||
return elements.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const
|
|
||||||
{
|
|
||||||
return elements.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end()
|
|
||||||
{
|
|
||||||
return elements.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const
|
|
||||||
{
|
|
||||||
return elements.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Move the underlying vector out of the OrderedSet.
|
|
||||||
std::vector<T> takeVector()
|
|
||||||
{
|
|
||||||
elementSet.clear();
|
|
||||||
return std::move(elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<T> elements;
|
|
||||||
DenseHashSet<T> elementSet{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
struct MutatingGeneralizer : TypeOnceVisitor
|
struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
NotNull<TypeArena> arena;
|
NotNull<TypeArena> arena;
|
||||||
|
@ -95,6 +30,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
|
|
||||||
bool isWithinFunction = false;
|
bool isWithinFunction = false;
|
||||||
|
bool avoidSealingTables = false;
|
||||||
|
|
||||||
MutatingGeneralizer(
|
MutatingGeneralizer(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
|
@ -102,7 +38,8 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
DenseHashMap<const void*, size_t> positiveTypes,
|
DenseHashMap<const void*, size_t> positiveTypes,
|
||||||
DenseHashMap<const void*, size_t> negativeTypes
|
DenseHashMap<const void*, size_t> negativeTypes,
|
||||||
|
bool avoidSealingTables
|
||||||
)
|
)
|
||||||
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
|
@ -111,6 +48,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
, cachedTypes(cachedTypes)
|
, cachedTypes(cachedTypes)
|
||||||
, positiveTypes(std::move(positiveTypes))
|
, positiveTypes(std::move(positiveTypes))
|
||||||
, negativeTypes(std::move(negativeTypes))
|
, negativeTypes(std::move(negativeTypes))
|
||||||
|
, avoidSealingTables(avoidSealingTables)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +99,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
LUAU_ASSERT(onlyType != haystack);
|
LUAU_ASSERT(onlyType != haystack);
|
||||||
emplaceType<BoundType>(asMutable(haystack), onlyType);
|
emplaceType<BoundType>(asMutable(haystack), onlyType);
|
||||||
}
|
}
|
||||||
else if (ut->options.empty())
|
else if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound2 && ut->options.empty())
|
||||||
{
|
{
|
||||||
emplaceType<BoundType>(asMutable(haystack), builtinTypes->neverType);
|
emplaceType<BoundType>(asMutable(haystack), builtinTypes->neverType);
|
||||||
}
|
}
|
||||||
|
@ -208,7 +146,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
LUAU_ASSERT(onlyType != needle);
|
LUAU_ASSERT(onlyType != needle);
|
||||||
emplaceType<BoundType>(asMutable(needle), onlyType);
|
emplaceType<BoundType>(asMutable(needle), onlyType);
|
||||||
}
|
}
|
||||||
else if (it->parts.empty())
|
else if (FFlag::LuauGeneralizationRemoveRecursiveUpperBound2 && it->parts.empty())
|
||||||
{
|
{
|
||||||
emplaceType<BoundType>(asMutable(needle), builtinTypes->unknownType);
|
emplaceType<BoundType>(asMutable(needle), builtinTypes->unknownType);
|
||||||
}
|
}
|
||||||
|
@ -336,15 +274,6 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TID>
|
|
||||||
static size_t getCount(const DenseHashMap<TID, size_t>& map, TID ty)
|
|
||||||
{
|
|
||||||
if (const size_t* count = map.find(ty))
|
|
||||||
return *count;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TableType&) override
|
bool visit(TypeId ty, const TableType&) override
|
||||||
{
|
{
|
||||||
if (cachedTypes->contains(ty))
|
if (cachedTypes->contains(ty))
|
||||||
|
@ -363,6 +292,7 @@ struct MutatingGeneralizer : TypeOnceVisitor
|
||||||
TableType* tt = getMutable<TableType>(ty);
|
TableType* tt = getMutable<TableType>(ty);
|
||||||
LUAU_ASSERT(tt);
|
LUAU_ASSERT(tt);
|
||||||
|
|
||||||
|
if (!avoidSealingTables)
|
||||||
tt->state = TableState::Sealed;
|
tt->state = TableState::Sealed;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -402,12 +332,28 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWithinFunction = false;
|
enum Polarity
|
||||||
Polarity polarity = Polarity::Positive;
|
{
|
||||||
|
Positive,
|
||||||
|
Negative,
|
||||||
|
Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
Polarity polarity = Positive;
|
||||||
|
|
||||||
void flip()
|
void flip()
|
||||||
{
|
{
|
||||||
polarity = invert(polarity);
|
switch (polarity)
|
||||||
|
{
|
||||||
|
case Positive:
|
||||||
|
polarity = Negative;
|
||||||
|
break;
|
||||||
|
case Negative:
|
||||||
|
polarity = Positive;
|
||||||
|
break;
|
||||||
|
case Both:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DenseHashSet<const void*> seenPositive{nullptr};
|
DenseHashSet<const void*> seenPositive{nullptr};
|
||||||
|
@ -417,7 +363,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
{
|
{
|
||||||
if (seenPositive.contains(ty))
|
if (seenPositive.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -425,7 +371,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenPositive.insert(ty);
|
seenPositive.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
{
|
{
|
||||||
if (seenNegative.contains(ty))
|
if (seenNegative.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -433,7 +379,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenNegative.insert(ty);
|
seenNegative.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
{
|
{
|
||||||
if (seenPositive.contains(ty) && seenNegative.contains(ty))
|
if (seenPositive.contains(ty) && seenNegative.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -442,21 +388,17 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenNegative.insert(ty);
|
seenNegative.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The keys in these maps are either TypeIds or TypePackIds. It's safe to
|
||||||
|
// mix them because we only use these pointers as unique keys. We never
|
||||||
|
// indirect them.
|
||||||
DenseHashMap<const void*, size_t> negativeTypes{0};
|
DenseHashMap<const void*, size_t> negativeTypes{0};
|
||||||
DenseHashMap<const void*, size_t> positiveTypes{0};
|
DenseHashMap<const void*, size_t> positiveTypes{0};
|
||||||
|
|
||||||
InsertionOrderedMap<TypeId, GeneralizationParams<TypeId>> types;
|
|
||||||
InsertionOrderedMap<TypePackId, GeneralizationParams<TypePackId>> typePacks;
|
|
||||||
|
|
||||||
OrderedSet<TypeId> unsealedTables;
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override
|
||||||
{
|
{
|
||||||
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
||||||
|
@ -467,24 +409,6 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
{
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
if (!subsumes(scope, ft.scope))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
GeneralizationParams<TypeId>& params = types[ty];
|
|
||||||
++params.useCount;
|
|
||||||
|
|
||||||
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!isWithinFunction)
|
|
||||||
params.foundOutsideFunctions = true;
|
|
||||||
|
|
||||||
params.polarity |= polarity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
||||||
return false;
|
return false;
|
||||||
|
@ -494,19 +418,16 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -518,26 +439,19 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
||||||
{
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
unsealedTables.insert(ty);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +464,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
|
|
||||||
Polarity p = polarity;
|
Polarity p = polarity;
|
||||||
polarity = Polarity::Mixed;
|
polarity = Both;
|
||||||
traverse(prop.type());
|
traverse(prop.type());
|
||||||
polarity = p;
|
polarity = p;
|
||||||
}
|
}
|
||||||
|
@ -558,27 +472,8 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if (tt.indexer)
|
if (tt.indexer)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
|
||||||
//
|
|
||||||
// (K) -> V
|
|
||||||
// (K, V) -> ()
|
|
||||||
// () -> {K}
|
|
||||||
//
|
|
||||||
// K and V therefore both have mixed polarity.
|
|
||||||
|
|
||||||
const Polarity p = polarity;
|
|
||||||
polarity = Polarity::Mixed;
|
|
||||||
traverse(tt.indexer->indexType);
|
traverse(tt.indexer->indexType);
|
||||||
traverse(tt.indexer->indexResultType);
|
traverse(tt.indexer->indexResultType);
|
||||||
polarity = p;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
traverse(tt.indexer->indexType);
|
|
||||||
traverse(tt.indexer->indexResultType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -589,17 +484,12 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
if (cachedTypes->contains(ty) || seenWithCurrentPolarity(ty))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const bool oldValue = isWithinFunction;
|
|
||||||
isWithinFunction = true;
|
|
||||||
|
|
||||||
flip();
|
flip();
|
||||||
traverse(ft.argTypes);
|
traverse(ft.argTypes);
|
||||||
flip();
|
flip();
|
||||||
|
|
||||||
traverse(ft.retTypes);
|
traverse(ft.retTypes);
|
||||||
|
|
||||||
isWithinFunction = oldValue;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,33 +506,18 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
if (!subsumes(scope, ftp.scope))
|
if (!subsumes(scope, ftp.scope))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
|
||||||
++params.useCount;
|
|
||||||
|
|
||||||
if (!isWithinFunction)
|
|
||||||
params.foundOutsideFunctions = true;
|
|
||||||
|
|
||||||
params.polarity |= polarity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[tp]++;
|
positiveTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[tp]++;
|
negativeTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[tp]++;
|
positiveTypes[tp]++;
|
||||||
negativeTypes[tp]++;
|
negativeTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1092,227 +967,13 @@ struct TypeCacher : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove occurrences of `needle` within `haystack`. This is used to cull cyclic bounds from free types.
|
|
||||||
*
|
|
||||||
* @param haystack Either the upper or lower bound of a free type.
|
|
||||||
* @param needle The type to be removed.
|
|
||||||
*/
|
|
||||||
[[nodiscard]]
|
|
||||||
static TypeId removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, DenseHashSet<TypeId>& seen, TypeId haystack, TypeId needle)
|
|
||||||
{
|
|
||||||
haystack = follow(haystack);
|
|
||||||
|
|
||||||
if (seen.find(haystack))
|
|
||||||
return haystack;
|
|
||||||
seen.insert(haystack);
|
|
||||||
|
|
||||||
if (const UnionType* ut = get<UnionType>(haystack))
|
|
||||||
{
|
|
||||||
OrderedSet<TypeId> newOptions;
|
|
||||||
|
|
||||||
for (TypeId option : ut)
|
|
||||||
{
|
|
||||||
if (option == needle)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (get<NeverType>(option))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LUAU_ASSERT(!get<UnionType>(option));
|
|
||||||
|
|
||||||
if (get<IntersectionType>(option))
|
|
||||||
newOptions.insert(removeType(arena, builtinTypes, seen, option, needle));
|
|
||||||
else
|
|
||||||
newOptions.insert(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newOptions.empty())
|
|
||||||
return builtinTypes->neverType;
|
|
||||||
else if (newOptions.size() == 1)
|
|
||||||
{
|
|
||||||
TypeId onlyType = *newOptions.begin();
|
|
||||||
LUAU_ASSERT(onlyType != haystack);
|
|
||||||
return onlyType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return arena->addType(UnionType{newOptions.takeVector()});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const IntersectionType* it = get<IntersectionType>(haystack))
|
|
||||||
{
|
|
||||||
OrderedSet<TypeId> newParts;
|
|
||||||
|
|
||||||
for (TypeId part : it)
|
|
||||||
{
|
|
||||||
part = follow(part);
|
|
||||||
|
|
||||||
if (part == needle)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (get<UnknownType>(part))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LUAU_ASSERT(!get<IntersectionType>(follow(part)));
|
|
||||||
|
|
||||||
if (get<UnionType>(part))
|
|
||||||
newParts.insert(removeType(arena, builtinTypes, seen, part, needle));
|
|
||||||
else
|
|
||||||
newParts.insert(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newParts.empty())
|
|
||||||
return builtinTypes->unknownType;
|
|
||||||
else if (newParts.size() == 1)
|
|
||||||
{
|
|
||||||
TypeId onlyType = *newParts.begin();
|
|
||||||
LUAU_ASSERT(onlyType != needle);
|
|
||||||
return onlyType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return arena->addType(IntersectionType{newParts.takeVector()});
|
|
||||||
}
|
|
||||||
|
|
||||||
return haystack;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TypeId> generalizeType(
|
|
||||||
NotNull<TypeArena> arena,
|
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
|
||||||
NotNull<Scope> scope,
|
|
||||||
TypeId freeTy,
|
|
||||||
const GeneralizationParams<TypeId>& params
|
|
||||||
)
|
|
||||||
{
|
|
||||||
freeTy = follow(freeTy);
|
|
||||||
|
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
|
||||||
LUAU_ASSERT(ft);
|
|
||||||
|
|
||||||
LUAU_ASSERT(isPositive(params.polarity) || isNegative(params.polarity));
|
|
||||||
|
|
||||||
const bool hasLowerBound = !get<NeverType>(follow(ft->lowerBound));
|
|
||||||
const bool hasUpperBound = !get<UnknownType>(follow(ft->upperBound));
|
|
||||||
|
|
||||||
const bool isWithinFunction = !params.foundOutsideFunctions;
|
|
||||||
|
|
||||||
if (!hasLowerBound && !hasUpperBound)
|
|
||||||
{
|
|
||||||
if ((params.polarity != Polarity::Mixed && params.useCount == 1) || !isWithinFunction)
|
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
|
||||||
return freeTy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// It is possible that this free type has other free types in its upper
|
|
||||||
// or lower bounds. If this is the case, we must replace those
|
|
||||||
// references with never (for the lower bound) or unknown (for the upper
|
|
||||||
// bound).
|
|
||||||
//
|
|
||||||
// If we do not do this, we get tautological bounds like a <: a <: unknown.
|
|
||||||
else if (isPositive(params.polarity) && !hasUpperBound)
|
|
||||||
{
|
|
||||||
TypeId lb = follow(ft->lowerBound);
|
|
||||||
if (FreeType* lowerFree = getMutable<FreeType>(lb); lowerFree && lowerFree->upperBound == freeTy)
|
|
||||||
lowerFree->upperBound = builtinTypes->unknownType;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DenseHashSet<TypeId> replaceSeen{nullptr};
|
|
||||||
lb = removeType(arena, builtinTypes, replaceSeen, lb, freeTy);
|
|
||||||
ft->lowerBound = lb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (follow(lb) != freeTy)
|
|
||||||
emplaceType<BoundType>(asMutable(freeTy), lb);
|
|
||||||
else if (!isWithinFunction || params.useCount == 1)
|
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if the lower bound is the type in question (eg 'a <: 'a), we don't actually have a lower bound.
|
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
|
||||||
return freeTy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TypeId ub = follow(ft->upperBound);
|
|
||||||
if (FreeType* upperFree = getMutable<FreeType>(ub); upperFree && upperFree->lowerBound == freeTy)
|
|
||||||
upperFree->lowerBound = builtinTypes->neverType;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the free type appears within its own upper bound, cull that cycle.
|
|
||||||
DenseHashSet<TypeId> replaceSeen{nullptr};
|
|
||||||
ub = removeType(arena, builtinTypes, replaceSeen, ub, freeTy);
|
|
||||||
ft->upperBound = ub;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (follow(ub) != freeTy)
|
|
||||||
emplaceType<BoundType>(asMutable(freeTy), ub);
|
|
||||||
else if (!isWithinFunction || params.useCount == 1)
|
|
||||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if the upper bound is the type in question, we don't actually have an upper bound.
|
|
||||||
emplaceType<GenericType>(asMutable(freeTy), scope, params.polarity);
|
|
||||||
return freeTy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TypePackId> generalizeTypePack(
|
|
||||||
NotNull<TypeArena> arena,
|
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
|
||||||
NotNull<Scope> scope,
|
|
||||||
TypePackId tp,
|
|
||||||
const GeneralizationParams<TypePackId>& params
|
|
||||||
)
|
|
||||||
{
|
|
||||||
tp = follow(tp);
|
|
||||||
|
|
||||||
if (tp->owningArena != arena)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const FreeTypePack* ftp = get<FreeTypePack>(tp);
|
|
||||||
if (!ftp)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
if (!subsumes(scope, ftp->scope))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
if (1 == params.useCount)
|
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(tp), builtinTypes->unknownTypePack);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emplaceTypePack<GenericTypePack>(asMutable(tp), scope, params.polarity);
|
|
||||||
return tp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sealTable(NotNull<Scope> scope, TypeId ty)
|
|
||||||
{
|
|
||||||
TableType* tableTy = getMutable<TableType>(follow(ty));
|
|
||||||
if (!tableTy)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!subsumes(scope, tableTy->scope))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tableTy->state == TableState::Unsealed || tableTy->state == TableState::Free)
|
|
||||||
tableTy->state = TableState::Sealed;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TypeId> generalize(
|
std::optional<TypeId> generalize(
|
||||||
NotNull<TypeArena> arena,
|
NotNull<TypeArena> arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
NotNull<Scope> scope,
|
NotNull<Scope> scope,
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
NotNull<DenseHashSet<TypeId>> cachedTypes,
|
||||||
TypeId ty
|
TypeId ty,
|
||||||
|
bool avoidSealingTables
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -1323,45 +984,7 @@ std::optional<TypeId> generalize(
|
||||||
FreeTypeSearcher fts{scope, cachedTypes};
|
FreeTypeSearcher fts{scope, cachedTypes};
|
||||||
fts.traverse(ty);
|
fts.traverse(ty);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
MutatingGeneralizer gen{arena, builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes), avoidSealingTables};
|
||||||
{
|
|
||||||
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
|
||||||
auto pushGeneric = [&](TypeId t)
|
|
||||||
{
|
|
||||||
if (functionTy)
|
|
||||||
functionTy->generics.push_back(t);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto pushGenericPack = [&](TypePackId tp)
|
|
||||||
{
|
|
||||||
if (functionTy)
|
|
||||||
functionTy->genericPacks.push_back(tp);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& [freeTy, params] : fts.types)
|
|
||||||
{
|
|
||||||
if (std::optional<TypeId> genericTy = generalizeType(arena, builtinTypes, scope, freeTy, params))
|
|
||||||
pushGeneric(*genericTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TypeId unsealedTableTy : fts.unsealedTables)
|
|
||||||
sealTable(scope, unsealedTableTy);
|
|
||||||
|
|
||||||
for (const auto& [freePackId, params] : fts.typePacks)
|
|
||||||
{
|
|
||||||
TypePackId freePack = follow(freePackId);
|
|
||||||
std::optional<TypePackId> generalizedTp = generalizeTypePack(arena, builtinTypes, scope, freePack, params);
|
|
||||||
|
|
||||||
if (generalizedTp)
|
|
||||||
pushGenericPack(freePack);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeCacher cacher{cachedTypes};
|
|
||||||
cacher.traverse(ty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MutatingGeneralizer gen{arena, builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes)};
|
|
||||||
|
|
||||||
gen.traverse(ty);
|
gen.traverse(ty);
|
||||||
|
|
||||||
|
@ -1392,7 +1015,6 @@ std::optional<TypeId> generalize(
|
||||||
ftv->genericPacks.push_back(gp);
|
ftv->genericPacks.push_back(gp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(LuauNonReentrantGeneralization)
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const auto gen = get<GenericType>(generic);
|
|
||||||
LUAU_ASSERT(gen);
|
|
||||||
if (subsumes(scope, gen->scope))
|
|
||||||
types[generic] = Polarity::None;
|
|
||||||
}
|
|
||||||
for (TypePackId genericPack : ft.genericPacks)
|
|
||||||
{
|
|
||||||
const auto gen = get<GenericTypePack>(genericPack);
|
|
||||||
LUAU_ASSERT(gen);
|
|
||||||
if (subsumes(scope, gen->scope))
|
|
||||||
packs[genericPack] = Polarity::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
flip();
|
|
||||||
traverse(ft.argTypes);
|
|
||||||
flip();
|
|
||||||
traverse(ft.retTypes);
|
|
||||||
|
|
||||||
polarity = p;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId, const ClassType&) 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::LuauNonReentrantGeneralization)
|
|
||||||
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
|
|
|
@ -61,7 +61,7 @@ TypeId Instantiation::clean(TypeId ty)
|
||||||
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
|
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
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.magic = ftv->magic;
|
||||||
clone.tags = ftv->tags;
|
clone.tags = ftv->tags;
|
||||||
clone.argNames = ftv->argNames;
|
clone.argNames = ftv->argNames;
|
||||||
|
|
|
@ -19,8 +19,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAttribute)
|
LUAU_FASTFLAG(LuauAttribute)
|
||||||
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -2282,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
|
bool visit(AstExprIndexName* node) override
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> ty = context->getType(node->expr))
|
if (std::optional<TypeId> ty = context->getType(node->expr))
|
||||||
|
@ -2378,33 +2325,12 @@ private:
|
||||||
|
|
||||||
if (prop && prop->deprecated)
|
if (prop && prop->deprecated)
|
||||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||||
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))
|
else if (const TableType* tty = get<TableType>(ty))
|
||||||
{
|
{
|
||||||
auto prop = tty->props.find(node->index.value);
|
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
|
// strip synthetic typeof() for builtin tables
|
||||||
if (tty->name && tty->name->compare(0, 7, "typeof(") == 0 && tty->name->back() == ')')
|
if (tty->name && tty->name->compare(0, 7, "typeof(") == 0 && tty->name->back() == ')')
|
||||||
|
@ -2412,26 +2338,6 @@ private:
|
||||||
else
|
else
|
||||||
report(node->location, prop->second, tty->name ? tty->name->c_str() : nullptr, node->index.value);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2449,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)
|
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());
|
std::string suggestion = prop.deprecatedSuggestion.empty() ? "" : format(", use '%s' instead", prop.deprecatedSuggestion.c_str());
|
||||||
|
@ -2478,63 +2364,6 @@ private:
|
||||||
else
|
else
|
||||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s' is deprecated%s", field, suggestion.c_str());
|
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
|
class LintTableOperations : AstVisitor
|
||||||
|
|
|
@ -15,12 +15,12 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
|
||||||
namespace Luau
|
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 default is to do nothing because we don't want to mess with
|
||||||
// the xml parsing done by the dcr script.
|
// the xml parsing done by the dcr script.
|
||||||
|
@ -38,6 +38,21 @@ void resetLogLuauProc()
|
||||||
logLuau = &defaultLogLuau;
|
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)
|
static bool contains(Position pos, Comment comment)
|
||||||
{
|
{
|
||||||
if (comment.location.contains(pos))
|
if (comment.location.contains(pos))
|
||||||
|
@ -60,9 +75,12 @@ bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
||||||
commentLocations.end(),
|
commentLocations.end(),
|
||||||
Comment{Lexeme::Comment, Location{pos, pos}},
|
Comment{Lexeme::Comment, Location{pos, pos}},
|
||||||
[](const Comment& a, const Comment& b)
|
[](const Comment& a, const Comment& b)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauIncrementalAutocompleteCommentDetection)
|
||||||
{
|
{
|
||||||
if (a.type == Lexeme::Comment)
|
if (a.type == Lexeme::Comment)
|
||||||
return a.location.end.line < b.location.end.line;
|
return a.location.end.line < b.location.end.line;
|
||||||
|
}
|
||||||
return a.location.end < b.location.end;
|
return a.location.end < b.location.end;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -70,7 +88,7 @@ bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
||||||
if (iter == commentLocations.end())
|
if (iter == commentLocations.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (contains(pos, *iter))
|
if (FFlag::LuauIncrementalAutocompleteCommentDetection ? contains(pos, *iter) : contains_DEPRECATED(pos, *iter))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Due to the nature of std::lower_bound, it is possible that iter points at a comment that ends
|
// 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};
|
ftv->level = TypeLevel{0, 0};
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
ftv->scope = nullptr;
|
||||||
}
|
}
|
||||||
else if (TableType* ttv = getMutable<TableType>(result))
|
else if (TableType* ttv = getMutable<TableType>(result))
|
||||||
{
|
{
|
||||||
|
@ -265,9 +285,6 @@ struct ClonePublicInterface : Substitution
|
||||||
|
|
||||||
TypeId type = cloneType(tf.type);
|
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/NonStrictTypeChecker.h"
|
||||||
|
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/AstQuery.h"
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Simplify.h"
|
#include "Luau/Simplify.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
|
|
|
@ -17,16 +17,12 @@
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeNegatedErrorToAnError)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeIntersectErrorToAnError)
|
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
||||||
LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100)
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNormalizeNegationFix)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixInfiniteRecursionInNormalization)
|
LUAU_FASTFLAGVARIABLE(LuauFixInfiniteRecursionInNormalization)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizedBufferIsNotUnknown)
|
LUAU_FASTFLAGVARIABLE(LuauNormalizedBufferIsNotUnknown)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeLimitFunctionSet)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationCatchMetatableCycles)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -585,7 +581,7 @@ NormalizationResult Normalizer::isIntersectionInhabited(TypeId left, TypeId righ
|
||||||
{
|
{
|
||||||
left = follow(left);
|
left = follow(left);
|
||||||
right = follow(right);
|
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)
|
if (cacheInhabitance)
|
||||||
{
|
{
|
||||||
|
@ -1691,13 +1687,6 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNormalizeLimitFunctionSet)
|
|
||||||
{
|
|
||||||
// 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);
|
here.booleans = unionOfBools(here.booleans, there.booleans);
|
||||||
unionClasses(here.classes, there.classes);
|
unionClasses(here.classes, there.classes);
|
||||||
|
|
||||||
|
@ -1709,7 +1698,6 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali
|
||||||
here.buffers = (get<NeverType>(there.buffers) ? here.buffers : there.buffers);
|
here.buffers = (get<NeverType>(there.buffers) ? here.buffers : there.buffers);
|
||||||
unionFunctions(here.functions, there.functions);
|
unionFunctions(here.functions, there.functions);
|
||||||
unionTables(here.tables, there.tables);
|
unionTables(here.tables, there.tables);
|
||||||
|
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1749,7 +1737,7 @@ NormalizationResult Normalizer::intersectNormalWithNegationTy(TypeId toNegate, N
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See above for an explanation of `ignoreSmallerTyvars`.
|
// See above for an explaination of `ignoreSmallerTyvars`.
|
||||||
NormalizationResult Normalizer::unionNormalWithTy(
|
NormalizationResult Normalizer::unionNormalWithTy(
|
||||||
NormalizedType& here,
|
NormalizedType& here,
|
||||||
TypeId there,
|
TypeId there,
|
||||||
|
@ -3064,7 +3052,7 @@ NormalizationResult Normalizer::intersectTyvarsWithTy(
|
||||||
return NormalizationResult::True;
|
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)
|
NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
|
||||||
{
|
{
|
||||||
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
||||||
|
@ -3082,17 +3070,11 @@ NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const Nor
|
||||||
return unionNormals(here, there, ignoreSmallerTyvars);
|
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
|
// This restriction can be relaxed when table intersection simplification is improved
|
||||||
if (here.tables.size() * there.tables.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
if (here.tables.size() * there.tables.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
||||||
return NormalizationResult::HitLimits;
|
return NormalizationResult::HitLimits;
|
||||||
|
|
||||||
if (FFlag::LuauNormalizeLimitFunctionSet)
|
|
||||||
{
|
|
||||||
if (here.functions.parts.size() * there.functions.parts.size() >= size_t(FInt::LuauNormalizeIntersectionLimit))
|
|
||||||
return NormalizationResult::HitLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
||||||
|
|
||||||
intersectClasses(here.classes, there.classes);
|
intersectClasses(here.classes, there.classes);
|
||||||
|
@ -3228,7 +3210,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
{
|
{
|
||||||
TypeId errors = here.errors;
|
TypeId errors = here.errors;
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
here.errors = FFlag::LuauNormalizeIntersectErrorToAnError && get<ErrorType>(errors) ? errors : there;
|
here.errors = errors;
|
||||||
}
|
}
|
||||||
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
||||||
{
|
{
|
||||||
|
@ -3325,16 +3307,11 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
clearNormal(here);
|
clearNormal(here);
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauNormalizeNegatedErrorToAnError && 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 = FFlag::LuauNormalizeIntersectErrorToAnError && get<ErrorType>(errors) ? errors : t;
|
|
||||||
}
|
|
||||||
else if (auto nt = get<NegationType>(t))
|
else if (auto nt = get<NegationType>(t))
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauNormalizeNegationFix)
|
||||||
here.tyvars = std::move(tyvars);
|
here.tyvars = std::move(tyvars);
|
||||||
|
|
||||||
return intersectNormalWithTy(here, nt->ty, seenTablePropPairs, seenSetTypes);
|
return intersectNormalWithTy(here, nt->ty, seenTablePropPairs, seenSetTypes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3364,43 +3341,19 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
return NormalizationResult::True;
|
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)
|
void makeTableShared(TypeId ty)
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> seen{nullptr};
|
ty = follow(ty);
|
||||||
makeTableShared(ty, seen);
|
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
|
// -------- Convert back from a normalized type to a type
|
||||||
|
@ -3502,10 +3455,7 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
||||||
result.reserve(result.size() + norm.tables.size());
|
result.reserve(result.size() + norm.tables.size());
|
||||||
for (auto table : norm.tables)
|
for (auto table : norm.tables)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNormalizationCatchMetatableCycles)
|
|
||||||
makeTableShared(table);
|
makeTableShared(table);
|
||||||
else
|
|
||||||
makeTableShared_DEPRECATED(table);
|
|
||||||
result.push_back(table);
|
result.push_back(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,7 +454,7 @@ SolveResult solveFunctionCall(
|
||||||
|
|
||||||
TypePackId resultPack = arena->freshTypePack(scope);
|
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};
|
Unifier2 u2{NotNull{arena}, builtinTypes, scope, iceReporter};
|
||||||
|
|
||||||
const bool occursCheckPassed = u2.unify(*overloadToUse, inferredTy);
|
const bool occursCheckPassed = u2.unify(*overloadToUse, inferredTy);
|
||||||
|
|
|
@ -13,7 +13,6 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
||||||
LUAU_FASTFLAG(LuauSyntheticErrors)
|
LUAU_FASTFLAG(LuauSyntheticErrors)
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -96,15 +95,13 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
||||||
return dest.addType(a);
|
return dest.addType(a);
|
||||||
else if constexpr (std::is_same_v<T, FunctionType>)
|
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.generics = a.generics;
|
||||||
clone.genericPacks = a.genericPacks;
|
clone.genericPacks = a.genericPacks;
|
||||||
clone.magic = a.magic;
|
clone.magic = a.magic;
|
||||||
clone.tags = a.tags;
|
clone.tags = a.tags;
|
||||||
clone.argNames = a.argNames;
|
clone.argNames = a.argNames;
|
||||||
clone.isCheckedFunction = a.isCheckedFunction;
|
clone.isCheckedFunction = a.isCheckedFunction;
|
||||||
if (FFlag::LuauDeprecatedAttribute)
|
|
||||||
clone.isDeprecatedFunction = a.isDeprecatedFunction;
|
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, TableType>)
|
else if constexpr (std::is_same_v<T, TableType>)
|
||||||
|
|
|
@ -22,9 +22,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingStopAtNormFail)
|
|
||||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -102,9 +99,6 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
||||||
else
|
else
|
||||||
result.insert(r);
|
result.insert(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const SubtypingReasoning& r : b)
|
for (const SubtypingReasoning& r : b)
|
||||||
|
@ -121,9 +115,6 @@ static SubtypingReasonings mergeReasonings(const SubtypingReasonings& a, const S
|
||||||
else
|
else
|
||||||
result.insert(r);
|
result.insert(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingEnableReasoningLimit && result.size() >= size_t(FInt::LuauSubtypingReasoningLimit))
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -424,14 +415,6 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope
|
||||||
|
|
||||||
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && result.normalizationTooComplex)
|
|
||||||
{
|
|
||||||
if (result.isCacheable)
|
|
||||||
resultCache[{subTy, superTy}] = result;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& [subTy, bounds] : env.mappedGenerics)
|
for (const auto& [subTy, bounds] : env.mappedGenerics)
|
||||||
{
|
{
|
||||||
const auto& lb = bounds.lowerBound;
|
const auto& lb = bounds.lowerBound;
|
||||||
|
@ -609,12 +592,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||||
{
|
{
|
||||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||||
|
if (semantic.isSubtype)
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && semantic.normalizationTooComplex)
|
|
||||||
{
|
|
||||||
result = semantic;
|
|
||||||
}
|
|
||||||
else if (semantic.isSubtype)
|
|
||||||
{
|
{
|
||||||
semantic.reasoning.clear();
|
semantic.reasoning.clear();
|
||||||
result = semantic;
|
result = semantic;
|
||||||
|
@ -629,12 +607,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||||
{
|
{
|
||||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||||
|
if (semantic.isSubtype)
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && semantic.normalizationTooComplex)
|
|
||||||
{
|
|
||||||
result = semantic;
|
|
||||||
}
|
|
||||||
else if (semantic.isSubtype)
|
|
||||||
{
|
{
|
||||||
// Clear the semantic reasoning, as any reasonings within
|
// Clear the semantic reasoning, as any reasonings within
|
||||||
// potentially contain invalid paths.
|
// potentially contain invalid paths.
|
||||||
|
@ -1109,10 +1082,6 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
for (TypeId ty : superUnion)
|
for (TypeId ty : superUnion)
|
||||||
{
|
{
|
||||||
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && next.normalizationTooComplex)
|
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
|
||||||
|
|
||||||
if (next.isSubtype)
|
if (next.isSubtype)
|
||||||
return SubtypingResult{true};
|
return SubtypingResult{true};
|
||||||
}
|
}
|
||||||
|
@ -1131,13 +1100,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Unio
|
||||||
std::vector<SubtypingResult> subtypings;
|
std::vector<SubtypingResult> subtypings;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (TypeId ty : subUnion)
|
for (TypeId ty : subUnion)
|
||||||
{
|
|
||||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Union}));
|
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Union}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
|
||||||
}
|
|
||||||
|
|
||||||
return SubtypingResult::all(subtypings);
|
return SubtypingResult::all(subtypings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,13 +1110,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
std::vector<SubtypingResult> subtypings;
|
std::vector<SubtypingResult> subtypings;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (TypeId ty : superIntersection)
|
for (TypeId ty : superIntersection)
|
||||||
{
|
|
||||||
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
|
||||||
}
|
|
||||||
|
|
||||||
return SubtypingResult::all(subtypings);
|
return SubtypingResult::all(subtypings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,13 +1120,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Inte
|
||||||
std::vector<SubtypingResult> subtypings;
|
std::vector<SubtypingResult> subtypings;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (TypeId ty : subIntersection)
|
for (TypeId ty : subIntersection)
|
||||||
{
|
|
||||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++, TypePath::Index::Variant::Intersection}));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && subtypings.back().normalizationTooComplex)
|
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
|
||||||
}
|
|
||||||
|
|
||||||
return SubtypingResult::any(subtypings);
|
return SubtypingResult::any(subtypings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1459,7 +1410,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
||||||
// of the supertype table.
|
// of the supertype table.
|
||||||
//
|
//
|
||||||
// There's a flaw here in that if the __index metamethod contributes a new
|
// 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
|
// 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
|
// compatible properties/shapes. We'll revisit this later when we have a
|
||||||
// better understanding of how important this is.
|
// better understanding of how important this is.
|
||||||
|
@ -1809,12 +1760,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||||
{
|
{
|
||||||
results.emplace_back();
|
results.emplace_back();
|
||||||
for (TypeId superTy : superTypes)
|
for (TypeId superTy : superTypes)
|
||||||
{
|
|
||||||
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
||||||
|
|
||||||
if (FFlag::LuauSubtypingStopAtNormFail && results.back().normalizationTooComplex)
|
|
||||||
return SubtypingResult{false, /* normalizationTooComplex */ true};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return SubtypingResult::all(results);
|
return SubtypingResult::all(results);
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceUpcast)
|
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceUpcast)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalFailsafe)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -139,13 +136,14 @@ TypeId matchLiteralType(
|
||||||
* things like replace explicit named properties with indexers as required
|
* things like replace explicit named properties with indexers as required
|
||||||
* by the expected type.
|
* by the expected type.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!isLiteral(expr))
|
if (!isLiteral(expr))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauBidirectionalInferenceUpcast)
|
if (FFlag::LuauBidirectionalInferenceUpcast)
|
||||||
{
|
{
|
||||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
||||||
return result.isSubtype ? expectedType : exprType;
|
return result.isSubtype
|
||||||
|
? expectedType
|
||||||
|
: exprType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return exprType;
|
return exprType;
|
||||||
|
@ -154,23 +152,11 @@ TypeId matchLiteralType(
|
||||||
expectedType = follow(expectedType);
|
expectedType = follow(expectedType);
|
||||||
exprType = follow(exprType);
|
exprType = follow(exprType);
|
||||||
|
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (get<AnyType>(expectedType) || get<UnknownType>(expectedType))
|
if (get<AnyType>(expectedType) || get<UnknownType>(expectedType))
|
||||||
{
|
{
|
||||||
// "Narrowing" to unknown or any is not going to do anything useful.
|
// "Narrowing" to unknown or any is not going to do anything useful.
|
||||||
return exprType;
|
return exprType;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (expr->is<AstExprConstantString>())
|
if (expr->is<AstExprConstantString>())
|
||||||
{
|
{
|
||||||
|
@ -244,21 +230,14 @@ TypeId matchLiteralType(
|
||||||
// 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.
|
// the non-literal thing: check for a subtype and upcast if valid.
|
||||||
auto result = subtyping->isSubtype(/*subTy=*/exprType, /*superTy=*/expectedType, unifier->scope);
|
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>())
|
if (auto exprTable = expr->as<AstExprTable>())
|
||||||
{
|
{
|
||||||
TableType* const tableTy = getMutable<TableType>(exprType);
|
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);
|
LUAU_ASSERT(tableTy);
|
||||||
|
|
||||||
const TableType* expectedTableTy = get<TableType>(expectedType);
|
const TableType* expectedTableTy = get<TableType>(expectedType);
|
||||||
|
@ -285,9 +264,6 @@ TypeId matchLiteralType(
|
||||||
|
|
||||||
DenseHashSet<AstExprConstantString*> keysToDelete{nullptr};
|
DenseHashSet<AstExprConstantString*> keysToDelete{nullptr};
|
||||||
|
|
||||||
DenseHashSet<TypeId> indexerKeyTypes{nullptr};
|
|
||||||
DenseHashSet<TypeId> indexerValueTypes{nullptr};
|
|
||||||
|
|
||||||
for (const AstExprTable::Item& item : exprTable->items)
|
for (const AstExprTable::Item& item : exprTable->items)
|
||||||
{
|
{
|
||||||
if (isRecord(item))
|
if (isRecord(item))
|
||||||
|
@ -295,11 +271,6 @@ TypeId matchLiteralType(
|
||||||
const AstArray<char>& s = item.key->as<AstExprConstantString>()->value;
|
const AstArray<char>& s = item.key->as<AstExprConstantString>()->value;
|
||||||
std::string keyStr{s.data, s.data + s.size};
|
std::string keyStr{s.data, s.data + s.size};
|
||||||
auto it = tableTy->props.find(keyStr);
|
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());
|
LUAU_ASSERT(it != tableTy->props.end());
|
||||||
|
|
||||||
Property& prop = it->second;
|
Property& prop = it->second;
|
||||||
|
@ -336,20 +307,13 @@ TypeId matchLiteralType(
|
||||||
toBlock
|
toBlock
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
{
|
|
||||||
indexerKeyTypes.insert(arena->addType(SingletonType{StringSingleton{keyStr}}));
|
|
||||||
indexerValueTypes.insert(matchedType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tableTy->indexer)
|
if (tableTy->indexer)
|
||||||
unifier->unify(matchedType, tableTy->indexer->indexResultType);
|
unifier->unify(matchedType, tableTy->indexer->indexResultType);
|
||||||
else
|
else
|
||||||
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
||||||
}
|
|
||||||
|
|
||||||
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's just an extra property and the expected type
|
// If it's just an extra property and the expected type
|
||||||
|
@ -372,25 +336,22 @@ TypeId matchLiteralType(
|
||||||
// quadratic in a hurry.
|
// quadratic in a hurry.
|
||||||
if (expectedProp.isShared())
|
if (expectedProp.isShared())
|
||||||
{
|
{
|
||||||
matchedType = matchLiteralType(
|
matchedType =
|
||||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock
|
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock);
|
||||||
);
|
|
||||||
prop.readTy = matchedType;
|
prop.readTy = matchedType;
|
||||||
prop.writeTy = matchedType;
|
prop.writeTy = matchedType;
|
||||||
}
|
}
|
||||||
else if (expectedReadTy)
|
else if (expectedReadTy)
|
||||||
{
|
{
|
||||||
matchedType = matchLiteralType(
|
matchedType =
|
||||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock
|
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedReadTy, propTy, item.value, toBlock);
|
||||||
);
|
|
||||||
prop.readTy = matchedType;
|
prop.readTy = matchedType;
|
||||||
prop.writeTy.reset();
|
prop.writeTy.reset();
|
||||||
}
|
}
|
||||||
else if (expectedWriteTy)
|
else if (expectedWriteTy)
|
||||||
{
|
{
|
||||||
matchedType = matchLiteralType(
|
matchedType =
|
||||||
astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedWriteTy, propTy, item.value, toBlock
|
matchLiteralType(astTypes, astExpectedTypes, builtinTypes, arena, unifier, subtyping, *expectedWriteTy, propTy, item.value, toBlock);
|
||||||
);
|
|
||||||
prop.readTy.reset();
|
prop.readTy.reset();
|
||||||
prop.writeTy = matchedType;
|
prop.writeTy = matchedType;
|
||||||
}
|
}
|
||||||
|
@ -407,15 +368,9 @@ TypeId matchLiteralType(
|
||||||
LUAU_ASSERT(matchedType);
|
LUAU_ASSERT(matchedType);
|
||||||
|
|
||||||
(*astExpectedTypes)[item.value] = 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)
|
else if (item.kind == AstExprTable::Item::List)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauBidirectionalInferenceCollectIndexerTypes || !FFlag::LuauBidirectionalInferenceElideAssert)
|
|
||||||
LUAU_ASSERT(tableTy->indexer);
|
LUAU_ASSERT(tableTy->indexer);
|
||||||
|
|
||||||
if (expectedTableTy->indexer)
|
if (expectedTableTy->indexer)
|
||||||
|
@ -437,19 +392,11 @@ TypeId matchLiteralType(
|
||||||
toBlock
|
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 the index result type is the prop type, we can replace it with the matched type here.
|
||||||
if (tableTy->indexer->indexResultType == *propTy)
|
if (tableTy->indexer->indexResultType == *propTy)
|
||||||
tableTy->indexer->indexResultType = matchedType;
|
tableTy->indexer->indexResultType = matchedType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (item.kind == AstExprTable::Item::General)
|
else if (item.kind == AstExprTable::Item::General)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -469,12 +416,6 @@ TypeId matchLiteralType(
|
||||||
// Populate expected types for non-string keys declared with [] (the code below will handle the case where they are strings)
|
// 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)
|
if (!item.key->as<AstExprConstantString>() && expectedTableTy->indexer)
|
||||||
(*astExpectedTypes)[item.key] = expectedTableTy->indexer->indexType;
|
(*astExpectedTypes)[item.key] = expectedTableTy->indexer->indexType;
|
||||||
|
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
|
||||||
{
|
|
||||||
indexerKeyTypes.insert(tKey);
|
|
||||||
indexerValueTypes.insert(tProp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(!"Unexpected");
|
LUAU_ASSERT(!"Unexpected");
|
||||||
|
@ -536,41 +477,11 @@ TypeId matchLiteralType(
|
||||||
// have one too.
|
// have one too.
|
||||||
// TODO: If the expected table also has an indexer, we might want to
|
// TODO: If the expected table also has an indexer, we might want to
|
||||||
// push the expected indexer's types into it.
|
// push the expected indexer's types into it.
|
||||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes && expectedTableTy->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)
|
if (expectedTableTy->indexer && !tableTy->indexer)
|
||||||
{
|
{
|
||||||
tableTy->indexer = expectedTableTy->indexer;
|
tableTy->indexer = expectedTableTy->indexer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return exprType;
|
return exprType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,28 +301,6 @@ struct StringifierState
|
||||||
emit(std::to_string(i).c_str());
|
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()
|
void indent()
|
||||||
{
|
{
|
||||||
indentation += 4;
|
indentation += 4;
|
||||||
|
@ -504,8 +482,6 @@ struct TypeStringifier
|
||||||
{
|
{
|
||||||
state.emit("'");
|
state.emit("'");
|
||||||
state.emit(state.getName(ty));
|
state.emit(state.getName(ty));
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(ftv.polarity);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -518,9 +494,6 @@ struct TypeStringifier
|
||||||
state.emit("'");
|
state.emit("'");
|
||||||
state.emit(state.getName(ty));
|
state.emit(state.getName(ty));
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(ftv.polarity);
|
|
||||||
|
|
||||||
if (!get<UnknownType>(upperBound))
|
if (!get<UnknownType>(upperBound))
|
||||||
{
|
{
|
||||||
state.emit(" <: ");
|
state.emit(" <: ");
|
||||||
|
@ -536,9 +509,6 @@ struct TypeStringifier
|
||||||
|
|
||||||
state.emit(state.getName(ty));
|
state.emit(state.getName(ty));
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(ftv.polarity);
|
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||||
{
|
{
|
||||||
state.emit("-");
|
state.emit("-");
|
||||||
|
@ -568,9 +538,6 @@ struct TypeStringifier
|
||||||
else
|
else
|
||||||
state.emit(state.getName(ty));
|
state.emit(state.getName(ty));
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(gtv.polarity);
|
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||||
{
|
{
|
||||||
state.emit("-");
|
state.emit("-");
|
||||||
|
@ -1255,9 +1222,6 @@ struct TypePackStringifier
|
||||||
state.emit(state.getName(tp));
|
state.emit(state.getName(tp));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(pack.polarity);
|
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||||
{
|
{
|
||||||
state.emit("-");
|
state.emit("-");
|
||||||
|
@ -1277,9 +1241,6 @@ struct TypePackStringifier
|
||||||
state.emit("free-");
|
state.emit("free-");
|
||||||
state.emit(state.getName(tp));
|
state.emit(state.getName(tp));
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 1)
|
|
||||||
state.emit(pack.polarity);
|
|
||||||
|
|
||||||
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
if (FInt::DebugLuauVerboseTypeNames >= 2)
|
||||||
{
|
{
|
||||||
state.emit("-");
|
state.emit("-");
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
|
LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
||||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAG(LuauParseOptionalAsNode)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -167,7 +167,7 @@ struct StringWriter : Writer
|
||||||
|
|
||||||
void symbol(std::string_view s) override
|
void symbol(std::string_view s) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData)
|
||||||
{
|
{
|
||||||
write(s);
|
write(s);
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ public:
|
||||||
first = !first;
|
first = !first;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStoreCSTData2 && commaPosition)
|
if (FFlag::LuauStoreCSTData && commaPosition)
|
||||||
{
|
{
|
||||||
writer.advance(*commaPosition);
|
writer.advance(*commaPosition);
|
||||||
commaPosition++;
|
commaPosition++;
|
||||||
|
@ -1229,18 +1229,9 @@ struct Printer_DEPRECATED
|
||||||
AstType* l = a->types.data[0];
|
AstType* l = a->types.data[0];
|
||||||
AstType* r = a->types.data[1];
|
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>();
|
auto lta = l->as<AstTypeReference>();
|
||||||
if (lta && lta->name == "nil")
|
if (lta && lta->name == "nil")
|
||||||
std::swap(l, r);
|
std::swap(l, r);
|
||||||
}
|
|
||||||
|
|
||||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||||
auto rta = r->as<AstTypeReference>();
|
auto rta = r->as<AstTypeReference>();
|
||||||
|
@ -1263,7 +1254,7 @@ struct Printer_DEPRECATED
|
||||||
|
|
||||||
for (size_t i = 0; i < a->types.size; ++i)
|
for (size_t i = 0; i < a->types.size; ++i)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauParseOptionalAsNode2)
|
if (FFlag::LuauParseOptionalAsNode)
|
||||||
{
|
{
|
||||||
if (a->types.data[i]->is<AstTypeOptional>())
|
if (a->types.data[i]->is<AstTypeOptional>())
|
||||||
{
|
{
|
||||||
|
@ -1498,7 +1489,6 @@ struct Printer
|
||||||
|
|
||||||
void visualize(AstExpr& expr)
|
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>())
|
if (const auto& a = expr.as<AstExprGroup>())
|
||||||
|
@ -1633,17 +1623,6 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const auto& a = expr.as<AstExprFunction>())
|
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");
|
writer.keyword("function");
|
||||||
visualizeFunctionBody(*a);
|
visualizeFunctionBody(*a);
|
||||||
}
|
}
|
||||||
|
@ -1895,7 +1874,6 @@ struct Printer
|
||||||
|
|
||||||
void visualize(AstStat& program)
|
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>())
|
if (const auto& block = program.as<AstStatBlock>())
|
||||||
|
@ -2133,36 +2111,13 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const auto& a = program.as<AstStatFunction>())
|
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");
|
writer.keyword("function");
|
||||||
visualize(*a->name);
|
visualize(*a->name);
|
||||||
visualizeFunctionBody(*a->func);
|
visualizeFunctionBody(*a->func);
|
||||||
}
|
}
|
||||||
else if (const auto& a = program.as<AstStatLocalFunction>())
|
else if (const auto& a = program.as<AstStatLocalFunction>())
|
||||||
{
|
{
|
||||||
for (const auto& attribute : a->func->attributes)
|
|
||||||
visualizeAttribute(*attribute);
|
|
||||||
|
|
||||||
const auto cstNode = lookupCstNode<CstStatLocalFunction>(a);
|
const auto cstNode = lookupCstNode<CstStatLocalFunction>(a);
|
||||||
if (FFlag::LuauFixFunctionWithAttributesStartLocation)
|
|
||||||
{
|
|
||||||
if (cstNode)
|
|
||||||
advance(cstNode->localKeywordPosition);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
advance(a->location.begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.keyword("local");
|
writer.keyword("local");
|
||||||
|
|
||||||
|
@ -2306,7 +2261,7 @@ struct Printer
|
||||||
|
|
||||||
if (program.hasSemicolon)
|
if (program.hasSemicolon)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData)
|
||||||
advanceBefore(program.location.end, 1);
|
advanceBefore(program.location.end, 1);
|
||||||
writer.symbol(";");
|
writer.symbol(";");
|
||||||
}
|
}
|
||||||
|
@ -2316,7 +2271,7 @@ struct Printer
|
||||||
{
|
{
|
||||||
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
||||||
|
|
||||||
// TODO(CLI-139347): need to handle return type (incl. parentheses of return type)
|
// TODO(CLI-139347): need to handle attributes, argument types, and return type (incl. parentheses of return type)
|
||||||
|
|
||||||
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
||||||
{
|
{
|
||||||
|
@ -2472,23 +2427,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)
|
void visualizeTypeAnnotation(AstType& typeAnnotation)
|
||||||
{
|
{
|
||||||
advance(typeAnnotation.location.begin);
|
advance(typeAnnotation.location.begin);
|
||||||
|
@ -2733,25 +2671,14 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeUnion>())
|
else if (const auto& a = typeAnnotation.as<AstTypeUnion>())
|
||||||
{
|
{
|
||||||
const auto cstNode = lookupCstNode<CstTypeUnion>(a);
|
if (a->types.size == 2)
|
||||||
|
|
||||||
if (!cstNode && a->types.size == 2)
|
|
||||||
{
|
{
|
||||||
AstType* l = a->types.data[0];
|
AstType* l = a->types.data[0];
|
||||||
AstType* r = a->types.data[1];
|
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>();
|
auto lta = l->as<AstTypeReference>();
|
||||||
if (lta && lta->name == "nil")
|
if (lta && lta->name == "nil")
|
||||||
std::swap(l, r);
|
std::swap(l, r);
|
||||||
}
|
|
||||||
|
|
||||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||||
auto rta = r->as<AstTypeReference>();
|
auto rta = r->as<AstTypeReference>();
|
||||||
|
@ -2772,20 +2699,12 @@ 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)
|
for (size_t i = 0; i < a->types.size; ++i)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauParseOptionalAsNode2)
|
if (FFlag::LuauParseOptionalAsNode)
|
||||||
{
|
{
|
||||||
if (const auto optional = a->types.data[i]->as<AstTypeOptional>())
|
if (a->types.data[i]->is<AstTypeOptional>())
|
||||||
{
|
{
|
||||||
advance(optional->location.begin);
|
|
||||||
writer.symbol("?");
|
writer.symbol("?");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2793,18 +2712,11 @@ struct Printer
|
||||||
|
|
||||||
if (i > 0)
|
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("|");
|
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)
|
if (wrap)
|
||||||
writer.symbol("(");
|
writer.symbol("(");
|
||||||
|
@ -2817,27 +2729,15 @@ struct Printer
|
||||||
}
|
}
|
||||||
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
|
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)
|
for (size_t i = 0; i < a->types.size; ++i)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
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("&");
|
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)
|
if (wrap)
|
||||||
writer.symbol("(");
|
writer.symbol("(");
|
||||||
|
@ -2886,7 +2786,7 @@ std::string toString(AstNode* node)
|
||||||
StringWriter writer;
|
StringWriter writer;
|
||||||
writer.pos = node->location.begin;
|
writer.pos = node->location.begin;
|
||||||
|
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData)
|
||||||
{
|
{
|
||||||
Printer printer(writer, CstNodeMap{nullptr});
|
Printer printer(writer, CstNodeMap{nullptr});
|
||||||
printer.writeTypes = true;
|
printer.writeTypes = true;
|
||||||
|
@ -2922,7 +2822,7 @@ void dump(AstNode* node)
|
||||||
std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
||||||
{
|
{
|
||||||
StringWriter writer;
|
StringWriter writer;
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData)
|
||||||
{
|
{
|
||||||
Printer(writer, cstNodeMap).visualizeBlock(block);
|
Printer(writer, cstNodeMap).visualizeBlock(block);
|
||||||
}
|
}
|
||||||
|
@ -2936,7 +2836,7 @@ std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
||||||
std::string transpileWithTypes(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
std::string transpileWithTypes(AstStatBlock& block, const CstNodeMap& cstNodeMap)
|
||||||
{
|
{
|
||||||
StringWriter writer;
|
StringWriter writer;
|
||||||
if (FFlag::LuauStoreCSTData2)
|
if (FFlag::LuauStoreCSTData)
|
||||||
{
|
{
|
||||||
Printer printer(writer, cstNodeMap);
|
Printer printer(writer, cstNodeMap);
|
||||||
printer.writeTypes = true;
|
printer.writeTypes = true;
|
||||||
|
|
|
@ -407,6 +407,41 @@ PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
|
||||||
return newTp;
|
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)
|
PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexer)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(get<TableType>(ty));
|
LUAU_ASSERT(get<TableType>(ty));
|
||||||
|
|
|
@ -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())
|
: index(Unifiable::freshIndex())
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, lowerBound(lowerBound)
|
, lowerBound(lowerBound)
|
||||||
, upperBound(upperBound)
|
, upperBound(upperBound)
|
||||||
, polarity(polarity)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,18 +543,16 @@ GenericType::GenericType(TypeLevel level)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericType::GenericType(const Name& name, Polarity polarity)
|
GenericType::GenericType(const Name& name)
|
||||||
: index(Unifiable::freshIndex())
|
: index(Unifiable::freshIndex())
|
||||||
, name(name)
|
, name(name)
|
||||||
, explicitName(true)
|
, explicitName(true)
|
||||||
, polarity(polarity)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericType::GenericType(Scope* scope, Polarity polarity)
|
GenericType::GenericType(Scope* scope)
|
||||||
: index(Unifiable::freshIndex())
|
: index(Unifiable::freshIndex())
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, polarity(polarity)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,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(
|
FunctionType::FunctionType(
|
||||||
std::vector<TypeId> generics,
|
std::vector<TypeId> generics,
|
||||||
std::vector<TypePackId> genericPacks,
|
std::vector<TypePackId> genericPacks,
|
||||||
|
@ -669,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() {}
|
||||||
|
|
||||||
Property::Property(
|
Property::Property(
|
||||||
|
@ -1271,9 +1306,9 @@ IntersectionTypeIterator end(const IntersectionType* itv)
|
||||||
return IntersectionTypeIterator{};
|
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)
|
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
||||||
|
|
|
@ -77,9 +77,9 @@ TypeId TypeArena::freshType_DEPRECATED(Scope* scope, TypeLevel level)
|
||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId TypeArena::freshTypePack(Scope* scope, Polarity polarity)
|
TypePackId TypeArena::freshTypePack(Scope* scope)
|
||||||
{
|
{
|
||||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope, polarity});
|
TypePackId allocated = typePacks.allocate(FreeTypePack{scope});
|
||||||
|
|
||||||
asMutable(allocated)->owningArena = this;
|
asMutable(allocated)->owningArena = this;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
LUAU_FASTFLAG(LuauStoreCSTData)
|
||||||
|
|
||||||
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
|
||||||
{
|
{
|
||||||
|
@ -308,7 +308,7 @@ public:
|
||||||
|
|
||||||
if (el)
|
if (el)
|
||||||
new (arg)
|
new (arg)
|
||||||
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location));
|
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData ? Location() : el->location));
|
||||||
else
|
else
|
||||||
new (arg) std::optional<AstArgumentName>();
|
new (arg) std::optional<AstArgumentName>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -2230,21 +2229,10 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
|
||||||
|
|
||||||
return builtinTypes->numberType;
|
return builtinTypes->numberType;
|
||||||
case AstExprBinary::Op::Concat:
|
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(leftType, builtinTypes->stringType, expr->left->location);
|
||||||
testIsSubtype(rightType, builtinTypes->stringType, expr->right->location);
|
testIsSubtype(rightType, builtinTypes->stringType, expr->right->location);
|
||||||
}
|
|
||||||
|
|
||||||
return builtinTypes->stringType;
|
return builtinTypes->stringType;
|
||||||
}
|
|
||||||
case AstExprBinary::Op::CompareGe:
|
case AstExprBinary::Op::CompareGe:
|
||||||
case AstExprBinary::Op::CompareGt:
|
case AstExprBinary::Op::CompareGt:
|
||||||
case AstExprBinary::Op::CompareLe:
|
case AstExprBinary::Op::CompareLe:
|
||||||
|
|
|
@ -46,32 +46,24 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
||||||
// when this value is set to a negative value, guessing will be totally disabled.
|
// when this value is set to a negative value, guessing will be totally disabled.
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||||
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauClipNestedAndRecursiveUnion)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionImprovements)
|
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionImprovements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDontForgetToReduceUnionFunc)
|
LUAU_FASTFLAGVARIABLE(LuauDontForgetToReduceUnionFunc)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSearchForRefineableType)
|
LUAU_FASTFLAGVARIABLE(LuauSearchForRefineableType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexDeferPendingIndexee)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
using TypeOrTypePackIdSet = DenseHashSet<const void*>;
|
using TypeOrTypePackIdSet = DenseHashSet<const void*>;
|
||||||
|
|
||||||
struct InstanceCollector_DEPRECATED : TypeOnceVisitor
|
struct InstanceCollector : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
VecDeque<TypeId> tys;
|
VecDeque<TypeId> tys;
|
||||||
VecDeque<TypePackId> tps;
|
VecDeque<TypePackId> tps;
|
||||||
|
@ -126,153 +118,6 @@ struct InstanceCollector_DEPRECATED : TypeOnceVisitor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InstanceCollector : TypeOnceVisitor
|
|
||||||
{
|
|
||||||
DenseHashSet<TypeId> recordedTys{nullptr};
|
|
||||||
VecDeque<TypeId> tys;
|
|
||||||
DenseHashSet<TypePackId> recordedTps{nullptr};
|
|
||||||
VecDeque<TypePackId> tps;
|
|
||||||
TypeOrTypePackIdSet shouldGuess{nullptr};
|
|
||||||
std::vector<const void*> typeFunctionInstanceStack;
|
|
||||||
std::vector<TypeId> cyclicInstance;
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
|
||||||
{
|
|
||||||
// TypeVisitor performs a depth-first traversal in the absence of
|
|
||||||
// cycles. This means that by pushing to the front of the queue, we will
|
|
||||||
// try to reduce deeper instances first if we start with the first thing
|
|
||||||
// in the queue. Consider Add<Add<Add<number, number>, number>, number>:
|
|
||||||
// we want to reduce the innermost Add<number, number> instantiation
|
|
||||||
// first.
|
|
||||||
|
|
||||||
typeFunctionInstanceStack.push_back(ty);
|
|
||||||
|
|
||||||
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && int(typeFunctionInstanceStack.size()) > DFInt::LuauTypeFamilyUseGuesserDepth)
|
|
||||||
shouldGuess.insert(ty);
|
|
||||||
|
|
||||||
if (!recordedTys.contains(ty))
|
|
||||||
{
|
|
||||||
recordedTys.insert(ty);
|
|
||||||
tys.push_front(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TypeId p : tfit.typeArguments)
|
|
||||||
traverse(p);
|
|
||||||
|
|
||||||
for (TypePackId p : tfit.packArguments)
|
|
||||||
traverse(p);
|
|
||||||
|
|
||||||
typeFunctionInstanceStack.pop_back();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cycle(TypeId ty) override
|
|
||||||
{
|
|
||||||
TypeId t = follow(ty);
|
|
||||||
|
|
||||||
if (get<TypeFunctionInstanceType>(t))
|
|
||||||
{
|
|
||||||
// If we see a type a second time and it's in the type function stack, it's a real cycle
|
|
||||||
if (std::find(typeFunctionInstanceStack.begin(), typeFunctionInstanceStack.end(), t) != typeFunctionInstanceStack.end())
|
|
||||||
cyclicInstance.push_back(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp, const TypeFunctionInstanceTypePack& tfitp) override
|
|
||||||
{
|
|
||||||
// TypeVisitor performs a depth-first traversal in the absence of
|
|
||||||
// cycles. This means that by pushing to the front of the queue, we will
|
|
||||||
// try to reduce deeper instances first if we start with the first thing
|
|
||||||
// in the queue. Consider Add<Add<Add<number, number>, number>, number>:
|
|
||||||
// we want to reduce the innermost Add<number, number> instantiation
|
|
||||||
// first.
|
|
||||||
|
|
||||||
typeFunctionInstanceStack.push_back(tp);
|
|
||||||
|
|
||||||
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && int(typeFunctionInstanceStack.size()) > DFInt::LuauTypeFamilyUseGuesserDepth)
|
|
||||||
shouldGuess.insert(tp);
|
|
||||||
|
|
||||||
if (!recordedTps.contains(tp))
|
|
||||||
{
|
|
||||||
recordedTps.insert(tp);
|
|
||||||
tps.push_front(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TypeId p : tfitp.typeArguments)
|
|
||||||
traverse(p);
|
|
||||||
|
|
||||||
for (TypePackId p : tfitp.packArguments)
|
|
||||||
traverse(p);
|
|
||||||
|
|
||||||
typeFunctionInstanceStack.pop_back();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UnscopedGenericFinder : TypeOnceVisitor
|
|
||||||
{
|
|
||||||
std::vector<TypeId> scopeGenTys;
|
|
||||||
std::vector<TypePackId> scopeGenTps;
|
|
||||||
bool foundUnscoped = false;
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
|
||||||
{
|
|
||||||
// Once we have found an unscoped generic, we will stop the traversal
|
|
||||||
return !foundUnscoped;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp) override
|
|
||||||
{
|
|
||||||
// Once we have found an unscoped generic, we will stop the traversal
|
|
||||||
return !foundUnscoped;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const GenericType&) override
|
|
||||||
{
|
|
||||||
if (std::find(scopeGenTys.begin(), scopeGenTys.end(), ty) == scopeGenTys.end())
|
|
||||||
foundUnscoped = true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp, const GenericTypePack&) override
|
|
||||||
{
|
|
||||||
if (std::find(scopeGenTps.begin(), scopeGenTps.end(), tp) == scopeGenTps.end())
|
|
||||||
foundUnscoped = true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const FunctionType& ftv) override
|
|
||||||
{
|
|
||||||
size_t startTyCount = scopeGenTys.size();
|
|
||||||
size_t startTpCount = scopeGenTps.size();
|
|
||||||
|
|
||||||
scopeGenTys.insert(scopeGenTys.end(), ftv.generics.begin(), ftv.generics.end());
|
|
||||||
scopeGenTps.insert(scopeGenTps.end(), ftv.genericPacks.begin(), ftv.genericPacks.end());
|
|
||||||
|
|
||||||
traverse(ftv.argTypes);
|
|
||||||
traverse(ftv.retTypes);
|
|
||||||
|
|
||||||
scopeGenTys.resize(startTyCount);
|
|
||||||
scopeGenTps.resize(startTpCount);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TypeFunctionReducer
|
struct TypeFunctionReducer
|
||||||
{
|
{
|
||||||
TypeFunctionContext ctx;
|
TypeFunctionContext ctx;
|
||||||
|
@ -513,6 +358,7 @@ struct TypeFunctionReducer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void stepType()
|
void stepType()
|
||||||
{
|
{
|
||||||
TypeId subject = follow(queuedTys.front());
|
TypeId subject = follow(queuedTys.front());
|
||||||
|
@ -522,30 +368,10 @@ struct TypeFunctionReducer
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogTypeFamilies)
|
if (FFlag::DebugLuauLogTypeFamilies)
|
||||||
printf("Trying to %sreduce %s\n", force ? "force " : "", toString(subject, {true}).c_str());
|
printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
|
||||||
|
|
||||||
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(subject))
|
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(subject))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2 && tfit->function->name == "user")
|
|
||||||
{
|
|
||||||
UnscopedGenericFinder finder;
|
|
||||||
finder.traverse(subject);
|
|
||||||
|
|
||||||
if (finder.foundUnscoped)
|
|
||||||
{
|
|
||||||
// Do not step into this type again
|
|
||||||
irreducible.insert(subject);
|
|
||||||
|
|
||||||
// Let the caller know this type will not become reducible
|
|
||||||
result.irreducibleTypes.insert(subject);
|
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogTypeFamilies)
|
|
||||||
printf("Irreducible due to an unscoped generic type\n");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SkipTestResult testCyclic = testForSkippability(subject);
|
SkipTestResult testCyclic = testForSkippability(subject);
|
||||||
|
|
||||||
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFunction)
|
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFunction)
|
||||||
|
@ -653,8 +479,6 @@ static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
||||||
{
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
{
|
||||||
InstanceCollector collector;
|
InstanceCollector collector;
|
||||||
|
|
||||||
|
@ -680,37 +504,8 @@ FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location loc
|
||||||
force
|
force
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
InstanceCollector_DEPRECATED collector;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
collector.traverse(entrypoint);
|
|
||||||
}
|
|
||||||
catch (RecursionLimitException&)
|
|
||||||
{
|
|
||||||
return FunctionGraphReductionResult{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collector.tys.empty() && collector.tps.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return reduceFunctionsInternal(
|
|
||||||
std::move(collector.tys),
|
|
||||||
std::move(collector.tps),
|
|
||||||
std::move(collector.shouldGuess),
|
|
||||||
std::move(collector.cyclicInstance),
|
|
||||||
location,
|
|
||||||
ctx,
|
|
||||||
force
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext ctx, bool force)
|
||||||
{
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
{
|
||||||
InstanceCollector collector;
|
InstanceCollector collector;
|
||||||
|
|
||||||
|
@ -736,33 +531,6 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location
|
||||||
force
|
force
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
InstanceCollector_DEPRECATED collector;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
collector.traverse(entrypoint);
|
|
||||||
}
|
|
||||||
catch (RecursionLimitException&)
|
|
||||||
{
|
|
||||||
return FunctionGraphReductionResult{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collector.tys.empty() && collector.tps.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return reduceFunctionsInternal(
|
|
||||||
std::move(collector.tys),
|
|
||||||
std::move(collector.tps),
|
|
||||||
std::move(collector.shouldGuess),
|
|
||||||
std::move(collector.cyclicInstance),
|
|
||||||
location,
|
|
||||||
ctx,
|
|
||||||
force
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPending(TypeId ty, ConstraintSolver* solver)
|
bool isPending(TypeId ty, ConstraintSolver* solver)
|
||||||
{
|
{
|
||||||
|
@ -856,42 +624,6 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FindUserTypeFunctionBlockers : TypeOnceVisitor
|
|
||||||
{
|
|
||||||
NotNull<TypeFunctionContext> ctx;
|
|
||||||
DenseHashSet<TypeId> blockingTypeMap{nullptr};
|
|
||||||
std::vector<TypeId> blockingTypes;
|
|
||||||
|
|
||||||
explicit FindUserTypeFunctionBlockers(NotNull<TypeFunctionContext> ctx)
|
|
||||||
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
|
||||||
, ctx(ctx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
|
||||||
{
|
|
||||||
if (isPending(ty, ctx->solver))
|
|
||||||
{
|
|
||||||
if (!blockingTypeMap.contains(ty))
|
|
||||||
{
|
|
||||||
blockingTypeMap.insert(ty);
|
|
||||||
blockingTypes.push_back(ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypePackId tp) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
TypeId instance,
|
TypeId instance,
|
||||||
const std::vector<TypeId>& typeParams,
|
const std::vector<TypeId>& typeParams,
|
||||||
|
@ -914,21 +646,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
||||||
if (!ctx->typeFunctionRuntime->allowEvaluation || (FFlag::LuauTypeFunResultInAutocomplete && typeFunction->userFuncData.definition->hasErrors))
|
if (!ctx->typeFunctionRuntime->allowEvaluation)
|
||||||
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauNewTypeFunReductionChecks2)
|
|
||||||
{
|
|
||||||
FindUserTypeFunctionBlockers check{ctx};
|
|
||||||
|
|
||||||
for (auto typeParam : typeParams)
|
|
||||||
check.traverse(follow(typeParam));
|
|
||||||
|
|
||||||
if (!check.blockingTypes.empty())
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
{
|
{
|
||||||
TypeId ty = follow(typeParam);
|
TypeId ty = follow(typeParam);
|
||||||
|
@ -937,15 +657,10 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
if (isPending(ty, ctx->solver))
|
if (isPending(ty, ctx->solver))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {ty}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {ty}, {}};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that whole type function environment is registered
|
// Ensure that whole type function environment is registered
|
||||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||||
{
|
{
|
||||||
// Cannot evaluate if a potential dependency couldn't be parsed
|
|
||||||
if (FFlag::LuauTypeFunResultInAutocomplete && definition.first->hasErrors)
|
|
||||||
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}};
|
|
||||||
|
|
||||||
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
||||||
{
|
{
|
||||||
// Failure to register at this point means that original definition had to error out and should not have been present in the
|
// Failure to register at this point means that original definition had to error out and should not have been present in the
|
||||||
|
@ -1159,16 +874,7 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
||||||
|
|
||||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, operandTy, "__len", Location{});
|
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, operandTy, "__len", Location{});
|
||||||
if (!mmType)
|
if (!mmType)
|
||||||
{
|
|
||||||
if (FFlag::LuauMetatablesHaveLength)
|
|
||||||
{
|
|
||||||
// If we have a metatable type with no __len, this means we still have a table with default length function
|
|
||||||
if (get<MetatableType>(normalizedOperand))
|
|
||||||
return {ctx->builtins->numberType, Reduction::MaybeOk, {}, {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
}
|
|
||||||
|
|
||||||
mmType = follow(*mmType);
|
mmType = follow(*mmType);
|
||||||
if (isPending(*mmType, ctx->solver))
|
if (isPending(*mmType, ctx->solver))
|
||||||
|
@ -1221,9 +927,6 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
||||||
if (isPending(operandTy, ctx->solver))
|
if (isPending(operandTy, ctx->solver))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
operandTy = follow(operandTy);
|
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
||||||
|
|
||||||
// if the operand failed to normalize, we can't reduce, but know nothing about inhabitance.
|
// if the operand failed to normalize, we can't reduce, but know nothing about inhabitance.
|
||||||
|
@ -1301,10 +1004,6 @@ std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunc
|
||||||
if (!allowEvaluation)
|
if (!allowEvaluation)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Do not evaluate type functions with parse errors inside
|
|
||||||
if (FFlag::LuauTypeFunResultInAutocomplete && function->hasErrors)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
prepareState();
|
prepareState();
|
||||||
|
|
||||||
lua_State* global = state.get();
|
lua_State* global = state.get();
|
||||||
|
@ -1347,6 +1046,7 @@ std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunc
|
||||||
|
|
||||||
std::string bytecode = builder.getBytecode();
|
std::string bytecode = builder.getBytecode();
|
||||||
|
|
||||||
|
|
||||||
// Separate sandboxed thread for individual execution and private globals
|
// Separate sandboxed thread for individual execution and private globals
|
||||||
lua_State* L = lua_newthread(global);
|
lua_State* L = lua_newthread(global);
|
||||||
LuauTempThreadPopper popper(global);
|
LuauTempThreadPopper popper(global);
|
||||||
|
@ -2117,50 +1817,28 @@ struct FindRefinementBlockers : TypeOnceVisitor
|
||||||
struct ContainsRefinableType : TypeOnceVisitor
|
struct ContainsRefinableType : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
ContainsRefinableType()
|
ContainsRefinableType() : TypeOnceVisitor(/* skipBoundTypes */ true) {}
|
||||||
: TypeOnceVisitor(/* skipBoundTypes */ true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override {
|
||||||
{
|
|
||||||
// Default case: if we find *some* type that's worth refining against,
|
// Default case: if we find *some* type that's worth refining against,
|
||||||
// then we can claim that this type contains a refineable type.
|
// then we can claim that this type contains a refineable type.
|
||||||
found = true;
|
found = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId Ty, const NoRefineType&) override
|
bool visit(TypeId Ty, const NoRefineType&) override {
|
||||||
{
|
|
||||||
// No refine types aren't interesting
|
// No refine types aren't interesting
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const TableType&) override
|
bool visit(TypeId ty, const TableType&) override { return !found; }
|
||||||
{
|
bool visit(TypeId ty, const MetatableType&) override { return !found; }
|
||||||
return !found;
|
bool visit(TypeId ty, const FunctionType&) override { return !found; }
|
||||||
}
|
bool visit(TypeId ty, const UnionType&) override { return !found; }
|
||||||
bool visit(TypeId ty, const MetatableType&) override
|
bool visit(TypeId ty, const IntersectionType&) override { return !found; }
|
||||||
{
|
bool visit(TypeId ty, const NegationType&) override { return !found; }
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
bool visit(TypeId ty, const FunctionType&) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
bool visit(TypeId ty, const UnionType&) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
bool visit(TypeId ty, const IntersectionType&) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
bool visit(TypeId ty, const NegationType&) override
|
|
||||||
{
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
|
@ -2245,18 +1923,6 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSimplyRefineNotNil)
|
|
||||||
{
|
|
||||||
if (auto negation = get<NegationType>(discriminant))
|
|
||||||
{
|
|
||||||
if (auto primitive = get<PrimitiveType>(follow(negation->ty)); primitive && primitive->type == PrimitiveType::NilType)
|
|
||||||
{
|
|
||||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
|
||||||
return {result.result, {}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
||||||
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
||||||
if (get<TableType>(target))
|
if (get<TableType>(target))
|
||||||
|
@ -2364,29 +2030,6 @@ struct CollectUnionTypeOptions : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const UnionType& ut) override
|
|
||||||
{
|
|
||||||
if (FFlag::LuauReduceUnionFollowUnionType)
|
|
||||||
{
|
|
||||||
// If we have something like:
|
|
||||||
//
|
|
||||||
// union<A | B, C | D>
|
|
||||||
//
|
|
||||||
// We probably just want to consider this to be the same as
|
|
||||||
//
|
|
||||||
// union<A, B, C, D>
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Copy of the default visit method.
|
|
||||||
options.insert(ty);
|
|
||||||
if (isPending(ty, ctx->solver))
|
|
||||||
blockingTypes.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
||||||
{
|
{
|
||||||
if (tfit.function->name != builtinTypeFunctions().unionFunc.name)
|
if (tfit.function->name != builtinTypeFunctions().unionFunc.name)
|
||||||
|
@ -2441,6 +2084,7 @@ TypeFunctionReductionResult<TypeId> unionTypeFunction(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {resultTy, Reduction::MaybeOk, {}, {}};
|
return {resultTy, Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2798,19 +2442,7 @@ bool searchPropsAndIndexer(
|
||||||
// index into tbl's indexer
|
// index into tbl's indexer
|
||||||
if (tblIndexer)
|
if (tblIndexer)
|
||||||
{
|
{
|
||||||
TypeId indexType = FFlag::LuauFixCyclicIndexInIndexer ? follow(tblIndexer->indexType) : tblIndexer->indexType;
|
if (isSubtype(ty, tblIndexer->indexType, ctx->scope, ctx->builtins, ctx->simplifier, *ctx->ice))
|
||||||
|
|
||||||
if (FFlag::LuauFixCyclicIndexInIndexer)
|
|
||||||
{
|
|
||||||
if (auto tfit = get<TypeFunctionInstanceType>(indexType))
|
|
||||||
{
|
|
||||||
// if we have an index function here, it means we're in a cycle, so let's see if it's well-founded if we tie the knot
|
|
||||||
if (tfit->function.get() == &builtinTypeFunctions().indexFunc)
|
|
||||||
indexType = follow(tblIndexer->indexResultType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSubtype(ty, indexType, ctx->scope, ctx->builtins, ctx->simplifier, *ctx->ice))
|
|
||||||
{
|
{
|
||||||
TypeId idxResultTy = follow(tblIndexer->indexResultType);
|
TypeId idxResultTy = follow(tblIndexer->indexResultType);
|
||||||
|
|
||||||
|
@ -2875,14 +2507,7 @@ bool tblIndexInto_DEPRECATED(TypeId indexer, TypeId indexee, DenseHashSet<TypeId
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tblIndexInto(
|
bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, DenseHashSet<TypeId>& seenSet, NotNull<TypeFunctionContext> ctx, bool isRaw)
|
||||||
TypeId indexer,
|
|
||||||
TypeId indexee,
|
|
||||||
DenseHashSet<TypeId>& result,
|
|
||||||
DenseHashSet<TypeId>& seenSet,
|
|
||||||
NotNull<TypeFunctionContext> ctx,
|
|
||||||
bool isRaw
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
indexer = follow(indexer);
|
indexer = follow(indexer);
|
||||||
indexee = follow(indexee);
|
indexee = follow(indexee);
|
||||||
|
@ -2993,10 +2618,6 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
TypeId indexeeTy = follow(typeParams.at(0));
|
TypeId indexeeTy = follow(typeParams.at(0));
|
||||||
|
|
||||||
if (FFlag::LuauIndexDeferPendingIndexee && isPending(indexeeTy, ctx->solver))
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {indexeeTy}, {}};
|
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
|
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
|
||||||
|
|
||||||
// if the indexee failed to normalize, we can't reduce, but know nothing about inhabitance.
|
// if the indexee failed to normalize, we can't reduce, but know nothing about inhabitance.
|
||||||
|
@ -3122,7 +2743,6 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the type being reduced to is a single type, no need to union
|
// If the type being reduced to is a single type, no need to union
|
||||||
if (properties.size() == 1)
|
if (properties.size() == 1)
|
||||||
return {*properties.begin(), Reduction::MaybeOk, {}, {}};
|
return {*properties.begin(), Reduction::MaybeOk, {}, {}};
|
||||||
|
@ -3366,39 +2986,6 @@ TypeFunctionReductionResult<TypeId> getmetatableTypeFunction(
|
||||||
return getmetatableHelper(targetTy, location, ctx);
|
return getmetatableHelper(targetTy, location, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> weakoptionalTypeFunc(
|
|
||||||
TypeId instance,
|
|
||||||
const std::vector<TypeId>& typeParams,
|
|
||||||
const std::vector<TypePackId>& packParams,
|
|
||||||
NotNull<TypeFunctionContext> ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (typeParams.size() != 1 || !packParams.empty())
|
|
||||||
{
|
|
||||||
ctx->ice->ice("weakoptional type function: encountered a type function instance without the required argument structure");
|
|
||||||
LUAU_ASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId targetTy = follow(typeParams.at(0));
|
|
||||||
|
|
||||||
if (isPending(targetTy, ctx->solver))
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {targetTy}, {}};
|
|
||||||
|
|
||||||
if (is<NeverType>(instance))
|
|
||||||
return {ctx->builtins->nilType, Reduction::MaybeOk, {}, {}};
|
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> targetNorm = ctx->normalizer->normalize(targetTy);
|
|
||||||
|
|
||||||
if (!targetNorm)
|
|
||||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
|
||||||
|
|
||||||
auto result = ctx->normalizer->isInhabited(targetNorm.get());
|
|
||||||
if (result == NormalizationResult::False)
|
|
||||||
return {ctx->builtins->nilType, Reduction::MaybeOk, {}, {}};
|
|
||||||
|
|
||||||
return {targetTy, Reduction::MaybeOk, {}, {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
: userFunc{"user", userDefinedTypeFunction}
|
: userFunc{"user", userDefinedTypeFunction}
|
||||||
|
@ -3428,7 +3015,6 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
, rawgetFunc{"rawget", rawgetTypeFunction}
|
, rawgetFunc{"rawget", rawgetTypeFunction}
|
||||||
, setmetatableFunc{"setmetatable", setmetatableTypeFunction}
|
, setmetatableFunc{"setmetatable", setmetatableTypeFunction}
|
||||||
, getmetatableFunc{"getmetatable", getmetatableTypeFunction}
|
, getmetatableFunc{"getmetatable", getmetatableTypeFunction}
|
||||||
, weakoptionalFunc{"weakoptional", weakoptionalTypeFunc}
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3437,7 +3023,7 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
|
||||||
// make a type function for a one-argument type function
|
// make a type function for a one-argument type function
|
||||||
auto mkUnaryTypeFunction = [&](const TypeFunction* tf)
|
auto mkUnaryTypeFunction = [&](const TypeFunction* tf)
|
||||||
{
|
{
|
||||||
TypeId t = arena->addType(GenericType{"T", Polarity::Negative});
|
TypeId t = arena->addType(GenericType{"T"});
|
||||||
GenericTypeDefinition genericT{t};
|
GenericTypeDefinition genericT{t};
|
||||||
|
|
||||||
return TypeFun{{genericT}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t}, {}})};
|
return TypeFun{{genericT}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t}, {}})};
|
||||||
|
@ -3446,8 +3032,8 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
|
||||||
// make a type function for a two-argument type function
|
// make a type function for a two-argument type function
|
||||||
auto mkBinaryTypeFunction = [&](const TypeFunction* tf)
|
auto mkBinaryTypeFunction = [&](const TypeFunction* tf)
|
||||||
{
|
{
|
||||||
TypeId t = arena->addType(GenericType{"T", Polarity::Negative});
|
TypeId t = arena->addType(GenericType{"T"});
|
||||||
TypeId u = arena->addType(GenericType{"U", Polarity::Negative});
|
TypeId u = arena->addType(GenericType{"U"});
|
||||||
GenericTypeDefinition genericT{t};
|
GenericTypeDefinition genericT{t};
|
||||||
GenericTypeDefinition genericU{u, {t}};
|
GenericTypeDefinition genericU{u, {t}};
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1655,7 +1656,10 @@ static int print(lua_State* L)
|
||||||
const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
|
const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
|
||||||
if (i > 1)
|
if (i > 1)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauTypeFunPrintFix)
|
||||||
result.append(1, '\t');
|
result.append(1, '\t');
|
||||||
|
else
|
||||||
|
result.append('\t', 1);
|
||||||
}
|
}
|
||||||
result.append(s, l);
|
result.append(s, l);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
|
@ -34,8 +34,8 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
LUAU_FASTFLAG(LuauModuleHoldsAstRoot)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -256,6 +256,7 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
||||||
currentModule->type = module.type;
|
currentModule->type = module.type;
|
||||||
currentModule->allocator = module.allocator;
|
currentModule->allocator = module.allocator;
|
||||||
currentModule->names = module.names;
|
currentModule->names = module.names;
|
||||||
|
if (FFlag::LuauModuleHoldsAstRoot)
|
||||||
currentModule->root = module.root;
|
currentModule->root = module.root;
|
||||||
|
|
||||||
iceHandler->moduleName = module.name;
|
iceHandler->moduleName = module.name;
|
||||||
|
@ -1318,26 +1319,10 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
||||||
// Extract the remaining return values of the call
|
// Extract the remaining return values of the call
|
||||||
// and check them against the parameter types of the iterator function.
|
// and check them against the parameter types of the iterator function.
|
||||||
auto [types, tail] = flatten(callRetPack);
|
auto [types, tail] = flatten(callRetPack);
|
||||||
|
|
||||||
if (FFlag::LuauStatForInFix)
|
|
||||||
{
|
|
||||||
if (!types.empty())
|
|
||||||
{
|
|
||||||
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
std::vector<TypeId> argTypes = std::vector<TypeId>(types.begin() + 1, types.end());
|
||||||
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
argPack = addTypePack(TypePackVar{TypePack{std::move(argTypes), tail}});
|
||||||
}
|
}
|
||||||
else
|
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}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Check if iterator function accepts 0 arguments
|
// Check if iterator function accepts 0 arguments
|
||||||
argPack = addTypePack(TypePack{});
|
argPack = addTypePack(TypePack{});
|
||||||
|
@ -1673,9 +1658,6 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||||
FreeType* ftv = getMutable<FreeType>(ty);
|
FreeType* ftv = getMutable<FreeType>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
ftv->forwardedTypeAlias = true;
|
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->typeAliasLocations[name] = typealias.location;
|
||||||
|
@ -1721,9 +1703,6 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& de
|
||||||
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||||
|
|
||||||
ctv->metatable = metaTy;
|
ctv->metatable = metaTy;
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredClass.location};
|
|
||||||
else
|
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypePackDetectCycles)
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -18,11 +18,10 @@ FreeTypePack::FreeTypePack(TypeLevel level)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeTypePack::FreeTypePack(Scope* scope, Polarity polarity)
|
FreeTypePack::FreeTypePack(Scope* scope)
|
||||||
: index(Unifiable::freshIndex())
|
: index(Unifiable::freshIndex())
|
||||||
, level{}
|
, level{}
|
||||||
, scope(scope)
|
, 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())
|
: index(Unifiable::freshIndex())
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, polarity(polarity)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,15 +147,6 @@ TypePackIterator& TypePackIterator::operator++()
|
||||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : 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;
|
currentIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -306,11 +304,7 @@ TypePack extendTypePack(
|
||||||
// also have to create a new tail.
|
// also have to create a new tail.
|
||||||
|
|
||||||
TypePack newPack;
|
TypePack newPack;
|
||||||
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
newPack.tail = arena.freshTypePack(ftp->scope);
|
||||||
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
result.tail = newPack.tail;
|
result.tail = newPack.tail;
|
||||||
size_t overridesIndex = 0;
|
size_t overridesIndex = 0;
|
||||||
|
@ -325,7 +319,7 @@ TypePack extendTypePack(
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSolverV2)
|
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);
|
t = arena.addType(ft);
|
||||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
trackInteriorFreeType(ftp->scope, t);
|
trackInteriorFreeType(ftp->scope, t);
|
||||||
|
@ -574,24 +568,4 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypes` member.");
|
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypes` member.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(tp);
|
|
||||||
if (!FFlag::LuauNonReentrantGeneralization)
|
|
||||||
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
|
} // namespace Luau
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifyMetatableWithAny)
|
LUAU_FASTFLAGVARIABLE(LuauUnifyMetatableWithAny)
|
||||||
LUAU_FASTFLAG(LuauExtraFollows)
|
LUAU_FASTFLAG(LuauExtraFollows)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -322,24 +321,11 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
if (shouldInstantiate)
|
if (shouldInstantiate)
|
||||||
{
|
{
|
||||||
for (auto generic : subFn->generics)
|
for (auto generic : subFn->generics)
|
||||||
{
|
genericSubstitutions[generic] = freshType(arena, builtinTypes, scope);
|
||||||
const GenericType* gen = get<GenericType>(generic);
|
|
||||||
LUAU_ASSERT(gen);
|
|
||||||
genericSubstitutions[generic] = freshType(scope, gen->polarity);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto genericPack : subFn->genericPacks)
|
for (auto genericPack : subFn->genericPacks)
|
||||||
{
|
|
||||||
if (FFlag::LuauNonReentrantGeneralization)
|
|
||||||
{
|
|
||||||
const GenericTypePack* gen = get<GenericTypePack>(genericPack);
|
|
||||||
LUAU_ASSERT(gen);
|
|
||||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
genericPackSubstitutions[genericPack] = arena->freshTypePack(scope);
|
genericPackSubstitutions[genericPack] = arena->freshTypePack(scope);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool argResult = unify(superFn->argTypes, subFn->argTypes);
|
bool argResult = unify(superFn->argTypes, subFn->argTypes);
|
||||||
bool retResult = unify(subFn->retTypes, superFn->retTypes);
|
bool retResult = unify(subFn->retTypes, superFn->retTypes);
|
||||||
|
@ -447,6 +433,9 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
|
||||||
superTypePackParamsIter++;
|
superTypePackParamsIter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subTable->selfTy && superTable->selfTy)
|
||||||
|
result &= unify(*subTable->selfTy, *superTable->selfTy);
|
||||||
|
|
||||||
if (subTable->indexer && superTable->indexer)
|
if (subTable->indexer && superTable->indexer)
|
||||||
{
|
{
|
||||||
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
|
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
|
||||||
|
@ -661,22 +650,27 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Polarity polarity = Polarity::Positive;
|
enum Polarity
|
||||||
|
{
|
||||||
|
Positive,
|
||||||
|
Negative,
|
||||||
|
Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
Polarity polarity = Positive;
|
||||||
|
|
||||||
void flip()
|
void flip()
|
||||||
{
|
{
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
polarity = Polarity::Negative;
|
polarity = Negative;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
polarity = Polarity::Positive;
|
polarity = Positive;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +681,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
{
|
{
|
||||||
if (seenPositive.contains(ty))
|
if (seenPositive.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -695,7 +689,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenPositive.insert(ty);
|
seenPositive.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
{
|
{
|
||||||
if (seenNegative.contains(ty))
|
if (seenNegative.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -703,7 +697,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenNegative.insert(ty);
|
seenNegative.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
{
|
{
|
||||||
if (seenPositive.contains(ty) && seenNegative.contains(ty))
|
if (seenPositive.contains(ty) && seenNegative.contains(ty))
|
||||||
return true;
|
return true;
|
||||||
|
@ -712,8 +706,6 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
seenNegative.insert(ty);
|
seenNegative.insert(ty);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -744,18 +736,16 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -770,18 +760,16 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
{
|
{
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[ty]++;
|
positiveTypes[ty]++;
|
||||||
negativeTypes[ty]++;
|
negativeTypes[ty]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +782,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
LUAU_ASSERT(prop.isShared());
|
LUAU_ASSERT(prop.isShared());
|
||||||
|
|
||||||
Polarity p = polarity;
|
Polarity p = polarity;
|
||||||
polarity = Polarity::Mixed;
|
polarity = Both;
|
||||||
traverse(prop.type());
|
traverse(prop.type());
|
||||||
polarity = p;
|
polarity = p;
|
||||||
}
|
}
|
||||||
|
@ -838,18 +826,16 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
switch (polarity)
|
switch (polarity)
|
||||||
{
|
{
|
||||||
case Polarity::Positive:
|
case Positive:
|
||||||
positiveTypes[tp]++;
|
positiveTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Negative:
|
case Negative:
|
||||||
negativeTypes[tp]++;
|
negativeTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
case Polarity::Mixed:
|
case Both:
|
||||||
positiveTypes[tp]++;
|
positiveTypes[tp]++;
|
||||||
negativeTypes[tp]++;
|
negativeTypes[tp]++;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
LUAU_ASSERT(!"Unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -955,23 +941,4 @@ OccursCheckResult Unifier2::occursCheck(DenseHashSet<TypePackId>& seen, TypePack
|
||||||
return OccursCheckResult::Pass;
|
return OccursCheckResult::Pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId Unifier2::freshType(NotNull<Scope> scope, Polarity polarity)
|
|
||||||
{
|
|
||||||
TypeId result = ::Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
|
||||||
newFreshTypes.emplace_back(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId Unifier2::freshTypePack(NotNull<Scope> scope, Polarity polarity)
|
|
||||||
{
|
|
||||||
TypePackId result = arena->freshTypePack(scope.get());
|
|
||||||
|
|
||||||
auto ftp = getMutable<FreeTypePack>(result);
|
|
||||||
LUAU_ASSERT(ftp);
|
|
||||||
ftp->polarity = polarity;
|
|
||||||
|
|
||||||
newFreshTypePacks.emplace_back(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -194,7 +194,6 @@ public:
|
||||||
{
|
{
|
||||||
Checked,
|
Checked,
|
||||||
Native,
|
Native,
|
||||||
Deprecated,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AstAttr(const Location& location, Type type);
|
AstAttr(const Location& location, Type type);
|
||||||
|
@ -454,7 +453,6 @@ public:
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
|
||||||
bool hasNativeAttribute() const;
|
bool hasNativeAttribute() const;
|
||||||
bool hasAttribute(AstAttr::Type attributeType) const;
|
|
||||||
|
|
||||||
AstArray<AstAttr*> attributes;
|
AstArray<AstAttr*> attributes;
|
||||||
AstArray<AstGenericType*> generics;
|
AstArray<AstGenericType*> generics;
|
||||||
|
@ -892,22 +890,14 @@ class AstStatTypeFunction : public AstStat
|
||||||
public:
|
public:
|
||||||
LUAU_RTTI(AstStatTypeFunction);
|
LUAU_RTTI(AstStatTypeFunction);
|
||||||
|
|
||||||
AstStatTypeFunction(
|
AstStatTypeFunction(const Location& location, const AstName& name, const Location& nameLocation, AstExprFunction* body, bool exported);
|
||||||
const Location& location,
|
|
||||||
const AstName& name,
|
|
||||||
const Location& nameLocation,
|
|
||||||
AstExprFunction* body,
|
|
||||||
bool exported,
|
|
||||||
bool hasErrors
|
|
||||||
);
|
|
||||||
|
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
|
||||||
AstName name;
|
AstName name;
|
||||||
Location nameLocation;
|
Location nameLocation;
|
||||||
AstExprFunction* body = nullptr;
|
AstExprFunction* body;
|
||||||
bool exported = false;
|
bool exported;
|
||||||
bool hasErrors = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AstStatDeclareGlobal : public AstStat
|
class AstStatDeclareGlobal : public AstStat
|
||||||
|
@ -960,7 +950,6 @@ public:
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
|
||||||
bool isCheckedFunction() const;
|
bool isCheckedFunction() const;
|
||||||
bool hasAttribute(AstAttr::Type attributeType) const;
|
|
||||||
|
|
||||||
AstArray<AstAttr*> attributes;
|
AstArray<AstAttr*> attributes;
|
||||||
AstName name;
|
AstName name;
|
||||||
|
@ -1117,7 +1106,6 @@ public:
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
|
||||||
bool isCheckedFunction() const;
|
bool isCheckedFunction() const;
|
||||||
bool hasAttribute(AstAttr::Type attributeType) const;
|
|
||||||
|
|
||||||
AstArray<AstAttr*> attributes;
|
AstArray<AstAttr*> attributes;
|
||||||
AstArray<AstGenericType*> generics;
|
AstArray<AstGenericType*> generics;
|
||||||
|
@ -1471,10 +1459,6 @@ public:
|
||||||
{
|
{
|
||||||
return visit(static_cast<AstStat*>(node));
|
return visit(static_cast<AstStat*>(node));
|
||||||
}
|
}
|
||||||
virtual bool visit(class AstStatTypeFunction* node)
|
|
||||||
{
|
|
||||||
return visit(static_cast<AstStat*>(node));
|
|
||||||
}
|
|
||||||
virtual bool visit(class AstStatDeclareFunction* node)
|
virtual bool visit(class AstStatDeclareFunction* node)
|
||||||
{
|
{
|
||||||
return visit(static_cast<AstStat*>(node));
|
return visit(static_cast<AstStat*>(node));
|
||||||
|
|
|
@ -112,7 +112,6 @@ public:
|
||||||
|
|
||||||
CstExprFunction();
|
CstExprFunction();
|
||||||
|
|
||||||
Position functionKeywordPosition{0, 0};
|
|
||||||
Position openGenericsPosition{0,0};
|
Position openGenericsPosition{0,0};
|
||||||
AstArray<Position> genericsCommaPositions;
|
AstArray<Position> genericsCommaPositions;
|
||||||
Position closeGenericsPosition{0,0};
|
Position closeGenericsPosition{0,0};
|
||||||
|
@ -275,24 +274,13 @@ public:
|
||||||
Position opPosition;
|
Position opPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CstStatFunction : public CstNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LUAU_CST_RTTI(CstStatFunction)
|
|
||||||
|
|
||||||
explicit CstStatFunction(Position functionKeywordPosition);
|
|
||||||
|
|
||||||
Position functionKeywordPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CstStatLocalFunction : public CstNode
|
class CstStatLocalFunction : public CstNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LUAU_CST_RTTI(CstStatLocalFunction)
|
LUAU_CST_RTTI(CstStatLocalFunction)
|
||||||
|
|
||||||
explicit CstStatLocalFunction(Position localKeywordPosition, Position functionKeywordPosition);
|
explicit CstStatLocalFunction(Position functionKeywordPosition);
|
||||||
|
|
||||||
Position localKeywordPosition;
|
|
||||||
Position functionKeywordPosition;
|
Position functionKeywordPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -433,28 +421,6 @@ public:
|
||||||
Position closePosition;
|
Position closePosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CstTypeUnion : public CstNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LUAU_CST_RTTI(CstTypeUnion)
|
|
||||||
|
|
||||||
CstTypeUnion(std::optional<Position> leadingPosition, AstArray<Position> separatorPositions);
|
|
||||||
|
|
||||||
std::optional<Position> leadingPosition;
|
|
||||||
AstArray<Position> separatorPositions;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CstTypeIntersection : public CstNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LUAU_CST_RTTI(CstTypeIntersection)
|
|
||||||
|
|
||||||
explicit CstTypeIntersection(std::optional<Position> leadingPosition, AstArray<Position> separatorPositions);
|
|
||||||
|
|
||||||
std::optional<Position> leadingPosition;
|
|
||||||
AstArray<Position> separatorPositions;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CstTypeSingletonString : public CstNode
|
class CstTypeSingletonString : public CstNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -125,7 +125,7 @@ private:
|
||||||
AstStat* parseFor();
|
AstStat* parseFor();
|
||||||
|
|
||||||
// funcname ::= Name {`.' Name} [`:' Name]
|
// funcname ::= Name {`.' Name} [`:' Name]
|
||||||
AstExpr* parseFunctionName(bool& hasself, AstName& debugname);
|
AstExpr* parseFunctionName(Location start_DEPRECATED, bool& hasself, AstName& debugname);
|
||||||
|
|
||||||
// function funcname funcbody
|
// function funcname funcbody
|
||||||
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
|
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
|
||||||
|
@ -157,9 +157,7 @@ private:
|
||||||
// type function Name ... end
|
// type function Name ... end
|
||||||
AstStat* parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition);
|
AstStat* parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition);
|
||||||
|
|
||||||
AstDeclaredClassProp parseDeclaredClassMethod(const AstArray<AstAttr*>& attributes);
|
AstDeclaredClassProp parseDeclaredClassMethod();
|
||||||
AstDeclaredClassProp parseDeclaredClassMethod_DEPRECATED();
|
|
||||||
|
|
||||||
|
|
||||||
// `declare global' Name: Type |
|
// `declare global' Name: Type |
|
||||||
// `declare function' Name`(' [parlist] `)' [`:` Type]
|
// `declare function' Name`(' [parlist] `)' [`:` Type]
|
||||||
|
@ -230,9 +228,9 @@ private:
|
||||||
Position colonPosition;
|
Position colonPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation);
|
||||||
// Remove with FFlagLuauStoreCSTData2
|
// Remove with FFlagLuauStoreCSTData
|
||||||
AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
|
AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional<Location> accessLocation);
|
||||||
|
|
||||||
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
|
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
|
||||||
AstType* parseFunctionTypeTail(
|
AstType* parseFunctionTypeTail(
|
||||||
|
|
|
@ -3,24 +3,9 @@
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static bool hasAttributeInArray(const AstArray<AstAttr*> attributes, AstAttr::Type attributeType)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
|
|
||||||
for (const auto attribute : attributes)
|
|
||||||
{
|
|
||||||
if (attribute->type == attributeType)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void visitTypeList(AstVisitor* visitor, const AstTypeList& list)
|
static void visitTypeList(AstVisitor* visitor, const AstTypeList& list)
|
||||||
{
|
{
|
||||||
for (AstType* ty : list.types)
|
for (AstType* ty : list.types)
|
||||||
|
@ -292,13 +277,6 @@ bool AstExprFunction::hasNativeAttribute() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstExprFunction::hasAttribute(const AstAttr::Type attributeType) const
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
|
|
||||||
return hasAttributeInArray(attributes, attributeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
AstExprTable::AstExprTable(const Location& location, const AstArray<Item>& items)
|
AstExprTable::AstExprTable(const Location& location, const AstArray<Item>& items)
|
||||||
: AstExpr(ClassIndex(), location)
|
: AstExpr(ClassIndex(), location)
|
||||||
, items(items)
|
, items(items)
|
||||||
|
@ -813,15 +791,13 @@ AstStatTypeFunction::AstStatTypeFunction(
|
||||||
const AstName& name,
|
const AstName& name,
|
||||||
const Location& nameLocation,
|
const Location& nameLocation,
|
||||||
AstExprFunction* body,
|
AstExprFunction* body,
|
||||||
bool exported,
|
bool exported
|
||||||
bool hasErrors
|
|
||||||
)
|
)
|
||||||
: AstStat(ClassIndex(), location)
|
: AstStat(ClassIndex(), location)
|
||||||
, name(name)
|
, name(name)
|
||||||
, nameLocation(nameLocation)
|
, nameLocation(nameLocation)
|
||||||
, body(body)
|
, body(body)
|
||||||
, exported(exported)
|
, exported(exported)
|
||||||
, hasErrors(hasErrors)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,13 +894,6 @@ bool AstStatDeclareFunction::isCheckedFunction() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstStatDeclareFunction::hasAttribute(AstAttr::Type attributeType) const
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
|
|
||||||
return hasAttributeInArray(attributes, attributeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
AstStatDeclareClass::AstStatDeclareClass(
|
AstStatDeclareClass::AstStatDeclareClass(
|
||||||
const Location& location,
|
const Location& location,
|
||||||
const AstName& name,
|
const AstName& name,
|
||||||
|
@ -1088,13 +1057,6 @@ bool AstTypeFunction::isCheckedFunction() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstTypeFunction::hasAttribute(AstAttr::Type attributeType) const
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
|
||||||
|
|
||||||
return hasAttributeInArray(attributes, attributeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
AstTypeTypeof::AstTypeTypeof(const Location& location, AstExpr* expr)
|
AstTypeTypeof::AstTypeTypeof(const Location& location, AstExpr* expr)
|
||||||
: AstType(ClassIndex(), location)
|
: AstType(ClassIndex(), location)
|
||||||
, expr(expr)
|
, expr(expr)
|
||||||
|
|
|
@ -38,8 +38,7 @@ CstExprIndexExpr::CstExprIndexExpr(Position openBracketPosition, Position closeB
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CstExprFunction::CstExprFunction()
|
CstExprFunction::CstExprFunction() : CstNode(CstClassIndex())
|
||||||
: CstNode(CstClassIndex())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,19 +129,12 @@ CstStatCompoundAssign::CstStatCompoundAssign(Position opPosition)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CstStatFunction::CstStatFunction(Position functionKeywordPosition)
|
CstStatLocalFunction::CstStatLocalFunction(Position functionKeywordPosition)
|
||||||
: CstNode(CstClassIndex())
|
: CstNode(CstClassIndex())
|
||||||
, functionKeywordPosition(functionKeywordPosition)
|
, functionKeywordPosition(functionKeywordPosition)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CstStatLocalFunction::CstStatLocalFunction(Position localKeywordPosition, Position functionKeywordPosition)
|
|
||||||
: CstNode(CstClassIndex())
|
|
||||||
, localKeywordPosition(localKeywordPosition)
|
|
||||||
, functionKeywordPosition(functionKeywordPosition)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CstGenericType::CstGenericType(std::optional<Position> defaultEqualsPosition)
|
CstGenericType::CstGenericType(std::optional<Position> defaultEqualsPosition)
|
||||||
: CstNode(CstClassIndex())
|
: CstNode(CstClassIndex())
|
||||||
, defaultEqualsPosition(defaultEqualsPosition)
|
, defaultEqualsPosition(defaultEqualsPosition)
|
||||||
|
@ -229,20 +221,6 @@ CstTypeTypeof::CstTypeTypeof(Position openPosition, Position closePosition)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CstTypeUnion::CstTypeUnion(std::optional<Position> leadingPosition, AstArray<Position> separatorPositions)
|
|
||||||
: CstNode(CstClassIndex())
|
|
||||||
, leadingPosition(leadingPosition)
|
|
||||||
, separatorPositions(separatorPositions)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CstTypeIntersection::CstTypeIntersection(std::optional<Position> leadingPosition, AstArray<Position> separatorPositions)
|
|
||||||
: CstNode(CstClassIndex())
|
|
||||||
, leadingPosition(leadingPosition)
|
|
||||||
, separatorPositions(separatorPositions)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CstTypeSingletonString::CstTypeSingletonString(AstArray<char> sourceString, CstExprConstantString::QuoteStyle quoteStyle, unsigned int blockDepth)
|
CstTypeSingletonString::CstTypeSingletonString(AstArray<char> sourceString, CstExprConstantString::QuoteStyle quoteStyle, unsigned int blockDepth)
|
||||||
: CstNode(CstClassIndex())
|
: CstNode(CstClassIndex())
|
||||||
, sourceString(sourceString)
|
, sourceString(sourceString)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,35 +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/RequireNavigator.h"
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
|
|
||||||
struct FileNavigationContext : Luau::Require::NavigationContext
|
|
||||||
{
|
|
||||||
using NavigateResult = Luau::Require::NavigationContext::NavigateResult;
|
|
||||||
|
|
||||||
FileNavigationContext(std::string requirerPath);
|
|
||||||
|
|
||||||
std::string getRequirerIdentifier() const override;
|
|
||||||
|
|
||||||
// Navigation interface
|
|
||||||
NavigateResult reset(const std::string& requirerChunkname) override;
|
|
||||||
NavigateResult jumpToAlias(const std::string& path) override;
|
|
||||||
|
|
||||||
NavigateResult toParent() override;
|
|
||||||
NavigateResult toChild(const std::string& component) override;
|
|
||||||
|
|
||||||
bool isConfigPresent() const override;
|
|
||||||
std::optional<std::string> getConfig() const override;
|
|
||||||
|
|
||||||
// Custom capabilities
|
|
||||||
bool isModulePresent() const;
|
|
||||||
std::optional<std::string> getIdentifier() const;
|
|
||||||
|
|
||||||
std::string path;
|
|
||||||
std::string suffix;
|
|
||||||
std::string requirerPath;
|
|
||||||
|
|
||||||
private:
|
|
||||||
NavigateResult storePathResult(PathResult result);
|
|
||||||
};
|
|
|
@ -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/Require.h"
|
|
||||||
|
|
||||||
#include "Luau/Compiler.h"
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
void requireConfigInit(luarequire_Configuration* config);
|
|
||||||
|
|
||||||
struct ReplRequirer
|
|
||||||
{
|
|
||||||
ReplRequirer(
|
|
||||||
std::function<Luau::CompileOptions()> copts,
|
|
||||||
std::function<bool()> coverageActive,
|
|
||||||
std::function<bool()> codegenEnabled,
|
|
||||||
std::function<void(lua_State*, int)> coverageTrack
|
|
||||||
);
|
|
||||||
|
|
||||||
std::function<Luau::CompileOptions()> copts;
|
|
||||||
std::function<bool()> coverageActive;
|
|
||||||
std::function<bool()> codegenEnabled;
|
|
||||||
std::function<void(lua_State*, int)> coverageTrack;
|
|
||||||
|
|
||||||
std::string absPath;
|
|
||||||
std::string relPath;
|
|
||||||
std::string suffix;
|
|
||||||
};
|
|
84
CLI/include/Luau/Require.h
Normal file
84
CLI/include/Luau/Require.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Config.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
class RequireResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ModuleStatus
|
||||||
|
{
|
||||||
|
Cached,
|
||||||
|
FileRead,
|
||||||
|
ErrorReported
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ResolvedRequire
|
||||||
|
{
|
||||||
|
ModuleStatus status;
|
||||||
|
std::string identifier;
|
||||||
|
std::string absolutePath;
|
||||||
|
std::string sourceCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RequireContext
|
||||||
|
{
|
||||||
|
virtual ~RequireContext() = default;
|
||||||
|
virtual std::string getPath() = 0;
|
||||||
|
virtual bool isRequireAllowed() = 0;
|
||||||
|
virtual bool isStdin() = 0;
|
||||||
|
virtual std::string createNewIdentifer(const std::string& path) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CacheManager
|
||||||
|
{
|
||||||
|
virtual ~CacheManager() = default;
|
||||||
|
virtual bool isCached(const std::string& path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorHandler
|
||||||
|
{
|
||||||
|
virtual ~ErrorHandler() = default;
|
||||||
|
virtual void reportError(const std::string message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
RequireResolver(std::string pathToResolve, RequireContext& requireContext, CacheManager& cacheManager, ErrorHandler& errorHandler);
|
||||||
|
|
||||||
|
[[nodiscard]] ResolvedRequire resolveRequire(std::function<void(const ModuleStatus)> completionCallback = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string pathToResolve;
|
||||||
|
|
||||||
|
RequireContext& requireContext;
|
||||||
|
CacheManager& cacheManager;
|
||||||
|
ErrorHandler& errorHandler;
|
||||||
|
|
||||||
|
ResolvedRequire resolvedRequire;
|
||||||
|
bool isRequireResolved = false;
|
||||||
|
|
||||||
|
Luau::Config config;
|
||||||
|
std::string lastSearchedDir;
|
||||||
|
bool isConfigFullyResolved = false;
|
||||||
|
|
||||||
|
[[nodiscard]] bool initialize();
|
||||||
|
|
||||||
|
ModuleStatus findModule();
|
||||||
|
ModuleStatus findModuleImpl();
|
||||||
|
|
||||||
|
[[nodiscard]] bool resolveAndStoreDefaultPaths();
|
||||||
|
std::optional<std::string> getRequiringContextAbsolute();
|
||||||
|
std::string getRequiringContextRelative();
|
||||||
|
|
||||||
|
[[nodiscard]] bool substituteAliasIfPresent(std::string& path);
|
||||||
|
std::optional<std::string> getAlias(std::string alias);
|
||||||
|
|
||||||
|
[[nodiscard]] bool parseNextConfig();
|
||||||
|
[[nodiscard]] bool parseConfigInDirectory(const std::string& directory);
|
||||||
|
};
|
|
@ -1,36 +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 <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
struct PathResult
|
|
||||||
{
|
|
||||||
enum class Status
|
|
||||||
{
|
|
||||||
SUCCESS,
|
|
||||||
AMBIGUOUS,
|
|
||||||
NOT_FOUND
|
|
||||||
};
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
std::string absPath;
|
|
||||||
std::string relPath;
|
|
||||||
std::string suffix;
|
|
||||||
};
|
|
||||||
|
|
||||||
PathResult getStdInResult();
|
|
||||||
|
|
||||||
PathResult getAbsolutePathResult(const std::string& path);
|
|
||||||
|
|
||||||
// If given an absolute path, this will implicitly call getAbsolutePathResult.
|
|
||||||
// Aliases prevent us from solely operating on relative paths, so we need to
|
|
||||||
// be able to fall back to operating on absolute paths if needed.
|
|
||||||
PathResult tryGetRelativePathResult(const std::string& path);
|
|
||||||
|
|
||||||
PathResult getParent(const std::string& absPath, const std::string& relPath);
|
|
||||||
PathResult getChild(const std::string& absPath, const std::string& relPath, const std::string& name);
|
|
||||||
|
|
||||||
bool isFilePresent(const std::string& path, const std::string& suffix);
|
|
||||||
std::optional<std::string> getFileContents(const std::string& path, const std::string& suffix);
|
|
|
@ -7,10 +7,9 @@
|
||||||
#include "Luau/TypeAttach.h"
|
#include "Luau/TypeAttach.h"
|
||||||
#include "Luau/Transpiler.h"
|
#include "Luau/Transpiler.h"
|
||||||
|
|
||||||
#include "Luau/AnalyzeRequirer.h"
|
|
||||||
#include "Luau/FileUtils.h"
|
#include "Luau/FileUtils.h"
|
||||||
#include "Luau/Flags.h"
|
#include "Luau/Flags.h"
|
||||||
#include "Luau/RequireNavigator.h"
|
#include "Luau/Require.h"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -174,18 +173,15 @@ struct CliFileResolver : Luau::FileResolver
|
||||||
{
|
{
|
||||||
std::string path{expr->value.data, expr->value.size};
|
std::string path{expr->value.data, expr->value.size};
|
||||||
|
|
||||||
FileNavigationContext navigationContext{context->name};
|
AnalysisRequireContext requireContext{context->name};
|
||||||
Luau::Require::ErrorHandler nullErrorHandler{};
|
AnalysisCacheManager cacheManager;
|
||||||
|
AnalysisErrorHandler errorHandler;
|
||||||
|
|
||||||
Luau::Require::Navigator navigator(navigationContext, nullErrorHandler);
|
RequireResolver resolver(path, requireContext, cacheManager, errorHandler);
|
||||||
if (navigator.navigate(path) != Luau::Require::Navigator::Status::Success)
|
RequireResolver::ResolvedRequire resolvedRequire = resolver.resolveRequire();
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
if (!navigationContext.isModulePresent())
|
if (resolvedRequire.status == RequireResolver::ModuleStatus::FileRead)
|
||||||
return std::nullopt;
|
return {{resolvedRequire.identifier}};
|
||||||
|
|
||||||
if (std::optional<std::string> identifier = navigationContext.getIdentifier())
|
|
||||||
return {{*identifier}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -197,6 +193,48 @@ struct CliFileResolver : Luau::FileResolver
|
||||||
return "stdin";
|
return "stdin";
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct AnalysisRequireContext : RequireResolver::RequireContext
|
||||||
|
{
|
||||||
|
explicit AnalysisRequireContext(std::string path)
|
||||||
|
: path(std::move(path))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getPath() override
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRequireAllowed() override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isStdin() override
|
||||||
|
{
|
||||||
|
return path == "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string createNewIdentifer(const std::string& path) override
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnalysisCacheManager : public RequireResolver::CacheManager
|
||||||
|
{
|
||||||
|
AnalysisCacheManager() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnalysisErrorHandler : RequireResolver::ErrorHandler
|
||||||
|
{
|
||||||
|
AnalysisErrorHandler() = default;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CliConfigResolver : Luau::ConfigResolver
|
struct CliConfigResolver : Luau::ConfigResolver
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#include "Luau/AnalyzeRequirer.h"
|
|
||||||
|
|
||||||
#include "Luau/RequireNavigator.h"
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::storePathResult(PathResult result)
|
|
||||||
{
|
|
||||||
if (result.status == PathResult::Status::AMBIGUOUS)
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Ambiguous;
|
|
||||||
|
|
||||||
if (result.status == PathResult::Status::NOT_FOUND)
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::NotFound;
|
|
||||||
|
|
||||||
path = result.absPath;
|
|
||||||
suffix = result.suffix;
|
|
||||||
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileNavigationContext::FileNavigationContext(std::string requirerPath)
|
|
||||||
{
|
|
||||||
std::string_view path = requirerPath;
|
|
||||||
if (path.size() >= 10 && path.substr(path.size() - 10) == "/init.luau")
|
|
||||||
{
|
|
||||||
path.remove_suffix(10);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 9 && path.substr(path.size() - 9) == "/init.lua")
|
|
||||||
{
|
|
||||||
path.remove_suffix(9);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 5 && path.substr(path.size() - 5) == ".luau")
|
|
||||||
{
|
|
||||||
path.remove_suffix(5);
|
|
||||||
}
|
|
||||||
else if (path.size() >= 4 && path.substr(path.size() - 4) == ".lua")
|
|
||||||
{
|
|
||||||
path.remove_suffix(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->requirerPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FileNavigationContext::getRequirerIdentifier() const
|
|
||||||
{
|
|
||||||
return requirerPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::reset(const std::string& requirerChunkname)
|
|
||||||
{
|
|
||||||
if (requirerChunkname == "-")
|
|
||||||
{
|
|
||||||
return storePathResult(getStdInResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
return storePathResult(tryGetRelativePathResult(requirerChunkname));
|
|
||||||
}
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::jumpToAlias(const std::string& path)
|
|
||||||
{
|
|
||||||
Luau::Require::NavigationContext::NavigateResult result = storePathResult(getAbsolutePathResult(path));
|
|
||||||
if (result != Luau::Require::NavigationContext::NavigateResult::Success)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return Luau::Require::NavigationContext::NavigateResult::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toParent()
|
|
||||||
{
|
|
||||||
return storePathResult(getParent(path, path));
|
|
||||||
}
|
|
||||||
|
|
||||||
Luau::Require::NavigationContext::NavigateResult FileNavigationContext::toChild(const std::string& component)
|
|
||||||
{
|
|
||||||
return storePathResult(getChild(path, path, component));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileNavigationContext::isModulePresent() const
|
|
||||||
{
|
|
||||||
return isFilePresent(path, suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> FileNavigationContext::getIdentifier() const
|
|
||||||
{
|
|
||||||
return path + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileNavigationContext::isConfigPresent() const
|
|
||||||
{
|
|
||||||
return isFilePresent(path, "/.luaurc");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> FileNavigationContext::getConfig() const
|
|
||||||
{
|
|
||||||
return getFileContents(path, "/.luaurc");
|
|
||||||
}
|
|
212
CLI/src/Repl.cpp
212
CLI/src/Repl.cpp
|
@ -14,7 +14,6 @@
|
||||||
#include "Luau/FileUtils.h"
|
#include "Luau/FileUtils.h"
|
||||||
#include "Luau/Flags.h"
|
#include "Luau/Flags.h"
|
||||||
#include "Luau/Profiler.h"
|
#include "Luau/Profiler.h"
|
||||||
#include "Luau/ReplRequirer.h"
|
|
||||||
#include "Luau/Require.h"
|
#include "Luau/Require.h"
|
||||||
|
|
||||||
#include "isocline.h"
|
#include "isocline.h"
|
||||||
|
@ -114,6 +113,172 @@ static int lua_loadstring(lua_State* L)
|
||||||
return 2; // return nil plus error message
|
return 2; // return nil plus error message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int finishrequire(lua_State* L)
|
||||||
|
{
|
||||||
|
if (lua_isstring(L, -1))
|
||||||
|
lua_error(L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RuntimeRequireContext : public RequireResolver::RequireContext
|
||||||
|
{
|
||||||
|
// In the context of the REPL, source is the calling context's chunkname.
|
||||||
|
//
|
||||||
|
// These chunknames have certain prefixes that indicate context. These
|
||||||
|
// are used when displaying debug information (see luaO_chunkid).
|
||||||
|
//
|
||||||
|
// Generally, the '@' prefix is used for filepaths, and the '=' prefix is
|
||||||
|
// used for custom chunknames, such as =stdin.
|
||||||
|
explicit RuntimeRequireContext(std::string source)
|
||||||
|
: source(std::move(source))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getPath() override
|
||||||
|
{
|
||||||
|
return source.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRequireAllowed() override
|
||||||
|
{
|
||||||
|
return isStdin() || (!source.empty() && source[0] == '@');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isStdin() override
|
||||||
|
{
|
||||||
|
return source == "=stdin";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string createNewIdentifer(const std::string& path) override
|
||||||
|
{
|
||||||
|
return "@" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string source;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeCacheManager : public RequireResolver::CacheManager
|
||||||
|
{
|
||||||
|
explicit RuntimeCacheManager(lua_State* L)
|
||||||
|
: L(L)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCached(const std::string& path) override
|
||||||
|
{
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, path.c_str());
|
||||||
|
bool cached = !lua_isnil(L, -1);
|
||||||
|
lua_pop(L, 2);
|
||||||
|
|
||||||
|
if (cached)
|
||||||
|
cacheKey = path;
|
||||||
|
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cacheKey;
|
||||||
|
|
||||||
|
private:
|
||||||
|
lua_State* L;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeErrorHandler : RequireResolver::ErrorHandler
|
||||||
|
{
|
||||||
|
explicit RuntimeErrorHandler(lua_State* L)
|
||||||
|
: L(L)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void reportError(const std::string message) override
|
||||||
|
{
|
||||||
|
luaL_errorL(L, "%s", message.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
lua_State* L;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lua_require(lua_State* L)
|
||||||
|
{
|
||||||
|
std::string name = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
RequireResolver::ResolvedRequire resolvedRequire;
|
||||||
|
{
|
||||||
|
lua_Debug ar;
|
||||||
|
lua_getinfo(L, 1, "s", &ar);
|
||||||
|
|
||||||
|
RuntimeRequireContext requireContext{ar.source};
|
||||||
|
RuntimeCacheManager cacheManager{L};
|
||||||
|
RuntimeErrorHandler errorHandler{L};
|
||||||
|
|
||||||
|
RequireResolver resolver(std::move(name), requireContext, cacheManager, errorHandler);
|
||||||
|
|
||||||
|
resolvedRequire = resolver.resolveRequire(
|
||||||
|
[L, &cacheKey = cacheManager.cacheKey](const RequireResolver::ModuleStatus status)
|
||||||
|
{
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES");
|
||||||
|
if (status == RequireResolver::ModuleStatus::Cached)
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedRequire.status == RequireResolver::ModuleStatus::Cached)
|
||||||
|
return finishrequire(L);
|
||||||
|
|
||||||
|
// module needs to run in a new thread, isolated from the rest
|
||||||
|
// note: we create ML on main thread so that it doesn't inherit environment of L
|
||||||
|
lua_State* GL = lua_mainthread(L);
|
||||||
|
lua_State* ML = lua_newthread(GL);
|
||||||
|
lua_xmove(GL, L, 1);
|
||||||
|
|
||||||
|
// new thread needs to have the globals sandboxed
|
||||||
|
luaL_sandboxthread(ML);
|
||||||
|
|
||||||
|
// now we can compile & run module on the new thread
|
||||||
|
std::string bytecode = Luau::compile(resolvedRequire.sourceCode, copts());
|
||||||
|
if (luau_load(ML, resolvedRequire.identifier.c_str(), bytecode.data(), bytecode.size(), 0) == 0)
|
||||||
|
{
|
||||||
|
if (codegen)
|
||||||
|
{
|
||||||
|
Luau::CodeGen::CompilationOptions nativeOptions;
|
||||||
|
Luau::CodeGen::compile(ML, -1, nativeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coverageActive())
|
||||||
|
coverageTrack(ML, -1);
|
||||||
|
|
||||||
|
int status = lua_resume(ML, L, 0);
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
if (lua_gettop(ML) == 0)
|
||||||
|
lua_pushstring(ML, "module must return a value");
|
||||||
|
else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
|
||||||
|
lua_pushstring(ML, "module must return a table or function");
|
||||||
|
}
|
||||||
|
else if (status == LUA_YIELD)
|
||||||
|
{
|
||||||
|
lua_pushstring(ML, "module can not yield");
|
||||||
|
}
|
||||||
|
else if (!lua_isstring(ML, -1))
|
||||||
|
{
|
||||||
|
lua_pushstring(ML, "unknown error while running module");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's now a return value on top of ML; L stack: _MODULES ML
|
||||||
|
lua_xmove(ML, L, 1);
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setfield(L, -4, resolvedRequire.absolutePath.c_str());
|
||||||
|
|
||||||
|
// L stack: _MODULES ML result
|
||||||
|
return finishrequire(L);
|
||||||
|
}
|
||||||
|
|
||||||
static int lua_collectgarbage(lua_State* L)
|
static int lua_collectgarbage(lua_State* L)
|
||||||
{
|
{
|
||||||
const char* option = luaL_optstring(L, 1, "collect");
|
const char* option = luaL_optstring(L, 1, "collect");
|
||||||
|
@ -164,39 +329,6 @@ static int lua_callgrind(lua_State* L)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void* createCliRequireContext(lua_State* L)
|
|
||||||
{
|
|
||||||
void* ctx = lua_newuserdatadtor(
|
|
||||||
L,
|
|
||||||
sizeof(ReplRequirer),
|
|
||||||
[](void* ptr)
|
|
||||||
{
|
|
||||||
static_cast<ReplRequirer*>(ptr)->~ReplRequirer();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!ctx)
|
|
||||||
luaL_error(L, "unable to allocate ReplRequirer");
|
|
||||||
|
|
||||||
ctx = new (ctx) ReplRequirer{
|
|
||||||
copts,
|
|
||||||
coverageActive,
|
|
||||||
[]()
|
|
||||||
{
|
|
||||||
return codegen;
|
|
||||||
},
|
|
||||||
coverageTrack,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Store ReplRequirer in the registry to keep it alive for the lifetime of
|
|
||||||
// this lua_State. Memory address is used as a key to avoid collisions.
|
|
||||||
lua_pushlightuserdata(L, ctx);
|
|
||||||
lua_insert(L, -2);
|
|
||||||
lua_settable(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupState(lua_State* L)
|
void setupState(lua_State* L)
|
||||||
{
|
{
|
||||||
if (codegen)
|
if (codegen)
|
||||||
|
@ -206,6 +338,7 @@ void setupState(lua_State* L)
|
||||||
|
|
||||||
static const luaL_Reg funcs[] = {
|
static const luaL_Reg funcs[] = {
|
||||||
{"loadstring", lua_loadstring},
|
{"loadstring", lua_loadstring},
|
||||||
|
{"require", lua_require},
|
||||||
{"collectgarbage", lua_collectgarbage},
|
{"collectgarbage", lua_collectgarbage},
|
||||||
#ifdef CALLGRIND
|
#ifdef CALLGRIND
|
||||||
{"callgrind", lua_callgrind},
|
{"callgrind", lua_callgrind},
|
||||||
|
@ -217,8 +350,6 @@ void setupState(lua_State* L)
|
||||||
luaL_register(L, NULL, funcs);
|
luaL_register(L, NULL, funcs);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
luaopen_require(L, requireConfigInit, createCliRequireContext(L));
|
|
||||||
|
|
||||||
luaL_sandbox(L);
|
luaL_sandbox(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,14 +712,7 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
|
||||||
// new thread needs to have the globals sandboxed
|
// new thread needs to have the globals sandboxed
|
||||||
luaL_sandboxthread(L);
|
luaL_sandboxthread(L);
|
||||||
|
|
||||||
// ignore file extension when storing module's chunkname
|
std::string chunkname = "@" + std::string(name);
|
||||||
std::string chunkname = "@";
|
|
||||||
std::string_view nameView = name;
|
|
||||||
if (size_t dotPos = nameView.find_last_of('.'); dotPos != std::string_view::npos)
|
|
||||||
{
|
|
||||||
nameView.remove_suffix(nameView.size() - dotPos);
|
|
||||||
}
|
|
||||||
chunkname += nameView;
|
|
||||||
|
|
||||||
std::string bytecode = Luau::compile(*source, copts());
|
std::string bytecode = Luau::compile(*source, copts());
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
|
@ -1,221 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#include "Luau/ReplRequirer.h"
|
|
||||||
|
|
||||||
#include "Luau/CodeGen.h"
|
|
||||||
#include "Luau/CodeGenOptions.h"
|
|
||||||
#include "Luau/Require.h"
|
|
||||||
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
static luarequire_WriteResult write(std::optional<std::string> contents, char* buffer, size_t bufferSize, size_t* sizeOut)
|
|
||||||
{
|
|
||||||
if (!contents)
|
|
||||||
return luarequire_WriteResult::WRITE_FAILURE;
|
|
||||||
|
|
||||||
size_t nullTerminatedSize = contents->size() + 1;
|
|
||||||
|
|
||||||
if (bufferSize < nullTerminatedSize)
|
|
||||||
{
|
|
||||||
*sizeOut = nullTerminatedSize;
|
|
||||||
return luarequire_WriteResult::WRITE_BUFFER_TOO_SMALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*sizeOut = nullTerminatedSize;
|
|
||||||
memcpy(buffer, contents->c_str(), nullTerminatedSize);
|
|
||||||
return luarequire_WriteResult::WRITE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_NavigateResult storePathResult(ReplRequirer* req, PathResult result)
|
|
||||||
{
|
|
||||||
if (result.status == PathResult::Status::AMBIGUOUS)
|
|
||||||
return NAVIGATE_AMBIGUOUS;
|
|
||||||
|
|
||||||
if (result.status == PathResult::Status::NOT_FOUND)
|
|
||||||
return NAVIGATE_NOT_FOUND;
|
|
||||||
|
|
||||||
req->absPath = result.absPath;
|
|
||||||
req->relPath = result.relPath;
|
|
||||||
req->suffix = result.suffix;
|
|
||||||
|
|
||||||
return NAVIGATE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_require_allowed(lua_State* L, void* ctx, const char* requirer_chunkname)
|
|
||||||
{
|
|
||||||
std::string_view chunkname = requirer_chunkname;
|
|
||||||
return chunkname == "=stdin" || (!chunkname.empty() && chunkname[0] == '@');
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_NavigateResult reset(lua_State* L, void* ctx, const char* requirer_chunkname)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
|
|
||||||
std::string chunkname = requirer_chunkname;
|
|
||||||
if (chunkname == "=stdin")
|
|
||||||
{
|
|
||||||
return storePathResult(req, getStdInResult());
|
|
||||||
}
|
|
||||||
else if (!chunkname.empty() && chunkname[0] == '@')
|
|
||||||
{
|
|
||||||
return storePathResult(req, tryGetRelativePathResult(chunkname.substr(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NAVIGATE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_NavigateResult jump_to_alias(lua_State* L, void* ctx, const char* path)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
|
|
||||||
luarequire_NavigateResult result = storePathResult(req, getAbsolutePathResult(path));
|
|
||||||
if (result != NAVIGATE_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
// Jumping to an absolute path breaks the relative-require chain. The best
|
|
||||||
// we can do is to store the absolute path itself.
|
|
||||||
req->relPath = req->absPath;
|
|
||||||
return NAVIGATE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_NavigateResult to_parent(lua_State* L, void* ctx)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return storePathResult(req, getParent(req->absPath, req->relPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_NavigateResult to_child(lua_State* L, void* ctx, const char* name)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return storePathResult(req, getChild(req->absPath, req->relPath, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_module_present(lua_State* L, void* ctx)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return isFilePresent(req->absPath, req->suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_WriteResult get_contents(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return write(getFileContents(req->absPath, req->suffix), buffer, buffer_size, size_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_WriteResult get_chunkname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return write("@" + req->relPath, buffer, buffer_size, size_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_WriteResult get_cache_key(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return write(req->absPath + req->suffix, buffer, buffer_size, size_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_config_present(lua_State* L, void* ctx)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return isFilePresent(req->absPath, "/.luaurc");
|
|
||||||
}
|
|
||||||
|
|
||||||
static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
return write(getFileContents(req->absPath, "/.luaurc"), buffer, buffer_size, size_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int load(lua_State* L, void* ctx, const char* chunkname, const char* contents)
|
|
||||||
{
|
|
||||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
|
||||||
|
|
||||||
// module needs to run in a new thread, isolated from the rest
|
|
||||||
// note: we create ML on main thread so that it doesn't inherit environment of L
|
|
||||||
lua_State* GL = lua_mainthread(L);
|
|
||||||
lua_State* ML = lua_newthread(GL);
|
|
||||||
lua_xmove(GL, L, 1);
|
|
||||||
|
|
||||||
// new thread needs to have the globals sandboxed
|
|
||||||
luaL_sandboxthread(ML);
|
|
||||||
|
|
||||||
// now we can compile & run module on the new thread
|
|
||||||
std::string bytecode = Luau::compile(contents, req->copts());
|
|
||||||
if (luau_load(ML, chunkname, bytecode.data(), bytecode.size(), 0) == 0)
|
|
||||||
{
|
|
||||||
if (req->codegenEnabled())
|
|
||||||
{
|
|
||||||
Luau::CodeGen::CompilationOptions nativeOptions;
|
|
||||||
Luau::CodeGen::compile(ML, -1, nativeOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req->coverageActive())
|
|
||||||
req->coverageTrack(ML, -1);
|
|
||||||
|
|
||||||
int status = lua_resume(ML, L, 0);
|
|
||||||
|
|
||||||
if (status == 0)
|
|
||||||
{
|
|
||||||
if (lua_gettop(ML) == 0)
|
|
||||||
lua_pushstring(ML, "module must return a value");
|
|
||||||
else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
|
|
||||||
lua_pushstring(ML, "module must return a table or function");
|
|
||||||
}
|
|
||||||
else if (status == LUA_YIELD)
|
|
||||||
{
|
|
||||||
lua_pushstring(ML, "module can not yield");
|
|
||||||
}
|
|
||||||
else if (!lua_isstring(ML, -1))
|
|
||||||
{
|
|
||||||
lua_pushstring(ML, "unknown error while running module");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add ML result to L stack
|
|
||||||
lua_xmove(ML, L, 1);
|
|
||||||
if (lua_isstring(L, -1))
|
|
||||||
lua_error(L);
|
|
||||||
|
|
||||||
// remove ML thread from L stack
|
|
||||||
lua_remove(L, -2);
|
|
||||||
|
|
||||||
// added one value to L stack: module result
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void requireConfigInit(luarequire_Configuration* config)
|
|
||||||
{
|
|
||||||
if (config == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
config->is_require_allowed = is_require_allowed;
|
|
||||||
config->reset = reset;
|
|
||||||
config->jump_to_alias = jump_to_alias;
|
|
||||||
config->to_parent = to_parent;
|
|
||||||
config->to_child = to_child;
|
|
||||||
config->is_module_present = is_module_present;
|
|
||||||
config->get_contents = get_contents;
|
|
||||||
config->is_config_present = is_config_present;
|
|
||||||
config->get_chunkname = get_chunkname;
|
|
||||||
config->get_cache_key = get_cache_key;
|
|
||||||
config->get_config = get_config;
|
|
||||||
config->load = load;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplRequirer::ReplRequirer(
|
|
||||||
std::function<Luau::CompileOptions()> copts,
|
|
||||||
std::function<bool()> coverageActive,
|
|
||||||
std::function<bool()> codegenEnabled,
|
|
||||||
std::function<void(lua_State*, int)> coverageTrack
|
|
||||||
)
|
|
||||||
: copts(std::move(copts))
|
|
||||||
, coverageActive(std::move(coverageActive))
|
|
||||||
, codegenEnabled(std::move(codegenEnabled))
|
|
||||||
, coverageTrack(std::move(coverageTrack))
|
|
||||||
{
|
|
||||||
}
|
|
313
CLI/src/Require.cpp
Normal file
313
CLI/src/Require.cpp
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include "Luau/Require.h"
|
||||||
|
|
||||||
|
#include "Luau/FileUtils.h"
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/Config.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
static constexpr char kRequireErrorGeneric[] = "error requiring module";
|
||||||
|
|
||||||
|
RequireResolver::RequireResolver(std::string path, RequireContext& requireContext, CacheManager& cacheManager, ErrorHandler& errorHandler)
|
||||||
|
: pathToResolve(std::move(path))
|
||||||
|
, requireContext(requireContext)
|
||||||
|
, cacheManager(cacheManager)
|
||||||
|
, errorHandler(errorHandler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RequireResolver::ResolvedRequire RequireResolver::resolveRequire(std::function<void(const ModuleStatus)> completionCallback)
|
||||||
|
{
|
||||||
|
if (isRequireResolved)
|
||||||
|
{
|
||||||
|
errorHandler.reportError("require statement has already been resolved");
|
||||||
|
return ResolvedRequire{ModuleStatus::ErrorReported};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialize())
|
||||||
|
return ResolvedRequire{ModuleStatus::ErrorReported};
|
||||||
|
|
||||||
|
resolvedRequire.status = findModule();
|
||||||
|
|
||||||
|
if (completionCallback)
|
||||||
|
completionCallback(resolvedRequire.status);
|
||||||
|
|
||||||
|
isRequireResolved = true;
|
||||||
|
return resolvedRequire;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasValidPrefix(std::string_view path)
|
||||||
|
{
|
||||||
|
return path.compare(0, 2, "./") == 0 || path.compare(0, 3, "../") == 0 || path.compare(0, 1, "@") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isPathAmbiguous(const std::string& path)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (const char* suffix : {".luau", ".lua"})
|
||||||
|
{
|
||||||
|
if (isFile(path + suffix))
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDirectory(path) && found)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::initialize()
|
||||||
|
{
|
||||||
|
if (!requireContext.isRequireAllowed())
|
||||||
|
{
|
||||||
|
errorHandler.reportError("require is not supported in this context");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAbsolutePath(pathToResolve))
|
||||||
|
{
|
||||||
|
errorHandler.reportError("cannot require an absolute path");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::replace(pathToResolve.begin(), pathToResolve.end(), '\\', '/');
|
||||||
|
|
||||||
|
if (!hasValidPrefix(pathToResolve))
|
||||||
|
{
|
||||||
|
errorHandler.reportError("require path must start with a valid prefix: ./, ../, or @");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substituteAliasIfPresent(pathToResolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequireResolver::ModuleStatus RequireResolver::findModule()
|
||||||
|
{
|
||||||
|
if (!resolveAndStoreDefaultPaths())
|
||||||
|
return ModuleStatus::ErrorReported;
|
||||||
|
|
||||||
|
if (isPathAmbiguous(resolvedRequire.absolutePath))
|
||||||
|
{
|
||||||
|
errorHandler.reportError("require path could not be resolved to a unique file");
|
||||||
|
return ModuleStatus::ErrorReported;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<const char*, 4> possibleSuffixes = {".luau", ".lua", "/init.luau", "/init.lua"};
|
||||||
|
size_t unsuffixedAbsolutePathSize = resolvedRequire.absolutePath.size();
|
||||||
|
|
||||||
|
for (const char* possibleSuffix : possibleSuffixes)
|
||||||
|
{
|
||||||
|
resolvedRequire.absolutePath += possibleSuffix;
|
||||||
|
|
||||||
|
if (cacheManager.isCached(resolvedRequire.absolutePath))
|
||||||
|
return ModuleStatus::Cached;
|
||||||
|
|
||||||
|
// Try to read the matching file
|
||||||
|
if (std::optional<std::string> source = readFile(resolvedRequire.absolutePath))
|
||||||
|
{
|
||||||
|
resolvedRequire.identifier = requireContext.createNewIdentifer(resolvedRequire.identifier + possibleSuffix);
|
||||||
|
resolvedRequire.sourceCode = *source;
|
||||||
|
return ModuleStatus::FileRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedRequire.absolutePath.resize(unsuffixedAbsolutePathSize); // truncate to remove suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasFileExtension(resolvedRequire.absolutePath, {".luau", ".lua"}) && isFile(resolvedRequire.absolutePath))
|
||||||
|
{
|
||||||
|
errorHandler.reportError("error requiring module: consider removing the file extension");
|
||||||
|
return ModuleStatus::ErrorReported;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorHandler.reportError(kRequireErrorGeneric);
|
||||||
|
return ModuleStatus::ErrorReported;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::resolveAndStoreDefaultPaths()
|
||||||
|
{
|
||||||
|
if (!isAbsolutePath(pathToResolve))
|
||||||
|
{
|
||||||
|
std::string identifierContext = getRequiringContextRelative();
|
||||||
|
std::optional<std::string> absolutePathContext = getRequiringContextAbsolute();
|
||||||
|
|
||||||
|
if (!absolutePathContext)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// resolvePath automatically sanitizes/normalizes the paths
|
||||||
|
std::optional<std::string> identifier = resolvePath(pathToResolve, identifierContext);
|
||||||
|
std::optional<std::string> absolutePath = resolvePath(pathToResolve, *absolutePathContext);
|
||||||
|
|
||||||
|
if (!identifier || !absolutePath)
|
||||||
|
{
|
||||||
|
errorHandler.reportError("could not resolve require path");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedRequire.identifier = std::move(*identifier);
|
||||||
|
resolvedRequire.absolutePath = std::move(*absolutePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Here we must explicitly sanitize, as the path is taken as is
|
||||||
|
std::string sanitizedPath = normalizePath(pathToResolve);
|
||||||
|
resolvedRequire.identifier = sanitizedPath;
|
||||||
|
resolvedRequire.absolutePath = std::move(sanitizedPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> RequireResolver::getRequiringContextAbsolute()
|
||||||
|
{
|
||||||
|
std::string requiringFile;
|
||||||
|
if (isAbsolutePath(requireContext.getPath()))
|
||||||
|
{
|
||||||
|
// We already have an absolute path for the requiring file
|
||||||
|
requiringFile = requireContext.getPath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Requiring file's stored path is relative to the CWD, must make absolute
|
||||||
|
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
||||||
|
if (!cwd)
|
||||||
|
{
|
||||||
|
errorHandler.reportError("could not determine current working directory");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireContext.isStdin())
|
||||||
|
{
|
||||||
|
// Require statement is being executed from REPL input prompt
|
||||||
|
// The requiring context is the pseudo-file "stdin" in the CWD
|
||||||
|
requiringFile = joinPaths(*cwd, "stdin");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Require statement is being executed in a file, must resolve relative to CWD
|
||||||
|
requiringFile = normalizePath(joinPaths(*cwd, requireContext.getPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::replace(requiringFile.begin(), requiringFile.end(), '\\', '/');
|
||||||
|
return requiringFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequireResolver::getRequiringContextRelative()
|
||||||
|
{
|
||||||
|
return requireContext.isStdin() ? "./" : requireContext.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::substituteAliasIfPresent(std::string& path)
|
||||||
|
{
|
||||||
|
if (path.size() < 1 || path[0] != '@')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// To ignore the '@' alias prefix when processing the alias
|
||||||
|
const size_t aliasStartPos = 1;
|
||||||
|
|
||||||
|
// If a directory separator was found, the length of the alias is the
|
||||||
|
// distance between the start of the alias and the separator. Otherwise,
|
||||||
|
// the whole string after the alias symbol is the alias.
|
||||||
|
size_t aliasLen = path.find_first_of("\\/");
|
||||||
|
if (aliasLen != std::string::npos)
|
||||||
|
aliasLen -= aliasStartPos;
|
||||||
|
|
||||||
|
const std::string potentialAlias = path.substr(aliasStartPos, aliasLen);
|
||||||
|
|
||||||
|
// Not worth searching when potentialAlias cannot be an alias
|
||||||
|
if (!Luau::isValidAlias(potentialAlias))
|
||||||
|
{
|
||||||
|
errorHandler.reportError("@" + potentialAlias + " is not a valid alias");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::optional<std::string> alias = getAlias(potentialAlias))
|
||||||
|
{
|
||||||
|
path = *alias + path.substr(potentialAlias.size() + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorHandler.reportError("@" + potentialAlias + " is not a valid alias");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> RequireResolver::getAlias(std::string alias)
|
||||||
|
{
|
||||||
|
std::transform(
|
||||||
|
alias.begin(),
|
||||||
|
alias.end(),
|
||||||
|
alias.begin(),
|
||||||
|
[](unsigned char c)
|
||||||
|
{
|
||||||
|
return ('A' <= c && c <= 'Z') ? (c + ('a' - 'A')) : c;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
while (!config.aliases.contains(alias) && !isConfigFullyResolved)
|
||||||
|
{
|
||||||
|
if (!parseNextConfig())
|
||||||
|
return std::nullopt; // error parsing config
|
||||||
|
}
|
||||||
|
if (!config.aliases.contains(alias) && isConfigFullyResolved)
|
||||||
|
return std::nullopt; // could not find alias
|
||||||
|
|
||||||
|
const Luau::Config::AliasInfo& aliasInfo = config.aliases[alias];
|
||||||
|
return resolvePath(aliasInfo.value, aliasInfo.configLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::parseNextConfig()
|
||||||
|
{
|
||||||
|
if (isConfigFullyResolved)
|
||||||
|
return true; // no config files left to parse
|
||||||
|
|
||||||
|
std::optional<std::string> directory;
|
||||||
|
if (lastSearchedDir.empty())
|
||||||
|
{
|
||||||
|
std::optional<std::string> requiringFile = getRequiringContextAbsolute();
|
||||||
|
if (!requiringFile)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
directory = getParentPath(*requiringFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
directory = getParentPath(lastSearchedDir);
|
||||||
|
|
||||||
|
if (directory)
|
||||||
|
{
|
||||||
|
lastSearchedDir = *directory;
|
||||||
|
if (!parseConfigInDirectory(*directory))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isConfigFullyResolved = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireResolver::parseConfigInDirectory(const std::string& directory)
|
||||||
|
{
|
||||||
|
std::string configPath = joinPaths(directory, Luau::kConfigName);
|
||||||
|
|
||||||
|
Luau::ConfigOptions::AliasOptions aliasOpts;
|
||||||
|
aliasOpts.configLocation = configPath;
|
||||||
|
aliasOpts.overwriteAliases = false;
|
||||||
|
|
||||||
|
Luau::ConfigOptions opts;
|
||||||
|
opts.aliasOptions = std::move(aliasOpts);
|
||||||
|
|
||||||
|
if (std::optional<std::string> contents = readFile(configPath))
|
||||||
|
{
|
||||||
|
std::optional<std::string> error = Luau::parseConfig(*contents, config, opts);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
errorHandler.reportError("error parsing " + configPath + "(" + *error + ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -1,119 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#include "Luau/RequirerUtils.h"
|
|
||||||
|
|
||||||
#include "Luau/FileUtils.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
static std::pair<PathResult::Status, std::string> getSuffixWithAmbiguityCheck(const std::string& path)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
std::string suffix;
|
|
||||||
|
|
||||||
for (const char* potentialSuffix : {".luau", ".lua"})
|
|
||||||
{
|
|
||||||
if (isFile(path + potentialSuffix))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
suffix = potentialSuffix;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isDirectory(path))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
for (const char* potentialSuffix : {"/init.luau", "/init.lua"})
|
|
||||||
{
|
|
||||||
if (isFile(path + potentialSuffix))
|
|
||||||
{
|
|
||||||
if (found)
|
|
||||||
return {PathResult::Status::AMBIGUOUS, ""};
|
|
||||||
|
|
||||||
suffix = potentialSuffix;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return {PathResult::Status::NOT_FOUND, ""};
|
|
||||||
|
|
||||||
return {PathResult::Status::SUCCESS, suffix};
|
|
||||||
}
|
|
||||||
|
|
||||||
static PathResult addSuffix(PathResult partialResult)
|
|
||||||
{
|
|
||||||
if (partialResult.status != PathResult::Status::SUCCESS)
|
|
||||||
return partialResult;
|
|
||||||
|
|
||||||
auto [status, suffix] = getSuffixWithAmbiguityCheck(partialResult.absPath);
|
|
||||||
if (status != PathResult::Status::SUCCESS)
|
|
||||||
return PathResult{status};
|
|
||||||
|
|
||||||
partialResult.suffix = std::move(suffix);
|
|
||||||
return partialResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getStdInResult()
|
|
||||||
{
|
|
||||||
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
|
||||||
if (!cwd)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
std::replace(cwd->begin(), cwd->end(), '\\', '/');
|
|
||||||
|
|
||||||
return PathResult{PathResult::Status::SUCCESS, *cwd + "/stdin", "./stdin", ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getAbsolutePathResult(const std::string& path)
|
|
||||||
{
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, path});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult tryGetRelativePathResult(const std::string& path)
|
|
||||||
{
|
|
||||||
if (isAbsolutePath(path))
|
|
||||||
return getAbsolutePathResult(path);
|
|
||||||
|
|
||||||
std::optional<std::string> cwd = getCurrentWorkingDirectory();
|
|
||||||
if (!cwd)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
std::optional<std::string> resolvedAbsPath = resolvePath(path, *cwd + "/stdin");
|
|
||||||
if (!resolvedAbsPath)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, std::move(*resolvedAbsPath), path});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getParent(const std::string& absPath, const std::string& relPath)
|
|
||||||
{
|
|
||||||
std::optional<std::string> parent = getParentPath(absPath);
|
|
||||||
if (!parent)
|
|
||||||
return PathResult{PathResult::Status::NOT_FOUND};
|
|
||||||
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, *parent, normalizePath(relPath + "/..")});
|
|
||||||
}
|
|
||||||
|
|
||||||
PathResult getChild(const std::string& absPath, const std::string& relPath, const std::string& name)
|
|
||||||
{
|
|
||||||
return addSuffix(PathResult{PathResult::Status::SUCCESS, joinPaths(absPath, name), joinPaths(relPath, name)});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFilePresent(const std::string& path, const std::string& suffix)
|
|
||||||
{
|
|
||||||
return isFile(path + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> getFileContents(const std::string& path, const std::string& suffix)
|
|
||||||
{
|
|
||||||
return readFile(path + suffix);
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ if(EXT_PLATFORM_STRING)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
option(LUAU_BUILD_CLI "Build CLI" ON)
|
option(LUAU_BUILD_CLI "Build CLI" ON)
|
||||||
option(LUAU_BUILD_TESTS "Build tests" ON)
|
option(LUAU_BUILD_TESTS "Build tests" ON)
|
||||||
|
@ -31,8 +31,6 @@ add_library(Luau.Analysis STATIC)
|
||||||
add_library(Luau.EqSat STATIC)
|
add_library(Luau.EqSat STATIC)
|
||||||
add_library(Luau.CodeGen STATIC)
|
add_library(Luau.CodeGen STATIC)
|
||||||
add_library(Luau.VM STATIC)
|
add_library(Luau.VM STATIC)
|
||||||
add_library(Luau.Require STATIC)
|
|
||||||
add_library(Luau.RequireNavigator STATIC)
|
|
||||||
add_library(isocline STATIC)
|
add_library(isocline STATIC)
|
||||||
|
|
||||||
if(LUAU_BUILD_CLI)
|
if(LUAU_BUILD_CLI)
|
||||||
|
@ -103,15 +101,6 @@ target_compile_features(Luau.VM PRIVATE cxx_std_11)
|
||||||
target_include_directories(Luau.VM PUBLIC VM/include)
|
target_include_directories(Luau.VM PUBLIC VM/include)
|
||||||
target_link_libraries(Luau.VM PUBLIC Luau.Common)
|
target_link_libraries(Luau.VM PUBLIC Luau.Common)
|
||||||
|
|
||||||
target_compile_features(Luau.Require PUBLIC cxx_std_17)
|
|
||||||
target_include_directories(Luau.Require PUBLIC Require/Runtime/include)
|
|
||||||
target_link_libraries(Luau.Require PUBLIC Luau.VM)
|
|
||||||
target_link_libraries(Luau.Require PRIVATE Luau.RequireNavigator)
|
|
||||||
|
|
||||||
target_compile_features(Luau.RequireNavigator PUBLIC cxx_std_17)
|
|
||||||
target_include_directories(Luau.RequireNavigator PUBLIC Require/Navigator/include)
|
|
||||||
target_link_libraries(Luau.RequireNavigator PUBLIC Luau.Config)
|
|
||||||
|
|
||||||
target_include_directories(isocline PUBLIC extern/isocline/include)
|
target_include_directories(isocline PUBLIC extern/isocline/include)
|
||||||
|
|
||||||
target_include_directories(Luau.VM.Internals INTERFACE VM/src)
|
target_include_directories(Luau.VM.Internals INTERFACE VM/src)
|
||||||
|
@ -226,12 +215,12 @@ if(LUAU_BUILD_CLI)
|
||||||
|
|
||||||
target_include_directories(Luau.Repl.CLI PRIVATE extern extern/isocline/include)
|
target_include_directories(Luau.Repl.CLI PRIVATE extern extern/isocline/include)
|
||||||
|
|
||||||
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.Config Luau.CodeGen Luau.VM Luau.Require Luau.CLI.lib isocline)
|
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.Config Luau.CodeGen Luau.VM Luau.CLI.lib isocline)
|
||||||
|
|
||||||
target_link_libraries(Luau.Repl.CLI PRIVATE osthreads)
|
target_link_libraries(Luau.Repl.CLI PRIVATE osthreads)
|
||||||
target_link_libraries(Luau.Analyze.CLI PRIVATE osthreads)
|
target_link_libraries(Luau.Analyze.CLI PRIVATE osthreads)
|
||||||
|
|
||||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis Luau.CLI.lib Luau.RequireNavigator)
|
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis Luau.CLI.lib)
|
||||||
|
|
||||||
target_link_libraries(Luau.Ast.CLI PRIVATE Luau.Ast Luau.Analysis Luau.CLI.lib)
|
target_link_libraries(Luau.Ast.CLI PRIVATE Luau.Ast Luau.Analysis Luau.CLI.lib)
|
||||||
|
|
||||||
|
@ -263,7 +252,7 @@ if(LUAU_BUILD_TESTS)
|
||||||
|
|
||||||
target_compile_options(Luau.CLI.Test PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.CLI.Test PRIVATE ${LUAU_OPTIONS})
|
||||||
target_include_directories(Luau.CLI.Test PRIVATE extern CLI)
|
target_include_directories(Luau.CLI.Test PRIVATE extern CLI)
|
||||||
target_link_libraries(Luau.CLI.Test PRIVATE Luau.Compiler Luau.Config Luau.CodeGen Luau.VM Luau.Require Luau.CLI.lib isocline)
|
target_link_libraries(Luau.CLI.Test PRIVATE Luau.Compiler Luau.Config Luau.CodeGen Luau.VM Luau.CLI.lib isocline)
|
||||||
target_link_libraries(Luau.CLI.Test PRIVATE osthreads)
|
target_link_libraries(Luau.CLI.Test PRIVATE osthreads)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauPopIncompleteCi)
|
||||||
|
|
||||||
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
||||||
// This makes sure that we save the pc (in case the Lua call needs to generate a backtrace) before the call,
|
// This makes sure that we save the pc (in case the Lua call needs to generate a backtrace) before the call,
|
||||||
// and restores the stack pointer after in case stack gets reallocated
|
// and restores the stack pointer after in case stack gets reallocated
|
||||||
|
@ -191,7 +193,14 @@ Closure* callProlog(lua_State* L, TValue* ra, StkId argtop, int nresults)
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
luaD_checkstackfornewci(L, ccl->stacksize);
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
return ccl;
|
return ccl;
|
||||||
}
|
}
|
||||||
|
@ -261,7 +270,14 @@ Closure* callFallback(lua_State* L, StkId ra, StkId argtop, int nresults)
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
luaD_checkstackfornewci(L, ccl->stacksize);
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019-2025 Roblox Corporation
|
Copyright (c) 2019-2024 Roblox Corporation
|
||||||
Copyright (c) 1994–2019 Lua.org, PUC-Rio.
|
Copyright (c) 1994–2019 Lua.org, PUC-Rio.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
|
34
Makefile
34
Makefile
|
@ -38,27 +38,19 @@ VM_SOURCES=$(wildcard VM/src/*.cpp)
|
||||||
VM_OBJECTS=$(VM_SOURCES:%=$(BUILD)/%.o)
|
VM_OBJECTS=$(VM_SOURCES:%=$(BUILD)/%.o)
|
||||||
VM_TARGET=$(BUILD)/libluauvm.a
|
VM_TARGET=$(BUILD)/libluauvm.a
|
||||||
|
|
||||||
REQUIRE_SOURCES=$(wildcard Require/Runtime/src/*.cpp)
|
|
||||||
REQUIRE_OBJECTS=$(REQUIRE_SOURCES:%=$(BUILD)/%.o)
|
|
||||||
REQUIRE_TARGET=$(BUILD)/libluaurequire.a
|
|
||||||
|
|
||||||
REQUIRENAVIGATOR_SOURCES=$(wildcard Require/Navigator/src/*.cpp)
|
|
||||||
REQUIRENAVIGATOR_OBJECTS=$(REQUIRENAVIGATOR_SOURCES:%=$(BUILD)/%.o)
|
|
||||||
REQUIRENAVIGATOR_TARGET=$(BUILD)/libluaurequirenavigator.a
|
|
||||||
|
|
||||||
ISOCLINE_SOURCES=extern/isocline/src/isocline.c
|
ISOCLINE_SOURCES=extern/isocline/src/isocline.c
|
||||||
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
|
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
|
||||||
ISOCLINE_TARGET=$(BUILD)/libisocline.a
|
ISOCLINE_TARGET=$(BUILD)/libisocline.a
|
||||||
|
|
||||||
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplRequirer.cpp CLI/src/RequirerUtils.cpp
|
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/Require.cpp
|
||||||
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
|
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
|
||||||
TESTS_TARGET=$(BUILD)/luau-tests
|
TESTS_TARGET=$(BUILD)/luau-tests
|
||||||
|
|
||||||
REPL_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplEntry.cpp CLI/src/ReplRequirer.cpp CLI/src/RequirerUtils.cpp
|
REPL_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Profiler.cpp CLI/src/Coverage.cpp CLI/src/Repl.cpp CLI/src/ReplEntry.cpp CLI/src/Require.cpp
|
||||||
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
|
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
|
||||||
REPL_CLI_TARGET=$(BUILD)/luau
|
REPL_CLI_TARGET=$(BUILD)/luau
|
||||||
|
|
||||||
ANALYZE_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Analyze.cpp CLI/src/AnalyzeRequirer.cpp CLI/src/RequirerUtils.cpp
|
ANALYZE_CLI_SOURCES=CLI/src/FileUtils.cpp CLI/src/Flags.cpp CLI/src/Require.cpp CLI/src/Analyze.cpp
|
||||||
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
|
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
|
||||||
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
|
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
|
||||||
|
|
||||||
|
@ -81,7 +73,7 @@ ifneq ($(opt),)
|
||||||
TESTS_ARGS+=-O$(opt)
|
TESTS_ARGS+=-O$(opt)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(CONFIG_OBJECTS) $(ANALYSIS_OBJECTS) $(EQSAT_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(REQUIRE_OBJECTS) $(REQUIRENAVIGATOR_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(REPL_CLI_OBJECTS) $(ANALYZE_CLI_OBJECTS) $(COMPILE_CLI_OBJECTS) $(BYTECODE_CLI_OBJECTS) $(FUZZ_OBJECTS)
|
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(CONFIG_OBJECTS) $(ANALYSIS_OBJECTS) $(EQSAT_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(REPL_CLI_OBJECTS) $(ANALYZE_CLI_OBJECTS) $(COMPILE_CLI_OBJECTS) $(BYTECODE_CLI_OBJECTS) $(FUZZ_OBJECTS)
|
||||||
EXECUTABLE_ALIASES = luau luau-analyze luau-compile luau-bytecode luau-tests
|
EXECUTABLE_ALIASES = luau luau-analyze luau-compile luau-bytecode luau-tests
|
||||||
|
|
||||||
# common flags
|
# common flags
|
||||||
|
@ -156,12 +148,10 @@ $(ANALYSIS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnaly
|
||||||
$(EQSAT_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IEqSat/include
|
$(EQSAT_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IEqSat/include
|
||||||
$(CODEGEN_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -ICodeGen/include -IVM/include -IVM/src # Code generation needs VM internals
|
$(CODEGEN_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -ICodeGen/include -IVM/include -IVM/src # Code generation needs VM internals
|
||||||
$(VM_OBJECTS): CXXFLAGS+=-std=c++11 -ICommon/include -IVM/include
|
$(VM_OBJECTS): CXXFLAGS+=-std=c++11 -ICommon/include -IVM/include
|
||||||
$(REQUIRE_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IVM/include -IAst/include -IConfig/include -IRequire/Navigator/include -IRequire/Runtime/include
|
|
||||||
$(REQUIRENAVIGATOR_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IConfig/include -IRequire/Navigator/include
|
|
||||||
$(ISOCLINE_OBJECTS): CXXFLAGS+=-Wno-unused-function -Iextern/isocline/include
|
$(ISOCLINE_OBJECTS): CXXFLAGS+=-Wno-unused-function -Iextern/isocline/include
|
||||||
$(TESTS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IConfig/include -IAnalysis/include -IEqSat/include -ICodeGen/include -IVM/include -IRequire/Runtime/include -ICLI/include -Iextern -DDOCTEST_CONFIG_DOUBLE_STRINGIFY
|
$(TESTS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IConfig/include -IAnalysis/include -IEqSat/include -ICodeGen/include -IVM/include -ICLI/include -Iextern -DDOCTEST_CONFIG_DOUBLE_STRINGIFY
|
||||||
$(REPL_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -IRequire/Runtime/include -Iextern -Iextern/isocline/include -ICLI/include
|
$(REPL_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -Iextern -Iextern/isocline/include -ICLI/include
|
||||||
$(ANALYZE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IEqSat/include -IConfig/include -IRequire/Navigator/include -Iextern -ICLI/include
|
$(ANALYZE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IEqSat/include -IConfig/include -Iextern -ICLI/include
|
||||||
$(COMPILE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -ICLI/include
|
$(COMPILE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -ICLI/include
|
||||||
$(BYTECODE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -ICLI/include
|
$(BYTECODE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -ICLI/include
|
||||||
$(FUZZ_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IAnalysis/include -IEqSat/include -IVM/include -ICodeGen/include -IConfig/include
|
$(FUZZ_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IAnalysis/include -IEqSat/include -IVM/include -ICodeGen/include -IConfig/include
|
||||||
|
@ -237,9 +227,9 @@ luau-tests: $(TESTS_TARGET)
|
||||||
ln -fs $^ $@
|
ln -fs $^ $@
|
||||||
|
|
||||||
# executable targets
|
# executable targets
|
||||||
$(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(REQUIRE_TARGET) $(REQUIRENAVIGATOR_TARGET) $(CONFIG_TARGET) $(ISOCLINE_TARGET)
|
$(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
||||||
$(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(REQUIRE_TARGET) $(REQUIRENAVIGATOR_TARGET) $(CONFIG_TARGET) $(ISOCLINE_TARGET)
|
$(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
||||||
$(ANALYZE_CLI_TARGET): $(ANALYZE_CLI_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(AST_TARGET) $(COMPILER_TARGET) $(VM_TARGET) $(REQUIRENAVIGATOR_TARGET) $(CONFIG_TARGET)
|
$(ANALYZE_CLI_TARGET): $(ANALYZE_CLI_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(COMPILER_TARGET) $(VM_TARGET)
|
||||||
$(COMPILE_CLI_TARGET): $(COMPILE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
$(COMPILE_CLI_TARGET): $(COMPILE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
||||||
$(BYTECODE_CLI_TARGET): $(BYTECODE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
$(BYTECODE_CLI_TARGET): $(BYTECODE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
||||||
|
|
||||||
|
@ -261,11 +251,9 @@ $(ANALYSIS_TARGET): $(ANALYSIS_OBJECTS)
|
||||||
$(EQSAT_TARGET): $(EQSAT_OBJECTS)
|
$(EQSAT_TARGET): $(EQSAT_OBJECTS)
|
||||||
$(CODEGEN_TARGET): $(CODEGEN_OBJECTS)
|
$(CODEGEN_TARGET): $(CODEGEN_OBJECTS)
|
||||||
$(VM_TARGET): $(VM_OBJECTS)
|
$(VM_TARGET): $(VM_OBJECTS)
|
||||||
$(REQUIRE_TARGET): $(REQUIRE_OBJECTS)
|
|
||||||
$(REQUIRENAVIGATOR_TARGET): $(REQUIRENAVIGATOR_OBJECTS)
|
|
||||||
$(ISOCLINE_TARGET): $(ISOCLINE_OBJECTS)
|
$(ISOCLINE_TARGET): $(ISOCLINE_OBJECTS)
|
||||||
|
|
||||||
$(AST_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(REQUIRE_TARGET) $(REQUIRENAVIGATOR_TARGET) $(ISOCLINE_TARGET):
|
$(AST_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET):
|
||||||
ar rcs $@ $^
|
ar rcs $@ $^
|
||||||
|
|
||||||
# object file targets
|
# object file targets
|
||||||
|
|
|
@ -24,7 +24,7 @@ You can install and run Luau by downloading the compiled binaries from [a recent
|
||||||
Alternatively, you can use one of the packaged distributions (note that these are not maintained by Luau development team):
|
Alternatively, you can use one of the packaged distributions (note that these are not maintained by Luau development team):
|
||||||
|
|
||||||
- macOS: [Install Homebrew](https://docs.brew.sh/Installation) and run `brew install luau`
|
- macOS: [Install Homebrew](https://docs.brew.sh/Installation) and run `brew install luau`
|
||||||
- Arch Linux: Luau has been added to the official Arch Linux packages repository under the extras repository (see [``luau``](https://archlinux.org/packages/extra/x86_64/luau/)), simply install using ``pacman``: ``pacman -Syu luau``
|
- Arch Linux: From the AUR (Arch Linux User Repository), install one of these packages via a AUR helper or manually (by cloning their repo and using ``makepkg``): [luau](https://aur.archlinux.org/packages/luau) (manual build), [luau-git](https://aur.archlinux.org/packages/luau-git) (manual build by cloning this repo), or [luau-bin](https://aur.archlinux.org/packages/luau-bin) (pre-built binaries from releases)
|
||||||
- Alpine Linux: [Enable community repositories](https://wiki.alpinelinux.org/w/index.php?title=Enable_Community_Repository) and run `apk add luau`
|
- Alpine Linux: [Enable community repositories](https://wiki.alpinelinux.org/w/index.php?title=Enable_Community_Repository) and run `apk add luau`
|
||||||
- Gentoo Linux: Luau is [officially packaged by Gentoo](https://packages.gentoo.org/packages/dev-lang/luau) and can be installed using `emerge dev-lang/luau`. You may have to unmask the package first before installing it (which can be done by including the `--autounmask=y` option in the `emerge` command).
|
- Gentoo Linux: Luau is [officially packaged by Gentoo](https://packages.gentoo.org/packages/dev-lang/luau) and can be installed using `emerge dev-lang/luau`. You may have to unmask the package first before installing it (which can be done by including the `--autounmask=y` option in the `emerge` command).
|
||||||
|
|
||||||
|
|
|
@ -1,22 +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 <string_view>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
enum class PathType
|
|
||||||
{
|
|
||||||
RelativeToCurrent,
|
|
||||||
RelativeToParent,
|
|
||||||
Aliased,
|
|
||||||
Unsupported
|
|
||||||
};
|
|
||||||
|
|
||||||
PathType getPathType(std::string_view path);
|
|
||||||
|
|
||||||
std::pair<std::string_view, std::string_view> splitPath(std::string_view path);
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,96 +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/Config.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// The RequireNavigator library provides a C++ interface for navigating the
|
|
||||||
// context in which require-by-string operates. This is used internally by the
|
|
||||||
// require-by-string runtime library to resolve paths based on the rules defined
|
|
||||||
// by its consumers.
|
|
||||||
//
|
|
||||||
// Directly linking against this library allows for inspection of the
|
|
||||||
// require-by-string path resolution algorithm's behavior without enabling the
|
|
||||||
// runtime library, which is useful for static tooling as well.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
// The ErrorHandler interface is used to report errors during navigation.
|
|
||||||
// The default implementation does nothing but can be overridden to enable
|
|
||||||
// custom error handling behavior.
|
|
||||||
class ErrorHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ErrorHandler() = default;
|
|
||||||
virtual void reportError(std::string message) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// NavigationContext is an pure virtual class that is intended to be implemented
|
|
||||||
// and injected into a Navigator.
|
|
||||||
//
|
|
||||||
// When a Navigator traverses a require path, its NavigationContext's methods
|
|
||||||
// are invoked, with the expectation that the NavigationContext will keep track
|
|
||||||
// of the current state of the navigation and provide information about the
|
|
||||||
// current context as needed.
|
|
||||||
class NavigationContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~NavigationContext() = default;
|
|
||||||
virtual std::string getRequirerIdentifier() const = 0;
|
|
||||||
|
|
||||||
enum class NavigateResult
|
|
||||||
{
|
|
||||||
Success,
|
|
||||||
Ambiguous,
|
|
||||||
NotFound
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual NavigateResult reset(const std::string& identifier) = 0;
|
|
||||||
virtual NavigateResult jumpToAlias(const std::string& path) = 0;
|
|
||||||
|
|
||||||
virtual NavigateResult toParent() = 0;
|
|
||||||
virtual NavigateResult toChild(const std::string& component) = 0;
|
|
||||||
|
|
||||||
virtual bool isConfigPresent() const = 0;
|
|
||||||
virtual std::optional<std::string> getConfig() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The Navigator class is responsible for traversing a given require path in the
|
|
||||||
// context of a given NavigationContext.
|
|
||||||
//
|
|
||||||
// The Navigator is not intended to be overridden. Rather, it expects a custom
|
|
||||||
// injected NavigationContext that provides the desired navigation behavior.
|
|
||||||
class Navigator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Status
|
|
||||||
{
|
|
||||||
Success,
|
|
||||||
ErrorReported
|
|
||||||
};
|
|
||||||
|
|
||||||
Navigator(NavigationContext& navigationContext, ErrorHandler& errorHandler);
|
|
||||||
[[nodiscard]] Status navigate(std::string path);
|
|
||||||
|
|
||||||
private:
|
|
||||||
using Error = std::optional<std::string>;
|
|
||||||
[[nodiscard]] Error navigateImpl(std::string_view path);
|
|
||||||
[[nodiscard]] Error navigateThroughPath(std::string_view path);
|
|
||||||
[[nodiscard]] Error navigateToAlias(const std::string& alias, const std::string& value);
|
|
||||||
[[nodiscard]] Error navigateToAndPopulateConfig(const std::string& desiredAlias);
|
|
||||||
|
|
||||||
NavigationContext& navigationContext;
|
|
||||||
ErrorHandler& errorHandler;
|
|
||||||
Luau::Config config;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,31 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
|
|
||||||
#include "Luau/PathUtilities.h"
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
PathType getPathType(std::string_view path)
|
|
||||||
{
|
|
||||||
if (path.size() >= 2 && path.substr(0, 2) == "./")
|
|
||||||
return PathType::RelativeToCurrent;
|
|
||||||
if (path.size() >= 3 && path.substr(0, 3) == "../")
|
|
||||||
return PathType::RelativeToParent;
|
|
||||||
if (path.size() >= 1 && path[0] == '@')
|
|
||||||
return PathType::Aliased;
|
|
||||||
|
|
||||||
return PathType::Unsupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::string_view, std::string_view> splitPath(std::string_view path)
|
|
||||||
{
|
|
||||||
size_t pos = path.find_first_of('/');
|
|
||||||
if (pos == std::string_view::npos)
|
|
||||||
return {path, {}};
|
|
||||||
|
|
||||||
return {path.substr(0, pos), path.substr(pos + 1)};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,208 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
|
|
||||||
#include "Luau/RequireNavigator.h"
|
|
||||||
|
|
||||||
#include "Luau/PathUtilities.h"
|
|
||||||
|
|
||||||
#include "Luau/Config.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <optional>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
static constexpr char kRequireErrorAmbiguous[] = "require path could not be resolved to a unique file";
|
|
||||||
static constexpr char kRequireErrorGeneric[] = "error requiring module";
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
using Error = std::optional<std::string>;
|
|
||||||
|
|
||||||
static Error toError(NavigationContext::NavigateResult result)
|
|
||||||
{
|
|
||||||
if (result == NavigationContext::NavigateResult::Success)
|
|
||||||
return std::nullopt;
|
|
||||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
|
||||||
return kRequireErrorAmbiguous;
|
|
||||||
else
|
|
||||||
return kRequireErrorGeneric;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string extractAlias(std::string_view path)
|
|
||||||
{
|
|
||||||
// To ignore the '@' alias prefix when processing the alias
|
|
||||||
const size_t aliasStartPos = 1;
|
|
||||||
|
|
||||||
// If a directory separator was found, the length of the alias is the
|
|
||||||
// distance between the start of the alias and the separator. Otherwise,
|
|
||||||
// the whole string after the alias symbol is the alias.
|
|
||||||
size_t aliasLen = path.find_first_of('/');
|
|
||||||
if (aliasLen != std::string::npos)
|
|
||||||
aliasLen -= aliasStartPos;
|
|
||||||
|
|
||||||
return std::string{path.substr(aliasStartPos, aliasLen)};
|
|
||||||
}
|
|
||||||
|
|
||||||
Navigator::Navigator(NavigationContext& navigationContext, ErrorHandler& errorHandler)
|
|
||||||
: navigationContext(navigationContext)
|
|
||||||
, errorHandler(errorHandler)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Navigator::Status Navigator::navigate(std::string path)
|
|
||||||
{
|
|
||||||
std::replace(path.begin(), path.end(), '\\', '/');
|
|
||||||
|
|
||||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
|
||||||
{
|
|
||||||
errorHandler.reportError(*error);
|
|
||||||
return Status::ErrorReported;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Error error = navigateImpl(path))
|
|
||||||
{
|
|
||||||
errorHandler.reportError(*error);
|
|
||||||
return Status::ErrorReported;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error Navigator::navigateImpl(std::string_view path)
|
|
||||||
{
|
|
||||||
PathType pathType = getPathType(path);
|
|
||||||
|
|
||||||
if (pathType == PathType::Unsupported)
|
|
||||||
return "require path must start with a valid prefix: ./, ../, or @";
|
|
||||||
|
|
||||||
if (pathType == PathType::Aliased)
|
|
||||||
{
|
|
||||||
std::string alias = extractAlias(path);
|
|
||||||
std::transform(
|
|
||||||
alias.begin(),
|
|
||||||
alias.end(),
|
|
||||||
alias.begin(),
|
|
||||||
[](unsigned char c)
|
|
||||||
{
|
|
||||||
return ('A' <= c && c <= 'Z') ? (c + ('a' - 'A')) : c;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Error error = navigateToAndPopulateConfig(alias))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (!config.aliases.contains(alias))
|
|
||||||
{
|
|
||||||
if (alias != "self")
|
|
||||||
return "@" + alias + " is not a valid alias";
|
|
||||||
|
|
||||||
// If the alias is "@self", we reset to the requirer's context and
|
|
||||||
// navigate directly from there.
|
|
||||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
|
||||||
return error;
|
|
||||||
if (Error error = navigateThroughPath(path))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Error error = navigateToAlias(alias, config.aliases[alias].value))
|
|
||||||
return error;
|
|
||||||
if (Error error = navigateThroughPath(path))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathType == PathType::RelativeToCurrent || pathType == PathType::RelativeToParent)
|
|
||||||
{
|
|
||||||
if (Error error = toError(navigationContext.toParent()))
|
|
||||||
return error;
|
|
||||||
if (Error error = navigateThroughPath(path))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error Navigator::navigateThroughPath(std::string_view path)
|
|
||||||
{
|
|
||||||
std::pair<std::string_view, std::string_view> components = splitPath(path);
|
|
||||||
if (path.size() >= 1 && path[0] == '@')
|
|
||||||
{
|
|
||||||
// If the path is aliased, we ignore the alias: this function assumes
|
|
||||||
// that navigation to an alias is handled by the caller.
|
|
||||||
components = splitPath(components.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!(components.first.empty() && components.second.empty()))
|
|
||||||
{
|
|
||||||
if (components.first == "." || components.first.empty())
|
|
||||||
{
|
|
||||||
components = splitPath(components.second);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (components.first == "..")
|
|
||||||
{
|
|
||||||
if (Error error = toError(navigationContext.toParent()))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Error error = toError(navigationContext.toChild(std::string{components.first})))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
components = splitPath(components.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error Navigator::navigateToAlias(const std::string& alias, const std::string& value)
|
|
||||||
{
|
|
||||||
PathType pathType = getPathType(value);
|
|
||||||
|
|
||||||
if (pathType == PathType::RelativeToCurrent || pathType == PathType::RelativeToParent)
|
|
||||||
{
|
|
||||||
if (Error error = navigateThroughPath(value))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
else if (pathType == PathType::Aliased)
|
|
||||||
{
|
|
||||||
return "@" + alias + " cannot point to other aliases";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Error error = toError(navigationContext.jumpToAlias(value)))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
|
||||||
{
|
|
||||||
while (!config.aliases.contains(desiredAlias))
|
|
||||||
{
|
|
||||||
if (navigationContext.toParent() != NavigationContext::NavigateResult::Success)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (navigationContext.isConfigPresent())
|
|
||||||
{
|
|
||||||
std::optional<std::string> configContents = navigationContext.getConfig();
|
|
||||||
if (!configContents)
|
|
||||||
return "could not get configuration file contents";
|
|
||||||
|
|
||||||
Luau::ConfigOptions opts;
|
|
||||||
Luau::ConfigOptions::AliasOptions aliasOpts;
|
|
||||||
aliasOpts.configLocation = "unused";
|
|
||||||
aliasOpts.overwriteAliases = false;
|
|
||||||
opts.aliasOptions = std::move(aliasOpts);
|
|
||||||
|
|
||||||
if (Error error = Luau::parseConfig(*configContents, config, opts))
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,117 +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 "lua.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Require-by-string assumes that the context in which it is embedded adheres to
|
|
||||||
// a particular structure.
|
|
||||||
//
|
|
||||||
// Each component in a require path either represents a module or a directory.
|
|
||||||
// Modules contain Luau code, whereas directories serve solely as organizational
|
|
||||||
// units. For the purposes of navigation, both modules and directories are
|
|
||||||
// functionally identical: modules and directories can both have children, which
|
|
||||||
// could themselves be modules or directories, and both types can have at most
|
|
||||||
// one parent, which could also be either a module or a directory.
|
|
||||||
//
|
|
||||||
// Without more context, it is impossible to tell which components in a given
|
|
||||||
// path "./foo/bar/baz" are modules and which are directories. To provide this
|
|
||||||
// context, the require-by-string runtime library must be opened with a
|
|
||||||
// luarequire_Configuration object, which defines the navigation behavior of the
|
|
||||||
// context in which Luau is embedded.
|
|
||||||
//
|
|
||||||
// Calls to to_parent and to_child signal a move up or down the context's
|
|
||||||
// hierarchy. The context is expected to maintain an internal state so that
|
|
||||||
// when is_module_present is called, require-by-string can determine whether it
|
|
||||||
// is currently pointing at a module or a directory.
|
|
||||||
//
|
|
||||||
// In a conventional filesystem context, "modules" map either to *.luau files or
|
|
||||||
// to directories on disk containing an init.luau file, whereas "directories"
|
|
||||||
// map to directories on disk not containing an init.luau file. In a more
|
|
||||||
// abstract context, a module and a directory could be represented by any
|
|
||||||
// nestable code unit and organizational unit, respectively.
|
|
||||||
//
|
|
||||||
// Require-by-string's runtime behavior can be additionally be configured in
|
|
||||||
// configuration files, such as .luaurc files in a filesystem context. The
|
|
||||||
// presence of a configuration file in the current context is signaled by the
|
|
||||||
// is_config_present function. Both modules and directories can contain
|
|
||||||
// configuration files; however, note that a given configuration file's scope is
|
|
||||||
// limited to the descendants of the module or directory in which it resides. In
|
|
||||||
// other words, when searching for a relevant configuration file for a given
|
|
||||||
// module, the search begins at the module's parent context and proceeds up the
|
|
||||||
// hierarchy from there, resolving to the first configuration file found.
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
enum luarequire_NavigateResult
|
|
||||||
{
|
|
||||||
NAVIGATE_SUCCESS,
|
|
||||||
NAVIGATE_AMBIGUOUS,
|
|
||||||
NAVIGATE_NOT_FOUND
|
|
||||||
};
|
|
||||||
|
|
||||||
// Functions returning WRITE_SUCCESS are expected to set their size_out argument
|
|
||||||
// to the number of bytes written to the buffer. If WRITE_BUFFER_TOO_SMALL is
|
|
||||||
// returned, size_out should be set to the required buffer size.
|
|
||||||
enum luarequire_WriteResult
|
|
||||||
{
|
|
||||||
WRITE_SUCCESS,
|
|
||||||
WRITE_BUFFER_TOO_SMALL,
|
|
||||||
WRITE_FAILURE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct luarequire_Configuration
|
|
||||||
{
|
|
||||||
// Returns whether requires are permitted from the given chunkname.
|
|
||||||
bool (*is_require_allowed)(lua_State* L, void* ctx, const char* requirer_chunkname);
|
|
||||||
|
|
||||||
// Resets the internal state to point at the requirer module.
|
|
||||||
luarequire_NavigateResult (*reset)(lua_State* L, void* ctx, const char* requirer_chunkname);
|
|
||||||
|
|
||||||
// Resets the internal state to point at an aliased module, given its exact
|
|
||||||
// path from a configuration file. This function is only called when an
|
|
||||||
// alias's path cannot be resolved relative to its configuration file.
|
|
||||||
luarequire_NavigateResult (*jump_to_alias)(lua_State* L, void* ctx, const char* path);
|
|
||||||
|
|
||||||
// Navigates through the context by making mutations to the internal state.
|
|
||||||
luarequire_NavigateResult (*to_parent)(lua_State* L, void* ctx);
|
|
||||||
luarequire_NavigateResult (*to_child)(lua_State* L, void* ctx, const char* name);
|
|
||||||
|
|
||||||
// Returns whether the context is currently pointing at a module.
|
|
||||||
bool (*is_module_present)(lua_State* L, void* ctx);
|
|
||||||
|
|
||||||
// Provides the contents of the current module. This function is only called
|
|
||||||
// if is_module_present returns true.
|
|
||||||
luarequire_WriteResult (*get_contents)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
|
||||||
|
|
||||||
// Provides a chunkname for the current module. This will be accessible
|
|
||||||
// through the debug library. This function is only called if
|
|
||||||
// is_module_present returns true.
|
|
||||||
luarequire_WriteResult (*get_chunkname)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
|
||||||
|
|
||||||
// Provides a cache key representing the current module. This function is
|
|
||||||
// only called if is_module_present returns true.
|
|
||||||
luarequire_WriteResult (*get_cache_key)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
|
||||||
|
|
||||||
// Returns whether a configuration file is present in the current context.
|
|
||||||
// If not, require-by-string will call to_parent until either a
|
|
||||||
// configuration file is present or NAVIGATE_FAILURE is returned (at root).
|
|
||||||
bool (*is_config_present)(lua_State* L, void* ctx);
|
|
||||||
|
|
||||||
// Provides the contents of the configuration file in the current context.
|
|
||||||
// This function is only called if is_config_present returns true.
|
|
||||||
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
|
||||||
|
|
||||||
// Executes the module and places the result on the stack. Returns the
|
|
||||||
// number of results placed on the stack.
|
|
||||||
int (*load)(lua_State* L, void* ctx, const char* chunkname, const char* contents);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Populates function pointers in the given luarequire_Configuration.
|
|
||||||
typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);
|
|
||||||
|
|
||||||
// Initializes the require library with the given configuration and context.
|
|
||||||
LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);
|
|
|
@ -1,124 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
|
|
||||||
#include "Navigation.h"
|
|
||||||
|
|
||||||
#include "Luau/Require.h"
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
static constexpr size_t initalFileBufferSize = 1024;
|
|
||||||
static constexpr size_t initalIdentifierBufferSize = 64;
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
static NavigationContext::NavigateResult convertNavigateResult(luarequire_NavigateResult result)
|
|
||||||
{
|
|
||||||
if (result == NAVIGATE_SUCCESS)
|
|
||||||
return NavigationContext::NavigateResult::Success;
|
|
||||||
if (result == NAVIGATE_AMBIGUOUS)
|
|
||||||
return NavigationContext::NavigateResult::Ambiguous;
|
|
||||||
|
|
||||||
return NavigationContext::NavigateResult::NotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeNavigationContext::RuntimeNavigationContext(luarequire_Configuration* config, lua_State* L, void* ctx, std::string requirerChunkname)
|
|
||||||
: config(config)
|
|
||||||
, L(L)
|
|
||||||
, ctx(ctx)
|
|
||||||
, requirerChunkname(std::move(requirerChunkname))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RuntimeNavigationContext::getRequirerIdentifier() const
|
|
||||||
{
|
|
||||||
return requirerChunkname;
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationContext::NavigateResult RuntimeNavigationContext::reset(const std::string& requirerChunkname)
|
|
||||||
{
|
|
||||||
return convertNavigateResult(config->reset(L, ctx, requirerChunkname.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationContext::NavigateResult RuntimeNavigationContext::jumpToAlias(const std::string& path)
|
|
||||||
{
|
|
||||||
return convertNavigateResult(config->jump_to_alias(L, ctx, path.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationContext::NavigateResult RuntimeNavigationContext::toParent()
|
|
||||||
{
|
|
||||||
return convertNavigateResult(config->to_parent(L, ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationContext::NavigateResult RuntimeNavigationContext::toChild(const std::string& component)
|
|
||||||
{
|
|
||||||
return convertNavigateResult(config->to_child(L, ctx, component.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeNavigationContext::isModulePresent() const
|
|
||||||
{
|
|
||||||
return config->is_module_present(L, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getContents() const
|
|
||||||
{
|
|
||||||
return getStringFromCWriter(config->get_contents, initalFileBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getChunkname() const
|
|
||||||
{
|
|
||||||
return getStringFromCWriter(config->get_chunkname, initalIdentifierBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getCacheKey() const
|
|
||||||
{
|
|
||||||
return getStringFromCWriter(config->get_cache_key, initalIdentifierBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RuntimeNavigationContext::isConfigPresent() const
|
|
||||||
{
|
|
||||||
return config->is_config_present(L, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getConfig() const
|
|
||||||
{
|
|
||||||
return getStringFromCWriter(config->get_config, initalFileBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> RuntimeNavigationContext::getStringFromCWriter(
|
|
||||||
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out),
|
|
||||||
size_t initalBufferSize
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
std::string buffer;
|
|
||||||
buffer.resize(initalBufferSize);
|
|
||||||
|
|
||||||
size_t size;
|
|
||||||
luarequire_WriteResult result = writer(L, ctx, buffer.data(), buffer.size(), &size);
|
|
||||||
if (result == WRITE_BUFFER_TOO_SMALL)
|
|
||||||
{
|
|
||||||
buffer.resize(size);
|
|
||||||
result = writer(L, ctx, buffer.data(), buffer.size(), &size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == WRITE_SUCCESS)
|
|
||||||
{
|
|
||||||
buffer.resize(size);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L)
|
|
||||||
: L(L)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeErrorHandler::reportError(std::string message)
|
|
||||||
{
|
|
||||||
luaL_errorL(L, "%s", message.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,58 +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/RequireNavigator.h"
|
|
||||||
#include "Luau/Require.h"
|
|
||||||
|
|
||||||
struct lua_State;
|
|
||||||
struct luarequire_Configuration;
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
class RuntimeNavigationContext : public NavigationContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RuntimeNavigationContext(luarequire_Configuration* config, lua_State* L, void* ctx, std::string requirerChunkname);
|
|
||||||
|
|
||||||
std::string getRequirerIdentifier() const override;
|
|
||||||
|
|
||||||
// Navigation interface
|
|
||||||
NavigateResult reset(const std::string& requirerChunkname) override;
|
|
||||||
NavigateResult jumpToAlias(const std::string& path) override;
|
|
||||||
|
|
||||||
NavigateResult toParent() override;
|
|
||||||
NavigateResult toChild(const std::string& component) override;
|
|
||||||
|
|
||||||
bool isConfigPresent() const override;
|
|
||||||
std::optional<std::string> getConfig() const override;
|
|
||||||
|
|
||||||
// Custom capabilities
|
|
||||||
bool isModulePresent() const;
|
|
||||||
std::optional<std::string> getContents() const;
|
|
||||||
std::optional<std::string> getChunkname() const;
|
|
||||||
std::optional<std::string> getCacheKey() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<std::string> getStringFromCWriter(
|
|
||||||
luarequire_WriteResult (*writer)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out),
|
|
||||||
size_t initalBufferSize
|
|
||||||
) const;
|
|
||||||
|
|
||||||
luarequire_Configuration* config;
|
|
||||||
lua_State* L;
|
|
||||||
void* ctx;
|
|
||||||
std::string requirerChunkname;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuntimeErrorHandler : public ErrorHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RuntimeErrorHandler(lua_State* L);
|
|
||||||
void reportError(std::string message) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
lua_State* L;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,52 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
|
|
||||||
#include "Luau/Require.h"
|
|
||||||
|
|
||||||
#include "RequireImpl.h"
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
|
||||||
{
|
|
||||||
if (!config.is_require_allowed)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: is_require_allowed");
|
|
||||||
if (!config.reset)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: reset");
|
|
||||||
if (!config.jump_to_alias)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: jump_to_alias");
|
|
||||||
if (!config.to_parent)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: to_parent");
|
|
||||||
if (!config.to_child)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: to_child");
|
|
||||||
if (!config.is_module_present)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: is_module_present");
|
|
||||||
if (!config.get_contents)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_contents");
|
|
||||||
if (!config.get_chunkname)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_chunkname");
|
|
||||||
if (!config.get_cache_key)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_cache_key");
|
|
||||||
if (!config.is_config_present)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: is_config_present");
|
|
||||||
if (!config.get_config)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: get_config");
|
|
||||||
if (!config.load)
|
|
||||||
luaL_error(L, "require configuration is missing required function pointer: load");
|
|
||||||
}
|
|
||||||
|
|
||||||
void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx)
|
|
||||||
{
|
|
||||||
luarequire_Configuration* config = static_cast<luarequire_Configuration*>(lua_newuserdata(L, sizeof(luarequire_Configuration)));
|
|
||||||
if (!config)
|
|
||||||
luaL_error(L, "failed to allocate memory for require configuration");
|
|
||||||
|
|
||||||
config_init(config);
|
|
||||||
validateConfig(L, *config);
|
|
||||||
|
|
||||||
lua_pushlightuserdata(L, ctx);
|
|
||||||
|
|
||||||
// "require" captures config and ctx as upvalues
|
|
||||||
lua_pushcclosure(L, Luau::Require::lua_require, "require", 2);
|
|
||||||
lua_setglobal(L, "require");
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
|
|
||||||
#include "RequireImpl.h"
|
|
||||||
|
|
||||||
#include "Navigation.h"
|
|
||||||
|
|
||||||
#include "Luau/RequireNavigator.h"
|
|
||||||
#include "Luau/Require.h"
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
static const char* cacheTableKey = "_MODULES";
|
|
||||||
|
|
||||||
struct ResolvedRequire
|
|
||||||
{
|
|
||||||
enum class Status
|
|
||||||
{
|
|
||||||
Cached,
|
|
||||||
ModuleRead,
|
|
||||||
ErrorReported
|
|
||||||
};
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
std::string contents;
|
|
||||||
std::string chunkname;
|
|
||||||
std::string cacheKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool isCached(lua_State* L, const std::string& key)
|
|
||||||
{
|
|
||||||
luaL_findtable(L, LUA_REGISTRYINDEX, cacheTableKey, 1);
|
|
||||||
lua_getfield(L, -1, key.c_str());
|
|
||||||
bool cached = !lua_isnil(L, -1);
|
|
||||||
lua_pop(L, 2);
|
|
||||||
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* L, void* ctx, std::string path)
|
|
||||||
{
|
|
||||||
lua_Debug ar;
|
|
||||||
lua_getinfo(L, 1, "s", &ar);
|
|
||||||
|
|
||||||
if (!lrc->is_require_allowed(L, ctx, ar.source))
|
|
||||||
luaL_error(L, "require is not supported in this context");
|
|
||||||
|
|
||||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, ar.source};
|
|
||||||
RuntimeErrorHandler errorHandler{L}; // Errors reported directly to lua_State.
|
|
||||||
|
|
||||||
Navigator navigator(navigationContext, errorHandler);
|
|
||||||
|
|
||||||
// Updates navigationContext while navigating through the given path.
|
|
||||||
Navigator::Status status = navigator.navigate(std::move(path));
|
|
||||||
if (status == Navigator::Status::ErrorReported)
|
|
||||||
return {ResolvedRequire::Status::ErrorReported};
|
|
||||||
|
|
||||||
if (!navigationContext.isModulePresent())
|
|
||||||
{
|
|
||||||
luaL_errorL(L, "no module present at resolved path");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cacheKey = navigationContext.getCacheKey();
|
|
||||||
if (!cacheKey)
|
|
||||||
{
|
|
||||||
errorHandler.reportError("could not get cache key for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCached(L, *cacheKey))
|
|
||||||
{
|
|
||||||
// Put cached result on top of stack before returning.
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
|
||||||
lua_getfield(L, -1, cacheKey->c_str());
|
|
||||||
lua_remove(L, -2);
|
|
||||||
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::Cached};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> chunkname = navigationContext.getChunkname();
|
|
||||||
if (!chunkname)
|
|
||||||
{
|
|
||||||
errorHandler.reportError("could not get chunkname for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> contents = navigationContext.getContents();
|
|
||||||
if (!contents)
|
|
||||||
{
|
|
||||||
errorHandler.reportError("could not get contents for module");
|
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResolvedRequire{
|
|
||||||
ResolvedRequire::Status::ModuleRead,
|
|
||||||
std::move(*contents),
|
|
||||||
std::move(*chunkname),
|
|
||||||
std::move(*cacheKey),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
int lua_require(lua_State* L)
|
|
||||||
{
|
|
||||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
||||||
if (!lrc)
|
|
||||||
luaL_error(L, "unable to find require configuration");
|
|
||||||
|
|
||||||
void* ctx = lua_tolightuserdata(L, lua_upvalueindex(2));
|
|
||||||
|
|
||||||
const char* path = luaL_checkstring(L, 1);
|
|
||||||
|
|
||||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, path);
|
|
||||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
int numResults = lrc->load(L, ctx, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
|
||||||
if (numResults > 1)
|
|
||||||
luaL_error(L, "module must return a single value");
|
|
||||||
|
|
||||||
// Cache the result
|
|
||||||
if (numResults == 1)
|
|
||||||
{
|
|
||||||
// Initial stack state
|
|
||||||
// (-1) result
|
|
||||||
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, cacheTableKey);
|
|
||||||
// (-2) result, (-1) cache table
|
|
||||||
|
|
||||||
lua_pushvalue(L, -2);
|
|
||||||
// (-3) result, (-2) cache table, (-1) result
|
|
||||||
|
|
||||||
lua_setfield(L, -2, resolvedRequire.cacheKey.c_str());
|
|
||||||
// (-2) result, (-1) cache table
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
// (-1) result
|
|
||||||
}
|
|
||||||
|
|
||||||
return numResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -1,11 +0,0 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct lua_State;
|
|
||||||
|
|
||||||
namespace Luau::Require
|
|
||||||
{
|
|
||||||
|
|
||||||
int lua_require(lua_State* L);
|
|
||||||
|
|
||||||
} // namespace Luau::Require
|
|
|
@ -169,6 +169,7 @@ target_sources(Luau.CodeGen PRIVATE
|
||||||
# Luau.Analysis Sources
|
# Luau.Analysis Sources
|
||||||
target_sources(Luau.Analysis PRIVATE
|
target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/Anyification.h
|
Analysis/include/Luau/Anyification.h
|
||||||
|
Analysis/include/Luau/AnyTypeSummary.h
|
||||||
Analysis/include/Luau/ApplyTypeFunction.h
|
Analysis/include/Luau/ApplyTypeFunction.h
|
||||||
Analysis/include/Luau/AstJsonEncoder.h
|
Analysis/include/Luau/AstJsonEncoder.h
|
||||||
Analysis/include/Luau/AstQuery.h
|
Analysis/include/Luau/AstQuery.h
|
||||||
|
@ -193,7 +194,6 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/Frontend.h
|
Analysis/include/Luau/Frontend.h
|
||||||
Analysis/include/Luau/Generalization.h
|
Analysis/include/Luau/Generalization.h
|
||||||
Analysis/include/Luau/GlobalTypes.h
|
Analysis/include/Luau/GlobalTypes.h
|
||||||
Analysis/include/Luau/InferPolarity.h
|
|
||||||
Analysis/include/Luau/InsertionOrderedMap.h
|
Analysis/include/Luau/InsertionOrderedMap.h
|
||||||
Analysis/include/Luau/Instantiation.h
|
Analysis/include/Luau/Instantiation.h
|
||||||
Analysis/include/Luau/Instantiation2.h
|
Analysis/include/Luau/Instantiation2.h
|
||||||
|
@ -207,7 +207,6 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/NonStrictTypeChecker.h
|
Analysis/include/Luau/NonStrictTypeChecker.h
|
||||||
Analysis/include/Luau/Normalize.h
|
Analysis/include/Luau/Normalize.h
|
||||||
Analysis/include/Luau/OverloadResolution.h
|
Analysis/include/Luau/OverloadResolution.h
|
||||||
Analysis/include/Luau/Polarity.h
|
|
||||||
Analysis/include/Luau/Predicate.h
|
Analysis/include/Luau/Predicate.h
|
||||||
Analysis/include/Luau/Quantify.h
|
Analysis/include/Luau/Quantify.h
|
||||||
Analysis/include/Luau/RecursionCounter.h
|
Analysis/include/Luau/RecursionCounter.h
|
||||||
|
@ -249,6 +248,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/VisitType.h
|
Analysis/include/Luau/VisitType.h
|
||||||
|
|
||||||
Analysis/src/Anyification.cpp
|
Analysis/src/Anyification.cpp
|
||||||
|
Analysis/src/AnyTypeSummary.cpp
|
||||||
Analysis/src/ApplyTypeFunction.cpp
|
Analysis/src/ApplyTypeFunction.cpp
|
||||||
Analysis/src/AstJsonEncoder.cpp
|
Analysis/src/AstJsonEncoder.cpp
|
||||||
Analysis/src/AstQuery.cpp
|
Analysis/src/AstQuery.cpp
|
||||||
|
@ -271,7 +271,6 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/src/Frontend.cpp
|
Analysis/src/Frontend.cpp
|
||||||
Analysis/src/Generalization.cpp
|
Analysis/src/Generalization.cpp
|
||||||
Analysis/src/GlobalTypes.cpp
|
Analysis/src/GlobalTypes.cpp
|
||||||
Analysis/src/InferPolarity.cpp
|
|
||||||
Analysis/src/Instantiation.cpp
|
Analysis/src/Instantiation.cpp
|
||||||
Analysis/src/Instantiation2.cpp
|
Analysis/src/Instantiation2.cpp
|
||||||
Analysis/src/IostreamHelpers.cpp
|
Analysis/src/IostreamHelpers.cpp
|
||||||
|
@ -397,9 +396,11 @@ target_sources(isocline PRIVATE
|
||||||
target_sources(Luau.CLI.lib PRIVATE
|
target_sources(Luau.CLI.lib PRIVATE
|
||||||
CLI/include/Luau/FileUtils.h
|
CLI/include/Luau/FileUtils.h
|
||||||
CLI/include/Luau/Flags.h
|
CLI/include/Luau/Flags.h
|
||||||
|
CLI/include/Luau/Require.h
|
||||||
|
|
||||||
CLI/src/FileUtils.cpp
|
CLI/src/FileUtils.cpp
|
||||||
CLI/src/Flags.cpp
|
CLI/src/Flags.cpp
|
||||||
|
CLI/src/Require.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TARGET Luau.Repl.CLI)
|
if(TARGET Luau.Repl.CLI)
|
||||||
|
@ -407,27 +408,18 @@ if(TARGET Luau.Repl.CLI)
|
||||||
target_sources(Luau.Repl.CLI PRIVATE
|
target_sources(Luau.Repl.CLI PRIVATE
|
||||||
CLI/include/Luau/Coverage.h
|
CLI/include/Luau/Coverage.h
|
||||||
CLI/include/Luau/Profiler.h
|
CLI/include/Luau/Profiler.h
|
||||||
CLI/include/Luau/ReplRequirer.h
|
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Coverage.cpp
|
CLI/src/Coverage.cpp
|
||||||
CLI/src/Profiler.cpp
|
CLI/src/Profiler.cpp
|
||||||
CLI/src/Repl.cpp
|
CLI/src/Repl.cpp
|
||||||
CLI/src/ReplEntry.cpp
|
CLI/src/ReplEntry.cpp
|
||||||
CLI/src/ReplRequirer.cpp
|
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET Luau.Analyze.CLI)
|
if(TARGET Luau.Analyze.CLI)
|
||||||
# Luau.Analyze.CLI Sources
|
# Luau.Analyze.CLI Sources
|
||||||
target_sources(Luau.Analyze.CLI PRIVATE
|
target_sources(Luau.Analyze.CLI PRIVATE
|
||||||
CLI/include/Luau/AnalyzeRequirer.h
|
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Analyze.cpp
|
CLI/src/Analyze.cpp
|
||||||
CLI/src/AnalyzeRequirer.cpp
|
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -441,6 +433,7 @@ endif()
|
||||||
if(TARGET Luau.UnitTest)
|
if(TARGET Luau.UnitTest)
|
||||||
# Luau.UnitTest Sources
|
# Luau.UnitTest Sources
|
||||||
target_sources(Luau.UnitTest PRIVATE
|
target_sources(Luau.UnitTest PRIVATE
|
||||||
|
tests/AnyTypeSummary.test.cpp
|
||||||
tests/AssemblyBuilderA64.test.cpp
|
tests/AssemblyBuilderA64.test.cpp
|
||||||
tests/AssemblyBuilderX64.test.cpp
|
tests/AssemblyBuilderX64.test.cpp
|
||||||
tests/AstJsonEncoder.test.cpp
|
tests/AstJsonEncoder.test.cpp
|
||||||
|
@ -474,7 +467,6 @@ if(TARGET Luau.UnitTest)
|
||||||
tests/FragmentAutocomplete.test.cpp
|
tests/FragmentAutocomplete.test.cpp
|
||||||
tests/Frontend.test.cpp
|
tests/Frontend.test.cpp
|
||||||
tests/Generalization.test.cpp
|
tests/Generalization.test.cpp
|
||||||
tests/InferPolarity.test.cpp
|
|
||||||
tests/InsertionOrderedMap.test.cpp
|
tests/InsertionOrderedMap.test.cpp
|
||||||
tests/Instantiation2.test.cpp
|
tests/Instantiation2.test.cpp
|
||||||
tests/IostreamOptional.h
|
tests/IostreamOptional.h
|
||||||
|
@ -561,14 +553,10 @@ if(TARGET Luau.CLI.Test)
|
||||||
target_sources(Luau.CLI.Test PRIVATE
|
target_sources(Luau.CLI.Test PRIVATE
|
||||||
CLI/include/Luau/Coverage.h
|
CLI/include/Luau/Coverage.h
|
||||||
CLI/include/Luau/Profiler.h
|
CLI/include/Luau/Profiler.h
|
||||||
CLI/include/Luau/ReplRequirer.h
|
|
||||||
CLI/include/Luau/RequirerUtils.h
|
|
||||||
|
|
||||||
CLI/src/Coverage.cpp
|
CLI/src/Coverage.cpp
|
||||||
CLI/src/Profiler.cpp
|
CLI/src/Profiler.cpp
|
||||||
CLI/src/Repl.cpp
|
CLI/src/Repl.cpp
|
||||||
CLI/src/ReplRequirer.cpp
|
|
||||||
CLI/src/RequirerUtils.cpp
|
|
||||||
|
|
||||||
tests/RegisterCallbacks.h
|
tests/RegisterCallbacks.h
|
||||||
tests/RegisterCallbacks.cpp
|
tests/RegisterCallbacks.cpp
|
||||||
|
@ -577,29 +565,6 @@ if(TARGET Luau.CLI.Test)
|
||||||
tests/main.cpp)
|
tests/main.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET Luau.Require)
|
|
||||||
# Luau.Require Sources
|
|
||||||
target_sources(Luau.Require PRIVATE
|
|
||||||
Require/Runtime/include/Luau/Require.h
|
|
||||||
|
|
||||||
Require/Runtime/src/Navigation.h
|
|
||||||
Require/Runtime/src/RequireImpl.h
|
|
||||||
|
|
||||||
Require/Runtime/src/Navigation.cpp
|
|
||||||
Require/Runtime/src/Require.cpp
|
|
||||||
Require/Runtime/src/RequireImpl.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET Luau.RequireNavigator)
|
|
||||||
# Luau.Require Sources
|
|
||||||
target_sources(Luau.RequireNavigator PRIVATE
|
|
||||||
Require/Navigator/include/Luau/PathUtilities.h
|
|
||||||
Require/Navigator/include/Luau/RequireNavigator.h
|
|
||||||
|
|
||||||
Require/Navigator/src/PathUtilities.cpp
|
|
||||||
Require/Navigator/src/RequireNavigator.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET Luau.Web)
|
if(TARGET Luau.Web)
|
||||||
# Luau.Web Sources
|
# Luau.Web Sources
|
||||||
target_sources(Luau.Web PRIVATE
|
target_sources(Luau.Web PRIVATE
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lvm.h"
|
#include "lvm.h"
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
||||||
|
|
||||||
#define CO_STATUS_ERROR -1
|
#define CO_STATUS_ERROR -1
|
||||||
#define CO_STATUS_BREAK -2
|
#define CO_STATUS_BREAK -2
|
||||||
|
|
||||||
|
@ -235,12 +237,20 @@ static int coclose(lua_State* L)
|
||||||
{
|
{
|
||||||
lua_pushboolean(L, false);
|
lua_pushboolean(L, false);
|
||||||
|
|
||||||
|
if (DFFlag::LuauStackLimit)
|
||||||
|
{
|
||||||
if (co->status == LUA_ERRMEM)
|
if (co->status == LUA_ERRMEM)
|
||||||
lua_pushstring(L, LUA_MEMERRMSG);
|
lua_pushstring(L, LUA_MEMERRMSG);
|
||||||
else if (co->status == LUA_ERRERR)
|
else if (co->status == LUA_ERRERR)
|
||||||
lua_pushstring(L, LUA_ERRERRMSG);
|
lua_pushstring(L, LUA_ERRERRMSG);
|
||||||
else if (lua_gettop(co))
|
else if (lua_gettop(co))
|
||||||
lua_xmove(co, L, 1); // move error message
|
lua_xmove(co, L, 1); // move error message
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lua_gettop(co))
|
||||||
|
lua_xmove(co, L, 1); // move error message
|
||||||
|
}
|
||||||
|
|
||||||
lua_resetthread(co);
|
lua_resetthread(co);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauStackLimit, false)
|
||||||
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauPopIncompleteCi, false)
|
||||||
|
|
||||||
// keep max stack allocation request under 1GB
|
// keep max stack allocation request under 1GB
|
||||||
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
||||||
|
|
||||||
|
@ -180,10 +183,10 @@ static void correctstack(lua_State* L, TValue* oldstack)
|
||||||
void luaD_reallocstack(lua_State* L, int newsize, int fornewci)
|
void luaD_reallocstack(lua_State* L, int newsize, int fornewci)
|
||||||
{
|
{
|
||||||
// throw 'out of memory' error because space for a custom error message cannot be guaranteed here
|
// throw 'out of memory' error because space for a custom error message cannot be guaranteed here
|
||||||
if (newsize > MAX_STACK_SIZE)
|
if (DFFlag::LuauStackLimit && newsize > MAX_STACK_SIZE)
|
||||||
{
|
{
|
||||||
// reallocation was performed to setup a new CallInfo frame, which we have to remove
|
// reallocation was performaed to setup a new CallInfo frame, which we have to remove
|
||||||
if (fornewci)
|
if (DFFlag::LuauPopIncompleteCi && fornewci)
|
||||||
{
|
{
|
||||||
CallInfo* cip = L->ci - 1;
|
CallInfo* cip = L->ci - 1;
|
||||||
|
|
||||||
|
@ -217,9 +220,19 @@ void luaD_reallocCI(lua_State* L, int newsize)
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaD_growstack(lua_State* L, int n)
|
void luaD_growstack(lua_State* L, int n)
|
||||||
|
{
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
{
|
{
|
||||||
luaD_reallocstack(L, getgrownstacksize(L, n), 0);
|
luaD_reallocstack(L, getgrownstacksize(L, n), 0);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (n <= L->stacksize) // double size is enough?
|
||||||
|
luaD_reallocstack(L, 2 * L->stacksize, 0);
|
||||||
|
else
|
||||||
|
luaD_reallocstack(L, L->stacksize + n, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CallInfo* luaD_growCI(lua_State* L)
|
CallInfo* luaD_growCI(lua_State* L)
|
||||||
{
|
{
|
||||||
|
|
|
@ -443,7 +443,7 @@ static void shrinkstack(lua_State* L)
|
||||||
|
|
||||||
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
||||||
luaD_reallocstack(L, L->stacksize / 2, 0); // still big enough...
|
luaD_reallocstack(L, L->stacksize / 2, 0); // still big enough...
|
||||||
condhardstacktests(luaD_reallocstack(L, s_used, 0));
|
condhardstacktests(luaD_reallocstack(L, s_used));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
|
|
||||||
#define luaC_checkGC(L) \
|
#define luaC_checkGC(L) \
|
||||||
{ \
|
{ \
|
||||||
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 0)); \
|
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK)); \
|
||||||
if (luaC_needsGC(L)) \
|
if (luaC_needsGC(L)) \
|
||||||
{ \
|
{ \
|
||||||
condhardmemtests(luaC_validate(L), 1); \
|
condhardmemtests(luaC_validate(L), 1); \
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauPopIncompleteCi)
|
||||||
|
|
||||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#if __has_warning("-Wc99-designator")
|
#if __has_warning("-Wc99-designator")
|
||||||
|
@ -935,7 +937,14 @@ reentry:
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
luaD_checkstackfornewci(L, ccl->stacksize);
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
|
@ -3071,7 +3080,14 @@ int luau_precall(lua_State* L, StkId func, int nresults)
|
||||||
L->base = ci->base;
|
L->base = ci->base;
|
||||||
// Note: L->top is assigned externally
|
// Note: L->top is assigned externally
|
||||||
|
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
luaD_checkstackfornewci(L, ccl->stacksize);
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
if (!ccl->isC)
|
if (!ccl->isC)
|
||||||
|
|
|
@ -134,7 +134,6 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
getMutable<TableType>(vector3MetaType)->props = {
|
getMutable<TableType>(vector3MetaType)->props = {
|
||||||
{"__add", {makeFunction(arena, nullopt, {vector3InstanceType, vector3InstanceType}, {vector3InstanceType})}},
|
{"__add", {makeFunction(arena, nullopt, {vector3InstanceType, vector3InstanceType}, {vector3InstanceType})}},
|
||||||
};
|
};
|
||||||
getMutable<TableType>(vector3MetaType)->state = TableState::Sealed;
|
|
||||||
|
|
||||||
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
||||||
|
|
||||||
|
|
1038
tests/AnyTypeSummary.test.cpp
Normal file
1038
tests/AnyTypeSummary.test.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,6 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
|
||||||
|
|
||||||
struct JsonEncoderFixture
|
struct JsonEncoderFixture
|
||||||
{
|
{
|
||||||
|
@ -441,9 +440,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr")
|
||||||
AstStat* expr = expectParseStatement("@checked function a(b) return c end");
|
AstStat* expr = expectParseStatement("@checked function a(b) return c end");
|
||||||
|
|
||||||
std::string_view expected =
|
std::string_view expected =
|
||||||
FFlag::LuauFixFunctionWithAttributesStartLocation
|
R"({"type":"AstStatFunction","location":"0,9 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,9 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})";
|
||||||
? R"({"type":"AstStatFunction","location":"0,0 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,0 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})"
|
|
||||||
: R"({"type":"AstStatFunction","location":"0,9 - 0,35","name":{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"a"},"func":{"type":"AstExprFunction","location":"0,9 - 0,35","attributes":[{"type":"AstAttr","location":"0,0 - 0,8","name":"checked"}],"generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,20 - 0,21"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,22 - 0,32","hasEnd":true,"body":[{"type":"AstStatReturn","location":"0,23 - 0,31","list":[{"type":"AstExprGlobal","location":"0,30 - 0,31","global":"c"}]}]},"functionDepth":1,"debugname":"a"}})";
|
|
||||||
|
|
||||||
CHECK(toJson(expr) == expected);
|
CHECK(toJson(expr) == expected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
LUAU_FASTFLAG(LuauAutocompleteUnionCopyPreviousSeen)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAG(LuauTypeFunResultInAutocomplete)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -4364,14 +4363,7 @@ foo(@1)
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "anonymous_autofilled_generic_on_argument_type_pack_vararg")
|
TEST_CASE_FIXTURE(ACFixture, "anonymous_autofilled_generic_on_argument_type_pack_vararg")
|
||||||
{
|
{
|
||||||
// Caveat lector! This is actually invalid syntax!
|
check(R"(
|
||||||
// The correct syntax would be as follows:
|
|
||||||
//
|
|
||||||
// local function foo(a: <T...>(T...) -> number)
|
|
||||||
//
|
|
||||||
// We leave it as-written here because we still expect autocomplete to
|
|
||||||
// handle this code sensibly.
|
|
||||||
CheckResult result = check(R"(
|
|
||||||
local function foo(a: <T...>(...: T...) -> number)
|
local function foo(a: <T...>(...: T...) -> number)
|
||||||
return a(4, 5, 6)
|
return a(4, 5, 6)
|
||||||
end
|
end
|
||||||
|
@ -4504,27 +4496,4 @@ this@2
|
||||||
CHECK_EQ(ac.entryMap.count("thisShouldBeThere"), 0);
|
CHECK_EQ(ac.entryMap.count("thisShouldBeThere"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_eval_in_autocomplete")
|
|
||||||
{
|
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
|
||||||
ScopedFastFlag luauTypeFunResultInAutocomplete{FFlag::LuauTypeFunResultInAutocomplete, true};
|
|
||||||
|
|
||||||
check(R"(
|
|
||||||
type function foo(x)
|
|
||||||
local tbl = types.newtable(nil, nil, nil)
|
|
||||||
tbl:setproperty(types.singleton("boolean"), x)
|
|
||||||
tbl:setproperty(types.singleton("number"), types.number)
|
|
||||||
return tbl
|
|
||||||
end
|
|
||||||
|
|
||||||
local function test(a: foo<string>)
|
|
||||||
return a.@1
|
|
||||||
end
|
|
||||||
)");
|
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
|
||||||
CHECK_EQ(ac.entryMap.count("boolean"), 1);
|
|
||||||
CHECK_EQ(ac.entryMap.count("number"), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue