Merge branch 'master' of https://github.com/Roblox/luau into autocomplete-context

This commit is contained in:
JohnnyMorganz 2022-07-30 16:37:45 +01:00
commit 33c3843c78
68 changed files with 1598 additions and 757 deletions

1
.gitignore vendored
View file

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

View file

@ -12,7 +12,8 @@
namespace Luau
{
struct Scope2;
struct Scope;
struct TypeVar;
using TypeId = const TypeVar*;
@ -38,7 +39,7 @@ struct GeneralizationConstraint
{
TypeId generalizedType;
TypeId sourceType;
Scope2* scope;
Scope* scope;
};
// subType ~ inst superType

View file

@ -17,21 +17,22 @@
namespace Luau
{
struct Scope2;
struct Scope;
using ScopePtr = std::shared_ptr<Scope>;
struct ConstraintGraphBuilder
{
// 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
// define the scope hierarchy.
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scopes;
std::vector<std::pair<Location, ScopePtr>> scopes;
ModuleName moduleName;
SingletonTypes& singletonTypes;
const NotNull<TypeArena> arena;
// The root scope of the module we're generating constraints for.
// This is null when the CGB is initially constructed.
Scope2* rootScope;
Scope* rootScope;
// A mapping of AST node to TypeId.
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
// A mapping of AST node to TypePackId.
@ -50,42 +51,42 @@ struct ConstraintGraphBuilder
// Occasionally constraint generation needs to produce an 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.
* @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.
* @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.
* @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.
*/
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.
* @param scope the scope to add the constraint to.
* @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.
* @param scope the scope to add the constraint to. Must not be null.
* @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
@ -94,23 +95,23 @@ struct ConstraintGraphBuilder
*/
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(NotNull<Scope2> scope, AstStatBlock* block);
void visit(NotNull<Scope2> scope, AstStatLocal* local);
void visit(NotNull<Scope2> scope, AstStatFor* for_);
void visit(NotNull<Scope2> scope, AstStatLocalFunction* function);
void visit(NotNull<Scope2> scope, AstStatFunction* function);
void visit(NotNull<Scope2> scope, AstStatReturn* ret);
void visit(NotNull<Scope2> scope, AstStatAssign* assign);
void visit(NotNull<Scope2> scope, AstStatIf* ifStatement);
void visit(NotNull<Scope2> scope, AstStatTypeAlias* alias);
void visit(const ScopePtr& scope, AstStat* stat);
void visit(const ScopePtr& scope, AstStatBlock* block);
void visit(const ScopePtr& scope, AstStatLocal* local);
void visit(const ScopePtr& scope, AstStatFor* for_);
void visit(const ScopePtr& scope, AstStatLocalFunction* function);
void visit(const ScopePtr& scope, AstStatFunction* function);
void visit(const ScopePtr& scope, AstStatReturn* ret);
void visit(const ScopePtr& scope, AstStatAssign* assign);
void visit(const ScopePtr& scope, AstStatIf* ifStatement);
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(NotNull<Scope2> scope, AstExpr* expr);
TypePackId checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs);
TypePackId checkPack(const ScopePtr& scope, AstExpr* expr);
/**
* Checks an expression that is expected to evaluate to one type.
@ -118,13 +119,13 @@ struct ConstraintGraphBuilder
* @param expr the expression to check.
* @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 check(NotNull<Scope2> scope, AstExprIndexName* indexName);
TypeId check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr);
TypeId check(NotNull<Scope2> scope, AstExprUnary* unary);
TypeId check(NotNull<Scope2> scope, AstExprBinary* binary);
TypeId checkExprTable(const ScopePtr& scope, AstExprTable* expr);
TypeId check(const ScopePtr& scope, AstExprIndexName* indexName);
TypeId check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
TypeId check(const ScopePtr& scope, AstExprUnary* unary);
TypeId check(const ScopePtr& scope, AstExprBinary* binary);
struct FunctionSignature
{
@ -133,20 +134,20 @@ struct ConstraintGraphBuilder
// The scope that encompasses the function's signature. May be nullptr
// if there was no need for a signature scope (the function has no
// generics).
Scope2* signatureScope;
ScopePtr signatureScope;
// The scope that encompasses the function's body. Is a child scope of
// 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.
* @param scope the interior scope of the body of the function.
* @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.
@ -154,7 +155,7 @@ struct ConstraintGraphBuilder
* @param ty the AST annotation to resolve.
* @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.
@ -162,14 +163,14 @@ struct ConstraintGraphBuilder
* @param tp the AST annotation to resolve.
* @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, GenericTypePackDefinition>> createGenericPacks(NotNull<Scope2> scope, AstArray<AstGenericTypePack> packs);
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics);
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 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
* 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.
* 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

View file

@ -25,7 +25,7 @@ struct ConstraintSolver
// is important to not add elements to this vector, lest the underlying
// storage that we retain pointers to be mutated underneath us.
const std::vector<NotNull<Constraint>> constraints;
NotNull<Scope2> rootScope;
NotNull<Scope> rootScope;
// This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance.
@ -40,7 +40,7 @@ struct ConstraintSolver
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
@ -121,6 +121,6 @@ private:
void unblock_(BlockedConstraintId progressed);
};
void dump(NotNull<Scope2> rootScope, struct ToStringOptions& opts);
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
} // namespace Luau

View file

@ -15,8 +15,8 @@ namespace Luau
struct ConstraintSolverLogger
{
std::string compileOutput();
void captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void prepareStepSnapshot(const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void prepareStepSnapshot(const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void commitPreparedStepSnapshot();
private:

View file

@ -152,7 +152,7 @@ struct Frontend
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
NotNull<Scope2> getGlobalScope2();
NotNull<Scope> getGlobalScope();
private:
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, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
std::unique_ptr<Scope2> globalScope2;
std::unique_ptr<Scope> globalScope;
public:
FileResolver* fileResolver;

View file

@ -69,7 +69,6 @@ struct Module
std::shared_ptr<AstNameTable> names;
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*, TypePackId> astTypePacks{nullptr};
@ -86,7 +85,6 @@ struct Module
bool timeout = false;
ScopePtr getModuleScope() const;
Scope2* getModuleScope2() const;
// 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.

View file

@ -7,9 +7,9 @@ namespace Luau
{
struct TypeArena;
struct Scope2;
struct Scope;
void quantify(TypeId ty, TypeLevel level);
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope);
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope);
} // 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.
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<Name, TypeId> typeBindings;
std::unordered_map<Name, TypePackId> typePackBindings;
TypePackId returnType;
bool breakOk = false;
std::optional<TypePackId> varargPack;
// All constraints belonging to this scope.
std::vector<ConstraintPtr> constraints;
TypeLevel level;
@ -45,7 +51,9 @@ struct Scope
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> lookupImportedType(const Name& moduleAlias, const Name& name);
@ -66,24 +74,4 @@ struct Scope
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

View file

@ -24,7 +24,7 @@ namespace Luau
{
struct TypeArena;
struct Scope2;
struct Scope;
/**
* There are three kinds of type variables:
@ -143,7 +143,7 @@ struct ConstrainedTypeVar
std::vector<TypeId> parts;
TypeLevel level;
Scope2* scope = nullptr;
Scope* scope = nullptr;
};
// 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);
TypeLevel level;
Scope2* scope = nullptr;
Scope* scope = nullptr;
/// These should all be generic
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
@ -344,7 +344,7 @@ struct TableTypeVar
TableState state = TableState::Unsealed;
TypeLevel level;
Scope2* scope = nullptr;
Scope* scope = nullptr;
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

View file

@ -8,7 +8,7 @@
namespace Luau
{
struct Scope2;
struct Scope;
/**
* 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
{
explicit Free(TypeLevel level);
explicit Free(Scope2* scope);
explicit Free(Scope* scope);
int index;
TypeLevel level;
Scope2* scope = nullptr;
Scope* scope = nullptr;
// True if this free type variable is part of a mutually
// recursive type alias whose definitions haven't been
// resolved yet.
@ -115,13 +115,13 @@ struct Generic
Generic();
explicit Generic(TypeLevel level);
explicit Generic(const Name& name);
explicit Generic(Scope2* scope);
explicit Generic(Scope* scope);
Generic(TypeLevel level, const Name& name);
Generic(Scope2* scope, const Name& name);
Generic(Scope* scope, const Name& name);
int index;
TypeLevel level;
Scope2* scope = nullptr;
Scope* scope = nullptr;
Name name;
bool explicitName = false;

View file

@ -12,7 +12,7 @@
#include <unordered_set>
#include <utility>
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix2)
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
static const std::unordered_set<std::string> kStatementStartingKeywords = {
"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);
auto canUnify = [&typeArena](TypeId subTy, TypeId superTy) {
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
InternalErrorReporter iceReporter;
UnifierSharedState unifierState(&iceReporter);
@ -168,7 +168,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
TypeId expectedType = follow(*typeAtPosition);
auto checkFunctionType = [typeArena, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
if (FFlag::LuauSelfCallAutocompleteFix2)
if (FFlag::LuauSelfCallAutocompleteFix3)
{
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
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;
else
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,
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
{
if (FFlag::LuauSelfCallAutocompleteFix2)
if (FFlag::LuauSelfCallAutocompleteFix3)
rootTy = follow(rootTy);
ty = follow(ty);
@ -236,7 +236,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
seen.insert(ty);
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
if (indexType == PropIndexType::Key)
return false;
@ -269,7 +269,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
}
};
auto isWrongIndexer = [typeArena, rootTy, indexType](Luau::TypeId type) {
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix2);
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
if (indexType == PropIndexType::Key)
return false;
@ -277,21 +277,20 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
bool calledWithSelf = indexType == PropIndexType::Colon;
auto isCompatibleCall = [typeArena, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
if (get<ClassTypeVar>(rootTy))
{
// Calls on classes require strict match between how function is declared and how it's called
return calledWithSelf == ftv->hasSelf;
}
// Strong match with definition is a success
if (calledWithSelf == ftv->hasSelf)
return true;
// If a call is made with ':', it is invalid if a function has incompatible first argument or no arguments at all
// If a call is made with '.', but it was declared with 'self', it is considered invalid if first argument is compatible
if (calledWithSelf || ftv->hasSelf)
// Calls on classes require strict match between how function is declared and how it's called
if (get<ClassTypeVar>(rootTy))
return false;
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
{
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
{
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
return calledWithSelf;
}
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
return calledWithSelf;
}
return !calledWithSelf;
@ -333,7 +332,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryKind::Property,
type,
prop.deprecated,
FFlag::LuauSelfCallAutocompleteFix2 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
FFlag::LuauSelfCallAutocompleteFix3 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
typeCorrect,
containingClass,
&prop,
@ -376,7 +375,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
{
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
if (FFlag::LuauSelfCallAutocompleteFix2)
if (FFlag::LuauSelfCallAutocompleteFix3)
{
if (auto mtable = get<TableTypeVar>(mt->metatable))
fillMetatableProps(mtable);
@ -442,7 +441,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryMap inner;
std::unordered_set<TypeId> innerSeen;
if (!FFlag::LuauSelfCallAutocompleteFix2)
if (!FFlag::LuauSelfCallAutocompleteFix3)
innerSeen = seen;
if (isNil(*iter))
@ -468,7 +467,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
++iter;
}
}
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix2)
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix3)
{
if (pt->metatable)
{
@ -476,7 +475,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
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);
}
@ -1405,7 +1404,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
TypeId ty = follow(*it);
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
if (!FFlag::LuauSelfCallAutocompleteFix2 && isString(ty))
if (!FFlag::LuauSelfCallAutocompleteFix3 && isString(ty))
return {
autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry, AutocompleteContext::Property};
else

View file

@ -14,7 +14,7 @@ namespace Luau
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
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)
, singletonTypes(getSingletonTypes())
, arena(arena)
@ -25,36 +25,34 @@ ConstraintGraphBuilder::ConstraintGraphBuilder(
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)});
}
NotNull<Scope2> ConstraintGraphBuilder::childScope(Location location, NotNull<Scope2> parent)
ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent)
{
auto scope = std::make_unique<Scope2>();
NotNull<Scope2> borrow = NotNull(scope.get());
scopes.emplace_back(location, std::move(scope));
auto scope = std::make_shared<Scope>(parent);
scopes.emplace_back(location, scope);
borrow->parent = parent;
borrow->returnType = parent->returnType;
parent->children.push_back(borrow);
scope->returnType = parent->returnType;
parent->children.push_back(NotNull(scope.get()));
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)});
}
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));
}
@ -63,13 +61,13 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
{
LUAU_ASSERT(scopes.empty());
LUAU_ASSERT(rootScope == nullptr);
scopes.emplace_back(block->location, std::make_unique<Scope2>());
rootScope = scopes.back().second.get();
NotNull<Scope2> borrow = NotNull(rootScope);
ScopePtr scope = std::make_shared<Scope>(singletonTypes.anyTypePack);
rootScope = scope.get();
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.
rootScope->typeBindings["nil"] = singletonTypes.nilType;
@ -78,10 +76,10 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
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};
@ -95,7 +93,7 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope,
visit(scope, stat);
}
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
{
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
@ -123,22 +121,24 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
LUAU_ASSERT(0);
}
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
{
std::vector<TypeId> varTypes;
for (AstLocal* local : local->vars)
{
TypeId ty = freshType(scope);
Location location = local->location;
if (local->annotation)
{
location = local->annotation->location;
TypeId annotation = resolveType(scope, local->annotation);
addConstraint(scope, SubtypeConstraint{ty, annotation});
}
varTypes.push_back(ty);
scope->bindings[local] = ty;
scope->bindings[local] = Binding{ty, location};
}
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)
{
@ -184,24 +184,24 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFor* for_)
checkNumber(for_->to);
checkNumber(for_->step);
NotNull<Scope2> forScope = childScope(for_->location, scope);
forScope->bindings[for_->var] = singletonTypes.numberType;
ScopePtr forScope = childScope(for_->location, scope);
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
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());
for (const auto& c : scope->constraints)
constraint->dependencies.push_back(NotNull{c.get()});
for (NotNull<Scope2> childScope : scope->children)
for (NotNull<Scope> childScope : scope->children)
addConstraints(constraint, childScope);
}
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction* function)
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
{
// Local
// 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.
functionType = arena->addType(BlockedTypeVar{});
scope->bindings[function->name] = functionType;
scope->bindings[function->name] = Binding{functionType, function->name->location};
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);
std::unique_ptr<Constraint> c{
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
addConstraints(c.get(), sig.bodyScope);
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
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.
// With or without self
@ -247,9 +247,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
else
{
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>())
{
@ -262,9 +262,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
else
{
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>())
{
@ -291,21 +291,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
checkFunctionBody(sig.bodyScope, function->func);
std::unique_ptr<Constraint> c{
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
addConstraints(c.get(), sig.bodyScope);
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
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);
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
// 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);
}
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
{
TypePackId varPackId = checkExprList(scope, assign->vars);
TypePackId valuePack = checkPack(scope, assign->values);
@ -331,21 +331,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
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);
NotNull<Scope2> thenScope = childScope(ifStatement->thenbody->location, scope);
ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope);
visit(thenScope, ifStatement->thenbody);
if (ifStatement->elsebody)
{
NotNull<Scope2> elseScope = childScope(ifStatement->elsebody->location, scope);
ScopePtr elseScope = childScope(ifStatement->elsebody->location, scope);
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: Generic type aliases
@ -371,7 +371,7 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatTypeAlias* alia
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)
return arena->addTypePack({});
@ -392,7 +392,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<Ast
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({});
TypePack* resultPack = getMutable<TypePack>(result);
@ -413,7 +413,7 @@ TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const As
return result;
}
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* expr)
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
{
RecursionCounter counter{&recursionCount};
@ -468,7 +468,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* exp
return result;
}
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
{
RecursionCounter counter{&recursionCount};
@ -548,7 +548,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
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 result = freshType(scope);
@ -564,7 +564,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* in
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 indexType = check(scope, indexExpr->index);
@ -579,7 +579,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* in
return result;
}
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
{
TypeId operandType = check(scope, unary->expr);
@ -599,7 +599,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
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 rightType = check(scope, binary->right);
@ -624,7 +624,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binar
return nullptr;
}
TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTable* expr)
TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTable* expr)
{
TypeId ty = arena->addType(TableTypeVar{});
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
@ -674,10 +674,10 @@ TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTabl
return ty;
}
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn)
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn)
{
Scope2* signatureScope = nullptr;
Scope2* bodyScope = nullptr;
ScopePtr signatureScope = nullptr;
ScopePtr bodyScope = nullptr;
TypePackId returnType = nullptr;
std::vector<TypeId> genericTypes;
@ -690,18 +690,17 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
// generics properly.
if (hasGenerics)
{
NotNull signatureBorrow = childScope(fn->location, parent);
signatureScope = signatureBorrow.get();
signatureScope = childScope(fn->location, parent);
// We need to assign returnType before creating bodyScope so that the
// return type gets propogated to bodyScope.
returnType = freshTypePack(signatureBorrow);
returnType = freshTypePack(signatureScope);
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, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
// We do not support default values on function generics, so we only
// care about the types involved.
@ -719,11 +718,10 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
}
else
{
NotNull bodyBorrow = childScope(fn->body->location, parent);
bodyScope = bodyBorrow.get();
bodyScope = childScope(fn->body->location, parent);
returnType = freshTypePack(bodyBorrow);
bodyBorrow->returnType = returnType;
returnType = freshTypePack(bodyScope);
bodyScope->returnType = returnType;
// 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
@ -731,27 +729,24 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
signatureScope = bodyScope;
}
NotNull bodyBorrow = NotNull(bodyScope);
NotNull signatureBorrow = NotNull(signatureScope);
if (fn->returnAnnotation)
{
TypePackId annotatedRetType = resolveTypePack(signatureBorrow, *fn->returnAnnotation);
addConstraint(signatureBorrow, PackSubtypeConstraint{returnType, annotatedRetType});
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
addConstraint(signatureScope, PackSubtypeConstraint{returnType, annotatedRetType});
}
std::vector<TypeId> argTypes;
for (AstLocal* local : fn->args)
{
TypeId t = freshType(signatureBorrow);
TypeId t = freshType(signatureScope);
argTypes.push_back(t);
signatureScope->bindings[local] = t;
signatureScope->bindings[local] = Binding{t, local->location};
if (local->annotation)
{
TypeId argAnnotation = resolveType(signatureBorrow, local->annotation);
addConstraint(signatureBorrow, SubtypeConstraint{t, argAnnotation});
TypeId argAnnotation = resolveType(signatureScope, local->annotation);
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,
// don't report it.
/* 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);
@ -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;
@ -834,7 +829,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
{
// TODO: Recursion limit.
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
Scope2* signatureScope = nullptr;
ScopePtr signatureScope = nullptr;
std::vector<TypeId> genericTypes;
std::vector<TypePackId> genericTypePacks;
@ -843,22 +838,21 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
// for the generic bindings to live on.
if (hasGenerics)
{
NotNull<Scope2> signatureBorrow = childScope(fn->location, scope);
signatureScope = signatureBorrow.get();
signatureScope = childScope(fn->location, scope);
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
for (const auto& [name, g] : genericDefinitions)
{
genericTypes.push_back(g.ty);
signatureBorrow->typeBindings[name] = g.ty;
signatureScope->typeBindings[name] = g.ty;
}
for (const auto& [name, g] : genericPackDefinitions)
{
genericTypePacks.push_back(g.tp);
signatureBorrow->typePackBindings[name] = g.tp;
signatureScope->typePackBindings[name] = g.tp;
}
}
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
// the signature scope is the parent scope if we don't have
// generics.
signatureScope = scope.get();
signatureScope = scope;
}
NotNull<Scope2> signatureBorrow(signatureScope);
TypePackId argTypes = resolveTypePack(signatureBorrow, fn->argTypes);
TypePackId returnTypes = resolveTypePack(signatureBorrow, fn->returnTypes);
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes);
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes);
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
// how to quantify/instantiate it.
@ -950,7 +942,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
return result;
}
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp)
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp)
{
TypePackId result;
if (auto expl = tp->as<AstTypePackExplicit>())
@ -964,7 +956,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
}
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
{
@ -976,7 +968,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
return result;
}
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list)
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list)
{
std::vector<TypeId> head;
@ -994,12 +986,12 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const
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;
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;
if (generic.defaultValue)
@ -1015,12 +1007,12 @@ std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::crea
}
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;
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;
if (generic.defaultValue)
@ -1035,7 +1027,7 @@ std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::
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))
return *f;
@ -1061,10 +1053,10 @@ void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
struct GlobalPrepopulator : AstVisitor
{
const NotNull<Scope2> globalScope;
const NotNull<Scope> globalScope;
const NotNull<TypeArena> arena;
GlobalPrepopulator(NotNull<Scope2> globalScope, NotNull<TypeArena> arena)
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
: globalScope(globalScope)
, arena(arena)
{
@ -1073,29 +1065,29 @@ struct GlobalPrepopulator : AstVisitor
bool visit(AstStatFunction* function) override
{
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
globalScope->bindings[g->name] = arena->addType(BlockedTypeVar{});
globalScope->bindings[g->name] = Binding{arena->addType(BlockedTypeVar{})};
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);
}
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)
result.push_back(NotNull{c.get()});
for (NotNull<Scope2> child : scope->children)
for (NotNull<Scope> child : scope->children)
collectConstraints(result, child);
}
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope)
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
{
std::vector<NotNull<Constraint>> result;
collectConstraints(result, rootScope);

View file

@ -13,31 +13,31 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
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)
{
auto d = toStringDetailed(v, opts);
auto d = toStringDetailed(v.typeId, opts);
opts.nameMap = d.nameMap;
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);
}
static void dumpConstraints(NotNull<Scope2> scope, ToStringOptions& opts)
static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
{
for (const ConstraintPtr& c : scope->constraints)
{
printf("\t%s\n", toString(*c, opts).c_str());
}
for (NotNull<Scope2> child : scope->children)
for (NotNull<Scope> child : scope->children)
dumpConstraints(child, opts);
}
void dump(NotNull<Scope2> rootScope, ToStringOptions& opts)
void dump(NotNull<Scope> rootScope, ToStringOptions& opts)
{
printf("constraints:\n");
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)
, constraints(collectConstraints(rootScope))
, rootScope(rootScope)

View file

@ -5,12 +5,12 @@
namespace Luau
{
static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& opts)
static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opts)
{
std::string output = "{\"bindings\":{";
bool comma = false;
for (const auto& [name, type] : scope->bindings)
for (const auto& [name, binding] : scope->bindings)
{
if (comma)
output += ",";
@ -19,7 +19,7 @@ static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& op
output += name.c_str();
output += "\": \"";
ToStringResult result = toStringDetailed(type, opts);
ToStringResult result = toStringDetailed(binding.typeId, opts);
opts.nameMap = std::move(result.nameMap);
output += result.name;
output += "\"";
@ -30,7 +30,7 @@ static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& op
output += "},\"children\":[";
comma = false;
for (const Scope2* child : scope->children)
for (const Scope* child : scope->children)
{
if (comma)
output += ",";
@ -96,7 +96,7 @@ std::string ConstraintSolverLogger::compileOutput()
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\":";
@ -109,7 +109,7 @@ void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* rootScope, st
}
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);

View file

@ -2,7 +2,6 @@
#include "Luau/BuiltinDefinitions.h"
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAG(LuauCheckLenMT)
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 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 rawlen<K, V>(obj: {[K]: V} | string): number
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;
// TODO: move this into kBuiltinDefinitionLuaSrc
if (FFlag::LuauCheckLenMT)
result += "declare function rawlen<K, V>(obj: {[K]: V} | string): number\n";
if (FFlag::LuauUnknownAndNeverType)
result += "declare function error<T>(message: T, level: number?): never\n";
else

View file

@ -766,35 +766,35 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
}
NotNull<Scope2> Frontend::getGlobalScope2()
NotNull<Scope> Frontend::getGlobalScope()
{
if (!globalScope2)
if (!globalScope)
{
const SingletonTypes& singletonTypes = getSingletonTypes();
globalScope2 = std::make_unique<Scope2>();
globalScope2->typeBindings["nil"] = singletonTypes.nilType;
globalScope2->typeBindings["number"] = singletonTypes.numberType;
globalScope2->typeBindings["string"] = singletonTypes.stringType;
globalScope2->typeBindings["boolean"] = singletonTypes.booleanType;
globalScope2->typeBindings["thread"] = singletonTypes.threadType;
globalScope = std::make_unique<Scope>(singletonTypes.anyTypePack);
globalScope->typeBindings["nil"] = singletonTypes.nilType;
globalScope->typeBindings["number"] = singletonTypes.numberType;
globalScope->typeBindings["string"] = singletonTypes.stringType;
globalScope->typeBindings["boolean"] = singletonTypes.booleanType;
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 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);
result->errors = std::move(cgb.errors);
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope)};
cs.run();
result->scope2s = std::move(cgb.scopes);
result->scopes = std::move(cgb.scopes);
result->astTypes = std::move(cgb.astTypes);
result->astTypePacks = std::move(cgb.astTypePacks);
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);

View file

@ -103,8 +103,8 @@ struct AstJsonEncoder : public AstVisitor
void write(double d)
{
char b[256];
sprintf(b, "%.17g", d);
char b[32];
snprintf(b, sizeof(b), "%.17g", d);
writeRaw(b);
}

View file

@ -100,29 +100,20 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
CloneState cloneState;
ScopePtr moduleScope = FFlag::DebugLuauDeferredConstraintResolution ? nullptr : getModuleScope();
Scope2* moduleScope2 = FFlag::DebugLuauDeferredConstraintResolution ? getModuleScope2() : nullptr;
ScopePtr moduleScope = getModuleScope();
TypePackId returnType = FFlag::DebugLuauDeferredConstraintResolution ? moduleScope2->returnType : moduleScope->returnType;
TypePackId returnType = moduleScope->returnType;
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
std::unordered_map<Name, TypeFun>* exportedTypeBindings =
FFlag::DebugLuauDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
returnType = clone(returnType, interfaceTypes, cloneState);
if (moduleScope)
moduleScope->returnType = returnType;
if (varargPack)
{
moduleScope->returnType = returnType;
if (varargPack)
{
varargPack = clone(*varargPack, interfaceTypes, cloneState);
moduleScope->varargPack = varargPack;
}
}
else
{
LUAU_ASSERT(moduleScope2);
moduleScope2->returnType = returnType; // TODO varargPack
varargPack = clone(*varargPack, interfaceTypes, cloneState);
moduleScope->varargPack = varargPack;
}
ForceNormal forceNormal{&interfaceTypes};
@ -201,10 +192,4 @@ ScopePtr Module::getModuleScope() const
return scopes.front().second;
}
Scope2* Module::getModuleScope2() const
{
LUAU_ASSERT(!scope2s.empty());
return scope2s.front().second.get();
}
} // namespace Luau

View file

@ -16,13 +16,13 @@ namespace Luau
{
/// @return true if outer encloses inner
static bool subsumes(Scope2* outer, Scope2* inner)
static bool subsumes(Scope* outer, Scope* inner)
{
while (inner)
{
if (inner == outer)
return true;
inner = inner->parent;
inner = inner->parent.get();
}
return false;
@ -33,7 +33,7 @@ struct Quantifier final : TypeVarOnceVisitor
TypeLevel level;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
Scope2* scope = nullptr;
Scope* scope = nullptr;
bool seenGenericType = false;
bool seenMutableType = false;
@ -43,20 +43,20 @@ struct Quantifier final : TypeVarOnceVisitor
LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution);
}
explicit Quantifier(Scope2* scope)
explicit Quantifier(Scope* scope)
: scope(scope)
{
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
}
/// @return true if outer encloses inner
bool subsumes(Scope2* outer, Scope2* inner)
bool subsumes(Scope* outer, Scope* inner)
{
while (inner)
{
if (inner == outer)
return true;
inner = inner->parent;
inner = inner->parent.get();
}
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};
q.traverse(ty);
@ -240,11 +240,11 @@ void quantify(TypeId ty, Scope2* scope)
struct PureQuantifier : Substitution
{
Scope2* scope;
Scope* scope;
std::vector<TypeId> insertedGenerics;
std::vector<TypePackId> insertedGenericPacks;
PureQuantifier(TypeArena* arena, Scope2* scope)
PureQuantifier(TypeArena* arena, Scope* scope)
: Substitution(TxnLog::empty(), arena)
, 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};
std::optional<TypeId> result = quantifier.substitute(ty);

View file

@ -21,22 +21,6 @@ Scope::Scope(const ScopePtr& parent, int 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)
{
const Scope* scope = this;
@ -121,48 +105,48 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
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)
{
auto it = s->bindings.find(sym);
if (it != s->bindings.end())
return it->second;
return it->second.typeId;
if (s->parent)
s = s->parent;
s = s->parent.get();
else
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)
{
auto it = s->typeBindings.find(name);
if (it != s->typeBindings.end())
return it->second;
s = s->parent;
s = s->parent.get();
}
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)
{
auto it = s->typePackBindings.find(name);
if (it != s->typePackBindings.end())
return it->second;
s = s->parent;
s = s->parent.get();
}
return std::nullopt;

View file

@ -12,6 +12,7 @@
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauSpecialTypesAsterisked, false)
/*
* Prefix generic typenames with gen-
@ -277,7 +278,10 @@ struct TypeVarStringifier
if (tv->ty.valueless_by_exception())
{
state.result.error = true;
state.emit("< VALUELESS BY EXCEPTION >");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS BY EXCEPTION *");
else
state.emit("< VALUELESS BY EXCEPTION >");
return;
}
@ -453,7 +457,10 @@ struct TypeVarStringifier
if (state.hasSeen(&ftv))
{
state.result.cycle = true;
state.emit("<CYCLE>");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
return;
}
@ -561,7 +568,10 @@ struct TypeVarStringifier
if (state.hasSeen(&ttv))
{
state.result.cycle = true;
state.emit("<CYCLE>");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
return;
}
@ -691,7 +701,10 @@ struct TypeVarStringifier
if (state.hasSeen(&uv))
{
state.result.cycle = true;
state.emit("<CYCLE>");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
return;
}
@ -758,7 +771,10 @@ struct TypeVarStringifier
if (state.hasSeen(&uv))
{
state.result.cycle = true;
state.emit("<CYCLE>");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
return;
}
@ -803,7 +819,10 @@ struct TypeVarStringifier
void operator()(TypeId, const ErrorTypeVar& tv)
{
state.result.error = true;
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
}
void operator()(TypeId, const LazyTypeVar& ltv)
@ -857,7 +876,10 @@ struct TypePackStringifier
if (tp->ty.valueless_by_exception())
{
state.result.error = true;
state.emit("< VALUELESS TP BY EXCEPTION >");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS TP BY EXCEPTION *");
else
state.emit("< VALUELESS TP BY EXCEPTION >");
return;
}
@ -881,7 +903,10 @@ struct TypePackStringifier
if (state.hasSeen(&tp))
{
state.result.cycle = true;
state.emit("<CYCLETP>");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLETP*");
else
state.emit("<CYCLETP>");
return;
}
@ -926,14 +951,22 @@ struct TypePackStringifier
void operator()(TypePackId, const Unifiable::Error& error)
{
state.result.error = true;
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
}
void operator()(TypePackId, const VariadicTypePack& pack)
{
state.emit("...");
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
state.emit("<hidden>");
{
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*hidden*");
else
state.emit("<hidden>");
}
stringify(pack.ty);
}
@ -1128,7 +1161,11 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
{
result.truncated = true;
result.name += "... <TRUNCATED>";
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>";
}
return result;
@ -1199,7 +1236,12 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
}
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
result.name += "... <TRUNCATED>";
{
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>";
}
return result;
}

View file

@ -69,6 +69,9 @@ struct TypeChecker2 : public AstVisitor
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
{
if (exprs.size == 0)
return arena.addTypePack(TypePack{{}, std::nullopt});
std::vector<TypeId> head;
for (size_t i = 0; i < exprs.size - 1; ++i)
@ -80,14 +83,14 @@ struct TypeChecker2 : public AstVisitor
return arena.addTypePack(TypePack{head, tail});
}
Scope2* findInnermostScope(Location location)
Scope* findInnermostScope(Location location)
{
Scope2* bestScope = module->getModuleScope2();
Location bestLocation = module->scope2s[0].first;
Scope* bestScope = module->getModuleScope().get();
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.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
@ -181,7 +184,7 @@ struct TypeChecker2 : public AstVisitor
bool visit(AstStatReturn* ret) override
{
Scope2* scope = findInnermostScope(ret->location);
Scope* scope = findInnermostScope(ret->location);
TypePackId expectedRetType = scope->returnType;
TypeArena arena;
@ -359,7 +362,7 @@ struct TypeChecker2 : public AstVisitor
bool visit(AstTypeReference* ty) override
{
Scope2* scope = findInnermostScope(ty->location);
Scope* scope = findInnermostScope(ty->location);
// TODO: Imported types
// TODO: Generic types

View file

@ -35,7 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false)
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
@ -45,7 +45,6 @@ LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
LUAU_FASTFLAG(LuauQuantifyConstrained)
LUAU_FASTFLAGVARIABLE(LuauFalsyPredicateReturnsNilInstead, false)
LUAU_FASTFLAGVARIABLE(LuauCheckLenMT, false)
LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, 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->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
if (FFlag::LuauSelfCallAutocompleteFix2)
if (FFlag::LuauSelfCallAutocompleteFix3)
ftv->hasSelf = true;
}
}
@ -2465,7 +2464,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
DenseHashSet<TypeId> seen{nullptr};
if (FFlag::LuauCheckLenMT && typeCouldHaveMetatable(operandType))
if (typeCouldHaveMetatable(operandType))
{
if (auto fnt = findMetatableEntry(operandType, "__len", expr.location, /* addErrors= */ true))
{
@ -3640,6 +3639,9 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes});
}
}
if (!currentModule->astTypes.find(&function))
currentModule->astTypes[&function] = ty;
}
else
ice("Checking non functional type");

View file

@ -12,7 +12,7 @@ Free::Free(TypeLevel level)
{
}
Free::Free(Scope2* scope)
Free::Free(Scope* scope)
: scope(scope)
{
}
@ -39,7 +39,7 @@ Generic::Generic(const Name& name)
{
}
Generic::Generic(Scope2* scope)
Generic::Generic(Scope* scope)
: index(++nextIndex)
, 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)
, scope(scope)
, name(name)

View file

@ -24,8 +24,6 @@ bool lua_telemetry_parsed_named_non_function_type = false;
LUAU_FASTFLAGVARIABLE(LuauErrorParseIntegerIssues, 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_hex_integer = false;
bool lua_telemetry_parsed_double_prefix_hex_integer = false;
@ -2920,39 +2918,34 @@ AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const
void Parser::nextLexeme()
{
if (options.captureComments || FFlag::LuauAlwaysCaptureHotComments)
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
{
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
const Lexeme& lexeme = lexer.current();
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
if (options.captureComments)
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
// The parser will turn this into a proper syntax error.
if (lexeme.type == Lexeme::BrokenComment)
return;
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
{
const Lexeme& lexeme = lexer.current();
const char* text = lexeme.data;
if (options.captureComments)
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
unsigned int end = lexeme.length;
while (end > 0 && isSpace(text[end - 1]))
--end;
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
// The parser will turn this into a proper syntax error.
if (lexeme.type == Lexeme::BrokenComment)
return;
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
{
const char* text = lexeme.data;
unsigned int end = lexeme.length;
while (end > 0 && isSpace(text[end - 1]))
--end;
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
}
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
}
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
}
else
lexer.next();
}
} // 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
#include "Repl.h"
int main(int argc, char** argv)
{
return replMain(argc, argv);

View file

@ -25,8 +25,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false)
LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
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
bool isExprMultRet(AstExpr* node)
{
if (!FFlag::LuauCompileBetterMultret)
return node->is<AstExprCall>() || node->is<AstExprVarargs>();
AstExprCall* expr = node->as<AstExprCall>();
if (!expr)
return node->is<AstExprVarargs>();
@ -310,27 +305,10 @@ struct Compiler
if (AstExprCall* expr = node->as<AstExprCall>())
{
// 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);
return false;
}
}
else
{
AstExprFunction* func = getFunctionExpr(expr->func);
Function* fi = func ? functions.find(func) : nullptr;
if (fi && fi->returnsOne)
{
compileExprTemp(node, target);
return false;
}
}
compileExprTemp(node, target);
return false;
}
// We temporarily swap out regTop to have targetTop work correctly...
@ -3437,30 +3415,7 @@ struct Compiler
bool visit(AstStatReturn* stat) override
{
if (FFlag::LuauCompileBetterMultret)
{
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;
}
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
return false;
}
@ -3601,7 +3556,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
trackValues(compiler.globals, compiler.variables, root);
// 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;
if (options.optimizationLevel >= 1)

