Sync to upstream/release/538 (#616)

This commit is contained in:
Arseny Kapoulkine 2022-07-28 21:24:07 -07:00 committed by GitHub
parent cb16555608
commit d3b566c258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1445 additions and 754 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@
/default.prof* /default.prof*
/fuzz-* /fuzz-*
/luau /luau
__pycache__

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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,22 +277,21 @@ 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)) if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
return calledWithSelf; 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}; autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry};
else else

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
} }

View File

@ -100,30 +100,21 @@ 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; moduleScope->returnType = returnType;
if (varargPack) if (varargPack)
{ {
varargPack = clone(*varargPack, interfaceTypes, cloneState); varargPack = clone(*varargPack, interfaceTypes, cloneState);
moduleScope->varargPack = varargPack; 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

View File

@ -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);

View File

@ -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;

View File

@ -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,6 +278,9 @@ struct TypeVarStringifier
if (tv->ty.valueless_by_exception()) if (tv->ty.valueless_by_exception())
{ {
state.result.error = true; state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS BY EXCEPTION *");
else
state.emit("< VALUELESS BY EXCEPTION >"); state.emit("< VALUELESS BY EXCEPTION >");
return; return;
} }
@ -453,6 +457,9 @@ struct TypeVarStringifier
if (state.hasSeen(&ftv)) if (state.hasSeen(&ftv))
{ {
state.result.cycle = true; state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>"); state.emit("<CYCLE>");
return; return;
} }
@ -561,6 +568,9 @@ struct TypeVarStringifier
if (state.hasSeen(&ttv)) if (state.hasSeen(&ttv))
{ {
state.result.cycle = true; state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>"); state.emit("<CYCLE>");
return; return;
} }
@ -691,6 +701,9 @@ struct TypeVarStringifier
if (state.hasSeen(&uv)) if (state.hasSeen(&uv))
{ {
state.result.cycle = true; state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>"); state.emit("<CYCLE>");
return; return;
} }
@ -758,6 +771,9 @@ struct TypeVarStringifier
if (state.hasSeen(&uv)) if (state.hasSeen(&uv))
{ {
state.result.cycle = true; state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>"); state.emit("<CYCLE>");
return; return;
} }
@ -803,6 +819,9 @@ struct TypeVarStringifier
void operator()(TypeId, const ErrorTypeVar& tv) void operator()(TypeId, const ErrorTypeVar& tv)
{ {
state.result.error = true; state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*"); state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
} }
@ -857,6 +876,9 @@ struct TypePackStringifier
if (tp->ty.valueless_by_exception()) if (tp->ty.valueless_by_exception())
{ {
state.result.error = true; state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS TP BY EXCEPTION *");
else
state.emit("< VALUELESS TP BY EXCEPTION >"); state.emit("< VALUELESS TP BY EXCEPTION >");
return; return;
} }
@ -881,6 +903,9 @@ struct TypePackStringifier
if (state.hasSeen(&tp)) if (state.hasSeen(&tp))
{ {
state.result.cycle = true; state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLETP*");
else
state.emit("<CYCLETP>"); state.emit("<CYCLETP>");
return; return;
} }
@ -926,6 +951,9 @@ struct TypePackStringifier
void operator()(TypePackId, const Unifiable::Error& error) void operator()(TypePackId, const Unifiable::Error& error)
{ {
state.result.error = true; state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*"); state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
} }
@ -933,7 +961,12 @@ struct TypePackStringifier
{ {
state.emit("..."); state.emit("...");
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden) if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
{
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*hidden*");
else
state.emit("<hidden>"); state.emit("<hidden>");
}
stringify(pack.ty); stringify(pack.ty);
} }
@ -1128,6 +1161,10 @@ 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;
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>"; result.name += "... <TRUNCATED>";
} }
@ -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)
{
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>"; result.name += "... <TRUNCATED>";
}
return result; return result;
} }

View File

@ -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

View File

