mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Merge branch 'master' of https://github.com/Roblox/luau into autocomplete-context
This commit is contained in:
commit
33c3843c78
68 changed files with 1598 additions and 757 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@
|
||||||
/default.prof*
|
/default.prof*
|
||||||
/fuzz-*
|
/fuzz-*
|
||||||
/luau
|
/luau
|
||||||
|
__pycache__
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Scope2;
|
struct Scope;
|
||||||
|
|
||||||
struct TypeVar;
|
struct TypeVar;
|
||||||
using TypeId = const TypeVar*;
|
using TypeId = const TypeVar*;
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ struct GeneralizationConstraint
|
||||||
{
|
{
|
||||||
TypeId generalizedType;
|
TypeId generalizedType;
|
||||||
TypeId sourceType;
|
TypeId sourceType;
|
||||||
Scope2* scope;
|
Scope* scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
// subType ~ inst superType
|
// subType ~ inst superType
|
||||||
|
|
|
@ -17,21 +17,22 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Scope2;
|
struct Scope;
|
||||||
|
using ScopePtr = std::shared_ptr<Scope>;
|
||||||
|
|
||||||
struct ConstraintGraphBuilder
|
struct ConstraintGraphBuilder
|
||||||
{
|
{
|
||||||
// A list of all the scopes in the module. This vector holds ownership of the
|
// A list of all the scopes in the module. This vector holds ownership of the
|
||||||
// scope pointers; the scopes themselves borrow pointers to other scopes to
|
// scope pointers; the scopes themselves borrow pointers to other scopes to
|
||||||
// define the scope hierarchy.
|
// define the scope hierarchy.
|
||||||
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scopes;
|
std::vector<std::pair<Location, ScopePtr>> scopes;
|
||||||
|
|
||||||
ModuleName moduleName;
|
ModuleName moduleName;
|
||||||
SingletonTypes& singletonTypes;
|
SingletonTypes& singletonTypes;
|
||||||
const NotNull<TypeArena> arena;
|
const NotNull<TypeArena> arena;
|
||||||
// The root scope of the module we're generating constraints for.
|
// The root scope of the module we're generating constraints for.
|
||||||
// This is null when the CGB is initially constructed.
|
// This is null when the CGB is initially constructed.
|
||||||
Scope2* rootScope;
|
Scope* rootScope;
|
||||||
// A mapping of AST node to TypeId.
|
// A mapping of AST node to TypeId.
|
||||||
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
||||||
// A mapping of AST node to TypePackId.
|
// A mapping of AST node to TypePackId.
|
||||||
|
@ -50,42 +51,42 @@ struct ConstraintGraphBuilder
|
||||||
// Occasionally constraint generation needs to produce an ICE.
|
// Occasionally constraint generation needs to produce an ICE.
|
||||||
const NotNull<InternalErrorReporter> ice;
|
const NotNull<InternalErrorReporter> ice;
|
||||||
|
|
||||||
NotNull<Scope2> globalScope;
|
NotNull<Scope> globalScope;
|
||||||
|
|
||||||
ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope);
|
ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope> globalScope);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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(NotNull<Scope2> scope);
|
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(NotNull<Scope2> scope);
|
TypePackId freshTypePack(const ScopePtr& scope);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fabricates a scope that is a child of another scope.
|
* Fabricates a scope that is a child of another scope.
|
||||||
* @param location the lexical extent of the scope in the source code.
|
* @param location the lexical extent of the scope in the source code.
|
||||||
* @param parent the parent scope of the new scope. Must not be null.
|
* @param parent the parent scope of the new scope. Must not be null.
|
||||||
*/
|
*/
|
||||||
NotNull<Scope2> childScope(Location location, NotNull<Scope2> parent);
|
ScopePtr childScope(Location location, const ScopePtr& parent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new constraint with no dependencies to a given scope.
|
* Adds a new constraint with no dependencies to a given scope.
|
||||||
* @param scope the scope to add the constraint to.
|
* @param scope the scope to add the constraint to.
|
||||||
* @param cv the constraint variant to add.
|
* @param cv the constraint variant to add.
|
||||||
*/
|
*/
|
||||||
void addConstraint(NotNull<Scope2> scope, ConstraintV cv);
|
void addConstraint(const ScopePtr& scope, ConstraintV cv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a constraint to a given scope.
|
* Adds a constraint to a given scope.
|
||||||
* @param scope the scope to add the constraint to. Must not be null.
|
* @param scope the scope to add the constraint to. Must not be null.
|
||||||
* @param c the constraint to add.
|
* @param c the constraint to add.
|
||||||
*/
|
*/
|
||||||
void addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c);
|
void addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entry point to the ConstraintGraphBuilder. This will construct a set
|
* The entry point to the ConstraintGraphBuilder. This will construct a set
|
||||||
|
@ -94,23 +95,23 @@ struct ConstraintGraphBuilder
|
||||||
*/
|
*/
|
||||||
void visit(AstStatBlock* block);
|
void visit(AstStatBlock* block);
|
||||||
|
|
||||||
void visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block);
|
void visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
|
||||||
|
|
||||||
void visit(NotNull<Scope2> scope, AstStat* stat);
|
void visit(const ScopePtr& scope, AstStat* stat);
|
||||||
void visit(NotNull<Scope2> scope, AstStatBlock* block);
|
void visit(const ScopePtr& scope, AstStatBlock* block);
|
||||||
void visit(NotNull<Scope2> scope, AstStatLocal* local);
|
void visit(const ScopePtr& scope, AstStatLocal* local);
|
||||||
void visit(NotNull<Scope2> scope, AstStatFor* for_);
|
void visit(const ScopePtr& scope, AstStatFor* for_);
|
||||||
void visit(NotNull<Scope2> scope, AstStatLocalFunction* function);
|
void visit(const ScopePtr& scope, AstStatLocalFunction* function);
|
||||||
void visit(NotNull<Scope2> scope, AstStatFunction* function);
|
void visit(const ScopePtr& scope, AstStatFunction* function);
|
||||||
void visit(NotNull<Scope2> scope, AstStatReturn* ret);
|
void visit(const ScopePtr& scope, AstStatReturn* ret);
|
||||||
void visit(NotNull<Scope2> scope, AstStatAssign* assign);
|
void visit(const ScopePtr& scope, AstStatAssign* assign);
|
||||||
void visit(NotNull<Scope2> scope, AstStatIf* ifStatement);
|
void visit(const ScopePtr& scope, AstStatIf* ifStatement);
|
||||||
void visit(NotNull<Scope2> scope, AstStatTypeAlias* alias);
|
void visit(const ScopePtr& scope, AstStatTypeAlias* alias);
|
||||||
|
|
||||||
TypePackId checkExprList(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs);
|
TypePackId checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs);
|
||||||
|
|
||||||
TypePackId checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs);
|
TypePackId checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs);
|
||||||
TypePackId checkPack(NotNull<Scope2> scope, AstExpr* expr);
|
TypePackId checkPack(const ScopePtr& scope, AstExpr* expr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks an expression that is expected to evaluate to one type.
|
* Checks an expression that is expected to evaluate to one type.
|
||||||
|
@ -118,13 +119,13 @@ struct ConstraintGraphBuilder
|
||||||
* @param expr the expression to check.
|
* @param expr the expression to check.
|
||||||
* @return the type of the expression.
|
* @return the type of the expression.
|
||||||
*/
|
*/
|
||||||
TypeId check(NotNull<Scope2> scope, AstExpr* expr);
|
TypeId check(const ScopePtr& scope, AstExpr* expr);
|
||||||
|
|
||||||
TypeId checkExprTable(NotNull<Scope2> scope, AstExprTable* expr);
|
TypeId checkExprTable(const ScopePtr& scope, AstExprTable* expr);
|
||||||
TypeId check(NotNull<Scope2> scope, AstExprIndexName* indexName);
|
TypeId check(const ScopePtr& scope, AstExprIndexName* indexName);
|
||||||
TypeId check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr);
|
TypeId check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
|
||||||
TypeId check(NotNull<Scope2> scope, AstExprUnary* unary);
|
TypeId check(const ScopePtr& scope, AstExprUnary* unary);
|
||||||
TypeId check(NotNull<Scope2> scope, AstExprBinary* binary);
|
TypeId check(const ScopePtr& scope, AstExprBinary* binary);
|
||||||
|
|
||||||
struct FunctionSignature
|
struct FunctionSignature
|
||||||
{
|
{
|
||||||
|
@ -133,20 +134,20 @@ struct ConstraintGraphBuilder
|
||||||
// The scope that encompasses the function's signature. May be nullptr
|
// The scope that encompasses the function's signature. May be nullptr
|
||||||
// if there was no need for a signature scope (the function has no
|
// if there was no need for a signature scope (the function has no
|
||||||
// generics).
|
// generics).
|
||||||
Scope2* signatureScope;
|
ScopePtr signatureScope;
|
||||||
// The scope that encompasses the function's body. Is a child scope of
|
// The scope that encompasses the function's body. Is a child scope of
|
||||||
// signatureScope, if present.
|
// signatureScope, if present.
|
||||||
NotNull<Scope2> bodyScope;
|
ScopePtr bodyScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
FunctionSignature checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn);
|
FunctionSignature checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the body of a function expression.
|
* Checks the body of a function expression.
|
||||||
* @param scope the interior scope of the body of the function.
|
* @param scope the interior scope of the body of the function.
|
||||||
* @param fn the function expression to check.
|
* @param fn the function expression to check.
|
||||||
*/
|
*/
|
||||||
void checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn);
|
void checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a type from its AST annotation.
|
* Resolves a type from its AST annotation.
|
||||||
|
@ -154,7 +155,7 @@ struct ConstraintGraphBuilder
|
||||||
* @param ty the AST annotation to resolve.
|
* @param ty the AST annotation to resolve.
|
||||||
* @return the type of the AST annotation.
|
* @return the type of the AST annotation.
|
||||||
**/
|
**/
|
||||||
TypeId resolveType(NotNull<Scope2> scope, AstType* ty);
|
TypeId resolveType(const ScopePtr& scope, AstType* ty);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a type pack from its AST annotation.
|
* Resolves a type pack from its AST annotation.
|
||||||
|
@ -162,14 +163,14 @@ struct ConstraintGraphBuilder
|
||||||
* @param tp the AST annotation to resolve.
|
* @param tp the AST annotation to resolve.
|
||||||
* @return the type pack of the AST annotation.
|
* @return the type pack of the AST annotation.
|
||||||
**/
|
**/
|
||||||
TypePackId resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp);
|
TypePackId resolveTypePack(const ScopePtr& scope, AstTypePack* tp);
|
||||||
|
|
||||||
TypePackId resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list);
|
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& list);
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics);
|
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics);
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(NotNull<Scope2> scope, AstArray<AstGenericTypePack> packs);
|
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(const ScopePtr& scope, AstArray<AstGenericTypePack> packs);
|
||||||
|
|
||||||
TypeId flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp);
|
TypeId flattenPack(const ScopePtr& scope, Location location, TypePackId tp);
|
||||||
|
|
||||||
void reportError(Location location, TypeErrorData err);
|
void reportError(Location location, TypeErrorData err);
|
||||||
void reportCodeTooComplex(Location location);
|
void reportCodeTooComplex(Location location);
|
||||||
|
@ -180,7 +181,7 @@ struct ConstraintGraphBuilder
|
||||||
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
|
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
|
||||||
* initial scan of the AST and note what globals are defined.
|
* initial scan of the AST and note what globals are defined.
|
||||||
*/
|
*/
|
||||||
void prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program);
|
void prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,6 +194,6 @@ struct ConstraintGraphBuilder
|
||||||
* @return a list of pointers to constraints contained within the scope graph.
|
* @return a list of pointers to constraints contained within the scope graph.
|
||||||
* None of these pointers should be null.
|
* None of these pointers should be null.
|
||||||
*/
|
*/
|
||||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope);
|
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct ConstraintSolver
|
||||||
// is important to not add elements to this vector, lest the underlying
|
// is important to not add elements to this vector, lest the underlying
|
||||||
// storage that we retain pointers to be mutated underneath us.
|
// storage that we retain pointers to be mutated underneath us.
|
||||||
const std::vector<NotNull<Constraint>> constraints;
|
const std::vector<NotNull<Constraint>> constraints;
|
||||||
NotNull<Scope2> rootScope;
|
NotNull<Scope> rootScope;
|
||||||
|
|
||||||
// This includes every constraint that has not been fully solved.
|
// This includes every constraint that has not been fully solved.
|
||||||
// A constraint can be both blocked and unsolved, for instance.
|
// A constraint can be both blocked and unsolved, for instance.
|
||||||
|
@ -40,7 +40,7 @@ struct ConstraintSolver
|
||||||
|
|
||||||
ConstraintSolverLogger logger;
|
ConstraintSolverLogger logger;
|
||||||
|
|
||||||
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope);
|
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to dispatch all pending constraints and reach a type solution
|
* Attempts to dispatch all pending constraints and reach a type solution
|
||||||
|
@ -121,6 +121,6 @@ private:
|
||||||
void unblock_(BlockedConstraintId progressed);
|
void unblock_(BlockedConstraintId progressed);
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump(NotNull<Scope2> rootScope, struct ToStringOptions& opts);
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -15,8 +15,8 @@ namespace Luau
|
||||||
struct ConstraintSolverLogger
|
struct ConstraintSolverLogger
|
||||||
{
|
{
|
||||||
std::string compileOutput();
|
std::string compileOutput();
|
||||||
void captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
void captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||||
void prepareStepSnapshot(const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
void prepareStepSnapshot(const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||||
void commitPreparedStepSnapshot();
|
void commitPreparedStepSnapshot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -152,7 +152,7 @@ struct Frontend
|
||||||
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
|
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
|
||||||
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
|
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
|
||||||
|
|
||||||
NotNull<Scope2> getGlobalScope2();
|
NotNull<Scope> getGlobalScope();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope);
|
ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope);
|
||||||
|
@ -169,7 +169,7 @@ private:
|
||||||
std::unordered_map<std::string, ScopePtr> environments;
|
std::unordered_map<std::string, ScopePtr> environments;
|
||||||
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
|
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
|
||||||
|
|
||||||
std::unique_ptr<Scope2> globalScope2;
|
std::unique_ptr<Scope> globalScope;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileResolver* fileResolver;
|
FileResolver* fileResolver;
|
||||||
|
|
|
@ -69,7 +69,6 @@ struct Module
|
||||||
std::shared_ptr<AstNameTable> names;
|
std::shared_ptr<AstNameTable> names;
|
||||||
|
|
||||||
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
|
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
|
||||||
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scope2s; // never empty
|
|
||||||
|
|
||||||
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
||||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
||||||
|
@ -86,7 +85,6 @@ struct Module
|
||||||
bool timeout = false;
|
bool timeout = false;
|
||||||
|
|
||||||
ScopePtr getModuleScope() const;
|
ScopePtr getModuleScope() const;
|
||||||
Scope2* getModuleScope2() const;
|
|
||||||
|
|
||||||
// Once a module has been typechecked, we clone its public interface into a separate arena.
|
// Once a module has been typechecked, we clone its public interface into a separate arena.
|
||||||
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
|
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
|
||||||
|
|
|
@ -7,9 +7,9 @@ namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct TypeArena;
|
struct TypeArena;
|
||||||
struct Scope2;
|
struct Scope;
|
||||||
|
|
||||||
void quantify(TypeId ty, TypeLevel level);
|
void quantify(TypeId ty, TypeLevel level);
|
||||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope);
|
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -32,10 +32,16 @@ struct Scope
|
||||||
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
|
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
|
||||||
|
|
||||||
const ScopePtr parent; // null for the root
|
const ScopePtr parent; // null for the root
|
||||||
|
|
||||||
|
// All the children of this scope.
|
||||||
|
std::vector<NotNull<Scope>> children;
|
||||||
std::unordered_map<Symbol, Binding> bindings;
|
std::unordered_map<Symbol, Binding> bindings;
|
||||||
|
std::unordered_map<Name, TypeId> typeBindings;
|
||||||
|
std::unordered_map<Name, TypePackId> typePackBindings;
|
||||||
TypePackId returnType;
|
TypePackId returnType;
|
||||||
bool breakOk = false;
|
|
||||||
std::optional<TypePackId> varargPack;
|
std::optional<TypePackId> varargPack;
|
||||||
|
// All constraints belonging to this scope.
|
||||||
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
|
|
||||||
|
@ -45,7 +51,9 @@ struct Scope
|
||||||
|
|
||||||
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
|
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
|
||||||
|
|
||||||
std::optional<TypeId> lookup(const Symbol& name);
|
std::optional<TypeId> lookup(Symbol sym);
|
||||||
|
std::optional<TypeId> lookupTypeBinding(const Name& name);
|
||||||
|
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
|
||||||
|
|
||||||
std::optional<TypeFun> lookupType(const Name& name);
|
std::optional<TypeFun> lookupType(const Name& name);
|
||||||
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name);
|
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name);
|
||||||
|
@ -66,24 +74,4 @@ struct Scope
|
||||||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Scope2
|
|
||||||
{
|
|
||||||
// The parent scope of this scope. Null if there is no parent (i.e. this
|
|
||||||
// is the module-level scope).
|
|
||||||
Scope2* parent = nullptr;
|
|
||||||
// All the children of this scope.
|
|
||||||
std::vector<NotNull<Scope2>> children;
|
|
||||||
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
|
|
||||||
std::unordered_map<Name, TypeId> typeBindings;
|
|
||||||
std::unordered_map<Name, TypePackId> typePackBindings;
|
|
||||||
TypePackId returnType;
|
|
||||||
std::optional<TypePackId> varargPack;
|
|
||||||
// All constraints belonging to this scope.
|
|
||||||
std::vector<ConstraintPtr> constraints;
|
|
||||||
|
|
||||||
std::optional<TypeId> lookup(Symbol sym);
|
|
||||||
std::optional<TypeId> lookupTypeBinding(const Name& name);
|
|
||||||
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct TypeArena;
|
struct TypeArena;
|
||||||
struct Scope2;
|
struct Scope;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There are three kinds of type variables:
|
* There are three kinds of type variables:
|
||||||
|
@ -143,7 +143,7 @@ struct ConstrainedTypeVar
|
||||||
|
|
||||||
std::vector<TypeId> parts;
|
std::vector<TypeId> parts;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
|
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
|
||||||
|
@ -275,7 +275,7 @@ struct FunctionTypeVar
|
||||||
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
/// These should all be generic
|
/// These should all be generic
|
||||||
std::vector<TypeId> generics;
|
std::vector<TypeId> generics;
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
|
@ -344,7 +344,7 @@ struct TableTypeVar
|
||||||
|
|
||||||
TableState state = TableState::Unsealed;
|
TableState state = TableState::Unsealed;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
std::optional<std::string> name;
|
std::optional<std::string> name;
|
||||||
|
|
||||||
// Sometimes we throw a type on a name to make for nicer error messages, but without creating any entry in the type namespace
|
// Sometimes we throw a type on a name to make for nicer error messages, but without creating any entry in the type namespace
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Scope2;
|
struct Scope;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 'level' of a TypeVar is an indirect way to talk about the scope that it 'belongs' too.
|
* The 'level' of a TypeVar is an indirect way to talk about the scope that it 'belongs' too.
|
||||||
|
@ -84,11 +84,11 @@ using Name = std::string;
|
||||||
struct Free
|
struct Free
|
||||||
{
|
{
|
||||||
explicit Free(TypeLevel level);
|
explicit Free(TypeLevel level);
|
||||||
explicit Free(Scope2* scope);
|
explicit Free(Scope* scope);
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
// True if this free type variable is part of a mutually
|
// True if this free type variable is part of a mutually
|
||||||
// recursive type alias whose definitions haven't been
|
// recursive type alias whose definitions haven't been
|
||||||
// resolved yet.
|
// resolved yet.
|
||||||
|
@ -115,13 +115,13 @@ struct Generic
|
||||||
Generic();
|
Generic();
|
||||||
explicit Generic(TypeLevel level);
|
explicit Generic(TypeLevel level);
|
||||||
explicit Generic(const Name& name);
|
explicit Generic(const Name& name);
|
||||||
explicit Generic(Scope2* scope);
|
explicit Generic(Scope* scope);
|
||||||
Generic(TypeLevel level, const Name& name);
|
Generic(TypeLevel level, const Name& name);
|
||||||
Generic(Scope2* scope, const Name& name);
|
Generic(Scope* scope, const Name& name);
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
Name name;
|
Name name;
|
||||||
bool explicitName = false;
|
bool explicitName = false;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix2)
|
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -149,7 +149,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
auto canUnify = [&typeArena](TypeId subTy, TypeId superTy) {
|
auto canUnify = [&typeArena](TypeId subTy, TypeId superTy) {
|
||||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
|
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||||
|
|
||||||
InternalErrorReporter iceReporter;
|
InternalErrorReporter iceReporter;
|
||||||
UnifierSharedState unifierState(&iceReporter);
|
UnifierSharedState unifierState(&iceReporter);
|
||||||
|
@ -168,7 +168,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||||
TypeId expectedType = follow(*typeAtPosition);
|
TypeId expectedType = follow(*typeAtPosition);
|
||||||
|
|
||||||
auto checkFunctionType = [typeArena, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
auto checkFunctionType = [typeArena, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
||||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||||
return checkTypeMatch(typeArena, *firstRetTy, expectedType);
|
return checkTypeMatch(typeArena, *firstRetTy, expectedType);
|
||||||
|
@ -209,7 +209,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
return checkTypeMatch(typeArena, ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
return checkTypeMatch(typeArena, ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||||
else
|
else
|
||||||
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||||
|
@ -226,7 +226,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
||||||
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
|
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
rootTy = follow(rootTy);
|
rootTy = follow(rootTy);
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -236,7 +236,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
seen.insert(ty);
|
seen.insert(ty);
|
||||||
|
|
||||||
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
|
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
|
||||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
|
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||||
|
|
||||||
if (indexType == PropIndexType::Key)
|
if (indexType == PropIndexType::Key)
|
||||||
return false;
|
return false;
|
||||||
|
@ -269,7 +269,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto isWrongIndexer = [typeArena, rootTy, indexType](Luau::TypeId type) {
|
auto isWrongIndexer = [typeArena, rootTy, indexType](Luau::TypeId type) {
|
||||||
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix2);
|
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
|
||||||
|
|
||||||
if (indexType == PropIndexType::Key)
|
if (indexType == PropIndexType::Key)
|
||||||
return false;
|
return false;
|
||||||
|
@ -277,21 +277,20 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
bool calledWithSelf = indexType == PropIndexType::Colon;
|
bool calledWithSelf = indexType == PropIndexType::Colon;
|
||||||
|
|
||||||
auto isCompatibleCall = [typeArena, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
|
auto isCompatibleCall = [typeArena, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
|
||||||
if (get<ClassTypeVar>(rootTy))
|
// Strong match with definition is a success
|
||||||
{
|
if (calledWithSelf == ftv->hasSelf)
|
||||||
// Calls on classes require strict match between how function is declared and how it's called
|
return true;
|
||||||
return calledWithSelf == ftv->hasSelf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a call is made with ':', it is invalid if a function has incompatible first argument or no arguments at all
|
// Calls on classes require strict match between how function is declared and how it's called
|
||||||
// If a call is made with '.', but it was declared with 'self', it is considered invalid if first argument is compatible
|
if (get<ClassTypeVar>(rootTy))
|
||||||
if (calledWithSelf || ftv->hasSelf)
|
return false;
|
||||||
|
|
||||||
|
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
|
||||||
|
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
|
||||||
|
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
||||||
{
|
{
|
||||||
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
|
||||||
{
|
return calledWithSelf;
|
||||||
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
|
|
||||||
return calledWithSelf;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !calledWithSelf;
|
return !calledWithSelf;
|
||||||
|
@ -333,7 +332,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
AutocompleteEntryKind::Property,
|
AutocompleteEntryKind::Property,
|
||||||
type,
|
type,
|
||||||
prop.deprecated,
|
prop.deprecated,
|
||||||
FFlag::LuauSelfCallAutocompleteFix2 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
|
FFlag::LuauSelfCallAutocompleteFix3 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
|
||||||
typeCorrect,
|
typeCorrect,
|
||||||
containingClass,
|
containingClass,
|
||||||
&prop,
|
&prop,
|
||||||
|
@ -376,7 +375,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
{
|
{
|
||||||
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
|
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
|
||||||
|
|
||||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
{
|
{
|
||||||
if (auto mtable = get<TableTypeVar>(mt->metatable))
|
if (auto mtable = get<TableTypeVar>(mt->metatable))
|
||||||
fillMetatableProps(mtable);
|
fillMetatableProps(mtable);
|
||||||
|
@ -442,7 +441,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
AutocompleteEntryMap inner;
|
AutocompleteEntryMap inner;
|
||||||
std::unordered_set<TypeId> innerSeen;
|
std::unordered_set<TypeId> innerSeen;
|
||||||
|
|
||||||
if (!FFlag::LuauSelfCallAutocompleteFix2)
|
if (!FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
innerSeen = seen;
|
innerSeen = seen;
|
||||||
|
|
||||||
if (isNil(*iter))
|
if (isNil(*iter))
|
||||||
|
@ -468,7 +467,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix2)
|
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
{
|
{
|
||||||
if (pt->metatable)
|
if (pt->metatable)
|
||||||
{
|
{
|
||||||
|
@ -476,7 +475,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
fillMetatableProps(mtable);
|
fillMetatableProps(mtable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauSelfCallAutocompleteFix2 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
else if (FFlag::LuauSelfCallAutocompleteFix3 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||||
{
|
{
|
||||||
autocompleteProps(module, typeArena, rootTy, getSingletonTypes().stringType, indexType, nodes, result, seen);
|
autocompleteProps(module, typeArena, rootTy, getSingletonTypes().stringType, indexType, nodes, result, seen);
|
||||||
}
|
}
|
||||||
|
@ -1405,7 +1404,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
TypeId ty = follow(*it);
|
TypeId ty = follow(*it);
|
||||||
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
|
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
|
||||||
|
|
||||||
if (!FFlag::LuauSelfCallAutocompleteFix2 && isString(ty))
|
if (!FFlag::LuauSelfCallAutocompleteFix3 && isString(ty))
|
||||||
return {
|
return {
|
||||||
autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
||||||
else
|
else
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Luau
|
||||||
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
||||||
|
|
||||||
ConstraintGraphBuilder::ConstraintGraphBuilder(
|
ConstraintGraphBuilder::ConstraintGraphBuilder(
|
||||||
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope)
|
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope> globalScope)
|
||||||
: moduleName(moduleName)
|
: moduleName(moduleName)
|
||||||
, singletonTypes(getSingletonTypes())
|
, singletonTypes(getSingletonTypes())
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
|
@ -25,36 +25,34 @@ ConstraintGraphBuilder::ConstraintGraphBuilder(
|
||||||
LUAU_ASSERT(arena);
|
LUAU_ASSERT(arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::freshType(NotNull<Scope2> scope)
|
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
|
||||||
{
|
{
|
||||||
return arena->addType(FreeTypeVar{scope});
|
return arena->addType(FreeTypeVar{scope.get()});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::freshTypePack(NotNull<Scope2> scope)
|
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
|
||||||
{
|
{
|
||||||
FreeTypePack f{scope};
|
FreeTypePack f{scope.get()};
|
||||||
return arena->addTypePack(TypePackVar{std::move(f)});
|
return arena->addTypePack(TypePackVar{std::move(f)});
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull<Scope2> ConstraintGraphBuilder::childScope(Location location, NotNull<Scope2> parent)
|
ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent)
|
||||||
{
|
{
|
||||||
auto scope = std::make_unique<Scope2>();
|
auto scope = std::make_shared<Scope>(parent);
|
||||||
NotNull<Scope2> borrow = NotNull(scope.get());
|
scopes.emplace_back(location, scope);
|
||||||
scopes.emplace_back(location, std::move(scope));
|
|
||||||
|
|
||||||
borrow->parent = parent;
|
scope->returnType = parent->returnType;
|
||||||
borrow->returnType = parent->returnType;
|
parent->children.push_back(NotNull(scope.get()));
|
||||||
parent->children.push_back(borrow);
|
|
||||||
|
|
||||||
return borrow;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, ConstraintV cv)
|
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, ConstraintV cv)
|
||||||
{
|
{
|
||||||
scope->constraints.emplace_back(new Constraint{std::move(cv)});
|
scope->constraints.emplace_back(new Constraint{std::move(cv)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c)
|
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
|
||||||
{
|
{
|
||||||
scope->constraints.emplace_back(std::move(c));
|
scope->constraints.emplace_back(std::move(c));
|
||||||
}
|
}
|
||||||
|
@ -63,13 +61,13 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(scopes.empty());
|
LUAU_ASSERT(scopes.empty());
|
||||||
LUAU_ASSERT(rootScope == nullptr);
|
LUAU_ASSERT(rootScope == nullptr);
|
||||||
scopes.emplace_back(block->location, std::make_unique<Scope2>());
|
ScopePtr scope = std::make_shared<Scope>(singletonTypes.anyTypePack);
|
||||||
rootScope = scopes.back().second.get();
|
rootScope = scope.get();
|
||||||
NotNull<Scope2> borrow = NotNull(rootScope);
|
scopes.emplace_back(block->location, scope);
|
||||||
|
|
||||||
rootScope->returnType = freshTypePack(borrow);
|
rootScope->returnType = freshTypePack(scope);
|
||||||
|
|
||||||
prepopulateGlobalScope(borrow, block);
|
prepopulateGlobalScope(scope, block);
|
||||||
|
|
||||||
// TODO: We should share the global scope.
|
// TODO: We should share the global scope.
|
||||||
rootScope->typeBindings["nil"] = singletonTypes.nilType;
|
rootScope->typeBindings["nil"] = singletonTypes.nilType;
|
||||||
|
@ -78,10 +76,10 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
||||||
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||||
rootScope->typeBindings["thread"] = singletonTypes.threadType;
|
rootScope->typeBindings["thread"] = singletonTypes.threadType;
|
||||||
|
|
||||||
visitBlockWithoutChildScope(borrow, block);
|
visitBlockWithoutChildScope(scope, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block)
|
void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
RecursionCounter counter{&recursionCount};
|
RecursionCounter counter{&recursionCount};
|
||||||
|
|
||||||
|
@ -95,7 +93,7 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope,
|
||||||
visit(scope, stat);
|
visit(scope, stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
|
||||||
{
|
{
|
||||||
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
|
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
|
||||||
|
|
||||||
|
@ -123,22 +121,24 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
|
||||||
LUAU_ASSERT(0);
|
LUAU_ASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
||||||
{
|
{
|
||||||
std::vector<TypeId> varTypes;
|
std::vector<TypeId> varTypes;
|
||||||
|
|
||||||
for (AstLocal* local : local->vars)
|
for (AstLocal* local : local->vars)
|
||||||
{
|
{
|
||||||
TypeId ty = freshType(scope);
|
TypeId ty = freshType(scope);
|
||||||
|
Location location = local->location;
|
||||||
|
|
||||||
if (local->annotation)
|
if (local->annotation)
|
||||||
{
|
{
|
||||||
|
location = local->annotation->location;
|
||||||
TypeId annotation = resolveType(scope, local->annotation);
|
TypeId annotation = resolveType(scope, local->annotation);
|
||||||
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
||||||
}
|
}
|
||||||
|
|
||||||
varTypes.push_back(ty);
|
varTypes.push_back(ty);
|
||||||
scope->bindings[local] = ty;
|
scope->bindings[local] = Binding{ty, location};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < local->values.size; ++i)
|
for (size_t i = 0; i < local->values.size; ++i)
|
||||||
|
@ -169,7 +169,7 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFor* for_)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||||
{
|
{
|
||||||
auto checkNumber = [&](AstExpr* expr)
|
auto checkNumber = [&](AstExpr* expr)
|
||||||
{
|
{
|
||||||
|
@ -184,24 +184,24 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFor* for_)
|
||||||
checkNumber(for_->to);
|
checkNumber(for_->to);
|
||||||
checkNumber(for_->step);
|
checkNumber(for_->step);
|
||||||
|
|
||||||
NotNull<Scope2> forScope = childScope(for_->location, scope);
|
ScopePtr forScope = childScope(for_->location, scope);
|
||||||
forScope->bindings[for_->var] = singletonTypes.numberType;
|
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
|
||||||
|
|
||||||
visit(forScope, for_->body);
|
visit(forScope, for_->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addConstraints(Constraint* constraint, NotNull<Scope2> scope)
|
void addConstraints(Constraint* constraint, NotNull<Scope> scope)
|
||||||
{
|
{
|
||||||
scope->constraints.reserve(scope->constraints.size() + scope->constraints.size());
|
scope->constraints.reserve(scope->constraints.size() + scope->constraints.size());
|
||||||
|
|
||||||
for (const auto& c : scope->constraints)
|
for (const auto& c : scope->constraints)
|
||||||
constraint->dependencies.push_back(NotNull{c.get()});
|
constraint->dependencies.push_back(NotNull{c.get()});
|
||||||
|
|
||||||
for (NotNull<Scope2> childScope : scope->children)
|
for (NotNull<Scope> childScope : scope->children)
|
||||||
addConstraints(constraint, childScope);
|
addConstraints(constraint, childScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction* function)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
||||||
{
|
{
|
||||||
// Local
|
// Local
|
||||||
// Global
|
// Global
|
||||||
|
@ -213,21 +213,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction*
|
||||||
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
|
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
|
||||||
|
|
||||||
functionType = arena->addType(BlockedTypeVar{});
|
functionType = arena->addType(BlockedTypeVar{});
|
||||||
scope->bindings[function->name] = functionType;
|
scope->bindings[function->name] = Binding{functionType, function->name->location};
|
||||||
|
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, function->func);
|
FunctionSignature sig = checkFunctionSignature(scope, function->func);
|
||||||
sig.bodyScope->bindings[function->name] = sig.signature;
|
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
||||||
|
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
|
|
||||||
std::unique_ptr<Constraint> c{
|
std::unique_ptr<Constraint> c{
|
||||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
|
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
|
||||||
addConstraints(c.get(), sig.bodyScope);
|
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* function)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
|
||||||
{
|
{
|
||||||
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
|
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
|
||||||
// With or without self
|
// With or without self
|
||||||
|
@ -247,9 +247,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
functionType = arena->addType(BlockedTypeVar{});
|
functionType = arena->addType(BlockedTypeVar{});
|
||||||
scope->bindings[localName->local] = functionType;
|
scope->bindings[localName->local] = Binding{functionType, localName->location};
|
||||||
}
|
}
|
||||||
sig.bodyScope->bindings[localName->local] = sig.signature;
|
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
||||||
}
|
}
|
||||||
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
||||||
{
|
{
|
||||||
|
@ -262,9 +262,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
functionType = arena->addType(BlockedTypeVar{});
|
functionType = arena->addType(BlockedTypeVar{});
|
||||||
rootScope->bindings[globalName->name] = functionType;
|
rootScope->bindings[globalName->name] = Binding{functionType, globalName->location};
|
||||||
}
|
}
|
||||||
sig.bodyScope->bindings[globalName->name] = sig.signature;
|
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
||||||
}
|
}
|
||||||
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
||||||
{
|
{
|
||||||
|
@ -291,21 +291,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
|
|
||||||
std::unique_ptr<Constraint> c{
|
std::unique_ptr<Constraint> c{
|
||||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
|
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
|
||||||
addConstraints(c.get(), sig.bodyScope);
|
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatReturn* ret)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
|
||||||
{
|
{
|
||||||
TypePackId exprTypes = checkPack(scope, ret->list);
|
TypePackId exprTypes = checkPack(scope, ret->list);
|
||||||
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
|
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatBlock* block)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
NotNull<Scope2> innerScope = childScope(block->location, scope);
|
ScopePtr innerScope = childScope(block->location, scope);
|
||||||
|
|
||||||
// In order to enable mutually-recursive type aliases, we need to
|
// In order to enable mutually-recursive type aliases, we need to
|
||||||
// populate the type bindings before we actually check any of the
|
// populate the type bindings before we actually check any of the
|
||||||
|
@ -323,7 +323,7 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatBlock* block)
|
||||||
visitBlockWithoutChildScope(innerScope, block);
|
visitBlockWithoutChildScope(innerScope, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
|
||||||
{
|
{
|
||||||
TypePackId varPackId = checkExprList(scope, assign->vars);
|
TypePackId varPackId = checkExprList(scope, assign->vars);
|
||||||
TypePackId valuePack = checkPack(scope, assign->values);
|
TypePackId valuePack = checkPack(scope, assign->values);
|
||||||
|
@ -331,21 +331,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
|
||||||
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
|
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatIf* ifStatement)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
|
||||||
{
|
{
|
||||||
check(scope, ifStatement->condition);
|
check(scope, ifStatement->condition);
|
||||||
|
|
||||||
NotNull<Scope2> thenScope = childScope(ifStatement->thenbody->location, scope);
|
ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope);
|
||||||
visit(thenScope, ifStatement->thenbody);
|
visit(thenScope, ifStatement->thenbody);
|
||||||
|
|
||||||
if (ifStatement->elsebody)
|
if (ifStatement->elsebody)
|
||||||
{
|
{
|
||||||
NotNull<Scope2> elseScope = childScope(ifStatement->elsebody->location, scope);
|
ScopePtr elseScope = childScope(ifStatement->elsebody->location, scope);
|
||||||
visit(elseScope, ifStatement->elsebody);
|
visit(elseScope, ifStatement->elsebody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatTypeAlias* alias)
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
|
||||||
{
|
{
|
||||||
// TODO: Exported type aliases
|
// TODO: Exported type aliases
|
||||||
// TODO: Generic type aliases
|
// TODO: Generic type aliases
|
||||||
|
@ -371,7 +371,7 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatTypeAlias* alia
|
||||||
addConstraint(scope, NameConstraint{ty, alias->name.value});
|
addConstraint(scope, NameConstraint{ty, alias->name.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs)
|
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs)
|
||||||
{
|
{
|
||||||
if (exprs.size == 0)
|
if (exprs.size == 0)
|
||||||
return arena->addTypePack({});
|
return arena->addTypePack({});
|
||||||
|
@ -392,7 +392,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<Ast
|
||||||
return arena->addTypePack(TypePack{std::move(types), last});
|
return arena->addTypePack(TypePack{std::move(types), last});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs)
|
TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs)
|
||||||
{
|
{
|
||||||
TypePackId result = arena->addTypePack({});
|
TypePackId result = arena->addTypePack({});
|
||||||
TypePack* resultPack = getMutable<TypePack>(result);
|
TypePack* resultPack = getMutable<TypePack>(result);
|
||||||
|
@ -413,7 +413,7 @@ TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const As
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* expr)
|
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
|
||||||
{
|
{
|
||||||
RecursionCounter counter{&recursionCount};
|
RecursionCounter counter{&recursionCount};
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* exp
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||||
{
|
{
|
||||||
RecursionCounter counter{&recursionCount};
|
RecursionCounter counter{&recursionCount};
|
||||||
|
|
||||||
|
@ -548,7 +548,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* indexName)
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
|
||||||
{
|
{
|
||||||
TypeId obj = check(scope, indexName->expr);
|
TypeId obj = check(scope, indexName->expr);
|
||||||
TypeId result = freshType(scope);
|
TypeId result = freshType(scope);
|
||||||
|
@ -564,7 +564,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* in
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr)
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
|
||||||
{
|
{
|
||||||
TypeId obj = check(scope, indexExpr->expr);
|
TypeId obj = check(scope, indexExpr->expr);
|
||||||
TypeId indexType = check(scope, indexExpr->index);
|
TypeId indexType = check(scope, indexExpr->index);
|
||||||
|
@ -579,7 +579,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* in
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
|
||||||
{
|
{
|
||||||
TypeId operandType = check(scope, unary->expr);
|
TypeId operandType = check(scope, unary->expr);
|
||||||
|
|
||||||
|
@ -599,7 +599,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
|
||||||
return singletonTypes.errorRecoveryType();
|
return singletonTypes.errorRecoveryType();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binary)
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary)
|
||||||
{
|
{
|
||||||
TypeId leftType = check(scope, binary->left);
|
TypeId leftType = check(scope, binary->left);
|
||||||
TypeId rightType = check(scope, binary->right);
|
TypeId rightType = check(scope, binary->right);
|
||||||
|
@ -624,7 +624,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binar
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTable* expr)
|
TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTable* expr)
|
||||||
{
|
{
|
||||||
TypeId ty = arena->addType(TableTypeVar{});
|
TypeId ty = arena->addType(TableTypeVar{});
|
||||||
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
||||||
|
@ -674,10 +674,10 @@ TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTabl
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn)
|
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn)
|
||||||
{
|
{
|
||||||
Scope2* signatureScope = nullptr;
|
ScopePtr signatureScope = nullptr;
|
||||||
Scope2* bodyScope = nullptr;
|
ScopePtr bodyScope = nullptr;
|
||||||
TypePackId returnType = nullptr;
|
TypePackId returnType = nullptr;
|
||||||
|
|
||||||
std::vector<TypeId> genericTypes;
|
std::vector<TypeId> genericTypes;
|
||||||
|
@ -690,18 +690,17 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||||
// generics properly.
|
// generics properly.
|
||||||
if (hasGenerics)
|
if (hasGenerics)
|
||||||
{
|
{
|
||||||
NotNull signatureBorrow = childScope(fn->location, parent);
|
signatureScope = childScope(fn->location, parent);
|
||||||
signatureScope = signatureBorrow.get();
|
|
||||||
|
|
||||||
// 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 propogated to bodyScope.
|
// return type gets propogated to bodyScope.
|
||||||
returnType = freshTypePack(signatureBorrow);
|
returnType = freshTypePack(signatureScope);
|
||||||
signatureScope->returnType = returnType;
|
signatureScope->returnType = returnType;
|
||||||
|
|
||||||
bodyScope = childScope(fn->body->location, signatureBorrow).get();
|
bodyScope = childScope(fn->body->location, signatureScope);
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
||||||
|
|
||||||
// We do not support default values on function generics, so we only
|
// We do not support default values on function generics, so we only
|
||||||
// care about the types involved.
|
// care about the types involved.
|
||||||
|
@ -719,11 +718,10 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NotNull bodyBorrow = childScope(fn->body->location, parent);
|
bodyScope = childScope(fn->body->location, parent);
|
||||||
bodyScope = bodyBorrow.get();
|
|
||||||
|
|
||||||
returnType = freshTypePack(bodyBorrow);
|
returnType = freshTypePack(bodyScope);
|
||||||
bodyBorrow->returnType = returnType;
|
bodyScope->returnType = returnType;
|
||||||
|
|
||||||
// To eliminate the need to branch on hasGenerics below, we say that the
|
// To eliminate the need to branch on hasGenerics below, we say that the
|
||||||
// signature scope is the body scope when there is no real signature
|
// signature scope is the body scope when there is no real signature
|
||||||
|
@ -731,27 +729,24 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||||
signatureScope = bodyScope;
|
signatureScope = bodyScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull bodyBorrow = NotNull(bodyScope);
|
|
||||||
NotNull signatureBorrow = NotNull(signatureScope);
|
|
||||||
|
|
||||||
if (fn->returnAnnotation)
|
if (fn->returnAnnotation)
|
||||||
{
|
{
|
||||||
TypePackId annotatedRetType = resolveTypePack(signatureBorrow, *fn->returnAnnotation);
|
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
|
||||||
addConstraint(signatureBorrow, PackSubtypeConstraint{returnType, annotatedRetType});
|
addConstraint(signatureScope, PackSubtypeConstraint{returnType, annotatedRetType});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeId> argTypes;
|
std::vector<TypeId> argTypes;
|
||||||
|
|
||||||
for (AstLocal* local : fn->args)
|
for (AstLocal* local : fn->args)
|
||||||
{
|
{
|
||||||
TypeId t = freshType(signatureBorrow);
|
TypeId t = freshType(signatureScope);
|
||||||
argTypes.push_back(t);
|
argTypes.push_back(t);
|
||||||
signatureScope->bindings[local] = t;
|
signatureScope->bindings[local] = Binding{t, local->location};
|
||||||
|
|
||||||
if (local->annotation)
|
if (local->annotation)
|
||||||
{
|
{
|
||||||
TypeId argAnnotation = resolveType(signatureBorrow, local->annotation);
|
TypeId argAnnotation = resolveType(signatureScope, local->annotation);
|
||||||
addConstraint(signatureBorrow, SubtypeConstraint{t, argAnnotation});
|
addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,11 +767,11 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||||
// Undo the workaround we made above: if there's no signature scope,
|
// Undo the workaround we made above: if there's no signature scope,
|
||||||
// don't report it.
|
// don't report it.
|
||||||
/* signatureScope */ hasGenerics ? signatureScope : nullptr,
|
/* signatureScope */ hasGenerics ? signatureScope : nullptr,
|
||||||
/* bodyScope */ bodyBorrow,
|
/* bodyScope */ bodyScope,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn)
|
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
||||||
{
|
{
|
||||||
visitBlockWithoutChildScope(scope, fn->body);
|
visitBlockWithoutChildScope(scope, fn->body);
|
||||||
|
|
||||||
|
@ -789,7 +784,7 @@ void ConstraintGraphBuilder::checkFunctionBody(NotNull<Scope2> scope, AstExprFun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
|
||||||
{
|
{
|
||||||
TypeId result = nullptr;
|
TypeId result = nullptr;
|
||||||
|
|
||||||
|
@ -834,7 +829,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
||||||
{
|
{
|
||||||
// TODO: Recursion limit.
|
// TODO: Recursion limit.
|
||||||
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
||||||
Scope2* signatureScope = nullptr;
|
ScopePtr signatureScope = nullptr;
|
||||||
|
|
||||||
std::vector<TypeId> genericTypes;
|
std::vector<TypeId> genericTypes;
|
||||||
std::vector<TypePackId> genericTypePacks;
|
std::vector<TypePackId> genericTypePacks;
|
||||||
|
@ -843,22 +838,21 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
||||||
// for the generic bindings to live on.
|
// for the generic bindings to live on.
|
||||||
if (hasGenerics)
|
if (hasGenerics)
|
||||||
{
|
{
|
||||||
NotNull<Scope2> signatureBorrow = childScope(fn->location, scope);
|
signatureScope = childScope(fn->location, scope);
|
||||||
signatureScope = signatureBorrow.get();
|
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
||||||
|
|
||||||
for (const auto& [name, g] : genericDefinitions)
|
for (const auto& [name, g] : genericDefinitions)
|
||||||
{
|
{
|
||||||
genericTypes.push_back(g.ty);
|
genericTypes.push_back(g.ty);
|
||||||
signatureBorrow->typeBindings[name] = g.ty;
|
signatureScope->typeBindings[name] = g.ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [name, g] : genericPackDefinitions)
|
for (const auto& [name, g] : genericPackDefinitions)
|
||||||
{
|
{
|
||||||
genericTypePacks.push_back(g.tp);
|
genericTypePacks.push_back(g.tp);
|
||||||
signatureBorrow->typePackBindings[name] = g.tp;
|
signatureScope->typePackBindings[name] = g.tp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -866,13 +860,11 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
||||||
// To eliminate the need to branch on hasGenerics below, we say that
|
// To eliminate the need to branch on hasGenerics below, we say that
|
||||||
// the signature scope is the parent scope if we don't have
|
// the signature scope is the parent scope if we don't have
|
||||||
// generics.
|
// generics.
|
||||||
signatureScope = scope.get();
|
signatureScope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull<Scope2> signatureBorrow(signatureScope);
|
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes);
|
||||||
|
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes);
|
||||||
TypePackId argTypes = resolveTypePack(signatureBorrow, fn->argTypes);
|
|
||||||
TypePackId returnTypes = resolveTypePack(signatureBorrow, fn->returnTypes);
|
|
||||||
|
|
||||||
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
|
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
|
||||||
// how to quantify/instantiate it.
|
// how to quantify/instantiate it.
|
||||||
|
@ -950,7 +942,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp)
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp)
|
||||||
{
|
{
|
||||||
TypePackId result;
|
TypePackId result;
|
||||||
if (auto expl = tp->as<AstTypePackExplicit>())
|
if (auto expl = tp->as<AstTypePackExplicit>())
|
||||||
|
@ -964,7 +956,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
|
||||||
}
|
}
|
||||||
else if (auto gen = tp->as<AstTypePackGeneric>())
|
else if (auto gen = tp->as<AstTypePackGeneric>())
|
||||||
{
|
{
|
||||||
result = arena->addTypePack(TypePackVar{GenericTypePack{scope, gen->genericName.value}});
|
result = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), gen->genericName.value}});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -976,7 +968,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list)
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list)
|
||||||
{
|
{
|
||||||
std::vector<TypeId> head;
|
std::vector<TypeId> head;
|
||||||
|
|
||||||
|
@ -994,12 +986,12 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const
|
||||||
return arena->addTypePack(TypePack{head, tail});
|
return arena->addTypePack(TypePack{head, tail});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics)
|
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<Name, GenericTypeDefinition>> result;
|
std::vector<std::pair<Name, GenericTypeDefinition>> result;
|
||||||
for (const auto& generic : generics)
|
for (const auto& generic : generics)
|
||||||
{
|
{
|
||||||
TypeId genericTy = arena->addType(GenericTypeVar{scope, generic.name.value});
|
TypeId genericTy = arena->addType(GenericTypeVar{scope.get(), generic.name.value});
|
||||||
std::optional<TypeId> defaultTy = std::nullopt;
|
std::optional<TypeId> defaultTy = std::nullopt;
|
||||||
|
|
||||||
if (generic.defaultValue)
|
if (generic.defaultValue)
|
||||||
|
@ -1015,12 +1007,12 @@ std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::crea
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
|
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
|
||||||
NotNull<Scope2> scope, AstArray<AstGenericTypePack> generics)
|
const ScopePtr& scope, AstArray<AstGenericTypePack> generics)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
|
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
|
||||||
for (const auto& generic : generics)
|
for (const auto& generic : generics)
|
||||||
{
|
{
|
||||||
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope, generic.name.value}});
|
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
|
||||||
std::optional<TypePackId> defaultTy = std::nullopt;
|
std::optional<TypePackId> defaultTy = std::nullopt;
|
||||||
|
|
||||||
if (generic.defaultValue)
|
if (generic.defaultValue)
|
||||||
|
@ -1035,7 +1027,7 @@ std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGraphBuilder::flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp)
|
TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, TypePackId tp)
|
||||||
{
|
{
|
||||||
if (auto f = first(tp))
|
if (auto f = first(tp))
|
||||||
return *f;
|
return *f;
|
||||||
|
@ -1061,10 +1053,10 @@ void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
|
||||||
|
|
||||||
struct GlobalPrepopulator : AstVisitor
|
struct GlobalPrepopulator : AstVisitor
|
||||||
{
|
{
|
||||||
const NotNull<Scope2> globalScope;
|
const NotNull<Scope> globalScope;
|
||||||
const NotNull<TypeArena> arena;
|
const NotNull<TypeArena> arena;
|
||||||
|
|
||||||
GlobalPrepopulator(NotNull<Scope2> globalScope, NotNull<TypeArena> arena)
|
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
|
||||||
: globalScope(globalScope)
|
: globalScope(globalScope)
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
{
|
{
|
||||||
|
@ -1073,29 +1065,29 @@ struct GlobalPrepopulator : AstVisitor
|
||||||
bool visit(AstStatFunction* function) override
|
bool visit(AstStatFunction* function) override
|
||||||
{
|
{
|
||||||
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
||||||
globalScope->bindings[g->name] = arena->addType(BlockedTypeVar{});
|
globalScope->bindings[g->name] = Binding{arena->addType(BlockedTypeVar{})};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void ConstraintGraphBuilder::prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program)
|
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
||||||
{
|
{
|
||||||
GlobalPrepopulator gp{NotNull{globalScope}, arena};
|
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
|
||||||
|
|
||||||
program->visit(&gp);
|
program->visit(&gp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope2> scope)
|
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope> scope)
|
||||||
{
|
{
|
||||||
for (const auto& c : scope->constraints)
|
for (const auto& c : scope->constraints)
|
||||||
result.push_back(NotNull{c.get()});
|
result.push_back(NotNull{c.get()});
|
||||||
|
|
||||||
for (NotNull<Scope2> child : scope->children)
|
for (NotNull<Scope> child : scope->children)
|
||||||
collectConstraints(result, child);
|
collectConstraints(result, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope)
|
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
|
||||||
{
|
{
|
||||||
std::vector<NotNull<Constraint>> result;
|
std::vector<NotNull<Constraint>> result;
|
||||||
collectConstraints(result, rootScope);
|
collectConstraints(result, rootScope);
|
||||||
|
|
|
@ -13,31 +13,31 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
[[maybe_unused]] static void dumpBindings(NotNull<Scope2> scope, ToStringOptions& opts)
|
[[maybe_unused]] static void dumpBindings(NotNull<Scope> scope, ToStringOptions& opts)
|
||||||
{
|
{
|
||||||
for (const auto& [k, v] : scope->bindings)
|
for (const auto& [k, v] : scope->bindings)
|
||||||
{
|
{
|
||||||
auto d = toStringDetailed(v, opts);
|
auto d = toStringDetailed(v.typeId, opts);
|
||||||
opts.nameMap = d.nameMap;
|
opts.nameMap = d.nameMap;
|
||||||
printf("\t%s : %s\n", k.c_str(), d.name.c_str());
|
printf("\t%s : %s\n", k.c_str(), d.name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NotNull<Scope2> child : scope->children)
|
for (NotNull<Scope> child : scope->children)
|
||||||
dumpBindings(child, opts);
|
dumpBindings(child, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumpConstraints(NotNull<Scope2> scope, ToStringOptions& opts)
|
static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
|
||||||
{
|
{
|
||||||
for (const ConstraintPtr& c : scope->constraints)
|
for (const ConstraintPtr& c : scope->constraints)
|
||||||
{
|
{
|
||||||
printf("\t%s\n", toString(*c, opts).c_str());
|
printf("\t%s\n", toString(*c, opts).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NotNull<Scope2> child : scope->children)
|
for (NotNull<Scope> child : scope->children)
|
||||||
dumpConstraints(child, opts);
|
dumpConstraints(child, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump(NotNull<Scope2> rootScope, ToStringOptions& opts)
|
void dump(NotNull<Scope> rootScope, ToStringOptions& opts)
|
||||||
{
|
{
|
||||||
printf("constraints:\n");
|
printf("constraints:\n");
|
||||||
dumpConstraints(rootScope, opts);
|
dumpConstraints(rootScope, opts);
|
||||||
|
@ -55,7 +55,7 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope)
|
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope)
|
||||||
: arena(arena)
|
: arena(arena)
|
||||||
, constraints(collectConstraints(rootScope))
|
, constraints(collectConstraints(rootScope))
|
||||||
, rootScope(rootScope)
|
, rootScope(rootScope)
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& opts)
|
static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opts)
|
||||||
{
|
{
|
||||||
std::string output = "{\"bindings\":{";
|
std::string output = "{\"bindings\":{";
|
||||||
|
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
for (const auto& [name, type] : scope->bindings)
|
for (const auto& [name, binding] : scope->bindings)
|
||||||
{
|
{
|
||||||
if (comma)
|
if (comma)
|
||||||
output += ",";
|
output += ",";
|
||||||
|
@ -19,7 +19,7 @@ static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& op
|
||||||
output += name.c_str();
|
output += name.c_str();
|
||||||
output += "\": \"";
|
output += "\": \"";
|
||||||
|
|
||||||
ToStringResult result = toStringDetailed(type, opts);
|
ToStringResult result = toStringDetailed(binding.typeId, opts);
|
||||||
opts.nameMap = std::move(result.nameMap);
|
opts.nameMap = std::move(result.nameMap);
|
||||||
output += result.name;
|
output += result.name;
|
||||||
output += "\"";
|
output += "\"";
|
||||||
|
@ -30,7 +30,7 @@ static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& op
|
||||||
output += "},\"children\":[";
|
output += "},\"children\":[";
|
||||||
comma = false;
|
comma = false;
|
||||||
|
|
||||||
for (const Scope2* child : scope->children)
|
for (const Scope* child : scope->children)
|
||||||
{
|
{
|
||||||
if (comma)
|
if (comma)
|
||||||
output += ",";
|
output += ",";
|
||||||
|
@ -96,7 +96,7 @@ std::string ConstraintSolverLogger::compileOutput()
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||||
{
|
{
|
||||||
std::string snapshot = "{\"type\":\"boundary\",\"rootScope\":";
|
std::string snapshot = "{\"type\":\"boundary\",\"rootScope\":";
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* rootScope, st
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolverLogger::prepareStepSnapshot(
|
void ConstraintSolverLogger::prepareStepSnapshot(
|
||||||
const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||||
{
|
{
|
||||||
// LUAU_ASSERT(!preparedSnapshot);
|
// LUAU_ASSERT(!preparedSnapshot);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||||
LUAU_FASTFLAG(LuauCheckLenMT)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -123,6 +122,7 @@ declare function tonumber<T>(value: T, radix: number?): number?
|
||||||
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
||||||
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
||||||
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
|
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
|
||||||
|
declare function rawlen<K, V>(obj: {[K]: V} | string): number
|
||||||
|
|
||||||
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
|
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
|
||||||
|
|
||||||
|
@ -206,10 +206,6 @@ std::string getBuiltinDefinitionSource()
|
||||||
|
|
||||||
std::string result = kBuiltinDefinitionLuaSrc;
|
std::string result = kBuiltinDefinitionLuaSrc;
|
||||||
|
|
||||||
// TODO: move this into kBuiltinDefinitionLuaSrc
|
|
||||||
if (FFlag::LuauCheckLenMT)
|
|
||||||
result += "declare function rawlen<K, V>(obj: {[K]: V} | string): number\n";
|
|
||||||
|
|
||||||
if (FFlag::LuauUnknownAndNeverType)
|
if (FFlag::LuauUnknownAndNeverType)
|
||||||
result += "declare function error<T>(message: T, level: number?): never\n";
|
result += "declare function error<T>(message: T, level: number?): never\n";
|
||||||
else
|
else
|
||||||
|
|
|
@ -766,35 +766,35 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
|
||||||
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
|
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull<Scope2> Frontend::getGlobalScope2()
|
NotNull<Scope> Frontend::getGlobalScope()
|
||||||
{
|
{
|
||||||
if (!globalScope2)
|
if (!globalScope)
|
||||||
{
|
{
|
||||||
const SingletonTypes& singletonTypes = getSingletonTypes();
|
const SingletonTypes& singletonTypes = getSingletonTypes();
|
||||||
|
|
||||||
globalScope2 = std::make_unique<Scope2>();
|
globalScope = std::make_unique<Scope>(singletonTypes.anyTypePack);
|
||||||
globalScope2->typeBindings["nil"] = singletonTypes.nilType;
|
globalScope->typeBindings["nil"] = singletonTypes.nilType;
|
||||||
globalScope2->typeBindings["number"] = singletonTypes.numberType;
|
globalScope->typeBindings["number"] = singletonTypes.numberType;
|
||||||
globalScope2->typeBindings["string"] = singletonTypes.stringType;
|
globalScope->typeBindings["string"] = singletonTypes.stringType;
|
||||||
globalScope2->typeBindings["boolean"] = singletonTypes.booleanType;
|
globalScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||||
globalScope2->typeBindings["thread"] = singletonTypes.threadType;
|
globalScope->typeBindings["thread"] = singletonTypes.threadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NotNull(globalScope2.get());
|
return NotNull(globalScope.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope)
|
ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope)
|
||||||
{
|
{
|
||||||
ModulePtr result = std::make_shared<Module>();
|
ModulePtr result = std::make_shared<Module>();
|
||||||
|
|
||||||
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope2()};
|
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope()};
|
||||||
cgb.visit(sourceModule.root);
|
cgb.visit(sourceModule.root);
|
||||||
result->errors = std::move(cgb.errors);
|
result->errors = std::move(cgb.errors);
|
||||||
|
|
||||||
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope)};
|
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope)};
|
||||||
cs.run();
|
cs.run();
|
||||||
|
|
||||||
result->scope2s = std::move(cgb.scopes);
|
result->scopes = std::move(cgb.scopes);
|
||||||
result->astTypes = std::move(cgb.astTypes);
|
result->astTypes = std::move(cgb.astTypes);
|
||||||
result->astTypePacks = std::move(cgb.astTypePacks);
|
result->astTypePacks = std::move(cgb.astTypePacks);
|
||||||
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
||||||
|
|
|
@ -103,8 +103,8 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
|
|
||||||
void write(double d)
|
void write(double d)
|
||||||
{
|
{
|
||||||
char b[256];
|
char b[32];
|
||||||
sprintf(b, "%.17g", d);
|
snprintf(b, sizeof(b), "%.17g", d);
|
||||||
writeRaw(b);
|
writeRaw(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,29 +100,20 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||||
|
|
||||||
CloneState cloneState;
|
CloneState cloneState;
|
||||||
|
|
||||||
ScopePtr moduleScope = FFlag::DebugLuauDeferredConstraintResolution ? nullptr : getModuleScope();
|
ScopePtr moduleScope = getModuleScope();
|
||||||
Scope2* moduleScope2 = FFlag::DebugLuauDeferredConstraintResolution ? getModuleScope2() : nullptr;
|
|
||||||
|
|
||||||
TypePackId returnType = FFlag::DebugLuauDeferredConstraintResolution ? moduleScope2->returnType : moduleScope->returnType;
|
TypePackId returnType = moduleScope->returnType;
|
||||||
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
|
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
|
||||||
std::unordered_map<Name, TypeFun>* exportedTypeBindings =
|
std::unordered_map<Name, TypeFun>* exportedTypeBindings =
|
||||||
FFlag::DebugLuauDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
|
FFlag::DebugLuauDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
|
||||||
|
|
||||||
returnType = clone(returnType, interfaceTypes, cloneState);
|
returnType = clone(returnType, interfaceTypes, cloneState);
|
||||||
|
|
||||||
if (moduleScope)
|
moduleScope->returnType = returnType;
|
||||||
|
if (varargPack)
|
||||||
{
|
{
|
||||||
moduleScope->returnType = returnType;
|
varargPack = clone(*varargPack, interfaceTypes, cloneState);
|
||||||
if (varargPack)
|
moduleScope->varargPack = varargPack;
|
||||||
{
|
|
||||||
varargPack = clone(*varargPack, interfaceTypes, cloneState);
|
|
||||||
moduleScope->varargPack = varargPack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(moduleScope2);
|
|
||||||
moduleScope2->returnType = returnType; // TODO varargPack
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ForceNormal forceNormal{&interfaceTypes};
|
ForceNormal forceNormal{&interfaceTypes};
|
||||||
|
@ -201,10 +192,4 @@ ScopePtr Module::getModuleScope() const
|
||||||
return scopes.front().second;
|
return scopes.front().second;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope2* Module::getModuleScope2() const
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!scope2s.empty());
|
|
||||||
return scope2s.front().second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -16,13 +16,13 @@ namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
/// @return true if outer encloses inner
|
/// @return true if outer encloses inner
|
||||||
static bool subsumes(Scope2* outer, Scope2* inner)
|
static bool subsumes(Scope* outer, Scope* inner)
|
||||||
{
|
{
|
||||||
while (inner)
|
while (inner)
|
||||||
{
|
{
|
||||||
if (inner == outer)
|
if (inner == outer)
|
||||||
return true;
|
return true;
|
||||||
inner = inner->parent;
|
inner = inner->parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -33,7 +33,7 @@ struct Quantifier final : TypeVarOnceVisitor
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
std::vector<TypeId> generics;
|
std::vector<TypeId> generics;
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
Scope2* scope = nullptr;
|
Scope* scope = nullptr;
|
||||||
bool seenGenericType = false;
|
bool seenGenericType = false;
|
||||||
bool seenMutableType = false;
|
bool seenMutableType = false;
|
||||||
|
|
||||||
|
@ -43,20 +43,20 @@ struct Quantifier final : TypeVarOnceVisitor
|
||||||
LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution);
|
LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Quantifier(Scope2* scope)
|
explicit Quantifier(Scope* scope)
|
||||||
: scope(scope)
|
: scope(scope)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
|
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return true if outer encloses inner
|
/// @return true if outer encloses inner
|
||||||
bool subsumes(Scope2* outer, Scope2* inner)
|
bool subsumes(Scope* outer, Scope* inner)
|
||||||
{
|
{
|
||||||
while (inner)
|
while (inner)
|
||||||
{
|
{
|
||||||
if (inner == outer)
|
if (inner == outer)
|
||||||
return true;
|
return true;
|
||||||
inner = inner->parent;
|
inner = inner->parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -216,7 +216,7 @@ void quantify(TypeId ty, TypeLevel level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void quantify(TypeId ty, Scope2* scope)
|
void quantify(TypeId ty, Scope* scope)
|
||||||
{
|
{
|
||||||
Quantifier q{scope};
|
Quantifier q{scope};
|
||||||
q.traverse(ty);
|
q.traverse(ty);
|
||||||
|
@ -240,11 +240,11 @@ void quantify(TypeId ty, Scope2* scope)
|
||||||
|
|
||||||
struct PureQuantifier : Substitution
|
struct PureQuantifier : Substitution
|
||||||
{
|
{
|
||||||
Scope2* scope;
|
Scope* scope;
|
||||||
std::vector<TypeId> insertedGenerics;
|
std::vector<TypeId> insertedGenerics;
|
||||||
std::vector<TypePackId> insertedGenericPacks;
|
std::vector<TypePackId> insertedGenericPacks;
|
||||||
|
|
||||||
PureQuantifier(TypeArena* arena, Scope2* scope)
|
PureQuantifier(TypeArena* arena, Scope* scope)
|
||||||
: Substitution(TxnLog::empty(), arena)
|
: Substitution(TxnLog::empty(), arena)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
{
|
{
|
||||||
|
@ -322,7 +322,7 @@ struct PureQuantifier : Substitution
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope)
|
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope)
|
||||||
{
|
{
|
||||||
PureQuantifier quantifier{arena, scope};
|
PureQuantifier quantifier{arena, scope};
|
||||||
std::optional<TypeId> result = quantifier.substitute(ty);
|
std::optional<TypeId> result = quantifier.substitute(ty);
|
||||||
|
|
|
@ -21,22 +21,6 @@ Scope::Scope(const ScopePtr& parent, int subLevel)
|
||||||
level.subLevel = subLevel;
|
level.subLevel = subLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> Scope::lookup(const Symbol& name)
|
|
||||||
{
|
|
||||||
Scope* scope = this;
|
|
||||||
|
|
||||||
while (scope)
|
|
||||||
{
|
|
||||||
auto it = scope->bindings.find(name);
|
|
||||||
if (it != scope->bindings.end())
|
|
||||||
return it->second.typeId;
|
|
||||||
|
|
||||||
scope = scope->parent.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TypeFun> Scope::lookupType(const Name& name)
|
std::optional<TypeFun> Scope::lookupType(const Name& name)
|
||||||
{
|
{
|
||||||
const Scope* scope = this;
|
const Scope* scope = this;
|
||||||
|
@ -121,48 +105,48 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> Scope2::lookup(Symbol sym)
|
std::optional<TypeId> Scope::lookup(Symbol sym)
|
||||||
{
|
{
|
||||||
Scope2* s = this;
|
Scope* s = this;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto it = s->bindings.find(sym);
|
auto it = s->bindings.find(sym);
|
||||||
if (it != s->bindings.end())
|
if (it != s->bindings.end())
|
||||||
return it->second;
|
return it->second.typeId;
|
||||||
|
|
||||||
if (s->parent)
|
if (s->parent)
|
||||||
s = s->parent;
|
s = s->parent.get();
|
||||||
else
|
else
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> Scope2::lookupTypeBinding(const Name& name)
|
std::optional<TypeId> Scope::lookupTypeBinding(const Name& name)
|
||||||
{
|
{
|
||||||
Scope2* s = this;
|
Scope* s = this;
|
||||||
while (s)
|
while (s)
|
||||||
{
|
{
|
||||||
auto it = s->typeBindings.find(name);
|
auto it = s->typeBindings.find(name);
|
||||||
if (it != s->typeBindings.end())
|
if (it != s->typeBindings.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
s = s->parent;
|
s = s->parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypePackId> Scope2::lookupTypePackBinding(const Name& name)
|
std::optional<TypePackId> Scope::lookupTypePackBinding(const Name& name)
|
||||||
{
|
{
|
||||||
Scope2* s = this;
|
Scope* s = this;
|
||||||
while (s)
|
while (s)
|
||||||
{
|
{
|
||||||
auto it = s->typePackBindings.find(name);
|
auto it = s->typePackBindings.find(name);
|
||||||
if (it != s->typePackBindings.end())
|
if (it != s->typePackBindings.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
s = s->parent;
|
s = s->parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
||||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSpecialTypesAsterisked, false)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prefix generic typenames with gen-
|
* Prefix generic typenames with gen-
|
||||||
|
@ -277,7 +278,10 @@ struct TypeVarStringifier
|
||||||
if (tv->ty.valueless_by_exception())
|
if (tv->ty.valueless_by_exception())
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
state.emit("< VALUELESS BY EXCEPTION >");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("* VALUELESS BY EXCEPTION *");
|
||||||
|
else
|
||||||
|
state.emit("< VALUELESS BY EXCEPTION >");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +457,10 @@ struct TypeVarStringifier
|
||||||
if (state.hasSeen(&ftv))
|
if (state.hasSeen(&ftv))
|
||||||
{
|
{
|
||||||
state.result.cycle = true;
|
state.result.cycle = true;
|
||||||
state.emit("<CYCLE>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*CYCLE*");
|
||||||
|
else
|
||||||
|
state.emit("<CYCLE>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +568,10 @@ struct TypeVarStringifier
|
||||||
if (state.hasSeen(&ttv))
|
if (state.hasSeen(&ttv))
|
||||||
{
|
{
|
||||||
state.result.cycle = true;
|
state.result.cycle = true;
|
||||||
state.emit("<CYCLE>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*CYCLE*");
|
||||||
|
else
|
||||||
|
state.emit("<CYCLE>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,7 +701,10 @@ struct TypeVarStringifier
|
||||||
if (state.hasSeen(&uv))
|
if (state.hasSeen(&uv))
|
||||||
{
|
{
|
||||||
state.result.cycle = true;
|
state.result.cycle = true;
|
||||||
state.emit("<CYCLE>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*CYCLE*");
|
||||||
|
else
|
||||||
|
state.emit("<CYCLE>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,7 +771,10 @@ struct TypeVarStringifier
|
||||||
if (state.hasSeen(&uv))
|
if (state.hasSeen(&uv))
|
||||||
{
|
{
|
||||||
state.result.cycle = true;
|
state.result.cycle = true;
|
||||||
state.emit("<CYCLE>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*CYCLE*");
|
||||||
|
else
|
||||||
|
state.emit("<CYCLE>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,7 +819,10 @@ struct TypeVarStringifier
|
||||||
void operator()(TypeId, const ErrorTypeVar& tv)
|
void operator()(TypeId, const ErrorTypeVar& tv)
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||||
|
else
|
||||||
|
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypeId, const LazyTypeVar& ltv)
|
void operator()(TypeId, const LazyTypeVar& ltv)
|
||||||
|
@ -857,7 +876,10 @@ struct TypePackStringifier
|
||||||
if (tp->ty.valueless_by_exception())
|
if (tp->ty.valueless_by_exception())
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
state.emit("< VALUELESS TP BY EXCEPTION >");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("* VALUELESS TP BY EXCEPTION *");
|
||||||
|
else
|
||||||
|
state.emit("< VALUELESS TP BY EXCEPTION >");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,7 +903,10 @@ struct TypePackStringifier
|
||||||
if (state.hasSeen(&tp))
|
if (state.hasSeen(&tp))
|
||||||
{
|
{
|
||||||
state.result.cycle = true;
|
state.result.cycle = true;
|
||||||
state.emit("<CYCLETP>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*CYCLETP*");
|
||||||
|
else
|
||||||
|
state.emit("<CYCLETP>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -926,14 +951,22 @@ struct TypePackStringifier
|
||||||
void operator()(TypePackId, const Unifiable::Error& error)
|
void operator()(TypePackId, const Unifiable::Error& error)
|
||||||
{
|
{
|
||||||
state.result.error = true;
|
state.result.error = true;
|
||||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||||
|
else
|
||||||
|
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(TypePackId, const VariadicTypePack& pack)
|
void operator()(TypePackId, const VariadicTypePack& pack)
|
||||||
{
|
{
|
||||||
state.emit("...");
|
state.emit("...");
|
||||||
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
|
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
|
||||||
state.emit("<hidden>");
|
{
|
||||||
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
state.emit("*hidden*");
|
||||||
|
else
|
||||||
|
state.emit("<hidden>");
|
||||||
|
}
|
||||||
stringify(pack.ty);
|
stringify(pack.ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,7 +1161,11 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
||||||
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
||||||
{
|
{
|
||||||
result.truncated = true;
|
result.truncated = true;
|
||||||
result.name += "... <TRUNCATED>";
|
|
||||||
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
result.name += "... *TRUNCATED*";
|
||||||
|
else
|
||||||
|
result.name += "... <TRUNCATED>";
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1199,7 +1236,12 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
||||||
result.name += "... <TRUNCATED>";
|
{
|
||||||
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
result.name += "... *TRUNCATED*";
|
||||||
|
else
|
||||||
|
result.name += "... <TRUNCATED>";
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,9 @@ struct TypeChecker2 : public AstVisitor
|
||||||
|
|
||||||
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
|
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
|
||||||
{
|
{
|
||||||
|
if (exprs.size == 0)
|
||||||
|
return arena.addTypePack(TypePack{{}, std::nullopt});
|
||||||
|
|
||||||
std::vector<TypeId> head;
|
std::vector<TypeId> head;
|
||||||
|
|
||||||
for (size_t i = 0; i < exprs.size - 1; ++i)
|
for (size_t i = 0; i < exprs.size - 1; ++i)
|
||||||
|
@ -80,14 +83,14 @@ struct TypeChecker2 : public AstVisitor
|
||||||
return arena.addTypePack(TypePack{head, tail});
|
return arena.addTypePack(TypePack{head, tail});
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope2* findInnermostScope(Location location)
|
Scope* findInnermostScope(Location location)
|
||||||
{
|
{
|
||||||
Scope2* bestScope = module->getModuleScope2();
|
Scope* bestScope = module->getModuleScope().get();
|
||||||
Location bestLocation = module->scope2s[0].first;
|
Location bestLocation = module->scopes[0].first;
|
||||||
|
|
||||||
for (size_t i = 0; i < module->scope2s.size(); ++i)
|
for (size_t i = 0; i < module->scopes.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& [scopeBounds, scope] = module->scope2s[i];
|
auto& [scopeBounds, scope] = module->scopes[i];
|
||||||
if (scopeBounds.encloses(location))
|
if (scopeBounds.encloses(location))
|
||||||
{
|
{
|
||||||
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
|
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
|
||||||
|
@ -181,7 +184,7 @@ struct TypeChecker2 : public AstVisitor
|
||||||
|
|
||||||
bool visit(AstStatReturn* ret) override
|
bool visit(AstStatReturn* ret) override
|
||||||
{
|
{
|
||||||
Scope2* scope = findInnermostScope(ret->location);
|
Scope* scope = findInnermostScope(ret->location);
|
||||||
TypePackId expectedRetType = scope->returnType;
|
TypePackId expectedRetType = scope->returnType;
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
|
@ -359,7 +362,7 @@ struct TypeChecker2 : public AstVisitor
|
||||||
|
|
||||||
bool visit(AstTypeReference* ty) override
|
bool visit(AstTypeReference* ty) override
|
||||||
{
|
{
|
||||||
Scope2* scope = findInnermostScope(ty->location);
|
Scope* scope = findInnermostScope(ty->location);
|
||||||
|
|
||||||
// TODO: Imported types
|
// TODO: Imported types
|
||||||
// TODO: Generic types
|
// TODO: Generic types
|
||||||
|
|
|
@ -35,7 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
|
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
|
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
||||||
|
@ -45,7 +45,6 @@ LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
|
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
|
||||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFalsyPredicateReturnsNilInstead, false)
|
LUAU_FASTFLAGVARIABLE(LuauFalsyPredicateReturnsNilInstead, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCheckLenMT, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
|
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
|
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
|
||||||
|
@ -1667,7 +1666,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
||||||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||||
|
|
||||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||||
ftv->hasSelf = true;
|
ftv->hasSelf = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2465,7 +2464,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||||
|
|
||||||
DenseHashSet<TypeId> seen{nullptr};
|
DenseHashSet<TypeId> seen{nullptr};
|
||||||
|
|
||||||
if (FFlag::LuauCheckLenMT && typeCouldHaveMetatable(operandType))
|
if (typeCouldHaveMetatable(operandType))
|
||||||
{
|
{
|
||||||
if (auto fnt = findMetatableEntry(operandType, "__len", expr.location, /* addErrors= */ true))
|
if (auto fnt = findMetatableEntry(operandType, "__len", expr.location, /* addErrors= */ true))
|
||||||
{
|
{
|
||||||
|
@ -3640,6 +3639,9 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
||||||
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!currentModule->astTypes.find(&function))
|
||||||
|
currentModule->astTypes[&function] = ty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ice("Checking non functional type");
|
ice("Checking non functional type");
|
||||||
|
|
|
@ -12,7 +12,7 @@ Free::Free(TypeLevel level)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Free::Free(Scope2* scope)
|
Free::Free(Scope* scope)
|
||||||
: scope(scope)
|
: scope(scope)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ Generic::Generic(const Name& name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Generic::Generic(Scope2* scope)
|
Generic::Generic(Scope* scope)
|
||||||
: index(++nextIndex)
|
: index(++nextIndex)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ Generic::Generic(TypeLevel level, const Name& name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Generic::Generic(Scope2* scope, const Name& name)
|
Generic::Generic(Scope* scope, const Name& name)
|
||||||
: index(++nextIndex)
|
: index(++nextIndex)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, name(name)
|
, name(name)
|
||||||
|
|
|
@ -24,8 +24,6 @@ bool lua_telemetry_parsed_named_non_function_type = false;
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorParseIntegerIssues, false)
|
LUAU_FASTFLAGVARIABLE(LuauErrorParseIntegerIssues, false)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysCaptureHotComments, false)
|
|
||||||
|
|
||||||
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
||||||
bool lua_telemetry_parsed_out_of_range_hex_integer = false;
|
bool lua_telemetry_parsed_out_of_range_hex_integer = false;
|
||||||
bool lua_telemetry_parsed_double_prefix_hex_integer = false;
|
bool lua_telemetry_parsed_double_prefix_hex_integer = false;
|
||||||
|
@ -2920,39 +2918,34 @@ AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const
|
||||||
|
|
||||||
void Parser::nextLexeme()
|
void Parser::nextLexeme()
|
||||||
{
|
{
|
||||||
if (options.captureComments || FFlag::LuauAlwaysCaptureHotComments)
|
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
|
||||||
|
|
||||||
|
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
||||||
{
|
{
|
||||||
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
|
const Lexeme& lexeme = lexer.current();
|
||||||
|
|
||||||
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
if (options.captureComments)
|
||||||
|
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||||
|
|
||||||
|
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||||
|
// The parser will turn this into a proper syntax error.
|
||||||
|
if (lexeme.type == Lexeme::BrokenComment)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
|
||||||
|
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
||||||
{
|
{
|
||||||
const Lexeme& lexeme = lexer.current();
|
const char* text = lexeme.data;
|
||||||
|
|
||||||
if (options.captureComments)
|
unsigned int end = lexeme.length;
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
while (end > 0 && isSpace(text[end - 1]))
|
||||||
|
--end;
|
||||||
|
|
||||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
||||||
// The parser will turn this into a proper syntax error.
|
|
||||||
if (lexeme.type == Lexeme::BrokenComment)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
|
|
||||||
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
|
||||||
{
|
|
||||||
const char* text = lexeme.data;
|
|
||||||
|
|
||||||
unsigned int end = lexeme.length;
|
|
||||||
while (end > 0 && isSpace(text[end - 1]))
|
|
||||||
--end;
|
|
||||||
|
|
||||||
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
|
||||||
}
|
|
||||||
|
|
||||||
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
lexer.next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -1,8 +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
|
||||||
#include "Repl.h"
|
#include "Repl.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
return replMain(argc, argv);
|
return replMain(argc, argv);
|
||||||
|
|
|
@ -25,8 +25,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
|
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
|
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -276,9 +274,6 @@ struct Compiler
|
||||||
// returns true if node can return multiple values; may conservatively return true even if expr is known to return just a single value
|
// returns true if node can return multiple values; may conservatively return true even if expr is known to return just a single value
|
||||||
bool isExprMultRet(AstExpr* node)
|
bool isExprMultRet(AstExpr* node)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauCompileBetterMultret)
|
|
||||||
return node->is<AstExprCall>() || node->is<AstExprVarargs>();
|
|
||||||
|
|
||||||
AstExprCall* expr = node->as<AstExprCall>();
|
AstExprCall* expr = node->as<AstExprCall>();
|
||||||
if (!expr)
|
if (!expr)
|
||||||
return node->is<AstExprVarargs>();
|
return node->is<AstExprVarargs>();
|
||||||
|
@ -310,27 +305,10 @@ struct Compiler
|
||||||
if (AstExprCall* expr = node->as<AstExprCall>())
|
if (AstExprCall* expr = node->as<AstExprCall>())
|
||||||
{
|
{
|
||||||
// Optimization: convert multret calls that always return one value to fixedret calls; this facilitates inlining/constant folding
|
// Optimization: convert multret calls that always return one value to fixedret calls; this facilitates inlining/constant folding
|
||||||
if (options.optimizationLevel >= 2)
|
if (options.optimizationLevel >= 2 && !isExprMultRet(node))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauCompileBetterMultret)
|
compileExprTemp(node, target);
|
||||||
{
|
return false;
|
||||||
if (!isExprMultRet(node))
|
|
||||||
{
|
|
||||||
compileExprTemp(node, target);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AstExprFunction* func = getFunctionExpr(expr->func);
|
|
||||||
Function* fi = func ? functions.find(func) : nullptr;
|
|
||||||
|
|
||||||
if (fi && fi->returnsOne)
|
|
||||||
{
|
|
||||||
compileExprTemp(node, target);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We temporarily swap out regTop to have targetTop work correctly...
|
// We temporarily swap out regTop to have targetTop work correctly...
|
||||||
|
@ -3437,30 +3415,7 @@ struct Compiler
|
||||||
|
|
||||||
bool visit(AstStatReturn* stat) override
|
bool visit(AstStatReturn* stat) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauCompileBetterMultret)
|
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
|
||||||
{
|
|
||||||
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
|
|
||||||
}
|
|
||||||
else if (stat->list.size == 1)
|
|
||||||
{
|
|
||||||
AstExpr* value = stat->list.data[0];
|
|
||||||
|
|
||||||
if (AstExprCall* expr = value->as<AstExprCall>())
|
|
||||||
{
|
|
||||||
AstExprFunction* func = self->getFunctionExpr(expr->func);
|
|
||||||
Function* fi = func ? self->functions.find(func) : nullptr;
|
|
||||||
|
|
||||||
returnsOne &= fi && fi->returnsOne;
|
|
||||||
}
|
|
||||||
else if (value->is<AstExprVarargs>())
|
|
||||||
{
|
|
||||||
returnsOne = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnsOne = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3601,7 +3556,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
||||||
trackValues(compiler.globals, compiler.variables, root);
|
trackValues(compiler.globals, compiler.variables, root);
|
||||||
|
|
||||||
// builtin folding is enabled on optimization level 2 since we can't deoptimize folding at runtime
|
// builtin folding is enabled on optimization level 2 since we can't deoptimize folding at runtime
|
||||||
if (options.optimizationLevel >= 2 && FFlag::LuauCompileFoldBuiltins)
|
if (options.optimizationLevel >= 2)
|
||||||
compiler.builtinsFold = &compiler.builtins;
|
compiler.builtinsFold = &compiler.builtins;
|
||||||
|
|
||||||
if (options.optimizationLevel >= 1)
|
if (options.optimizationLevel >= 1)
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileModelBuiltins, false)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -155,7 +153,7 @@ struct CostVisitor : AstVisitor
|
||||||
{
|
{
|
||||||
// builtin cost modeling is different from regular calls because we use FASTCALL to compile these
|
// builtin cost modeling is different from regular calls because we use FASTCALL to compile these
|
||||||
// thus we use a cheaper baseline, don't account for function, and assume constant/local copy is free
|
// thus we use a cheaper baseline, don't account for function, and assume constant/local copy is free
|
||||||
bool builtin = FFlag::LuauCompileModelBuiltins && builtins.find(expr) != nullptr;
|
bool builtin = builtins.find(expr) != nullptr;
|
||||||
bool builtinShort = builtin && expr->args.size <= 2; // FASTCALL1/2
|
bool builtinShort = builtin && expr->args.size <= 2; // FASTCALL1/2
|
||||||
|
|
||||||
Cost cost = builtin ? 2 : 3;
|
Cost cost = builtin ? 2 : 3;
|
||||||
|
|
|
@ -267,6 +267,7 @@ if(TARGET Luau.UnitTest)
|
||||||
tests/Error.test.cpp
|
tests/Error.test.cpp
|
||||||
tests/Frontend.test.cpp
|
tests/Frontend.test.cpp
|
||||||
tests/JsonEncoder.test.cpp
|
tests/JsonEncoder.test.cpp
|
||||||
|
tests/Lexer.test.cpp
|
||||||
tests/Linter.test.cpp
|
tests/Linter.test.cpp
|
||||||
tests/LValue.test.cpp
|
tests/LValue.test.cpp
|
||||||
tests/Module.test.cpp
|
tests/Module.test.cpp
|
||||||
|
|
|
@ -34,8 +34,6 @@
|
||||||
* therefore call luaC_checkGC before luaC_checkthreadsleep to guarantee the object is pushed to an awake thread.
|
* therefore call luaC_checkGC before luaC_checkthreadsleep to guarantee the object is pushed to an awake thread.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLazyAtoms)
|
|
||||||
|
|
||||||
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
||||||
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
||||||
"$URL: www.lua.org $\n";
|
"$URL: www.lua.org $\n";
|
||||||
|
@ -54,7 +52,6 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
#define updateatom(L, ts) \
|
#define updateatom(L, ts) \
|
||||||
if (FFlag::LuauLazyAtoms) \
|
|
||||||
{ \
|
{ \
|
||||||
if (ts->atom == ATOM_UNDEF) \
|
if (ts->atom == ATOM_UNDEF) \
|
||||||
ts->atom = L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1; \
|
ts->atom = L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1; \
|
||||||
|
|
|
@ -130,15 +130,14 @@ static int db_traceback(lua_State* L)
|
||||||
|
|
||||||
if (ar.currentline > 0)
|
if (ar.currentline > 0)
|
||||||
{
|
{
|
||||||
char line[32];
|
char line[32]; // manual conversion for performance
|
||||||
#ifdef _MSC_VER
|
char* lineend = line + sizeof(line);
|
||||||
_itoa(ar.currentline, line, 10); // 5x faster than sprintf
|
char* lineptr = lineend;
|
||||||
#else
|
for (unsigned int r = ar.currentline; r > 0; r /= 10)
|
||||||
sprintf(line, "%d", ar.currentline);
|
*--lineptr = '0' + (r % 10);
|
||||||
#endif
|
|
||||||
|
|
||||||
luaL_addchar(&buf, ':');
|
luaL_addchar(&buf, ':');
|
||||||
luaL_addstring(&buf, line);
|
luaL_addlstring(&buf, lineptr, lineend - lineptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ar.name)
|
if (ar.name)
|
||||||
|
|
|
@ -529,7 +529,7 @@ const char* lua_debugtrace(lua_State* L)
|
||||||
if (ar.currentline > 0)
|
if (ar.currentline > 0)
|
||||||
{
|
{
|
||||||
char line[32];
|
char line[32];
|
||||||
sprintf(line, ":%d", ar.currentline);
|
snprintf(line, sizeof(line), ":%d", ar.currentline);
|
||||||
|
|
||||||
offset = append(buf, sizeof(buf), offset, line);
|
offset = append(buf, sizeof(buf), offset, line);
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@ const char* lua_debugtrace(lua_State* L)
|
||||||
if (depth > limit1 + limit2 && level == limit1 - 1)
|
if (depth > limit1 + limit2 && level == limit1 - 1)
|
||||||
{
|
{
|
||||||
char skip[32];
|
char skip[32];
|
||||||
sprintf(skip, "... (+%d frames)\n", int(depth - limit1 - limit2));
|
snprintf(skip, sizeof(skip), "... (+%d frames)\n", int(depth - limit1 - limit2));
|
||||||
|
|
||||||
offset = append(buf, sizeof(buf), offset, skip);
|
offset = append(buf, sizeof(buf), offset, skip);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLazyAtoms, false)
|
|
||||||
|
|
||||||
unsigned int luaS_hash(const char* str, size_t len)
|
unsigned int luaS_hash(const char* str, size_t len)
|
||||||
{
|
{
|
||||||
// Note that this hashing algorithm is replicated in BytecodeBuilder.cpp, BytecodeBuilder::getStringHash
|
// Note that this hashing algorithm is replicated in BytecodeBuilder.cpp, BytecodeBuilder::getStringHash
|
||||||
|
@ -84,7 +82,7 @@ static TString* newlstr(lua_State* L, const char* str, size_t l, unsigned int h)
|
||||||
ts->memcat = L->activememcat;
|
ts->memcat = L->activememcat;
|
||||||
memcpy(ts->data, str, l);
|
memcpy(ts->data, str, l);
|
||||||
ts->data[l] = '\0'; /* ending 0 */
|
ts->data[l] = '\0'; /* ending 0 */
|
||||||
ts->atom = FFlag::LuauLazyAtoms ? ATOM_UNDEF : L->global->cb.useratom ? L->global->cb.useratom(ts->data, l) : -1;
|
ts->atom = ATOM_UNDEF;
|
||||||
tb = &L->global->strt;
|
tb = &L->global->strt;
|
||||||
h = lmod(h, tb->size);
|
h = lmod(h, tb->size);
|
||||||
ts->next = tb->hash[h]; /* chain new entry */
|
ts->next = tb->hash[h]; /* chain new entry */
|
||||||
|
@ -165,9 +163,7 @@ TString* luaS_buffinish(lua_State* L, TString* ts)
|
||||||
|
|
||||||
ts->hash = h;
|
ts->hash = h;
|
||||||
ts->data[ts->len] = '\0'; // ending 0
|
ts->data[ts->len] = '\0'; // ending 0
|
||||||
|
ts->atom = ATOM_UNDEF;
|
||||||
// Complete string object
|
|
||||||
ts->atom = FFlag::LuauLazyAtoms ? ATOM_UNDEF : L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1;
|
|
||||||
ts->next = tb->hash[bucket]; // chain new entry
|
ts->next = tb->hash[bucket]; // chain new entry
|
||||||
tb->hash[bucket] = ts;
|
tb->hash[bucket] = ts;
|
||||||
|
|
||||||
|
|
|
@ -979,14 +979,14 @@ static int str_format(lua_State* L)
|
||||||
{
|
{
|
||||||
case 'c':
|
case 'c':
|
||||||
{
|
{
|
||||||
sprintf(buff, form, (int)luaL_checknumber(L, arg));
|
snprintf(buff, sizeof(buff), form, (int)luaL_checknumber(L, arg));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
addInt64Format(form, formatIndicator, formatItemSize);
|
addInt64Format(form, formatIndicator, formatItemSize);
|
||||||
sprintf(buff, form, (long long)luaL_checknumber(L, arg));
|
snprintf(buff, sizeof(buff), form, (long long)luaL_checknumber(L, arg));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'o':
|
case 'o':
|
||||||
|
@ -997,7 +997,7 @@ static int str_format(lua_State* L)
|
||||||
double argValue = luaL_checknumber(L, arg);
|
double argValue = luaL_checknumber(L, arg);
|
||||||
addInt64Format(form, formatIndicator, formatItemSize);
|
addInt64Format(form, formatIndicator, formatItemSize);
|
||||||
unsigned long long v = (argValue < 0) ? (unsigned long long)(long long)argValue : (unsigned long long)argValue;
|
unsigned long long v = (argValue < 0) ? (unsigned long long)(long long)argValue : (unsigned long long)argValue;
|
||||||
sprintf(buff, form, v);
|
snprintf(buff, sizeof(buff), form, v);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'e':
|
case 'e':
|
||||||
|
@ -1006,7 +1006,7 @@ static int str_format(lua_State* L)
|
||||||
case 'g':
|
case 'g':
|
||||||
case 'G':
|
case 'G':
|
||||||
{
|
{
|
||||||
sprintf(buff, form, (double)luaL_checknumber(L, arg));
|
snprintf(buff, sizeof(buff), form, (double)luaL_checknumber(L, arg));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'q':
|
case 'q':
|
||||||
|
@ -1028,7 +1028,7 @@ static int str_format(lua_State* L)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf(buff, form, s);
|
snprintf(buff, sizeof(buff), form, s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,6 @@ static_assert(TKey{{NULL}, {0}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough
|
||||||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
|
static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
|
||||||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
|
static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
|
||||||
|
|
||||||
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
|
||||||
#define invalidateTMcache(t) t->tmcache = 0
|
|
||||||
|
|
||||||
// empty hash data points to dummynode so that we can always dereference it
|
// empty hash data points to dummynode so that we can always dereference it
|
||||||
const LuaNode luaH_dummynode = {
|
const LuaNode luaH_dummynode = {
|
||||||
{{NULL}, {0}, LUA_TNIL}, /* value */
|
{{NULL}, {0}, LUA_TNIL}, /* value */
|
||||||
|
@ -667,15 +664,18 @@ TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
|
||||||
if (p != luaO_nilobject)
|
if (p != luaO_nilobject)
|
||||||
return cast_to(TValue*, p);
|
return cast_to(TValue*, p);
|
||||||
else
|
else
|
||||||
{
|
return luaH_newkey(L, t, key);
|
||||||
if (ttisnil(key))
|
}
|
||||||
luaG_runerror(L, "table index is nil");
|
|
||||||
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
|
TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
|
||||||
luaG_runerror(L, "table index is NaN");
|
{
|
||||||
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
if (ttisnil(key))
|
||||||
luaG_runerror(L, "table index contains NaN");
|
luaG_runerror(L, "table index is nil");
|
||||||
return newkey(L, t, key);
|
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
|
||||||
}
|
luaG_runerror(L, "table index is NaN");
|
||||||
|
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
||||||
|
luaG_runerror(L, "table index contains NaN");
|
||||||
|
return newkey(L, t, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue* luaH_setnum(lua_State* L, Table* t, int key)
|
TValue* luaH_setnum(lua_State* L, Table* t, int key)
|
||||||
|
|
|
@ -11,12 +11,16 @@
|
||||||
|
|
||||||
#define gval2slot(t, v) int(cast_to(LuaNode*, static_cast<const TValue*>(v)) - t->node)
|
#define gval2slot(t, v) int(cast_to(LuaNode*, static_cast<const TValue*>(v)) - t->node)
|
||||||
|
|
||||||
|
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
||||||
|
#define invalidateTMcache(t) t->tmcache = 0
|
||||||
|
|
||||||
LUAI_FUNC const TValue* luaH_getnum(Table* t, int key);
|
LUAI_FUNC const TValue* luaH_getnum(Table* t, int key);
|
||||||
LUAI_FUNC TValue* luaH_setnum(lua_State* L, Table* t, int key);
|
LUAI_FUNC TValue* luaH_setnum(lua_State* L, Table* t, int key);
|
||||||
LUAI_FUNC const TValue* luaH_getstr(Table* t, TString* key);
|
LUAI_FUNC const TValue* luaH_getstr(Table* t, TString* key);
|
||||||
LUAI_FUNC TValue* luaH_setstr(lua_State* L, Table* t, TString* key);
|
LUAI_FUNC TValue* luaH_setstr(lua_State* L, Table* t, TString* key);
|
||||||
LUAI_FUNC const TValue* luaH_get(Table* t, const TValue* key);
|
LUAI_FUNC const TValue* luaH_get(Table* t, const TValue* key);
|
||||||
LUAI_FUNC TValue* luaH_set(lua_State* L, Table* t, const TValue* key);
|
LUAI_FUNC TValue* luaH_set(lua_State* L, Table* t, const TValue* key);
|
||||||
|
LUAI_FUNC TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key);
|
||||||
LUAI_FUNC Table* luaH_new(lua_State* L, int narray, int lnhash);
|
LUAI_FUNC Table* luaH_new(lua_State* L, int narray, int lnhash);
|
||||||
LUAI_FUNC void luaH_resizearray(lua_State* L, Table* t, int nasize);
|
LUAI_FUNC void luaH_resizearray(lua_State* L, Table* t, int nasize);
|
||||||
LUAI_FUNC void luaH_resizehash(lua_State* L, Table* t, int nhsize);
|
LUAI_FUNC void luaH_resizehash(lua_State* L, Table* t, int nhsize);
|
||||||
|
@ -26,4 +30,6 @@ LUAI_FUNC int luaH_getn(Table* t);
|
||||||
LUAI_FUNC Table* luaH_clone(lua_State* L, Table* tt);
|
LUAI_FUNC Table* luaH_clone(lua_State* L, Table* tt);
|
||||||
LUAI_FUNC void luaH_clear(Table* tt);
|
LUAI_FUNC void luaH_clear(Table* tt);
|
||||||
|
|
||||||
|
#define luaH_setslot(L, t, slot, key) (invalidateTMcache(t), (slot == luaO_nilobject ? luaH_newkey(L, t, key) : cast_to(TValue*, slot)))
|
||||||
|
|
||||||
extern const LuaNode luaH_dummynode;
|
extern const LuaNode luaH_dummynode;
|
||||||
|
|
|
@ -33,7 +33,7 @@ LUAU_FASTFLAGVARIABLE(LuauLenTM, false)
|
||||||
// 3. VM_PROTECT macro saves savedpc and restores base for you; most external calls need to be wrapped into that. However, it does NOT restore
|
// 3. VM_PROTECT macro saves savedpc and restores base for you; most external calls need to be wrapped into that. However, it does NOT restore
|
||||||
// ra/rb/rc!
|
// ra/rb/rc!
|
||||||
// 4. When copying an object to any existing object as a field, generally speaking you need to call luaC_barrier! Be careful with all setobj calls
|
// 4. When copying an object to any existing object as a field, generally speaking you need to call luaC_barrier! Be careful with all setobj calls
|
||||||
// 5. To make 4 easier to follow, please use setobj2s for copies to stack and setobj for other copies.
|
// 5. To make 4 easier to follow, please use setobj2s for copies to stack, setobj2t for writes to tables, and setobj for other copies.
|
||||||
// 6. You can define HARDSTACKTESTS in llimits.h which will aggressively realloc stack; with address sanitizer this should be effective at finding
|
// 6. You can define HARDSTACKTESTS in llimits.h which will aggressively realloc stack; with address sanitizer this should be effective at finding
|
||||||
// stack corruption bugs
|
// stack corruption bugs
|
||||||
// 7. Many external Lua functions can call GC! GC will *not* traverse pointers to new objects that aren't reachable from Lua root. Be careful when
|
// 7. Many external Lua functions can call GC! GC will *not* traverse pointers to new objects that aren't reachable from Lua root. Be careful when
|
||||||
|
@ -458,7 +458,7 @@ static void luau_execute(lua_State* L)
|
||||||
|
|
||||||
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
||||||
{
|
{
|
||||||
setobj(L, gval(n), ra);
|
setobj2t(L, gval(n), ra);
|
||||||
luaC_barriert(L, h, ra);
|
luaC_barriert(L, h, ra);
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
|
@ -672,7 +672,7 @@ static void luau_execute(lua_State* L)
|
||||||
// fast-path: value is in expected slot
|
// fast-path: value is in expected slot
|
||||||
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
||||||
{
|
{
|
||||||
setobj(L, gval(n), ra);
|
setobj2t(L, gval(n), ra);
|
||||||
luaC_barriert(L, h, ra);
|
luaC_barriert(L, h, ra);
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
|
@ -684,7 +684,7 @@ static void luau_execute(lua_State* L)
|
||||||
int cachedslot = gval2slot(h, res);
|
int cachedslot = gval2slot(h, res);
|
||||||
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
|
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
|
||||||
VM_PATCH_C(pc - 2, cachedslot);
|
VM_PATCH_C(pc - 2, cachedslot);
|
||||||
setobj(L, res, ra);
|
setobj2t(L, res, ra);
|
||||||
luaC_barriert(L, h, ra);
|
luaC_barriert(L, h, ra);
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLenTM)
|
LUAU_FASTFLAG(LuauLenTM)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauBetterNewindex, false)
|
||||||
|
|
||||||
/* limit for table tag-method chains (to avoid loops) */
|
/* limit for table tag-method chains (to avoid loops) */
|
||||||
#define MAXTAGLOOP 100
|
#define MAXTAGLOOP 100
|
||||||
|
|
||||||
|
@ -142,24 +144,50 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
||||||
{ /* `t' is a table? */
|
{ /* `t' is a table? */
|
||||||
Table* h = hvalue(t);
|
Table* h = hvalue(t);
|
||||||
|
|
||||||
if (h->readonly)
|
if (FFlag::LuauBetterNewindex)
|
||||||
luaG_readonlyerror(L);
|
{
|
||||||
|
const TValue* oldval = luaH_get(h, key);
|
||||||
|
|
||||||
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */
|
/* should we assign the key? (if key is valid or __newindex is not set) */
|
||||||
|
if (!ttisnil(oldval) || (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
|
||||||
|
{
|
||||||
|
if (h->readonly)
|
||||||
|
luaG_readonlyerror(L);
|
||||||
|
|
||||||
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */
|
/* luaH_set would work but would repeat the lookup so we use luaH_setslot that can reuse oldval if it's safe */
|
||||||
|
TValue* newval = luaH_setslot(L, h, oldval, key);
|
||||||
|
|
||||||
if (!ttisnil(oldval) || /* result is no nil? */
|
L->cachedslot = gval2slot(h, newval); /* remember slot to accelerate future lookups */
|
||||||
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
|
|
||||||
{ /* or no TM? */
|
setobj2t(L, newval, val);
|
||||||
setobj2t(L, oldval, val);
|
luaC_barriert(L, h, val);
|
||||||
luaC_barriert(L, h, val);
|
return;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
/* fallthrough to metamethod */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (h->readonly)
|
||||||
|
luaG_readonlyerror(L);
|
||||||
|
|
||||||
|
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */
|
||||||
|
|
||||||
|
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */
|
||||||
|
|
||||||
|
if (!ttisnil(oldval) || /* result is no nil? */
|
||||||
|
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
|
||||||
|
{ /* or no TM? */
|
||||||
|
setobj2t(L, oldval, val);
|
||||||
|
luaC_barriert(L, h, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* else will try the tag method */
|
||||||
}
|
}
|
||||||
/* else will try the tag method */
|
|
||||||
}
|
}
|
||||||
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
|
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
|
||||||
luaG_indexerror(L, t, key);
|
luaG_indexerror(L, t, key);
|
||||||
|
|
||||||
if (ttisfunction(tm))
|
if (ttisfunction(tm))
|
||||||
{
|
{
|
||||||
callTM(L, tm, t, key, val);
|
callTM(L, tm, t, key, val);
|
||||||
|
|
129
rfcs/disallow-proposals-leading-to-ambiguity-in-grammar.md
Normal file
129
rfcs/disallow-proposals-leading-to-ambiguity-in-grammar.md
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
# Disallow `name T` and `name(T)` in future syntactic extensions for type annotations
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
We propose to disallow the syntax ``<name> `('`` as well as `<name> <type>` in future syntax extensions for type annotations to ensure that all existing programs continue to parse correctly. This still keeps the door open for future syntax extensions of different forms such as ``<name> `<' <type> `>'``.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Lua and by extension Luau's syntax is very free form, which means that when the parser finishes parsing a node, it doesn't try to look for a semi-colon or any termination token e.g. a `{` to start a block, or `;` to end a statement, or a newline, etc. It just immediately invokes the next parser to figure out how to parse the next node based on the remainder's starting token.
|
||||||
|
|
||||||
|
That feature is sometimes quite troublesome when we want to add new syntax.
|
||||||
|
|
||||||
|
We have had cases where we talked about using syntax like `setmetatable(T, MT)` and `keyof T`. They all look innocent, but when you look beyond that, and try to apply it onto Luau's grammar, things break down really fast.
|
||||||
|
|
||||||
|
### `F(T)`?
|
||||||
|
|
||||||
|
An example that _will_ cause a change in semantics:
|
||||||
|
|
||||||
|
```
|
||||||
|
local t: F
|
||||||
|
(u):m()
|
||||||
|
```
|
||||||
|
|
||||||
|
where today, `local t: F` is one statement, and `(u):m()` is another. If we had the syntax for `F(T)` here, it becomes invalid input because it gets parsed as
|
||||||
|
|
||||||
|
```
|
||||||
|
local t: F(u)
|
||||||
|
:m()
|
||||||
|
```
|
||||||
|
|
||||||
|
This is important because of the `setmetatable(T, MT)` case:
|
||||||
|
|
||||||
|
```
|
||||||
|
type Foo = setmetatable({ x: number }, { ... })
|
||||||
|
```
|
||||||
|
|
||||||
|
For `setmetatable`, the parser isn't sure whether `{}` is actually a type or an expression, because _today_ `setmetatable` is parsed as a type reference, and `({}, {})` is the remainder that we'll attempt to parse as a statement. This means `{ x: number }` is invalid table _literal_. Recovery by backtracking is technically possible here, but this means performance loss on invalid input + may introduce false positives wrt how things are parsed. We'd much rather take a very strict stance about how things get parsed.
|
||||||
|
|
||||||
|
### `F T`?
|
||||||
|
|
||||||
|
An example that _will_ cause a change in semantics:
|
||||||
|
|
||||||
|
```
|
||||||
|
local function f(t): F T
|
||||||
|
(t or u):m()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
where today, the return type annotation `F T` is simply parsed as just `F`, followed by a ambiguous parse error from the statement `T(t or u)` because its `(` is on the next line. If at some point in the future we were to allow `T` followed by `(` on the next line, then there's yet another semantic change. `F T` could be parsed as a type annotation and the first statement is `(t or u):m()` instead of `F` followed by `T(t or u):m()`.
|
||||||
|
|
||||||
|
For `keyof`, here's a practical example of the above issue:
|
||||||
|
|
||||||
|
```
|
||||||
|
type Vec2 = {x: number, y: number}
|
||||||
|
|
||||||
|
local function f(t, u): keyof Vec2
|
||||||
|
(t or u):m()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
There's three possible outcomes:
|
||||||
|
1. Return type of `f` is `keyof`, statement throws a parse error because `(` is on the next line after `Vec2`,
|
||||||
|
2. Return type of `f` is `keyof Vec2` and next statement is `(t or u):m()`, or
|
||||||
|
3. Return type of `f` is `keyof` and next statement is `Vec2(t or u):m()` (if we allow `(` on the next line to be part of previous line).
|
||||||
|
|
||||||
|
This particular case is even worse when we keep going:
|
||||||
|
|
||||||
|
```
|
||||||
|
local function f(t): F
|
||||||
|
T(t or u):m()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
local function f(t): F T
|
||||||
|
{1, 2, 3}
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
where today, `F` is the return type annotation of `f`, and `T(t or u):m()`/`T{1, 2, 3}` is the first statement, respectively.
|
||||||
|
|
||||||
|
Adding some syntax for `F T` **will** cause the parser to change the semantics of the above three examples.
|
||||||
|
|
||||||
|
### But what about `typeof(...)`?
|
||||||
|
|
||||||
|
This syntax is grandfathered in because the parser supported `typeof(...)` before we stabilized our syntax, and especially before type annotations were released to the public, so we didn't need to worry about compatibility here. We are very glad that we used parentheses in this case, because it's natural for expressions to belong within parentheses `()`, and types to belong within angles `<>`.
|
||||||
|
|
||||||
|
## The One Exception with a caveat
|
||||||
|
|
||||||
|
This is a strict requirement!
|
||||||
|
|
||||||
|
`function() -> ()` has been talked about in the past, and this one is different despite falling under the same category as ``<name> `('``. The token `function` is in actual fact a "hard keyword," meaning that it cannot be parsed as a type annotation because it is not an identifier, just a keyword.
|
||||||
|
|
||||||
|
Likewise, we also have talked about adding standalone `function` as a type annotation (semantics of it is irrelevant for this RFC)
|
||||||
|
|
||||||
|
It's possible that we may end up adding both, but the requirements are as such:
|
||||||
|
1. `function() -> ()` must be added first before standalone `function`, OR
|
||||||
|
2. `function` can be added first, but with a future-proofing parse error if `<` or `(` follows after it
|
||||||
|
|
||||||
|
If #1 is what ends up happening, there's not much to worry about because the type annotation parser will parse greedily already, so any new valid input will remain valid and have same semantics, except it also allows omitting of `(` and `<`.
|
||||||
|
|
||||||
|
If #2 is what ends up happening, there could be a problem if we didn't future-proof against `<` and `(` to follow `function`:
|
||||||
|
|
||||||
|
```
|
||||||
|
return f :: function(T) -> U
|
||||||
|
```
|
||||||
|
|
||||||
|
which would be a parse error because at the point of `(` we expect one of `until`, `end`, or `EOF`, and
|
||||||
|
|
||||||
|
```
|
||||||
|
return f :: function<a>(a) -> a
|
||||||
|
```
|
||||||
|
|
||||||
|
which would also be a parse error by the time we reach `->`, that is the production of the above is semantically equivalent to `(f < a) > (a)` which would compare whether the value of `f` is less than the value of `a`, then whether the result of that value is greater than `a`.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
Only allow these syntax when used inside parentheses e.g. `(F T)` or `(F(T))`. This makes it inconsistent with the existing `typeof(...)` type annotation, and changing that over is also breaking change.
|
||||||
|
|
||||||
|
Support backtracking in the parser, so if `: MyType(t or u):m()` is invalid syntax, revert and parse `MyType` as a type, and `(t or u):m()` as an expression statement. Even so, this option is terrible for:
|
||||||
|
1. parsing performance (backtracking means losing progress on invalid input),
|
||||||
|
2. user experience (why was this annotation parsed as `X(...)` instead of `X` followed by a statement `(...)`),
|
||||||
|
3. has false positives (`foo(bar)(baz)` may be parsed as `foo(bar)` as the type annotation and `(baz)` is the remainder to parse)
|
||||||
|
|
||||||
|
## Drawbacks
|
||||||
|
|
||||||
|
To be able to expose some kind of type-level operations using `F<T>` syntax, means one of the following must be chosen:
|
||||||
|
1. introduce the concept of "magic type functions" into type inference, or
|
||||||
|
2. introduce them into the prelude as `export type F<T> = ...` (where `...` is to be read as "we haven't decided")
|
|
@ -31,9 +31,9 @@ Because we care about backward compatibility, we need some new syntax in order t
|
||||||
|
|
||||||
1. A string chunk (`` `...{ ``, `}...{`, and `` }...` ``) where `...` is a range of 0 to many characters.
|
1. A string chunk (`` `...{ ``, `}...{`, and `` }...` ``) where `...` is a range of 0 to many characters.
|
||||||
* `\` escapes `` ` ``, `{`, and itself `\`.
|
* `\` escapes `` ` ``, `{`, and itself `\`.
|
||||||
* Restriction: the string interpolation literal must have at least one value to interpolate. We do not need 3 ways to express a single line string literal.
|
|
||||||
* The pairs must be on the same line (unless a `\` escapes the newline) but expressions needn't be on the same line.
|
* The pairs must be on the same line (unless a `\` escapes the newline) but expressions needn't be on the same line.
|
||||||
2. An expression between the braces. This is the value that will be interpolated into the string.
|
2. An expression between the braces. This is the value that will be interpolated into the string.
|
||||||
|
* Restriction: we explicitly reject `{{` as it is considered an attempt to escape and get a single `{` character at runtime.
|
||||||
3. Formatting specification may follow after the expression, delimited by an unambiguous character.
|
3. Formatting specification may follow after the expression, delimited by an unambiguous character.
|
||||||
* Restriction: the formatting specification must be constant at parse time.
|
* Restriction: the formatting specification must be constant at parse time.
|
||||||
* In the absence of an explicit formatting specification, the `%*` token will be used.
|
* In the absence of an explicit formatting specification, the `%*` token will be used.
|
||||||
|
@ -61,7 +61,6 @@ local set2 = Set.new({0, 5, 4})
|
||||||
print(`{set1} ∪ {set2} = {Set.union(set1, set2)}`)
|
print(`{set1} ∪ {set2} = {Set.union(set1, set2)}`)
|
||||||
--> {0, 1, 3} ∪ {0, 5, 4} = {0, 1, 3, 4, 5}
|
--> {0, 1, 3} ∪ {0, 5, 4} = {0, 1, 3, 4, 5}
|
||||||
|
|
||||||
-- For illustrative purposes. These are illegal specifically because they don't interpolate anything.
|
|
||||||
print(`Some example escaping the braces \{like so}`)
|
print(`Some example escaping the braces \{like so}`)
|
||||||
print(`backslash \ that escapes the space is not a part of the string...`)
|
print(`backslash \ that escapes the space is not a part of the string...`)
|
||||||
print(`backslash \\ will escape the second backslash...`)
|
print(`backslash \\ will escape the second backslash...`)
|
||||||
|
@ -88,13 +87,25 @@ print(`Welcome to \
|
||||||
-- Luau!
|
-- Luau!
|
||||||
```
|
```
|
||||||
|
|
||||||
This expression will not be allowed to come after a `prefixexp`. I believe this is fully additive, so a future RFC may allow this. So for now, we explicitly reject the following:
|
This expression can also come after a `prefixexp`:
|
||||||
|
|
||||||
```
|
```
|
||||||
local name = "world"
|
local name = "world"
|
||||||
print`Hello {name}`
|
print`Hello {name}`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The restriction on `{{` exists solely for the people coming from languages e.g. C#, Rust, or Python which uses `{{` to escape and get the character `{` at runtime. We're also rejecting this at parse time too, since the proper way to escape it is `\{`, so:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
print(`{{1, 2, 3}} = {myCoolSet}`) -- parse error
|
||||||
|
```
|
||||||
|
|
||||||
|
If we did not apply this as a parse error, then the above would wind up printing as the following, which is obviously a gotcha we can and should avoid.
|
||||||
|
|
||||||
|
```
|
||||||
|
--> table: 0xSOMEADDRESS = {1, 2, 3}
|
||||||
|
```
|
||||||
|
|
||||||
Since the string interpolation expression is going to be lowered into a `string.format` call, we'll also need to extend `string.format`. The bare minimum to support the lowering is to add a new token whose definition is to perform a `tostring` call. `%*` is currently an invalid token, so this is a backward compatible extension. This RFC shall define `%*` to have the same behavior as if `tostring` was called.
|
Since the string interpolation expression is going to be lowered into a `string.format` call, we'll also need to extend `string.format`. The bare minimum to support the lowering is to add a new token whose definition is to perform a `tostring` call. `%*` is currently an invalid token, so this is a backward compatible extension. This RFC shall define `%*` to have the same behavior as if `tostring` was called.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
@ -121,6 +132,13 @@ print(string.format("%* %* %*", return_two_nils()))
|
||||||
--> error: value #3 is missing, got 2
|
--> error: value #3 is missing, got 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It must be said that we are not allowing this style of string literals in type annotations at this time, regardless of zero or many interpolating expressions, so the following two type annotations below are illegal syntax:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local foo: `foo`
|
||||||
|
local bar: `bar{baz}`
|
||||||
|
```
|
||||||
|
|
||||||
## Drawbacks
|
## Drawbacks
|
||||||
|
|
||||||
If we want to use backticks for other purposes, it may introduce some potential ambiguity. One option to solve that is to only ever produce string interpolation tokens from the context of an expression. This is messy but doable because the parser and the lexer are already implemented to work in tandem. The other option is to pick a different delimiter syntax to keep backticks available for use in the future.
|
If we want to use backticks for other purposes, it may introduce some potential ambiguity. One option to solve that is to only ever produce string interpolation tokens from the context of an expression. This is messy but doable because the parser and the lexer are already implemented to work in tandem. The other option is to pick a different delimiter syntax to keep backticks available for use in the future.
|
||||||
|
|
|
@ -2946,7 +2946,7 @@ local abc = b@1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_on_class")
|
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_on_class")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
loadDefinition(R"(
|
loadDefinition(R"(
|
||||||
declare class Foo
|
declare class Foo
|
||||||
|
@ -2984,9 +2984,25 @@ t.@1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "do_compatible_self_calls")
|
||||||
|
{
|
||||||
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
|
check(R"(
|
||||||
|
local t = {}
|
||||||
|
function t:m() end
|
||||||
|
t:@1
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
REQUIRE(ac.entryMap.count("m"));
|
||||||
|
CHECK(!ac.entryMap["m"].wrongIndexType);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls")
|
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
|
@ -3002,7 +3018,7 @@ t:@1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_2")
|
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
local f: (() -> number) & ((number) -> number) = function(x: number?) return 2 end
|
local f: (() -> number) & ((number) -> number) = function(x: number?) return 2 end
|
||||||
|
@ -3017,7 +3033,7 @@ t:@1
|
||||||
CHECK(ac.entryMap["f"].wrongIndexType);
|
CHECK(ac.entryMap["f"].wrongIndexType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_provisional")
|
TEST_CASE_FIXTURE(ACFixture, "do_wrong_compatible_self_calls")
|
||||||
{
|
{
|
||||||
check(R"(
|
check(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
|
@ -3032,9 +3048,26 @@ t:@1
|
||||||
CHECK(!ac.entryMap["m"].wrongIndexType);
|
CHECK(!ac.entryMap["m"].wrongIndexType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "no_wrong_compatible_self_calls_with_generics")
|
||||||
|
{
|
||||||
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
|
check(R"(
|
||||||
|
local t = {}
|
||||||
|
function t.m<T>(a: T) end
|
||||||
|
t:@1
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
REQUIRE(ac.entryMap.count("m"));
|
||||||
|
// While this call is compatible with the type, this requires instantiation of a generic type which we don't perform
|
||||||
|
CHECK(ac.entryMap["m"].wrongIndexType);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "string_prim_self_calls_are_fine")
|
TEST_CASE_FIXTURE(ACFixture, "string_prim_self_calls_are_fine")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
local s = "hello"
|
local s = "hello"
|
||||||
|
@ -3053,7 +3086,7 @@ s:@1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "string_prim_non_self_calls_are_avoided")
|
TEST_CASE_FIXTURE(ACFixture, "string_prim_non_self_calls_are_avoided")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
local s = "hello"
|
local s = "hello"
|
||||||
|
@ -3070,7 +3103,7 @@ s.@1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_non_self_calls_are_fine")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_non_self_calls_are_fine")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
string.@1
|
string.@1
|
||||||
|
@ -3101,7 +3134,7 @@ table.@1
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_self_calls_are_invalid")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_self_calls_are_invalid")
|
||||||
{
|
{
|
||||||
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
|
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
string:@1
|
string:@1
|
||||||
|
@ -3113,8 +3146,11 @@ string:@1
|
||||||
CHECK(ac.entryMap["byte"].wrongIndexType == true);
|
CHECK(ac.entryMap["byte"].wrongIndexType == true);
|
||||||
REQUIRE(ac.entryMap.count("char"));
|
REQUIRE(ac.entryMap.count("char"));
|
||||||
CHECK(ac.entryMap["char"].wrongIndexType == true);
|
CHECK(ac.entryMap["char"].wrongIndexType == true);
|
||||||
|
|
||||||
|
// We want the next test to evaluate to 'true', but we have to allow function defined with 'self' to be callable with ':'
|
||||||
|
// We may change the definition of the string metatable to not use 'self' types in the future (like byte/char/pack/unpack)
|
||||||
REQUIRE(ac.entryMap.count("sub"));
|
REQUIRE(ac.entryMap.count("sub"));
|
||||||
CHECK(ac.entryMap["sub"].wrongIndexType == true);
|
CHECK(ac.entryMap["sub"].wrongIndexType == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "source_module_preservation_and_invalidation")
|
TEST_CASE_FIXTURE(ACFixture, "source_module_preservation_and_invalidation")
|
||||||
|
|
|
@ -4352,8 +4352,6 @@ TEST_CASE("LoopUnrollControlFlow")
|
||||||
{"LuauCompileLoopUnrollThresholdMaxBoost", 300},
|
{"LuauCompileLoopUnrollThresholdMaxBoost", 300},
|
||||||
};
|
};
|
||||||
|
|
||||||
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
|
|
||||||
|
|
||||||
// break jumps to the end
|
// break jumps to the end
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
for i=1,3 do
|
for i=1,3 do
|
||||||
|
@ -4669,8 +4667,6 @@ TEST_CASE("LoopUnrollCostBuiltins")
|
||||||
{"LuauCompileLoopUnrollThresholdMaxBoost", 300},
|
{"LuauCompileLoopUnrollThresholdMaxBoost", 300},
|
||||||
};
|
};
|
||||||
|
|
||||||
ScopedFastFlag sff("LuauCompileModelBuiltins", true);
|
|
||||||
|
|
||||||
// this loop uses builtins and is close to the cost budget so it's important that we model builtins as cheaper than regular calls
|
// this loop uses builtins and is close to the cost budget so it's important that we model builtins as cheaper than regular calls
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
function cipher(block, nonce)
|
function cipher(block, nonce)
|
||||||
|
@ -5893,8 +5889,6 @@ RETURN R0 2
|
||||||
|
|
||||||
TEST_CASE("OptimizationLevel")
|
TEST_CASE("OptimizationLevel")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauAlwaysCaptureHotComments", true);
|
|
||||||
|
|
||||||
// at optimization level 1, no inlining is performed
|
// at optimization level 1, no inlining is performed
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
local function foo(a)
|
local function foo(a)
|
||||||
|
@ -5964,8 +5958,6 @@ RETURN R1 -1
|
||||||
|
|
||||||
TEST_CASE("BuiltinFolding")
|
TEST_CASE("BuiltinFolding")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
|
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
return
|
return
|
||||||
math.abs(-42),
|
math.abs(-42),
|
||||||
|
@ -6073,8 +6065,6 @@ RETURN R0 48
|
||||||
|
|
||||||
TEST_CASE("BuiltinFoldingProhibited")
|
TEST_CASE("BuiltinFoldingProhibited")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
|
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
return
|
return
|
||||||
math.abs(),
|
math.abs(),
|
||||||
|
@ -6108,9 +6098,6 @@ L3: RETURN R0 -1
|
||||||
|
|
||||||
TEST_CASE("BuiltinFoldingMultret")
|
TEST_CASE("BuiltinFoldingMultret")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff1("LuauCompileFoldBuiltins", true);
|
|
||||||
ScopedFastFlag sff2("LuauCompileBetterMultret", true);
|
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
local NoLanes: Lanes = --[[ ]] 0b0000000000000000000000000000000
|
local NoLanes: Lanes = --[[ ]] 0b0000000000000000000000000000000
|
||||||
local OffscreenLane: Lane = --[[ ]] 0b1000000000000000000000000000000
|
local OffscreenLane: Lane = --[[ ]] 0b1000000000000000000000000000000
|
||||||
|
|
|
@ -316,7 +316,8 @@ TEST_CASE("Errors")
|
||||||
|
|
||||||
TEST_CASE("Events")
|
TEST_CASE("Events")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauLenTM", true);
|
ScopedFastFlag sff1("LuauLenTM", true);
|
||||||
|
ScopedFastFlag sff2("LuauBetterNewindex", true);
|
||||||
|
|
||||||
runConformance("events.lua");
|
runConformance("events.lua");
|
||||||
}
|
}
|
||||||
|
@ -490,8 +491,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
|
|
||||||
TEST_CASE("Types")
|
TEST_CASE("Types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCheckLenMT", true);
|
|
||||||
|
|
||||||
runConformance("types.lua", [](lua_State* L) {
|
runConformance("types.lua", [](lua_State* L) {
|
||||||
Luau::NullModuleResolver moduleResolver;
|
Luau::NullModuleResolver moduleResolver;
|
||||||
Luau::InternalErrorReporter iceHandler;
|
Luau::InternalErrorReporter iceHandler;
|
||||||
|
@ -862,8 +861,6 @@ TEST_CASE("ApiCalls")
|
||||||
|
|
||||||
TEST_CASE("ApiAtoms")
|
TEST_CASE("ApiAtoms")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauLazyAtoms", true);
|
|
||||||
|
|
||||||
StateRef globalState(luaL_newstate(), lua_close);
|
StateRef globalState(luaL_newstate(), lua_close);
|
||||||
lua_State* L = globalState.get();
|
lua_State* L = globalState.get();
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
static TypeId requireBinding(NotNull<Scope2> scope, const char* name)
|
static TypeId requireBinding(NotNull<Scope> scope, const char* name)
|
||||||
{
|
{
|
||||||
auto b = linearSearchForBinding(scope, name);
|
auto b = linearSearchForBinding(scope, name);
|
||||||
LUAU_ASSERT(b.has_value());
|
LUAU_ASSERT(b.has_value());
|
||||||
|
@ -26,7 +26,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "hello")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
cgb.visit(block);
|
cgb.visit(block);
|
||||||
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
|
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||||
|
|
||||||
ConstraintSolver cs{&arena, rootScope};
|
ConstraintSolver cs{&arena, rootScope};
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "generic_function")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
cgb.visit(block);
|
cgb.visit(block);
|
||||||
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
|
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||||
|
|
||||||
ConstraintSolver cs{&arena, rootScope};
|
ConstraintSolver cs{&arena, rootScope};
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "proper_let_generalization")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
cgb.visit(block);
|
cgb.visit(block);
|
||||||
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
|
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||||
|
|
||||||
ToStringOptions opts;
|
ToStringOptions opts;
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,7 @@ std::optional<TypeId> Fixture::getType(const std::string& name)
|
||||||
REQUIRE(module);
|
REQUIRE(module);
|
||||||
|
|
||||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return linearSearchForBinding(module->getModuleScope2(), name.c_str());
|
return linearSearchForBinding(module->getModuleScope().get(), name.c_str());
|
||||||
else
|
else
|
||||||
return lookupName(module->getModuleScope(), name);
|
return lookupName(module->getModuleScope(), name);
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
|
||||||
|
|
||||||
ConstraintGraphBuilderFixture::ConstraintGraphBuilderFixture()
|
ConstraintGraphBuilderFixture::ConstraintGraphBuilderFixture()
|
||||||
: Fixture()
|
: Fixture()
|
||||||
, cgb(mainModuleName, &arena, NotNull(&ice), frontend.getGlobalScope2())
|
, cgb(mainModuleName, &arena, NotNull(&ice), frontend.getGlobalScope())
|
||||||
, forceTheFlag{"DebugLuauDeferredConstraintResolution", true}
|
, forceTheFlag{"DebugLuauDeferredConstraintResolution", true}
|
||||||
{
|
{
|
||||||
BlockedTypeVar::nextIndex = 0;
|
BlockedTypeVar::nextIndex = 0;
|
||||||
|
@ -479,17 +479,17 @@ std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> linearSearchForBinding(Scope2* scope, const char* name)
|
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name)
|
||||||
{
|
{
|
||||||
while (scope)
|
while (scope)
|
||||||
{
|
{
|
||||||
for (const auto& [n, ty] : scope->bindings)
|
for (const auto& [n, ty] : scope->bindings)
|
||||||
{
|
{
|
||||||
if (n.astName() == name)
|
if (n.astName() == name)
|
||||||
return ty;
|
return ty.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
scope = scope->parent;
|
scope = scope->parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
|
@ -192,7 +192,7 @@ void dump(const std::vector<Constraint>& constraints);
|
||||||
|
|
||||||
std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name); // Warning: This function runs in O(n**2)
|
std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name); // Warning: This function runs in O(n**2)
|
||||||
|
|
||||||
std::optional<TypeId> linearSearchForBinding(Scope2* scope, const char* name);
|
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
||||||
|
|
141
tests/Lexer.test.cpp
Normal file
141
tests/Lexer.test.cpp
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include "Luau/Lexer.h"
|
||||||
|
|
||||||
|
#include "Fixture.h"
|
||||||
|
#include "ScopedFlags.h"
|
||||||
|
|
||||||
|
#include "doctest.h"
|
||||||
|
|
||||||
|
using namespace Luau;
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("LexerTests");
|
||||||
|
|
||||||
|
TEST_CASE("broken_string_works")
|
||||||
|
{
|
||||||
|
const std::string testInput = "[[";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
Lexeme lexeme = lexer.next();
|
||||||
|
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenString);
|
||||||
|
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("broken_comment")
|
||||||
|
{
|
||||||
|
const std::string testInput = "--[[ ";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
Lexeme lexeme = lexer.next();
|
||||||
|
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenComment);
|
||||||
|
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("broken_comment_kept")
|
||||||
|
{
|
||||||
|
const std::string testInput = "--[[ ";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
lexer.setSkipComments(true);
|
||||||
|
CHECK_EQ(lexer.next().type, Lexeme::Type::BrokenComment);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("comment_skipped")
|
||||||
|
{
|
||||||
|
const std::string testInput = "-- ";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
lexer.setSkipComments(true);
|
||||||
|
CHECK_EQ(lexer.next().type, Lexeme::Type::Eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("multilineCommentWithLexemeInAndAfter")
|
||||||
|
{
|
||||||
|
const std::string testInput = "--[[ function \n"
|
||||||
|
"]] end";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
Lexeme comment = lexer.next();
|
||||||
|
Lexeme end = lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(comment.type, Lexeme::Type::BlockComment);
|
||||||
|
CHECK_EQ(comment.location, Luau::Location(Luau::Position(0, 0), Luau::Position(1, 2)));
|
||||||
|
CHECK_EQ(end.type, Lexeme::Type::ReservedEnd);
|
||||||
|
CHECK_EQ(end.location, Luau::Location(Luau::Position(1, 3), Luau::Position(1, 6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("testBrokenEscapeTolerant")
|
||||||
|
{
|
||||||
|
const std::string testInput = "'\\3729472897292378'";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
Lexeme item = lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(item.type, Lexeme::QuotedString);
|
||||||
|
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, int(testInput.size()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("testBigDelimiters")
|
||||||
|
{
|
||||||
|
const std::string testInput = "--[===[\n"
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
|
"]===]";
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
Lexeme item = lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(item.type, Lexeme::Type::BlockComment);
|
||||||
|
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(4, 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("lookahead")
|
||||||
|
{
|
||||||
|
const std::string testInput = "foo --[[ comment ]] bar : nil end";
|
||||||
|
|
||||||
|
Luau::Allocator alloc;
|
||||||
|
AstNameTable table(alloc);
|
||||||
|
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
||||||
|
lexer.setSkipComments(true);
|
||||||
|
lexer.next(); // must call next() before reading data from lexer at least once
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, Lexeme::Name);
|
||||||
|
CHECK_EQ(lexer.current().name, std::string("foo"));
|
||||||
|
CHECK_EQ(lexer.lookahead().type, Lexeme::Name);
|
||||||
|
CHECK_EQ(lexer.lookahead().name, std::string("bar"));
|
||||||
|
|
||||||
|
lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, Lexeme::Name);
|
||||||
|
CHECK_EQ(lexer.current().name, std::string("bar"));
|
||||||
|
CHECK_EQ(lexer.lookahead().type, ':');
|
||||||
|
|
||||||
|
lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, ':');
|
||||||
|
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedNil);
|
||||||
|
|
||||||
|
lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, Lexeme::ReservedNil);
|
||||||
|
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedEnd);
|
||||||
|
|
||||||
|
lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, Lexeme::ReservedEnd);
|
||||||
|
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
|
||||||
|
|
||||||
|
lexer.next();
|
||||||
|
|
||||||
|
CHECK_EQ(lexer.current().type, Lexeme::Eof);
|
||||||
|
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
|
@ -97,138 +97,6 @@ TEST_CASE("initial_double_is_aligned")
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("LexerTests");
|
|
||||||
|
|
||||||
TEST_CASE("broken_string_works")
|
|
||||||
{
|
|
||||||
const std::string testInput = "[[";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
Lexeme lexeme = lexer.next();
|
|
||||||
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenString);
|
|
||||||
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("broken_comment")
|
|
||||||
{
|
|
||||||
const std::string testInput = "--[[ ";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
Lexeme lexeme = lexer.next();
|
|
||||||
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenComment);
|
|
||||||
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 6)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("broken_comment_kept")
|
|
||||||
{
|
|
||||||
const std::string testInput = "--[[ ";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
lexer.setSkipComments(true);
|
|
||||||
CHECK_EQ(lexer.next().type, Lexeme::Type::BrokenComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("comment_skipped")
|
|
||||||
{
|
|
||||||
const std::string testInput = "-- ";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
lexer.setSkipComments(true);
|
|
||||||
CHECK_EQ(lexer.next().type, Lexeme::Type::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("multilineCommentWithLexemeInAndAfter")
|
|
||||||
{
|
|
||||||
const std::string testInput = "--[[ function \n"
|
|
||||||
"]] end";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
Lexeme comment = lexer.next();
|
|
||||||
Lexeme end = lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(comment.type, Lexeme::Type::BlockComment);
|
|
||||||
CHECK_EQ(comment.location, Luau::Location(Luau::Position(0, 0), Luau::Position(1, 2)));
|
|
||||||
CHECK_EQ(end.type, Lexeme::Type::ReservedEnd);
|
|
||||||
CHECK_EQ(end.location, Luau::Location(Luau::Position(1, 3), Luau::Position(1, 6)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("testBrokenEscapeTolerant")
|
|
||||||
{
|
|
||||||
const std::string testInput = "'\\3729472897292378'";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
Lexeme item = lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(item.type, Lexeme::QuotedString);
|
|
||||||
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, int(testInput.size()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("testBigDelimiters")
|
|
||||||
{
|
|
||||||
const std::string testInput = "--[===[\n"
|
|
||||||
"\n"
|
|
||||||
"\n"
|
|
||||||
"\n"
|
|
||||||
"]===]";
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
Lexeme item = lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(item.type, Lexeme::Type::BlockComment);
|
|
||||||
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(4, 5)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("lookahead")
|
|
||||||
{
|
|
||||||
const std::string testInput = "foo --[[ comment ]] bar : nil end";
|
|
||||||
|
|
||||||
Luau::Allocator alloc;
|
|
||||||
AstNameTable table(alloc);
|
|
||||||
Lexer lexer(testInput.c_str(), testInput.size(), table);
|
|
||||||
lexer.setSkipComments(true);
|
|
||||||
lexer.next(); // must call next() before reading data from lexer at least once
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, Lexeme::Name);
|
|
||||||
CHECK_EQ(lexer.current().name, std::string("foo"));
|
|
||||||
CHECK_EQ(lexer.lookahead().type, Lexeme::Name);
|
|
||||||
CHECK_EQ(lexer.lookahead().name, std::string("bar"));
|
|
||||||
|
|
||||||
lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, Lexeme::Name);
|
|
||||||
CHECK_EQ(lexer.current().name, std::string("bar"));
|
|
||||||
CHECK_EQ(lexer.lookahead().type, ':');
|
|
||||||
|
|
||||||
lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, ':');
|
|
||||||
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedNil);
|
|
||||||
|
|
||||||
lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, Lexeme::ReservedNil);
|
|
||||||
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedEnd);
|
|
||||||
|
|
||||||
lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, Lexeme::ReservedEnd);
|
|
||||||
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
|
|
||||||
|
|
||||||
lexer.next();
|
|
||||||
|
|
||||||
CHECK_EQ(lexer.current().type, Lexeme::Eof);
|
|
||||||
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_SUITE_END();
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ParserTests");
|
TEST_SUITE_BEGIN("ParserTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "basic_parse")
|
TEST_CASE_FIXTURE(Fixture, "basic_parse")
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
|
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("ToString");
|
TEST_SUITE_BEGIN("ToString");
|
||||||
|
|
||||||
|
@ -267,8 +268,16 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_type_when_length_is_exceeded")
|
||||||
o.maxTypeLength = 40;
|
o.maxTypeLength = 40;
|
||||||
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
|
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
|
||||||
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
|
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
|
||||||
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
{
|
||||||
|
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... *TRUNCATED*");
|
||||||
|
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... *TRUNCATED*");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
||||||
|
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
|
TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
|
||||||
|
@ -286,8 +295,17 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
|
||||||
o.maxTypeLength = 40;
|
o.maxTypeLength = 40;
|
||||||
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
|
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
|
||||||
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
|
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
|
||||||
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
{
|
||||||
|
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... *TRUNCATED*");
|
||||||
|
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... *TRUNCATED*");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
||||||
|
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table_state_braces")
|
TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table_state_braces")
|
||||||
|
@ -497,7 +515,10 @@ local function target(callback: nil) return callback(4, "hello") end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("(nil) -> (*error-type*)", toString(requireType("target")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "toStringGenericPack")
|
TEST_CASE_FIXTURE(Fixture, "toStringGenericPack")
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferAnyError");
|
TEST_SUITE_BEGIN("TypeInferAnyError");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
|
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
|
||||||
|
@ -94,7 +96,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("a")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("a")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
|
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
|
||||||
|
@ -110,7 +115,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("a")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("a")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "length_of_error_type_does_not_produce_an_error")
|
TEST_CASE_FIXTURE(Fixture, "length_of_error_type_does_not_produce_an_error")
|
||||||
|
@ -225,7 +233,10 @@ TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error")
|
||||||
|
|
||||||
CHECK_EQ("unknown", err->name);
|
CHECK_EQ("unknown", err->name);
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("a")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("a")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
|
TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
|
||||||
|
@ -234,7 +245,10 @@ TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
|
||||||
local a = Utility.Create "Foo" {}
|
local a = Utility.Create "Foo" {}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("a")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("a")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_complex_function_with_any")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_complex_function_with_any")
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
|
||||||
|
@ -952,7 +953,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
|
||||||
CHECK_EQ("number", toString(requireType("a")));
|
CHECK_EQ("number", toString(requireType("a")));
|
||||||
CHECK_EQ("string", toString(requireType("b")));
|
CHECK_EQ("string", toString(requireType("b")));
|
||||||
CHECK_EQ("boolean", toString(requireType("c")));
|
CHECK_EQ("boolean", toString(requireType("c")));
|
||||||
CHECK_EQ("<error-type>", toString(requireType("d")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("d")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("d")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments")
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -907,13 +908,19 @@ TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language")
|
||||||
REQUIRE(tm1);
|
REQUIRE(tm1);
|
||||||
|
|
||||||
CHECK_EQ("(string) -> number", toString(tm1->wantedType));
|
CHECK_EQ("(string) -> number", toString(tm1->wantedType));
|
||||||
CHECK_EQ("(string, <error-type>) -> number", toString(tm1->givenType));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("(string, *error-type*) -> number", toString(tm1->givenType));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(string, <error-type>) -> number", toString(tm1->givenType));
|
||||||
|
|
||||||
auto tm2 = get<TypeMismatch>(result.errors[1]);
|
auto tm2 = get<TypeMismatch>(result.errors[1]);
|
||||||
REQUIRE(tm2);
|
REQUIRE(tm2);
|
||||||
|
|
||||||
CHECK_EQ("(number, number) -> (number, number)", toString(tm2->wantedType));
|
CHECK_EQ("(number, number) -> (number, number)", toString(tm2->wantedType));
|
||||||
CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("(string, *error-type*) -> number", toString(tm2->givenType));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
||||||
|
@ -1526,10 +1533,21 @@ function t:b() return 2 end -- not OK
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(R"(Type '(<error-type>) -> number' could not be converted into '() -> number'
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
{
|
||||||
|
CHECK_EQ(R"(Type '(*error-type*) -> number' could not be converted into '() -> number'
|
||||||
caused by:
|
caused by:
|
||||||
Argument count mismatch. Function expects 1 argument, but none are specified)",
|
Argument count mismatch. Function expects 1 argument, but none are specified)",
|
||||||
toString(result.errors[0]));
|
toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(R"(Type '(<error-type>) -> number' could not be converted into '() -> number'
|
||||||
|
caused by:
|
||||||
|
Argument count mismatch. Function expects 1 argument, but none are specified)",
|
||||||
|
toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
|
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauCheckGenericHOFTypes)
|
LUAU_FASTFLAG(LuauCheckGenericHOFTypes)
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1003,7 +1004,10 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_quantifying")
|
||||||
|
|
||||||
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
|
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
|
||||||
REQUIRE(t0);
|
REQUIRE(t0);
|
||||||
CHECK_EQ("<error-type>", toString(t0->type));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(t0->type));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(t0->type));
|
||||||
|
|
||||||
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
|
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
|
||||||
return get<OccursCheckFailed>(err);
|
return get<OccursCheckFailed>(err);
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferLoops");
|
TEST_SUITE_BEGIN("TypeInferLoops");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "for_loop")
|
TEST_CASE_FIXTURE(Fixture, "for_loop")
|
||||||
|
@ -142,7 +144,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
|
||||||
CHECK_EQ(2, result.errors.size());
|
CHECK_EQ(2, result.errors.size());
|
||||||
|
|
||||||
TypeId p = requireType("p");
|
TypeId p = requireType("p");
|
||||||
CHECK_EQ("<error-type>", toString(p));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(p));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
|
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferModules");
|
TEST_SUITE_BEGIN("TypeInferModules");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
||||||
|
@ -143,7 +145,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export")
|
||||||
|
|
||||||
auto hootyType = requireType(bModule, "Hooty");
|
auto hootyType = requireType(bModule, "Hooty");
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(hootyType));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(hootyType));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(hootyType));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
|
||||||
|
@ -244,7 +249,11 @@ local ModuleA = require(game.A)
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
std::optional<TypeId> oty = requireType("ModuleA");
|
std::optional<TypeId> oty = requireType("ModuleA");
|
||||||
CHECK_EQ("<error-type>", toString(*oty));
|
|
||||||
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(*oty));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(*oty));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types")
|
||||||
|
|
|
@ -490,8 +490,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCheckLenMT", true);
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local foo = {
|
local foo = {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeduceFindMatchReturnTypes)
|
LUAU_FASTFLAG(LuauDeduceFindMatchReturnTypes)
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -49,7 +50,10 @@ TEST_CASE_FIXTURE(Fixture, "string_index")
|
||||||
REQUIRE(nat);
|
REQUIRE(nat);
|
||||||
CHECK_EQ("string", toString(nat->ty));
|
CHECK_EQ("string", toString(nat->ty));
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("t")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("t")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("t")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "string_method")
|
TEST_CASE_FIXTURE(Fixture, "string_method")
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -526,7 +527,10 @@ TEST_CASE_FIXTURE(Fixture, "type_narrow_to_vector")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28})));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireTypeAtPosition({3, 28})));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "nonoptional_type_can_narrow_to_nil_if_sense_is_true")
|
TEST_CASE_FIXTURE(Fixture, "nonoptional_type_can_narrow_to_nil_if_sense_is_true")
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||||
LUAU_FASTFLAG(LuauFixLocationSpanTableIndexExpr);
|
LUAU_FASTFLAG(LuauFixLocationSpanTableIndexExpr);
|
||||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -237,10 +238,21 @@ TEST_CASE_FIXTURE(Fixture, "type_errors_infer_types")
|
||||||
// TODO: Should we assert anything about these tests when DCR is being used?
|
// TODO: Should we assert anything about these tests when DCR is being used?
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
{
|
{
|
||||||
CHECK_EQ("<error-type>", toString(requireType("c")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
CHECK_EQ("<error-type>", toString(requireType("d")));
|
{
|
||||||
CHECK_EQ("<error-type>", toString(requireType("e")));
|
CHECK_EQ("*error-type*", toString(requireType("c")));
|
||||||
CHECK_EQ("<error-type>", toString(requireType("f")));
|
CHECK_EQ("*error-type*", toString(requireType("d")));
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("e")));
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("f")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("c")));
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("d")));
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("e")));
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("f")));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,7 +662,11 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_isoptional")
|
||||||
|
|
||||||
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
|
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
|
||||||
REQUIRE(t0);
|
REQUIRE(t0);
|
||||||
CHECK_EQ("<error-type>", toString(t0->type));
|
|
||||||
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(t0->type));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(t0->type));
|
||||||
|
|
||||||
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
|
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
|
||||||
return get<OccursCheckFailed>(err);
|
return get<OccursCheckFailed>(err);
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
struct TryUnifyFixture : Fixture
|
struct TryUnifyFixture : Fixture
|
||||||
{
|
{
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
|
@ -121,7 +123,10 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "members_of_failed_typepack_unification_are_u
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_EQ("a", toString(requireType("a")));
|
CHECK_EQ("a", toString(requireType("a")));
|
||||||
CHECK_EQ("<error-type>", toString(requireType("b")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("b")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("b")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_constrained")
|
TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_constrained")
|
||||||
|
@ -136,7 +141,10 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_con
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_EQ("a", toString(requireType("a")));
|
CHECK_EQ("a", toString(requireType("a")));
|
||||||
CHECK_EQ("<error-type>", toString(requireType("b")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("b")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("b")));
|
||||||
CHECK_EQ("number", toString(requireType("c")));
|
CHECK_EQ("number", toString(requireType("c")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
||||||
|
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -199,7 +200,10 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property")
|
||||||
CHECK_EQ(mup->missing[0], *bTy);
|
CHECK_EQ(mup->missing[0], *bTy);
|
||||||
CHECK_EQ(mup->key, "x");
|
CHECK_EQ(mup->key, "x");
|
||||||
|
|
||||||
CHECK_EQ("<error-type>", toString(requireType("r")));
|
if (FFlag::LuauSpecialTypesAsterisked)
|
||||||
|
CHECK_EQ("*error-type*", toString(requireType("r")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<error-type>", toString(requireType("r")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any")
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct TypePackFixture
|
||||||
|
|
||||||
TypePackId freshTypePack()
|
TypePackId freshTypePack()
|
||||||
{
|
{
|
||||||
typePacks.emplace_back(new TypePackVar{Unifiable::Free{{}}});
|
typePacks.emplace_back(new TypePackVar{Unifiable::Free{TypeLevel{}}});
|
||||||
return typePacks.back().get();
|
return typePacks.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -380,7 +380,8 @@ assert(ecall(function() return "a" + "b" end) == "attempt to perform arithmetic
|
||||||
assert(ecall(function() return 1 > nil end) == "attempt to compare nil < number") -- note reversed order (by design)
|
assert(ecall(function() return 1 > nil end) == "attempt to compare nil < number") -- note reversed order (by design)
|
||||||
assert(ecall(function() return "a" <= 5 end) == "attempt to compare string <= number")
|
assert(ecall(function() return "a" <= 5 end) == "attempt to compare string <= number")
|
||||||
|
|
||||||
assert(ecall(function() local t = {} setmetatable(t, { __newindex = function(t,i,v) end }) t[nil] = 2 end) == "table index is nil")
|
assert(ecall(function() local t = {} t[nil] = 2 end) == "table index is nil")
|
||||||
|
assert(ecall(function() local t = {} t[0/0] = 2 end) == "table index is NaN")
|
||||||
|
|
||||||
-- for loop type errors
|
-- for loop type errors
|
||||||
assert(ecall(function() for i='a',2 do end end) == "invalid 'for' initial value (number expected, got string)")
|
assert(ecall(function() for i='a',2 do end end) == "invalid 'for' initial value (number expected, got string)")
|
||||||
|
|
|
@ -424,4 +424,57 @@ do
|
||||||
assert(not ok and err:match("table or string expected"))
|
assert(not ok and err:match("table or string expected"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- verify that NaN/nil keys are passed to __newindex even though table assignment with them anywhere in the chain fails
|
||||||
|
do
|
||||||
|
assert(pcall(function() local t = {} t[nil] = 5 end) == false)
|
||||||
|
assert(pcall(function() local t = {} setmetatable(t, { __newindex = {} }) t[nil] = 5 end) == false)
|
||||||
|
assert(pcall(function() local t = {} setmetatable(t, { __newindex = function() end }) t[nil] = 5 end) == true)
|
||||||
|
|
||||||
|
assert(pcall(function() local t = {} t[0/0] = 5 end) == false)
|
||||||
|
assert(pcall(function() local t = {} setmetatable(t, { __newindex = {} }) t[0/0] = 5 end) == false)
|
||||||
|
assert(pcall(function() local t = {} setmetatable(t, { __newindex = function() end }) t[0/0] = 5 end) == true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- verify that __newindex gets called for frozen tables but only if the assignment is to a key absent from the table
|
||||||
|
do
|
||||||
|
local ni = {}
|
||||||
|
local t = table.create(2)
|
||||||
|
|
||||||
|
t[1] = 42
|
||||||
|
-- t[2] is semantically absent with storage allocated for it
|
||||||
|
|
||||||
|
t.a = 1
|
||||||
|
t.b = 2
|
||||||
|
t.b = nil -- this sets 'b' value to nil but leaves key as is to exercise more internal paths -- no observable behavior change expected between b and other absent keys
|
||||||
|
|
||||||
|
setmetatable(t, { __newindex = function(_, k, v)
|
||||||
|
assert(v == 42)
|
||||||
|
table.insert(ni, k)
|
||||||
|
end })
|
||||||
|
table.freeze(t)
|
||||||
|
|
||||||
|
-- "redundant" combinations are there to test all three of SETTABLEN/SETTABLEKS/SETTABLE
|
||||||
|
assert(pcall(function() t.a = 42 end) == false)
|
||||||
|
assert(pcall(function() t[1] = 42 end) == false)
|
||||||
|
assert(pcall(function() local key key = "a" t[key] = 42 end) == false)
|
||||||
|
assert(pcall(function() local key key = 1 t[key] = 42 end) == false)
|
||||||
|
|
||||||
|
-- now repeat the same for keys absent from the table: b (semantically absent), c (physically absent), 2 (semantically absent), 3 (physically absent)
|
||||||
|
assert(pcall(function() t.b = 42 end) == true)
|
||||||
|
assert(pcall(function() t.c = 42 end) == true)
|
||||||
|
assert(pcall(function() local key key = "b" t[key] = 42 end) == true)
|
||||||
|
assert(pcall(function() local key key = "c" t[key] = 42 end) == true)
|
||||||
|
assert(pcall(function() t[2] = 42 end) == true)
|
||||||
|
assert(pcall(function() t[3] = 42 end) == true)
|
||||||
|
assert(pcall(function() local key key = 2 t[key] = 42 end) == true)
|
||||||
|
assert(pcall(function() local key key = 3 t[key] = 42 end) == true)
|
||||||
|
|
||||||
|
-- validate the assignment sequence
|
||||||
|
local ei = { "b", "c", "b", "c", 2, 3, 2, 3 }
|
||||||
|
assert(#ni == #ei)
|
||||||
|
for k,v in ni do
|
||||||
|
assert(ei[k] == v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
|
@ -61,7 +61,7 @@ static bool debuggerPresent()
|
||||||
static int testAssertionHandler(const char* expr, const char* file, int line, const char* function)
|
static int testAssertionHandler(const char* expr, const char* file, int line, const char* function)
|
||||||
{
|
{
|
||||||
if (debuggerPresent())
|
if (debuggerPresent())
|
||||||
LUAU_DEBUGBREAK();
|
LUAU_DEBUGBREAK();
|
||||||
|
|
||||||
ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr));
|
ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr));
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -298,5 +298,3 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ AutocompleteTest.as_types
|
||||||
AutocompleteTest.autocomplete_boolean_singleton
|
AutocompleteTest.autocomplete_boolean_singleton
|
||||||
AutocompleteTest.autocomplete_default_type_pack_parameters
|
AutocompleteTest.autocomplete_default_type_pack_parameters
|
||||||
AutocompleteTest.autocomplete_default_type_parameters
|
AutocompleteTest.autocomplete_default_type_parameters
|
||||||
AutocompleteTest.autocomplete_documentation_symbols
|
|
||||||
AutocompleteTest.autocomplete_end_with_fn_exprs
|
AutocompleteTest.autocomplete_end_with_fn_exprs
|
||||||
AutocompleteTest.autocomplete_end_with_lambda
|
AutocompleteTest.autocomplete_end_with_lambda
|
||||||
AutocompleteTest.autocomplete_explicit_type_pack
|
AutocompleteTest.autocomplete_explicit_type_pack
|
||||||
|
@ -65,47 +64,34 @@ AutocompleteTest.autocomplete_until_in_repeat
|
||||||
AutocompleteTest.autocomplete_while_middle_keywords
|
AutocompleteTest.autocomplete_while_middle_keywords
|
||||||
AutocompleteTest.autocompleteProp_index_function_metamethod_is_variadic
|
AutocompleteTest.autocompleteProp_index_function_metamethod_is_variadic
|
||||||
AutocompleteTest.bias_toward_inner_scope
|
AutocompleteTest.bias_toward_inner_scope
|
||||||
AutocompleteTest.comments
|
|
||||||
AutocompleteTest.cyclic_table
|
AutocompleteTest.cyclic_table
|
||||||
|
AutocompleteTest.do_compatible_self_calls
|
||||||
AutocompleteTest.do_not_overwrite_context_sensitive_kws
|
AutocompleteTest.do_not_overwrite_context_sensitive_kws
|
||||||
AutocompleteTest.do_not_suggest_internal_module_type
|
AutocompleteTest.do_not_suggest_internal_module_type
|
||||||
AutocompleteTest.do_not_suggest_synthetic_table_name
|
AutocompleteTest.do_wrong_compatible_self_calls
|
||||||
AutocompleteTest.dont_offer_any_suggestions_from_the_end_of_a_comment
|
|
||||||
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment
|
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment
|
||||||
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment_at_the_very_end_of_the_file
|
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment_at_the_very_end_of_the_file
|
||||||
AutocompleteTest.dont_offer_any_suggestions_from_within_a_comment
|
AutocompleteTest.dont_offer_any_suggestions_from_within_a_comment
|
||||||
AutocompleteTest.dont_suggest_local_before_its_definition
|
AutocompleteTest.dont_suggest_local_before_its_definition
|
||||||
AutocompleteTest.empty_program
|
|
||||||
AutocompleteTest.function_expr_params
|
AutocompleteTest.function_expr_params
|
||||||
AutocompleteTest.function_in_assignment_has_parentheses
|
AutocompleteTest.function_in_assignment_has_parentheses
|
||||||
AutocompleteTest.function_in_assignment_has_parentheses_2
|
AutocompleteTest.function_in_assignment_has_parentheses_2
|
||||||
AutocompleteTest.function_parameters
|
AutocompleteTest.function_parameters
|
||||||
AutocompleteTest.function_result_passed_to_function_has_parentheses
|
AutocompleteTest.function_result_passed_to_function_has_parentheses
|
||||||
AutocompleteTest.function_type_types
|
|
||||||
AutocompleteTest.generic_types
|
AutocompleteTest.generic_types
|
||||||
AutocompleteTest.get_member_completions
|
|
||||||
AutocompleteTest.get_string_completions
|
|
||||||
AutocompleteTest.get_suggestions_for_new_statement
|
|
||||||
AutocompleteTest.get_suggestions_for_the_very_start_of_the_script
|
AutocompleteTest.get_suggestions_for_the_very_start_of_the_script
|
||||||
AutocompleteTest.global_function_params
|
AutocompleteTest.global_function_params
|
||||||
AutocompleteTest.global_functions_are_not_scoped_lexically
|
AutocompleteTest.global_functions_are_not_scoped_lexically
|
||||||
AutocompleteTest.if_then_else_elseif_completions
|
AutocompleteTest.if_then_else_elseif_completions
|
||||||
AutocompleteTest.if_then_else_full_keywords
|
AutocompleteTest.if_then_else_full_keywords
|
||||||
AutocompleteTest.keyword_members
|
|
||||||
AutocompleteTest.keyword_methods
|
AutocompleteTest.keyword_methods
|
||||||
AutocompleteTest.keyword_types
|
AutocompleteTest.keyword_types
|
||||||
AutocompleteTest.leave_numbers_alone
|
|
||||||
AutocompleteTest.library_non_self_calls_are_fine
|
AutocompleteTest.library_non_self_calls_are_fine
|
||||||
AutocompleteTest.library_self_calls_are_invalid
|
AutocompleteTest.library_self_calls_are_invalid
|
||||||
AutocompleteTest.local_function
|
AutocompleteTest.local_function
|
||||||
AutocompleteTest.local_function_params
|
AutocompleteTest.local_function_params
|
||||||
AutocompleteTest.local_functions_fall_out_of_scope
|
AutocompleteTest.local_functions_fall_out_of_scope
|
||||||
AutocompleteTest.local_initializer
|
|
||||||
AutocompleteTest.local_initializer_2
|
|
||||||
AutocompleteTest.local_names
|
|
||||||
AutocompleteTest.local_types_builtin
|
|
||||||
AutocompleteTest.method_call_inside_function_body
|
AutocompleteTest.method_call_inside_function_body
|
||||||
AutocompleteTest.method_call_inside_if_conditional
|
|
||||||
AutocompleteTest.module_type_members
|
AutocompleteTest.module_type_members
|
||||||
AutocompleteTest.modules_with_types
|
AutocompleteTest.modules_with_types
|
||||||
AutocompleteTest.nested_member_completions
|
AutocompleteTest.nested_member_completions
|
||||||
|
@ -114,16 +100,13 @@ AutocompleteTest.no_function_name_suggestions
|
||||||
AutocompleteTest.no_incompatible_self_calls
|
AutocompleteTest.no_incompatible_self_calls
|
||||||
AutocompleteTest.no_incompatible_self_calls_2
|
AutocompleteTest.no_incompatible_self_calls_2
|
||||||
AutocompleteTest.no_incompatible_self_calls_on_class
|
AutocompleteTest.no_incompatible_self_calls_on_class
|
||||||
AutocompleteTest.no_incompatible_self_calls_provisional
|
AutocompleteTest.no_wrong_compatible_self_calls_with_generics
|
||||||
AutocompleteTest.not_the_var_we_are_defining
|
|
||||||
AutocompleteTest.optional_members
|
AutocompleteTest.optional_members
|
||||||
AutocompleteTest.private_types
|
AutocompleteTest.private_types
|
||||||
AutocompleteTest.recommend_statement_starting_keywords
|
|
||||||
AutocompleteTest.recursive_function
|
AutocompleteTest.recursive_function
|
||||||
AutocompleteTest.recursive_function_global
|
AutocompleteTest.recursive_function_global
|
||||||
AutocompleteTest.recursive_function_local
|
AutocompleteTest.recursive_function_local
|
||||||
AutocompleteTest.return_types
|
AutocompleteTest.return_types
|
||||||
AutocompleteTest.skip_current_local
|
|
||||||
AutocompleteTest.sometimes_the_metatable_is_an_error
|
AutocompleteTest.sometimes_the_metatable_is_an_error
|
||||||
AutocompleteTest.source_module_preservation_and_invalidation
|
AutocompleteTest.source_module_preservation_and_invalidation
|
||||||
AutocompleteTest.statement_between_two_statements
|
AutocompleteTest.statement_between_two_statements
|
||||||
|
@ -154,9 +137,7 @@ AutocompleteTest.type_correct_suggestion_in_table
|
||||||
AutocompleteTest.type_scoping_easy
|
AutocompleteTest.type_scoping_easy
|
||||||
AutocompleteTest.unsealed_table
|
AutocompleteTest.unsealed_table
|
||||||
AutocompleteTest.unsealed_table_2
|
AutocompleteTest.unsealed_table_2
|
||||||
AutocompleteTest.user_defined_globals
|
|
||||||
AutocompleteTest.user_defined_local_functions_in_own_definition
|
AutocompleteTest.user_defined_local_functions_in_own_definition
|
||||||
BuiltinDefinitionsTest.lib_documentation_symbols
|
|
||||||
BuiltinTests.aliased_string_format
|
BuiltinTests.aliased_string_format
|
||||||
BuiltinTests.assert_removes_falsy_types
|
BuiltinTests.assert_removes_falsy_types
|
||||||
BuiltinTests.assert_removes_falsy_types2
|
BuiltinTests.assert_removes_falsy_types2
|
||||||
|
@ -231,16 +212,10 @@ BuiltinTests.tonumber_returns_optional_number_type
|
||||||
BuiltinTests.tonumber_returns_optional_number_type2
|
BuiltinTests.tonumber_returns_optional_number_type2
|
||||||
BuiltinTests.xpcall
|
BuiltinTests.xpcall
|
||||||
DefinitionTests.class_definition_function_prop
|
DefinitionTests.class_definition_function_prop
|
||||||
DefinitionTests.class_definitions_cannot_extend_non_class
|
|
||||||
DefinitionTests.class_definitions_cannot_overload_non_function
|
|
||||||
DefinitionTests.declaring_generic_functions
|
DefinitionTests.declaring_generic_functions
|
||||||
DefinitionTests.definition_file_class_function_args
|
DefinitionTests.definition_file_class_function_args
|
||||||
DefinitionTests.definition_file_classes
|
DefinitionTests.definition_file_classes
|
||||||
DefinitionTests.definition_file_loading
|
DefinitionTests.definition_file_loading
|
||||||
DefinitionTests.definitions_documentation_symbols
|
|
||||||
DefinitionTests.documentation_symbols_dont_attach_to_persistent_types
|
|
||||||
DefinitionTests.load_definition_file_errors_do_not_pollute_global_scope
|
|
||||||
DefinitionTests.no_cyclic_defined_classes
|
|
||||||
DefinitionTests.single_class_type_identity_in_global_types
|
DefinitionTests.single_class_type_identity_in_global_types
|
||||||
FrontendTest.accumulate_cached_errors
|
FrontendTest.accumulate_cached_errors
|
||||||
FrontendTest.accumulate_cached_errors_in_consistent_order
|
FrontendTest.accumulate_cached_errors_in_consistent_order
|
||||||
|
@ -256,12 +231,9 @@ FrontendTest.cycle_error_paths
|
||||||
FrontendTest.cycle_errors_can_be_fixed
|
FrontendTest.cycle_errors_can_be_fixed
|
||||||
FrontendTest.cycle_incremental_type_surface
|
FrontendTest.cycle_incremental_type_surface
|
||||||
FrontendTest.cycle_incremental_type_surface_longer
|
FrontendTest.cycle_incremental_type_surface_longer
|
||||||
FrontendTest.discard_type_graphs
|
|
||||||
FrontendTest.dont_recheck_script_that_hasnt_been_marked_dirty
|
FrontendTest.dont_recheck_script_that_hasnt_been_marked_dirty
|
||||||
FrontendTest.dont_reparse_clean_file_when_linting
|
FrontendTest.dont_reparse_clean_file_when_linting
|
||||||
FrontendTest.environments
|
FrontendTest.environments
|
||||||
FrontendTest.find_a_require
|
|
||||||
FrontendTest.find_a_require_inside_a_function
|
|
||||||
FrontendTest.ignore_require_to_nonexistent_file
|
FrontendTest.ignore_require_to_nonexistent_file
|
||||||
FrontendTest.imported_table_modification_2
|
FrontendTest.imported_table_modification_2
|
||||||
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
|
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
|
||||||
|
@ -269,29 +241,97 @@ FrontendTest.no_use_after_free_with_type_fun_instantiation
|
||||||
FrontendTest.nocheck_cycle_used_by_checked
|
FrontendTest.nocheck_cycle_used_by_checked
|
||||||
FrontendTest.nocheck_modules_are_typed
|
FrontendTest.nocheck_modules_are_typed
|
||||||
FrontendTest.produce_errors_for_unchanged_file_with_a_syntax_error
|
FrontendTest.produce_errors_for_unchanged_file_with_a_syntax_error
|
||||||
FrontendTest.produce_errors_for_unchanged_file_with_errors
|
|
||||||
FrontendTest.re_report_type_error_in_required_file
|
FrontendTest.re_report_type_error_in_required_file
|
||||||
FrontendTest.real_source
|
|
||||||
FrontendTest.recheck_if_dependent_script_is_dirty
|
FrontendTest.recheck_if_dependent_script_is_dirty
|
||||||
|
FrontendTest.reexport_cyclic_type
|
||||||
|
FrontendTest.reexport_type_alias
|
||||||
FrontendTest.report_require_to_nonexistent_file
|
FrontendTest.report_require_to_nonexistent_file
|
||||||
FrontendTest.report_syntax_error_in_required_file
|
FrontendTest.report_syntax_error_in_required_file
|
||||||
FrontendTest.reports_errors_from_multiple_sources
|
FrontendTest.reports_errors_from_multiple_sources
|
||||||
FrontendTest.stats_are_not_reset_between_checks
|
FrontendTest.stats_are_not_reset_between_checks
|
||||||
FrontendTest.test_lint_uses_correct_config
|
|
||||||
FrontendTest.test_pruneParentSegments
|
|
||||||
FrontendTest.trace_requires_in_nonstrict_mode
|
FrontendTest.trace_requires_in_nonstrict_mode
|
||||||
FrontendTest.typecheck_twice_for_ast_types
|
GenericsTests.apply_type_function_nested_generics1
|
||||||
|
GenericsTests.apply_type_function_nested_generics2
|
||||||
|
GenericsTests.better_mismatch_error_messages
|
||||||
|
GenericsTests.bound_tables_do_not_clone_original_fields
|
||||||
|
GenericsTests.check_generic_typepack_function
|
||||||
|
GenericsTests.check_mutual_generic_functions
|
||||||
|
GenericsTests.correctly_instantiate_polymorphic_member_functions
|
||||||
|
GenericsTests.do_not_always_instantiate_generic_intersection_types
|
||||||
|
GenericsTests.do_not_infer_generic_functions
|
||||||
|
GenericsTests.dont_substitute_bound_types
|
||||||
|
GenericsTests.dont_unify_bound_types
|
||||||
|
GenericsTests.duplicate_generic_type_packs
|
||||||
|
GenericsTests.duplicate_generic_types
|
||||||
|
GenericsTests.error_detailed_function_mismatch_generic_pack
|
||||||
|
GenericsTests.error_detailed_function_mismatch_generic_types
|
||||||
|
GenericsTests.factories_of_generics
|
||||||
|
GenericsTests.function_arguments_can_be_polytypes
|
||||||
|
GenericsTests.function_results_can_be_polytypes
|
||||||
|
GenericsTests.generic_argument_count_too_few
|
||||||
|
GenericsTests.generic_argument_count_too_many
|
||||||
|
GenericsTests.generic_factories
|
||||||
|
GenericsTests.generic_functions_dont_cache_type_parameters
|
||||||
|
GenericsTests.generic_functions_in_types
|
||||||
|
GenericsTests.generic_functions_should_be_memory_safe
|
||||||
|
GenericsTests.generic_table_method
|
||||||
|
GenericsTests.generic_type_pack_syntax
|
||||||
|
GenericsTests.generic_type_pack_unification1
|
||||||
|
GenericsTests.generic_type_pack_unification2
|
||||||
|
GenericsTests.generic_type_pack_unification3
|
||||||
|
GenericsTests.infer_generic_function_function_argument
|
||||||
|
GenericsTests.infer_generic_function_function_argument_overloaded
|
||||||
|
GenericsTests.infer_generic_lib_function_function_argument
|
||||||
|
GenericsTests.infer_generic_property
|
||||||
|
GenericsTests.inferred_local_vars_can_be_polytypes
|
||||||
|
GenericsTests.instantiate_cyclic_generic_function
|
||||||
|
GenericsTests.instantiate_generic_function_in_assignments
|
||||||
|
GenericsTests.instantiate_generic_function_in_assignments2
|
||||||
|
GenericsTests.instantiated_function_argument_names
|
||||||
|
GenericsTests.instantiation_sharing_types
|
||||||
|
GenericsTests.local_vars_can_be_instantiated_polytypes
|
||||||
|
GenericsTests.mutable_state_polymorphism
|
||||||
|
GenericsTests.no_stack_overflow_from_quantifying
|
||||||
|
GenericsTests.properties_can_be_instantiated_polytypes
|
||||||
|
GenericsTests.properties_can_be_polytypes
|
||||||
|
GenericsTests.rank_N_types_via_typeof
|
||||||
|
GenericsTests.reject_clashing_generic_and_pack_names
|
||||||
|
GenericsTests.self_recursive_instantiated_param
|
||||||
|
GenericsTests.substitution_with_bound_table
|
||||||
|
GenericsTests.typefuns_sharing_types
|
||||||
|
GenericsTests.variadic_generics
|
||||||
|
IntersectionTypes.argument_is_intersection
|
||||||
|
IntersectionTypes.error_detailed_intersection_all
|
||||||
|
IntersectionTypes.error_detailed_intersection_part
|
||||||
|
IntersectionTypes.fx_intersection_as_argument
|
||||||
|
IntersectionTypes.fx_union_as_argument_fails
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_with_all_parts_missing_the_property
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_with_mixed_types
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_with_one_part_missing_the_property
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_with_one_property_of_type_any
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_with_property_guaranteed_to_exist
|
||||||
|
IntersectionTypes.index_on_an_intersection_type_works_at_arbitrary_depth
|
||||||
|
IntersectionTypes.no_stack_overflow_from_flattenintersection
|
||||||
|
IntersectionTypes.overload_is_not_a_function
|
||||||
|
IntersectionTypes.propagates_name
|
||||||
|
IntersectionTypes.select_correct_union_fn
|
||||||
|
IntersectionTypes.should_still_pick_an_overload_whose_arguments_are_unions
|
||||||
|
IntersectionTypes.table_combines
|
||||||
|
IntersectionTypes.table_combines_missing
|
||||||
|
IntersectionTypes.table_extra_ok
|
||||||
|
IntersectionTypes.table_intersection_setmetatable
|
||||||
|
IntersectionTypes.table_intersection_write
|
||||||
|
IntersectionTypes.table_intersection_write_sealed
|
||||||
|
IntersectionTypes.table_intersection_write_sealed_indirect
|
||||||
|
IntersectionTypes.table_write_sealed_indirect
|
||||||
isSubtype.functions_and_any
|
isSubtype.functions_and_any
|
||||||
isSubtype.intersection_of_functions_of_different_arities
|
isSubtype.intersection_of_functions_of_different_arities
|
||||||
isSubtype.intersection_of_tables
|
isSubtype.intersection_of_tables
|
||||||
isSubtype.table_with_any_prop
|
isSubtype.table_with_any_prop
|
||||||
isSubtype.table_with_table_prop
|
isSubtype.table_with_table_prop
|
||||||
isSubtype.tables
|
isSubtype.tables
|
||||||
Linter.BuiltinGlobalWrite
|
|
||||||
Linter.DeprecatedApi
|
Linter.DeprecatedApi
|
||||||
Linter.LocalShadowGlobal
|
|
||||||
Linter.TableOperations
|
Linter.TableOperations
|
||||||
Linter.use_all_parent_scopes_for_globals
|
|
||||||
ModuleTests.any_persistance_does_not_leak
|
ModuleTests.any_persistance_does_not_leak
|
||||||
ModuleTests.builtin_types_point_into_globalTypes_arena
|
ModuleTests.builtin_types_point_into_globalTypes_arena
|
||||||
ModuleTests.clone_self_property
|
ModuleTests.clone_self_property
|
||||||
|
@ -342,45 +382,237 @@ Normalize.skip_force_normal_on_external_types
|
||||||
Normalize.union_of_distinct_free_types
|
Normalize.union_of_distinct_free_types
|
||||||
Normalize.variadic_tail_is_marked_normal
|
Normalize.variadic_tail_is_marked_normal
|
||||||
Normalize.visiting_a_type_twice_is_not_considered_normal
|
Normalize.visiting_a_type_twice_is_not_considered_normal
|
||||||
ParseErrorRecovery.empty_function_type_error_recovery
|
|
||||||
ParseErrorRecovery.extra_table_indexer_recovery
|
|
||||||
ParseErrorRecovery.extra_token_in_consume
|
|
||||||
ParseErrorRecovery.extra_token_in_consume_match
|
|
||||||
ParseErrorRecovery.extra_token_in_consume_match_end
|
|
||||||
ParseErrorRecovery.generic_type_list_recovery
|
ParseErrorRecovery.generic_type_list_recovery
|
||||||
ParseErrorRecovery.multiple_parse_errors
|
|
||||||
ParseErrorRecovery.recovery_of_parenthesized_expressions
|
ParseErrorRecovery.recovery_of_parenthesized_expressions
|
||||||
ParseErrorRecovery.statement_error_recovery_expected
|
|
||||||
ParseErrorRecovery.statement_error_recovery_unexpected
|
|
||||||
ParserTests.break_return_not_last_error
|
|
||||||
ParserTests.continue_not_last_error
|
|
||||||
ParserTests.error_on_confusable
|
|
||||||
ParserTests.error_on_non_utf8_sequence
|
|
||||||
ParserTests.error_on_unicode
|
|
||||||
ParserTests.export_is_an_identifier_only_when_followed_by_type
|
|
||||||
ParserTests.functions_cannot_have_return_annotations_if_extensions_are_disabled
|
|
||||||
ParserTests.illegal_type_alias_if_extensions_are_disabled
|
|
||||||
ParserTests.incomplete_statement_error
|
|
||||||
ParserTests.local_cannot_have_annotation_with_extensions_disabled
|
|
||||||
ParserTests.parse_compound_assignment_error_call
|
|
||||||
ParserTests.parse_compound_assignment_error_multiple
|
|
||||||
ParserTests.parse_compound_assignment_error_not_lvalue
|
|
||||||
ParserTests.parse_error_function_call
|
|
||||||
ParserTests.parse_error_function_call_newline
|
|
||||||
ParserTests.parse_error_messages
|
|
||||||
ParserTests.parse_error_table_literal
|
|
||||||
ParserTests.parse_error_type_name
|
|
||||||
ParserTests.parse_nesting_based_end_detection
|
|
||||||
ParserTests.parse_nesting_based_end_detection_failsafe_earlier
|
ParserTests.parse_nesting_based_end_detection_failsafe_earlier
|
||||||
ParserTests.parse_nesting_based_end_detection_local_function
|
ParserTests.parse_nesting_based_end_detection_local_function
|
||||||
ParserTests.parse_nesting_based_end_detection_local_repeat
|
ProvisionalTests.bail_early_if_unification_is_too_complicated
|
||||||
ParserTests.parse_nesting_based_end_detection_nested
|
ProvisionalTests.choose_the_right_overload_for_pcall
|
||||||
ParserTests.parse_nesting_based_end_detection_single_line
|
ProvisionalTests.constrained_is_level_dependent
|
||||||
ParserTests.parse_numbers_error
|
ProvisionalTests.discriminate_from_x_not_equal_to_nil
|
||||||
ParserTests.parse_numbers_range_error
|
ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack
|
||||||
ParserTests.stop_if_line_ends_with_hyphen
|
ProvisionalTests.error_on_eq_metamethod_returning_a_type_other_than_boolean
|
||||||
ParserTests.type_alias_error_messages
|
ProvisionalTests.free_is_not_bound_to_any
|
||||||
|
ProvisionalTests.function_returns_many_things_but_first_of_it_is_forgotten
|
||||||
|
ProvisionalTests.greedy_inference_with_shared_self_triggers_function_with_no_returns
|
||||||
|
ProvisionalTests.invariant_table_properties_means_instantiating_tables_in_call_is_unsound
|
||||||
|
ProvisionalTests.it_should_be_agnostic_of_actual_size
|
||||||
|
ProvisionalTests.lower_bounds_calculation_is_too_permissive_with_overloaded_higher_order_functions
|
||||||
|
ProvisionalTests.lvalue_equals_another_lvalue_with_no_overlap
|
||||||
|
ProvisionalTests.normalization_fails_on_certain_kinds_of_cyclic_tables
|
||||||
|
ProvisionalTests.operator_eq_completely_incompatible
|
||||||
|
ProvisionalTests.pcall_returns_at_least_two_value_but_function_returns_nothing
|
||||||
|
ProvisionalTests.setmetatable_constrains_free_type_into_free_table
|
||||||
|
ProvisionalTests.typeguard_inference_incomplete
|
||||||
|
ProvisionalTests.weird_fail_to_unify_type_pack
|
||||||
|
ProvisionalTests.weirditer_should_not_loop_forever
|
||||||
|
ProvisionalTests.while_body_are_also_refined
|
||||||
|
ProvisionalTests.xpcall_returns_what_f_returns
|
||||||
|
RefinementTest.and_constraint
|
||||||
|
RefinementTest.and_or_peephole_refinement
|
||||||
|
RefinementTest.apply_refinements_on_astexprindexexpr_whose_subscript_expr_is_constant_string
|
||||||
|
RefinementTest.assert_a_to_be_truthy_then_assert_a_to_be_number
|
||||||
|
RefinementTest.assert_non_binary_expressions_actually_resolve_constraints
|
||||||
|
RefinementTest.assign_table_with_refined_property_with_a_similar_type_is_illegal
|
||||||
|
RefinementTest.call_a_more_specific_function_using_typeguard
|
||||||
|
RefinementTest.correctly_lookup_a_shadowed_local_that_which_was_previously_refined
|
||||||
|
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined
|
||||||
|
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined2
|
||||||
|
RefinementTest.discriminate_from_isa_of_x
|
||||||
|
RefinementTest.discriminate_from_truthiness_of_x
|
||||||
|
RefinementTest.discriminate_on_properties_of_disjoint_tables_where_that_property_is_true_or_false
|
||||||
|
RefinementTest.discriminate_tag
|
||||||
|
RefinementTest.either_number_or_string
|
||||||
|
RefinementTest.eliminate_subclasses_of_instance
|
||||||
|
RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil
|
||||||
|
RefinementTest.free_type_is_equal_to_an_lvalue
|
||||||
|
RefinementTest.impossible_type_narrow_is_not_an_error
|
||||||
|
RefinementTest.index_on_a_refined_property
|
||||||
|
RefinementTest.invert_is_truthy_constraint
|
||||||
|
RefinementTest.invert_is_truthy_constraint_ifelse_expression
|
||||||
|
RefinementTest.is_truthy_constraint
|
||||||
|
RefinementTest.is_truthy_constraint_ifelse_expression
|
||||||
|
RefinementTest.lvalue_is_equal_to_a_term
|
||||||
|
RefinementTest.lvalue_is_equal_to_another_lvalue
|
||||||
|
RefinementTest.lvalue_is_not_nil
|
||||||
|
RefinementTest.merge_should_be_fully_agnostic_of_hashmap_ordering
|
||||||
|
RefinementTest.narrow_property_of_a_bounded_variable
|
||||||
|
RefinementTest.narrow_this_large_union
|
||||||
|
RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true
|
||||||
|
RefinementTest.not_a_and_not_b
|
||||||
|
RefinementTest.not_a_and_not_b2
|
||||||
|
RefinementTest.not_a_or_not_b
|
||||||
|
RefinementTest.not_a_or_not_b2
|
||||||
|
RefinementTest.not_and_constraint
|
||||||
|
RefinementTest.not_t_or_some_prop_of_t
|
||||||
|
RefinementTest.or_predicate_with_truthy_predicates
|
||||||
|
RefinementTest.parenthesized_expressions_are_followed_through
|
||||||
|
RefinementTest.refine_a_property_not_to_be_nil_through_an_intersection_table
|
||||||
|
RefinementTest.refine_the_correct_types_opposite_of_when_a_is_not_number_or_string
|
||||||
|
RefinementTest.refine_unknowns
|
||||||
|
RefinementTest.string_not_equal_to_string_or_nil
|
||||||
|
RefinementTest.term_is_equal_to_an_lvalue
|
||||||
|
RefinementTest.truthy_constraint_on_properties
|
||||||
|
RefinementTest.type_assertion_expr_carry_its_constraints
|
||||||
|
RefinementTest.type_comparison_ifelse_expression
|
||||||
|
RefinementTest.type_guard_can_filter_for_intersection_of_tables
|
||||||
|
RefinementTest.type_guard_can_filter_for_overloaded_function
|
||||||
|
RefinementTest.type_guard_narrowed_into_nothingness
|
||||||
|
RefinementTest.type_narrow_for_all_the_userdata
|
||||||
|
RefinementTest.type_narrow_to_vector
|
||||||
|
RefinementTest.typeguard_cast_free_table_to_vector
|
||||||
|
RefinementTest.typeguard_cast_instance_or_vector3_to_vector
|
||||||
|
RefinementTest.typeguard_doesnt_leak_to_elseif
|
||||||
|
RefinementTest.typeguard_in_assert_position
|
||||||
|
RefinementTest.typeguard_in_if_condition_position
|
||||||
|
RefinementTest.typeguard_narrows_for_functions
|
||||||
|
RefinementTest.typeguard_narrows_for_table
|
||||||
|
RefinementTest.typeguard_not_to_be_string
|
||||||
|
RefinementTest.typeguard_only_look_up_types_from_global_scope
|
||||||
|
RefinementTest.unknown_lvalue_is_not_synonymous_with_other_on_not_equal
|
||||||
|
RefinementTest.what_nonsensical_condition
|
||||||
|
RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table
|
||||||
|
RefinementTest.x_is_not_instance_or_else_not_part
|
||||||
RuntimeLimits.typescript_port_of_Result_type
|
RuntimeLimits.typescript_port_of_Result_type
|
||||||
|
TableTests.a_free_shape_can_turn_into_a_scalar_if_it_is_compatible
|
||||||
|
TableTests.a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible
|
||||||
|
TableTests.access_index_metamethod_that_returns_variadic
|
||||||
|
TableTests.accidentally_checked_prop_in_opposite_branch
|
||||||
|
TableTests.assigning_to_an_unsealed_table_with_string_literal_should_infer_new_properties_over_indexer
|
||||||
|
TableTests.augment_nested_table
|
||||||
|
TableTests.augment_table
|
||||||
|
TableTests.builtin_table_names
|
||||||
|
TableTests.call_method
|
||||||
|
TableTests.call_method_with_explicit_self_argument
|
||||||
|
TableTests.cannot_augment_sealed_table
|
||||||
|
TableTests.cannot_call_tables
|
||||||
|
TableTests.cannot_change_type_of_unsealed_table_prop
|
||||||
|
TableTests.casting_sealed_tables_with_props_into_table_with_indexer
|
||||||
|
TableTests.casting_tables_with_props_into_table_with_indexer3
|
||||||
|
TableTests.casting_tables_with_props_into_table_with_indexer4
|
||||||
|
TableTests.casting_unsealed_tables_with_props_into_table_with_indexer
|
||||||
|
TableTests.checked_prop_too_early
|
||||||
|
TableTests.common_table_element_general
|
||||||
|
TableTests.common_table_element_inner_index
|
||||||
|
TableTests.common_table_element_inner_prop
|
||||||
|
TableTests.common_table_element_list
|
||||||
|
TableTests.common_table_element_union_assignment
|
||||||
|
TableTests.common_table_element_union_in_call
|
||||||
|
TableTests.common_table_element_union_in_call_tail
|
||||||
|
TableTests.common_table_element_union_in_prop
|
||||||
|
TableTests.confusing_indexing
|
||||||
|
TableTests.defining_a_method_for_a_builtin_sealed_table_must_fail
|
||||||
|
TableTests.defining_a_method_for_a_local_sealed_table_must_fail
|
||||||
|
TableTests.defining_a_method_for_a_local_unsealed_table_is_ok
|
||||||
|
TableTests.defining_a_self_method_for_a_builtin_sealed_table_must_fail
|
||||||
|
TableTests.defining_a_self_method_for_a_local_sealed_table_must_fail
|
||||||
|
TableTests.defining_a_self_method_for_a_local_unsealed_table_is_ok
|
||||||
|
TableTests.dont_crash_when_setmetatable_does_not_produce_a_metatabletypevar
|
||||||
|
TableTests.dont_hang_when_trying_to_look_up_in_cyclic_metatable_index
|
||||||
|
TableTests.dont_invalidate_the_properties_iterator_of_free_table_when_rolled_back
|
||||||
|
TableTests.dont_leak_free_table_props
|
||||||
|
TableTests.dont_quantify_table_that_belongs_to_outer_scope
|
||||||
|
TableTests.dont_seal_an_unsealed_table_by_passing_it_to_a_function_that_takes_a_sealed_table
|
||||||
|
TableTests.dont_suggest_exact_match_keys
|
||||||
|
TableTests.error_detailed_indexer_key
|
||||||
|
TableTests.error_detailed_indexer_value
|
||||||
|
TableTests.error_detailed_metatable_prop
|
||||||
|
TableTests.error_detailed_prop
|
||||||
|
TableTests.error_detailed_prop_nested
|
||||||
|
TableTests.expected_indexer_from_table_union
|
||||||
|
TableTests.expected_indexer_value_type_extra
|
||||||
|
TableTests.expected_indexer_value_type_extra_2
|
||||||
|
TableTests.explicitly_typed_table
|
||||||
|
TableTests.explicitly_typed_table_error
|
||||||
|
TableTests.explicitly_typed_table_with_indexer
|
||||||
|
TableTests.found_like_key_in_table_function_call
|
||||||
|
TableTests.found_like_key_in_table_property_access
|
||||||
|
TableTests.found_multiple_like_keys
|
||||||
|
TableTests.function_calls_produces_sealed_table_given_unsealed_table
|
||||||
|
TableTests.generalize_table_argument
|
||||||
|
TableTests.getmetatable_returns_pointer_to_metatable
|
||||||
|
TableTests.give_up_after_one_metatable_index_look_up
|
||||||
|
TableTests.hide_table_error_properties
|
||||||
|
TableTests.indexer_fn
|
||||||
|
TableTests.indexer_on_sealed_table_must_unify_with_free_table
|
||||||
|
TableTests.indexer_table
|
||||||
|
TableTests.indexing_from_a_table_should_prefer_properties_when_possible
|
||||||
|
TableTests.inequality_operators_imply_exactly_matching_types
|
||||||
|
TableTests.infer_array_2
|
||||||
|
TableTests.infer_indexer_from_value_property_in_literal
|
||||||
|
TableTests.inferred_return_type_of_free_table
|
||||||
|
TableTests.inferring_crazy_table_should_also_be_quick
|
||||||
|
TableTests.instantiate_table_cloning
|
||||||
|
TableTests.instantiate_table_cloning_2
|
||||||
|
TableTests.instantiate_table_cloning_3
|
||||||
|
TableTests.instantiate_tables_at_scope_level
|
||||||
|
TableTests.invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound
|
||||||
|
TableTests.leaking_bad_metatable_errors
|
||||||
|
TableTests.length_operator_intersection
|
||||||
|
TableTests.length_operator_non_table_union
|
||||||
|
TableTests.length_operator_union
|
||||||
|
TableTests.length_operator_union_errors
|
||||||
|
TableTests.less_exponential_blowup_please
|
||||||
|
TableTests.meta_add
|
||||||
|
TableTests.meta_add_both_ways
|
||||||
|
TableTests.meta_add_inferred
|
||||||
|
TableTests.metatable_mismatch_should_fail
|
||||||
|
TableTests.missing_metatable_for_sealed_tables_do_not_get_inferred
|
||||||
|
TableTests.mixed_tables_with_implicit_numbered_keys
|
||||||
|
TableTests.MixedPropertiesAndIndexers
|
||||||
|
TableTests.nil_assign_doesnt_hit_indexer
|
||||||
|
TableTests.okay_to_add_property_to_unsealed_tables_by_function_call
|
||||||
|
TableTests.only_ascribe_synthetic_names_at_module_scope
|
||||||
|
TableTests.oop_indexer_works
|
||||||
|
TableTests.oop_polymorphic
|
||||||
|
TableTests.open_table_unification_2
|
||||||
|
TableTests.pass_a_union_of_tables_to_a_function_that_requires_a_table
|
||||||
|
TableTests.pass_a_union_of_tables_to_a_function_that_requires_a_table_2
|
||||||
|
TableTests.pass_incompatible_union_to_a_generic_table_without_crashing
|
||||||
|
TableTests.passing_compatible_unions_to_a_generic_table_without_crashing
|
||||||
|
TableTests.persistent_sealed_table_is_immutable
|
||||||
|
TableTests.property_lookup_through_tabletypevar_metatable
|
||||||
|
TableTests.quantify_even_that_table_was_never_exported_at_all
|
||||||
|
TableTests.quantify_metatables_of_metatables_of_table
|
||||||
|
TableTests.quantifying_a_bound_var_works
|
||||||
|
TableTests.reasonable_error_when_adding_a_nonexistent_property_to_an_array_like_table
|
||||||
|
TableTests.recursive_metatable_type_call
|
||||||
|
TableTests.result_is_always_any_if_lhs_is_any
|
||||||
|
TableTests.result_is_bool_for_equality_operators_if_lhs_is_any
|
||||||
|
TableTests.right_table_missing_key
|
||||||
|
TableTests.right_table_missing_key2
|
||||||
|
TableTests.scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type
|
||||||
|
TableTests.scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type
|
||||||
|
TableTests.setmetatable_cant_be_used_to_mutate_global_types
|
||||||
|
TableTests.shared_selfs
|
||||||
|
TableTests.shared_selfs_from_free_param
|
||||||
|
TableTests.shared_selfs_through_metatables
|
||||||
|
TableTests.table_function_check_use_after_free
|
||||||
|
TableTests.table_indexing_error_location
|
||||||
|
TableTests.table_insert_should_cope_with_optional_properties_in_nonstrict
|
||||||
|
TableTests.table_insert_should_cope_with_optional_properties_in_strict
|
||||||
|
TableTests.table_length
|
||||||
|
TableTests.table_param_row_polymorphism_2
|
||||||
|
TableTests.table_param_row_polymorphism_3
|
||||||
|
TableTests.table_simple_call
|
||||||
|
TableTests.table_subtyping_with_extra_props_dont_report_multiple_errors
|
||||||
|
TableTests.table_subtyping_with_missing_props_dont_report_multiple_errors
|
||||||
|
TableTests.table_subtyping_with_missing_props_dont_report_multiple_errors2
|
||||||
|
TableTests.table_unifies_into_map
|
||||||
|
TableTests.tables_get_names_from_their_locals
|
||||||
|
TableTests.tc_member_function
|
||||||
|
TableTests.tc_member_function_2
|
||||||
|
TableTests.top_table_type
|
||||||
|
TableTests.type_mismatch_on_massive_table_is_cut_short
|
||||||
|
TableTests.unification_of_unions_in_a_self_referential_type
|
||||||
|
TableTests.unifying_tables_shouldnt_uaf1
|
||||||
|
TableTests.unifying_tables_shouldnt_uaf2
|
||||||
|
TableTests.used_colon_correctly
|
||||||
|
TableTests.used_colon_instead_of_dot
|
||||||
|
TableTests.used_dot_instead_of_colon
|
||||||
|
TableTests.used_dot_instead_of_colon_but_correctly
|
||||||
|
TableTests.user_defined_table_types_are_named
|
||||||
|
TableTests.width_subtyping
|
||||||
ToDot.bound_table
|
ToDot.bound_table
|
||||||
ToDot.class
|
ToDot.class
|
||||||
ToDot.function
|
ToDot.function
|
||||||
|
@ -401,9 +633,13 @@ ToString.toStringNamedFunction_id
|
||||||
ToString.toStringNamedFunction_map
|
ToString.toStringNamedFunction_map
|
||||||
ToString.toStringNamedFunction_overrides_param_names
|
ToString.toStringNamedFunction_overrides_param_names
|
||||||
ToString.toStringNamedFunction_variadics
|
ToString.toStringNamedFunction_variadics
|
||||||
TranspilerTests.attach_types
|
|
||||||
TranspilerTests.type_lists_should_be_emitted_correctly
|
TranspilerTests.type_lists_should_be_emitted_correctly
|
||||||
TranspilerTests.types_should_not_be_considered_cyclic_if_they_are_not_recursive
|
TranspilerTests.types_should_not_be_considered_cyclic_if_they_are_not_recursive
|
||||||
|
TryUnifyTests.cli_41095_concat_log_in_sealed_table_unification
|
||||||
|
TryUnifyTests.members_of_failed_typepack_unification_are_unified_with_errorType
|
||||||
|
TryUnifyTests.result_of_failed_typepack_unification_is_constrained
|
||||||
|
TryUnifyTests.typepack_unification_should_trim_free_tails
|
||||||
|
TryUnifyTests.variadics_should_use_reversed_properly
|
||||||
TypeAliases.basic_alias
|
TypeAliases.basic_alias
|
||||||
TypeAliases.cannot_steal_hoisted_type_alias
|
TypeAliases.cannot_steal_hoisted_type_alias
|
||||||
TypeAliases.cli_38393_recursive_intersection_oom
|
TypeAliases.cli_38393_recursive_intersection_oom
|
||||||
|
@ -446,6 +682,37 @@ TypeAliases.type_alias_local_synthetic_mutation
|
||||||
TypeAliases.type_alias_of_an_imported_recursive_generic_type
|
TypeAliases.type_alias_of_an_imported_recursive_generic_type
|
||||||
TypeAliases.type_alias_of_an_imported_recursive_type
|
TypeAliases.type_alias_of_an_imported_recursive_type
|
||||||
TypeAliases.use_table_name_and_generic_params_in_errors
|
TypeAliases.use_table_name_and_generic_params_in_errors
|
||||||
|
TypeInfer.check_expr_recursion_limit
|
||||||
|
TypeInfer.checking_should_not_ice
|
||||||
|
TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error
|
||||||
|
TypeInfer.cyclic_follow
|
||||||
|
TypeInfer.do_not_bind_a_free_table_to_a_union_containing_that_table
|
||||||
|
TypeInfer.dont_report_type_errors_within_an_AstStatError
|
||||||
|
TypeInfer.follow_on_new_types_in_substitution
|
||||||
|
TypeInfer.free_typevars_introduced_within_control_flow_constructs_do_not_get_an_elevated_TypeLevel
|
||||||
|
TypeInfer.globals
|
||||||
|
TypeInfer.globals2
|
||||||
|
TypeInfer.index_expr_should_be_checked
|
||||||
|
TypeInfer.infer_assignment_value_types
|
||||||
|
TypeInfer.infer_assignment_value_types_mutable_lval
|
||||||
|
TypeInfer.infer_through_group_expr
|
||||||
|
TypeInfer.infer_type_assertion_value_type
|
||||||
|
TypeInfer.no_heap_use_after_free_error
|
||||||
|
TypeInfer.no_infinite_loop_when_trying_to_unify_uh_this
|
||||||
|
TypeInfer.no_stack_overflow_from_isoptional
|
||||||
|
TypeInfer.no_stack_overflow_from_isoptional2
|
||||||
|
TypeInfer.recursive_metatable_crash
|
||||||
|
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
|
||||||
|
TypeInfer.tc_if_else_expressions1
|
||||||
|
TypeInfer.tc_if_else_expressions2
|
||||||
|
TypeInfer.tc_if_else_expressions_expected_type_1
|
||||||
|
TypeInfer.tc_if_else_expressions_expected_type_2
|
||||||
|
TypeInfer.tc_if_else_expressions_expected_type_3
|
||||||
|
TypeInfer.tc_if_else_expressions_type_union
|
||||||
|
TypeInfer.type_infer_recursion_limit_no_ice
|
||||||
|
TypeInfer.types stored in astResolvedTypes
|
||||||
|
TypeInfer.warn_on_lowercase_parent_property
|
||||||
|
TypeInfer.weird_case
|
||||||
TypeInferAnyError.any_type_propagates
|
TypeInferAnyError.any_type_propagates
|
||||||
TypeInferAnyError.assign_prop_to_table_by_calling_any_yields_any
|
TypeInferAnyError.assign_prop_to_table_by_calling_any_yields_any
|
||||||
TypeInferAnyError.call_to_any_yields_any
|
TypeInferAnyError.call_to_any_yields_any
|
||||||
|
@ -496,26 +763,289 @@ TypeInferClasses.we_can_infer_that_a_parameter_must_be_a_particular_class
|
||||||
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
|
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
|
||||||
TypeInferFunctions.another_indirect_function_case_where_it_is_ok_to_provide_too_many_arguments
|
TypeInferFunctions.another_indirect_function_case_where_it_is_ok_to_provide_too_many_arguments
|
||||||
TypeInferFunctions.another_recursive_local_function
|
TypeInferFunctions.another_recursive_local_function
|
||||||
|
TypeInferFunctions.calling_function_with_anytypepack_doesnt_leak_free_types
|
||||||
|
TypeInferFunctions.calling_function_with_incorrect_argument_type_yields_errors_spanning_argument
|
||||||
TypeInferFunctions.cannot_hoist_interior_defns_into_signature
|
TypeInferFunctions.cannot_hoist_interior_defns_into_signature
|
||||||
TypeInferFunctions.check_function_before_lambda_that_uses_it
|
TypeInferFunctions.check_function_before_lambda_that_uses_it
|
||||||
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
|
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
|
||||||
TypeInferFunctions.cyclic_function_type_in_args
|
TypeInferFunctions.cyclic_function_type_in_args
|
||||||
TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists
|
TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists
|
||||||
|
TypeInferFunctions.dont_infer_parameter_types_for_functions_from_their_call_site
|
||||||
|
TypeInferFunctions.dont_mutate_the_underlying_head_of_typepack_when_calling_with_self
|
||||||
TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict
|
TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict
|
||||||
|
TypeInferFunctions.error_detailed_function_mismatch_arg
|
||||||
|
TypeInferFunctions.error_detailed_function_mismatch_arg_count
|
||||||
|
TypeInferFunctions.error_detailed_function_mismatch_ret
|
||||||
|
TypeInferFunctions.error_detailed_function_mismatch_ret_count
|
||||||
|
TypeInferFunctions.error_detailed_function_mismatch_ret_mult
|
||||||
TypeInferFunctions.first_argument_can_be_optional
|
TypeInferFunctions.first_argument_can_be_optional
|
||||||
|
TypeInferFunctions.free_is_not_bound_to_unknown
|
||||||
TypeInferFunctions.func_expr_doesnt_leak_free
|
TypeInferFunctions.func_expr_doesnt_leak_free
|
||||||
|
TypeInferFunctions.function_cast_error_uses_correct_language
|
||||||
|
TypeInferFunctions.function_decl_non_self_sealed_overwrite
|
||||||
|
TypeInferFunctions.function_decl_non_self_sealed_overwrite_2
|
||||||
|
TypeInferFunctions.function_decl_non_self_unsealed_overwrite
|
||||||
|
TypeInferFunctions.function_decl_quantify_right_type
|
||||||
|
TypeInferFunctions.function_does_not_return_enough_values
|
||||||
|
TypeInferFunctions.function_statement_sealed_table_assignment_through_indexer
|
||||||
TypeInferFunctions.higher_order_function_2
|
TypeInferFunctions.higher_order_function_2
|
||||||
TypeInferFunctions.higher_order_function_4
|
TypeInferFunctions.higher_order_function_4
|
||||||
|
TypeInferFunctions.ignored_return_values
|
||||||
|
TypeInferFunctions.inconsistent_higher_order_function
|
||||||
|
TypeInferFunctions.inconsistent_return_types
|
||||||
|
TypeInferFunctions.infer_anonymous_function_arguments
|
||||||
|
TypeInferFunctions.infer_anonymous_function_arguments_outside_call
|
||||||
TypeInferFunctions.infer_return_type_from_selected_overload
|
TypeInferFunctions.infer_return_type_from_selected_overload
|
||||||
TypeInferFunctions.infer_that_function_does_not_return_a_table
|
TypeInferFunctions.infer_that_function_does_not_return_a_table
|
||||||
|
TypeInferFunctions.inferred_higher_order_functions_are_quantified_at_the_right_time
|
||||||
|
TypeInferFunctions.inferred_higher_order_functions_are_quantified_at_the_right_time2
|
||||||
TypeInferFunctions.it_is_ok_not_to_supply_enough_retvals
|
TypeInferFunctions.it_is_ok_not_to_supply_enough_retvals
|
||||||
TypeInferFunctions.it_is_ok_to_oversaturate_a_higher_order_function_argument
|
TypeInferFunctions.it_is_ok_to_oversaturate_a_higher_order_function_argument
|
||||||
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
|
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
|
||||||
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
|
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
|
||||||
TypeInferFunctions.mutual_recursion
|
TypeInferFunctions.mutual_recursion
|
||||||
|
TypeInferFunctions.no_lossy_function_type
|
||||||
|
TypeInferFunctions.occurs_check_failure_in_function_return_type
|
||||||
|
TypeInferFunctions.quantify_constrained_types
|
||||||
|
TypeInferFunctions.record_matching_overload
|
||||||
TypeInferFunctions.recursive_function
|
TypeInferFunctions.recursive_function
|
||||||
TypeInferFunctions.recursive_local_function
|
TypeInferFunctions.recursive_local_function
|
||||||
|
TypeInferFunctions.report_exiting_without_return_nonstrict
|
||||||
|
TypeInferFunctions.report_exiting_without_return_strict
|
||||||
|
TypeInferFunctions.return_type_by_overload
|
||||||
|
TypeInferFunctions.strict_mode_ok_with_missing_arguments
|
||||||
|
TypeInferFunctions.too_few_arguments_variadic
|
||||||
|
TypeInferFunctions.too_few_arguments_variadic_generic
|
||||||
|
TypeInferFunctions.too_few_arguments_variadic_generic2
|
||||||
TypeInferFunctions.too_many_arguments
|
TypeInferFunctions.too_many_arguments
|
||||||
|
TypeInferFunctions.too_many_return_values
|
||||||
TypeInferFunctions.toposort_doesnt_break_mutual_recursion
|
TypeInferFunctions.toposort_doesnt_break_mutual_recursion
|
||||||
TypeInferFunctions.vararg_function_is_quantified
|
TypeInferFunctions.vararg_function_is_quantified
|
||||||
TypeInferFunctions.vararg_functions_should_allow_calls_of_any_types_and_size
|
TypeInferFunctions.vararg_functions_should_allow_calls_of_any_types_and_size
|
||||||
|
TypeInferLoops.correctly_scope_locals_while
|
||||||
|
TypeInferLoops.for_in_loop
|
||||||
|
TypeInferLoops.for_in_loop_error_on_factory_not_returning_the_right_amount_of_values
|
||||||
|
TypeInferLoops.for_in_loop_error_on_iterator_requiring_args_but_none_given
|
||||||
|
TypeInferLoops.for_in_loop_on_error
|
||||||
|
TypeInferLoops.for_in_loop_on_non_function
|
||||||
|
TypeInferLoops.for_in_loop_should_fail_with_non_function_iterator
|
||||||
|
TypeInferLoops.for_in_loop_where_iteratee_is_free
|
||||||
|
TypeInferLoops.for_in_loop_with_custom_iterator
|
||||||
|
TypeInferLoops.for_in_loop_with_next
|
||||||
|
TypeInferLoops.for_in_with_a_custom_iterator_should_type_check
|
||||||
|
TypeInferLoops.for_in_with_an_iterator_of_type_any
|
||||||
|
TypeInferLoops.for_in_with_just_one_iterator_is_ok
|
||||||
|
TypeInferLoops.fuzz_fail_missing_instantitation_follow
|
||||||
|
TypeInferLoops.ipairs_produces_integral_indices
|
||||||
|
TypeInferLoops.loop_iter_basic
|
||||||
|
TypeInferLoops.loop_iter_iter_metamethod
|
||||||
|
TypeInferLoops.loop_iter_no_indexer_nonstrict
|
||||||
|
TypeInferLoops.loop_iter_no_indexer_strict
|
||||||
|
TypeInferLoops.loop_iter_trailing_nil
|
||||||
|
TypeInferLoops.loop_typecheck_crash_on_empty_optional
|
||||||
|
TypeInferLoops.properly_infer_iteratee_is_a_free_table
|
||||||
|
TypeInferLoops.repeat_loop
|
||||||
|
TypeInferLoops.repeat_loop_condition_binds_to_its_block
|
||||||
|
TypeInferLoops.symbols_in_repeat_block_should_not_be_visible_beyond_until_condition
|
||||||
|
TypeInferLoops.unreachable_code_after_infinite_loop
|
||||||
|
TypeInferLoops.varlist_declared_by_for_in_loop_should_be_free
|
||||||
|
TypeInferLoops.while_loop
|
||||||
|
TypeInferModules.bound_free_table_export_is_ok
|
||||||
|
TypeInferModules.constrained_anyification_clone_immutable_types
|
||||||
|
TypeInferModules.custom_require_global
|
||||||
|
TypeInferModules.do_not_modify_imported_types
|
||||||
|
TypeInferModules.do_not_modify_imported_types_2
|
||||||
|
TypeInferModules.do_not_modify_imported_types_3
|
||||||
|
TypeInferModules.do_not_modify_imported_types_4
|
||||||
|
TypeInferModules.general_require_call_expression
|
||||||
|
TypeInferModules.general_require_type_mismatch
|
||||||
|
TypeInferModules.module_type_conflict
|
||||||
|
TypeInferModules.module_type_conflict_instantiated
|
||||||
|
TypeInferModules.require
|
||||||
|
TypeInferModules.require_a_variadic_function
|
||||||
|
TypeInferModules.require_failed_module
|
||||||
|
TypeInferModules.require_module_that_does_not_export
|
||||||
|
TypeInferModules.require_types
|
||||||
|
TypeInferModules.type_error_of_unknown_qualified_type
|
||||||
|
TypeInferModules.warn_if_you_try_to_require_a_non_modulescript
|
||||||
|
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_another_overload_works
|
||||||
|
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2
|
||||||
|
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
||||||
|
TypeInferOOP.inferred_methods_of_free_tables_have_the_same_level_as_the_enclosing_table
|
||||||
|
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
||||||
|
TypeInferOOP.method_depends_on_table
|
||||||
|
TypeInferOOP.methods_are_topologically_sorted
|
||||||
|
TypeInferOOP.nonstrict_self_mismatch_tail
|
||||||
|
TypeInferOOP.object_constructor_can_refer_to_method_of_self
|
||||||
|
TypeInferOOP.table_oop
|
||||||
|
TypeInferOperators.and_adds_boolean
|
||||||
|
TypeInferOperators.and_adds_boolean_no_superfluous_union
|
||||||
|
TypeInferOperators.and_binexps_dont_unify
|
||||||
|
TypeInferOperators.and_or_ternary
|
||||||
|
TypeInferOperators.CallAndOrOfFunctions
|
||||||
|
TypeInferOperators.cannot_compare_tables_that_do_not_have_the_same_metatable
|
||||||
|
TypeInferOperators.cannot_indirectly_compare_types_that_do_not_have_a_metatable
|
||||||
|
TypeInferOperators.cannot_indirectly_compare_types_that_do_not_offer_overloaded_ordering_operators
|
||||||
|
TypeInferOperators.cli_38355_recursive_union
|
||||||
|
TypeInferOperators.compare_numbers
|
||||||
|
TypeInferOperators.compare_strings
|
||||||
|
TypeInferOperators.compound_assign_basic
|
||||||
|
TypeInferOperators.compound_assign_metatable
|
||||||
|
TypeInferOperators.compound_assign_mismatch_metatable
|
||||||
|
TypeInferOperators.compound_assign_mismatch_op
|
||||||
|
TypeInferOperators.compound_assign_mismatch_result
|
||||||
|
TypeInferOperators.concat_op_on_free_lhs_and_string_rhs
|
||||||
|
TypeInferOperators.concat_op_on_string_lhs_and_free_rhs
|
||||||
|
TypeInferOperators.disallow_string_and_types_without_metatables_from_arithmetic_binary_ops
|
||||||
|
TypeInferOperators.dont_strip_nil_from_rhs_or_operator
|
||||||
|
TypeInferOperators.equality_operations_succeed_if_any_union_branch_succeeds
|
||||||
|
TypeInferOperators.error_on_invalid_operand_types_to_relational_operators
|
||||||
|
TypeInferOperators.error_on_invalid_operand_types_to_relational_operators2
|
||||||
|
TypeInferOperators.expected_types_through_binary_and
|
||||||
|
TypeInferOperators.expected_types_through_binary_or
|
||||||
|
TypeInferOperators.in_nonstrict_mode_strip_nil_from_intersections_when_considering_relational_operators
|
||||||
|
TypeInferOperators.infer_any_in_all_modes_when_lhs_is_unknown
|
||||||
|
TypeInferOperators.operator_eq_operands_are_not_subtypes_of_each_other_but_has_overlap
|
||||||
|
TypeInferOperators.operator_eq_verifies_types_do_intersect
|
||||||
|
TypeInferOperators.or_joins_types
|
||||||
|
TypeInferOperators.or_joins_types_with_no_extras
|
||||||
|
TypeInferOperators.primitive_arith_no_metatable
|
||||||
|
TypeInferOperators.primitive_arith_no_metatable_with_follows
|
||||||
|
TypeInferOperators.primitive_arith_possible_metatable
|
||||||
|
TypeInferOperators.produce_the_correct_error_message_when_comparing_a_table_with_a_metatable_with_one_that_does_not
|
||||||
|
TypeInferOperators.refine_and_or
|
||||||
|
TypeInferOperators.some_primitive_binary_ops
|
||||||
|
TypeInferOperators.strict_binary_op_where_lhs_unknown
|
||||||
|
TypeInferOperators.strip_nil_from_lhs_or_operator
|
||||||
|
TypeInferOperators.strip_nil_from_lhs_or_operator2
|
||||||
|
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection
|
||||||
|
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection_on_rhs
|
||||||
|
TypeInferOperators.typecheck_unary_len_error
|
||||||
|
TypeInferOperators.typecheck_unary_minus
|
||||||
|
TypeInferOperators.typecheck_unary_minus_error
|
||||||
|
TypeInferOperators.unary_not_is_boolean
|
||||||
|
TypeInferOperators.unknown_type_in_comparison
|
||||||
|
TypeInferOperators.UnknownGlobalCompoundAssign
|
||||||
|
TypeInferPrimitives.cannot_call_primitives
|
||||||
|
TypeInferPrimitives.CheckMethodsOfNumber
|
||||||
|
TypeInferPrimitives.string_function_other
|
||||||
|
TypeInferPrimitives.string_index
|
||||||
|
TypeInferPrimitives.string_length
|
||||||
|
TypeInferPrimitives.string_method
|
||||||
|
TypeInferUnknownNever.array_like_table_of_never_is_inhabitable
|
||||||
|
TypeInferUnknownNever.assign_to_global_which_is_never
|
||||||
|
TypeInferUnknownNever.assign_to_local_which_is_never
|
||||||
|
TypeInferUnknownNever.assign_to_prop_which_is_never
|
||||||
|
TypeInferUnknownNever.assign_to_subscript_which_is_never
|
||||||
|
TypeInferUnknownNever.call_never
|
||||||
|
TypeInferUnknownNever.dont_unify_operands_if_one_of_the_operand_is_never_in_any_ordering_operators
|
||||||
|
TypeInferUnknownNever.index_on_never
|
||||||
|
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never
|
||||||
|
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never
|
||||||
|
TypeInferUnknownNever.length_of_never
|
||||||
|
TypeInferUnknownNever.math_operators_and_never
|
||||||
|
TypeInferUnknownNever.never_is_reflexive
|
||||||
|
TypeInferUnknownNever.never_subtype_and_string_supertype
|
||||||
|
TypeInferUnknownNever.string_subtype_and_unknown_supertype
|
||||||
|
TypeInferUnknownNever.table_with_prop_of_type_never_is_also_reflexive
|
||||||
|
TypeInferUnknownNever.table_with_prop_of_type_never_is_uninhabitable
|
||||||
|
TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable
|
||||||
|
TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable2
|
||||||
|
TypeInferUnknownNever.unary_minus_of_never
|
||||||
|
TypeInferUnknownNever.unknown_is_reflexive
|
||||||
|
TypePackTests.cyclic_type_packs
|
||||||
|
TypePackTests.higher_order_function
|
||||||
|
TypePackTests.multiple_varargs_inference_are_not_confused
|
||||||
|
TypePackTests.no_return_size_should_be_zero
|
||||||
|
TypePackTests.pack_tail_unification_check
|
||||||
|
TypePackTests.parenthesized_varargs_returns_any
|
||||||
|
TypePackTests.self_and_varargs_should_work
|
||||||
|
TypePackTests.type_alias_backwards_compatible
|
||||||
|
TypePackTests.type_alias_default_export
|
||||||
|
TypePackTests.type_alias_default_mixed_self
|
||||||
|
TypePackTests.type_alias_default_type_chained
|
||||||
|
TypePackTests.type_alias_default_type_errors
|
||||||
|
TypePackTests.type_alias_default_type_explicit
|
||||||
|
TypePackTests.type_alias_default_type_pack_explicit
|
||||||
|
TypePackTests.type_alias_default_type_pack_self_chained_tp
|
||||||
|
TypePackTests.type_alias_default_type_pack_self_tp
|
||||||
|
TypePackTests.type_alias_default_type_pack_self_ty
|
||||||
|
TypePackTests.type_alias_default_type_self
|
||||||
|
TypePackTests.type_alias_default_type_skip_brackets
|
||||||
|
TypePackTests.type_alias_defaults_confusing_types
|
||||||
|
TypePackTests.type_alias_defaults_recursive_type
|
||||||
|
TypePackTests.type_alias_type_pack_explicit
|
||||||
|
TypePackTests.type_alias_type_pack_explicit_multi
|
||||||
|
TypePackTests.type_alias_type_pack_explicit_multi_tostring
|
||||||
|
TypePackTests.type_alias_type_pack_multi
|
||||||
|
TypePackTests.type_alias_type_pack_variadic
|
||||||
|
TypePackTests.type_alias_type_packs
|
||||||
|
TypePackTests.type_alias_type_packs_errors
|
||||||
|
TypePackTests.type_alias_type_packs_import
|
||||||
|
TypePackTests.type_alias_type_packs_nested
|
||||||
|
TypePackTests.type_pack_hidden_free_tail_infinite_growth
|
||||||
|
TypePackTests.type_pack_type_parameters
|
||||||
|
TypePackTests.varargs_inference_through_multiple_scopes
|
||||||
|
TypePackTests.variadic_argument_tail
|
||||||
|
TypePackTests.variadic_pack_syntax
|
||||||
|
TypePackTests.variadic_packs
|
||||||
|
TypeSingletons.bool_singleton_subtype
|
||||||
|
TypeSingletons.bool_singletons
|
||||||
|
TypeSingletons.bool_singletons_mismatch
|
||||||
|
TypeSingletons.enums_using_singletons
|
||||||
|
TypeSingletons.enums_using_singletons_mismatch
|
||||||
|
TypeSingletons.enums_using_singletons_subtyping
|
||||||
|
TypeSingletons.error_detailed_tagged_union_mismatch_bool
|
||||||
|
TypeSingletons.error_detailed_tagged_union_mismatch_string
|
||||||
|
TypeSingletons.function_call_with_singletons_mismatch
|
||||||
|
TypeSingletons.if_then_else_expression_singleton_options
|
||||||
|
TypeSingletons.indexing_on_string_singletons
|
||||||
|
TypeSingletons.indexing_on_union_of_string_singletons
|
||||||
|
TypeSingletons.no_widening_from_callsites
|
||||||
|
TypeSingletons.overloaded_function_call_with_singletons
|
||||||
|
TypeSingletons.overloaded_function_call_with_singletons_mismatch
|
||||||
|
TypeSingletons.return_type_of_f_is_not_widened
|
||||||
|
TypeSingletons.string_singleton_subtype
|
||||||
|
TypeSingletons.string_singletons
|
||||||
|
TypeSingletons.string_singletons_escape_chars
|
||||||
|
TypeSingletons.string_singletons_mismatch
|
||||||
|
TypeSingletons.table_insert_with_a_singleton_argument
|
||||||
|
TypeSingletons.table_properties_alias_or_parens_is_indexer
|
||||||
|
TypeSingletons.table_properties_singleton_strings
|
||||||
|
TypeSingletons.table_properties_singleton_strings_mismatch
|
||||||
|
TypeSingletons.table_properties_type_error_escapes
|
||||||
|
TypeSingletons.tagged_unions_immutable_tag
|
||||||
|
TypeSingletons.tagged_unions_using_singletons
|
||||||
|
TypeSingletons.tagged_unions_using_singletons_mismatch
|
||||||
|
TypeSingletons.taking_the_length_of_string_singleton
|
||||||
|
TypeSingletons.taking_the_length_of_union_of_string_singleton
|
||||||
|
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
|
||||||
|
TypeSingletons.widening_happens_almost_everywhere
|
||||||
|
TypeSingletons.widening_happens_almost_everywhere_except_for_tables
|
||||||
|
TypeVarTests.visit_once
|
||||||
|
UnionTypes.error_detailed_optional
|
||||||
|
UnionTypes.error_detailed_union_all
|
||||||
|
UnionTypes.error_detailed_union_part
|
||||||
|
UnionTypes.error_takes_optional_arguments
|
||||||
|
UnionTypes.index_on_a_union_type_with_missing_property
|
||||||
|
UnionTypes.index_on_a_union_type_with_mixed_types
|
||||||
|
UnionTypes.index_on_a_union_type_with_one_optional_property
|
||||||
|
UnionTypes.index_on_a_union_type_with_one_property_of_type_any
|
||||||
|
UnionTypes.index_on_a_union_type_with_property_guaranteed_to_exist
|
||||||
|
UnionTypes.index_on_a_union_type_works_at_arbitrary_depth
|
||||||
|
UnionTypes.optional_arguments
|
||||||
|
UnionTypes.optional_assignment_errors
|
||||||
|
UnionTypes.optional_call_error
|
||||||
|
UnionTypes.optional_field_access_error
|
||||||
|
UnionTypes.optional_index_error
|
||||||
|
UnionTypes.optional_length_error
|
||||||
|
UnionTypes.optional_missing_key_error_details
|
||||||
|
UnionTypes.optional_union_follow
|
||||||
|
UnionTypes.optional_union_functions
|
||||||
|
UnionTypes.optional_union_members
|
||||||
|
UnionTypes.optional_union_methods
|
||||||
|
UnionTypes.return_types_can_be_disjoint
|
||||||
|
UnionTypes.table_union_write_indirect
|
||||||
|
UnionTypes.unify_sealed_table_union_check
|
||||||
|
UnionTypes.unify_unsealed_table_union_check
|
||||||
|
UnionTypes.union_equality_comparisons
|
||||||
|
|
|
@ -14,6 +14,11 @@ def loadFailList():
|
||||||
with open(FAIL_LIST_PATH) as f:
|
with open(FAIL_LIST_PATH) as f:
|
||||||
return set(map(str.strip, f.readlines()))
|
return set(map(str.strip, f.readlines()))
|
||||||
|
|
||||||
|
def safeParseInt(i, default=0):
|
||||||
|
try:
|
||||||
|
return int(i)
|
||||||
|
except ValueError:
|
||||||
|
return default
|
||||||
|
|
||||||
class Handler(x.ContentHandler):
|
class Handler(x.ContentHandler):
|
||||||
def __init__(self, failList):
|
def __init__(self, failList):
|
||||||
|
@ -22,6 +27,8 @@ class Handler(x.ContentHandler):
|
||||||
|
|
||||||
self.results = {} # {DottedName: TrueIfTheTestPassed}
|
self.results = {} # {DottedName: TrueIfTheTestPassed}
|
||||||
|
|
||||||
|
self.numSkippedTests = 0
|
||||||
|
|
||||||
def startElement(self, name, attrs):
|
def startElement(self, name, attrs):
|
||||||
if name == "TestSuite":
|
if name == "TestSuite":
|
||||||
self.currentTest.append(attrs["name"])
|
self.currentTest.append(attrs["name"])
|
||||||
|
@ -30,10 +37,7 @@ class Handler(x.ContentHandler):
|
||||||
|
|
||||||
elif name == "OverallResultsAsserts":
|
elif name == "OverallResultsAsserts":
|
||||||
if self.currentTest:
|
if self.currentTest:
|
||||||
try:
|
failed = 0 != safeParseInt(attrs["failures"])
|
||||||
failed = 0 != int(attrs["failures"])
|
|
||||||
except ValueError:
|
|
||||||
failed = False
|
|
||||||
|
|
||||||
dottedName = ".".join(self.currentTest)
|
dottedName = ".".join(self.currentTest)
|
||||||
shouldFail = dottedName in self.failList
|
shouldFail = dottedName in self.failList
|
||||||
|
@ -45,6 +49,9 @@ class Handler(x.ContentHandler):
|
||||||
|
|
||||||
self.results[dottedName] = not failed
|
self.results[dottedName] = not failed
|
||||||
|
|
||||||
|
elif name == 'OverallResultsTestCases':
|
||||||
|
self.numSkippedTests = safeParseInt(attrs.get("skipped", 0))
|
||||||
|
|
||||||
def endElement(self, name):
|
def endElement(self, name):
|
||||||
if name == "TestCase":
|
if name == "TestCase":
|
||||||
self.currentTest.pop()
|
self.currentTest.pop()
|
||||||
|
@ -111,6 +118,10 @@ def main():
|
||||||
print(name, file=f)
|
print(name, file=f)
|
||||||
print("Updated faillist.txt")
|
print("Updated faillist.txt")
|
||||||
|
|
||||||
|
if handler.numSkippedTests > 0:
|
||||||
|
print('{} test(s) were skipped! That probably means that a test segfaulted!'.format(handler.numSkippedTests), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
sys.exit(
|
sys.exit(
|
||||||
0
|
0
|
||||||
if all(
|
if all(
|
||||||
|
|
Loading…
Add table
Reference in a new issue