View file

@ -6,8 +6,6 @@
#include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauCompileModelBuiltins, false)
namespace Luau
{
namespace Compile
@ -155,7 +153,7 @@ struct CostVisitor : AstVisitor
{
// 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
bool builtin = FFlag::LuauCompileModelBuiltins && builtins.find(expr) != nullptr;
bool builtin = builtins.find(expr) != nullptr;
bool builtinShort = builtin && expr->args.size <= 2; // FASTCALL1/2
Cost cost = builtin ? 2 : 3;

View file

@ -267,6 +267,7 @@ if(TARGET Luau.UnitTest)
tests/Error.test.cpp
tests/Frontend.test.cpp
tests/JsonEncoder.test.cpp
tests/Lexer.test.cpp
tests/Linter.test.cpp
tests/LValue.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.
*/
LUAU_FASTFLAG(LuauLazyAtoms)
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"
"$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) \
if (FFlag::LuauLazyAtoms) \
{ \
if (ts->atom == ATOM_UNDEF) \
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)
{
char line[32];
#ifdef _MSC_VER
_itoa(ar.currentline, line, 10); // 5x faster than sprintf
#else
sprintf(line, "%d", ar.currentline);
#endif
char line[32]; // manual conversion for performance
char* lineend = line + sizeof(line);
char* lineptr = lineend;
for (unsigned int r = ar.currentline; r > 0; r /= 10)
*--lineptr = '0' + (r % 10);
luaL_addchar(&buf, ':');
luaL_addstring(&buf, line);
luaL_addlstring(&buf, lineptr, lineend - lineptr);
}
if (ar.name)

View file

@ -529,7 +529,7 @@ const char* lua_debugtrace(lua_State* L)
if (ar.currentline > 0)
{
char line[32];
sprintf(line, ":%d", ar.currentline);
snprintf(line, sizeof(line), ":%d", ar.currentline);
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)
{
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);

View file

@ -7,8 +7,6 @@
#include <string.h>
LUAU_FASTFLAGVARIABLE(LuauLazyAtoms, false)
unsigned int luaS_hash(const char* str, size_t len)
{
// 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;
memcpy(ts->data, str, l);
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;
h = lmod(h, tb->size);
ts->next = tb->hash[h]; /* chain new entry */
@ -165,9 +163,7 @@ TString* luaS_buffinish(lua_State* L, TString* ts)
ts->hash = h;
ts->data[ts->len] = '\0'; // ending 0
// Complete string object
ts->atom = FFlag::LuauLazyAtoms ? ATOM_UNDEF : L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1;
ts->atom = ATOM_UNDEF;
ts->next = tb->hash[bucket]; // chain new entry
tb->hash[bucket] = ts;

View file

@ -979,14 +979,14 @@ static int str_format(lua_State* L)
{
case 'c':
{
sprintf(buff, form, (int)luaL_checknumber(L, arg));
snprintf(buff, sizeof(buff), form, (int)luaL_checknumber(L, arg));
break;
}
case 'd':
case 'i':
{
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;
}
case 'o':
@ -997,7 +997,7 @@ static int str_format(lua_State* L)
double argValue = luaL_checknumber(L, arg);
addInt64Format(form, formatIndicator, formatItemSize);
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;
}
case 'e':
@ -1006,7 +1006,7 @@ static int str_format(lua_State* L)
case 'g':
case 'G':
{
sprintf(buff, form, (double)luaL_checknumber(L, arg));
snprintf(buff, sizeof(buff), form, (double)luaL_checknumber(L, arg));
break;
}
case 'q':
@ -1028,7 +1028,7 @@ static int str_format(lua_State* L)
}
else
{
sprintf(buff, form, s);
snprintf(buff, sizeof(buff), form, s);
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");
// 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
const LuaNode luaH_dummynode = {
{{NULL}, {0}, LUA_TNIL}, /* value */
@ -667,15 +664,18 @@ TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
if (p != luaO_nilobject)
return cast_to(TValue*, p);
else
{
if (ttisnil(key))
luaG_runerror(L, "table index is nil");
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
luaG_runerror(L, "table index is NaN");
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
luaG_runerror(L, "table index contains NaN");
return newkey(L, t, key);
}
return luaH_newkey(L, t, key);
}
TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
{
if (ttisnil(key))
luaG_runerror(L, "table index is nil");
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
luaG_runerror(L, "table index is NaN");
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
luaG_runerror(L, "table index contains NaN");
return newkey(L, t, key);
}
TValue* luaH_setnum(lua_State* L, Table* t, int key)

View file

@ -11,12 +11,16 @@
#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 TValue* luaH_setnum(lua_State* L, Table* t, int 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 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_newkey(lua_State* L, Table* t, const TValue* key);
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_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 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;

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
// 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
// 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
// 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
@ -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))
{
setobj(L, gval(n), ra);
setobj2t(L, gval(n), ra);
luaC_barriert(L, h, ra);
VM_NEXT();
}
@ -672,7 +672,7 @@ static void luau_execute(lua_State* L)
// fast-path: value is in expected slot
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);
VM_NEXT();
}
@ -684,7 +684,7 @@ static void luau_execute(lua_State* L)
int cachedslot = gval2slot(h, res);
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
VM_PATCH_C(pc - 2, cachedslot);
setobj(L, res, ra);
setobj2t(L, res, ra);
luaC_barriert(L, h, ra);
VM_NEXT();
}