@ -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))
{ {

View File

@ -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)

View File

@ -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;
@ -2919,8 +2917,6 @@ 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; Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
@ -2951,8 +2947,5 @@ void Parser::nextLexeme()
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type; type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
} }
} }
else
lexer.next();
}
} // namespace Luau } // namespace Luau

View File

@ -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);

View File

@ -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,28 +305,11 @@ 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)
{
if (!isExprMultRet(node))
{ {
compileExprTemp(node, target); compileExprTemp(node, target);
return false; 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...
// This is a crude hack but it's necessary for correctness :( // This is a crude hack but it's necessary for correctness :(
@ -3436,31 +3414,8 @@ 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)

View File

@ -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;

View File

@ -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

View File

@ -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; \

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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,6 +664,10 @@ 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);
}
TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
{ {
if (ttisnil(key)) if (ttisnil(key))
luaG_runerror(L, "table index is nil"); luaG_runerror(L, "table index is nil");
@ -676,7 +677,6 @@ TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
luaG_runerror(L, "table index contains NaN"); luaG_runerror(L, "table index contains NaN");
return newkey(L, t, key); 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)
{ {

View File

@ -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;

View File

@ -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();
} }

View File

@ -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,6 +144,30 @@ 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 (FFlag::LuauBetterNewindex)
{
const TValue* oldval = luaH_get(h, key);
/* 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);
/* 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);
L->cachedslot = gval2slot(h, newval); /* remember slot to accelerate future lookups */
setobj2t(L, newval, val);
luaC_barriert(L, h, val);
return;
}
/* fallthrough to metamethod */
}
else
{
if (h->readonly) if (h->readonly)
luaG_readonlyerror(L); luaG_readonlyerror(L);
@ -158,8 +184,10 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
} }
/* 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);

View File

@ -2845,7 +2845,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
@ -2883,9 +2883,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 = {}
@ -2901,7 +2917,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
@ -2916,7 +2932,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 = {}
@ -2931,9 +2947,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"
@ -2952,7 +2985,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"
@ -2969,7 +3002,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
@ -3000,7 +3033,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
@ -3012,8 +3045,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")

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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();

View File

@ -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")

View File

@ -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,9 +268,17 @@ 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), "(() -> ()) -> () -> ()");
if (FFlag::LuauSpecialTypesAsterisked)
{
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("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
CHECK_EQ(toString(requireType("f3"), 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,10 +295,19 @@ 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), "(() -> ()) -> () -> ()");
if (FFlag::LuauSpecialTypesAsterisked)
{
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("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
CHECK_EQ(toString(requireType("f3"), 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")
{ {
TableTypeVar ttv{TableState::Sealed, TypeLevel{}}; TableTypeVar ttv{TableState::Sealed, TypeLevel{}};
@ -497,6 +515,9 @@ local function target(callback: nil) return callback(4, "hello") end
)"); )");
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(nil) -> (*error-type*)", toString(requireType("target")));
else
CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target"))); CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target")));
} }

View File

@ -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,6 +96,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error")
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a"))); CHECK_EQ("<error-type>", toString(requireType("a")));
} }
@ -110,6 +115,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a"))); CHECK_EQ("<error-type>", toString(requireType("a")));
} }
@ -225,6 +233,9 @@ TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error")
CHECK_EQ("unknown", err->name); CHECK_EQ("unknown", err->name);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a"))); CHECK_EQ("<error-type>", toString(requireType("a")));
} }
@ -234,6 +245,9 @@ TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
local a = Utility.Create "Foo" {} local a = Utility.Create "Foo" {}
)"); )");
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a"))); CHECK_EQ("<error-type>", toString(requireType("a")));
} }

View File

@ -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,6 +953,9 @@ 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")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("d")));
else
CHECK_EQ("<error-type>", toString(requireType("d"))); CHECK_EQ("<error-type>", toString(requireType("d")));
} }

View File

@ -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,12 +908,18 @@ 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));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(string, *error-type*) -> number", toString(tm1->givenType));
else
CHECK_EQ("(string, <error-type>) -> number", toString(tm1->givenType)); 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));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(string, *error-type*) -> number", toString(tm2->givenType));
else
CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType)); CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType));
} }
@ -1526,12 +1533,23 @@ function t:b() return 2 end -- not OK
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSpecialTypesAsterisked)
{
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]));
}
else
{
CHECK_EQ(R"(Type '(<error-type>) -> number' could not be converted into '() -> number' 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]));
} }
}
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -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,6 +1004,9 @@ 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);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(t0->type));
else
CHECK_EQ("<error-type>", toString(t0->type)); 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) {

View File

@ -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,6 +144,9 @@ 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");
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(p));
else
CHECK_EQ("<error-type>", toString(p)); CHECK_EQ("<error-type>", toString(p));
} }

View File

@ -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,6 +145,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export")
auto hootyType = requireType(bModule, "Hooty"); auto hootyType = requireType(bModule, "Hooty");
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(hootyType));
else
CHECK_EQ("<error-type>", toString(hootyType)); CHECK_EQ("<error-type>", toString(hootyType));
} }
@ -244,6 +249,10 @@ 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");
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(*oty));
else
CHECK_EQ("<error-type>", toString(*oty)); CHECK_EQ("<error-type>", toString(*oty));
} }

View File

@ -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 = {

View File

@ -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,6 +50,9 @@ TEST_CASE_FIXTURE(Fixture, "string_index")
REQUIRE(nat); REQUIRE(nat);
CHECK_EQ("string", toString(nat->ty)); CHECK_EQ("string", toString(nat->ty));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("t")));
else
CHECK_EQ("<error-type>", toString(requireType("t"))); CHECK_EQ("<error-type>", toString(requireType("t")));
} }

View File

@ -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,6 +527,9 @@ TEST_CASE_FIXTURE(Fixture, "type_narrow_to_vector")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireTypeAtPosition({3, 28})));
else
CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28}))); CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28})));
} }

View File

@ -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;
@ -236,12 +237,23 @@ 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)
{
if (FFlag::LuauSpecialTypesAsterisked)
{
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")));
}
else
{ {
CHECK_EQ("<error-type>", toString(requireType("c"))); CHECK_EQ("<error-type>", toString(requireType("c")));
CHECK_EQ("<error-type>", toString(requireType("d"))); CHECK_EQ("<error-type>", toString(requireType("d")));
CHECK_EQ("<error-type>", toString(requireType("e"))); CHECK_EQ("<error-type>", toString(requireType("e")));
CHECK_EQ("<error-type>", toString(requireType("f"))); CHECK_EQ("<error-type>", toString(requireType("f")));
} }
}
} }
TEST_CASE_FIXTURE(Fixture, "should_be_able_to_infer_this_without_stack_overflowing") TEST_CASE_FIXTURE(Fixture, "should_be_able_to_infer_this_without_stack_overflowing")
@ -650,6 +662,10 @@ 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);
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(t0->type));
else
CHECK_EQ("<error-type>", toString(t0->type)); 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) {

View File

@ -9,6 +9,8 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
struct TryUnifyFixture : Fixture struct TryUnifyFixture : Fixture
{ {
TypeArena arena; TypeArena arena;
@ -121,6 +123,9 @@ 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")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("b")));
else
CHECK_EQ("<error-type>", toString(requireType("b"))); CHECK_EQ("<error-type>", toString(requireType("b")));
} }
@ -136,6 +141,9 @@ 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")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("b")));
else
CHECK_EQ("<error-type>", toString(requireType("b"))); CHECK_EQ("<error-type>", toString(requireType("b")));
CHECK_EQ("number", toString(requireType("c"))); CHECK_EQ("number", toString(requireType("c")));
} }

View File

@ -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,6 +200,9 @@ 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");
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("r")));
else
CHECK_EQ("<error-type>", toString(requireType("r"))); CHECK_EQ("<error-type>", toString(requireType("r")));
} }

View File

@ -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();
} }

View File

@ -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)")

View File

@ -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'

View File

@ -298,5 +298,3 @@ int main(int argc, char** argv)
} }
return result; return result;
} }

View File

@ -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

View File

@ -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(