mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/533 (#560)
This commit is contained in:
parent
348ad4d417
commit
08ab7da4db
@ -1,10 +1,10 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@ -47,18 +47,24 @@ struct InstantiationConstraint
|
||||
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>;
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
Constraint(ConstraintV&& c, Location location);
|
||||
explicit Constraint(ConstraintV&& c);
|
||||
|
||||
Constraint(const Constraint&) = delete;
|
||||
Constraint& operator=(const Constraint&) = delete;
|
||||
|
||||
ConstraintV c;
|
||||
Location location;
|
||||
std::vector<NotNull<Constraint>> dependencies;
|
||||
};
|
||||
|
||||
|
@ -17,20 +17,7 @@
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
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 Scope2;
|
||||
|
||||
struct ConstraintGraphBuilder
|
||||
{
|
||||
@ -47,6 +34,10 @@ struct ConstraintGraphBuilder
|
||||
// A mapping of AST node to TypePackId.
|
||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{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);
|
||||
|
||||
@ -73,9 +64,8 @@ struct ConstraintGraphBuilder
|
||||
* 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 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.
|
||||
@ -99,6 +89,7 @@ struct ConstraintGraphBuilder
|
||||
void visit(Scope2* scope, AstStatReturn* ret);
|
||||
void visit(Scope2* scope, AstStatAssign* assign);
|
||||
void visit(Scope2* scope, AstStatIf* ifStatement);
|
||||
void visit(Scope2* scope, AstStatTypeAlias* alias);
|
||||
|
||||
TypePackId checkExprList(Scope2* scope, const AstArray<AstExpr*>& exprs);
|
||||
|
||||
@ -124,6 +115,24 @@ struct ConstraintGraphBuilder
|
||||
* @param fn the function expression to check.
|
||||
*/
|
||||
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 GeneralizationConstraint& 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);
|
||||
/**
|
||||
@ -85,7 +86,7 @@ struct ConstraintSolver
|
||||
* @param subType the sub-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
|
||||
@ -93,7 +94,7 @@ struct ConstraintSolver
|
||||
* @param subPack the sub-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:
|
||||
/**
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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 <optional>
|
||||
|
@ -169,6 +169,13 @@ struct GenericError
|
||||
bool operator==(const GenericError& rhs) const;
|
||||
};
|
||||
|
||||
struct InternalError
|
||||
{
|
||||
std::string message;
|
||||
|
||||
bool operator==(const InternalError& rhs) const;
|
||||
};
|
||||
|
||||
struct CannotCallNonFunction
|
||||
{
|
||||
TypeId ty;
|
||||
@ -293,12 +300,12 @@ struct NormalizationTooComplex
|
||||
}
|
||||
};
|
||||
|
||||
using TypeErrorData =
|
||||
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
|
||||
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
|
||||
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed,
|
||||
ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotInferBinaryOperation,
|
||||
MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated, NormalizationTooComplex>;
|
||||
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
|
||||
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
|
||||
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError,
|
||||
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
|
||||
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
|
||||
TypesAreUnrelated, NormalizationTooComplex>;
|
||||
|
||||
struct TypeError
|
||||
{
|
||||
@ -339,7 +346,13 @@ T* get(TypeError& e)
|
||||
|
||||
using ErrorVec = std::vector<TypeError>;
|
||||
|
||||
struct TypeErrorToStringOptions
|
||||
{
|
||||
FileResolver* fileResolver = nullptr;
|
||||
};
|
||||
|
||||
std::string toString(const TypeError& error);
|
||||
std::string toString(const TypeError& error, TypeErrorToStringOptions options);
|
||||
|
||||
bool containsParseErrorName(const TypeError& error);
|
||||
|
||||
@ -356,4 +369,24 @@ struct InternalErrorReporter
|
||||
[[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
|
||||
|
@ -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 UnknownPropButFoundLikeProp& e);
|
||||
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 MissingProperties& 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
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/FileResolver.h"
|
||||
#include "Luau/ParseOptions.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/ParseResult.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
|
||||
#include <memory>
|
||||
@ -19,7 +20,9 @@ struct Module;
|
||||
|
||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||
using ModulePtr = std::shared_ptr<Module>;
|
||||
struct Scope2;
|
||||
|
||||
class AstType;
|
||||
class AstTypePack;
|
||||
|
||||
/// Root of the AST of a parsed source file
|
||||
struct SourceModule
|
||||
@ -73,6 +76,8 @@ struct Module
|
||||
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
|
||||
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{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;
|
||||
ErrorVec errors;
|
||||
|
@ -9,8 +9,8 @@ namespace Luau
|
||||
|
||||
struct InternalErrorReporter;
|
||||
|
||||
bool isSubtype(TypeId superTy, TypeId subTy, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypePackId superTy, TypePackId subTy, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, 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, const ModulePtr& module, InternalErrorReporter& ice);
|
||||
|
@ -6,8 +6,6 @@
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
|
||||
LUAU_FASTFLAG(LuauRecursionLimitException);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -39,21 +37,12 @@ private:
|
||||
|
||||
struct RecursionLimiter : RecursionCounter
|
||||
{
|
||||
// TODO: remove ctx after LuauRecursionLimitException is removed
|
||||
RecursionLimiter(int* count, int limit, const char* ctx)
|
||||
RecursionLimiter(int* count, int limit)
|
||||
: RecursionCounter(count)
|
||||
{
|
||||
LUAU_ASSERT(ctx);
|
||||
if (limit > 0 && *count > limit)
|
||||
{
|
||||
if (FFlag::LuauRecursionLimitException)
|
||||
throw RecursionLimitException();
|
||||
else
|
||||
{
|
||||
std::string m = "Internal recursion counter limit exceeded: ";
|
||||
m += ctx;
|
||||
throw std::runtime_error(m);
|
||||
}
|
||||
throw RecursionLimitException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
|
||||
@ -64,4 +65,21 @@ struct Scope
|
||||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||
};
|
||||
|
||||
struct Scope2
|
||||
{
|
||||
// The parent scope of this scope. Null if there is no parent (i.e. this
|
||||
// is the module-level scope).
|
||||
Scope2* parent = nullptr;
|
||||
// All the children of this scope.
|
||||
std::vector<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
|
||||
|
@ -287,7 +287,6 @@ struct FunctionTypeVar
|
||||
bool hasSelf;
|
||||
Tags tags;
|
||||
bool hasNoGenerics = false;
|
||||
bool generalized = false;
|
||||
};
|
||||
|
||||
enum class TableState
|
||||
|
@ -117,6 +117,7 @@ struct Generic
|
||||
explicit Generic(const Name& name);
|
||||
explicit Generic(Scope2* scope);
|
||||
Generic(TypeLevel level, const Name& name);
|
||||
Generic(Scope2* scope, const Name& name);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
|
@ -79,12 +79,8 @@ private:
|
||||
void tryUnifySingletons(TypeId subTy, TypeId superTy);
|
||||
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = 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 tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyIndexer(const TableIndexer& subIndexer, const TableIndexer& superIndexer);
|
||||
|
||||
TypeId widen(TypeId ty);
|
||||
TypePackId widen(TypePackId tp);
|
||||
|
@ -169,7 +169,7 @@ struct GenericTypeVarVisitor
|
||||
|
||||
void traverse(TypeId ty)
|
||||
{
|
||||
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit, "TypeVarVisitor"};
|
||||
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit};
|
||||
|
||||
if (visit_detail::hasSeen(seen, ty))
|
||||
{
|
||||
|
@ -317,7 +317,7 @@ TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
|
||||
if (tp->persistent)
|
||||
return tp;
|
||||
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypePackId");
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||
|
||||
TypePackId& res = cloneState.seenTypePacks[tp];
|
||||
|
||||
@ -335,7 +335,7 @@ TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
|
||||
if (typeId->persistent)
|
||||
return typeId;
|
||||
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypeId");
|
||||
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
|
||||
|
||||
TypeId& res = cloneState.seenTypes[typeId];
|
||||
|
||||
|
@ -5,9 +5,8 @@
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
Constraint::Constraint(ConstraintV&& c, Location location)
|
||||
Constraint::Constraint(ConstraintV&& c)
|
||||
: c(std::move(c))
|
||||
, location(location)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2,28 +2,13 @@
|
||||
|
||||
#include "Luau/ConstraintGraphBuilder.h"
|
||||
|
||||
#include "Luau/Scope.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
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)
|
||||
: singletonTypes(getSingletonTypes())
|
||||
, arena(arena)
|
||||
@ -59,10 +44,10 @@ Scope2* ConstraintGraphBuilder::childScope(Location location, Scope2* parent)
|
||||
return borrow;
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::addConstraint(Scope2* scope, ConstraintV cv, Location location)
|
||||
void ConstraintGraphBuilder::addConstraint(Scope2* scope, ConstraintV cv)
|
||||
{
|
||||
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)
|
||||
@ -79,6 +64,13 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
||||
rootScope = scopes.back().second.get();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -102,6 +94,8 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStat* stat)
|
||||
checkPack(scope, e->expr);
|
||||
else if (auto i = stat->as<AstStatIf>())
|
||||
visit(scope, i);
|
||||
else if (auto a = stat->as<AstStatTypeAlias>())
|
||||
visit(scope, a);
|
||||
else
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
@ -114,8 +108,14 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatLocal* local)
|
||||
|
||||
for (AstLocal* local : local->vars)
|
||||
{
|
||||
// TODO annotations
|
||||
TypeId ty = freshType(scope);
|
||||
|
||||
if (local->annotation)
|
||||
{
|
||||
TypeId annotation = resolveType(scope, local->annotation);
|
||||
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
||||
}
|
||||
|
||||
varTypes.push_back(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()};
|
||||
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
|
||||
addConstraint(scope, PackSubtypeConstraint{exprPack, tailPack}, local->location);
|
||||
addConstraint(scope, PackSubtypeConstraint{exprPack, tailPack});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId exprType = check(scope, local->values.data[i]);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
addConstraint(scope, std::move(c));
|
||||
@ -240,7 +240,7 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatFunction* function)
|
||||
|
||||
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);
|
||||
|
||||
addConstraint(scope, std::move(c));
|
||||
@ -251,13 +251,26 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatReturn* ret)
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
visit(scope, stat);
|
||||
}
|
||||
@ -267,7 +280,7 @@ void ConstraintGraphBuilder::visit(Scope2* scope, AstStatAssign* assign)
|
||||
TypePackId varPackId = checkExprList(scope, assign->vars);
|
||||
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)
|
||||
@ -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)
|
||||
{
|
||||
LUAU_ASSERT(scope);
|
||||
@ -350,13 +385,13 @@ TypePackId ConstraintGraphBuilder::checkPack(Scope2* scope, AstExpr* expr)
|
||||
astOriginalCallTypes[call->func] = fnType;
|
||||
|
||||
TypeId instantiatedType = freshType(scope);
|
||||
addConstraint(scope, InstantiationConstraint{instantiatedType, fnType}, expr->location);
|
||||
addConstraint(scope, InstantiationConstraint{instantiatedType, fnType});
|
||||
|
||||
TypePackId rets = freshTypePack(scope);
|
||||
FunctionTypeVar ftv(arena->addTypePack(TypePack{args, {}}), rets);
|
||||
TypeId inferredFnType = arena->addType(ftv);
|
||||
|
||||
addConstraint(scope, SubtypeConstraint{inferredFnType, instantiatedType}, expr->location);
|
||||
addConstraint(scope, SubtypeConstraint{inferredFnType, instantiatedType});
|
||||
result = rets;
|
||||
}
|
||||
else
|
||||
@ -413,7 +448,7 @@ TypeId ConstraintGraphBuilder::check(Scope2* scope, AstExpr* expr)
|
||||
TypePack onePack{{typeResult}, freshTypePack(scope)};
|
||||
TypePackId oneTypePack = arena->addTypePack(std::move(onePack));
|
||||
|
||||
addConstraint(scope, PackSubtypeConstraint{packResult, oneTypePack}, expr->location);
|
||||
addConstraint(scope, PackSubtypeConstraint{packResult, oneTypePack});
|
||||
|
||||
return typeResult;
|
||||
}
|
||||
@ -454,7 +489,7 @@ TypeId ConstraintGraphBuilder::check(Scope2* scope, AstExprIndexName* indexName)
|
||||
|
||||
TypeId expectedTableType = arena->addType(std::move(ttv));
|
||||
|
||||
addConstraint(scope, SubtypeConstraint{obj, expectedTableType}, indexName->location);
|
||||
addConstraint(scope, SubtypeConstraint{obj, expectedTableType});
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -465,8 +500,7 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
||||
LUAU_ASSERT(ttv);
|
||||
|
||||
auto createIndexer = [this, scope, ttv](
|
||||
TypeId currentIndexType, TypeId currentResultType, Location itemLocation, std::optional<Location> keyLocation) {
|
||||
auto createIndexer = [this, scope, ttv](TypeId currentIndexType, TypeId currentResultType) {
|
||||
if (!ttv->indexer)
|
||||
{
|
||||
TypeId indexType = this->freshType(scope);
|
||||
@ -474,8 +508,8 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||
ttv->indexer = TableIndexer{indexType, resultType};
|
||||
}
|
||||
|
||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexType, currentIndexType}, keyLocation ? *keyLocation : itemLocation);
|
||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType}, itemLocation);
|
||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
|
||||
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
|
||||
};
|
||||
|
||||
for (const AstExprTable::Item& item : expr->items)
|
||||
@ -495,13 +529,13 @@ TypeId ConstraintGraphBuilder::checkExprTable(Scope2* scope, AstExprTable* expr)
|
||||
}
|
||||
else
|
||||
{
|
||||
createIndexer(keyTy, itemTy, item.value->location, item.key->location);
|
||||
createIndexer(keyTy, itemTy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
innerScope->returnType = returnType;
|
||||
|
||||
if (fn->returnAnnotation)
|
||||
{
|
||||
TypePackId annotatedRetType = resolveTypePack(innerScope, *fn->returnAnnotation);
|
||||
addConstraint(innerScope, PackSubtypeConstraint{returnType, annotatedRetType});
|
||||
}
|
||||
|
||||
std::vector<TypeId> argTypes;
|
||||
|
||||
for (AstLocal* local : fn->args)
|
||||
{
|
||||
TypeId t = freshType(innerScope);
|
||||
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};
|
||||
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
|
||||
LUAU_ASSERT(actualFunctionType);
|
||||
@ -541,10 +589,171 @@ void ConstraintGraphBuilder::checkFunctionBody(Scope2* scope, AstExprFunction* f
|
||||
if (nullptr != getFallthrough(fn->body))
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (const auto& c : scope->constraints)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/Instantiation.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/Quantify.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Unifier.h"
|
||||
@ -179,6 +180,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
||||
success = tryDispatch(*gc, constraint, force);
|
||||
else if (auto ic = get<InstantiationConstraint>(*constraint))
|
||||
success = tryDispatch(*ic, constraint, force);
|
||||
else if (auto nc = get<NameConstraint>(*constraint))
|
||||
success = tryDispatch(*nc, constraint);
|
||||
else
|
||||
LUAU_ASSERT(0);
|
||||
|
||||
@ -197,7 +200,7 @@ bool ConstraintSolver::tryDispatch(const SubtypeConstraint& c, NotNull<const Con
|
||||
else if (isBlocked(c.superType))
|
||||
return block(c.superType, constraint);
|
||||
|
||||
unify(c.subType, c.superType, constraint->location);
|
||||
unify(c.subType, c.superType);
|
||||
|
||||
unblock(c.subType);
|
||||
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)
|
||||
{
|
||||
unify(c.subPack, c.superPack, constraint->location);
|
||||
unify(c.subPack, c.superPack);
|
||||
unblock(c.subPack);
|
||||
unblock(c.superPack);
|
||||
|
||||
@ -222,7 +225,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||
if (isBlocked(c.generalizedType))
|
||||
asMutable(c.generalizedType)->ty.emplace<BoundTypeVar>(c.sourceType);
|
||||
else
|
||||
unify(c.generalizedType, c.sourceType, constraint->location);
|
||||
unify(c.generalizedType, c.sourceType);
|
||||
|
||||
TypeId generalized = quantify(arena, c.sourceType, c.scope);
|
||||
*asMutable(c.sourceType) = *generalized;
|
||||
@ -243,12 +246,28 @@ bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<con
|
||||
std::optional<TypeId> instantiated = inst.substitute(c.superType);
|
||||
LUAU_ASSERT(instantiated); // TODO FIXME HANDLE THIS
|
||||
|
||||
unify(c.subType, *instantiated, constraint->location);
|
||||
unify(c.subType, *instantiated);
|
||||
unblock(c.subType);
|
||||
|
||||
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)
|
||||
{
|
||||
blocked[target].push_back(constraint);
|
||||
@ -321,19 +340,19 @@ bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
|
||||
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};
|
||||
Unifier u{arena, Mode::Strict, location, Covariant, sharedState};
|
||||
Unifier u{arena, Mode::Strict, Location{}, Covariant, sharedState};
|
||||
|
||||
u.tryUnify(subType, superType);
|
||||
u.log.commit();
|
||||
}
|
||||
|
||||
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack, Location location)
|
||||
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack)
|
||||
{
|
||||
UnifierSharedState sharedState{&iceReporter};
|
||||
Unifier u{arena, Mode::Strict, location, Covariant, sharedState};
|
||||
Unifier u{arena, Mode::Strict, Location{}, Covariant, sharedState};
|
||||
|
||||
u.tryUnify(subPack, superPack);
|
||||
u.log.commit();
|
||||
|
@ -7,6 +7,9 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
std::string s = "expects ";
|
||||
@ -49,6 +52,8 @@ namespace Luau
|
||||
|
||||
struct ErrorConverter
|
||||
{
|
||||
FileResolver* fileResolver = nullptr;
|
||||
|
||||
std::string operator()(const Luau::TypeMismatch& tm) const
|
||||
{
|
||||
std::string givenTypeName = Luau::toString(tm.givenType);
|
||||
@ -62,8 +67,18 @@ struct ErrorConverter
|
||||
{
|
||||
if (auto wantedDefinitionModule = getDefinitionModuleName(tm.wantedType))
|
||||
{
|
||||
result = "Type '" + givenTypeName + "' from '" + *givenDefinitionModule + "' could not be converted into '" + wantedTypeName +
|
||||
"' from '" + *wantedDefinitionModule + "'";
|
||||
if (FFlag::LuauTypeMismatchModuleNameResolution && fileResolver != nullptr)
|
||||
{
|
||||
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())
|
||||
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())
|
||||
{
|
||||
@ -280,6 +302,11 @@ struct ErrorConverter
|
||||
return e.message;
|
||||
}
|
||||
|
||||
std::string operator()(const Luau::InternalError& e) const
|
||||
{
|
||||
return e.message;
|
||||
}
|
||||
|
||||
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
||||
{
|
||||
return "Cannot call non-function " + toString(e.ty);
|
||||
@ -598,6 +625,11 @@ bool GenericError::operator==(const GenericError& rhs) const
|
||||
return message == rhs.message;
|
||||
}
|
||||
|
||||
bool InternalError::operator==(const InternalError& rhs) const
|
||||
{
|
||||
return message == rhs.message;
|
||||
}
|
||||
|
||||
bool CannotCallNonFunction::operator==(const CannotCallNonFunction& rhs) const
|
||||
{
|
||||
return ty == rhs.ty;
|
||||
@ -685,7 +717,12 @@ bool TypesAreUnrelated::operator==(const TypesAreUnrelated& rhs) const
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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, InternalError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||
{
|
||||
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)
|
||||
{
|
||||
std::runtime_error error("Internal error in " + moduleName + " at " + toString(location) + ": " + message);
|
||||
if (FFlag::LuauUseInternalCompilerErrorException)
|
||||
{
|
||||
InternalCompilerError error(message, moduleName, location);
|
||||
|
||||
if (onInternalError)
|
||||
onInternalError(error.what());
|
||||
if (onInternalError)
|
||||
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)
|
||||
{
|
||||
std::runtime_error error("Internal error in " + moduleName + ": " + message);
|
||||
if (FFlag::LuauUseInternalCompilerErrorException)
|
||||
{
|
||||
InternalCompilerError error(message, moduleName);
|
||||
|
||||
if (onInternalError)
|
||||
onInternalError(error.what());
|
||||
if (onInternalError)
|
||||
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
|
||||
|
@ -801,6 +801,8 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
|
||||
result->astTypes = std::move(cgb.astTypes);
|
||||
result->astTypePacks = std::move(cgb.astTypePacks);
|
||||
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
||||
result->astResolvedTypes = std::move(cgb.astResolvedTypes);
|
||||
result->astResolvedTypePacks = std::move(cgb.astResolvedTypePacks);
|
||||
|
||||
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>)
|
||||
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>)
|
||||
stream << "CannotCallNonFunction { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
#include "Luau/ConstraintGraphBuilder.h" // FIXME: For Scope2 TODO pull out into its own header
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -2,11 +2,10 @@
|
||||
|
||||
#include "Luau/Quantify.h"
|
||||
|
||||
#include "Luau/ConstraintGraphBuilder.h" // TODO for Scope2; move to separate header
|
||||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/Substitution.h"
|
||||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
#include "Luau/ConstraintGraphBuilder.h" // TODO for Scope2; move to separate header
|
||||
|
||||
LUAU_FASTFLAG(LuauAlwaysQuantify);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
@ -177,8 +176,6 @@ void quantify(TypeId ty, TypeLevel level)
|
||||
|
||||
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
||||
ftv->hasNoGenerics = true;
|
||||
|
||||
ftv->generalized = true;
|
||||
}
|
||||
|
||||
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)
|
||||
ftv->hasNoGenerics = true;
|
||||
|
||||
ftv->generalized = true;
|
||||
}
|
||||
|
||||
struct PureQuantifier : Substitution
|
||||
|
@ -121,4 +121,36 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
|
||||
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
|
||||
|
@ -1411,6 +1411,12 @@ std::string toString(const Constraint& c, ToStringOptions& opts)
|
||||
opts.nameMap = std::move(superStr.nameMap);
|
||||
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
|
||||
{
|
||||
LUAU_ASSERT(false);
|
||||
|
@ -7,6 +7,9 @@
|
||||
#include "Luau/AstQuery.h"
|
||||
#include "Luau/Clone.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
|
||||
{
|
||||
@ -39,6 +42,104 @@ struct TypeChecker2 : public AstVisitor
|
||||
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
|
||||
{
|
||||
size_t count = std::min(assign->vars.size, assign->values.size);
|
||||
@ -62,6 +163,30 @@ struct TypeChecker2 : public AstVisitor
|
||||
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
|
||||
{
|
||||
TypePackId expectedRetType = lookupPack(call);
|
||||
@ -91,6 +216,35 @@ struct TypeChecker2 : public AstVisitor
|
||||
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
|
||||
{
|
||||
TypeId leftType = lookupType(indexName->expr);
|
||||
@ -144,6 +298,25 @@ struct TypeChecker2 : public AstVisitor
|
||||
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)
|
||||
{
|
||||
module->errors.emplace_back(location, sourceModule->name, std::move(data));
|
||||
|
@ -35,13 +35,9 @@ LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRecursionLimitException, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauApplyTypeFunctionFix, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
|
||||
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)
|
||||
{
|
||||
if (FFlag::LuauRecursionLimitException)
|
||||
{
|
||||
try
|
||||
{
|
||||
return checkWithoutRecursionCheck(module, mode, environmentScope);
|
||||
}
|
||||
catch (const RecursionLimitException&)
|
||||
{
|
||||
reportErrorCodeTooComplex(module.root->location);
|
||||
return std::move(currentModule);
|
||||
}
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
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)
|
||||
@ -445,22 +434,15 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
|
||||
reportErrorCodeTooComplex(block.location);
|
||||
return;
|
||||
}
|
||||
if (FFlag::LuauRecursionLimitException)
|
||||
{
|
||||
try
|
||||
{
|
||||
checkBlockWithoutRecursionCheck(scope, block);
|
||||
}
|
||||
catch (const RecursionLimitException&)
|
||||
{
|
||||
reportErrorCodeTooComplex(block.location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
checkBlockWithoutRecursionCheck(scope, block);
|
||||
}
|
||||
catch (const RecursionLimitException&)
|
||||
{
|
||||
reportErrorCodeTooComplex(block.location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& block)
|
||||
@ -1917,7 +1899,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
||||
|
||||
for (TypeId t : utv)
|
||||
{
|
||||
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit, "getIndexTypeForType unions");
|
||||
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
// Not needed when we normalize types.
|
||||
if (get<AnyTypeVar>(follow(t)))
|
||||
@ -1967,7 +1949,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
||||
|
||||
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))
|
||||
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};
|
||||
table.definitionModuleName = currentModuleName;
|
||||
return addType(table);
|
||||
@ -5175,9 +5157,7 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack
|
||||
|
||||
bool ApplyTypeFunction::isDirty(TypeId ty)
|
||||
{
|
||||
if (FFlag::LuauApplyTypeFunctionFix && typeArguments.count(ty))
|
||||
return true;
|
||||
else if (!FFlag::LuauApplyTypeFunctionFix && get<GenericTypeVar>(ty))
|
||||
if (typeArguments.count(ty))
|
||||
return true;
|
||||
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
|
||||
{
|
||||
@ -5191,9 +5171,7 @@ bool ApplyTypeFunction::isDirty(TypeId ty)
|
||||
|
||||
bool ApplyTypeFunction::isDirty(TypePackId tp)
|
||||
{
|
||||
if (FFlag::LuauApplyTypeFunctionFix && typePackArguments.count(tp))
|
||||
return true;
|
||||
else if (!FFlag::LuauApplyTypeFunctionFix && get<GenericTypePack>(tp))
|
||||
if (typePackArguments.count(tp))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@ -5218,29 +5196,15 @@ bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
|
||||
TypeId ApplyTypeFunction::clean(TypeId ty)
|
||||
{
|
||||
TypeId& arg = typeArguments[ty];
|
||||
if (FFlag::LuauApplyTypeFunctionFix)
|
||||
{
|
||||
LUAU_ASSERT(arg);
|
||||
return arg;
|
||||
}
|
||||
else if (arg)
|
||||
return arg;
|
||||
else
|
||||
return addType(FreeTypeVar{level});
|
||||
LUAU_ASSERT(arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
TypePackId ApplyTypeFunction::clean(TypePackId tp)
|
||||
{
|
||||
TypePackId& arg = typePackArguments[tp];
|
||||
if (FFlag::LuauApplyTypeFunctionFix)
|
||||
{
|
||||
LUAU_ASSERT(arg);
|
||||
return arg;
|
||||
}
|
||||
else if (arg)
|
||||
return arg;
|
||||
else
|
||||
return addTypePack(FreeTypePack{level});
|
||||
LUAU_ASSERT(arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
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);
|
||||
bool needsClone = follow(tf.type) == target;
|
||||
bool shouldMutate = (!FFlag::LuauOnlyMutateInstantiatedTables || getTableType(tf.type));
|
||||
bool shouldMutate = getTableType(tf.type);
|
||||
TableTypeVar* ttv = getMutableTableType(target);
|
||||
|
||||
if (shouldMutate && ttv && needsClone)
|
||||
|
@ -23,7 +23,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauSubtypingAddOptPropsToUnsealedTables)
|
||||
LUAU_FASTFLAG(LuauNonCopyableTypeVarFields)
|
||||
|
||||
namespace Luau
|
||||
@ -172,22 +171,15 @@ bool isString(TypeId ty)
|
||||
// Returns true when ty is a supertype of string
|
||||
bool maybeString(TypeId ty)
|
||||
{
|
||||
if (FFlag::LuauSubtypingAddOptPropsToUnsealedTables)
|
||||
{
|
||||
ty = follow(ty);
|
||||
ty = follow(ty);
|
||||
|
||||
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty))
|
||||
return true;
|
||||
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty))
|
||||
return true;
|
||||
|
||||
if (auto utv = get<UnionTypeVar>(ty))
|
||||
return std::any_of(begin(utv), end(utv), maybeString);
|
||||
if (auto utv = get<UnionTypeVar>(ty))
|
||||
return std::any_of(begin(utv), end(utv), maybeString);
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isString(ty);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isThread(TypeId ty)
|
||||
@ -369,7 +361,7 @@ bool maybeSingleton(TypeId ty)
|
||||
|
||||
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
||||
{
|
||||
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit, "hasLength");
|
||||
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
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;
|
||||
|
||||
Error::Error()
|
||||
|
@ -17,11 +17,8 @@ LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeInferLowerBoundsIterationLimit, 2000);
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance2, false);
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingAddOptPropsToUnsealedTables, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTxnLogRefreshFunctionPointers, false)
|
||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||
|
||||
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)
|
||||
{
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "TypeId tryUnify_");
|
||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
++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)
|
||||
{
|
||||
RecursionLimiter _ra(&sharedState.counters.recursionCount,
|
||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit, "TypePackId tryUnify_");
|
||||
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
++sharedState.counters.iterationCount;
|
||||
|
||||
@ -1316,12 +1313,9 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
|
||||
tryUnify_(subFunction->retTypes, superFunction->retTypes);
|
||||
}
|
||||
|
||||
if (FFlag::LuauTxnLogRefreshFunctionPointers)
|
||||
{
|
||||
// Updating the log may have invalidated the function pointers
|
||||
superFunction = log.getMutable<FunctionTypeVar>(superTy);
|
||||
subFunction = log.getMutable<FunctionTypeVar>(subTy);
|
||||
}
|
||||
// Updating the log may have invalidated the function pointers
|
||||
superFunction = log.getMutable<FunctionTypeVar>(superTy);
|
||||
subFunction = log.getMutable<FunctionTypeVar>(subTy);
|
||||
|
||||
ctx = context;
|
||||
|
||||
@ -1360,9 +1354,6 @@ struct Resetter
|
||||
|
||||
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* subTable = log.getMutable<TableTypeVar>(subTy);
|
||||
|
||||
@ -1379,8 +1370,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||
{
|
||||
auto subIter = subTable->props.find(propName);
|
||||
|
||||
if (subIter == subTable->props.end() && (!FFlag::LuauSubtypingAddOptPropsToUnsealedTables || subTable->state == TableState::Unsealed) &&
|
||||
!isOptional(superProp.type))
|
||||
if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type))
|
||||
missingProperties.push_back(propName);
|
||||
}
|
||||
|
||||
@ -1398,7 +1388,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1443,7 +1433,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||
if (innerState.errors.empty())
|
||||
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? }`
|
||||
// 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?)
|
||||
@ -1512,9 +1502,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
|
||||
else if (variance == Covariant)
|
||||
{
|
||||
}
|
||||
else if (!FFlag::LuauSubtypingAddOptPropsToUnsealedTables && isOptional(prop.type))
|
||||
{
|
||||
}
|
||||
else if (superTable->state == TableState::Free)
|
||||
{
|
||||
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}});
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const MetatableTypeVar* superMetatable = get<MetatableTypeVar>(superTy);
|
||||
@ -2068,14 +1765,6 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
|
||||
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)
|
||||
{
|
||||
while (true)
|
||||
@ -2435,7 +2124,7 @@ void Unifier::occursCheck(TypeId needle, TypeId haystack)
|
||||
void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
|
||||
{
|
||||
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) {
|
||||
occursCheck(seen, needle, tv);
|
||||
@ -2506,7 +2195,7 @@ void Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
|
||||
ice("Expected needle pack to be free");
|
||||
|
||||
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))
|
||||
{
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "FileUtils.h"
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauTimeTracing)
|
||||
LUAU_FASTFLAG(LuauTypeMismatchModuleNameResolution)
|
||||
|
||||
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))
|
||||
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
|
||||
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_WERROR "Warnings as errors" 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)
|
||||
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(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)
|
||||
# disable partial redundancy elimination which regresses interpreter codegen substantially in VS2022:
|
||||
# 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)
|
||||
// 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.
|
||||
// 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.
|
||||
// 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.
|
||||
// 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.
|
||||
@ -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.
|
||||
// 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.
|
||||
|
||||
// # 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
|
||||
{
|
||||
// NOP: noop
|
||||
@ -380,8 +389,10 @@ enum LuauOpcode
|
||||
// Bytecode tags, used internally for bytecode encoded as a string
|
||||
enum LuauBytecodeTag
|
||||
{
|
||||
// Bytecode version
|
||||
LBC_VERSION = 2,
|
||||
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
|
||||
LBC_VERSION_MIN = 2,
|
||||
LBC_VERSION_MAX = 2,
|
||||
LBC_VERSION_TARGET = 2,
|
||||
// Types of constant table entries
|
||||
LBC_CONSTANT_NIL = 0,
|
||||
LBC_CONSTANT_BOOLEAN,
|
||||
|
@ -119,6 +119,8 @@ public:
|
||||
|
||||
static std::string getError(const std::string& message);
|
||||
|
||||
static uint8_t getVersion();
|
||||
|
||||
private:
|
||||
struct Constant
|
||||
{
|
||||
|
@ -9,6 +9,9 @@
|
||||
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 kMaxClosureCount = 1 << 15;
|
||||
|
||||
@ -572,7 +575,10 @@ void BytecodeBuilder::finalize()
|
||||
bytecode.reserve(capacity);
|
||||
|
||||
// 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);
|
||||
|
||||
@ -1040,7 +1046,7 @@ void BytecodeBuilder::expandJumps()
|
||||
|
||||
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;
|
||||
result += char(0);
|
||||
result += message;
|
||||
@ -1048,6 +1054,12 @@ std::string BytecodeBuilder::getError(const std::string& message)
|
||||
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
|
||||
void BytecodeBuilder::validate() const
|
||||
{
|
||||
|
@ -16,8 +16,6 @@
|
||||
#include <bitset>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileIterNoPairs, false)
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
|
||||
|
||||
@ -2672,7 +2670,7 @@ struct Compiler
|
||||
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
||||
{
|
||||
skipOp = LOP_FORGPREP_NEXT;
|
||||
loopOp = FFlag::LuauCompileIterNoPairs ? LOP_FORGLOOP : LOP_FORGLOOP_NEXT;
|
||||
loopOp = LOP_FORGLOOP;
|
||||
}
|
||||
}
|
||||
else if (stat->values.size == 2)
|
||||
@ -2682,7 +2680,7 @@ struct Compiler
|
||||
if (builtin.isGlobal("next")) // for .. in next,t
|
||||
{
|
||||
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;
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
if (version != LBC_VERSION)
|
||||
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
||||
{
|
||||
char chunkid[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;
|
||||
}
|
||||
|
||||
|
@ -261,8 +261,6 @@ L1: RETURN R0 0
|
||||
|
||||
TEST_CASE("ForBytecode")
|
||||
{
|
||||
ScopedFastFlag sff2("LuauCompileIterNoPairs", false);
|
||||
|
||||
// basic for loop: variable directly refers to internal iteration index (R2)
|
||||
CHECK_EQ("\n" + compileFunction0("for i=1,5 do print(i) end"), R"(
|
||||
LOADN R2 1
|
||||
@ -329,7 +327,7 @@ L0: GETIMPORT R5 3
|
||||
MOVE R6 R3
|
||||
MOVE R7 R4
|
||||
CALL R5 2 0
|
||||
L1: FORGLOOP_NEXT R0 L0
|
||||
L1: FORGLOOP R0 L0 2
|
||||
RETURN R0 0
|
||||
)");
|
||||
|
||||
@ -342,7 +340,7 @@ L0: GETIMPORT R5 3
|
||||
MOVE R6 R3
|
||||
MOVE R7 R4
|
||||
CALL R5 2 0
|
||||
L1: FORGLOOP_NEXT R0 L0
|
||||
L1: FORGLOOP R0 L0 2
|
||||
RETURN R0 0
|
||||
)");
|
||||
}
|
||||
@ -2262,8 +2260,6 @@ TEST_CASE("TypeAliasing")
|
||||
|
||||
TEST_CASE("DebugLineInfo")
|
||||
{
|
||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
||||
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Lines);
|
||||
Luau::compileOrThrow(bcb, R"(
|
||||
@ -2313,7 +2309,7 @@ return result
|
||||
15: L0: MOVE R7 R1
|
||||
15: MOVE R8 R5
|
||||
15: CONCAT R1 R7 R8
|
||||
14: L1: FORGLOOP_NEXT R2 L0
|
||||
14: L1: FORGLOOP R2 L0 1
|
||||
17: RETURN R1 1
|
||||
)");
|
||||
}
|
||||
@ -2545,8 +2541,6 @@ a
|
||||
|
||||
TEST_CASE("DebugSource")
|
||||
{
|
||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
||||
|
||||
const char* source = R"(
|
||||
local kSelectedBiomes = {
|
||||
['Mountains'] = true,
|
||||
@ -2614,7 +2608,7 @@ L0: MOVE R7 R1
|
||||
MOVE R8 R5
|
||||
CONCAT R1 R7 R8
|
||||
14: for k in pairs(kSelectedBiomes) do
|
||||
L1: FORGLOOP_NEXT R2 L0
|
||||
L1: FORGLOOP R2 L0 1
|
||||
17: return result
|
||||
RETURN R1 1
|
||||
)");
|
||||
@ -2622,8 +2616,6 @@ RETURN R1 1
|
||||
|
||||
TEST_CASE("DebugLocals")
|
||||
{
|
||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
||||
|
||||
const char* source = R"(
|
||||
function foo(e, f)
|
||||
local a = 1
|
||||
@ -2661,12 +2653,12 @@ end
|
||||
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 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 4: reg 3, start pc 26 line 16, end pc 30 line 16
|
||||
local 5: reg 0, start pc 0 line 3, end pc 34 line 21
|
||||
local 6: reg 1, start pc 0 line 3, end pc 34 line 21
|
||||
local 7: reg 2, start pc 1 line 4, end pc 34 line 21
|
||||
local 8: reg 3, start pc 34 line 21, end pc 34 line 21
|
||||
local 3: reg 3, start pc 22 line 12, end pc 25 line 12
|
||||
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 35 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 35 line 21
|
||||
local 8: reg 3, start pc 35 line 21, end pc 35 line 21
|
||||
3: LOADN R2 1
|
||||
4: LOADN R5 1
|
||||
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 R10 R7
|
||||
8: CALL R8 2 0
|
||||
7: L3: FORGLOOP_NEXT R3 L2
|
||||
7: L3: FORGLOOP R3 L2 2
|
||||
11: LOADN R3 2
|
||||
12: GETIMPORT R4 1
|
||||
12: LOADN R5 2
|
||||
@ -3795,8 +3787,6 @@ RETURN R0 1
|
||||
|
||||
TEST_CASE("SharedClosure")
|
||||
{
|
||||
ScopedFastFlag sff("LuauCompileIterNoPairs", false);
|
||||
|
||||
// closures can be shared even if functions refer to upvalues, as long as upvalues are top-level
|
||||
CHECK_EQ("\n" + compileFunction(R"(
|
||||
local val = ...
|
||||
@ -3939,7 +3929,7 @@ L2: GETIMPORT R5 1
|
||||
NEWCLOSURE R6 P1
|
||||
CAPTURE VAL R3
|
||||
CALL R5 1 0
|
||||
L3: FORGLOOP_NEXT R0 L2
|
||||
L3: FORGLOOP R0 L2 2
|
||||
LOADN R2 1
|
||||
LOADN R0 10
|
||||
LOADN R1 1
|
||||
|
@ -2,13 +2,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Config.h"
|
||||
#include "Luau/ConstraintGraphBuilder.h"
|
||||
#include "Luau/FileResolver.h"
|
||||
#include "Luau/Frontend.h"
|
||||
#include "Luau/IostreamHelpers.h"
|
||||
#include "Luau/Linter.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
|
@ -279,7 +279,6 @@ TEST_CASE_FIXTURE(Fixture, "clone_recursion_limit")
|
||||
int limit = 400;
|
||||
#endif
|
||||
ScopedFastInt luauTypeCloneRecursionLimit{"LuauTypeCloneRecursionLimit", limit};
|
||||
ScopedFastFlag sff{"LuauRecursionLimitException", true};
|
||||
|
||||
TypeArena src;
|
||||
|
||||
|
@ -12,7 +12,6 @@ using namespace Luau;
|
||||
struct NormalizeFixture : Fixture
|
||||
{
|
||||
ScopedFastFlag sff1{"LuauLowerBoundsCalculation", true};
|
||||
ScopedFastFlag sff2{"LuauTableSubtypingVariance2", true};
|
||||
};
|
||||
|
||||
void createSomeClasses(TypeChecker& typeChecker)
|
||||
|
@ -264,10 +264,13 @@ TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type")
|
||||
}
|
||||
)LUA";
|
||||
|
||||
CheckResult result = check(src);
|
||||
CodeTooComplex ctc;
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
(void)check(src);
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
else
|
||||
CHECK_THROWS_AS(check(src), std::exception);
|
||||
CHECK(hasError(result, &ctc));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -409,8 +409,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "toStringDetailed2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local base = {}
|
||||
function base:one() return 1 end
|
||||
|
@ -7,8 +7,21 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
||||
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")
|
||||
{
|
||||
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")));
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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"(
|
||||
--!strict
|
||||
|
@ -30,11 +30,21 @@ TEST_CASE_FIXTURE(Fixture, "successful_check")
|
||||
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")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
function double(x: number)
|
||||
return x * 2
|
||||
return 2
|
||||
end
|
||||
|
||||
local four = double(2)
|
||||
@ -47,7 +57,7 @@ TEST_CASE_FIXTURE(Fixture, "function_parameter_annotations_are_checked")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
function double(x: number)
|
||||
return x * 2
|
||||
return 2
|
||||
end
|
||||
|
||||
local four = double("two")
|
||||
@ -70,13 +80,13 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotations_are_checked")
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fiftyType);
|
||||
REQUIRE(ftv != nullptr);
|
||||
|
||||
TypePackId retPack = ftv->retTypes;
|
||||
TypePackId retPack = follow(ftv->retTypes);
|
||||
const TypePack* tp = get<TypePack>(retPack);
|
||||
REQUIRE(tp != nullptr);
|
||||
|
||||
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")
|
||||
@ -116,6 +126,23 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotation_should_continuously_parse
|
||||
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")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
@ -632,7 +659,10 @@ int AssertionCatcher::tripped;
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice")
|
||||
{
|
||||
ScopedFastFlag sffs{"DebugLuauMagicTypes", true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"DebugLuauMagicTypes", true},
|
||||
{"LuauUseInternalCompilerErrorException", false},
|
||||
};
|
||||
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sffs{"DebugLuauMagicTypes", true};
|
||||
|
||||
AssertionCatcher ac;
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"DebugLuauMagicTypes", true},
|
||||
{"LuauUseInternalCompilerErrorException", false},
|
||||
};
|
||||
|
||||
bool caught = false;
|
||||
|
||||
@ -662,8 +693,44 @@ TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_handler")
|
||||
std::runtime_error);
|
||||
|
||||
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")
|
||||
|
@ -700,11 +700,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
-- 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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
||||
|
||||
// Mutability in type function application right now can create strange recursive types
|
||||
CheckResult result = check(R"(
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function sum<a>(x: a, y: a, f: (a, a) -> a)
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauApplyTypeFunctionFix", true};
|
||||
|
||||
// https://github.com/Roblox/luau/issues/484
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
@ -1153,8 +1142,6 @@ local complex: ComplexObject<string> = {
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauApplyTypeFunctionFix", true};
|
||||
|
||||
// https://github.com/Roblox/luau/issues/484
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
@ -12,8 +12,6 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauTableSubtypingVariance2)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferModules");
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
||||
@ -326,16 +324,9 @@ local b: B.T = a
|
||||
CheckResult result = frontend.check("game/C");
|
||||
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:
|
||||
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")
|
||||
@ -367,16 +358,9 @@ local b: B.T = a
|
||||
CheckResult result = frontend.check("game/D");
|
||||
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:
|
||||
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();
|
||||
|
@ -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")
|
||||
{
|
||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sffs[]{
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local x: { ["<>"] : number }
|
||||
|
@ -276,8 +276,6 @@ TEST_CASE_FIXTURE(Fixture, "open_table_unification")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "open_table_unification_2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a = {}
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local T = {}
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function empty() return {} end
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t = {x = 1}
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauUnsealedTableLiteral", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t = {x = 1}
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type StringToStringMap = { [string]: string }
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function foo(a: {[string]: number, a: string}) end
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local vec3 = {x = 1, y = 2, z = 3}
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local buttons = {}
|
||||
@ -2013,8 +1981,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_should_cope_with_optional_prope
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop")
|
||||
{
|
||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = { x: number, y: number }
|
||||
type B = { x: number, y: string }
|
||||
@ -2031,8 +1997,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_prop_nested")
|
||||
{
|
||||
ScopedFastFlag LuauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type AS = { x: number, y: number }
|
||||
type BS = { x: number, y: string }
|
||||
@ -2054,11 +2018,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "error_detailed_metatable_prop")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a1 = setmetatable({ x = 2, y = 3 }, { __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")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = { [number]: string }
|
||||
type B = { [string]: string }
|
||||
@ -2103,8 +2060,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value")
|
||||
{
|
||||
ScopedFastFlag luauTableSubtypingVariance2{"LuauTableSubtypingVariance2", true}; // Only for new path
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = { [number]: number }
|
||||
type B = { [number]: string }
|
||||
@ -2121,10 +2076,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
|
||||
{
|
||||
ScopedFastFlag sffs[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
@ -2140,11 +2091,6 @@ a.p = { x = 9 }
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
|
||||
{
|
||||
ScopedFastFlag sffs[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
@ -2166,10 +2112,6 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
|
||||
{
|
||||
ScopedFastFlag sffs[]{
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Super = { x : number }
|
||||
@ -2185,10 +2127,6 @@ a.p = { x = 9 }
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_metatable_type_call")
|
||||
{
|
||||
ScopedFastFlag sff[]{
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local b
|
||||
b = setmetatable({}, {__call = b})
|
||||
@ -2201,11 +2139,6 @@ b()
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "table_subtyping_shouldn't_add_optional_properties_to_sealed_tables")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauSubtypingAddOptPropsToUnsealedTables", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X<T> = T
|
||||
type K = X<typeof(math)>
|
||||
@ -2725,8 +2656,6 @@ type K = X<typeof(math)>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiate_table_cloning_3")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauOnlyMutateInstantiatedTables", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X<T> = T
|
||||
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")
|
||||
{
|
||||
ScopedFastFlag luauSubtypingAddOptPropsToUnsealedTables{"LuauSubtypingAddOptPropsToUnsealedTables", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type X = { { x: boolean?, y: boolean? } }
|
||||
|
||||
|
@ -887,8 +887,6 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
||||
{
|
||||
ScopedFastFlag subtypingVariance{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
--!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")
|
||||
{
|
||||
ScopedFastInt sfi("LuauTypeInferRecursionLimit", 2);
|
||||
ScopedFastFlag sff{"LuauRecursionLimitException", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function complex()
|
||||
|
@ -428,12 +428,6 @@ y = x
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unify_sealed_table_union_check")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
{"LuauUnsealedTableLiteral", true},
|
||||
{"LuauSubtypingAddOptPropsToUnsealedTables", true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- the difference between this and unify_unsealed_table_union_check is the type annotation on x
|
||||
local t = { x = 3, y = true }
|
||||
|
@ -10,14 +10,9 @@ using namespace Luau;
|
||||
|
||||
LUAU_FASTINT(LuauVisitRecursionLimit)
|
||||
|
||||
struct VisitTypeVarFixture : Fixture
|
||||
{
|
||||
ScopedFastFlag flag2 = {"LuauRecursionLimitException", true};
|
||||
};
|
||||
|
||||
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};
|
||||
|
||||
@ -30,7 +25,7 @@ TEST_CASE_FIXTURE(VisitTypeVarFixture, "throw_when_limit_is_exceeded")
|
||||
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};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user