View file

@ -14,6 +14,8 @@
LUAU_FASTFLAG(LuauLenTM)
LUAU_FASTFLAGVARIABLE(LuauBetterNewindex, false)
/* limit for table tag-method chains (to avoid loops) */
#define MAXTAGLOOP 100
@ -142,24 +144,50 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
{ /* `t' is a table? */
Table* h = hvalue(t);
if (h->readonly)
luaG_readonlyerror(L);
if (FFlag::LuauBetterNewindex)
{
const TValue* oldval = luaH_get(h, key);
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */
/* should we assign the key? (if key is valid or __newindex is not set) */
if (!ttisnil(oldval) || (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{
if (h->readonly)
luaG_readonlyerror(L);
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */
/* luaH_set would work but would repeat the lookup so we use luaH_setslot that can reuse oldval if it's safe */
TValue* newval = luaH_setslot(L, h, oldval, key);
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{ /* or no TM? */
setobj2t(L, oldval, val);
luaC_barriert(L, h, val);
return;
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)
luaG_readonlyerror(L);
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{ /* or no TM? */
setobj2t(L, oldval, val);
luaC_barriert(L, h, val);
return;
}
/* else will try the tag method */
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_indexerror(L, t, key);
if (ttisfunction(tm))
{
callTM(L, tm, t, key, val);

View file

@ -0,0 +1,129 @@
# Disallow `name T` and `name(T)` in future syntactic extensions for type annotations
## Summary
We propose to disallow the syntax ``<name> `('`` as well as `<name> <type>` in future syntax extensions for type annotations to ensure that all existing programs continue to parse correctly. This still keeps the door open for future syntax extensions of different forms such as ``<name> `<' <type> `>'``.
## Motivation
Lua and by extension Luau's syntax is very free form, which means that when the parser finishes parsing a node, it doesn't try to look for a semi-colon or any termination token e.g. a `{` to start a block, or `;` to end a statement, or a newline, etc. It just immediately invokes the next parser to figure out how to parse the next node based on the remainder's starting token.
That feature is sometimes quite troublesome when we want to add new syntax.
We have had cases where we talked about using syntax like `setmetatable(T, MT)` and `keyof T`. They all look innocent, but when you look beyond that, and try to apply it onto Luau's grammar, things break down really fast.
### `F(T)`?
An example that _will_ cause a change in semantics:
```
local t: F
(u):m()
```
where today, `local t: F` is one statement, and `(u):m()` is another. If we had the syntax for `F(T)` here, it becomes invalid input because it gets parsed as
```
local t: F(u)
:m()
```
This is important because of the `setmetatable(T, MT)` case:
```
type Foo = setmetatable({ x: number }, { ... })
```
For `setmetatable`, the parser isn't sure whether `{}` is actually a type or an expression, because _today_ `setmetatable` is parsed as a type reference, and `({}, {})` is the remainder that we'll attempt to parse as a statement. This means `{ x: number }` is invalid table _literal_. Recovery by backtracking is technically possible here, but this means performance loss on invalid input + may introduce false positives wrt how things are parsed. We'd much rather take a very strict stance about how things get parsed.
### `F T`?
An example that _will_ cause a change in semantics:
```
local function f(t): F T
(t or u):m()
end
```
where today, the return type annotation `F T` is simply parsed as just `F`, followed by a ambiguous parse error from the statement `T(t or u)` because its `(` is on the next line. If at some point in the future we were to allow `T` followed by `(` on the next line, then there's yet another semantic change. `F T` could be parsed as a type annotation and the first statement is `(t or u):m()` instead of `F` followed by `T(t or u):m()`.
For `keyof`, here's a practical example of the above issue:
```
type Vec2 = {x: number, y: number}
local function f(t, u): keyof Vec2
(t or u):m()
end
```
There's three possible outcomes:
1. Return type of `f` is `keyof`, statement throws a parse error because `(` is on the next line after `Vec2`,
2. Return type of `f` is `keyof Vec2` and next statement is `(t or u):m()`, or
3. Return type of `f` is `keyof` and next statement is `Vec2(t or u):m()` (if we allow `(` on the next line to be part of previous line).
This particular case is even worse when we keep going:
```
local function f(t): F
T(t or u):m()
end
```
```
local function f(t): F T
{1, 2, 3}
end
```
where today, `F` is the return type annotation of `f`, and `T(t or u):m()`/`T{1, 2, 3}` is the first statement, respectively.
Adding some syntax for `F T` **will** cause the parser to change the semantics of the above three examples.
### But what about `typeof(...)`?
This syntax is grandfathered in because the parser supported `typeof(...)` before we stabilized our syntax, and especially before type annotations were released to the public, so we didn't need to worry about compatibility here. We are very glad that we used parentheses in this case, because it's natural for expressions to belong within parentheses `()`, and types to belong within angles `<>`.
## The One Exception with a caveat
This is a strict requirement!
`function() -> ()` has been talked about in the past, and this one is different despite falling under the same category as ``<name> `('``. The token `function` is in actual fact a "hard keyword," meaning that it cannot be parsed as a type annotation because it is not an identifier, just a keyword.
Likewise, we also have talked about adding standalone `function` as a type annotation (semantics of it is irrelevant for this RFC)
It's possible that we may end up adding both, but the requirements are as such:
1. `function() -> ()` must be added first before standalone `function`, OR
2. `function` can be added first, but with a future-proofing parse error if `<` or `(` follows after it
If #1 is what ends up happening, there's not much to worry about because the type annotation parser will parse greedily already, so any new valid input will remain valid and have same semantics, except it also allows omitting of `(` and `<`.
If #2 is what ends up happening, there could be a problem if we didn't future-proof against `<` and `(` to follow `function`:
```
return f :: function(T) -> U
```
which would be a parse error because at the point of `(` we expect one of `until`, `end`, or `EOF`, and
```
return f :: function<a>(a) -> a
```
which would also be a parse error by the time we reach `->`, that is the production of the above is semantically equivalent to `(f < a) > (a)` which would compare whether the value of `f` is less than the value of `a`, then whether the result of that value is greater than `a`.
## Alternatives
Only allow these syntax when used inside parentheses e.g. `(F T)` or `(F(T))`. This makes it inconsistent with the existing `typeof(...)` type annotation, and changing that over is also breaking change.
Support backtracking in the parser, so if `: MyType(t or u):m()` is invalid syntax, revert and parse `MyType` as a type, and `(t or u):m()` as an expression statement. Even so, this option is terrible for:
1. parsing performance (backtracking means losing progress on invalid input),
2. user experience (why was this annotation parsed as `X(...)` instead of `X` followed by a statement `(...)`),
3. has false positives (`foo(bar)(baz)` may be parsed as `foo(bar)` as the type annotation and `(baz)` is the remainder to parse)
## Drawbacks
To be able to expose some kind of type-level operations using `F<T>` syntax, means one of the following must be chosen:
1. introduce the concept of "magic type functions" into type inference, or
2. introduce them into the prelude as `export type F<T> = ...` (where `...` is to be read as "we haven't decided")

View file

@ -31,9 +31,9 @@ Because we care about backward compatibility, we need some new syntax in order t
1. A string chunk (`` `...{ ``, `}...{`, and `` }...` ``) where `...` is a range of 0 to many characters.
* `\` escapes `` ` ``, `{`, and itself `\`.
* Restriction: the string interpolation literal must have at least one value to interpolate. We do not need 3 ways to express a single line string literal.
* The pairs must be on the same line (unless a `\` escapes the newline) but expressions needn't be on the same line.
2. An expression between the braces. This is the value that will be interpolated into the string.
* Restriction: we explicitly reject `{{` as it is considered an attempt to escape and get a single `{` character at runtime.
3. Formatting specification may follow after the expression, delimited by an unambiguous character.
* Restriction: the formatting specification must be constant at parse time.
* In the absence of an explicit formatting specification, the `%*` token will be used.
@ -61,7 +61,6 @@ local set2 = Set.new({0, 5, 4})
print(`{set1} {set2} = {Set.union(set1, set2)}`)
--> {0, 1, 3} {0, 5, 4} = {0, 1, 3, 4, 5}
-- For illustrative purposes. These are illegal specifically because they don't interpolate anything.
print(`Some example escaping the braces \{like so}`)
print(`backslash \ that escapes the space is not a part of the string...`)
print(`backslash \\ will escape the second backslash...`)
@ -88,13 +87,25 @@ print(`Welcome to \
-- Luau!
```
This expression will not be allowed to come after a `prefixexp`. I believe this is fully additive, so a future RFC may allow this. So for now, we explicitly reject the following:
This expression can also come after a `prefixexp`:
```
local name = "world"
print`Hello {name}`
```
The restriction on `{{` exists solely for the people coming from languages e.g. C#, Rust, or Python which uses `{{` to escape and get the character `{` at runtime. We're also rejecting this at parse time too, since the proper way to escape it is `\{`, so:
```lua
print(`{{1, 2, 3}} = {myCoolSet}`) -- parse error
```
If we did not apply this as a parse error, then the above would wind up printing as the following, which is obviously a gotcha we can and should avoid.
```
--> table: 0xSOMEADDRESS = {1, 2, 3}
```
Since the string interpolation expression is going to be lowered into a `string.format` call, we'll also need to extend `string.format`. The bare minimum to support the lowering is to add a new token whose definition is to perform a `tostring` call. `%*` is currently an invalid token, so this is a backward compatible extension. This RFC shall define `%*` to have the same behavior as if `tostring` was called.
```lua
@ -121,6 +132,13 @@ print(string.format("%* %* %*", return_two_nils()))
--> error: value #3 is missing, got 2
```
It must be said that we are not allowing this style of string literals in type annotations at this time, regardless of zero or many interpolating expressions, so the following two type annotations below are illegal syntax:
```lua
local foo: `foo`
local bar: `bar{baz}`
```
## Drawbacks
If we want to use backticks for other purposes, it may introduce some potential ambiguity. One option to solve that is to only ever produce string interpolation tokens from the context of an expression. This is messy but doable because the parser and the lexer are already implemented to work in tandem. The other option is to pick a different delimiter syntax to keep backticks available for use in the future.

View file

@ -2946,7 +2946,7 @@ local abc = b@1
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_on_class")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
loadDefinition(R"(
declare class Foo
@ -2984,9 +2984,25 @@ t.@1
}
}
TEST_CASE_FIXTURE(ACFixture, "do_compatible_self_calls")
{
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
local t = {}
function t:m() end
t:@1
)");
auto ac = autocomplete('1');
REQUIRE(ac.entryMap.count("m"));
CHECK(!ac.entryMap["m"].wrongIndexType);
}
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
local t = {}
@ -3002,7 +3018,7 @@ t:@1
TEST_CASE_FIXTURE(ACFixture, "no_incompatible_self_calls_2")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
local f: (() -> number) & ((number) -> number) = function(x: number?) return 2 end
@ -3017,7 +3033,7 @@ t:@1
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"(
local t = {}
@ -3032,9 +3048,26 @@ t:@1
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")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
local s = "hello"
@ -3053,7 +3086,7 @@ s:@1
TEST_CASE_FIXTURE(ACFixture, "string_prim_non_self_calls_are_avoided")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
local s = "hello"
@ -3070,7 +3103,7 @@ s.@1
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_non_self_calls_are_fine")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
string.@1
@ -3101,7 +3134,7 @@ table.@1
TEST_CASE_FIXTURE(ACBuiltinsFixture, "library_self_calls_are_invalid")
{
ScopedFastFlag selfCallAutocompleteFix2{"LuauSelfCallAutocompleteFix2", true};
ScopedFastFlag selfCallAutocompleteFix3{"LuauSelfCallAutocompleteFix3", true};
check(R"(
string:@1
@ -3113,8 +3146,11 @@ string:@1
CHECK(ac.entryMap["byte"].wrongIndexType == true);
REQUIRE(ac.entryMap.count("char"));
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"));
CHECK(ac.entryMap["sub"].wrongIndexType == true);
CHECK(ac.entryMap["sub"].wrongIndexType == false);
}
TEST_CASE_FIXTURE(ACFixture, "source_module_preservation_and_invalidation")

View file

@ -4352,8 +4352,6 @@ TEST_CASE("LoopUnrollControlFlow")
{"LuauCompileLoopUnrollThresholdMaxBoost", 300},
};
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
// break jumps to the end
CHECK_EQ("\n" + compileFunction(R"(
for i=1,3 do
@ -4669,8 +4667,6 @@ TEST_CASE("LoopUnrollCostBuiltins")
{"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
CHECK_EQ("\n" + compileFunction(R"(
function cipher(block, nonce)
@ -5893,8 +5889,6 @@ RETURN R0 2
TEST_CASE("OptimizationLevel")
{
ScopedFastFlag sff("LuauAlwaysCaptureHotComments", true);
// at optimization level 1, no inlining is performed
CHECK_EQ("\n" + compileFunction(R"(
local function foo(a)
@ -5964,8 +5958,6 @@ RETURN R1 -1
TEST_CASE("BuiltinFolding")
{
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
CHECK_EQ("\n" + compileFunction(R"(
return
math.abs(-42),
@ -6073,8 +6065,6 @@ RETURN R0 48
TEST_CASE("BuiltinFoldingProhibited")
{
ScopedFastFlag sff("LuauCompileFoldBuiltins", true);
CHECK_EQ("\n" + compileFunction(R"(
return
math.abs(),
@ -6108,9 +6098,6 @@ L3: RETURN R0 -1
TEST_CASE("BuiltinFoldingMultret")
{
ScopedFastFlag sff1("LuauCompileFoldBuiltins", true);
ScopedFastFlag sff2("LuauCompileBetterMultret", true);
CHECK_EQ("\n" + compileFunction(R"(
local NoLanes: Lanes = --[[ ]] 0b0000000000000000000000000000000
local OffscreenLane: Lane = --[[ ]] 0b1000000000000000000000000000000

View file

@ -316,7 +316,8 @@ TEST_CASE("Errors")
TEST_CASE("Events")
{
ScopedFastFlag sff("LuauLenTM", true);
ScopedFastFlag sff1("LuauLenTM", true);
ScopedFastFlag sff2("LuauBetterNewindex", true);
runConformance("events.lua");
}
@ -490,8 +491,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
TEST_CASE("Types")
{
ScopedFastFlag sff("LuauCheckLenMT", true);
runConformance("types.lua", [](lua_State* L) {
Luau::NullModuleResolver moduleResolver;
Luau::InternalErrorReporter iceHandler;
@ -862,8 +861,6 @@ TEST_CASE("ApiCalls")
TEST_CASE("ApiAtoms")
{
ScopedFastFlag sff("LuauLazyAtoms", true);
StateRef globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();

View file

@ -9,7 +9,7 @@
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);
LUAU_ASSERT(b.has_value());
@ -26,7 +26,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "hello")
)");
cgb.visit(block);
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
ConstraintSolver cs{&arena, rootScope};
@ -46,7 +46,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "generic_function")
)");
cgb.visit(block);
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
ConstraintSolver cs{&arena, rootScope};
@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "proper_let_generalization")
)");
cgb.visit(block);
NotNull<Scope2> rootScope = NotNull(cgb.rootScope);
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
ToStringOptions opts;

View file

@ -258,7 +258,7 @@ std::optional<TypeId> Fixture::getType(const std::string& name)
REQUIRE(module);
if (FFlag::DebugLuauDeferredConstraintResolution)
return linearSearchForBinding(module->getModuleScope2(), name.c_str());
return linearSearchForBinding(module->getModuleScope().get(), name.c_str());
else
return lookupName(module->getModuleScope(), name);
}
@ -434,7 +434,7 @@ BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
ConstraintGraphBuilderFixture::ConstraintGraphBuilderFixture()
: Fixture()
, cgb(mainModuleName, &arena, NotNull(&ice), frontend.getGlobalScope2())
, cgb(mainModuleName, &arena, NotNull(&ice), frontend.getGlobalScope())
, forceTheFlag{"DebugLuauDeferredConstraintResolution", true}
{
BlockedTypeVar::nextIndex = 0;
@ -479,17 +479,17 @@ std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name)
return std::nullopt;
}
std::optional<TypeId> linearSearchForBinding(Scope2* scope, const char* name)
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name)
{
while (scope)
{
for (const auto& [n, ty] : scope->bindings)
{
if (n.astName() == name)
return ty;
return ty.typeId;
}
scope = scope->parent;
scope = scope->parent.get();
}
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> linearSearchForBinding(Scope2* scope, const char* name);
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
} // 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_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_CASE_FIXTURE(Fixture, "basic_parse")

View file

@ -10,6 +10,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
TEST_SUITE_BEGIN("ToString");
@ -267,8 +268,16 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_type_when_length_is_exceeded")
o.maxTypeLength = 40;
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
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("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
}
}
TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
@ -286,8 +295,17 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
o.maxTypeLength = 40;
CHECK_EQ(toString(requireType("f0"), o), "() -> ()");
CHECK_EQ(toString(requireType("f1"), o), "(() -> ()) -> () -> ()");
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
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("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
}
}
TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table_state_braces")
@ -497,7 +515,10 @@ local function target(callback: nil) return callback(4, "hello") end
)");
LUAU_REQUIRE_ERRORS(result);
CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(nil) -> (*error-type*)", toString(requireType("target")));
else
CHECK_EQ("(nil) -> (<error-type>)", toString(requireType("target")));
}
TEST_CASE_FIXTURE(Fixture, "toStringGenericPack")

View file

@ -13,6 +13,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
TEST_SUITE_BEGIN("TypeInferAnyError");
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
@ -94,7 +96,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error")
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("<error-type>", toString(requireType("a")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
@ -110,7 +115,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("<error-type>", toString(requireType("a")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "length_of_error_type_does_not_produce_an_error")
@ -225,7 +233,10 @@ TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error")
CHECK_EQ("unknown", err->name);
CHECK_EQ("<error-type>", toString(requireType("a")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
@ -234,7 +245,10 @@ TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
local a = Utility.Create "Foo" {}
)");
CHECK_EQ("<error-type>", toString(requireType("a")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("a")));
else
CHECK_EQ("<error-type>", toString(requireType("a")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_complex_function_with_any")

View file

@ -9,6 +9,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
TEST_SUITE_BEGIN("BuiltinTests");
@ -952,7 +953,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
CHECK_EQ("number", toString(requireType("a")));
CHECK_EQ("string", toString(requireType("b")));
CHECK_EQ("boolean", toString(requireType("c")));
CHECK_EQ("<error-type>", toString(requireType("d")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("d")));
else
CHECK_EQ("<error-type>", toString(requireType("d")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments")

View file

@ -14,6 +14,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
TEST_SUITE_BEGIN("TypeInferFunctions");
@ -907,13 +908,19 @@ TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language")
REQUIRE(tm1);
CHECK_EQ("(string) -> number", toString(tm1->wantedType));
CHECK_EQ("(string, <error-type>) -> number", toString(tm1->givenType));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(string, *error-type*) -> number", toString(tm1->givenType));
else
CHECK_EQ("(string, <error-type>) -> number", toString(tm1->givenType));
auto tm2 = get<TypeMismatch>(result.errors[1]);
REQUIRE(tm2);
CHECK_EQ("(number, number) -> (number, number)", toString(tm2->wantedType));
CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("(string, *error-type*) -> number", toString(tm2->givenType));
else
CHECK_EQ("(string, <error-type>) -> number", toString(tm2->givenType));
}
TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
@ -1526,10 +1533,21 @@ function t:b() return 2 end -- not OK
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(R"(Type '(<error-type>) -> number' could not be converted into '() -> number'
if (FFlag::LuauSpecialTypesAsterisked)
{
CHECK_EQ(R"(Type '(*error-type*) -> number' could not be converted into '() -> number'
caused by:
Argument count mismatch. Function expects 1 argument, but none are specified)",
toString(result.errors[0]));
toString(result.errors[0]));
}
else
{
CHECK_EQ(R"(Type '(<error-type>) -> number' could not be converted into '() -> number'
caused by:
Argument count mismatch. Function expects 1 argument, but none are specified)",
toString(result.errors[0]));
}
}
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")

View file

@ -10,6 +10,7 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauCheckGenericHOFTypes)
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
using namespace Luau;
@ -1003,7 +1004,10 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_quantifying")
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
REQUIRE(t0);
CHECK_EQ("<error-type>", toString(t0->type));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(t0->type));
else
CHECK_EQ("<error-type>", toString(t0->type));
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
return get<OccursCheckFailed>(err);

View file

@ -13,6 +13,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
TEST_SUITE_BEGIN("TypeInferLoops");
TEST_CASE_FIXTURE(Fixture, "for_loop")
@ -142,7 +144,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
CHECK_EQ(2, result.errors.size());
TypeId p = requireType("p");
CHECK_EQ("<error-type>", toString(p));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(p));
else
CHECK_EQ("<error-type>", toString(p));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")

View file

@ -12,6 +12,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
TEST_SUITE_BEGIN("TypeInferModules");
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
@ -143,7 +145,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export")
auto hootyType = requireType(bModule, "Hooty");
CHECK_EQ("<error-type>", toString(hootyType));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(hootyType));
else
CHECK_EQ("<error-type>", toString(hootyType));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
@ -244,7 +249,11 @@ local ModuleA = require(game.A)
LUAU_REQUIRE_NO_ERRORS(result);
std::optional<TypeId> oty = requireType("ModuleA");
CHECK_EQ("<error-type>", toString(*oty));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(*oty));
else
CHECK_EQ("<error-type>", toString(*oty));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types")

View file

@ -490,8 +490,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error")
{
ScopedFastFlag sff("LuauCheckLenMT", true);
CheckResult result = check(R"(
--!strict
local foo = {

View file

@ -12,6 +12,7 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauDeduceFindMatchReturnTypes)
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
using namespace Luau;
@ -49,7 +50,10 @@ TEST_CASE_FIXTURE(Fixture, "string_index")
REQUIRE(nat);
CHECK_EQ("string", toString(nat->ty));
CHECK_EQ("<error-type>", toString(requireType("t")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("t")));
else
CHECK_EQ("<error-type>", toString(requireType("t")));
}
TEST_CASE_FIXTURE(Fixture, "string_method")

View file

@ -8,6 +8,7 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
using namespace Luau;
@ -526,7 +527,10 @@ TEST_CASE_FIXTURE(Fixture, "type_narrow_to_vector")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28})));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireTypeAtPosition({3, 28})));
else
CHECK_EQ("<error-type>", toString(requireTypeAtPosition({3, 28})));
}
TEST_CASE_FIXTURE(Fixture, "nonoptional_type_can_narrow_to_nil_if_sense_is_true")

View file

@ -16,6 +16,7 @@
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauFixLocationSpanTableIndexExpr);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAG(LuauSpecialTypesAsterisked);
using namespace Luau;
@ -237,10 +238,21 @@ TEST_CASE_FIXTURE(Fixture, "type_errors_infer_types")
// TODO: Should we assert anything about these tests when DCR is being used?
if (!FFlag::DebugLuauDeferredConstraintResolution)
{
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")));
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("d")));
CHECK_EQ("<error-type>", toString(requireType("e")));
CHECK_EQ("<error-type>", toString(requireType("f")));
}
}
}
@ -650,7 +662,11 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_isoptional")
std::optional<TypeFun> t0 = getMainModule()->getModuleScope()->lookupType("t0");
REQUIRE(t0);
CHECK_EQ("<error-type>", toString(t0->type));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(t0->type));
else
CHECK_EQ("<error-type>", toString(t0->type));
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& err) {
return get<OccursCheckFailed>(err);

View file

@ -9,6 +9,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
struct TryUnifyFixture : Fixture
{
TypeArena arena;
@ -121,7 +123,10 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "members_of_failed_typepack_unification_are_u
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("a", toString(requireType("a")));
CHECK_EQ("<error-type>", toString(requireType("b")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("b")));
else
CHECK_EQ("<error-type>", toString(requireType("b")));
}
TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_constrained")
@ -136,7 +141,10 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_con
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("a", toString(requireType("a")));
CHECK_EQ("<error-type>", toString(requireType("b")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("b")));
else
CHECK_EQ("<error-type>", toString(requireType("b")));
CHECK_EQ("number", toString(requireType("c")));
}

View file

@ -7,6 +7,7 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTFLAG(LuauSpecialTypesAsterisked)
using namespace Luau;
@ -199,7 +200,10 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property")
CHECK_EQ(mup->missing[0], *bTy);
CHECK_EQ(mup->key, "x");
CHECK_EQ("<error-type>", toString(requireType("r")));
if (FFlag::LuauSpecialTypesAsterisked)
CHECK_EQ("*error-type*", toString(requireType("r")));
else
CHECK_EQ("<error-type>", toString(requireType("r")));
}
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any")

View file

@ -25,7 +25,7 @@ struct TypePackFixture
TypePackId freshTypePack()
{
typePacks.emplace_back(new TypePackVar{Unifiable::Free{{}}});
typePacks.emplace_back(new TypePackVar{Unifiable::Free{TypeLevel{}}});
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 "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
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"))
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'

View file

@ -61,7 +61,7 @@ static bool debuggerPresent()
static int testAssertionHandler(const char* expr, const char* file, int line, const char* function)
{
if (debuggerPresent())
LUAU_DEBUGBREAK();
LUAU_DEBUGBREAK();
ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr));
return 1;
@ -298,5 +298,3 @@ int main(int argc, char** argv)
}
return result;
}

View file

@ -44,7 +44,6 @@ AutocompleteTest.as_types
AutocompleteTest.autocomplete_boolean_singleton
AutocompleteTest.autocomplete_default_type_pack_parameters
AutocompleteTest.autocomplete_default_type_parameters
AutocompleteTest.autocomplete_documentation_symbols
AutocompleteTest.autocomplete_end_with_fn_exprs
AutocompleteTest.autocomplete_end_with_lambda
AutocompleteTest.autocomplete_explicit_type_pack
@ -65,47 +64,34 @@ AutocompleteTest.autocomplete_until_in_repeat
AutocompleteTest.autocomplete_while_middle_keywords
AutocompleteTest.autocompleteProp_index_function_metamethod_is_variadic
AutocompleteTest.bias_toward_inner_scope
AutocompleteTest.comments
AutocompleteTest.cyclic_table
AutocompleteTest.do_compatible_self_calls
AutocompleteTest.do_not_overwrite_context_sensitive_kws
AutocompleteTest.do_not_suggest_internal_module_type
AutocompleteTest.do_not_suggest_synthetic_table_name
AutocompleteTest.dont_offer_any_suggestions_from_the_end_of_a_comment
AutocompleteTest.do_wrong_compatible_self_calls
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_comment
AutocompleteTest.dont_suggest_local_before_its_definition
AutocompleteTest.empty_program
AutocompleteTest.function_expr_params
AutocompleteTest.function_in_assignment_has_parentheses
AutocompleteTest.function_in_assignment_has_parentheses_2
AutocompleteTest.function_parameters
AutocompleteTest.function_result_passed_to_function_has_parentheses
AutocompleteTest.function_type_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.global_function_params
AutocompleteTest.global_functions_are_not_scoped_lexically
AutocompleteTest.if_then_else_elseif_completions
AutocompleteTest.if_then_else_full_keywords
AutocompleteTest.keyword_members
AutocompleteTest.keyword_methods
AutocompleteTest.keyword_types
AutocompleteTest.leave_numbers_alone
AutocompleteTest.library_non_self_calls_are_fine
AutocompleteTest.library_self_calls_are_invalid
AutocompleteTest.local_function
AutocompleteTest.local_function_params
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_if_conditional
AutocompleteTest.module_type_members
AutocompleteTest.modules_with_types
AutocompleteTest.nested_member_completions
@ -114,16 +100,13 @@ AutocompleteTest.no_function_name_suggestions
AutocompleteTest.no_incompatible_self_calls
AutocompleteTest.no_incompatible_self_calls_2
AutocompleteTest.no_incompatible_self_calls_on_class
AutocompleteTest.no_incompatible_self_calls_provisional
AutocompleteTest.not_the_var_we_are_defining
AutocompleteTest.no_wrong_compatible_self_calls_with_generics
AutocompleteTest.optional_members
AutocompleteTest.private_types
AutocompleteTest.recommend_statement_starting_keywords
AutocompleteTest.recursive_function
AutocompleteTest.recursive_function_global
AutocompleteTest.recursive_function_local
AutocompleteTest.return_types
AutocompleteTest.skip_current_local
AutocompleteTest.sometimes_the_metatable_is_an_error
AutocompleteTest.source_module_preservation_and_invalidation
AutocompleteTest.statement_between_two_statements
@ -154,9 +137,7 @@ AutocompleteTest.type_correct_suggestion_in_table
AutocompleteTest.type_scoping_easy
AutocompleteTest.unsealed_table
AutocompleteTest.unsealed_table_2
AutocompleteTest.user_defined_globals
AutocompleteTest.user_defined_local_functions_in_own_definition
BuiltinDefinitionsTest.lib_documentation_symbols
BuiltinTests.aliased_string_format
BuiltinTests.assert_removes_falsy_types
BuiltinTests.assert_removes_falsy_types2
@ -231,16 +212,10 @@ BuiltinTests.tonumber_returns_optional_number_type
BuiltinTests.tonumber_returns_optional_number_type2
BuiltinTests.xpcall
DefinitionTests.class_definition_function_prop
DefinitionTests.class_definitions_cannot_extend_non_class
DefinitionTests.class_definitions_cannot_overload_non_function
DefinitionTests.declaring_generic_functions
DefinitionTests.definition_file_class_function_args
DefinitionTests.definition_file_classes
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
FrontendTest.accumulate_cached_errors
FrontendTest.accumulate_cached_errors_in_consistent_order
@ -256,12 +231,9 @@ FrontendTest.cycle_error_paths
FrontendTest.cycle_errors_can_be_fixed
FrontendTest.cycle_incremental_type_surface
FrontendTest.cycle_incremental_type_surface_longer
FrontendTest.discard_type_graphs
FrontendTest.dont_recheck_script_that_hasnt_been_marked_dirty
FrontendTest.dont_reparse_clean_file_when_linting
FrontendTest.environments
FrontendTest.find_a_require
FrontendTest.find_a_require_inside_a_function
FrontendTest.ignore_require_to_nonexistent_file
FrontendTest.imported_table_modification_2
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_modules_are_typed
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.real_source
FrontendTest.recheck_if_dependent_script_is_dirty
FrontendTest.reexport_cyclic_type
FrontendTest.reexport_type_alias
FrontendTest.report_require_to_nonexistent_file
FrontendTest.report_syntax_error_in_required_file
FrontendTest.reports_errors_from_multiple_sources
FrontendTest.stats_are_not_reset_between_checks
FrontendTest.test_lint_uses_correct_config
FrontendTest.test_pruneParentSegments
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.intersection_of_functions_of_different_arities
isSubtype.intersection_of_tables
isSubtype.table_with_any_prop
isSubtype.table_with_table_prop
isSubtype.tables
Linter.BuiltinGlobalWrite
Linter.DeprecatedApi
Linter.LocalShadowGlobal
Linter.TableOperations
Linter.use_all_parent_scopes_for_globals
ModuleTests.any_persistance_does_not_leak
ModuleTests.builtin_types_point_into_globalTypes_arena
ModuleTests.clone_self_property
@ -342,45 +382,237 @@ Normalize.skip_force_normal_on_external_types
Normalize.union_of_distinct_free_types
Normalize.variadic_tail_is_marked_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.multiple_parse_errors
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_local_function
ParserTests.parse_nesting_based_end_detection_local_repeat
ParserTests.parse_nesting_based_end_detection_nested
ParserTests.parse_nesting_based_end_detection_single_line
ParserTests.parse_numbers_error
ParserTests.parse_numbers_range_error
ParserTests.stop_if_line_ends_with_hyphen
ParserTests.type_alias_error_messages
ProvisionalTests.bail_early_if_unification_is_too_complicated
ProvisionalTests.choose_the_right_overload_for_pcall
ProvisionalTests.constrained_is_level_dependent
ProvisionalTests.discriminate_from_x_not_equal_to_nil
ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack
ProvisionalTests.error_on_eq_metamethod_returning_a_type_other_than_boolean
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
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.class
ToDot.function
@ -401,9 +633,13 @@ ToString.toStringNamedFunction_id
ToString.toStringNamedFunction_map
ToString.toStringNamedFunction_overrides_param_names
ToString.toStringNamedFunction_variadics
TranspilerTests.attach_types
TranspilerTests.type_lists_should_be_emitted_correctly
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.cannot_steal_hoisted_type_alias
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_type
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.assign_prop_to_table_by_calling_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
TypeInferFunctions.another_indirect_function_case_where_it_is_ok_to_provide_too_many_arguments
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.check_function_before_lambda_that_uses_it
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
TypeInferFunctions.cyclic_function_type_in_args
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.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.free_is_not_bound_to_unknown
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_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_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_to_oversaturate_a_higher_order_function_argument
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
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_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_return_values
TypeInferFunctions.toposort_doesnt_break_mutual_recursion
TypeInferFunctions.vararg_function_is_quantified
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:
return set(map(str.strip, f.readlines()))
def safeParseInt(i, default=0):
try:
return int(i)
except ValueError:
return default
class Handler(x.ContentHandler):
def __init__(self, failList):
@ -22,6 +27,8 @@ class Handler(x.ContentHandler):
self.results = {} # {DottedName: TrueIfTheTestPassed}
self.numSkippedTests = 0
def startElement(self, name, attrs):
if name == "TestSuite":
self.currentTest.append(attrs["name"])
@ -30,10 +37,7 @@ class Handler(x.ContentHandler):
elif name == "OverallResultsAsserts":
if self.currentTest:
try:
failed = 0 != int(attrs["failures"])
except ValueError:
failed = False
failed = 0 != safeParseInt(attrs["failures"])
dottedName = ".".join(self.currentTest)
shouldFail = dottedName in self.failList
@ -45,6 +49,9 @@ class Handler(x.ContentHandler):
self.results[dottedName] = not failed
elif name == 'OverallResultsTestCases':
self.numSkippedTests = safeParseInt(attrs.get("skipped", 0))
def endElement(self, name):
if name == "TestCase":
self.currentTest.pop()
@ -111,6 +118,10 @@ def main():
print(name, file=f)
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(
0
if all(