mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-12 21:10:37 +00:00
Sync to upstream/release/533 (#560)
This commit is contained in:
parent
348ad4d417
commit
08ab7da4db
54 changed files with 968 additions and 693 deletions
|
@ -1,10 +1,10 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Location.h"
|
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Variant.h"
|
#include "Luau/Variant.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -47,18 +47,24 @@ struct InstantiationConstraint
|
||||||
TypeId superType;
|
TypeId superType;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint>;
|
// name(namedType) = name
|
||||||
|
struct NameConstraint
|
||||||
|
{
|
||||||
|
TypeId namedType;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, NameConstraint>;
|
||||||
using ConstraintPtr = std::unique_ptr<struct Constraint>;
|
using ConstraintPtr = std::unique_ptr<struct Constraint>;
|
||||||
|
|
||||||
struct Constraint
|
struct Constraint
|
||||||
{
|
{
|
||||||
Constraint(ConstraintV&& c, Location location);
|
explicit Constraint(ConstraintV&& c);
|
||||||
|
|
||||||
Constraint(const Constraint&) = delete;
|
Constraint(const Constraint&) = delete;
|
||||||
Constraint& operator=(const Constraint&) = delete;
|
Constraint& operator=(const Constraint&) = delete;
|
||||||
|
|
||||||
ConstraintV c;
|
ConstraintV c;
|
||||||
Location location;
|
|
||||||
std::vector<NotNull<Constraint>> dependencies;
|
std::vector<NotNull<Constraint>> dependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,20 +17,7 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Scope2
|
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<Scope2*> children;
|
|
||||||
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
|
|
||||||
TypePackId returnType;
|
|
||||||
// All constraints belonging to this scope.
|
|
||||||
std::vector<ConstraintPtr> constraints;
|
|
||||||
|
|
||||||
std::optional<TypeId> lookup(Symbol sym);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConstraintGraphBuilder
|
struct ConstraintGraphBuilder
|
||||||
{
|
{
|
||||||
|
@ -47,6 +34,10 @@ struct ConstraintGraphBuilder
|
||||||
// A mapping of AST node to TypePackId.
|
// A mapping of AST node to TypePackId.
|
||||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
||||||
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
|
||||||
|
// Types resolved from type annotations. Analogous to astTypes.
|
||||||
|
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
|
||||||
|
// Type packs resolved from type annotations. Analogous to astTypePacks.
|
||||||
|
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
|
||||||
|
|
||||||
explicit ConstraintGraphBuilder(TypeArena* arena);
|
explicit ConstraintGraphBuilder(TypeArena* arena);
|
||||||
|
|
||||||
|
@ -73,9 +64,8 @@ struct ConstraintGraphBuilder
|
||||||
* Adds a new constraint with no dependencies to a given scope.
|
* Adds a new constraint with no dependencies to a given scope.
|
||||||
* @param scope the scope to add the constraint to. Must not be null.
|
* @param scope the scope to add the constraint to. Must not be null.
|
||||||
* @param cv the constraint variant to add.
|
* @param cv the constraint variant to add.
|
||||||
* @param location the location to attribute to the constraint.
|
|
||||||
*/
|
*/
|
||||||
void addConstraint(Scope2* scope, ConstraintV cv, Location location);
|
void addConstraint(Scope2* scope, ConstraintV cv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a constraint to a given scope.
|
* Adds a constraint to a given scope.
|
||||||
|
@ -99,6 +89,7 @@ struct ConstraintGraphBuilder
|
||||||
void visit(Scope2* scope, AstStatReturn* ret);
|
void visit(Scope2* scope, AstStatReturn* ret);
|
||||||
void visit(Scope2* scope, AstStatAssign* assign);
|
void visit(Scope2* scope, AstStatAssign* assign);
|
||||||
void visit(Scope2* scope, AstStatIf* ifStatement);
|
void visit(Scope2* scope, AstStatIf* ifStatement);
|
||||||
|
void visit(Scope2* scope, AstStatTypeAlias* alias);
|
||||||
|
|
||||||
TypePackId checkExprList(Scope2* scope, const AstArray<AstExpr*>& exprs);
|
TypePackId checkExprList(Scope2* scope, const AstArray<AstExpr*>& exprs);
|
||||||
|
|
||||||
|
@ -124,6 +115,24 @@ struct ConstraintGraphBuilder
|
||||||
* @param fn the function expression to check.
|
* @param fn the function expression to check.
|
||||||
*/
|
*/
|
||||||
void checkFunctionBody(Scope2* scope, AstExprFunction* fn);
|
void checkFunctionBody(Scope2* scope, AstExprFunction* fn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a type from its AST annotation.
|
||||||
|
* @param scope the scope that the type annotation appears within.
|
||||||
|
* @param ty the AST annotation to resolve.
|
||||||
|
* @return the type of the AST annotation.
|
||||||
|
**/
|
||||||
|
TypeId resolveType(Scope2* scope, AstType* ty);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a type pack from its AST annotation.
|
||||||
|
* @param scope the scope that the type annotation appears within.
|
||||||
|
* @param tp the AST annotation to resolve.
|
||||||
|
* @return the type pack of the AST annotation.
|
||||||
|
**/
|
||||||
|
TypePackId resolveTypePack(Scope2* scope, AstTypePack* tp);
|
||||||
|
|
||||||
|
TypePackId resolveTypePack(Scope2* scope, const AstTypeList& list);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct ConstraintSolver
|
||||||
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const InstantiationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const InstantiationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
|
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
|
||||||
|
|
||||||
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
|
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +86,7 @@ struct ConstraintSolver
|
||||||
* @param subType the sub-type to unify.
|
* @param subType the sub-type to unify.
|
||||||
* @param superType the super-type to unify.
|
* @param superType the super-type to unify.
|
||||||
*/
|
*/
|
||||||
void unify(TypeId subType, TypeId superType, Location location);
|
void unify(TypeId subType, TypeId superType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Unifier and performs a single unification operation. Commits
|
* Creates a new Unifier and performs a single unification operation. Commits
|
||||||
|
@ -93,7 +94,7 @@ struct ConstraintSolver
|
||||||
* @param subPack the sub-type pack to unify.
|
* @param subPack the sub-type pack to unify.
|
||||||
* @param superPack the super-type pack to unify.
|
* @param superPack the super-type pack to unify.
|
||||||
*/
|
*/
|
||||||
void unify(TypePackId subPack, TypePackId superPack, Location location);
|
void unify(TypePackId subPack, TypePackId superPack);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
|
||||||
#include "Luau/ConstraintGraphBuilder.h"
|
#include "Luau/Constraint.h"
|
||||||
|
#include "Luau/NotNull.h"
|
||||||
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
|
@ -169,6 +169,13 @@ struct GenericError
|
||||||
bool operator==(const GenericError& rhs) const;
|
bool operator==(const GenericError& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct InternalError
|
||||||
|
{
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
bool operator==(const InternalError& rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
struct CannotCallNonFunction
|
struct CannotCallNonFunction
|
||||||
{
|
{
|
||||||
TypeId ty;
|
TypeId ty;
|
||||||
|
@ -293,12 +300,12 @@ struct NormalizationTooComplex
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using TypeErrorData =
|
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
|
||||||
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
|
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
|
||||||
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
|
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError,
|
||||||
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed,
|
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
|
||||||
ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotInferBinaryOperation,
|
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
|
||||||
MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated, NormalizationTooComplex>;
|
TypesAreUnrelated, NormalizationTooComplex>;
|
||||||
|
|
||||||
struct TypeError
|
struct TypeError
|
||||||
{
|
{
|
||||||
|
@ -339,7 +346,13 @@ T* get(TypeError& e)
|
||||||
|
|
||||||
using ErrorVec = std::vector<TypeError>;
|
using ErrorVec = std::vector<TypeError>;
|
||||||
|
|
||||||
|
struct TypeErrorToStringOptions
|
||||||
|
{
|
||||||
|
FileResolver* fileResolver = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
std::string toString(const TypeError& error);
|
std::string toString(const TypeError& error);
|
||||||
|
std::string toString(const TypeError& error, TypeErrorToStringOptions options);
|
||||||
|
|
||||||
bool containsParseErrorName(const TypeError& error);
|
bool containsParseErrorName(const TypeError& error);
|
||||||
|
|
||||||
|
@ -356,4 +369,24 @@ struct InternalErrorReporter
|
||||||
[[noreturn]] void ice(const std::string& message);
|
[[noreturn]] void ice(const std::string& message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InternalCompilerError : public std::exception {
|
||||||
|
public:
|
||||||
|
explicit InternalCompilerError(const std::string& message, const std::string& moduleName)
|
||||||
|
: message(message)
|
||||||
|
, moduleName(moduleName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit InternalCompilerError(const std::string& message, const std::string& moduleName, const Location& location)
|
||||||
|
: message(message)
|
||||||
|
, moduleName(moduleName)
|
||||||
|
, location(location)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual const char* what() const throw();
|
||||||
|
|
||||||
|
const std::string message;
|
||||||
|
const std::string moduleName;
|
||||||
|
const std::optional<Location> location;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -30,6 +30,7 @@ std::ostream& operator<<(std::ostream& lhs, const OccursCheckFailed& error);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const UnknownRequire& error);
|
std::ostream& operator<<(std::ostream& lhs, const UnknownRequire& error);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const UnknownPropButFoundLikeProp& e);
|
std::ostream& operator<<(std::ostream& lhs, const UnknownPropButFoundLikeProp& e);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const GenericError& error);
|
std::ostream& operator<<(std::ostream& lhs, const GenericError& error);
|
||||||
|
std::ostream& operator<<(std::ostream& lhs, const InternalError& error);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const FunctionExitsWithoutReturning& error);
|
std::ostream& operator<<(std::ostream& lhs, const FunctionExitsWithoutReturning& error);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const MissingProperties& error);
|
std::ostream& operator<<(std::ostream& lhs, const MissingProperties& error);
|
||||||
std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
|
std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Error.h"
|
||||||
#include "Luau/FileResolver.h"
|
#include "Luau/FileResolver.h"
|
||||||
#include "Luau/ParseOptions.h"
|
#include "Luau/ParseOptions.h"
|
||||||
#include "Luau/Error.h"
|
|
||||||
#include "Luau/ParseResult.h"
|
#include "Luau/ParseResult.h"
|
||||||
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -19,7 +20,9 @@ struct Module;
|
||||||
|
|
||||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||||
using ModulePtr = std::shared_ptr<Module>;
|
using ModulePtr = std::shared_ptr<Module>;
|
||||||
struct Scope2;
|
|
||||||
|
class AstType;
|
||||||
|
class AstTypePack;
|
||||||
|
|
||||||
/// Root of the AST of a parsed source file
|
/// Root of the AST of a parsed source file
|
||||||
struct SourceModule
|
struct SourceModule
|
||||||
|
@ -73,6 +76,8 @@ struct Module
|
||||||
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
|
||||||
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
|
||||||
DenseHashMap<const AstExpr*, TypeId> astOverloadResolvedTypes{nullptr};
|
DenseHashMap<const AstExpr*, TypeId> astOverloadResolvedTypes{nullptr};
|
||||||
|
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
|
||||||
|
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
|
||||||
|
|
||||||
std::unordered_map<Name, TypeId> declaredGlobals;
|
std::unordered_map<Name, TypeId> declaredGlobals;
|
||||||
ErrorVec errors;
|
ErrorVec errors;
|
||||||
|
|
|
@ -9,8 +9,8 @@ namespace Luau
|
||||||
|
|
||||||
struct InternalErrorReporter;
|
struct InternalErrorReporter;
|
||||||
|
|
||||||
bool isSubtype(TypeId superTy, TypeId subTy, InternalErrorReporter& ice);
|
bool isSubtype(TypeId subTy, TypeId superTy, InternalErrorReporter& ice);
|
||||||
bool isSubtype(TypePackId superTy, TypePackId subTy, InternalErrorReporter& ice);
|
bool isSubtype(TypePackId subTy, TypePackId superTy, InternalErrorReporter& ice);
|
||||||
|
|
||||||
std::pair<TypeId, bool> normalize(TypeId ty, TypeArena& arena, InternalErrorReporter& ice);
|
std::pair<TypeId, bool> normalize(TypeId ty, TypeArena& arena, InternalErrorReporter& ice);
|
||||||
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice);
|
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice);
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRecursionLimitException);
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -39,21 +37,12 @@ private:
|
||||||
|
|
||||||
struct RecursionLimiter : RecursionCounter
|
struct RecursionLimiter : RecursionCounter
|
||||||
{
|
{
|
||||||
// TODO: remove ctx after LuauRecursionLimitException is removed
|
RecursionLimiter(int* count, int limit)
|
||||||
RecursionLimiter(int* count, int limit, const char* ctx)
|
|
||||||
: RecursionCounter(count)
|
: RecursionCounter(count)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(ctx);
|
|
||||||
if (limit > 0 && *count > limit)
|
if (limit > 0 && *count > limit)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauRecursionLimitException)
|
throw RecursionLimitException();
|
||||||
throw RecursionLimitException();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string m = "Internal recursion counter limit exceeded: ";
|
|
||||||
m += ctx;
|
|
||||||
throw std::runtime_error(m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Constraint.h"
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
@ -64,4 +65,21 @@ struct Scope
|
||||||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Scope2
|
||||||
|
{
|
||||||
|
// The parent scope of this scope. Null if there is no parent (i.e. this
|
||||||
|
// is the module-level scope).
|
||||||
|
Scope2* parent = nullptr;
|
||||||
|
// All the children of this scope.
|
||||||
|
std::vector<Scope2*> children;
|
||||||
|
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
|
||||||
|
std::unordered_map<Name, TypeId> typeBindings;
|
||||||
|
TypePackId returnType;
|
||||||
|
// All constraints belonging to this scope.
|
||||||
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
|
std::optional<TypeId> lookup(Symbol sym);
|
||||||
|
std::optional<TypeId> lookupTypeBinding(const Name& name);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -287,7 +287,6 @@ struct FunctionTypeVar
|
||||||
bool hasSelf;
|
bool hasSelf;
|
||||||
Tags tags;
|
Tags tags;
|
||||||
bool hasNoGenerics = false;
|
bool hasNoGenerics = false;
|
||||||
bool generalized = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TableState
|
enum class TableState
|
||||||
|
|
|
@ -117,6 +117,7 @@ struct Generic
|
||||||
explicit Generic(const Name& name);
|
explicit Generic(const Name& name);
|
||||||
explicit Generic(Scope2* scope);
|
explicit Generic(Scope2* scope);
|
||||||
Generic(TypeLevel level, const Name& name);
|
Generic(TypeLevel level, const Name& name);
|
||||||
|
Generic(Scope2* scope, const Name& name);
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
|
|
|
@ -79,12 +79,8 @@ private:
|
||||||
void tryUnifySingletons(TypeId subTy, TypeId superTy);
|
void tryUnifySingletons(TypeId subTy, TypeId superTy);
|
||||||
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);
|
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);
|
||||||
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false);
|
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false);
|
||||||
void DEPRECATED_tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false);
|
|
||||||
void tryUnifyFreeTable(TypeId subTy, TypeId superTy);
|
|
||||||
void tryUnifySealedTables(TypeId subTy, TypeId superTy, bool isIntersection);
|
|
||||||
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
|
||||||
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
||||||
void tryUnifyIndexer(const TableIndexer& subIndexer, const TableIndexer& superIndexer);
|
|
||||||
|
|
||||||
TypeId widen(TypeId ty);
|
TypeId widen(TypeId ty);
|
||||||
TypePackId widen(TypePackId tp);
|
TypePackId widen(TypePackId tp);
|
||||||
|
|
|
@ -169,7 +169,7 @@ struct GenericTypeVarVisitor
|
||||||
|
|
||||||
void traverse(TypeId ty)
|
void traverse(TypeId ty)
|
||||||
{
|
{
|
||||||
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit, "TypeVarVisitor"};
|
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit};
|
||||||
|
|
||||||
if (visit_detail::hasSeen(seen, ty))
|
if (visit_detail::hasSeen(seen, ty))
|
||||||
{
|
{
|
||||||
|
|
|
@ -317,7 +317,7 @@ TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
|
||||||
if (tp->persistent)
|
if (tp->persistent)
|
||||||
return tp;
|
return tp;
|
||||||
|
|
||||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypePackId");
|
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||||
|
|
||||||
TypePackId& res = cloneState.seenTypePacks[tp];
|
TypePackId& res = cloneState.seenTypePacks[tp];
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
|
||||||
if (typeId->persistent)
|
if (typeId->persistent)
|
||||||
return typeId;
|
return typeId;
|
||||||
|
|
||||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypeId");
|
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||||
|
|
||||||
TypeId& res = cloneState.seenTypes[typeId];
|
TypeId& res = cloneState.seenTypes[typeId];
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,8 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
Constraint::Constraint(ConstraintV&& c, Location location)
|
Constraint::Constraint(ConstraintV&& c)
|
||||||
: c(std::move(c))
|
: c(std::move(c))
|
||||||
, location(location)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,13 @@
|
||||||
|
|
||||||
#include "Luau/ConstraintGraphBuilder.h"
|
#include "Luau/ConstraintGraphBuilder.h"
|
||||||
|
|
||||||
|
#include "Luau/Scope.h"
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
||||||
|
|
||||||
std::optional<TypeId> Scope2::lookup(Symbol sym)
|
|
||||||
{
|
|
||||||
Scope2* s = this;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
auto it = s->bindings.find(sym);
|
|
||||||
if (it != s->bindings.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
if (s->parent)
|
|
||||||
s = s->parent;
|
|
||||||
else
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstraintGraphBuilder::ConstraintGraphBuilder(TypeArena* arena)
|
ConstraintGraphBuilder::ConstraintGraphBuilder(TypeArena* arena)
|
||||||
: singletonTypes(getSingletonTypes())
|
: singletonTypes(getSingletonTypes())
|
||||||
, arena(arena)
|
, arena(arena)
|
||||||
|
@ -59,10 +44,10 @@ Scope2* ConstraintGraphBuilder::childScope(Location location, Scope2* parent)
|
||||||
return borrow;
|
return borrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::addConstraint(Scope2* scope, ConstraintV cv, Location location)
|
void ConstraintGraphBuilder::addConstraint(Scope2* scope, ConstraintV cv)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
scope->constraints.emplace_back(new Constraint{std::move(cv), location});
|
scope->constraints.emplace_back(new Constraint{std::move(cv)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::addConstraint(Scope2* scope, std::unique_ptr<Constraint> c)
|
void ConstraintGraphBuilder::addConstraint(Scope2* scope, std::unique_ptr<Constraint> c)
|
||||||
|
@ -79,6 +64,13 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
||||||
rootScope = scopes.back().second.get();
|
rootScope = scopes.back().second.get();
|
||||||
rootScope->returnType = freshTypePack(rootScope);
|
rootScope->returnType = freshTypePack(rootScope);
|
||||||
|
|
||||||
|
// TODO: We should share the global scope.
|
||||||
|
rootScope->typeBindings["nil"] = singletonTypes.nilType;
|
||||||
|
rootScope->typeBindings["number"] = singletonTypes.numberType;
|
||||||
|
rootScope->typeBindings["string"] = singletonTypes.stringType;
|
||||||
|
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||||
|
rootScope->typeBindings["thread"] = singletonTypes.threadType;
|
||||||
|
|
||||||
visit(rootScope, block);
|
visit(rootScope, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +94,8 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStat* stat)
|
||||||
checkPack(scope, e->expr);
|
checkPack(scope, e->expr);
|
||||||
else if (auto i = stat->as<AstStatIf>())
|
else if (auto i = stat->as<AstStatIf>())
|
||||||
visit(scope, i);
|
visit(scope, i);
|
||||||
|
else if (auto a = stat->as<AstStatTypeAlias>())
|
||||||
|
visit(scope, a);
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(0);
|
LUAU_ASSERT(0);
|
||||||
}
|
}
|
||||||
|
@ -114,8 +108,14 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatLocal* local)
|
||||||
|
|
||||||
for (AstLocal* local : local->vars)
|
for (AstLocal* local : local->vars)
|
||||||
{
|
{
|
||||||
// TODO annotations
|
|
||||||
TypeId ty = freshType(scope);
|
TypeId ty = freshType(scope);
|
||||||
|
|
||||||
|
if (local->annotation)
|
||||||
|
{
|
||||||
|
TypeId annotation = resolveType(scope, local->annotation);
|
||||||
|
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
||||||
|
}
|
||||||
|
|
||||||
varTypes.push_back(ty);
|
varTypes.push_back(ty);
|
||||||
scope->bindings[local] = ty;
|
scope->bindings[local] = ty;
|
||||||
}
|
}
|
||||||
|
@ -136,14 +136,14 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatLocal* local)
|
||||||
{
|
{
|
||||||
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
|
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
|
||||||
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
|
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
|
||||||
addConstraint(scope, PackSubtypeConstraint{exprPack, tailPack}, local->location);
|
addConstraint(scope, PackSubtypeConstraint{exprPack, tailPack});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId exprType = check(scope, local->values.data[i]);
|
TypeId exprType = check(scope, local->values.data[i]);
|
||||||
if (i < varTypes.size())
|
if (i < varTypes.size())
|
||||||
addConstraint(scope, SubtypeConstraint{varTypes[i], exprType}, local->vars.data[i]->location);
|
addConstraint(scope, SubtypeConstraint{varTypes[i], exprType});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatLocalFunction* function
|
||||||
|
|
||||||
checkFunctionBody(innerScope, function->func);
|
checkFunctionBody(innerScope, function->func);
|
||||||
|
|
||||||
std::unique_ptr<Constraint> c{new Constraint{GeneralizationConstraint{functionType, actualFunctionType, innerScope}, function->location}};
|
std::unique_ptr<Constraint> c{new Constraint{GeneralizationConstraint{functionType, actualFunctionType, innerScope}}};
|
||||||
addConstraints(c.get(), innerScope);
|
addConstraints(c.get(), innerScope);
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
|
@ -240,7 +240,7 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatFunction* function)
|
||||||
|
|
||||||
checkFunctionBody(innerScope, function->func);
|
checkFunctionBody(innerScope, function->func);
|
||||||
|
|
||||||
std::unique_ptr<Constraint> c{new Constraint{GeneralizationConstraint{functionType, actualFunctionType, innerScope}, function->location}};
|
std::unique_ptr<Constraint> c{new Constraint{GeneralizationConstraint{functionType, actualFunctionType, innerScope}}};
|
||||||
addConstraints(c.get(), innerScope);
|
addConstraints(c.get(), innerScope);
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
|
@ -251,13 +251,26 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatReturn* ret)
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
TypePackId exprTypes = checkPack(scope, ret->list);
|
TypePackId exprTypes = checkPack(scope, ret->list);
|
||||||
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType}, ret->location);
|
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(Scope2* scope, AstStatBlock* block)
|
void ConstraintGraphBuilder::visit(Scope2* scope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
|
// In order to enable mutually-recursive type aliases, we need to
|
||||||
|
// populate the type bindings before we actually check any of the
|
||||||
|
// alias statements. Since we're not ready to actually resolve
|
||||||
|
// any of the annotations, we just use a fresh type for now.
|
||||||
|
for (AstStat* stat : block->body)
|
||||||
|
{
|
||||||
|
if (auto alias = stat->as<AstStatTypeAlias>())
|
||||||
|
{
|
||||||
|
TypeId initialType = freshType(scope);
|
||||||
|
scope->typeBindings[alias->name.value] = initialType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (AstStat* stat : block->body)
|
for (AstStat* stat : block->body)
|
||||||
visit(scope, stat);
|
visit(scope, stat);
|
||||||
}
|
}
|
||||||
|
@ -267,7 +280,7 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatAssign* assign)
|
||||||
TypePackId varPackId = checkExprList(scope, assign->vars);
|
TypePackId varPackId = checkExprList(scope, assign->vars);
|
||||||
TypePackId valuePack = checkPack(scope, assign->values);
|
TypePackId valuePack = checkPack(scope, assign->values);
|
||||||
|
|
||||||
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId}, assign->location);
|
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGraphBuilder::visit(Scope2* scope, AstStatIf* ifStatement)
|
void ConstraintGraphBuilder::visit(Scope2* scope, AstStatIf* ifStatement)
|
||||||
|
@ -284,6 +297,28 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatIf* ifStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConstraintGraphBuilder::visit(Scope2* scope, AstStatTypeAlias* alias)
|
||||||
|
{
|
||||||
|
// TODO: Exported type aliases
|
||||||
|
// TODO: Generic type aliases
|
||||||
|
|
||||||
|
auto it = scope->typeBindings.find(alias->name.value);
|
||||||
|
// This should always be here since we do a separate pass over the
|
||||||
|
// AST to set up typeBindings. If it's not, we've somehow skipped
|
||||||
|
// this alias in that first pass.
|
||||||
|
LUAU_ASSERT(it != scope->typeBindings.end());
|
||||||
|
|
||||||
|
TypeId ty = resolveType(scope, alias->type);
|
||||||
|
|
||||||
|
// Rather than using a subtype constraint, we instead directly bind
|
||||||
|
// the free type we generated in the first pass to the resolved type.
|
||||||
|
// This prevents a case where you could cause another constraint to
|
||||||
|
// bind the free alias type to an unrelated type, causing havoc.
|
||||||
|
asMutable(it->second)->ty.emplace<BoundTypeVar>(ty);
|
||||||
|
|
||||||
|
addConstraint(scope, NameConstraint{ty, alias->name.value});
|
||||||
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGraphBuilder::checkPack(Scope2* scope, AstArray<AstExpr*> exprs)
|
TypePackId ConstraintGraphBuilder::checkPack(Scope2* scope, AstArray<AstExpr*> exprs)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
@ -350,13 +385,13 @@ TypePackId ConstraintGraphBuilder::checkPack(Scope2* scope, AstExpr* expr)
|
||||||
astOriginalCallTypes[call->func] = fnType;
|
astOriginalCallTypes[call->func] = fnType;
|
||||||
|
|
||||||
TypeId instantiatedType = freshType(scope);
|
TypeId instantiatedType = freshType(scope);
|
||||||
addConstraint(scope, InstantiationConstraint{instantiatedType, fnType}, expr->location);
|
addConstraint(scope, InstantiationConstraint{instantiatedType, fnType});
|
||||||
|
|
||||||
TypePackId rets = freshTypePack(scope);
|
TypePackId rets = freshTypePack(scope);
|
||||||
FunctionTypeVar ftv(arena->addTypePack(TypePack{args, {}}), rets);
|
FunctionTypeVar ftv(arena->addTypePack(TypePack{args, {}}), rets);
|
||||||
TypeId inferredFnType = arena->addType(ftv);
|
TypeId inferredFnType = arena->addType(ftv);
|
||||||
|
|
||||||
addConstraint(scope, SubtypeConstraint{inferredFnType, instantiatedType}, expr->location);
|
addConstraint(scope, SubtypeConstraint{inferredFnType, instantiatedType});
|
||||||
result = rets;
|
result = rets;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -413,7 +448,7 @@ TypeId ConstraintGraphBuilder::check(Scope2* scope, AstExpr* expr)
|
||||||
TypePack onePack{{typeResult}, freshTypePack(scope)};
|
TypePack onePack{{typeResult}, freshTypePack(scope)};
|
||||||
TypePackId oneTypePack = arena->addTypePack(std::move(onePack));
|
TypePackId oneTypePack = arena->addTypePack(std::move(onePack));
|
||||||
|
|
||||||
addConstraint(scope, PackSubtypeConstraint{packResult, oneTypePack}, expr->location);
|
addConstraint(scope, PackSubtypeConstraint{packResult, oneTypePack});
|
||||||
|
|
||||||
return typeResult;
|
return typeResult;
|
||||||
}
|
}
|
||||||
|
@ -454,7 +489,7 @@ TypeId ConstraintGraphBuilder::check(Scope2* scope, AstExprIndexName* indexName)
|
||||||
|
|
||||||
TypeId expectedTableType = arena->addType(std::move(ttv));
|
TypeId expectedTableType = arena->addType(std::move(ttv));
|
||||||
|
|
||||||
addConstraint(scope, SubtypeConstraint{obj, expectedTableType}, indexName->location);
|
addConstraint(scope, SubtypeConstraint{obj, expectedTableType});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -465,8 +500,7 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||||
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
||||||
LUAU_ASSERT(ttv);
|
LUAU_ASSERT(ttv);
|
||||||
|
|
||||||
auto createIndexer = [this, scope, ttv](
|
auto createIndexer = [this, scope, ttv](TypeId currentIndexType, TypeId currentResultType) {
|
||||||
TypeId currentIndexType, TypeId currentResultType, Location itemLocation, std::optional<Location> keyLocation) {
|
|
||||||
if (!ttv->indexer)
|
if (!ttv->indexer)
|
||||||
{
|
{
|
||||||
TypeId indexType = this->freshType(scope);
|
TypeId indexType = this->freshType(scope);
|
||||||
|
@ -474,8 +508,8 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||||
ttv->indexer = TableIndexer{indexType, resultType};
|
ttv->indexer = TableIndexer{indexType, resultType};
|
||||||
}
|
}
|
||||||
|
|
||||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexType, currentIndexType}, keyLocation ? *keyLocation : itemLocation);
|
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
|
||||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType}, itemLocation);
|
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const AstExprTable::Item& item : expr->items)
|
for (const AstExprTable::Item& item : expr->items)
|
||||||
|
@ -495,13 +529,13 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
createIndexer(keyTy, itemTy, item.value->location, item.key->location);
|
createIndexer(keyTy, itemTy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId numberType = singletonTypes.numberType;
|
TypeId numberType = singletonTypes.numberType;
|
||||||
createIndexer(numberType, itemTy, item.value->location, std::nullopt);
|
createIndexer(numberType, itemTy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,15 +548,29 @@ std::pair<TypeId, Scope2*> ConstraintGraphBuilder::checkFunctionSignature(Scope2
|
||||||
TypePackId returnType = freshTypePack(innerScope);
|
TypePackId returnType = freshTypePack(innerScope);
|
||||||
innerScope->returnType = returnType;
|
innerScope->returnType = returnType;
|
||||||
|
|
||||||
|
if (fn->returnAnnotation)
|
||||||
|
{
|
||||||
|
TypePackId annotatedRetType = resolveTypePack(innerScope, *fn->returnAnnotation);
|
||||||
|
addConstraint(innerScope, PackSubtypeConstraint{returnType, annotatedRetType});
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<TypeId> argTypes;
|
std::vector<TypeId> argTypes;
|
||||||
|
|
||||||
for (AstLocal* local : fn->args)
|
for (AstLocal* local : fn->args)
|
||||||
{
|
{
|
||||||
TypeId t = freshType(innerScope);
|
TypeId t = freshType(innerScope);
|
||||||
argTypes.push_back(t);
|
argTypes.push_back(t);
|
||||||
innerScope->bindings[local] = t; // TODO annotations
|
innerScope->bindings[local] = t;
|
||||||
|
|
||||||
|
if (local->annotation)
|
||||||
|
{
|
||||||
|
TypeId argAnnotation = resolveType(innerScope, local->annotation);
|
||||||
|
addConstraint(innerScope, SubtypeConstraint{t, argAnnotation});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Vararg annotation.
|
||||||
|
|
||||||
FunctionTypeVar actualFunction{arena->addTypePack(argTypes), returnType};
|
FunctionTypeVar actualFunction{arena->addTypePack(argTypes), returnType};
|
||||||
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
|
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
|
||||||
LUAU_ASSERT(actualFunctionType);
|
LUAU_ASSERT(actualFunctionType);
|
||||||
|
@ -541,10 +589,171 @@ void ConstraintGraphBuilder::checkFunctionBody(Scope2* scope, AstExprFunction* f
|
||||||
if (nullptr != getFallthrough(fn->body))
|
if (nullptr != getFallthrough(fn->body))
|
||||||
{
|
{
|
||||||
TypePackId empty = arena->addTypePack({}); // TODO we could have CSG retain one of these forever
|
TypePackId empty = arena->addTypePack({}); // TODO we could have CSG retain one of these forever
|
||||||
addConstraint(scope, PackSubtypeConstraint{scope->returnType, empty}, fn->body->location);
|
addConstraint(scope, PackSubtypeConstraint{scope->returnType, empty});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeId ConstraintGraphBuilder::resolveType(Scope2* scope, AstType* ty)
|
||||||
|
{
|
||||||
|
TypeId result = nullptr;
|
||||||
|
|
||||||
|
if (auto ref = ty->as<AstTypeReference>())
|
||||||
|
{
|
||||||
|
// TODO: Support imported types w/ require tracing.
|
||||||
|
// TODO: Support generic type references.
|
||||||
|
LUAU_ASSERT(!ref->prefix);
|
||||||
|
LUAU_ASSERT(!ref->hasParameterList);
|
||||||
|
|
||||||
|
// TODO: If it doesn't exist, should we introduce a free binding?
|
||||||
|
// This is probably important for handling type aliases.
|
||||||
|
result = scope->lookupTypeBinding(ref->name.value).value_or(singletonTypes.errorRecoveryType());
|
||||||
|
}
|
||||||
|
else if (auto tab = ty->as<AstTypeTable>())
|
||||||
|
{
|
||||||
|
TableTypeVar::Props props;
|
||||||
|
std::optional<TableIndexer> indexer;
|
||||||
|
|
||||||
|
for (const AstTableProp& prop : tab->props)
|
||||||
|
{
|
||||||
|
std::string name = prop.name.value;
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
TypeId propTy = resolveType(scope, prop.type);
|
||||||
|
// TODO: Fill in location.
|
||||||
|
props[name] = {propTy};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tab->indexer)
|
||||||
|
{
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
indexer = TableIndexer{
|
||||||
|
resolveType(scope, tab->indexer->indexType),
|
||||||
|
resolveType(scope, tab->indexer->resultType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove TypeLevel{} here, we don't need it.
|
||||||
|
result = arena->addType(TableTypeVar{props, indexer, TypeLevel{}, TableState::Sealed});
|
||||||
|
}
|
||||||
|
else if (auto fn = ty->as<AstTypeFunction>())
|
||||||
|
{
|
||||||
|
// TODO: Generic functions.
|
||||||
|
// TODO: Scope (though it may not be needed).
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
TypePackId argTypes = resolveTypePack(scope, fn->argTypes);
|
||||||
|
TypePackId returnTypes = resolveTypePack(scope, fn->returnTypes);
|
||||||
|
|
||||||
|
// TODO: Is this the right constructor to use?
|
||||||
|
result = arena->addType(FunctionTypeVar{argTypes, returnTypes});
|
||||||
|
|
||||||
|
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(result);
|
||||||
|
ftv->argNames.reserve(fn->argNames.size);
|
||||||
|
for (const auto& el : fn->argNames)
|
||||||
|
{
|
||||||
|
if (el)
|
||||||
|
{
|
||||||
|
const auto& [name, location] = *el;
|
||||||
|
ftv->argNames.push_back(FunctionArgument{name.value, location});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ftv->argNames.push_back(std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto tof = ty->as<AstTypeTypeof>())
|
||||||
|
{
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
TypeId exprType = check(scope, tof->expr);
|
||||||
|
result = exprType;
|
||||||
|
}
|
||||||
|
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
||||||
|
{
|
||||||
|
std::vector<TypeId> parts;
|
||||||
|
for (AstType* part : unionAnnotation->types)
|
||||||
|
{
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
parts.push_back(resolveType(scope, part));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = arena->addType(UnionTypeVar{parts});
|
||||||
|
}
|
||||||
|
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
||||||
|
{
|
||||||
|
std::vector<TypeId> parts;
|
||||||
|
for (AstType* part : intersectionAnnotation->types)
|
||||||
|
{
|
||||||
|
// TODO: Recursion limit.
|
||||||
|
parts.push_back(resolveType(scope, part));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = arena->addType(IntersectionTypeVar{parts});
|
||||||
|
}
|
||||||
|
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
|
||||||
|
{
|
||||||
|
result = arena->addType(SingletonTypeVar(BooleanSingleton{boolAnnotation->value}));
|
||||||
|
}
|
||||||
|
else if (auto stringAnnotation = ty->as<AstTypeSingletonString>())
|
||||||
|
{
|
||||||
|
result = arena->addType(SingletonTypeVar(StringSingleton{std::string(stringAnnotation->value.data, stringAnnotation->value.size)}));
|
||||||
|
}
|
||||||
|
else if (ty->is<AstTypeError>())
|
||||||
|
{
|
||||||
|
result = singletonTypes.errorRecoveryType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(0);
|
||||||
|
result = singletonTypes.errorRecoveryType();
|
||||||
|
}
|
||||||
|
|
||||||
|
astResolvedTypes[ty] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePackId ConstraintGraphBuilder::resolveTypePack(Scope2* scope, AstTypePack* tp)
|
||||||
|
{
|
||||||
|
TypePackId result;
|
||||||
|
if (auto expl = tp->as<AstTypePackExplicit>())
|
||||||
|
{
|
||||||
|
result = resolveTypePack(scope, expl->typeList);
|
||||||
|
}
|
||||||
|
else if (auto var = tp->as<AstTypePackVariadic>())
|
||||||
|
{
|
||||||
|
TypeId ty = resolveType(scope, var->variadicType);
|
||||||
|
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
|
||||||
|
}
|
||||||
|
else if (auto gen = tp->as<AstTypePackGeneric>())
|
||||||
|
{
|
||||||
|
result = arena->addTypePack(TypePackVar{GenericTypePack{scope, gen->genericName.value}});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(0);
|
||||||
|
result = singletonTypes.errorRecoveryTypePack();
|
||||||
|
}
|
||||||
|
|
||||||
|
astResolvedTypePacks[tp] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePackId ConstraintGraphBuilder::resolveTypePack(Scope2* scope, const AstTypeList& list)
|
||||||
|
{
|
||||||
|
std::vector<TypeId> head;
|
||||||
|
|
||||||
|
for (AstType* headTy : list.types)
|
||||||
|
{
|
||||||
|
head.push_back(resolveType(scope, headTy));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypePackId> tail = std::nullopt;
|
||||||
|
if (list.tailType)
|
||||||
|
{
|
||||||
|
tail = resolveTypePack(scope, list.tailType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arena->addTypePack(TypePack{head, tail});
|
||||||
|
}
|
||||||
|
|
||||||
void collectConstraints(std::vector<NotNull<Constraint>>& result, Scope2* scope)
|
void collectConstraints(std::vector<NotNull<Constraint>>& result, Scope2* scope)
|
||||||
{
|
{
|
||||||
for (const auto& c : scope->constraints)
|
for (const auto& c : scope->constraints)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "Luau/ConstraintSolver.h"
|
#include "Luau/ConstraintSolver.h"
|
||||||
#include "Luau/Instantiation.h"
|
#include "Luau/Instantiation.h"
|
||||||
|
#include "Luau/Location.h"
|
||||||
#include "Luau/Quantify.h"
|
#include "Luau/Quantify.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/Unifier.h"
|
#include "Luau/Unifier.h"
|
||||||
|
@ -179,6 +180,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
||||||
success = tryDispatch(*gc, constraint, force);
|
success = tryDispatch(*gc, constraint, force);
|
||||||
else if (auto ic = get<InstantiationConstraint>(*constraint))
|
else if (auto ic = get<InstantiationConstraint>(*constraint))
|
||||||
success = tryDispatch(*ic, constraint, force);
|
success = tryDispatch(*ic, constraint, force);
|
||||||
|
else if (auto nc = get<NameConstraint>(*constraint))
|
||||||
|
success = tryDispatch(*nc, constraint);
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(0);
|
LUAU_ASSERT(0);
|
||||||
|
|
||||||
|
@ -197,7 +200,7 @@ bool ConstraintSolver::tryDispatch(const SubtypeConstraint& c, NotNull<const Con
|
||||||
else if (isBlocked(c.superType))
|
else if (isBlocked(c.superType))
|
||||||
return block(c.superType, constraint);
|
return block(c.superType, constraint);
|
||||||
|
|
||||||
unify(c.subType, c.superType, constraint->location);
|
unify(c.subType, c.superType);
|
||||||
|
|
||||||
unblock(c.subType);
|
unblock(c.subType);
|
||||||
unblock(c.superType);
|
unblock(c.superType);
|
||||||
|
@ -207,7 +210,7 @@ bool ConstraintSolver::tryDispatch(const SubtypeConstraint& c, NotNull<const Con
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force)
|
bool ConstraintSolver::tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force)
|
||||||
{
|
{
|
||||||
unify(c.subPack, c.superPack, constraint->location);
|
unify(c.subPack, c.superPack);
|
||||||
unblock(c.subPack);
|
unblock(c.subPack);
|
||||||
unblock(c.superPack);
|
unblock(c.superPack);
|
||||||
|
|
||||||
|
@ -222,7 +225,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
if (isBlocked(c.generalizedType))
|
if (isBlocked(c.generalizedType))
|
||||||
asMutable(c.generalizedType)->ty.emplace<BoundTypeVar>(c.sourceType);
|
asMutable(c.generalizedType)->ty.emplace<BoundTypeVar>(c.sourceType);
|
||||||
else
|
else
|
||||||
unify(c.generalizedType, c.sourceType, constraint->location);
|
unify(c.generalizedType, c.sourceType);
|
||||||
|
|
||||||
TypeId generalized = quantify(arena, c.sourceType, c.scope);
|
TypeId generalized = quantify(arena, c.sourceType, c.scope);
|
||||||
*asMutable(c.sourceType) = *generalized;
|
*asMutable(c.sourceType) = *generalized;
|
||||||
|
@ -243,12 +246,28 @@ bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<con
|
||||||
std::optional<TypeId> instantiated = inst.substitute(c.superType);
|
std::optional<TypeId> instantiated = inst.substitute(c.superType);
|
||||||
LUAU_ASSERT(instantiated); // TODO FIXME HANDLE THIS
|
LUAU_ASSERT(instantiated); // TODO FIXME HANDLE THIS
|
||||||
|
|
||||||
unify(c.subType, *instantiated, constraint->location);
|
unify(c.subType, *instantiated);
|
||||||
unblock(c.subType);
|
unblock(c.subType);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint)
|
||||||
|
{
|
||||||
|
if (isBlocked(c.namedType))
|
||||||
|
return block(c.namedType, constraint);
|
||||||
|
|
||||||
|
TypeId target = follow(c.namedType);
|
||||||
|
if (TableTypeVar* ttv = getMutable<TableTypeVar>(target))
|
||||||
|
ttv->name = c.name;
|
||||||
|
else if (MetatableTypeVar* mtv = getMutable<MetatableTypeVar>(target))
|
||||||
|
mtv->syntheticName = c.name;
|
||||||
|
else
|
||||||
|
return block(c.namedType, constraint);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint)
|
void ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint)
|
||||||
{
|
{
|
||||||
blocked[target].push_back(constraint);
|
blocked[target].push_back(constraint);
|
||||||
|
@ -321,19 +340,19 @@ bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
|
||||||
return blockedIt != blockedConstraints.end() && blockedIt->second > 0;
|
return blockedIt != blockedConstraints.end() && blockedIt->second > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::unify(TypeId subType, TypeId superType, Location location)
|
void ConstraintSolver::unify(TypeId subType, TypeId superType)
|
||||||
{
|
{
|
||||||
UnifierSharedState sharedState{&iceReporter};
|
UnifierSharedState sharedState{&iceReporter};
|
||||||
Unifier u{arena, Mode::Strict, location, Covariant, sharedState};
|
Unifier u{arena, Mode::Strict, Location{}, Covariant, sharedState};
|
||||||
|
|
||||||
u.tryUnify(subType, superType);
|
u.tryUnify(subType, superType);
|
||||||
u.log.commit();
|
u.log.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack, Location location)
|
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack)
|
||||||
{
|
{
|
||||||
UnifierSharedState sharedState{&iceReporter};
|
UnifierSharedState sharedState{&iceReporter};
|
||||||
Unifier u{arena, Mode::Strict, location, Covariant, sharedState};
|
Unifier u{arena, Mode::Strict, Location{}, Covariant, sharedState};
|
||||||
|
|
||||||
u.tryUnify(subPack, superPack);
|
u.tryUnify(subPack, superPack);
|
||||||
u.log.commit();
|
u.log.commit();
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeMismatchModuleNameResolution, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUseInternalCompilerErrorException, false)
|
||||||
|
|
||||||
static std::string wrongNumberOfArgsString(size_t expectedCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false)
|
static std::string wrongNumberOfArgsString(size_t expectedCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false)
|
||||||
{
|
{
|
||||||
std::string s = "expects ";
|
std::string s = "expects ";
|
||||||
|
@ -49,6 +52,8 @@ namespace Luau
|
||||||
|
|
||||||
struct ErrorConverter
|
struct ErrorConverter
|
||||||
{
|
{
|
||||||
|
FileResolver* fileResolver = nullptr;
|
||||||
|
|
||||||
std::string operator()(const Luau::TypeMismatch& tm) const
|
std::string operator()(const Luau::TypeMismatch& tm) const
|
||||||
{
|
{
|
||||||
std::string givenTypeName = Luau::toString(tm.givenType);
|
std::string givenTypeName = Luau::toString(tm.givenType);
|
||||||
|
@ -62,8 +67,18 @@ struct ErrorConverter
|
||||||
{
|
{
|
||||||
if (auto wantedDefinitionModule = getDefinitionModuleName(tm.wantedType))
|
if (auto wantedDefinitionModule = getDefinitionModuleName(tm.wantedType))
|
||||||
{
|
{
|
||||||
result = "Type '" + givenTypeName + "' from '" + *givenDefinitionModule + "' could not be converted into '" + wantedTypeName +
|
if (FFlag::LuauTypeMismatchModuleNameResolution && fileResolver != nullptr)
|
||||||
"' from '" + *wantedDefinitionModule + "'";
|
{
|
||||||
|
std::string givenModuleName = fileResolver->getHumanReadableModuleName(*givenDefinitionModule);
|
||||||
|
std::string wantedModuleName = fileResolver->getHumanReadableModuleName(*wantedDefinitionModule);
|
||||||
|
result = "Type '" + givenTypeName + "' from '" + givenModuleName + "' could not be converted into '" + wantedTypeName +
|
||||||
|
"' from '" + wantedModuleName + "'";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = "Type '" + givenTypeName + "' from '" + *givenDefinitionModule + "' could not be converted into '" + wantedTypeName +
|
||||||
|
"' from '" + *wantedDefinitionModule + "'";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +93,14 @@ struct ErrorConverter
|
||||||
if (!tm.reason.empty())
|
if (!tm.reason.empty())
|
||||||
result += tm.reason + " ";
|
result += tm.reason + " ";
|
||||||
|
|
||||||
result += Luau::toString(*tm.error);
|
if (FFlag::LuauTypeMismatchModuleNameResolution)
|
||||||
|
{
|
||||||
|
result += Luau::toString(*tm.error, TypeErrorToStringOptions{fileResolver});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += Luau::toString(*tm.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!tm.reason.empty())
|
else if (!tm.reason.empty())
|
||||||
{
|
{
|
||||||
|
@ -280,6 +302,11 @@ struct ErrorConverter
|
||||||
return e.message;
|
return e.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string operator()(const Luau::InternalError& e) const
|
||||||
|
{
|
||||||
|
return e.message;
|
||||||
|
}
|
||||||
|
|
||||||
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
||||||
{
|
{
|
||||||
return "Cannot call non-function " + toString(e.ty);
|
return "Cannot call non-function " + toString(e.ty);
|
||||||
|
@ -598,6 +625,11 @@ bool GenericError::operator==(const GenericError& rhs) const
|
||||||
return message == rhs.message;
|
return message == rhs.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InternalError::operator==(const InternalError& rhs) const
|
||||||
|
{
|
||||||
|
return message == rhs.message;
|
||||||
|
}
|
||||||
|
|
||||||
bool CannotCallNonFunction::operator==(const CannotCallNonFunction& rhs) const
|
bool CannotCallNonFunction::operator==(const CannotCallNonFunction& rhs) const
|
||||||
{
|
{
|
||||||
return ty == rhs.ty;
|
return ty == rhs.ty;
|
||||||
|
@ -685,7 +717,12 @@ bool TypesAreUnrelated::operator==(const TypesAreUnrelated& rhs) const
|
||||||
|
|
||||||
std::string toString(const TypeError& error)
|
std::string toString(const TypeError& error)
|
||||||
{
|
{
|
||||||
ErrorConverter converter;
|
return toString(error, TypeErrorToStringOptions{});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(const TypeError& error, TypeErrorToStringOptions options)
|
||||||
|
{
|
||||||
|
ErrorConverter converter{options.fileResolver};
|
||||||
return Luau::visit(converter, error.data);
|
return Luau::visit(converter, error.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,6 +810,9 @@ void copyError(T& e, TypeArena& destArena, CloneState cloneState)
|
||||||
else if constexpr (std::is_same_v<T, GenericError>)
|
else if constexpr (std::is_same_v<T, GenericError>)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, InternalError>)
|
||||||
|
{
|
||||||
|
}
|
||||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||||
{
|
{
|
||||||
e.ty = clone(e.ty);
|
e.ty = clone(e.ty);
|
||||||
|
@ -847,22 +887,51 @@ void copyErrors(ErrorVec& errors, TypeArena& destArena)
|
||||||
|
|
||||||
void InternalErrorReporter::ice(const std::string& message, const Location& location)
|
void InternalErrorReporter::ice(const std::string& message, const Location& location)
|
||||||
{
|
{
|
||||||
std::runtime_error error("Internal error in " + moduleName + " at " + toString(location) + ": " + message);
|
if (FFlag::LuauUseInternalCompilerErrorException)
|
||||||
|
{
|
||||||
|
InternalCompilerError error(message, moduleName, location);
|
||||||
|
|
||||||
if (onInternalError)
|
if (onInternalError)
|
||||||
onInternalError(error.what());
|
onInternalError(error.what());
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::runtime_error error("Internal error in " + moduleName + " at " + toString(location) + ": " + message);
|
||||||
|
|
||||||
|
if (onInternalError)
|
||||||
|
onInternalError(error.what());
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::ice(const std::string& message)
|
void InternalErrorReporter::ice(const std::string& message)
|
||||||
{
|
{
|
||||||
std::runtime_error error("Internal error in " + moduleName + ": " + message);
|
if (FFlag::LuauUseInternalCompilerErrorException)
|
||||||
|
{
|
||||||
|
InternalCompilerError error(message, moduleName);
|
||||||
|
|
||||||
if (onInternalError)
|
if (onInternalError)
|
||||||
onInternalError(error.what());
|
onInternalError(error.what());
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::runtime_error error("Internal error in " + moduleName + ": " + message);
|
||||||
|
|
||||||
|
if (onInternalError)
|
||||||
|
onInternalError(error.what());
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* InternalCompilerError::what() const throw()
|
||||||
|
{
|
||||||
|
return this->message.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -801,6 +801,8 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
|
||||||
result->astTypes = std::move(cgb.astTypes);
|
result->astTypes = std::move(cgb.astTypes);
|
||||||
result->astTypePacks = std::move(cgb.astTypePacks);
|
result->astTypePacks = std::move(cgb.astTypePacks);
|
||||||
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
||||||
|
result->astResolvedTypes = std::move(cgb.astResolvedTypes);
|
||||||
|
result->astResolvedTypePacks = std::move(cgb.astResolvedTypePacks);
|
||||||
|
|
||||||
result->clonePublicInterface(iceHandler);
|
result->clonePublicInterface(iceHandler);
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, GenericError>)
|
else if constexpr (std::is_same_v<T, GenericError>)
|
||||||
stream << "GenericError { " << err.message << " }";
|
stream << "GenericError { " << err.message << " }";
|
||||||
|
else if constexpr (std::is_same_v<T, InternalError>)
|
||||||
|
stream << "InternalError { " << err.message << " }";
|
||||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||||
stream << "CannotCallNonFunction { " << toString(err.ty) << " }";
|
stream << "CannotCallNonFunction { " << toString(err.ty) << " }";
|
||||||
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
#include "Luau/VisitTypeVar.h"
|
#include "Luau/VisitTypeVar.h"
|
||||||
#include "Luau/ConstraintGraphBuilder.h" // FIXME: For Scope2 TODO pull out into its own header
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
#include "Luau/Quantify.h"
|
#include "Luau/Quantify.h"
|
||||||
|
|
||||||
#include "Luau/ConstraintGraphBuilder.h" // TODO for Scope2; move to separate header
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TxnLog.h"
|
|
||||||
#include "Luau/Substitution.h"
|
#include "Luau/Substitution.h"
|
||||||
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/VisitTypeVar.h"
|
#include "Luau/VisitTypeVar.h"
|
||||||
#include "Luau/ConstraintGraphBuilder.h" // TODO for Scope2; move to separate header
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauAlwaysQuantify);
|
LUAU_FASTFLAG(LuauAlwaysQuantify);
|
||||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||||
|
@ -177,8 +176,6 @@ void quantify(TypeId ty, TypeLevel level)
|
||||||
|
|
||||||
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
||||||
ftv->hasNoGenerics = true;
|
ftv->hasNoGenerics = true;
|
||||||
|
|
||||||
ftv->generalized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void quantify(TypeId ty, Scope2* scope)
|
void quantify(TypeId ty, Scope2* scope)
|
||||||
|
@ -201,8 +198,6 @@ void quantify(TypeId ty, Scope2* scope)
|
||||||
|
|
||||||
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
||||||
ftv->hasNoGenerics = true;
|
ftv->hasNoGenerics = true;
|
||||||
|
|
||||||
ftv->generalized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PureQuantifier : Substitution
|
struct PureQuantifier : Substitution
|
||||||
|
|
|
@ -121,4 +121,36 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<TypeId> Scope2::lookup(Symbol sym)
|
||||||
|
{
|
||||||
|
Scope2* s = this;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto it = s->bindings.find(sym);
|
||||||
|
if (it != s->bindings.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
if (s->parent)
|
||||||
|
s = s->parent;
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypeId> Scope2::lookupTypeBinding(const Name& name)
|
||||||
|
{
|
||||||
|
Scope2* s = this;
|
||||||
|
while (s)
|
||||||
|
{
|
||||||
|
auto it = s->typeBindings.find(name);
|
||||||
|
if (it != s->typeBindings.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
s = s->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -1411,6 +1411,12 @@ std::string toString(const Constraint& c, ToStringOptions& opts)
|
||||||
opts.nameMap = std::move(superStr.nameMap);
|
opts.nameMap = std::move(superStr.nameMap);
|
||||||
return subStr.name + " ~ inst " + superStr.name;
|
return subStr.name + " ~ inst " + superStr.name;
|
||||||
}
|
}
|
||||||
|
else if (const NameConstraint* nc = Luau::get<NameConstraint>(c))
|
||||||
|
{
|
||||||
|
ToStringResult namedStr = toStringDetailed(nc->namedType, opts);
|
||||||
|
opts.nameMap = std::move(namedStr.nameMap);
|
||||||
|
return "@name(" + namedStr.name + ") = " + nc->name;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include "Luau/AstQuery.h"
|
#include "Luau/AstQuery.h"
|
||||||
#include "Luau/Clone.h"
|
#include "Luau/Clone.h"
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
|
#include "Luau/ConstraintGraphBuilder.h" // FIXME move Scope2 into its own header
|
||||||
|
#include "Luau/Unifier.h"
|
||||||
|
#include "Luau/ToString.h"
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -39,6 +42,104 @@ struct TypeChecker2 : public AstVisitor
|
||||||
return follow(*ty);
|
return follow(*ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeId lookupAnnotation(AstType* annotation)
|
||||||
|
{
|
||||||
|
TypeId* ty = module->astResolvedTypes.find(annotation);
|
||||||
|
LUAU_ASSERT(ty);
|
||||||
|
return follow(*ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
|
||||||
|
{
|
||||||
|
std::vector<TypeId> head;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < exprs.size - 1; ++i)
|
||||||
|
{
|
||||||
|
head.push_back(lookupType(exprs.data[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePackId tail = lookupPack(exprs.data[exprs.size - 1]);
|
||||||
|
return arena.addTypePack(TypePack{head, tail});
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope2* findInnermostScope(Location location)
|
||||||
|
{
|
||||||
|
Scope2* bestScope = module->getModuleScope2();
|
||||||
|
Location bestLocation = module->scope2s[0].first;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < module->scope2s.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& [scopeBounds, scope] = module->scope2s[i];
|
||||||
|
if (scopeBounds.encloses(location))
|
||||||
|
{
|
||||||
|
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
|
||||||
|
{
|
||||||
|
bestScope = scope.get();
|
||||||
|
bestLocation = scopeBounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Is this sound? This relies on the fact that scopes are inserted
|
||||||
|
// into the scope list in the order that they appear in the AST.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AstStatLocal* local) override
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < local->values.size; ++i)
|
||||||
|
{
|
||||||
|
AstExpr* value = local->values.data[i];
|
||||||
|
if (i == local->values.size - 1)
|
||||||
|
{
|
||||||
|
if (i < local->values.size)
|
||||||
|
{
|
||||||
|
TypePackId valueTypes = lookupPack(value);
|
||||||
|
auto it = begin(valueTypes);
|
||||||
|
for (size_t j = i; j < local->vars.size; ++j)
|
||||||
|
{
|
||||||
|
if (it == end(valueTypes))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstLocal* var = local->vars.data[i];
|
||||||
|
if (var->annotation)
|
||||||
|
{
|
||||||
|
TypeId varType = lookupAnnotation(var->annotation);
|
||||||
|
if (!isSubtype(*it, varType, ice))
|
||||||
|
{
|
||||||
|
reportError(TypeMismatch{varType, *it}, value->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId valueType = lookupType(value);
|
||||||
|
AstLocal* var = local->vars.data[i];
|
||||||
|
|
||||||
|
if (var->annotation)
|
||||||
|
{
|
||||||
|
TypeId varType = lookupAnnotation(var->annotation);
|
||||||
|
if (!isSubtype(varType, valueType, ice))
|
||||||
|
{
|
||||||
|
reportError(TypeMismatch{varType, valueType}, value->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstStatAssign* assign) override
|
bool visit(AstStatAssign* assign) override
|
||||||
{
|
{
|
||||||
size_t count = std::min(assign->vars.size, assign->values.size);
|
size_t count = std::min(assign->vars.size, assign->values.size);
|
||||||
|
@ -62,6 +163,30 @@ struct TypeChecker2 : public AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstStatReturn* ret) override
|
||||||
|
{
|
||||||
|
Scope2* scope = findInnermostScope(ret->location);
|
||||||
|
TypePackId expectedRetType = scope->returnType;
|
||||||
|
|
||||||
|
TypeArena arena;
|
||||||
|
TypePackId actualRetType = reconstructPack(ret->list, arena);
|
||||||
|
|
||||||
|
UnifierSharedState sharedState{&ice};
|
||||||
|
Unifier u{&arena, Mode::Strict, ret->location, Covariant, sharedState};
|
||||||
|
u.anyIsTop = true;
|
||||||
|
|
||||||
|
u.tryUnify(actualRetType, expectedRetType);
|
||||||
|
const bool ok = u.errors.empty() && u.log.empty();
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
for (const TypeError& e : u.errors)
|
||||||
|
module->errors.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstExprCall* call) override
|
bool visit(AstExprCall* call) override
|
||||||
{
|
{
|
||||||
TypePackId expectedRetType = lookupPack(call);
|
TypePackId expectedRetType = lookupPack(call);
|
||||||
|
@ -91,6 +216,35 @@ struct TypeChecker2 : public AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstExprFunction* fn) override
|
||||||
|
{
|
||||||
|
TypeId inferredFnTy = lookupType(fn);
|
||||||
|
const FunctionTypeVar* inferredFtv = get<FunctionTypeVar>(inferredFnTy);
|
||||||
|
LUAU_ASSERT(inferredFtv);
|
||||||
|
|
||||||
|
auto argIt = begin(inferredFtv->argTypes);
|
||||||
|
for (const auto& arg : fn->args)
|
||||||
|
{
|
||||||
|
if (argIt == end(inferredFtv->argTypes))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (arg->annotation)
|
||||||
|
{
|
||||||
|
TypeId inferredArgTy = *argIt;
|
||||||
|
TypeId annotatedArgTy = lookupAnnotation(arg->annotation);
|
||||||
|
|
||||||
|
if (!isSubtype(annotatedArgTy, inferredArgTy, ice))
|
||||||
|
{
|
||||||
|
reportError(TypeMismatch{annotatedArgTy, inferredArgTy}, arg->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++argIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(AstExprIndexName* indexName) override
|
bool visit(AstExprIndexName* indexName) override
|
||||||
{
|
{
|
||||||
TypeId leftType = lookupType(indexName->expr);
|
TypeId leftType = lookupType(indexName->expr);
|
||||||
|
@ -144,6 +298,25 @@ struct TypeChecker2 : public AstVisitor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(AstType* ty) override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AstTypeReference* ty) override
|
||||||
|
{
|
||||||
|
Scope2* scope = findInnermostScope(ty->location);
|
||||||
|
|
||||||
|
// TODO: Imported types
|
||||||
|
// TODO: Generic types
|
||||||
|
if (!scope->lookupTypeBinding(ty->name.value))
|
||||||
|
{
|
||||||
|
reportError(UnknownSymbol{ty->name.value, UnknownSymbol::Context::Type}, ty->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void reportError(TypeErrorData&& data, const Location& location)
|
void reportError(TypeErrorData&& data, const Location& location)
|
||||||
{
|
{
|
||||||
module->errors.emplace_back(location, sourceModule->name, std::move(data));
|
module->errors.emplace_back(location, sourceModule->name, std::move(data));
|
||||||
|
|
|
@ -35,13 +35,9 @@ LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
|
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
|
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRecursionLimitException, false);
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauApplyTypeFunctionFix, false);
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
|
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
|
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
|
||||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||||
|
@ -275,22 +271,15 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan
|
||||||
|
|
||||||
ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope)
|
ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauRecursionLimitException)
|
try
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return checkWithoutRecursionCheck(module, mode, environmentScope);
|
|
||||||
}
|
|
||||||
catch (const RecursionLimitException&)
|
|
||||||
{
|
|
||||||
reportErrorCodeTooComplex(module.root->location);
|
|
||||||
return std::move(currentModule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return checkWithoutRecursionCheck(module, mode, environmentScope);
|
return checkWithoutRecursionCheck(module, mode, environmentScope);
|
||||||
}
|
}
|
||||||
|
catch (const RecursionLimitException&)
|
||||||
|
{
|
||||||
|
reportErrorCodeTooComplex(module.root->location);
|
||||||
|
return std::move(currentModule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope)
|
ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope)
|
||||||
|
@ -445,22 +434,15 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
|
||||||
reportErrorCodeTooComplex(block.location);
|
reportErrorCodeTooComplex(block.location);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (FFlag::LuauRecursionLimitException)
|
try
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
checkBlockWithoutRecursionCheck(scope, block);
|
|
||||||
}
|
|
||||||
catch (const RecursionLimitException&)
|
|
||||||
{
|
|
||||||
reportErrorCodeTooComplex(block.location);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
checkBlockWithoutRecursionCheck(scope, block);
|
checkBlockWithoutRecursionCheck(scope, block);
|
||||||
}
|
}
|
||||||
|
catch (const RecursionLimitException&)
|
||||||
|
{
|
||||||
|
reportErrorCodeTooComplex(block.location);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& block)
|
void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& block)
|
||||||
|
@ -1917,7 +1899,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
||||||
|
|
||||||
for (TypeId t : utv)
|
for (TypeId t : utv)
|
||||||
{
|
{
|
||||||
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit, "getIndexTypeForType unions");
|
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
// Not needed when we normalize types.
|
// Not needed when we normalize types.
|
||||||
if (get<AnyTypeVar>(follow(t)))
|
if (get<AnyTypeVar>(follow(t)))
|
||||||
|
@ -1967,7 +1949,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
||||||
|
|
||||||
for (TypeId t : itv->parts)
|
for (TypeId t : itv->parts)
|
||||||
{
|
{
|
||||||
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit, "getIndexTypeFromType intersections");
|
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, t, name, location, false))
|
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, t, name, location, false))
|
||||||
parts.push_back(*ty);
|
parts.push_back(*ty);
|
||||||
|
@ -2190,7 +2172,7 @@ TypeId TypeChecker::checkExprTable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TableState state = (expr.items.size == 0 || isNonstrictMode() || FFlag::LuauUnsealedTableLiteral) ? TableState::Unsealed : TableState::Sealed;
|
TableState state = TableState::Unsealed;
|
||||||
TableTypeVar table = TableTypeVar{std::move(props), indexer, scope->level, state};
|
TableTypeVar table = TableTypeVar{std::move(props), indexer, scope->level, state};
|
||||||
table.definitionModuleName = currentModuleName;
|
table.definitionModuleName = currentModuleName;
|
||||||
return addType(table);
|
return addType(table);
|
||||||
|
@ -5175,9 +5157,7 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack
|
||||||
|
|
||||||
bool ApplyTypeFunction::isDirty(TypeId ty)
|
bool ApplyTypeFunction::isDirty(TypeId ty)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauApplyTypeFunctionFix && typeArguments.count(ty))
|
if (typeArguments.count(ty))
|
||||||
return true;
|
|
||||||
else if (!FFlag::LuauApplyTypeFunctionFix && get<GenericTypeVar>(ty))
|
|
||||||
return true;
|
return true;
|
||||||
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
|
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
|
||||||
{
|
{
|
||||||
|
@ -5191,9 +5171,7 @@ bool ApplyTypeFunction::isDirty(TypeId ty)
|
||||||
|
|
||||||
bool ApplyTypeFunction::isDirty(TypePackId tp)
|
bool ApplyTypeFunction::isDirty(TypePackId tp)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauApplyTypeFunctionFix && typePackArguments.count(tp))
|
if (typePackArguments.count(tp))
|
||||||
return true;
|
|
||||||
else if (!FFlag::LuauApplyTypeFunctionFix && get<GenericTypePack>(tp))
|
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -5218,29 +5196,15 @@ bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
|
||||||
TypeId ApplyTypeFunction::clean(TypeId ty)
|
TypeId ApplyTypeFunction::clean(TypeId ty)
|
||||||
{
|
{
|
||||||
TypeId& arg = typeArguments[ty];
|
TypeId& arg = typeArguments[ty];
|
||||||
if (FFlag::LuauApplyTypeFunctionFix)
|
LUAU_ASSERT(arg);
|
||||||
{
|
return arg;
|
||||||
LUAU_ASSERT(arg);
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
else if (arg)
|
|
||||||
return arg;
|
|
||||||
else
|
|
||||||
return addType(FreeTypeVar{level});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId ApplyTypeFunction::clean(TypePackId tp)
|
TypePackId ApplyTypeFunction::clean(TypePackId tp)
|
||||||
{
|
{
|
||||||
TypePackId& arg = typePackArguments[tp];
|
TypePackId& arg = typePackArguments[tp];
|
||||||
if (FFlag::LuauApplyTypeFunctionFix)
|
LUAU_ASSERT(arg);
|
||||||
{
|
return arg;
|
||||||
LUAU_ASSERT(arg);
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
else if (arg)
|
|
||||||
return arg;
|
|
||||||
else
|
|
||||||
return addTypePack(FreeTypePack{level});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
|
||||||
|
@ -5273,7 +5237,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
|
||||||
|
|
||||||
TypeId target = follow(instantiated);
|
TypeId target = follow(instantiated);
|
||||||
bool needsClone = follow(tf.type) == target;
|
bool needsClone = follow(tf.type) == target;
|
||||||
bool shouldMutate = (!FFlag::LuauOnlyMutateInstantiatedTables || getTableType(tf.type));
|
bool shouldMutate = getTableType(tf.type);
|
||||||
TableTypeVar* ttv = getMutableTableType(target);
|
TableTypeVar* ttv = getMutableTableType(target);
|
||||||
|
|
||||||
if (shouldMutate && ttv && needsClone)
|
if (shouldMutate && ttv && needsClone)
|
||||||
|
|
|
@ -23,7 +23,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauSubtypingAddOptPropsToUnsealedTables)
|
|
||||||
LUAU_FASTFLAG(LuauNonCopyableTypeVarFields)
|
LUAU_FASTFLAG(LuauNonCopyableTypeVarFields)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -172,22 +171,15 @@ bool isString(TypeId ty)
|
||||||
// Returns true when ty is a supertype of string
|
// Returns true when ty is a supertype of string
|
||||||
bool maybeString(TypeId ty)
|
bool maybeString(TypeId ty)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauSubtypingAddOptPropsToUnsealedTables)
|
ty = follow(ty);
|
||||||
{
|
|
||||||
ty = follow(ty);
|
|
||||||
|
|
||||||
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty))
|
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (auto utv = get<UnionTypeVar>(ty))
|
if (auto utv = get<UnionTypeVar>(ty))
|
||||||
return std::any_of(begin(utv), end(utv), maybeString);
|
return std::any_of(begin(utv), end(utv), maybeString);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return isString(ty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isThread(TypeId ty)
|
bool isThread(TypeId ty)
|
||||||
|
@ -369,7 +361,7 @@ bool maybeSingleton(TypeId ty)
|
||||||
|
|
||||||
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
||||||
{
|
{
|
||||||
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit, "hasLength");
|
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,14 @@ Generic::Generic(TypeLevel level, const Name& name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Generic::Generic(Scope2* scope, const Name& name)
|
||||||
|
: index(++nextIndex)
|
||||||
|
, scope(scope)
|
||||||
|
, name(name)
|
||||||
|
, explicitName(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int Generic::nextIndex = 0;
|
int Generic::nextIndex = 0;
|
||||||
|
|
||||||
Error::Error()
|
Error::Error()
|
||||||
|
|
|
@ -17,11 +17,8 @@ LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
|
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeInferLowerBoundsIterationLimit, 2000);
|
LUAU_FASTINTVARIABLE(LuauTypeInferLowerBoundsIterationLimit, 2000);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance2, false);
|
|
||||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingAddOptPropsToUnsealedTables, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTxnLogRefreshFunctionPointers, false)
|
|
||||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -354,7 +351,7 @@ void Unifier::tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall, bool i
|
||||||
void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection)
|
void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection)
|
||||||
{
|
{
|
||||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "TypeId tryUnify_");
|
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
++sharedState.counters.iterationCount;
|
++sharedState.counters.iterationCount;
|
||||||
|
|
||||||
|
@ -983,7 +980,7 @@ void Unifier::tryUnify(TypePackId subTp, TypePackId superTp, bool isFunctionCall
|
||||||
void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall)
|
void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall)
|
||||||
{
|
{
|
||||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "TypePackId tryUnify_");
|
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
++sharedState.counters.iterationCount;
|
++sharedState.counters.iterationCount;
|
||||||
|
|
||||||
|
@ -1316,12 +1313,9 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
|
||||||
tryUnify_(subFunction->retTypes, superFunction->retTypes);
|
tryUnify_(subFunction->retTypes, superFunction->retTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauTxnLogRefreshFunctionPointers)
|
// Updating the log may have invalidated the function pointers
|
||||||
{
|
superFunction = log.getMutable<FunctionTypeVar>(superTy);
|
||||||
// Updating the log may have invalidated the function pointers
|
subFunction = log.getMutable<FunctionTypeVar>(subTy);
|
||||||
superFunction = log.getMutable<FunctionTypeVar>(superTy);
|
|
||||||
subFunction = log.getMutable<FunctionTypeVar>(subTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = context;
|
ctx = context;
|
||||||
|
|
||||||
|
@ -1360,9 +1354,6 @@ struct Resetter
|
||||||
|
|
||||||
void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauTableSubtypingVariance2)
|
|
||||||
return DEPRECATED_tryUnifyTables(subTy, superTy, isIntersection);
|
|
||||||
|
|
||||||
TableTypeVar* superTable = log.getMutable<TableTypeVar>(superTy);
|
TableTypeVar* superTable = log.getMutable<TableTypeVar>(superTy);
|
||||||
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
|
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
|
||||||
|
|
||||||
|
@ -1379,8 +1370,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||||
{
|
{
|
||||||
auto subIter = subTable->props.find(propName);
|
auto subIter = subTable->props.find(propName);
|
||||||
|
|
||||||
if (subIter == subTable->props.end() && (!FFlag::LuauSubtypingAddOptPropsToUnsealedTables || subTable->state == TableState::Unsealed) &&
|
if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type))
|
||||||
!isOptional(superProp.type))
|
|
||||||
missingProperties.push_back(propName);
|
missingProperties.push_back(propName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1398,7 +1388,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||||
{
|
{
|
||||||
auto superIter = superTable->props.find(propName);
|
auto superIter = superTable->props.find(propName);
|
||||||
|
|
||||||
if (superIter == superTable->props.end() && (FFlag::LuauSubtypingAddOptPropsToUnsealedTables || !isOptional(subProp.type)))
|
if (superIter == superTable->props.end())
|
||||||
extraProperties.push_back(propName);
|
extraProperties.push_back(propName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1443,7 +1433,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||||
if (innerState.errors.empty())
|
if (innerState.errors.empty())
|
||||||
log.concat(std::move(innerState.log));
|
log.concat(std::move(innerState.log));
|
||||||
}
|
}
|
||||||
else if ((!FFlag::LuauSubtypingAddOptPropsToUnsealedTables || subTable->state == TableState::Unsealed) && isOptional(prop.type))
|
else if (subTable->state == TableState::Unsealed && isOptional(prop.type))
|
||||||
// This is sound because unsealed table types are precise, so `{ p : T } <: { p : T, q : U? }`
|
// This is sound because unsealed table types are precise, so `{ p : T } <: { p : T, q : U? }`
|
||||||
// since if `t : { p : T }` then we are guaranteed that `t.q` is `nil`.
|
// since if `t : { p : T }` then we are guaranteed that `t.q` is `nil`.
|
||||||
// TODO: if the supertype is written to, the subtype may no longer be precise (alias analysis?)
|
// TODO: if the supertype is written to, the subtype may no longer be precise (alias analysis?)
|
||||||
|
@ -1512,9 +1502,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||||
else if (variance == Covariant)
|
else if (variance == Covariant)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else if (!FFlag::LuauSubtypingAddOptPropsToUnsealedTables && isOptional(prop.type))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (superTable->state == TableState::Free)
|
else if (superTable->state == TableState::Free)
|
||||||
{
|
{
|
||||||
PendingType* pendingSuper = log.queue(superTy);
|
PendingType* pendingSuper = log.queue(superTy);
|
||||||
|
@ -1639,296 +1626,6 @@ TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> see
|
||||||
return types->addType(UnionTypeVar{{getSingletonTypes().nilType, ty}});
|
return types->addType(UnionTypeVar{{getSingletonTypes().nilType, ty}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unifier::DEPRECATED_tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauTableSubtypingVariance2);
|
|
||||||
Resetter resetter{&variance};
|
|
||||||
variance = Invariant;
|
|
||||||
|
|
||||||
TableTypeVar* superTable = log.getMutable<TableTypeVar>(superTy);
|
|
||||||
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
|
|
||||||
|
|
||||||
if (!superTable || !subTable)
|
|
||||||
ice("passed non-table types to unifyTables");
|
|
||||||
|
|
||||||
if (superTable->state == TableState::Sealed && subTable->state == TableState::Sealed)
|
|
||||||
return tryUnifySealedTables(subTy, superTy, isIntersection);
|
|
||||||
else if ((superTable->state == TableState::Sealed && subTable->state == TableState::Unsealed) ||
|
|
||||||
(superTable->state == TableState::Unsealed && subTable->state == TableState::Sealed))
|
|
||||||
return tryUnifySealedTables(subTy, superTy, isIntersection);
|
|
||||||
else if ((superTable->state == TableState::Sealed && subTable->state == TableState::Generic) ||
|
|
||||||
(superTable->state == TableState::Generic && subTable->state == TableState::Sealed))
|
|
||||||
reportError(TypeError{location, TypeMismatch{superTy, subTy}});
|
|
||||||
else if ((superTable->state == TableState::Free) != (subTable->state == TableState::Free)) // one table is free and the other is not
|
|
||||||
{
|
|
||||||
TypeId freeTypeId = subTable->state == TableState::Free ? subTy : superTy;
|
|
||||||
TypeId otherTypeId = subTable->state == TableState::Free ? superTy : subTy;
|
|
||||||
|
|
||||||
return tryUnifyFreeTable(otherTypeId, freeTypeId);
|
|
||||||
}
|
|
||||||
else if (superTable->state == TableState::Free && subTable->state == TableState::Free)
|
|
||||||
{
|
|
||||||
tryUnifyFreeTable(subTy, superTy);
|
|
||||||
|
|
||||||
// avoid creating a cycle when the types are already pointing at each other
|
|
||||||
if (follow(superTy) != follow(subTy))
|
|
||||||
{
|
|
||||||
log.bindTable(superTy, subTy);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (superTable->state != TableState::Sealed && subTable->state != TableState::Sealed)
|
|
||||||
{
|
|
||||||
// All free tables are checked in one of the branches above
|
|
||||||
LUAU_ASSERT(superTable->state != TableState::Free);
|
|
||||||
LUAU_ASSERT(subTable->state != TableState::Free);
|
|
||||||
|
|
||||||
// Tables must have exactly the same props and their types must all unify
|
|
||||||
// I honestly have no idea if this is remotely close to reasonable.
|
|
||||||
for (const auto& [name, prop] : superTable->props)
|
|
||||||
{
|
|
||||||
const auto& r = subTable->props.find(name);
|
|
||||||
if (r == subTable->props.end())
|
|
||||||
reportError(TypeError{location, UnknownProperty{subTy, name}});
|
|
||||||
else
|
|
||||||
tryUnify_(r->second.type, prop.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (superTable->indexer && subTable->indexer)
|
|
||||||
tryUnifyIndexer(*subTable->indexer, *superTable->indexer);
|
|
||||||
else if (superTable->indexer)
|
|
||||||
{
|
|
||||||
// passing/assigning a table without an indexer to something that has one
|
|
||||||
// e.g. table.insert(t, 1) where t is a non-sealed table and doesn't have an indexer.
|
|
||||||
if (subTable->state == TableState::Unsealed)
|
|
||||||
{
|
|
||||||
log.changeIndexer(subTy, superTable->indexer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reportError(TypeError{location, CannotExtendTable{subTy, CannotExtendTable::Indexer}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (superTable->state == TableState::Sealed)
|
|
||||||
{
|
|
||||||
// lt is sealed and so it must be possible for rt to have precisely the same shape
|
|
||||||
// Verify that this is the case, then bind rt to lt.
|
|
||||||
ice("unsealed tables are not working yet", location);
|
|
||||||
}
|
|
||||||
else if (subTable->state == TableState::Sealed)
|
|
||||||
return tryUnifyTables(superTy, subTy, isIntersection);
|
|
||||||
else
|
|
||||||
ice("tryUnifyTables");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unifier::tryUnifyFreeTable(TypeId subTy, TypeId superTy)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauTableSubtypingVariance2);
|
|
||||||
TableTypeVar* freeTable = log.getMutable<TableTypeVar>(superTy);
|
|
||||||
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
|
|
||||||
|
|
||||||
if (!freeTable || !subTable)
|
|
||||||
ice("passed non-table types to tryUnifyFreeTable");
|
|
||||||
|
|
||||||
// Any properties in freeTable must unify with those in otherTable.
|
|
||||||
// Then bind freeTable to otherTable.
|
|
||||||
for (const auto& [freeName, freeProp] : freeTable->props)
|
|
||||||
{
|
|
||||||
if (auto subProp = findTablePropertyRespectingMeta(subTy, freeName))
|
|
||||||
{
|
|
||||||
tryUnify_(*subProp, freeProp.type);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TypeVars are commonly cyclic, so it is entirely possible
|
|
||||||
* for unifying a property of a table to change the table itself!
|
|
||||||
* We need to check for this and start over if we notice this occurring.
|
|
||||||
*
|
|
||||||
* I believe this is guaranteed to terminate eventually because this will
|
|
||||||
* only happen when a free table is bound to another table.
|
|
||||||
*/
|
|
||||||
if (!log.getMutable<TableTypeVar>(superTy) || !log.getMutable<TableTypeVar>(subTy))
|
|
||||||
return tryUnify_(subTy, superTy);
|
|
||||||
|
|
||||||
if (TableTypeVar* pendingFreeTtv = log.getMutable<TableTypeVar>(superTy); pendingFreeTtv && pendingFreeTtv->boundTo)
|
|
||||||
return tryUnify_(subTy, superTy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the other table is also free, then we are learning that it has more
|
|
||||||
// properties than we previously thought. Else, it is an error.
|
|
||||||
if (subTable->state == TableState::Free)
|
|
||||||
{
|
|
||||||
PendingType* pendingSub = log.queue(subTy);
|
|
||||||
TableTypeVar* pendingSubTtv = getMutable<TableTypeVar>(pendingSub);
|
|
||||||
LUAU_ASSERT(pendingSubTtv);
|
|
||||||
pendingSubTtv->props.insert({freeName, freeProp});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reportError(TypeError{location, UnknownProperty{subTy, freeName}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freeTable->indexer && subTable->indexer)
|
|
||||||
{
|
|
||||||
Unifier innerState = makeChildUnifier();
|
|
||||||
innerState.tryUnifyIndexer(*subTable->indexer, *freeTable->indexer);
|
|
||||||
|
|
||||||
checkChildUnifierTypeMismatch(innerState.errors, superTy, subTy);
|
|
||||||
|
|
||||||
log.concat(std::move(innerState.log));
|
|
||||||
}
|
|
||||||
else if (subTable->state == TableState::Free && freeTable->indexer)
|
|
||||||
{
|
|
||||||
log.changeIndexer(superTy, subTable->indexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!freeTable->boundTo && subTable->state != TableState::Free)
|
|
||||||
{
|
|
||||||
log.bindTable(superTy, subTy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unifier::tryUnifySealedTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauTableSubtypingVariance2);
|
|
||||||
TableTypeVar* superTable = log.getMutable<TableTypeVar>(superTy);
|
|
||||||
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
|
|
||||||
|
|
||||||
if (!superTable || !subTable)
|
|
||||||
ice("passed non-table types to unifySealedTables");
|
|
||||||
|
|
||||||
std::vector<std::string> missingPropertiesInSuper;
|
|
||||||
bool isUnnamedTable = subTable->name == std::nullopt && subTable->syntheticName == std::nullopt;
|
|
||||||
bool errorReported = false;
|
|
||||||
|
|
||||||
// Optimization: First test that the property sets are compatible without doing any recursive unification
|
|
||||||
if (!subTable->indexer)
|
|
||||||
{
|
|
||||||
for (const auto& [propName, superProp] : superTable->props)
|
|
||||||
{
|
|
||||||
auto subIter = subTable->props.find(propName);
|
|
||||||
if (subIter == subTable->props.end() && !isOptional(superProp.type))
|
|
||||||
missingPropertiesInSuper.push_back(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!missingPropertiesInSuper.empty())
|
|
||||||
{
|
|
||||||
reportError(TypeError{location, MissingProperties{superTy, subTy, std::move(missingPropertiesInSuper)}});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Unifier innerState = makeChildUnifier();
|
|
||||||
|
|
||||||
// Tables must have exactly the same props and their types must all unify
|
|
||||||
for (const auto& it : superTable->props)
|
|
||||||
{
|
|
||||||
const auto& r = subTable->props.find(it.first);
|
|
||||||
if (r == subTable->props.end())
|
|
||||||
{
|
|
||||||
if (isOptional(it.second.type))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
missingPropertiesInSuper.push_back(it.first);
|
|
||||||
|
|
||||||
innerState.reportError(TypeError{location, TypeMismatch{superTy, subTy}});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isUnnamedTable && r->second.location)
|
|
||||||
{
|
|
||||||
size_t oldErrorSize = innerState.errors.size();
|
|
||||||
Location old = innerState.location;
|
|
||||||
innerState.location = *r->second.location;
|
|
||||||
innerState.tryUnify_(r->second.type, it.second.type);
|
|
||||||
innerState.location = old;
|
|
||||||
|
|
||||||
if (oldErrorSize != innerState.errors.size() && !errorReported)
|
|
||||||
{
|
|
||||||
errorReported = true;
|
|
||||||
reportError(innerState.errors.back());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
innerState.tryUnify_(r->second.type, it.second.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (superTable->indexer || subTable->indexer)
|
|
||||||
{
|
|
||||||
if (superTable->indexer && subTable->indexer)
|
|
||||||
innerState.tryUnifyIndexer(*subTable->indexer, *superTable->indexer);
|
|
||||||
else if (subTable->state == TableState::Unsealed)
|
|
||||||
{
|
|
||||||
if (superTable->indexer && !subTable->indexer)
|
|
||||||
{
|
|
||||||
log.changeIndexer(subTy, superTable->indexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (superTable->state == TableState::Unsealed)
|
|
||||||
{
|
|
||||||
if (subTable->indexer && !superTable->indexer)
|
|
||||||
{
|
|
||||||
log.changeIndexer(superTy, subTable->indexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (superTable->indexer)
|
|
||||||
{
|
|
||||||
innerState.tryUnify_(getSingletonTypes().stringType, superTable->indexer->indexType);
|
|
||||||
for (const auto& [name, type] : subTable->props)
|
|
||||||
{
|
|
||||||
const auto& it = superTable->props.find(name);
|
|
||||||
if (it == superTable->props.end())
|
|
||||||
innerState.tryUnify_(type.type, superTable->indexer->indexResultType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
innerState.reportError(TypeError{location, TypeMismatch{superTy, subTy}});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errorReported)
|
|
||||||
log.concat(std::move(innerState.log));
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!missingPropertiesInSuper.empty())
|
|
||||||
{
|
|
||||||
reportError(TypeError{location, MissingProperties{superTy, subTy, std::move(missingPropertiesInSuper)}});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the superTy is an immediate part of an intersection type, do not do extra-property check.
|
|
||||||
// Otherwise, we would falsely generate an extra-property-error for 's' in this code:
|
|
||||||
// local a: {n: number} & {s: string} = {n=1, s=""}
|
|
||||||
// When checking against the table '{n: number}'.
|
|
||||||
if (!isIntersection && superTable->state != TableState::Unsealed && !superTable->indexer)
|
|
||||||
{
|
|
||||||
// Check for extra properties in the subTy
|
|
||||||
std::vector<std::string> extraPropertiesInSub;
|
|
||||||
|
|
||||||
for (const auto& [subKey, subProp] : subTable->props)
|
|
||||||
{
|
|
||||||
const auto& superIt = superTable->props.find(subKey);
|
|
||||||
if (superIt == superTable->props.end())
|
|
||||||
{
|
|
||||||
if (isOptional(subProp.type))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
extraPropertiesInSub.push_back(subKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!extraPropertiesInSub.empty())
|
|
||||||
{
|
|
||||||
reportError(TypeError{location, MissingProperties{superTy, subTy, std::move(extraPropertiesInSub), MissingProperties::Extra}});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkChildUnifierTypeMismatch(innerState.errors, superTy, subTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
{
|
{
|
||||||
const MetatableTypeVar* superMetatable = get<MetatableTypeVar>(superTy);
|
const MetatableTypeVar* superMetatable = get<MetatableTypeVar>(superTy);
|
||||||
|
@ -2068,14 +1765,6 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unifier::tryUnifyIndexer(const TableIndexer& subIndexer, const TableIndexer& superIndexer)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauTableSubtypingVariance2);
|
|
||||||
|
|
||||||
tryUnify_(subIndexer.indexType, superIndexer.indexType);
|
|
||||||
tryUnify_(subIndexer.indexResultType, superIndexer.indexResultType);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -2435,7 +2124,7 @@ void Unifier::occursCheck(TypeId needle, TypeId haystack)
|
||||||
void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
|
void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
|
||||||
{
|
{
|
||||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "occursCheck for TypeId");
|
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
auto check = [&](TypeId tv) {
|
auto check = [&](TypeId tv) {
|
||||||
occursCheck(seen, needle, tv);
|
occursCheck(seen, needle, tv);
|
||||||
|
@ -2506,7 +2195,7 @@ void Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
|
||||||
ice("Expected needle pack to be free");
|
ice("Expected needle pack to be free");
|
||||||
|
|
||||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "occursCheck for TypePackId");
|
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
while (!log.getMutable<ErrorTypeVar>(haystack))
|
while (!log.getMutable<ErrorTypeVar>(haystack))
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauTimeTracing)
|
LUAU_FASTFLAG(DebugLuauTimeTracing)
|
||||||
|
LUAU_FASTFLAG(LuauTypeMismatchModuleNameResolution)
|
||||||
|
|
||||||
enum class ReportFormat
|
enum class ReportFormat
|
||||||
{
|
{
|
||||||
|
@ -49,6 +50,9 @@ static void reportError(const Luau::Frontend& frontend, ReportFormat format, con
|
||||||
|
|
||||||
if (const Luau::SyntaxError* syntaxError = Luau::get_if<Luau::SyntaxError>(&error.data))
|
if (const Luau::SyntaxError* syntaxError = Luau::get_if<Luau::SyntaxError>(&error.data))
|
||||||
report(format, humanReadableName.c_str(), error.location, "SyntaxError", syntaxError->message.c_str());
|
report(format, humanReadableName.c_str(), error.location, "SyntaxError", syntaxError->message.c_str());
|
||||||
|
else if (FFlag::LuauTypeMismatchModuleNameResolution)
|
||||||
|
report(format, humanReadableName.c_str(), error.location, "TypeError",
|
||||||
|
Luau::toString(error, Luau::TypeErrorToStringOptions{frontend.fileResolver}).c_str());
|
||||||
else
|
else
|
||||||
report(format, humanReadableName.c_str(), error.location, "TypeError", Luau::toString(error).c_str());
|
report(format, humanReadableName.c_str(), error.location, "TypeError", Luau::toString(error).c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ option(LUAU_BUILD_TESTS "Build tests" ON)
|
||||||
option(LUAU_BUILD_WEB "Build Web module" OFF)
|
option(LUAU_BUILD_WEB "Build Web module" OFF)
|
||||||
option(LUAU_WERROR "Warnings as errors" OFF)
|
option(LUAU_WERROR "Warnings as errors" OFF)
|
||||||
option(LUAU_STATIC_CRT "Link with the static CRT (/MT)" OFF)
|
option(LUAU_STATIC_CRT "Link with the static CRT (/MT)" OFF)
|
||||||
|
option(LUAU_EXTERN_C "Use extern C for all APIs" OFF)
|
||||||
|
|
||||||
if(LUAU_STATIC_CRT)
|
if(LUAU_STATIC_CRT)
|
||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
@ -115,6 +116,14 @@ target_compile_options(Luau.CodeGen PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(isocline PRIVATE ${LUAU_OPTIONS} ${ISOCLINE_OPTIONS})
|
target_compile_options(isocline PRIVATE ${LUAU_OPTIONS} ${ISOCLINE_OPTIONS})
|
||||||
|
|
||||||
|
if(LUAU_EXTERN_C)
|
||||||
|
# enable extern "C" for VM (lua.h, lualib.h) and Compiler (luacode.h) to make Luau friendlier to use from non-C++ languages
|
||||||
|
# note that we enable LUA_USE_LONGJMP=1 as well; otherwise functions like luaL_error will throw C++ exceptions, which can't be done from extern "C" functions
|
||||||
|
target_compile_definitions(Luau.VM PUBLIC LUA_USE_LONGJMP=1)
|
||||||
|
target_compile_definitions(Luau.VM PUBLIC LUA_API=extern\"C\")
|
||||||
|
target_compile_definitions(Luau.Compiler PUBLIC LUACODE_API=extern\"C\")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (MSVC AND MSVC_VERSION GREATER_EQUAL 1924)
|
if (MSVC AND MSVC_VERSION GREATER_EQUAL 1924)
|
||||||
# disable partial redundancy elimination which regresses interpreter codegen substantially in VS2022:
|
# disable partial redundancy elimination which regresses interpreter codegen substantially in VS2022:
|
||||||
# https://developercommunity.visualstudio.com/t/performance-regression-on-a-complex-interpreter-lo/1631863
|
# https://developercommunity.visualstudio.com/t/performance-regression-on-a-complex-interpreter-lo/1631863
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
|
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
|
||||||
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
|
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
|
||||||
|
|
||||||
// Bytecode definitions
|
// # Bytecode definitions
|
||||||
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
|
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
|
||||||
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
|
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
|
||||||
//
|
//
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
|
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
|
||||||
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
|
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
|
||||||
|
|
||||||
// Bytecode indices
|
// # Bytecode indices
|
||||||
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
|
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
|
||||||
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
|
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
|
||||||
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
|
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
|
||||||
|
@ -29,6 +29,15 @@
|
||||||
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
|
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
|
||||||
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
|
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
|
||||||
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more.
|
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more.
|
||||||
|
|
||||||
|
// # Bytecode versions
|
||||||
|
// Bytecode serialized format embeds a version number, that dictates both the serialized form as well as the allowed instructions. As long as the bytecode version falls into supported
|
||||||
|
// range (indicated by LBC_BYTECODE_MIN / LBC_BYTECODE_MAX) and was produced by Luau compiler, it should load and execute correctly.
|
||||||
|
//
|
||||||
|
// Note that Luau runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected
|
||||||
|
// that Luau users can recompile bytecode from source on Luau version upgrades if necessary.
|
||||||
|
|
||||||
|
// Bytecode opcode, part of the instruction header
|
||||||
enum LuauOpcode
|
enum LuauOpcode
|
||||||
{
|
{
|
||||||
// NOP: noop
|
// NOP: noop
|
||||||
|
@ -380,8 +389,10 @@ enum LuauOpcode
|
||||||
// Bytecode tags, used internally for bytecode encoded as a string
|
// Bytecode tags, used internally for bytecode encoded as a string
|
||||||
enum LuauBytecodeTag
|
enum LuauBytecodeTag
|
||||||
{
|
{
|
||||||
// Bytecode version
|
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
|
||||||
LBC_VERSION = 2,
|
LBC_VERSION_MIN = 2,
|
||||||
|
LBC_VERSION_MAX = 2,
|
||||||
|
LBC_VERSION_TARGET = 2,
|
||||||
// Types of constant table entries
|
// Types of constant table entries
|
||||||
LBC_CONSTANT_NIL = 0,
|
LBC_CONSTANT_NIL = 0,
|
||||||
LBC_CONSTANT_BOOLEAN,
|
LBC_CONSTANT_BOOLEAN,
|
||||||
|
|
|
@ -119,6 +119,8 @@ public:
|
||||||
|
|
||||||
static std::string getError(const std::string& message);
|
static std::string getError(const std::string& message);
|
||||||
|
|
||||||
|
static uint8_t getVersion();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Constant
|
struct Constant
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static_assert(LBC_VERSION_TARGET >= LBC_VERSION_MIN && LBC_VERSION_TARGET <= LBC_VERSION_MAX, "Invalid bytecode version setup");
|
||||||
|
static_assert(LBC_VERSION_MAX <= 127, "Bytecode version should be 7-bit so that we can extend the serialization to use varint transparently");
|
||||||
|
|
||||||
static const uint32_t kMaxConstantCount = 1 << 23;
|
static const uint32_t kMaxConstantCount = 1 << 23;
|
||||||
static const uint32_t kMaxClosureCount = 1 << 15;
|
static const uint32_t kMaxClosureCount = 1 << 15;
|
||||||
|
|
||||||
|
@ -572,7 +575,10 @@ void BytecodeBuilder::finalize()
|
||||||
bytecode.reserve(capacity);
|
bytecode.reserve(capacity);
|
||||||
|
|
||||||
// assemble final bytecode blob
|
// assemble final bytecode blob
|
||||||
bytecode = char(LBC_VERSION);
|
uint8_t version = getVersion();
|
||||||
|
LUAU_ASSERT(version >= LBC_VERSION_MIN && version <= LBC_VERSION_MAX);
|
||||||
|
|
||||||
|
bytecode = char(version);
|
||||||
|
|
||||||
writeStringTable(bytecode);
|
writeStringTable(bytecode);
|
||||||
|
|
||||||
|
@ -1040,7 +1046,7 @@ void BytecodeBuilder::expandJumps()
|
||||||
|
|
||||||
std::string BytecodeBuilder::getError(const std::string& message)
|
std::string BytecodeBuilder::getError(const std::string& message)
|
||||||
{
|
{
|
||||||
// 0 acts as a special marker for error bytecode (it's equal to LBC_VERSION for valid bytecode blobs)
|
// 0 acts as a special marker for error bytecode (it's equal to LBC_VERSION_TARGET for valid bytecode blobs)
|
||||||
std::string result;
|
std::string result;
|
||||||
result += char(0);
|
result += char(0);
|
||||||
result += message;
|
result += message;
|
||||||
|
@ -1048,6 +1054,12 @@ std::string BytecodeBuilder::getError(const std::string& message)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t BytecodeBuilder::getVersion()
|
||||||
|
{
|
||||||
|
// This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags
|
||||||
|
return LBC_VERSION_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LUAU_ASSERTENABLED
|
#ifdef LUAU_ASSERTENABLED
|
||||||
void BytecodeBuilder::validate() const
|
void BytecodeBuilder::validate() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileIterNoPairs, false)
|
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
|
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
|
||||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
|
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
|
||||||
|
|
||||||
|
@ -2672,7 +2670,7 @@ struct Compiler
|
||||||
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
||||||
{
|
{
|
||||||
skipOp = LOP_FORGPREP_NEXT;
|
skipOp = LOP_FORGPREP_NEXT;
|
||||||
loopOp = FFlag::LuauCompileIterNoPairs ? LOP_FORGLOOP : LOP_FORGLOOP_NEXT;
|
loopOp = LOP_FORGLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stat->values.size == 2)
|
else if (stat->values.size == 2)
|
||||||
|
@ -2682,7 +2680,7 @@ struct Compiler
|
||||||
if (builtin.isGlobal("next")) // for .. in next,t
|
if (builtin.isGlobal("next")) // for .. in next,t
|
||||||
{
|
{
|
||||||
skipOp = LOP_FORGPREP_NEXT;
|
skipOp = LOP_FORGPREP_NEXT;
|
||||||
loopOp = FFlag::LuauCompileIterNoPairs ? LOP_FORGLOOP : LOP_FORGLOOP_NEXT;
|
loopOp = LOP_FORGLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page)
|
||||||
{
|
{
|
||||||
void (*dtor)(lua_State*, void*) = nullptr;
|
void (*dtor)(lua_State*, void*) = nullptr;
|
||||||
dtor = L->global->udatagc[u->tag];
|
dtor = L->global->udatagc[u->tag];
|
||||||
|
// TODO: access to L here is highly unsafe since this is called during internal GC traversal
|
||||||
|
// certain operations such as lua_getthreaddata are okay, but by and large this risks crashes on improper use
|
||||||
if (dtor)
|
if (dtor)
|
||||||
dtor(L, u->data);
|
dtor(L, u->data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,11 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version != LBC_VERSION)
|
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
||||||
{
|
{
|
||||||
char chunkid[LUA_IDSIZE];
|
char chunkid[LUA_IDSIZE];
|
||||||
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
|
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
|
||||||
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version);
|
lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -261,8 +261,6 @@ L1: RETURN R0 0
|
||||||
|
|
||||||
TEST_CASE("ForBytecode")
|
TEST_CASE("ForBytecode")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff2("LuauCompileIterNoPairs", false);
|
|
||||||
|
|
||||||
// basic for loop: variable directly refers to internal iteration index (R2)
|
// basic for loop: variable directly refers to internal iteration index (R2)
|
||||||
CHECK_EQ("\n" + compileFunction0("for i=1,5 do print(i) end"), R"(
|
CHECK_EQ("\n" + compileFunction0("for i=1,5 do print(i) end"), R"(
|
||||||
LOADN R2 1
|
LOADN R2 1
|
||||||
|
@ -329,7 +327,7 @@ L0: GETIMPORT R5 3
|
||||||
MOVE R6 R3
|
MOVE R6 R3
|
||||||
MOVE R7 R4
|
MOVE R7 R4
|
||||||
CALL R5 2 0
|
CALL R5 2 0
|
||||||
L1: FORGLOOP_NEXT R0 L0
|
L1: FORGLOOP R0 L0 2
|
||||||
RETURN R0 0
|
RETURN R0 0
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
@ -342,7 +340,7 @@ L0: GETIMPORT R5 3
|
||||||
MOVE R6 R3
|
MOVE R6 R3
|
||||||
MOVE R7 R4
|
MOVE R7 R4
|
||||||
CALL R5 2 0
|
CALL R5 2 0
|
||||||
L1: FORGLOOP_NEXT R0 L0
|
L1: FORGLOOP R0 L0 2
|
||||||
RETURN R0 0
|
RETURN R0 0
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -2262,8 +2260,6 @@ TEST_CASE("TypeAliasing")
|
||||||
|
|
||||||
TEST_CASE("DebugLineInfo")
|
TEST_CASE("DebugLineInfo")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
|
||||||
|
|
||||||
Luau::BytecodeBuilder bcb;
|
Luau::BytecodeBuilder bcb;
|
||||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Lines);
|
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Lines);
|
||||||
Luau::compileOrThrow(bcb, R"(
|
Luau::compileOrThrow(bcb, R"(
|
||||||
|
@ -2313,7 +2309,7 @@ return result
|
||||||
15: L0: MOVE R7 R1
|
15: L0: MOVE R7 R1
|
||||||
15: MOVE R8 R5
|
15: MOVE R8 R5
|
||||||
15: CONCAT R1 R7 R8
|
15: CONCAT R1 R7 R8
|
||||||
14: L1: FORGLOOP_NEXT R2 L0
|
14: L1: FORGLOOP R2 L0 1
|
||||||
17: RETURN R1 1
|
17: RETURN R1 1
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -2545,8 +2541,6 @@ a
|
||||||
|
|
||||||
TEST_CASE("DebugSource")
|
TEST_CASE("DebugSource")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
|
||||||
|
|
||||||
const char* source = R"(
|
const char* source = R"(
|
||||||
local kSelectedBiomes = {
|
local kSelectedBiomes = {
|
||||||
['Mountains'] = true,
|
['Mountains'] = true,
|
||||||
|
@ -2614,7 +2608,7 @@ L0: MOVE R7 R1
|
||||||
MOVE R8 R5
|
MOVE R8 R5
|
||||||
CONCAT R1 R7 R8
|
CONCAT R1 R7 R8
|
||||||
14: for k in pairs(kSelectedBiomes) do
|
14: for k in pairs(kSelectedBiomes) do
|
||||||
L1: FORGLOOP_NEXT R2 L0
|
L1: FORGLOOP R2 L0 1
|
||||||
17: return result
|
17: return result
|
||||||
RETURN R1 1
|
RETURN R1 1
|
||||||
)");
|
)");
|
||||||
|
@ -2622,8 +2616,6 @@ RETURN R1 1
|
||||||
|
|
||||||
TEST_CASE("DebugLocals")
|
TEST_CASE("DebugLocals")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
|
||||||
|
|
||||||
const char* source = R"(
|
const char* source = R"(
|
||||||
function foo(e, f)
|
function foo(e, f)
|
||||||
local a = 1
|
local a = 1
|
||||||
|
@ -2661,12 +2653,12 @@ end
|
||||||
local 0: reg 5, start pc 5 line 5, end pc 8 line 5
|
local 0: reg 5, start pc 5 line 5, end pc 8 line 5
|
||||||
local 1: reg 6, start pc 14 line 8, end pc 18 line 8
|
local 1: reg 6, start pc 14 line 8, end pc 18 line 8
|
||||||
local 2: reg 7, start pc 14 line 8, end pc 18 line 8
|
local 2: reg 7, start pc 14 line 8, end pc 18 line 8
|
||||||
local 3: reg 3, start pc 21 line 12, end pc 24 line 12
|
local 3: reg 3, start pc 22 line 12, end pc 25 line 12
|
||||||
local 4: reg 3, start pc 26 line 16, end pc 30 line 16
|
local 4: reg 3, start pc 27 line 16, end pc 31 line 16
|
||||||
local 5: reg 0, start pc 0 line 3, end pc 34 line 21
|
local 5: reg 0, start pc 0 line 3, end pc 35 line 21
|
||||||
local 6: reg 1, start pc 0 line 3, end pc 34 line 21
|
local 6: reg 1, start pc 0 line 3, end pc 35 line 21
|
||||||
local 7: reg 2, start pc 1 line 4, end pc 34 line 21
|
local 7: reg 2, start pc 1 line 4, end pc 35 line 21
|
||||||
local 8: reg 3, start pc 34 line 21, end pc 34 line 21
|
local 8: reg 3, start pc 35 line 21, end pc 35 line 21
|
||||||
3: LOADN R2 1
|
3: LOADN R2 1
|
||||||
4: LOADN R5 1
|
4: LOADN R5 1
|
||||||
4: LOADN R3 3
|
4: LOADN R3 3
|
||||||
|
@ -2683,7 +2675,7 @@ local 8: reg 3, start pc 34 line 21, end pc 34 line 21
|
||||||
8: MOVE R9 R6
|
8: MOVE R9 R6
|
||||||
8: MOVE R10 R7
|
8: MOVE R10 R7
|
||||||
8: CALL R8 2 0
|
8: CALL R8 2 0
|
||||||
7: L3: FORGLOOP_NEXT R3 L2
|
7: L3: FORGLOOP R3 L2 2
|
||||||
11: LOADN R3 2
|
11: LOADN R3 2
|
||||||
12: GETIMPORT R4 1
|
12: GETIMPORT R4 1
|
||||||
12: LOADN R5 2
|
12: LOADN R5 2
|
||||||
|
@ -3795,8 +3787,6 @@ RETURN R0 1
|
||||||
|
|
||||||
TEST_CASE("SharedClosure")
|
TEST_CASE("SharedClosure")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
|
||||||
|
|
||||||
// closures can be shared even if functions refer to upvalues, as long as upvalues are top-level
|
// closures can be shared even if functions refer to upvalues, as long as upvalues are top-level
|
||||||
CHECK_EQ("\n" + compileFunction(R"(
|
CHECK_EQ("\n" + compileFunction(R"(
|
||||||
local val = ...
|
local val = ...
|
||||||
|
@ -3939,7 +3929,7 @@ L2: GETIMPORT R5 1
|
||||||
NEWCLOSURE R6 P1
|
NEWCLOSURE R6 P1
|
||||||
CAPTURE VAL R3
|
CAPTURE VAL R3
|
||||||
CALL R5 1 0
|
CALL R5 1 0
|
||||||
L3: FORGLOOP_NEXT R0 L2
|
L3: FORGLOOP R0 L2 2
|
||||||
LOADN R2 1
|
LOADN R2 1
|
||||||
LOADN R0 10
|
LOADN R0 10
|
||||||
LOADN R1 1
|
LOADN R1 1
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Config.h"
|
#include "Luau/Config.h"
|
||||||
#include "Luau/ConstraintGraphBuilder.h"
|
|
||||||
#include "Luau/FileResolver.h"
|
#include "Luau/FileResolver.h"
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
#include "Luau/IostreamHelpers.h"
|
#include "Luau/IostreamHelpers.h"
|
||||||
#include "Luau/Linter.h"
|
#include "Luau/Linter.h"
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
|
@ -279,7 +279,6 @@ TEST_CASE_FIXTURE(Fixture, "clone_recursion_limit")
|
||||||
int limit = 400;
|
int limit = 400;
|
||||||
#endif
|
#endif
|
||||||
ScopedFastInt luauTypeCloneRecursionLimit{"LuauTypeCloneRecursionLimit", limit};
|
ScopedFastInt luauTypeCloneRecursionLimit{"LuauTypeCloneRecursionLimit", limit};
|
||||||
ScopedFastFlag sff{"LuauRecursionLimitException", true};
|
|
||||||
|
|
||||||
TypeArena src;
|
TypeArena src;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ using namespace Luau;
|
||||||
struct NormalizeFixture : Fixture
|
struct NormalizeFixture : Fixture
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff1{"LuauLowerBoundsCalculation", true};
|
ScopedFastFlag sff1{"LuauLowerBoundsCalculation", true};
|
||||||
ScopedFastFlag sff2{"LuauTableSubtypingVariance2", true};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void createSomeClasses(TypeChecker& typeChecker)
|
void createSomeClasses(TypeChecker& typeChecker)
|
||||||
|
|
|
@ -264,10 +264,13 @@ TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type")
|
||||||
}
|
}
|
||||||
)LUA";
|
)LUA";
|
||||||
|
|
||||||
|
CheckResult result = check(src);
|
||||||
|
CodeTooComplex ctc;
|
||||||
|
|
||||||
if (FFlag::LuauLowerBoundsCalculation)
|
if (FFlag::LuauLowerBoundsCalculation)
|
||||||
(void)check(src);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
else
|
else
|
||||||
CHECK_THROWS_AS(check(src), std::exception);
|
CHECK(hasError(result, &ctc));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -409,8 +409,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "toStringDetailed2")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "toStringDetailed2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local base = {}
|
local base = {}
|
||||||
function base:one() return 1 end
|
function base:one() return 1 end
|
||||||
|
|
|
@ -7,8 +7,21 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeAliases");
|
TEST_SUITE_BEGIN("TypeAliases");
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "basic_alias")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type T = number
|
||||||
|
local x: T = 1
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
CHECK_EQ("number", toString(requireType("x")));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias")
|
TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
@ -24,6 +37,63 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias")
|
||||||
CHECK_EQ("t1 where t1 = () -> t1?", toString(requireType("g")));
|
CHECK_EQ("t1 where t1 = () -> t1?", toString(requireType("g")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "names_are_ascribed")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type T = { x: number }
|
||||||
|
local x: T
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
CHECK_EQ("T", toString(requireType("x")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
|
||||||
|
{
|
||||||
|
// This is a tricky case. In order to support recursive type aliases,
|
||||||
|
// we first walk the block and generate free types as placeholders.
|
||||||
|
// We then walk the AST as normal. If we declare a type alias as below,
|
||||||
|
// we generate a free type. We then begin our normal walk, examining
|
||||||
|
// local x: T = "foo", which establishes two constraints:
|
||||||
|
// a <: b
|
||||||
|
// string <: a
|
||||||
|
// We then visit the type alias, and establish that
|
||||||
|
// b <: number
|
||||||
|
// Then, when solving these constraints, we dispatch them in the order
|
||||||
|
// they appear above. This means that a ~ b, and a ~ string, thus
|
||||||
|
// b ~ string. This means the b <: number constraint has no effect.
|
||||||
|
// Essentially we've "stolen" the alias's type out from under it.
|
||||||
|
// This test ensures that we don't actually do this.
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local x: T = "foo"
|
||||||
|
type T = number
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
{
|
||||||
|
CHECK(result.errors[0] == TypeError{
|
||||||
|
Location{{1, 21}, {1, 26}},
|
||||||
|
getMainSourceModule()->name,
|
||||||
|
TypeMismatch{
|
||||||
|
getSingletonTypes().numberType,
|
||||||
|
getSingletonTypes().stringType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK(result.errors[0] == TypeError{
|
||||||
|
Location{{1, 8}, {1, 26}},
|
||||||
|
getMainSourceModule()->name,
|
||||||
|
TypeMismatch{
|
||||||
|
getSingletonTypes().numberType,
|
||||||
|
getSingletonTypes().stringType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_when_stringified")
|
TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_when_stringified")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
@ -41,7 +111,22 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_whe
|
||||||
CHECK_EQ(typeChecker.numberType, tm->givenType);
|
CHECK_EQ(typeChecker.numberType, tm->givenType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types")
|
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
type T = { f: number, g: U }
|
||||||
|
type U = { h: number, i: T? }
|
||||||
|
local x: T = { f = 37, g = { h = 5, i = nil } }
|
||||||
|
x.g.i = x
|
||||||
|
local y: T = { f = 3, g = { h = 5, i = nil } }
|
||||||
|
y.g.i = y
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
|
|
@ -30,11 +30,21 @@ TEST_CASE_FIXTURE(Fixture, "successful_check")
|
||||||
dumpErrors(result);
|
dumpErrors(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "variable_type_is_supertype")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local x: number = 1
|
||||||
|
local y: number? = x
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "function_parameters_can_have_annotations")
|
TEST_CASE_FIXTURE(Fixture, "function_parameters_can_have_annotations")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function double(x: number)
|
function double(x: number)
|
||||||
return x * 2
|
return 2
|
||||||
end
|
end
|
||||||
|
|
||||||
local four = double(2)
|
local four = double(2)
|
||||||
|
@ -47,7 +57,7 @@ TEST_CASE_FIXTURE(Fixture, "function_parameter_annotations_are_checked")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function double(x: number)
|
function double(x: number)
|
||||||
return x * 2
|
return 2
|
||||||
end
|
end
|
||||||
|
|
||||||
local four = double("two")
|
local four = double("two")
|
||||||
|
@ -70,13 +80,13 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotations_are_checked")
|
||||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fiftyType);
|
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fiftyType);
|
||||||
REQUIRE(ftv != nullptr);
|
REQUIRE(ftv != nullptr);
|
||||||
|
|
||||||
TypePackId retPack = ftv->retTypes;
|
TypePackId retPack = follow(ftv->retTypes);
|
||||||
const TypePack* tp = get<TypePack>(retPack);
|
const TypePack* tp = get<TypePack>(retPack);
|
||||||
REQUIRE(tp != nullptr);
|
REQUIRE(tp != nullptr);
|
||||||
|
|
||||||
REQUIRE_EQ(1, tp->head.size());
|
REQUIRE_EQ(1, tp->head.size());
|
||||||
|
|
||||||
REQUIRE_EQ(typeChecker.anyType, tp->head[0]);
|
REQUIRE_EQ(typeChecker.anyType, follow(tp->head[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "function_return_multret_annotations_are_checked")
|
TEST_CASE_FIXTURE(Fixture, "function_return_multret_annotations_are_checked")
|
||||||
|
@ -116,6 +126,23 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotation_should_continuously_parse
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "unknown_type_reference_generates_error")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local x: IDoNotExist
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(result.errors[0] == TypeError{
|
||||||
|
Location{{1, 17}, {1, 28}},
|
||||||
|
getMainSourceModule()->name,
|
||||||
|
UnknownSymbol{
|
||||||
|
"IDoNotExist",
|
||||||
|
UnknownSymbol::Context::Type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "typeof_variable_type_annotation_should_return_its_type")
|
TEST_CASE_FIXTURE(Fixture, "typeof_variable_type_annotation_should_return_its_type")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
@ -632,7 +659,10 @@ int AssertionCatcher::tripped;
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice")
|
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"DebugLuauMagicTypes", true};
|
ScopedFastFlag sffs[] = {
|
||||||
|
{"DebugLuauMagicTypes", true},
|
||||||
|
{"LuauUseInternalCompilerErrorException", false},
|
||||||
|
};
|
||||||
|
|
||||||
AssertionCatcher ac;
|
AssertionCatcher ac;
|
||||||
|
|
||||||
|
@ -646,9 +676,10 @@ TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_handler")
|
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_handler")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs{"DebugLuauMagicTypes", true};
|
ScopedFastFlag sffs[] = {
|
||||||
|
{"DebugLuauMagicTypes", true},
|
||||||
AssertionCatcher ac;
|
{"LuauUseInternalCompilerErrorException", false},
|
||||||
|
};
|
||||||
|
|
||||||
bool caught = false;
|
bool caught = false;
|
||||||
|
|
||||||
|
@ -662,8 +693,44 @@ TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_handler")
|
||||||
std::runtime_error);
|
std::runtime_error);
|
||||||
|
|
||||||
CHECK_EQ(true, caught);
|
CHECK_EQ(true, caught);
|
||||||
|
}
|
||||||
|
|
||||||
frontend.iceHandler.onInternalError = {};
|
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_exception_with_flag")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{"DebugLuauMagicTypes", true},
|
||||||
|
{"LuauUseInternalCompilerErrorException", true},
|
||||||
|
};
|
||||||
|
|
||||||
|
AssertionCatcher ac;
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(check(R"(
|
||||||
|
local a: _luau_ice = 55
|
||||||
|
)"),
|
||||||
|
InternalCompilerError);
|
||||||
|
|
||||||
|
LUAU_ASSERT(1 == AssertionCatcher::tripped);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_exception_with_flag_handler")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{"DebugLuauMagicTypes", true},
|
||||||
|
{"LuauUseInternalCompilerErrorException", true},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool caught = false;
|
||||||
|
|
||||||
|
frontend.iceHandler.onInternalError = [&](const char*) {
|
||||||
|
caught = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(check(R"(
|
||||||
|
local a: _luau_ice = 55
|
||||||
|
)"),
|
||||||
|
InternalCompilerError);
|
||||||
|
|
||||||
|
CHECK_EQ(true, caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "luau_ice_is_not_special_without_the_flag")
|
TEST_CASE_FIXTURE(Fixture, "luau_ice_is_not_special_without_the_flag")
|
||||||
|
|
|
@ -700,11 +700,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
|
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
-- At one point this produced a UAF
|
-- At one point this produced a UAF
|
||||||
|
@ -979,8 +974,6 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_generic_function_in_assignments2")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
TEST_CASE_FIXTURE(Fixture, "self_recursive_instantiated_param")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
|
||||||
|
|
||||||
// Mutability in type function application right now can create strange recursive types
|
// Mutability in type function application right now can create strange recursive types
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Table = { a: number }
|
type Table = { a: number }
|
||||||
|
@ -1015,8 +1008,6 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_quantifying")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function sum<a>(x: a, y: a, f: (a, a) -> a)
|
local function sum<a>(x: a, y: a, f: (a, a) -> a)
|
||||||
return f(x, y)
|
return f(x, y)
|
||||||
|
@ -1123,8 +1114,6 @@ TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauApplyTypeFunctionFix", true};
|
|
||||||
|
|
||||||
// https://github.com/Roblox/luau/issues/484
|
// https://github.com/Roblox/luau/issues/484
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -1153,8 +1142,6 @@ local complex: ComplexObject<string> = {
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics2")
|
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauApplyTypeFunctionFix", true};
|
|
||||||
|
|
||||||
// https://github.com/Roblox/luau/issues/484
|
// https://github.com/Roblox/luau/issues/484
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauTableSubtypingVariance2)
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferModules");
|
TEST_SUITE_BEGIN("TypeInferModules");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
||||||
|
@ -326,16 +324,9 @@ local b: B.T = a
|
||||||
CheckResult result = frontend.check("game/C");
|
CheckResult result = frontend.check("game/C");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (FFlag::LuauTableSubtypingVariance2)
|
CHECK_EQ(toString(result.errors[0]), R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'
|
||||||
{
|
|
||||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'
|
|
||||||
caused by:
|
caused by:
|
||||||
Property 'x' is not compatible. Type 'number' could not be converted into 'string')");
|
Property 'x' is not compatible. Type 'number' could not be converted into 'string')");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "module_type_conflict_instantiated")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "module_type_conflict_instantiated")
|
||||||
|
@ -367,16 +358,9 @@ local b: B.T = a
|
||||||
CheckResult result = frontend.check("game/D");
|
CheckResult result = frontend.check("game/D");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (FFlag::LuauTableSubtypingVariance2)
|
CHECK_EQ(toString(result.errors[0]), R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'
|
||||||
{
|
|
||||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'
|
|
||||||
caused by:
|
caused by:
|
||||||
Property 'x' is not compatible. Type 'number' could not be converted into 'string')");
|
Property 'x' is not compatible. Type 'number' could not be converted into 'string')");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHECK_EQ(toString(result.errors[0]), "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -353,8 +353,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_non_binary_expressions_actually_resol
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
|
TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal")
|
||||||
{
|
{
|
||||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: {x: number?} = {x = nil}
|
local t: {x: number?} = {x = nil}
|
||||||
|
|
||||||
|
|
|
@ -260,10 +260,6 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_alias_or_parens_is_indexer")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[]{
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local x: { ["<>"] : number }
|
local x: { ["<>"] : number }
|
||||||
|
|
|
@ -276,8 +276,6 @@ TEST_CASE_FIXTURE(Fixture, "open_table_unification")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "open_table_unification_2")
|
TEST_CASE_FIXTURE(Fixture, "open_table_unification_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = {}
|
local a = {}
|
||||||
a.x = 99
|
a.x = 99
|
||||||
|
@ -347,8 +345,6 @@ TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_1")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_2")
|
TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
function foo(o)
|
function foo(o)
|
||||||
|
@ -370,8 +366,6 @@ TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_2")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_3")
|
TEST_CASE_FIXTURE(Fixture, "table_param_row_polymorphism_3")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local T = {}
|
local T = {}
|
||||||
T.bar = 'hello'
|
T.bar = 'hello'
|
||||||
|
@ -477,8 +471,6 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_add_property_to_free_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignment")
|
TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignment")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local t = { u = {} }
|
local t = { u = {} }
|
||||||
|
@ -512,8 +504,6 @@ TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "width_subtyping")
|
TEST_CASE_FIXTURE(Fixture, "width_subtyping")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
function f(x : { q : number })
|
function f(x : { q : number })
|
||||||
|
@ -772,8 +762,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_for_left_unsealed_table_from_right_han
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "sealed_table_value_can_infer_an_indexer")
|
TEST_CASE_FIXTURE(Fixture, "sealed_table_value_can_infer_an_indexer")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: { a: string, [number]: string } = { a = "foo" }
|
local t: { a: string, [number]: string } = { a = "foo" }
|
||||||
)");
|
)");
|
||||||
|
@ -783,8 +771,6 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_value_can_infer_an_indexer")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "array_factory_function")
|
TEST_CASE_FIXTURE(Fixture, "array_factory_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function empty() return {} end
|
function empty() return {} end
|
||||||
local array: {string} = empty()
|
local array: {string} = empty()
|
||||||
|
@ -1175,8 +1161,6 @@ TEST_CASE_FIXTURE(Fixture, "defining_a_self_method_for_a_local_sealed_table_must
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "defining_a_method_for_a_local_unsealed_table_is_ok")
|
TEST_CASE_FIXTURE(Fixture, "defining_a_method_for_a_local_unsealed_table_is_ok")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {x = 1}
|
local t = {x = 1}
|
||||||
function t.m() end
|
function t.m() end
|
||||||
|
@ -1187,8 +1171,6 @@ TEST_CASE_FIXTURE(Fixture, "defining_a_method_for_a_local_unsealed_table_is_ok")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "defining_a_self_method_for_a_local_unsealed_table_is_ok")
|
TEST_CASE_FIXTURE(Fixture, "defining_a_self_method_for_a_local_unsealed_table_is_ok")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {x = 1}
|
local t = {x = 1}
|
||||||
function t:m() end
|
function t:m() end
|
||||||
|
@ -1468,11 +1450,6 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer")
|
TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type StringToStringMap = { [string]: string }
|
type StringToStringMap = { [string]: string }
|
||||||
local rt: StringToStringMap = { ["foo"] = 1 }
|
local rt: StringToStringMap = { ["foo"] = 1 }
|
||||||
|
@ -1518,11 +1495,6 @@ TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer2")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer3")
|
TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer3")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function foo(a: {[string]: number, a: string}) end
|
local function foo(a: {[string]: number, a: string}) end
|
||||||
foo({ a = 1 })
|
foo({ a = 1 })
|
||||||
|
@ -1609,8 +1581,6 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_extra_props_dont_report_multipl
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_extra_props_is_ok")
|
TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_extra_props_is_ok")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local vec3 = {x = 1, y = 2, z = 3}
|
local vec3 = {x = 1, y = 2, z = 3}
|
||||||
local vec1 = {x = 1}
|
local vec1 = {x = 1}
|
||||||
|
@ -1998,8 +1968,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_should_cope_with_optional_prope
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_should_cope_with_optional_properties_in_strict")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_should_cope_with_optional_properties_in_strict")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local buttons = {}
|
local buttons = {}
|
||||||
|
@ -2013,8 +1981,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_should_cope_with_optional_prope
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop")
|
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop")
|
||||||
{
|
{
|
||||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number, y: number }
|
type A = { x: number, y: number }
|
||||||
type B = { x: number, y: string }
|
type B = { x: number, y: string }
|
||||||
|
@ -2031,8 +1997,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop_nested")
|
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop_nested")
|
||||||
{
|
{
|
||||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type AS = { x: number, y: number }
|
type AS = { x: number, y: number }
|
||||||
type BS = { x: number, y: string }
|
type BS = { x: number, y: string }
|
||||||
|
@ -2054,11 +2018,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "error_detailed_metatable_prop")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "error_detailed_metatable_prop")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a1 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
|
local a1 = setmetatable({ x = 2, y = 3 }, { __call = function(s) end });
|
||||||
local b1 = setmetatable({ x = 2, y = "hello" }, { __call = function(s) end });
|
local b1 = setmetatable({ x = 2, y = "hello" }, { __call = function(s) end });
|
||||||
|
@ -2085,8 +2044,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_key")
|
TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_key")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { [number]: string }
|
type A = { [number]: string }
|
||||||
type B = { [string]: string }
|
type B = { [string]: string }
|
||||||
|
@ -2103,8 +2060,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value")
|
TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { [number]: number }
|
type A = { [number]: number }
|
||||||
type B = { [number]: string }
|
type B = { [number]: string }
|
||||||
|
@ -2121,10 +2076,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
|
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type Super = { x : number }
|
type Super = { x : number }
|
||||||
|
@ -2140,11 +2091,6 @@ a.p = { x = 9 }
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
|
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type Super = { x : number }
|
type Super = { x : number }
|
||||||
|
@ -2166,10 +2112,6 @@ caused by:
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
|
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[]{
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type Super = { x : number }
|
type Super = { x : number }
|
||||||
|
@ -2185,10 +2127,6 @@ a.p = { x = 9 }
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_metatable_type_call")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_metatable_type_call")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[]{
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local b
|
local b
|
||||||
b = setmetatable({}, {__call = b})
|
b = setmetatable({}, {__call = b})
|
||||||
|
@ -2201,11 +2139,6 @@ b()
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_shouldn't_add_optional_properties_to_sealed_tables")
|
TEST_CASE_FIXTURE(Fixture, "table_subtyping_shouldn't_add_optional_properties_to_sealed_tables")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauSubtypingAddOptPropsToUnsealedTables", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
local function setNumber(t: { p: number? }, x:number) t.p = x end
|
local function setNumber(t: { p: number? }, x:number) t.p = x end
|
||||||
|
@ -2706,8 +2639,6 @@ type t0<t32> = any
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "instantiate_table_cloning_2")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "instantiate_table_cloning_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type X<T> = T
|
type X<T> = T
|
||||||
type K = X<typeof(math)>
|
type K = X<typeof(math)>
|
||||||
|
@ -2725,8 +2656,6 @@ type K = X<typeof(math)>
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "instantiate_table_cloning_3")
|
TEST_CASE_FIXTURE(Fixture, "instantiate_table_cloning_3")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type X<T> = T
|
type X<T> = T
|
||||||
local a = {}
|
local a = {}
|
||||||
|
@ -2977,8 +2906,6 @@ TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "expected_indexer_value_type_extra")
|
TEST_CASE_FIXTURE(Fixture, "expected_indexer_value_type_extra")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauSubtypingAddOptPropsToUnsealedTables{"LuauSubtypingAddOptPropsToUnsealedTables", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type X = { { x: boolean?, y: boolean? } }
|
type X = { { x: boolean?, y: boolean? } }
|
||||||
|
|
||||||
|
|
|
@ -887,8 +887,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag subtypingVariance{"LuauTableSubtypingVariance2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
--!nolint
|
--!nolint
|
||||||
|
@ -928,7 +926,6 @@ TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_no_ice")
|
TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_no_ice")
|
||||||
{
|
{
|
||||||
ScopedFastInt sfi("LuauTypeInferRecursionLimit", 2);
|
ScopedFastInt sfi("LuauTypeInferRecursionLimit", 2);
|
||||||
ScopedFastFlag sff{"LuauRecursionLimitException", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function complex()
|
function complex()
|
||||||
|
|
|
@ -428,12 +428,6 @@ y = x
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
|
||||||
{"LuauTableSubtypingVariance2", true},
|
|
||||||
{"LuauUnsealedTableLiteral", true},
|
|
||||||
{"LuauSubtypingAddOptPropsToUnsealedTables", true},
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
-- the difference between this and unify_unsealed_table_union_check is the type annotation on x
|
-- the difference between this and unify_unsealed_table_union_check is the type annotation on x
|
||||||
local t = { x = 3, y = true }
|
local t = { x = 3, y = true }
|
||||||
|
|
|
@ -10,14 +10,9 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTINT(LuauVisitRecursionLimit)
|
LUAU_FASTINT(LuauVisitRecursionLimit)
|
||||||
|
|
||||||
struct VisitTypeVarFixture : Fixture
|
|
||||||
{
|
|
||||||
ScopedFastFlag flag2 = {"LuauRecursionLimitException", true};
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("VisitTypeVar");
|
TEST_SUITE_BEGIN("VisitTypeVar");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(VisitTypeVarFixture, "throw_when_limit_is_exceeded")
|
TEST_CASE_FIXTURE(Fixture, "throw_when_limit_is_exceeded")
|
||||||
{
|
{
|
||||||
ScopedFastInt sfi{"LuauVisitRecursionLimit", 3};
|
ScopedFastInt sfi{"LuauVisitRecursionLimit", 3};
|
||||||
|
|
||||||
|
@ -30,7 +25,7 @@ TEST_CASE_FIXTURE(VisitTypeVarFixture, "throw_when_limit_is_exceeded")
|
||||||
CHECK_THROWS_AS(toString(tType), RecursionLimitException);
|
CHECK_THROWS_AS(toString(tType), RecursionLimitException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(VisitTypeVarFixture, "dont_throw_when_limit_is_high_enough")
|
TEST_CASE_FIXTURE(Fixture, "dont_throw_when_limit_is_high_enough")
|
||||||
{
|
{
|
||||||
ScopedFastInt sfi{"LuauVisitRecursionLimit", 8};
|
ScopedFastInt sfi{"LuauVisitRecursionLimit", 8};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue