mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/544 (#669)
- Remove type definitions of `utf8.nfcnormalize`/`nfdnormalize`/`graphemes` that aren't supported by standalone Luau library - Add `lua_costatus` to retrieve extended thread status (similar to `coroutine.status`) - Improve GC sweeping performance (2-10% improvement on allocation-heavy benchmarks)
This commit is contained in:
parent
b2e357da30
commit
ce2c3b3a4e
@ -19,9 +19,12 @@ using ScopePtr = std::shared_ptr<Scope>;
|
||||
// A substitution which replaces free types by any
|
||||
struct Anyification : Substitution
|
||||
{
|
||||
Anyification(TypeArena* arena, NotNull<Scope> scope, InternalErrorReporter* iceHandler, TypeId anyType, TypePackId anyTypePack);
|
||||
Anyification(TypeArena* arena, const ScopePtr& scope, InternalErrorReporter* iceHandler, TypeId anyType, TypePackId anyTypePack);
|
||||
Anyification(TypeArena* arena, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler, TypeId anyType,
|
||||
TypePackId anyTypePack);
|
||||
Anyification(TypeArena* arena, const ScopePtr& scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler, TypeId anyType,
|
||||
TypePackId anyTypePack);
|
||||
NotNull<Scope> scope;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
InternalErrorReporter* iceHandler;
|
||||
|
||||
TypeId anyType;
|
||||
|
@ -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/Frontend.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
|
||||
@ -8,6 +9,7 @@ namespace Luau
|
||||
{
|
||||
|
||||
void registerBuiltinTypes(TypeChecker& typeChecker);
|
||||
void registerBuiltinTypes(Frontend& frontend);
|
||||
|
||||
TypeId makeUnion(TypeArena& arena, std::vector<TypeId>&& types);
|
||||
TypeId makeIntersection(TypeArena& arena, std::vector<TypeId>&& types);
|
||||
@ -15,6 +17,7 @@ TypeId makeIntersection(TypeArena& arena, std::vector<TypeId>&& types);
|
||||
/** Build an optional 't'
|
||||
*/
|
||||
TypeId makeOption(TypeChecker& typeChecker, TypeArena& arena, TypeId t);
|
||||
TypeId makeOption(Frontend& frontend, TypeArena& arena, TypeId t);
|
||||
|
||||
/** Small utility function for building up type definitions from C++.
|
||||
*/
|
||||
@ -41,12 +44,17 @@ void assignPropDocumentationSymbols(TableTypeVar::Props& props, const std::strin
|
||||
|
||||
std::string getBuiltinDefinitionSource();
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, Binding binding);
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, Binding binding);
|
||||
std::optional<Binding> tryGetGlobalBinding(TypeChecker& typeChecker, const std::string& name);
|
||||
void addGlobalBinding(Frontend& frontend, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(Frontend& frontend, const std::string& name, Binding binding);
|
||||
void addGlobalBinding(Frontend& frontend, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(Frontend& frontend, const ScopePtr& scope, const std::string& name, Binding binding);
|
||||
std::optional<Binding> tryGetGlobalBinding(Frontend& frontend, const std::string& name);
|
||||
Binding* tryGetGlobalBindingRef(TypeChecker& typeChecker, const std::string& name);
|
||||
TypeId getGlobalBinding(Frontend& frontend, const std::string& name);
|
||||
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name);
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -21,6 +21,8 @@ namespace Luau
|
||||
struct Scope;
|
||||
using ScopePtr = std::shared_ptr<Scope>;
|
||||
|
||||
struct DcrLogger;
|
||||
|
||||
struct ConstraintGraphBuilder
|
||||
{
|
||||
// A list of all the scopes in the module. This vector holds ownership of the
|
||||
@ -30,7 +32,7 @@ struct ConstraintGraphBuilder
|
||||
|
||||
ModuleName moduleName;
|
||||
ModulePtr module;
|
||||
SingletonTypes& singletonTypes;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
const NotNull<TypeArena> arena;
|
||||
// The root scope of the module we're generating constraints for.
|
||||
// This is null when the CGB is initially constructed.
|
||||
@ -58,9 +60,10 @@ struct ConstraintGraphBuilder
|
||||
const NotNull<InternalErrorReporter> ice;
|
||||
|
||||
ScopePtr globalScope;
|
||||
DcrLogger* logger;
|
||||
|
||||
ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena, NotNull<ModuleResolver> moduleResolver,
|
||||
NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope);
|
||||
NotNull<SingletonTypes> singletonTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, DcrLogger* logger);
|
||||
|
||||
/**
|
||||
* Fabricates a new free type belonging to a given scope.
|
||||
|
@ -5,14 +5,16 @@
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/ConstraintSolverLogger.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct DcrLogger;
|
||||
|
||||
// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
|
||||
// never dereference this pointer.
|
||||
using BlockedConstraintId = const void*;
|
||||
@ -40,6 +42,7 @@ struct HashInstantiationSignature
|
||||
struct ConstraintSolver
|
||||
{
|
||||
TypeArena* arena;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
InternalErrorReporter iceReporter;
|
||||
// The entire set of constraints that the solver is trying to resolve.
|
||||
std::vector<NotNull<Constraint>> constraints;
|
||||
@ -69,10 +72,10 @@ struct ConstraintSolver
|
||||
NotNull<ModuleResolver> moduleResolver;
|
||||
std::vector<RequireCycle> requireCycles;
|
||||
|
||||
ConstraintSolverLogger logger;
|
||||
DcrLogger* logger;
|
||||
|
||||
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope, ModuleName moduleName, NotNull<ModuleResolver> moduleResolver,
|
||||
std::vector<RequireCycle> requireCycles);
|
||||
explicit ConstraintSolver(TypeArena* arena, NotNull<SingletonTypes> singletonTypes, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger);
|
||||
|
||||
/**
|
||||
* Attempts to dispatch all pending constraints and reach a type solution
|
||||
|
@ -1,29 +0,0 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct ConstraintSolverLogger
|
||||
{
|
||||
std::string compileOutput();
|
||||
void captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void prepareStepSnapshot(
|
||||
const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints, bool force);
|
||||
void commitPreparedStepSnapshot();
|
||||
|
||||
private:
|
||||
std::vector<std::string> snapshots;
|
||||
std::optional<std::string> preparedSnapshot;
|
||||
ToStringOptions opts;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
131
Analysis/include/Luau/DcrLogger.h
Normal file
131
Analysis/include/Luau/DcrLogger.h
Normal file
@ -0,0 +1,131 @@
|
||||
// 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/NotNull.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct ErrorSnapshot
|
||||
{
|
||||
std::string message;
|
||||
Location location;
|
||||
};
|
||||
|
||||
struct BindingSnapshot
|
||||
{
|
||||
std::string typeId;
|
||||
std::string typeString;
|
||||
Location location;
|
||||
};
|
||||
|
||||
struct TypeBindingSnapshot
|
||||
{
|
||||
std::string typeId;
|
||||
std::string typeString;
|
||||
};
|
||||
|
||||
struct ConstraintGenerationLog
|
||||
{
|
||||
std::string source;
|
||||
std::unordered_map<std::string, Location> constraintLocations;
|
||||
std::vector<ErrorSnapshot> errors;
|
||||
};
|
||||
|
||||
struct ScopeSnapshot
|
||||
{
|
||||
std::unordered_map<Name, BindingSnapshot> bindings;
|
||||
std::unordered_map<Name, TypeBindingSnapshot> typeBindings;
|
||||
std::unordered_map<Name, TypeBindingSnapshot> typePackBindings;
|
||||
std::vector<ScopeSnapshot> children;
|
||||
};
|
||||
|
||||
enum class ConstraintBlockKind
|
||||
{
|
||||
TypeId,
|
||||
TypePackId,
|
||||
ConstraintId,
|
||||
};
|
||||
|
||||
struct ConstraintBlock
|
||||
{
|
||||
ConstraintBlockKind kind;
|
||||
std::string stringification;
|
||||
};
|
||||
|
||||
struct ConstraintSnapshot
|
||||
{
|
||||
std::string stringification;
|
||||
std::vector<ConstraintBlock> blocks;
|
||||
};
|
||||
|
||||
struct BoundarySnapshot
|
||||
{
|
||||
std::unordered_map<std::string, ConstraintSnapshot> constraints;
|
||||
ScopeSnapshot rootScope;
|
||||
};
|
||||
|
||||
struct StepSnapshot
|
||||
{
|
||||
std::string currentConstraint;
|
||||
bool forced;
|
||||
std::unordered_map<std::string, ConstraintSnapshot> unsolvedConstraints;
|
||||
ScopeSnapshot rootScope;
|
||||
};
|
||||
|
||||
struct TypeSolveLog
|
||||
{
|
||||
BoundarySnapshot initialState;
|
||||
std::vector<StepSnapshot> stepStates;
|
||||
BoundarySnapshot finalState;
|
||||
};
|
||||
|
||||
struct TypeCheckLog
|
||||
{
|
||||
std::vector<ErrorSnapshot> errors;
|
||||
};
|
||||
|
||||
using ConstraintBlockTarget = Variant<TypeId, TypePackId, NotNull<const Constraint>>;
|
||||
|
||||
struct DcrLogger
|
||||
{
|
||||
std::string compileOutput();
|
||||
|
||||
void captureSource(std::string source);
|
||||
void captureGenerationError(const TypeError& error);
|
||||
void captureConstraintLocation(NotNull<const Constraint> constraint, Location location);
|
||||
|
||||
void pushBlock(NotNull<const Constraint> constraint, TypeId block);
|
||||
void pushBlock(NotNull<const Constraint> constraint, TypePackId block);
|
||||
void pushBlock(NotNull<const Constraint> constraint, NotNull<const Constraint> block);
|
||||
void popBlock(TypeId block);
|
||||
void popBlock(TypePackId block);
|
||||
void popBlock(NotNull<const Constraint> block);
|
||||
|
||||
void captureInitialSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
StepSnapshot prepareStepSnapshot(const Scope* rootScope, NotNull<const Constraint> current, bool force, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void commitStepSnapshot(StepSnapshot snapshot);
|
||||
void captureFinalSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
|
||||
void captureTypeCheckError(const TypeError& error);
|
||||
private:
|
||||
ConstraintGenerationLog generationLog;
|
||||
std::unordered_map<NotNull<const Constraint>, std::vector<ConstraintBlockTarget>> constraintBlocks;
|
||||
TypeSolveLog solveLog;
|
||||
TypeCheckLog checkLog;
|
||||
|
||||
ToStringOptions opts;
|
||||
|
||||
std::vector<ConstraintBlock> snapshotBlocks(NotNull<const Constraint> constraint);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
@ -1,3 +1,4 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/DenseHash.h"
|
||||
|
@ -174,6 +174,9 @@ private:
|
||||
ScopePtr globalScope;
|
||||
|
||||
public:
|
||||
SingletonTypes singletonTypes_;
|
||||
const NotNull<SingletonTypes> singletonTypes;
|
||||
|
||||
FileResolver* fileResolver;
|
||||
FrontendModuleResolver moduleResolver;
|
||||
FrontendModuleResolver moduleResolverForAutocomplete;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Luau/NotNull.h"
|
||||
@ -232,4 +233,15 @@ void write(JsonEmitter& emitter, const std::optional<T>& v)
|
||||
emitter.writeRaw("null");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(JsonEmitter& emitter, const std::unordered_map<std::string, T>& map)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
|
||||
for (const auto& [k, v] : map)
|
||||
o.writePair(k, v);
|
||||
|
||||
o.finish();
|
||||
}
|
||||
|
||||
} // namespace Luau::Json
|
||||
|
@ -90,7 +90,7 @@ struct Module
|
||||
|
||||
// Once a module has been typechecked, we clone its public interface into a separate arena.
|
||||
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
|
||||
void clonePublicInterface(InternalErrorReporter& ice);
|
||||
void clonePublicInterface(NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -1,4 +1,5 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/NotNull.h"
|
||||
@ -12,17 +13,20 @@ namespace Luau
|
||||
struct InternalErrorReporter;
|
||||
struct Module;
|
||||
struct Scope;
|
||||
struct SingletonTypes;
|
||||
|
||||
using ModulePtr = std::shared_ptr<Module>;
|
||||
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Scope> scope, TypeArena& arena, InternalErrorReporter& ice);
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Module> module, InternalErrorReporter& ice);
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, NotNull<Scope> scope, TypeArena& arena, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, NotNull<Module> module, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, const ModulePtr& module, InternalErrorReporter& ice);
|
||||
std::pair<TypeId, bool> normalize(
|
||||
TypeId ty, NotNull<Scope> scope, TypeArena& arena, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Module> module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(
|
||||
TypePackId ty, NotNull<Scope> scope, TypeArena& arena, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, NotNull<Module> module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
std::pair<TypePackId, bool> normalize(TypePackId ty, const ModulePtr& module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice);
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -31,6 +31,8 @@ struct TypeArena
|
||||
TypeId freshType(TypeLevel level);
|
||||
TypeId freshType(Scope* scope);
|
||||
|
||||
TypePackId freshTypePack(Scope* scope);
|
||||
|
||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
|
||||
TypePackId addTypePack(TypePack pack);
|
||||
|
@ -4,10 +4,14 @@
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/NotNull.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
void check(const SourceModule& sourceModule, Module* module);
|
||||
struct DcrLogger;
|
||||
struct SingletonTypes;
|
||||
|
||||
void check(NotNull<SingletonTypes> singletonTypes, DcrLogger* logger, const SourceModule& sourceModule, Module* module);
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
// within a program are borrowed pointers into this set.
|
||||
struct TypeChecker
|
||||
{
|
||||
explicit TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHandler);
|
||||
explicit TypeChecker(ModuleResolver* resolver, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler);
|
||||
TypeChecker(const TypeChecker&) = delete;
|
||||
TypeChecker& operator=(const TypeChecker&) = delete;
|
||||
|
||||
@ -353,6 +353,7 @@ public:
|
||||
ModuleName currentModuleName;
|
||||
|
||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
InternalErrorReporter* iceHandler;
|
||||
|
||||
UnifierSharedState unifierState;
|
||||
|
@ -15,10 +15,12 @@ struct TxnLog;
|
||||
|
||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||
|
||||
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, const std::string& entry, Location location);
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId ty, const std::string& name, Location location);
|
||||
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, TypeId type, const std::string& prop,
|
||||
const Location& location, bool addErrors, InternalErrorReporter& handle);
|
||||
std::optional<TypeId> findMetatableEntry(
|
||||
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId type, const std::string& entry, Location location);
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(
|
||||
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId ty, const std::string& name, Location location);
|
||||
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
|
||||
TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle);
|
||||
|
||||
// Returns the minimum and maximum number of types the argument list can accept.
|
||||
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp);
|
||||
|
@ -586,7 +586,7 @@ bool isOverloadedFunction(TypeId ty);
|
||||
// True when string is a subtype of ty
|
||||
bool maybeString(TypeId ty);
|
||||
|
||||
std::optional<TypeId> getMetatable(TypeId type);
|
||||
std::optional<TypeId> getMetatable(TypeId type, NotNull<struct SingletonTypes> singletonTypes);
|
||||
TableTypeVar* getMutableTableType(TypeId type);
|
||||
const TableTypeVar* getTableType(TypeId type);
|
||||
|
||||
@ -614,21 +614,6 @@ bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount);
|
||||
|
||||
struct SingletonTypes
|
||||
{
|
||||
const TypeId nilType;
|
||||
const TypeId numberType;
|
||||
const TypeId stringType;
|
||||
const TypeId booleanType;
|
||||
const TypeId threadType;
|
||||
const TypeId trueType;
|
||||
const TypeId falseType;
|
||||
const TypeId anyType;
|
||||
const TypeId unknownType;
|
||||
const TypeId neverType;
|
||||
|
||||
const TypePackId anyTypePack;
|
||||
const TypePackId neverTypePack;
|
||||
const TypePackId uninhabitableTypePack;
|
||||
|
||||
SingletonTypes();
|
||||
~SingletonTypes();
|
||||
SingletonTypes(const SingletonTypes&) = delete;
|
||||
@ -644,9 +629,28 @@ private:
|
||||
bool debugFreezeArena = false;
|
||||
|
||||
TypeId makeStringMetatable();
|
||||
|
||||
public:
|
||||
const TypeId nilType;
|
||||
const TypeId numberType;
|
||||
const TypeId stringType;
|
||||
const TypeId booleanType;
|
||||
const TypeId threadType;
|
||||
const TypeId trueType;
|
||||
const TypeId falseType;
|
||||
const TypeId anyType;
|
||||
const TypeId unknownType;
|
||||
const TypeId neverType;
|
||||
const TypeId errorType;
|
||||
|
||||
const TypePackId anyTypePack;
|
||||
const TypePackId neverTypePack;
|
||||
const TypePackId uninhabitableTypePack;
|
||||
const TypePackId errorTypePack;
|
||||
};
|
||||
|
||||
SingletonTypes& getSingletonTypes();
|
||||
// Clip with FFlagLuauNoMoreGlobalSingletonTypes
|
||||
SingletonTypes& DEPRECATED_getSingletonTypes();
|
||||
|
||||
void persist(TypeId ty);
|
||||
void persist(TypePackId tp);
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/ParseOptions.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/Substitution.h"
|
||||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/UnifierSharedState.h"
|
||||
|
||||
#include <unordered_set>
|
||||
@ -23,11 +24,14 @@ enum Variance
|
||||
// A substitution which replaces singleton types by their wider types
|
||||
struct Widen : Substitution
|
||||
{
|
||||
Widen(TypeArena* arena)
|
||||
Widen(TypeArena* arena, NotNull<SingletonTypes> singletonTypes)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
, singletonTypes(singletonTypes)
|
||||
{
|
||||
}
|
||||
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
|
||||
bool isDirty(TypeId ty) override;
|
||||
bool isDirty(TypePackId ty) override;
|
||||
TypeId clean(TypeId ty) override;
|
||||
@ -47,6 +51,7 @@ struct UnifierOptions
|
||||
struct Unifier
|
||||
{
|
||||
TypeArena* const types;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
Mode mode;
|
||||
|
||||
NotNull<Scope> scope; // const Scope maybe
|
||||
@ -59,8 +64,8 @@ struct Unifier
|
||||
|
||||
UnifierSharedState& sharedState;
|
||||
|
||||
Unifier(TypeArena* types, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance, UnifierSharedState& sharedState,
|
||||
TxnLog* parentLog = nullptr);
|
||||
Unifier(TypeArena* types, NotNull<SingletonTypes> singletonTypes, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance,
|
||||
UnifierSharedState& sharedState, TxnLog* parentLog = nullptr);
|
||||
|
||||
// Test whether the two type vars unify. Never commits the result.
|
||||
ErrorVec canUnify(TypeId subTy, TypeId superTy);
|
||||
|
@ -11,17 +11,20 @@ LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
Anyification::Anyification(TypeArena* arena, NotNull<Scope> scope, InternalErrorReporter* iceHandler, TypeId anyType, TypePackId anyTypePack)
|
||||
Anyification::Anyification(TypeArena* arena, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler,
|
||||
TypeId anyType, TypePackId anyTypePack)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
, scope(scope)
|
||||
, singletonTypes(singletonTypes)
|
||||
, iceHandler(iceHandler)
|
||||
, anyType(anyType)
|
||||
, anyTypePack(anyTypePack)
|
||||
{
|
||||
}
|
||||
|
||||
Anyification::Anyification(TypeArena* arena, const ScopePtr& scope, InternalErrorReporter* iceHandler, TypeId anyType, TypePackId anyTypePack)
|
||||
: Anyification(arena, NotNull{scope.get()}, iceHandler, anyType, anyTypePack)
|
||||
Anyification::Anyification(TypeArena* arena, const ScopePtr& scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler,
|
||||
TypeId anyType, TypePackId anyTypePack)
|
||||
: Anyification(arena, NotNull{scope.get()}, singletonTypes, iceHandler, anyType, anyTypePack)
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,7 +74,7 @@ TypeId Anyification::clean(TypeId ty)
|
||||
for (TypeId& ty : copy)
|
||||
ty = replace(ty);
|
||||
TypeId res = copy.size() == 1 ? copy[0] : addType(UnionTypeVar{std::move(copy)});
|
||||
auto [t, ok] = normalize(res, scope, *arena, *iceHandler);
|
||||
auto [t, ok] = normalize(res, scope, *arena, singletonTypes, *iceHandler);
|
||||
if (!ok)
|
||||
normalizationTooComplex = true;
|
||||
return t;
|
||||
|
@ -14,8 +14,6 @@
|
||||
|
||||
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteFixGlobalOrder, false)
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
||||
@ -137,27 +135,28 @@ static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* n
|
||||
return *it;
|
||||
}
|
||||
|
||||
static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, TypeArena* typeArena)
|
||||
static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, TypeArena* typeArena, NotNull<SingletonTypes> singletonTypes)
|
||||
{
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
Unifier unifier(typeArena, Mode::Strict, scope, Location(), Variance::Covariant, unifierState);
|
||||
Unifier unifier(typeArena, singletonTypes, Mode::Strict, scope, Location(), Variance::Covariant, unifierState);
|
||||
|
||||
return unifier.canUnify(subTy, superTy).empty();
|
||||
}
|
||||
|
||||
static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typeArena, AstNode* node, Position position, TypeId ty)
|
||||
static TypeCorrectKind checkTypeCorrectKind(
|
||||
const Module& module, TypeArena* typeArena, NotNull<SingletonTypes> singletonTypes, AstNode* node, Position position, TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
NotNull<Scope> moduleScope{module.getModuleScope().get()};
|
||||
|
||||
auto canUnify = [&typeArena, moduleScope](TypeId subTy, TypeId superTy) {
|
||||
auto canUnify = [&typeArena, singletonTypes, moduleScope](TypeId subTy, TypeId superTy) {
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
Unifier unifier(typeArena, Mode::Strict, moduleScope, Location(), Variance::Covariant, unifierState);
|
||||
Unifier unifier(typeArena, singletonTypes, Mode::Strict, moduleScope, Location(), Variance::Covariant, unifierState);
|
||||
|
||||
unifier.tryUnify(subTy, superTy);
|
||||
bool ok = unifier.errors.empty();
|
||||
@ -171,11 +170,11 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||
|
||||
TypeId expectedType = follow(*typeAtPosition);
|
||||
|
||||
auto checkFunctionType = [typeArena, moduleScope, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
||||
auto checkFunctionType = [typeArena, singletonTypes, moduleScope, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena);
|
||||
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, singletonTypes);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -214,7 +213,8 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
||||
}
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
return checkTypeMatch(ty, expectedType, NotNull{module.getModuleScope().get()}, typeArena) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
return checkTypeMatch(ty, expectedType, NotNull{module.getModuleScope().get()}, typeArena, singletonTypes) ? TypeCorrectKind::Correct
|
||||
: TypeCorrectKind::None;
|
||||
else
|
||||
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
}
|
||||
@ -226,8 +226,8 @@ enum class PropIndexType
|
||||
Key,
|
||||
};
|
||||
|
||||
static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId rootTy, TypeId ty, PropIndexType indexType,
|
||||
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
||||
static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNull<SingletonTypes> singletonTypes, TypeId rootTy, TypeId ty,
|
||||
PropIndexType indexType, const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
||||
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
|
||||
{
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
@ -272,7 +272,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
return colonIndex;
|
||||
}
|
||||
};
|
||||
auto isWrongIndexer = [typeArena, &module, rootTy, indexType](Luau::TypeId type) {
|
||||
auto isWrongIndexer = [typeArena, singletonTypes, &module, rootTy, indexType](Luau::TypeId type) {
|
||||
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
if (indexType == PropIndexType::Key)
|
||||
@ -280,7 +280,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
|
||||
bool calledWithSelf = indexType == PropIndexType::Colon;
|
||||
|
||||
auto isCompatibleCall = [typeArena, &module, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
|
||||
auto isCompatibleCall = [typeArena, singletonTypes, &module, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
|
||||
// Strong match with definition is a success
|
||||
if (calledWithSelf == ftv->hasSelf)
|
||||
return true;
|
||||
@ -293,7 +293,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
|
||||
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
||||
{
|
||||
if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena))
|
||||
if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, singletonTypes))
|
||||
return calledWithSelf;
|
||||
}
|
||||
|
||||
@ -327,8 +327,9 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
if (result.count(name) == 0 && name != kParseNameError)
|
||||
{
|
||||
Luau::TypeId type = Luau::follow(prop.type);
|
||||
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
|
||||
: checkTypeCorrectKind(module, typeArena, nodes.back(), {{}, {}}, type);
|
||||
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key
|
||||
? TypeCorrectKind::Correct
|
||||
: checkTypeCorrectKind(module, typeArena, singletonTypes, nodes.back(), {{}, {}}, type);
|
||||
ParenthesesRecommendation parens =
|
||||
indexType == PropIndexType::Key ? ParenthesesRecommendation::None : getParenRecommendation(type, nodes, typeCorrect);
|
||||
|
||||
@ -355,13 +356,13 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
TypeId followed = follow(indexIt->second.type);
|
||||
if (get<TableTypeVar>(followed) || get<MetatableTypeVar>(followed))
|
||||
{
|
||||
autocompleteProps(module, typeArena, rootTy, followed, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, followed, indexType, nodes, result, seen);
|
||||
}
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(followed))
|
||||
{
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retTypes);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -371,13 +372,13 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
containingClass = containingClass.value_or(cls);
|
||||
fillProps(cls->props);
|
||||
if (cls->parent)
|
||||
autocompleteProps(module, typeArena, rootTy, *cls->parent, indexType, nodes, result, seen, containingClass);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingClass);
|
||||
}
|
||||
else if (auto tbl = get<TableTypeVar>(ty))
|
||||
fillProps(tbl->props);
|
||||
else if (auto mt = get<MetatableTypeVar>(ty))
|
||||
{
|
||||
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, mt->table, indexType, nodes, result, seen);
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
@ -395,12 +396,12 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
{
|
||||
TypeId followed = follow(indexIt->second.type);
|
||||
if (get<TableTypeVar>(followed) || get<MetatableTypeVar>(followed))
|
||||
autocompleteProps(module, typeArena, rootTy, followed, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, followed, indexType, nodes, result, seen);
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(followed))
|
||||
{
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retTypes);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,7 +414,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
AutocompleteEntryMap inner;
|
||||
std::unordered_set<TypeId> innerSeen = seen;
|
||||
|
||||
autocompleteProps(module, typeArena, rootTy, ty, indexType, nodes, inner, innerSeen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, ty, indexType, nodes, inner, innerSeen);
|
||||
|
||||
for (auto& pair : inner)
|
||||
result.insert(pair);
|
||||
@ -436,7 +437,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
if (iter == endIter)
|
||||
return;
|
||||
|
||||
autocompleteProps(module, typeArena, rootTy, *iter, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *iter, indexType, nodes, result, seen);
|
||||
|
||||
++iter;
|
||||
|
||||
@ -454,7 +455,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
continue;
|
||||
}
|
||||
|
||||
autocompleteProps(module, typeArena, rootTy, *iter, indexType, nodes, inner, innerSeen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, *iter, indexType, nodes, inner, innerSeen);
|
||||
|
||||
std::unordered_set<std::string> toRemove;
|
||||
|
||||
@ -481,7 +482,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||
}
|
||||
else if (FFlag::LuauSelfCallAutocompleteFix3 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||
{
|
||||
autocompleteProps(module, typeArena, rootTy, getSingletonTypes().stringType, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, rootTy, singletonTypes->stringType, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,18 +507,18 @@ static void autocompleteKeywords(
|
||||
}
|
||||
}
|
||||
|
||||
static void autocompleteProps(
|
||||
const Module& module, TypeArena* typeArena, TypeId ty, PropIndexType indexType, const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result)
|
||||
static void autocompleteProps(const Module& module, TypeArena* typeArena, NotNull<SingletonTypes> singletonTypes, TypeId ty, PropIndexType indexType,
|
||||
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result)
|
||||
{
|
||||
std::unordered_set<TypeId> seen;
|
||||
autocompleteProps(module, typeArena, ty, ty, indexType, nodes, result, seen);
|
||||
autocompleteProps(module, typeArena, singletonTypes, ty, ty, indexType, nodes, result, seen);
|
||||
}
|
||||
|
||||
AutocompleteEntryMap autocompleteProps(
|
||||
const Module& module, TypeArena* typeArena, TypeId ty, PropIndexType indexType, const std::vector<AstNode*>& nodes)
|
||||
AutocompleteEntryMap autocompleteProps(const Module& module, TypeArena* typeArena, NotNull<SingletonTypes> singletonTypes, TypeId ty,
|
||||
PropIndexType indexType, const std::vector<AstNode*>& nodes)
|
||||
{
|
||||
AutocompleteEntryMap result;
|
||||
autocompleteProps(module, typeArena, ty, indexType, nodes, result);
|
||||
autocompleteProps(module, typeArena, singletonTypes, ty, indexType, nodes, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1079,19 +1080,11 @@ T* extractStat(const std::vector<AstNode*>& ancestry)
|
||||
|
||||
static bool isBindingLegalAtCurrentPosition(const Symbol& symbol, const Binding& binding, Position pos)
|
||||
{
|
||||
if (FFlag::LuauAutocompleteFixGlobalOrder)
|
||||
{
|
||||
if (symbol.local)
|
||||
return binding.location.end < pos;
|
||||
if (symbol.local)
|
||||
return binding.location.end < pos;
|
||||
|
||||
// Builtin globals have an empty location; for defined globals, we want pos to be outside of the definition range to suggest it
|
||||
return binding.location == Location() || !binding.location.containsClosed(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default Location used for global bindings, which are always legal.
|
||||
return binding.location == Location() || binding.location.end < pos;
|
||||
}
|
||||
// Builtin globals have an empty location; for defined globals, we want pos to be outside of the definition range to suggest it
|
||||
return binding.location == Location() || !binding.location.containsClosed(pos);
|
||||
}
|
||||
|
||||
static AutocompleteEntryMap autocompleteStatement(
|
||||
@ -1220,12 +1213,14 @@ static AutocompleteContext autocompleteExpression(const SourceModule& sourceModu
|
||||
{
|
||||
LUAU_ASSERT(!ancestry.empty());
|
||||
|
||||
NotNull<SingletonTypes> singletonTypes = typeChecker.singletonTypes;
|
||||
|
||||
AstNode* node = ancestry.rbegin()[0];
|
||||
|
||||
if (node->is<AstExprIndexName>())
|
||||
{
|
||||
if (auto it = module.astTypes.find(node->asExpr()))
|
||||
autocompleteProps(module, typeArena, *it, PropIndexType::Point, ancestry, result);
|
||||
autocompleteProps(module, typeArena, singletonTypes, *it, PropIndexType::Point, ancestry, result);
|
||||
}
|
||||
else if (autocompleteIfElseExpression(node, ancestry, position, result))
|
||||
return AutocompleteContext::Keyword;
|
||||
@ -1249,7 +1244,7 @@ static AutocompleteContext autocompleteExpression(const SourceModule& sourceModu
|
||||
std::string n = toString(name);
|
||||
if (!result.count(n))
|
||||
{
|
||||
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, node, position, binding.typeId);
|
||||
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, singletonTypes, node, position, binding.typeId);
|
||||
|
||||
result[n] = {AutocompleteEntryKind::Binding, binding.typeId, binding.deprecated, false, typeCorrect, std::nullopt, std::nullopt,
|
||||
binding.documentationSymbol, {}, getParenRecommendation(binding.typeId, ancestry, typeCorrect)};
|
||||
@ -1259,9 +1254,9 @@ static AutocompleteContext autocompleteExpression(const SourceModule& sourceModu
|
||||
scope = scope->parent;
|
||||
}
|
||||
|
||||
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, node, position, typeChecker.nilType);
|
||||
TypeCorrectKind correctForTrue = checkTypeCorrectKind(module, typeArena, node, position, getSingletonTypes().trueType);
|
||||
TypeCorrectKind correctForFalse = checkTypeCorrectKind(module, typeArena, node, position, getSingletonTypes().falseType);
|
||||
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, singletonTypes, node, position, typeChecker.nilType);
|
||||
TypeCorrectKind correctForTrue = checkTypeCorrectKind(module, typeArena, singletonTypes, node, position, singletonTypes->trueType);
|
||||
TypeCorrectKind correctForFalse = checkTypeCorrectKind(module, typeArena, singletonTypes, node, position, singletonTypes->falseType);
|
||||
TypeCorrectKind correctForFunction =
|
||||
functionIsExpectedAt(module, node, position).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
|
||||
@ -1396,6 +1391,8 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||
if (isWithinComment(sourceModule, position))
|
||||
return {};
|
||||
|
||||
NotNull<SingletonTypes> singletonTypes = typeChecker.singletonTypes;
|
||||
|
||||
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(sourceModule, position);
|
||||
LUAU_ASSERT(!ancestry.empty());
|
||||
AstNode* node = ancestry.back();
|
||||
@ -1422,10 +1419,11 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
|
||||
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix3 && isString(ty))
|
||||
return {autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry,
|
||||
AutocompleteContext::Property};
|
||||
return {autocompleteProps(
|
||||
*module, typeArena, singletonTypes, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry),
|
||||
ancestry, AutocompleteContext::Property};
|
||||
else
|
||||
return {autocompleteProps(*module, typeArena, ty, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
||||
return {autocompleteProps(*module, typeArena, singletonTypes, ty, indexType, ancestry), ancestry, AutocompleteContext::Property};
|
||||
}
|
||||
else if (auto typeReference = node->as<AstTypeReference>())
|
||||
{
|
||||
@ -1548,7 +1546,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||
{
|
||||
if (auto it = module->astExpectedTypes.find(exprTable))
|
||||
{
|
||||
auto result = autocompleteProps(*module, typeArena, *it, PropIndexType::Key, ancestry);
|
||||
auto result = autocompleteProps(*module, typeArena, singletonTypes, *it, PropIndexType::Key, ancestry);
|
||||
|
||||
// Remove keys that are already completed
|
||||
for (const auto& item : exprTable->items)
|
||||
@ -1590,7 +1588,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||
if (auto idxExpr = ancestry.at(ancestry.size() - 2)->as<AstExprIndexExpr>())
|
||||
{
|
||||
if (auto it = module->astTypes.find(idxExpr->expr))
|
||||
autocompleteProps(*module, typeArena, follow(*it), PropIndexType::Point, ancestry, result);
|
||||
autocompleteProps(*module, typeArena, singletonTypes, follow(*it), PropIndexType::Point, ancestry, result);
|
||||
}
|
||||
else if (auto binExpr = ancestry.at(ancestry.size() - 2)->as<AstExprBinary>())
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -45,6 +46,11 @@ TypeId makeIntersection(TypeArena& arena, std::vector<TypeId>&& types)
|
||||
return arena.addType(IntersectionTypeVar{std::move(types)});
|
||||
}
|
||||
|
||||
TypeId makeOption(Frontend& frontend, TypeArena& arena, TypeId t)
|
||||
{
|
||||
return makeUnion(arena, {frontend.typeChecker.nilType, t});
|
||||
}
|
||||
|
||||
TypeId makeOption(TypeChecker& typeChecker, TypeArena& arena, TypeId t)
|
||||
{
|
||||
return makeUnion(arena, {typeChecker.nilType, t});
|
||||
@ -128,34 +134,50 @@ Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol)
|
||||
};
|
||||
}
|
||||
|
||||
void addGlobalBinding(Frontend& frontend, const std::string& name, TypeId ty, const std::string& packageName)
|
||||
{
|
||||
addGlobalBinding(frontend, frontend.getGlobalScope(), name, ty, packageName);
|
||||
}
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, TypeId ty, const std::string& packageName)
|
||||
{
|
||||
addGlobalBinding(typeChecker, typeChecker.globalScope, name, ty, packageName);
|
||||
}
|
||||
|
||||
void addGlobalBinding(Frontend& frontend, const std::string& name, Binding binding)
|
||||
{
|
||||
addGlobalBinding(frontend, frontend.getGlobalScope(), name, binding);
|
||||
}
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, Binding binding)
|
||||
{
|
||||
addGlobalBinding(typeChecker, typeChecker.globalScope, name, binding);
|
||||
}
|
||||
|
||||
void addGlobalBinding(Frontend& frontend, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName)
|
||||
{
|
||||
std::string documentationSymbol = packageName + "/global/" + name;
|
||||
addGlobalBinding(frontend, scope, name, Binding{ty, Location{}, {}, {}, documentationSymbol});
|
||||
}
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName)
|
||||
{
|
||||
std::string documentationSymbol = packageName + "/global/" + name;
|
||||
addGlobalBinding(typeChecker, scope, name, Binding{ty, Location{}, {}, {}, documentationSymbol});
|
||||
}
|
||||
|
||||
void addGlobalBinding(Frontend& frontend, const ScopePtr& scope, const std::string& name, Binding binding)
|
||||
{
|
||||
addGlobalBinding(frontend.typeChecker, scope, name, binding);
|
||||
}
|
||||
|
||||
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, Binding binding)
|
||||
{
|
||||
scope->bindings[typeChecker.globalNames.names->getOrAdd(name.c_str())] = binding;
|
||||
}
|
||||
|
||||
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name)
|
||||
{
|
||||
auto t = tryGetGlobalBinding(typeChecker, name);
|
||||
LUAU_ASSERT(t.has_value());
|
||||
return t->typeId;
|
||||
}
|
||||
|
||||
std::optional<Binding> tryGetGlobalBinding(TypeChecker& typeChecker, const std::string& name)
|
||||
{
|
||||
AstName astName = typeChecker.globalNames.names->getOrAdd(name.c_str());
|
||||
@ -166,6 +188,23 @@ std::optional<Binding> tryGetGlobalBinding(TypeChecker& typeChecker, const std::
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name)
|
||||
{
|
||||
auto t = tryGetGlobalBinding(typeChecker, name);
|
||||
LUAU_ASSERT(t.has_value());
|
||||
return t->typeId;
|
||||
}
|
||||
|
||||
TypeId getGlobalBinding(Frontend& frontend, const std::string& name)
|
||||
{
|
||||
return getGlobalBinding(frontend.typeChecker, name);
|
||||
}
|
||||
|
||||
std::optional<Binding> tryGetGlobalBinding(Frontend& frontend, const std::string& name)
|
||||
{
|
||||
return tryGetGlobalBinding(frontend.typeChecker, name);
|
||||
}
|
||||
|
||||
Binding* tryGetGlobalBindingRef(TypeChecker& typeChecker, const std::string& name)
|
||||
{
|
||||
AstName astName = typeChecker.globalNames.names->get(name.c_str());
|
||||
@ -195,6 +234,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
TypeId nilType = typeChecker.nilType;
|
||||
|
||||
TypeArena& arena = typeChecker.globalTypes;
|
||||
NotNull<SingletonTypes> singletonTypes = typeChecker.singletonTypes;
|
||||
|
||||
LoadDefinitionFileResult loadResult = Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), "@luau");
|
||||
LUAU_ASSERT(loadResult.success);
|
||||
@ -203,7 +243,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level, TableState::Generic});
|
||||
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(getSingletonTypes().stringType);
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(singletonTypes->stringType, singletonTypes);
|
||||
LUAU_ASSERT(stringMetatableTy);
|
||||
const TableTypeVar* stringMetatableTable = get<TableTypeVar>(follow(*stringMetatableTy));
|
||||
LUAU_ASSERT(stringMetatableTable);
|
||||
@ -277,6 +317,98 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
||||
attachDcrMagicFunction(getGlobalBinding(typeChecker, "require"), dcrMagicFunctionRequire);
|
||||
}
|
||||
|
||||
void registerBuiltinTypes(Frontend& frontend)
|
||||
{
|
||||
LUAU_ASSERT(!frontend.globalTypes.typeVars.isFrozen());
|
||||
LUAU_ASSERT(!frontend.globalTypes.typePacks.isFrozen());
|
||||
|
||||
TypeId nilType = frontend.typeChecker.nilType;
|
||||
|
||||
TypeArena& arena = frontend.globalTypes;
|
||||
NotNull<SingletonTypes> singletonTypes = frontend.singletonTypes;
|
||||
|
||||
LoadDefinitionFileResult loadResult = frontend.loadDefinitionFile(getBuiltinDefinitionSource(), "@luau");
|
||||
LUAU_ASSERT(loadResult.success);
|
||||
|
||||
TypeId genericK = arena.addType(GenericTypeVar{"K"});
|
||||
TypeId genericV = arena.addType(GenericTypeVar{"V"});
|
||||
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), frontend.getGlobalScope()->level, TableState::Generic});
|
||||
|
||||
std::optional<TypeId> stringMetatableTy = getMetatable(singletonTypes->stringType, singletonTypes);
|
||||
LUAU_ASSERT(stringMetatableTy);
|
||||
const TableTypeVar* stringMetatableTable = get<TableTypeVar>(follow(*stringMetatableTy));
|
||||
LUAU_ASSERT(stringMetatableTable);
|
||||
|
||||
auto it = stringMetatableTable->props.find("__index");
|
||||
LUAU_ASSERT(it != stringMetatableTable->props.end());
|
||||
|
||||
addGlobalBinding(frontend, "string", it->second.type, "@luau");
|
||||
|
||||
// next<K, V>(t: Table<K, V>, i: K?) -> (K, V)
|
||||
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(frontend, arena, genericK)}});
|
||||
addGlobalBinding(frontend, "next",
|
||||
arena.addType(FunctionTypeVar{{genericK, genericV}, {}, nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})}), "@luau");
|
||||
|
||||
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
|
||||
|
||||
TypeId pairsNext = arena.addType(FunctionTypeVar{nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})});
|
||||
TypePackId pairsReturnTypePack = arena.addTypePack(TypePack{{pairsNext, mapOfKtoV, nilType}});
|
||||
|
||||
// pairs<K, V>(t: Table<K, V>) -> ((Table<K, V>, K?) -> (K, V), Table<K, V>, nil)
|
||||
addGlobalBinding(frontend, "pairs", arena.addType(FunctionTypeVar{{genericK, genericV}, {}, pairsArgsTypePack, pairsReturnTypePack}), "@luau");
|
||||
|
||||
TypeId genericMT = arena.addType(GenericTypeVar{"MT"});
|
||||
|
||||
TableTypeVar tab{TableState::Generic, frontend.getGlobalScope()->level};
|
||||
TypeId tabTy = arena.addType(tab);
|
||||
|
||||
TypeId tableMetaMT = arena.addType(MetatableTypeVar{tabTy, genericMT});
|
||||
|
||||
addGlobalBinding(frontend, "getmetatable", makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@luau");
|
||||
|
||||
// clang-format off
|
||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
||||
addGlobalBinding(frontend, "setmetatable",
|
||||
arena.addType(
|
||||
FunctionTypeVar{
|
||||
{genericMT},
|
||||
{},
|
||||
arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tableMetaMT}})
|
||||
}
|
||||
), "@luau"
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
for (const auto& pair : frontend.getGlobalScope()->bindings)
|
||||
{
|
||||
persist(pair.second.typeId);
|
||||
|
||||
if (TableTypeVar* ttv = getMutable<TableTypeVar>(pair.second.typeId))
|
||||
{
|
||||
if (!ttv->name)
|
||||
ttv->name = toString(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
attachMagicFunction(getGlobalBinding(frontend, "assert"), magicFunctionAssert);
|
||||
attachMagicFunction(getGlobalBinding(frontend, "setmetatable"), magicFunctionSetMetaTable);
|
||||
attachMagicFunction(getGlobalBinding(frontend, "select"), magicFunctionSelect);
|
||||
|
||||
if (TableTypeVar* ttv = getMutable<TableTypeVar>(getGlobalBinding(frontend, "table")))
|
||||
{
|
||||
// tabTy is a generic table type which we can't express via declaration syntax yet
|
||||
ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze");
|
||||
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
|
||||
|
||||
attachMagicFunction(ttv->props["pack"].type, magicFunctionPack);
|
||||
}
|
||||
|
||||
attachMagicFunction(getGlobalBinding(frontend, "require"), magicFunctionRequire);
|
||||
attachDcrMagicFunction(getGlobalBinding(frontend, "require"), dcrMagicFunctionRequire);
|
||||
}
|
||||
|
||||
|
||||
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
|
||||
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
|
||||
{
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
|
||||
LUAU_FASTINT(LuauCheckRecursionLimit);
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
||||
|
||||
#include "Luau/Scope.h"
|
||||
|
||||
@ -35,17 +37,20 @@ static std::optional<AstExpr*> matchRequire(const AstExprCall& call)
|
||||
}
|
||||
|
||||
ConstraintGraphBuilder::ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena,
|
||||
NotNull<ModuleResolver> moduleResolver, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope)
|
||||
NotNull<ModuleResolver> moduleResolver, NotNull<SingletonTypes> singletonTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, DcrLogger* logger)
|
||||
: moduleName(moduleName)
|
||||
, module(module)
|
||||
, singletonTypes(getSingletonTypes())
|
||||
, singletonTypes(singletonTypes)
|
||||
, arena(arena)
|
||||
, rootScope(nullptr)
|
||||
, moduleResolver(moduleResolver)
|
||||
, ice(ice)
|
||||
, globalScope(globalScope)
|
||||
, logger(logger)
|
||||
{
|
||||
LUAU_ASSERT(arena);
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
LUAU_ASSERT(logger);
|
||||
|
||||
LUAU_ASSERT(module);
|
||||
}
|
||||
|
||||
@ -66,6 +71,7 @@ ScopePtr ConstraintGraphBuilder::childScope(AstNode* node, const ScopePtr& paren
|
||||
scopes.emplace_back(node->location, scope);
|
||||
|
||||
scope->returnType = parent->returnType;
|
||||
scope->varargPack = parent->varargPack;
|
||||
|
||||
parent->children.push_back(NotNull{scope.get()});
|
||||
module->astScopes[node] = scope.get();
|
||||
@ -282,7 +288,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||
return;
|
||||
|
||||
TypeId t = check(scope, expr);
|
||||
addConstraint(scope, expr->location, SubtypeConstraint{t, singletonTypes.numberType});
|
||||
addConstraint(scope, expr->location, SubtypeConstraint{t, singletonTypes->numberType});
|
||||
};
|
||||
|
||||
checkNumber(for_->from);
|
||||
@ -290,7 +296,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||
checkNumber(for_->step);
|
||||
|
||||
ScopePtr forScope = childScope(for_, scope);
|
||||
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
|
||||
forScope->bindings[for_->var] = Binding{singletonTypes->numberType, for_->var->location};
|
||||
|
||||
visit(forScope, for_->body);
|
||||
}
|
||||
@ -435,7 +441,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* funct
|
||||
}
|
||||
else if (AstExprError* err = function->name->as<AstExprError>())
|
||||
{
|
||||
functionType = singletonTypes.errorRecoveryType();
|
||||
functionType = singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
LUAU_ASSERT(functionType != nullptr);
|
||||
@ -657,12 +663,18 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareFunction
|
||||
std::vector<TypeId> genericTys;
|
||||
genericTys.reserve(generics.size());
|
||||
for (auto& [name, generic] : generics)
|
||||
{
|
||||
genericTys.push_back(generic.ty);
|
||||
scope->privateTypeBindings[name] = TypeFun{generic.ty};
|
||||
}
|
||||
|
||||
std::vector<TypePackId> genericTps;
|
||||
genericTps.reserve(genericPacks.size());
|
||||
for (auto& [name, generic] : genericPacks)
|
||||
{
|
||||
genericTps.push_back(generic.tp);
|
||||
scope->privateTypePackBindings[name] = generic.tp;
|
||||
}
|
||||
|
||||
ScopePtr funScope = scope;
|
||||
if (!generics.empty() || !genericPacks.empty())
|
||||
@ -710,7 +722,7 @@ TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* exp
|
||||
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
||||
{
|
||||
reportCodeTooComplex(expr->location);
|
||||
return singletonTypes.errorRecoveryTypePack();
|
||||
return singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
|
||||
TypePackId result = nullptr;
|
||||
@ -758,7 +770,7 @@ TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* exp
|
||||
if (scope->varargPack)
|
||||
result = *scope->varargPack;
|
||||
else
|
||||
result = singletonTypes.errorRecoveryTypePack();
|
||||
result = singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -778,7 +790,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
||||
{
|
||||
reportCodeTooComplex(expr->location);
|
||||
return singletonTypes.errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
TypeId result = nullptr;
|
||||
@ -786,20 +798,20 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||
if (auto group = expr->as<AstExprGroup>())
|
||||
result = check(scope, group->expr);
|
||||
else if (expr->is<AstExprConstantString>())
|
||||
result = singletonTypes.stringType;
|
||||
result = singletonTypes->stringType;
|
||||
else if (expr->is<AstExprConstantNumber>())
|
||||
result = singletonTypes.numberType;
|
||||
result = singletonTypes->numberType;
|
||||
else if (expr->is<AstExprConstantBool>())
|
||||
result = singletonTypes.booleanType;
|
||||
result = singletonTypes->booleanType;
|
||||
else if (expr->is<AstExprConstantNil>())
|
||||
result = singletonTypes.nilType;
|
||||
result = singletonTypes->nilType;
|
||||
else if (auto a = expr->as<AstExprLocal>())
|
||||
{
|
||||
std::optional<TypeId> ty = scope->lookup(a->local);
|
||||
if (ty)
|
||||
result = *ty;
|
||||
else
|
||||
result = singletonTypes.errorRecoveryType(); // FIXME? Record an error at this point?
|
||||
result = singletonTypes->errorRecoveryType(); // FIXME? Record an error at this point?
|
||||
}
|
||||
else if (auto g = expr->as<AstExprGlobal>())
|
||||
{
|
||||
@ -812,7 +824,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||
* global that is not already in-scope is definitely an unknown symbol.
|
||||
*/
|
||||
reportError(g->location, UnknownSymbol{g->name.value});
|
||||
result = singletonTypes.errorRecoveryType(); // FIXME? Record an error at this point?
|
||||
result = singletonTypes->errorRecoveryType(); // FIXME? Record an error at this point?
|
||||
}
|
||||
}
|
||||
else if (expr->is<AstExprVarargs>())
|
||||
@ -842,7 +854,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||
else if (auto err = expr->as<AstExprError>())
|
||||
{
|
||||
// Open question: Should we traverse into this?
|
||||
result = singletonTypes.errorRecoveryType();
|
||||
result = singletonTypes->errorRecoveryType();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -903,7 +915,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
|
||||
}
|
||||
|
||||
LUAU_UNREACHABLE();
|
||||
return singletonTypes.errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary)
|
||||
@ -1003,7 +1015,7 @@ TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTabl
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId numberType = singletonTypes.numberType;
|
||||
TypeId numberType = singletonTypes->numberType;
|
||||
// FIXME? The location isn't quite right here. Not sure what is
|
||||
// right.
|
||||
createIndexer(item.value->location, numberType, itemTy);
|
||||
@ -1068,6 +1080,23 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||
signatureScope = bodyScope;
|
||||
}
|
||||
|
||||
std::optional<TypePackId> varargPack;
|
||||
|
||||
if (fn->vararg)
|
||||
{
|
||||
if (fn->varargAnnotation)
|
||||
{
|
||||
TypePackId annotationType = resolveTypePack(signatureScope, fn->varargAnnotation);
|
||||
varargPack = annotationType;
|
||||
}
|
||||
else
|
||||
{
|
||||
varargPack = arena->freshTypePack(signatureScope.get());
|
||||
}
|
||||
|
||||
signatureScope->varargPack = varargPack;
|
||||
}
|
||||
|
||||
if (fn->returnAnnotation)
|
||||
{
|
||||
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
|
||||
@ -1092,7 +1121,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
||||
// TODO: Vararg annotation.
|
||||
// TODO: Preserve argument names in the function's type.
|
||||
|
||||
FunctionTypeVar actualFunction{arena->addTypePack(argTypes), returnType};
|
||||
FunctionTypeVar actualFunction{arena->addTypePack(argTypes, varargPack), returnType};
|
||||
actualFunction.hasNoGenerics = !hasGenerics;
|
||||
actualFunction.generics = std::move(genericTypes);
|
||||
actualFunction.genericPacks = std::move(genericTypePacks);
|
||||
@ -1175,7 +1204,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, b
|
||||
else
|
||||
{
|
||||
reportError(ty->location, UnknownSymbol{ref->name.value, UnknownSymbol::Context::Type});
|
||||
result = singletonTypes.errorRecoveryType();
|
||||
result = singletonTypes->errorRecoveryType();
|
||||
}
|
||||
}
|
||||
else if (auto tab = ty->as<AstTypeTable>())
|
||||
@ -1308,12 +1337,12 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, b
|
||||
}
|
||||
else if (ty->is<AstTypeError>())
|
||||
{
|
||||
result = singletonTypes.errorRecoveryType();
|
||||
result = singletonTypes->errorRecoveryType();
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(0);
|
||||
result = singletonTypes.errorRecoveryType();
|
||||
result = singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
astResolvedTypes[ty] = result;
|
||||
@ -1341,13 +1370,13 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp
|
||||
else
|
||||
{
|
||||
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
|
||||
result = singletonTypes.errorRecoveryTypePack();
|
||||
result = singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(0);
|
||||
result = singletonTypes.errorRecoveryTypePack();
|
||||
result = singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
|
||||
astResolvedTypePacks[tp] = result;
|
||||
@ -1430,11 +1459,17 @@ TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location locat
|
||||
void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err)
|
||||
{
|
||||
errors.push_back(TypeError{location, moduleName, std::move(err)});
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->captureGenerationError(errors.back());
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
|
||||
{
|
||||
errors.push_back(TypeError{location, moduleName, CodeTooComplex{}});
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->captureGenerationError(errors.back());
|
||||
}
|
||||
|
||||
struct GlobalPrepopulator : AstVisitor
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "Luau/Quantify.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Unifier.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
|
||||
@ -50,8 +51,8 @@ static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
|
||||
dumpConstraints(child, opts);
|
||||
}
|
||||
|
||||
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||
const TypeFun& fn, const std::vector<TypeId>& rawTypeArguments, const std::vector<TypePackId>& rawPackArguments, TypeArena* arena)
|
||||
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
|
||||
const TypeFun& fn, const std::vector<TypeId>& rawTypeArguments, const std::vector<TypePackId>& rawPackArguments)
|
||||
{
|
||||
std::vector<TypeId> saturatedTypeArguments;
|
||||
std::vector<TypeId> extraTypes;
|
||||
@ -131,7 +132,7 @@ static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments
|
||||
if (!defaultTy)
|
||||
break;
|
||||
|
||||
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(getSingletonTypes().errorRecoveryType());
|
||||
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(singletonTypes->errorRecoveryType());
|
||||
atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
|
||||
saturatedTypeArguments.push_back(instantiatedDefault);
|
||||
}
|
||||
@ -149,7 +150,7 @@ static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments
|
||||
if (!defaultTp)
|
||||
break;
|
||||
|
||||
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(getSingletonTypes().errorRecoveryTypePack());
|
||||
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(singletonTypes->errorRecoveryTypePack());
|
||||
atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
|
||||
saturatedPackArguments.push_back(instantiatedDefault);
|
||||
}
|
||||
@ -167,12 +168,12 @@ static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments
|
||||
// even if they're missing, so we use the error type as a filler.
|
||||
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i)
|
||||
{
|
||||
saturatedTypeArguments.push_back(getSingletonTypes().errorRecoveryType());
|
||||
saturatedTypeArguments.push_back(singletonTypes->errorRecoveryType());
|
||||
}
|
||||
|
||||
for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
|
||||
{
|
||||
saturatedPackArguments.push_back(getSingletonTypes().errorRecoveryTypePack());
|
||||
saturatedPackArguments.push_back(singletonTypes->errorRecoveryTypePack());
|
||||
}
|
||||
|
||||
// At this point, these two conditions should be true. If they aren't we
|
||||
@ -242,14 +243,16 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
||||
}
|
||||
}
|
||||
|
||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope, ModuleName moduleName, NotNull<ModuleResolver> moduleResolver,
|
||||
std::vector<RequireCycle> requireCycles)
|
||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<SingletonTypes> singletonTypes, NotNull<Scope> rootScope, ModuleName moduleName,
|
||||
NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger)
|
||||
: arena(arena)
|
||||
, singletonTypes(singletonTypes)
|
||||
, constraints(collectConstraints(rootScope))
|
||||
, rootScope(rootScope)
|
||||
, currentModuleName(std::move(moduleName))
|
||||
, moduleResolver(moduleResolver)
|
||||
, requireCycles(requireCycles)
|
||||
, logger(logger)
|
||||
{
|
||||
opts.exhaustive = true;
|
||||
|
||||
@ -262,6 +265,9 @@ ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope, M
|
||||
block(dep, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
LUAU_ASSERT(logger);
|
||||
}
|
||||
|
||||
void ConstraintSolver::run()
|
||||
@ -277,7 +283,7 @@ void ConstraintSolver::run()
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
logger.captureBoundarySnapshot(rootScope, unsolvedConstraints);
|
||||
logger->captureInitialSolverState(rootScope, unsolvedConstraints);
|
||||
}
|
||||
|
||||
auto runSolverPass = [&](bool force) {
|
||||
@ -294,10 +300,11 @@ void ConstraintSolver::run()
|
||||
}
|
||||
|
||||
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
|
||||
StepSnapshot snapshot;
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
logger.prepareStepSnapshot(rootScope, c, unsolvedConstraints, force);
|
||||
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
||||
}
|
||||
|
||||
bool success = tryDispatch(c, force);
|
||||
@ -311,7 +318,7 @@ void ConstraintSolver::run()
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
logger.commitPreparedStepSnapshot();
|
||||
logger->commitStepSnapshot(snapshot);
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
@ -347,8 +354,7 @@ void ConstraintSolver::run()
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
logger.captureBoundarySnapshot(rootScope, unsolvedConstraints);
|
||||
printf("Logger output:\n%s\n", logger.compileOutput().c_str());
|
||||
logger->captureFinalSolverState(rootScope, unsolvedConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,7 +522,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
|
||||
|
||||
if (isBlocked(leftType))
|
||||
{
|
||||
asMutable(resultType)->ty.emplace<BoundTypeVar>(getSingletonTypes().errorRecoveryType());
|
||||
asMutable(resultType)->ty.emplace<BoundTypeVar>(singletonTypes->errorRecoveryType());
|
||||
// reportError(constraint->location, CannotInferBinaryOperation{c.op, std::nullopt, CannotInferBinaryOperation::Operation});
|
||||
return true;
|
||||
}
|
||||
@ -571,7 +577,7 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
||||
if (0 == iteratorTypes.size())
|
||||
{
|
||||
Anyification anyify{
|
||||
arena, constraint->scope, &iceReporter, getSingletonTypes().errorRecoveryType(), getSingletonTypes().errorRecoveryTypePack()};
|
||||
arena, constraint->scope, singletonTypes, &iceReporter, singletonTypes->errorRecoveryType(), singletonTypes->errorRecoveryTypePack()};
|
||||
std::optional<TypePackId> anyified = anyify.substitute(c.variables);
|
||||
LUAU_ASSERT(anyified);
|
||||
unify(*anyified, c.variables, constraint->scope);
|
||||
@ -585,11 +591,11 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
||||
|
||||
if (get<FunctionTypeVar>(nextTy))
|
||||
{
|
||||
TypeId tableTy = getSingletonTypes().nilType;
|
||||
TypeId tableTy = singletonTypes->nilType;
|
||||
if (iteratorTypes.size() >= 2)
|
||||
tableTy = iteratorTypes[1];
|
||||
|
||||
TypeId firstIndexTy = getSingletonTypes().nilType;
|
||||
TypeId firstIndexTy = singletonTypes->nilType;
|
||||
if (iteratorTypes.size() >= 3)
|
||||
firstIndexTy = iteratorTypes[2];
|
||||
|
||||
@ -644,7 +650,7 @@ struct InfiniteTypeFinder : TypeVarOnceVisitor
|
||||
if (!tf.has_value())
|
||||
return true;
|
||||
|
||||
auto [typeArguments, packArguments] = saturateArguments(*tf, petv.typeArguments, petv.packArguments, solver->arena);
|
||||
auto [typeArguments, packArguments] = saturateArguments(solver->arena, solver->singletonTypes, *tf, petv.typeArguments, petv.packArguments);
|
||||
|
||||
if (follow(tf->type) == follow(signature.fn.type) && (signature.arguments != typeArguments || signature.packArguments != packArguments))
|
||||
{
|
||||
@ -698,7 +704,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||
if (!tf.has_value())
|
||||
{
|
||||
reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location);
|
||||
bindResult(getSingletonTypes().errorRecoveryType());
|
||||
bindResult(singletonTypes->errorRecoveryType());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -710,7 +716,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||
return true;
|
||||
}
|
||||
|
||||
auto [typeArguments, packArguments] = saturateArguments(*tf, petv->typeArguments, petv->packArguments, arena);
|
||||
auto [typeArguments, packArguments] = saturateArguments(arena, singletonTypes, *tf, petv->typeArguments, petv->packArguments);
|
||||
|
||||
bool sameTypes = std::equal(typeArguments.begin(), typeArguments.end(), tf->typeParams.begin(), tf->typeParams.end(), [](auto&& itp, auto&& p) {
|
||||
return itp == p.ty;
|
||||
@ -757,7 +763,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||
if (itf.foundInfiniteType)
|
||||
{
|
||||
// TODO (CLI-56761): Report an error.
|
||||
bindResult(getSingletonTypes().errorRecoveryType());
|
||||
bindResult(singletonTypes->errorRecoveryType());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -780,7 +786,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||
if (!maybeInstantiated.has_value())
|
||||
{
|
||||
// TODO (CLI-56761): Report an error.
|
||||
bindResult(getSingletonTypes().errorRecoveryType());
|
||||
bindResult(singletonTypes->errorRecoveryType());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -894,7 +900,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
||||
return block_(iteratorTy);
|
||||
|
||||
auto anyify = [&](auto ty) {
|
||||
Anyification anyify{arena, constraint->scope, &iceReporter, getSingletonTypes().anyType, getSingletonTypes().anyTypePack};
|
||||
Anyification anyify{arena, constraint->scope, singletonTypes, &iceReporter, singletonTypes->anyType, singletonTypes->anyTypePack};
|
||||
std::optional anyified = anyify.substitute(ty);
|
||||
if (!anyified)
|
||||
reportError(CodeTooComplex{}, constraint->location);
|
||||
@ -904,7 +910,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
||||
|
||||
auto errorify = [&](auto ty) {
|
||||
Anyification anyify{
|
||||
arena, constraint->scope, &iceReporter, getSingletonTypes().errorRecoveryType(), getSingletonTypes().errorRecoveryTypePack()};
|
||||
arena, constraint->scope, singletonTypes, &iceReporter, singletonTypes->errorRecoveryType(), singletonTypes->errorRecoveryTypePack()};
|
||||
std::optional errorified = anyify.substitute(ty);
|
||||
if (!errorified)
|
||||
reportError(CodeTooComplex{}, constraint->location);
|
||||
@ -973,7 +979,7 @@ bool ConstraintSolver::tryDispatchIterableFunction(
|
||||
: firstIndexTy;
|
||||
|
||||
// nextTy : (tableTy, indexTy?) -> (indexTy, valueTailTy...)
|
||||
const TypePackId nextArgPack = arena->addTypePack({tableTy, arena->addType(UnionTypeVar{{firstIndex, getSingletonTypes().nilType}})});
|
||||
const TypePackId nextArgPack = arena->addTypePack({tableTy, arena->addType(UnionTypeVar{{firstIndex, singletonTypes->nilType}})});
|
||||
const TypePackId valueTailTy = arena->addTypePack(FreeTypePack{constraint->scope});
|
||||
const TypePackId nextRetPack = arena->addTypePack(TypePack{{firstIndex}, valueTailTy});
|
||||
|
||||
@ -995,23 +1001,35 @@ void ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constrai
|
||||
|
||||
void ConstraintSolver::block(NotNull<const Constraint> target, NotNull<const Constraint> constraint)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->pushBlock(constraint, target);
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
printf("block Constraint %s on\t%s\n", toString(*target, opts).c_str(), toString(*constraint, opts).c_str());
|
||||
|
||||
block_(target, constraint);
|
||||
}
|
||||
|
||||
bool ConstraintSolver::block(TypeId target, NotNull<const Constraint> constraint)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->pushBlock(constraint, target);
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
printf("block TypeId %s on\t%s\n", toString(target, opts).c_str(), toString(*constraint, opts).c_str());
|
||||
|
||||
block_(target, constraint);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConstraintSolver::block(TypePackId target, NotNull<const Constraint> constraint)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->pushBlock(constraint, target);
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
printf("block TypeId %s on\t%s\n", toString(target, opts).c_str(), toString(*constraint, opts).c_str());
|
||||
|
||||
block_(target, constraint);
|
||||
return false;
|
||||
}
|
||||
@ -1042,16 +1060,25 @@ void ConstraintSolver::unblock_(BlockedConstraintId progressed)
|
||||
|
||||
void ConstraintSolver::unblock(NotNull<const Constraint> progressed)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->popBlock(progressed);
|
||||
|
||||
return unblock_(progressed);
|
||||
}
|
||||
|
||||
void ConstraintSolver::unblock(TypeId progressed)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->popBlock(progressed);
|
||||
|
||||
return unblock_(progressed);
|
||||
}
|
||||
|
||||
void ConstraintSolver::unblock(TypePackId progressed)
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->popBlock(progressed);
|
||||
|
||||
return unblock_(progressed);
|
||||
}
|
||||
|
||||
@ -1086,13 +1113,13 @@ bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
|
||||
void ConstraintSolver::unify(TypeId subType, TypeId superType, NotNull<Scope> scope)
|
||||
{
|
||||
UnifierSharedState sharedState{&iceReporter};
|
||||
Unifier u{arena, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
|
||||
u.tryUnify(subType, superType);
|
||||
|
||||
if (!u.errors.empty())
|
||||
{
|
||||
TypeId errorType = getSingletonTypes().errorRecoveryType();
|
||||
TypeId errorType = singletonTypes->errorRecoveryType();
|
||||
u.tryUnify(subType, errorType);
|
||||
u.tryUnify(superType, errorType);
|
||||
}
|
||||
@ -1108,7 +1135,7 @@ void ConstraintSolver::unify(TypeId subType, TypeId superType, NotNull<Scope> sc
|
||||
void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope)
|
||||
{
|
||||
UnifierSharedState sharedState{&iceReporter};
|
||||
Unifier u{arena, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
|
||||
u.tryUnify(subPack, superPack);
|
||||
|
||||
@ -1133,7 +1160,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l
|
||||
if (info.name.empty())
|
||||
{
|
||||
reportError(UnknownRequire{}, location);
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
std::string humanReadableName = moduleResolver->getHumanReadableModuleName(info.name);
|
||||
@ -1141,7 +1168,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l
|
||||
for (const auto& [location, path] : requireCycles)
|
||||
{
|
||||
if (!path.empty() && path.front() == humanReadableName)
|
||||
return getSingletonTypes().anyType;
|
||||
return singletonTypes->anyType;
|
||||
}
|
||||
|
||||
ModulePtr module = moduleResolver->getModule(info.name);
|
||||
@ -1150,24 +1177,24 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l
|
||||
if (!moduleResolver->moduleExists(info.name) && !info.optional)
|
||||
reportError(UnknownRequire{humanReadableName}, location);
|
||||
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
if (module->type != SourceCode::Type::Module)
|
||||
{
|
||||
reportError(IllegalRequire{humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location);
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
TypePackId modulePack = module->getModuleScope()->returnType;
|
||||
if (get<Unifiable::Error>(modulePack))
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
|
||||
std::optional<TypeId> moduleType = first(modulePack);
|
||||
if (!moduleType)
|
||||
{
|
||||
reportError(IllegalRequire{humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location);
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
return *moduleType;
|
||||
|
@ -1,150 +0,0 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/ConstraintSolverLogger.h"
|
||||
|
||||
#include "Luau/JsonEmitter.h"
|
||||
#include "Luau/ToString.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauFixNameMaps);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static void dumpScopeAndChildren(const Scope* scope, Json::JsonEmitter& emitter, ToStringOptions& opts)
|
||||
{
|
||||
emitter.writeRaw("{");
|
||||
Json::write(emitter, "bindings");
|
||||
emitter.writeRaw(":");
|
||||
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
|
||||
for (const auto& [name, binding] : scope->bindings)
|
||||
{
|
||||
if (FFlag::LuauFixNameMaps)
|
||||
o.writePair(name.c_str(), toString(binding.typeId, opts));
|
||||
else
|
||||
{
|
||||
ToStringResult result = toStringDetailed(binding.typeId, opts);
|
||||
opts.DEPRECATED_nameMap = std::move(result.DEPRECATED_nameMap);
|
||||
o.writePair(name.c_str(), result.name);
|
||||
}
|
||||
}
|
||||
|
||||
o.finish();
|
||||
emitter.writeRaw(",");
|
||||
Json::write(emitter, "children");
|
||||
emitter.writeRaw(":");
|
||||
|
||||
Json::ArrayEmitter a = emitter.writeArray();
|
||||
for (const Scope* child : scope->children)
|
||||
{
|
||||
emitter.writeComma();
|
||||
dumpScopeAndChildren(child, emitter, opts);
|
||||
}
|
||||
|
||||
a.finish();
|
||||
emitter.writeRaw("}");
|
||||
}
|
||||
|
||||
static std::string dumpConstraintsToDot(std::vector<NotNull<const Constraint>>& constraints, ToStringOptions& opts)
|
||||
{
|
||||
std::string result = "digraph Constraints {\n";
|
||||
result += "rankdir=LR\n";
|
||||
|
||||
std::unordered_set<NotNull<const Constraint>> contained;
|
||||
for (NotNull<const Constraint> c : constraints)
|
||||
{
|
||||
contained.insert(c);
|
||||
}
|
||||
|
||||
for (NotNull<const Constraint> c : constraints)
|
||||
{
|
||||
std::string shape;
|
||||
if (get<SubtypeConstraint>(*c))
|
||||
shape = "box";
|
||||
else if (get<PackSubtypeConstraint>(*c))
|
||||
shape = "box3d";
|
||||
else
|
||||
shape = "oval";
|
||||
|
||||
std::string id = std::to_string(reinterpret_cast<size_t>(c.get()));
|
||||
result += id;
|
||||
result += " [label=\"";
|
||||
result += toString(*c, opts);
|
||||
result += "\" shape=" + shape + "];\n";
|
||||
|
||||
for (NotNull<const Constraint> dep : c->dependencies)
|
||||
{
|
||||
if (contained.count(dep) == 0)
|
||||
continue;
|
||||
|
||||
result += std::to_string(reinterpret_cast<size_t>(dep.get()));
|
||||
result += " -> ";
|
||||
result += id;
|
||||
result += ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
result += "}";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ConstraintSolverLogger::compileOutput()
|
||||
{
|
||||
Json::JsonEmitter emitter;
|
||||
emitter.writeRaw("[");
|
||||
for (const std::string& snapshot : snapshots)
|
||||
{
|
||||
emitter.writeComma();
|
||||
emitter.writeRaw(snapshot);
|
||||
}
|
||||
|
||||
emitter.writeRaw("]");
|
||||
return emitter.str();
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
Json::JsonEmitter emitter;
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("type", "boundary");
|
||||
o.writePair("constraintGraph", dumpConstraintsToDot(unsolvedConstraints, opts));
|
||||
emitter.writeComma();
|
||||
Json::write(emitter, "rootScope");
|
||||
emitter.writeRaw(":");
|
||||
dumpScopeAndChildren(rootScope, emitter, opts);
|
||||
o.finish();
|
||||
|
||||
snapshots.push_back(emitter.str());
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::prepareStepSnapshot(
|
||||
const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints, bool force)
|
||||
{
|
||||
Json::JsonEmitter emitter;
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("type", "step");
|
||||
o.writePair("constraintGraph", dumpConstraintsToDot(unsolvedConstraints, opts));
|
||||
o.writePair("currentId", std::to_string(reinterpret_cast<size_t>(current.get())));
|
||||
o.writePair("current", toString(*current, opts));
|
||||
o.writePair("force", force);
|
||||
emitter.writeComma();
|
||||
Json::write(emitter, "rootScope");
|
||||
emitter.writeRaw(":");
|
||||
dumpScopeAndChildren(rootScope, emitter, opts);
|
||||
o.finish();
|
||||
|
||||
preparedSnapshot = emitter.str();
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::commitPreparedStepSnapshot()
|
||||
{
|
||||
if (preparedSnapshot)
|
||||
{
|
||||
snapshots.push_back(std::move(*preparedSnapshot));
|
||||
preparedSnapshot = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Luau
|
395
Analysis/src/DcrLogger.cpp
Normal file
395
Analysis/src/DcrLogger.cpp
Normal file
@ -0,0 +1,395 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/DcrLogger.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Luau/JsonEmitter.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
namespace Json
|
||||
{
|
||||
|
||||
void write(JsonEmitter& emitter, const Location& location)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("beginLine", location.begin.line);
|
||||
o.writePair("beginColumn", location.begin.column);
|
||||
o.writePair("endLine", location.end.line);
|
||||
o.writePair("endColumn", location.end.column);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ErrorSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("message", snapshot.message);
|
||||
o.writePair("location", snapshot.location);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const BindingSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("typeId", snapshot.typeId);
|
||||
o.writePair("typeString", snapshot.typeString);
|
||||
o.writePair("location", snapshot.location);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const TypeBindingSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("typeId", snapshot.typeId);
|
||||
o.writePair("typeString", snapshot.typeString);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ConstraintGenerationLog& log)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("source", log.source);
|
||||
|
||||
emitter.writeComma();
|
||||
write(emitter, "constraintLocations");
|
||||
emitter.writeRaw(":");
|
||||
|
||||
ObjectEmitter locationEmitter = emitter.writeObject();
|
||||
|
||||
for (const auto& [id, location] : log.constraintLocations)
|
||||
{
|
||||
locationEmitter.writePair(id, location);
|
||||
}
|
||||
|
||||
locationEmitter.finish();
|
||||
o.writePair("errors", log.errors);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ScopeSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("bindings", snapshot.bindings);
|
||||
o.writePair("typeBindings", snapshot.typeBindings);
|
||||
o.writePair("typePackBindings", snapshot.typePackBindings);
|
||||
o.writePair("children", snapshot.children);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ConstraintBlockKind& kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ConstraintBlockKind::TypeId:
|
||||
return write(emitter, "type");
|
||||
case ConstraintBlockKind::TypePackId:
|
||||
return write(emitter, "typePack");
|
||||
case ConstraintBlockKind::ConstraintId:
|
||||
return write(emitter, "constraint");
|
||||
default:
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ConstraintBlock& block)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("kind", block.kind);
|
||||
o.writePair("stringification", block.stringification);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const ConstraintSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("stringification", snapshot.stringification);
|
||||
o.writePair("blocks", snapshot.blocks);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const BoundarySnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("rootScope", snapshot.rootScope);
|
||||
o.writePair("constraints", snapshot.constraints);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const StepSnapshot& snapshot)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("currentConstraint", snapshot.currentConstraint);
|
||||
o.writePair("forced", snapshot.forced);
|
||||
o.writePair("unsolvedConstraints", snapshot.unsolvedConstraints);
|
||||
o.writePair("rootScope", snapshot.rootScope);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const TypeSolveLog& log)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("initialState", log.initialState);
|
||||
o.writePair("stepStates", log.stepStates);
|
||||
o.writePair("finalState", log.finalState);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
void write(JsonEmitter& emitter, const TypeCheckLog& log)
|
||||
{
|
||||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("errors", log.errors);
|
||||
o.finish();
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
|
||||
static std::string toPointerId(NotNull<const Constraint> ptr)
|
||||
{
|
||||
return std::to_string(reinterpret_cast<size_t>(ptr.get()));
|
||||
}
|
||||
|
||||
static ScopeSnapshot snapshotScope(const Scope* scope, ToStringOptions& opts)
|
||||
{
|
||||
std::unordered_map<Name, BindingSnapshot> bindings;
|
||||
std::unordered_map<Name, TypeBindingSnapshot> typeBindings;
|
||||
std::unordered_map<Name, TypeBindingSnapshot> typePackBindings;
|
||||
std::vector<ScopeSnapshot> children;
|
||||
|
||||
for (const auto& [name, binding] : scope->bindings)
|
||||
{
|
||||
std::string id = std::to_string(reinterpret_cast<size_t>(binding.typeId));
|
||||
ToStringResult result = toStringDetailed(binding.typeId, opts);
|
||||
|
||||
bindings[name.c_str()] = BindingSnapshot{
|
||||
id,
|
||||
result.name,
|
||||
binding.location,
|
||||
};
|
||||
}
|
||||
|
||||
for (const auto& [name, tf] : scope->exportedTypeBindings)
|
||||
{
|
||||
std::string id = std::to_string(reinterpret_cast<size_t>(tf.type));
|
||||
|
||||
typeBindings[name] = TypeBindingSnapshot{
|
||||
id,
|
||||
toString(tf.type, opts),
|
||||
};
|
||||
}
|
||||
|
||||
for (const auto& [name, tf] : scope->privateTypeBindings)
|
||||
{
|
||||
std::string id = std::to_string(reinterpret_cast<size_t>(tf.type));
|
||||
|
||||
typeBindings[name] = TypeBindingSnapshot{
|
||||
id,
|
||||
toString(tf.type, opts),
|
||||
};
|
||||
}
|
||||
|
||||
for (const auto& [name, tp] : scope->privateTypePackBindings)
|
||||
{
|
||||
std::string id = std::to_string(reinterpret_cast<size_t>(tp));
|
||||
|
||||
typePackBindings[name] = TypeBindingSnapshot{
|
||||
id,
|
||||
toString(tp, opts),
|
||||
};
|
||||
}
|
||||
|
||||
for (const auto& child : scope->children)
|
||||
{
|
||||
children.push_back(snapshotScope(child.get(), opts));
|
||||
}
|
||||
|
||||
return ScopeSnapshot{
|
||||
bindings,
|
||||
typeBindings,
|
||||
typePackBindings,
|
||||
children,
|
||||
};
|
||||
}
|
||||
|
||||
std::string DcrLogger::compileOutput()
|
||||
{
|
||||
Json::JsonEmitter emitter;
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("generation", generationLog);
|
||||
o.writePair("solve", solveLog);
|
||||
o.writePair("check", checkLog);
|
||||
o.finish();
|
||||
|
||||
return emitter.str();
|
||||
}
|
||||
|
||||
void DcrLogger::captureSource(std::string source)
|
||||
{
|
||||
generationLog.source = std::move(source);
|
||||
}
|
||||
|
||||
void DcrLogger::captureGenerationError(const TypeError& error)
|
||||
{
|
||||
std::string stringifiedError = toString(error);
|
||||
generationLog.errors.push_back(ErrorSnapshot {
|
||||
/* message */ stringifiedError,
|
||||
/* location */ error.location,
|
||||
});
|
||||
}
|
||||
|
||||
void DcrLogger::captureConstraintLocation(NotNull<const Constraint> constraint, Location location)
|
||||
{
|
||||
std::string id = toPointerId(constraint);
|
||||
generationLog.constraintLocations[id] = location;
|
||||
}
|
||||
|
||||
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, TypeId block)
|
||||
{
|
||||
constraintBlocks[constraint].push_back(block);
|
||||
}
|
||||
|
||||
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, TypePackId block)
|
||||
{
|
||||
constraintBlocks[constraint].push_back(block);
|
||||
}
|
||||
|
||||
void DcrLogger::pushBlock(NotNull<const Constraint> constraint, NotNull<const Constraint> block)
|
||||
{
|
||||
constraintBlocks[constraint].push_back(block);
|
||||
}
|
||||
|
||||
void DcrLogger::popBlock(TypeId block)
|
||||
{
|
||||
for (auto& [_, list] : constraintBlocks)
|
||||
{
|
||||
list.erase(std::remove(list.begin(), list.end(), block), list.end());
|
||||
}
|
||||
}
|
||||
|
||||
void DcrLogger::popBlock(TypePackId block)
|
||||
{
|
||||
for (auto& [_, list] : constraintBlocks)
|
||||
{
|
||||
list.erase(std::remove(list.begin(), list.end(), block), list.end());
|
||||
}
|
||||
}
|
||||
|
||||
void DcrLogger::popBlock(NotNull<const Constraint> block)
|
||||
{
|
||||
for (auto& [_, list] : constraintBlocks)
|
||||
{
|
||||
list.erase(std::remove(list.begin(), list.end(), block), list.end());
|
||||
}
|
||||
}
|
||||
|
||||
void DcrLogger::captureInitialSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
solveLog.initialState.rootScope = snapshotScope(rootScope, opts);
|
||||
solveLog.initialState.constraints.clear();
|
||||
|
||||
for (NotNull<const Constraint> c : unsolvedConstraints)
|
||||
{
|
||||
std::string id = toPointerId(c);
|
||||
solveLog.initialState.constraints[id] = {
|
||||
toString(*c.get(), opts),
|
||||
snapshotBlocks(c),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
StepSnapshot DcrLogger::prepareStepSnapshot(const Scope* rootScope, NotNull<const Constraint> current, bool force, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
ScopeSnapshot scopeSnapshot = snapshotScope(rootScope, opts);
|
||||
std::string currentId = toPointerId(current);
|
||||
std::unordered_map<std::string, ConstraintSnapshot> constraints;
|
||||
|
||||
for (NotNull<const Constraint> c : unsolvedConstraints)
|
||||
{
|
||||
std::string id = toPointerId(c);
|
||||
constraints[id] = {
|
||||
toString(*c.get(), opts),
|
||||
snapshotBlocks(c),
|
||||
};
|
||||
}
|
||||
|
||||
return StepSnapshot{
|
||||
currentId,
|
||||
force,
|
||||
constraints,
|
||||
scopeSnapshot,
|
||||
};
|
||||
}
|
||||
|
||||
void DcrLogger::commitStepSnapshot(StepSnapshot snapshot)
|
||||
{
|
||||
solveLog.stepStates.push_back(std::move(snapshot));
|
||||
}
|
||||
|
||||
void DcrLogger::captureFinalSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
solveLog.finalState.rootScope = snapshotScope(rootScope, opts);
|
||||
solveLog.finalState.constraints.clear();
|
||||
|
||||
for (NotNull<const Constraint> c : unsolvedConstraints)
|
||||
{
|
||||
std::string id = toPointerId(c);
|
||||
solveLog.finalState.constraints[id] = {
|
||||
toString(*c.get(), opts),
|
||||
snapshotBlocks(c),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void DcrLogger::captureTypeCheckError(const TypeError& error)
|
||||
{
|
||||
std::string stringifiedError = toString(error);
|
||||
checkLog.errors.push_back(ErrorSnapshot {
|
||||
/* message */ stringifiedError,
|
||||
/* location */ error.location,
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<ConstraintBlock> DcrLogger::snapshotBlocks(NotNull<const Constraint> c)
|
||||
{
|
||||
auto it = constraintBlocks.find(c);
|
||||
if (it == constraintBlocks.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ConstraintBlock> snapshot;
|
||||
|
||||
for (const ConstraintBlockTarget& target : it->second)
|
||||
{
|
||||
if (const TypeId* ty = get_if<TypeId>(&target))
|
||||
{
|
||||
snapshot.push_back({
|
||||
ConstraintBlockKind::TypeId,
|
||||
toString(*ty, opts),
|
||||
});
|
||||
}
|
||||
else if (const TypePackId* tp = get_if<TypePackId>(&target))
|
||||
{
|
||||
snapshot.push_back({
|
||||
ConstraintBlockKind::TypePackId,
|
||||
toString(*tp, opts),
|
||||
});
|
||||
}
|
||||
else if (const NotNull<const Constraint>* c = get_if<NotNull<const Constraint>>(&target))
|
||||
{
|
||||
snapshot.push_back({
|
||||
ConstraintBlockKind::ConstraintId,
|
||||
toString(*(c->get()), opts),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
@ -187,13 +187,9 @@ declare utf8: {
|
||||
char: (...number) -> string,
|
||||
charpattern: string,
|
||||
codes: (string) -> ((string, number) -> (number, number), string, number),
|
||||
-- FIXME
|
||||
codepoint: (string, number?, number?) -> (number, ...number),
|
||||
codepoint: (string, number?, number?) -> ...number,
|
||||
len: (string, number?, number?) -> (number?, number?),
|
||||
offset: (string, number?, number?) -> number,
|
||||
nfdnormalize: (string) -> string,
|
||||
nfcnormalize: (string) -> string,
|
||||
graphemes: (string, number?, number?) -> (() -> (number, number)),
|
||||
}
|
||||
|
||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Luau/Config.h"
|
||||
#include "Luau/ConstraintGraphBuilder.h"
|
||||
#include "Luau/ConstraintSolver.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
#include "Luau/FileResolver.h"
|
||||
#include "Luau/Parser.h"
|
||||
#include "Luau/Scope.h"
|
||||
@ -23,10 +24,12 @@
|
||||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteDynamicLimits, false)
|
||||
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false)
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -389,11 +392,12 @@ double getTimestamp()
|
||||
} // namespace
|
||||
|
||||
Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options)
|
||||
: fileResolver(fileResolver)
|
||||
: singletonTypes(NotNull{FFlag::LuauNoMoreGlobalSingletonTypes ? &singletonTypes_ : &DEPRECATED_getSingletonTypes()})
|
||||
, fileResolver(fileResolver)
|
||||
, moduleResolver(this)
|
||||
, moduleResolverForAutocomplete(this)
|
||||
, typeChecker(&moduleResolver, &iceHandler)
|
||||
, typeCheckerForAutocomplete(&moduleResolverForAutocomplete, &iceHandler)
|
||||
, typeChecker(&moduleResolver, singletonTypes, &iceHandler)
|
||||
, typeCheckerForAutocomplete(&moduleResolverForAutocomplete, singletonTypes, &iceHandler)
|
||||
, configResolver(configResolver)
|
||||
, options(options)
|
||||
{
|
||||
@ -837,11 +841,22 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
|
||||
{
|
||||
ModulePtr result = std::make_shared<Module>();
|
||||
|
||||
ConstraintGraphBuilder cgb{sourceModule.name, result, &result->internalTypes, NotNull(&moduleResolver), NotNull(&iceHandler), getGlobalScope()};
|
||||
std::unique_ptr<DcrLogger> logger;
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
logger = std::make_unique<DcrLogger>();
|
||||
std::optional<SourceCode> source = fileResolver->readSource(sourceModule.name);
|
||||
if (source)
|
||||
{
|
||||
logger->captureSource(source->source);
|
||||
}
|
||||
}
|
||||
|
||||
ConstraintGraphBuilder cgb{sourceModule.name, result, &result->internalTypes, NotNull(&moduleResolver), singletonTypes, NotNull(&iceHandler), getGlobalScope(), logger.get()};
|
||||
cgb.visit(sourceModule.root);
|
||||
result->errors = std::move(cgb.errors);
|
||||
|
||||
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope), sourceModule.name, NotNull(&moduleResolver), requireCycles};
|
||||
ConstraintSolver cs{&result->internalTypes, singletonTypes, NotNull(cgb.rootScope), sourceModule.name, NotNull(&moduleResolver), requireCycles, logger.get()};
|
||||
cs.run();
|
||||
|
||||
for (TypeError& e : cs.errors)
|
||||
@ -855,9 +870,15 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
|
||||
result->astResolvedTypePacks = std::move(cgb.astResolvedTypePacks);
|
||||
result->type = sourceModule.type;
|
||||
|
||||
Luau::check(sourceModule, result.get());
|
||||
Luau::check(singletonTypes, logger.get(), sourceModule, result.get());
|
||||
|
||||
result->clonePublicInterface(iceHandler);
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
{
|
||||
std::string output = logger->compileOutput();
|
||||
printf("%s\n", output.c_str());
|
||||
}
|
||||
|
||||
result->clonePublicInterface(singletonTypes, iceHandler);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLintGlobalNeverReadBeforeWritten, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLintComparisonPrecedence, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLintFixDeprecationMessage, false)
|
||||
|
||||
namespace Luau
|
||||
@ -2954,7 +2953,7 @@ std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const Sc
|
||||
if (context.warningEnabled(LintWarning::Code_IntegerParsing))
|
||||
LintIntegerParsing::process(context);
|
||||
|
||||
if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence) && FFlag::LuauLintComparisonPrecedence)
|
||||
if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence))
|
||||
LintComparisonPrecedence::process(context);
|
||||
|
||||
std::sort(context.result.begin(), context.result.end(), WarningComparator());
|
||||
|
@ -92,10 +92,12 @@ struct ForceNormal : TypeVarOnceVisitor
|
||||
|
||||
struct ClonePublicInterface : Substitution
|
||||
{
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
NotNull<Module> module;
|
||||
|
||||
ClonePublicInterface(const TxnLog* log, Module* module)
|
||||
ClonePublicInterface(const TxnLog* log, NotNull<SingletonTypes> singletonTypes, Module* module)
|
||||
: Substitution(log, &module->interfaceTypes)
|
||||
, singletonTypes(singletonTypes)
|
||||
, module(module)
|
||||
{
|
||||
LUAU_ASSERT(module);
|
||||
@ -147,7 +149,7 @@ struct ClonePublicInterface : Substitution
|
||||
else
|
||||
{
|
||||
module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}});
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +165,7 @@ struct ClonePublicInterface : Substitution
|
||||
else
|
||||
{
|
||||
module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}});
|
||||
return getSingletonTypes().errorRecoveryTypePack();
|
||||
return singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +210,7 @@ Module::~Module()
|
||||
unfreeze(internalTypes);
|
||||
}
|
||||
|
||||
void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
void Module::clonePublicInterface(NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
LUAU_ASSERT(interfaceTypes.typeVars.empty());
|
||||
LUAU_ASSERT(interfaceTypes.typePacks.empty());
|
||||
@ -222,7 +224,7 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
std::unordered_map<Name, TypeFun>* exportedTypeBindings = &moduleScope->exportedTypeBindings;
|
||||
|
||||
TxnLog log;
|
||||
ClonePublicInterface clonePublicInterface{&log, this};
|
||||
ClonePublicInterface clonePublicInterface{&log, singletonTypes, this};
|
||||
|
||||
if (FFlag::LuauClonePublicInterfaceLess)
|
||||
returnType = clonePublicInterface.cloneTypePack(returnType);
|
||||
@ -243,12 +245,12 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
normalize(returnType, NotNull{this}, ice);
|
||||
normalize(returnType, NotNull{this}, singletonTypes, ice);
|
||||
if (FFlag::LuauForceExportSurfacesToBeNormal)
|
||||
forceNormal.traverse(returnType);
|
||||
if (varargPack)
|
||||
{
|
||||
normalize(*varargPack, NotNull{this}, ice);
|
||||
normalize(*varargPack, NotNull{this}, singletonTypes, ice);
|
||||
if (FFlag::LuauForceExportSurfacesToBeNormal)
|
||||
forceNormal.traverse(*varargPack);
|
||||
}
|
||||
@ -264,7 +266,7 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
tf = clone(tf, interfaceTypes, cloneState);
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
normalize(tf.type, NotNull{this}, ice);
|
||||
normalize(tf.type, NotNull{this}, singletonTypes, ice);
|
||||
|
||||
// We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables
|
||||
// won't be marked normal. If the types aren't normal by now, they never will be.
|
||||
@ -275,7 +277,7 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
|
||||
if (param.defaultValue)
|
||||
{
|
||||
normalize(*param.defaultValue, NotNull{this}, ice);
|
||||
normalize(*param.defaultValue, NotNull{this}, singletonTypes, ice);
|
||||
forceNormal.traverse(*param.defaultValue);
|
||||
}
|
||||
}
|
||||
@ -301,7 +303,7 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
||||
ty = clone(ty, interfaceTypes, cloneState);
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
normalize(ty, NotNull{this}, ice);
|
||||
normalize(ty, NotNull{this}, singletonTypes, ice);
|
||||
|
||||
if (FFlag::LuauForceExportSurfacesToBeNormal)
|
||||
forceNormal.traverse(ty);
|
||||
|
@ -54,11 +54,11 @@ struct Replacer
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, InternalErrorReporter& ice)
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
TypeArena arena;
|
||||
Unifier u{&arena, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{&arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
u.anyIsTop = true;
|
||||
|
||||
u.tryUnify(subTy, superTy);
|
||||
@ -66,11 +66,11 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, InternalError
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, InternalErrorReporter& ice)
|
||||
bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
TypeArena arena;
|
||||
Unifier u{&arena, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
Unifier u{&arena, singletonTypes, Mode::Strict, scope, Location{}, Covariant, sharedState};
|
||||
u.anyIsTop = true;
|
||||
|
||||
u.tryUnify(subPack, superPack);
|
||||
@ -133,15 +133,17 @@ struct Normalize final : TypeVarVisitor
|
||||
{
|
||||
using TypeVarVisitor::Set;
|
||||
|
||||
Normalize(TypeArena& arena, NotNull<Scope> scope, InternalErrorReporter& ice)
|
||||
Normalize(TypeArena& arena, NotNull<Scope> scope, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
: arena(arena)
|
||||
, scope(scope)
|
||||
, singletonTypes(singletonTypes)
|
||||
, ice(ice)
|
||||
{
|
||||
}
|
||||
|
||||
TypeArena& arena;
|
||||
NotNull<Scope> scope;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
InternalErrorReporter& ice;
|
||||
|
||||
int iterationLimit = 0;
|
||||
@ -499,9 +501,9 @@ struct Normalize final : TypeVarVisitor
|
||||
|
||||
for (TypeId& part : result)
|
||||
{
|
||||
if (isSubtype(ty, part, scope, ice))
|
||||
if (isSubtype(ty, part, scope, singletonTypes, ice))
|
||||
return; // no need to do anything
|
||||
else if (isSubtype(part, ty, scope, ice))
|
||||
else if (isSubtype(part, ty, scope, singletonTypes, ice))
|
||||
{
|
||||
part = ty; // replace the less general type by the more general one
|
||||
return;
|
||||
@ -553,12 +555,12 @@ struct Normalize final : TypeVarVisitor
|
||||
bool merged = false;
|
||||
for (TypeId& part : result->parts)
|
||||
{
|
||||
if (isSubtype(part, ty, scope, ice))
|
||||
if (isSubtype(part, ty, scope, singletonTypes, ice))
|
||||
{
|
||||
merged = true;
|
||||
break; // no need to do anything
|
||||
}
|
||||
else if (isSubtype(ty, part, scope, ice))
|
||||
else if (isSubtype(ty, part, scope, singletonTypes, ice))
|
||||
{
|
||||
merged = true;
|
||||
part = ty; // replace the less general type by the more general one
|
||||
@ -691,13 +693,14 @@ struct Normalize final : TypeVarVisitor
|
||||
/**
|
||||
* @returns A tuple of TypeId and a success indicator. (true indicates that the normalization completed successfully)
|
||||
*/
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Scope> scope, TypeArena& arena, InternalErrorReporter& ice)
|
||||
std::pair<TypeId, bool> normalize(
|
||||
TypeId ty, NotNull<Scope> scope, TypeArena& arena, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
CloneState state;
|
||||
if (FFlag::DebugLuauCopyBeforeNormalizing)
|
||||
(void)clone(ty, arena, state);
|
||||
|
||||
Normalize n{arena, scope, ice};
|
||||
Normalize n{arena, scope, singletonTypes, ice};
|
||||
n.traverse(ty);
|
||||
|
||||
return {ty, !n.limitExceeded};
|
||||
@ -707,39 +710,40 @@ std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Scope> scope, TypeArena& ar
|
||||
// reclaim memory used by wantonly allocated intermediate types here.
|
||||
// The main wrinkle here is that we don't want clone() to copy a type if the source and dest
|
||||
// arena are the same.
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Module> module, InternalErrorReporter& ice)
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, NotNull<Module> module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
return normalize(ty, NotNull{module->getModuleScope().get()}, module->internalTypes, ice);
|
||||
return normalize(ty, NotNull{module->getModuleScope().get()}, module->internalTypes, singletonTypes, ice);
|
||||
}
|
||||
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice)
|
||||
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
return normalize(ty, NotNull{module.get()}, ice);
|
||||
return normalize(ty, NotNull{module.get()}, singletonTypes, ice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns A tuple of TypeId and a success indicator. (true indicates that the normalization completed successfully)
|
||||
*/
|
||||
std::pair<TypePackId, bool> normalize(TypePackId tp, NotNull<Scope> scope, TypeArena& arena, InternalErrorReporter& ice)
|
||||
std::pair<TypePackId, bool> normalize(
|
||||
TypePackId tp, NotNull<Scope> scope, TypeArena& arena, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
CloneState state;
|
||||
if (FFlag::DebugLuauCopyBeforeNormalizing)
|
||||
(void)clone(tp, arena, state);
|
||||
|
||||
Normalize n{arena, scope, ice};
|
||||
Normalize n{arena, scope, singletonTypes, ice};
|
||||
n.traverse(tp);
|
||||
|
||||
return {tp, !n.limitExceeded};
|
||||
}
|
||||
|
||||
std::pair<TypePackId, bool> normalize(TypePackId tp, NotNull<Module> module, InternalErrorReporter& ice)
|
||||
std::pair<TypePackId, bool> normalize(TypePackId tp, NotNull<Module> module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
return normalize(tp, NotNull{module->getModuleScope().get()}, module->internalTypes, ice);
|
||||
return normalize(tp, NotNull{module->getModuleScope().get()}, module->internalTypes, singletonTypes, ice);
|
||||
}
|
||||
|
||||
std::pair<TypePackId, bool> normalize(TypePackId tp, const ModulePtr& module, InternalErrorReporter& ice)
|
||||
std::pair<TypePackId, bool> normalize(TypePackId tp, const ModulePtr& module, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter& ice)
|
||||
{
|
||||
return normalize(tp, NotNull{module.get()}, ice);
|
||||
return normalize(tp, NotNull{module.get()}, singletonTypes, ice);
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
@ -40,6 +40,15 @@ TypeId TypeArena::freshType(Scope* scope)
|
||||
return allocated;
|
||||
}
|
||||
|
||||
TypePackId TypeArena::freshTypePack(Scope* scope)
|
||||
{
|
||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope});
|
||||
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
TypePackId TypeArena::addTypePack(std::initializer_list<TypeId> types)
|
||||
{
|
||||
TypePackId allocated = typePacks.allocate(TypePack{std::move(types)});
|
||||
|
@ -1,8 +1,6 @@
|
||||
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/TypeChecker2.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/AstQuery.h"
|
||||
#include "Luau/Clone.h"
|
||||
@ -13,6 +11,12 @@
|
||||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/Unifier.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -54,18 +58,22 @@ struct StackPusher
|
||||
|
||||
struct TypeChecker2
|
||||
{
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
DcrLogger* logger;
|
||||
InternalErrorReporter ice; // FIXME accept a pointer from Frontend
|
||||
const SourceModule* sourceModule;
|
||||
Module* module;
|
||||
InternalErrorReporter ice; // FIXME accept a pointer from Frontend
|
||||
SingletonTypes& singletonTypes;
|
||||
|
||||
std::vector<NotNull<Scope>> stack;
|
||||
|
||||
TypeChecker2(const SourceModule* sourceModule, Module* module)
|
||||
: sourceModule(sourceModule)
|
||||
TypeChecker2(NotNull<SingletonTypes> singletonTypes, DcrLogger* logger, const SourceModule* sourceModule, Module* module)
|
||||
: singletonTypes(singletonTypes)
|
||||
, logger(logger)
|
||||
, sourceModule(sourceModule)
|
||||
, module(module)
|
||||
, singletonTypes(getSingletonTypes())
|
||||
{
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
LUAU_ASSERT(logger);
|
||||
}
|
||||
|
||||
std::optional<StackPusher> pushStack(AstNode* node)
|
||||
@ -85,7 +93,7 @@ struct TypeChecker2
|
||||
if (tp)
|
||||
return follow(*tp);
|
||||
else
|
||||
return singletonTypes.anyTypePack;
|
||||
return singletonTypes->anyTypePack;
|
||||
}
|
||||
|
||||
TypeId lookupType(AstExpr* expr)
|
||||
@ -101,7 +109,7 @@ struct TypeChecker2
|
||||
if (tp)
|
||||
return flattenPack(*tp);
|
||||
|
||||
return singletonTypes.anyType;
|
||||
return singletonTypes->anyType;
|
||||
}
|
||||
|
||||
TypeId lookupAnnotation(AstType* annotation)
|
||||
@ -253,7 +261,7 @@ struct TypeChecker2
|
||||
TypePackId actualRetType = reconstructPack(ret->list, arena);
|
||||
|
||||
UnifierSharedState sharedState{&ice};
|
||||
Unifier u{&arena, Mode::Strict, stack.back(), ret->location, Covariant, sharedState};
|
||||
Unifier u{&arena, singletonTypes, Mode::Strict, stack.back(), ret->location, Covariant, sharedState};
|
||||
u.anyIsTop = true;
|
||||
|
||||
u.tryUnify(actualRetType, expectedRetType);
|
||||
@ -299,7 +307,7 @@ struct TypeChecker2
|
||||
if (var->annotation)
|
||||
{
|
||||
TypeId varType = lookupAnnotation(var->annotation);
|
||||
if (!isSubtype(*it, varType, stack.back(), ice))
|
||||
if (!isSubtype(*it, varType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{varType, *it}, value->location);
|
||||
}
|
||||
@ -317,7 +325,7 @@ struct TypeChecker2
|
||||
if (var->annotation)
|
||||
{
|
||||
TypeId varType = lookupAnnotation(var->annotation);
|
||||
if (!isSubtype(varType, valueType, stack.back(), ice))
|
||||
if (!isSubtype(varType, valueType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{varType, valueType}, value->location);
|
||||
}
|
||||
@ -340,7 +348,7 @@ struct TypeChecker2
|
||||
|
||||
// "Render" a type pack out to an array of a given length. Expands
|
||||
// variadics and various other things to get there.
|
||||
static std::vector<TypeId> flatten(TypeArena& arena, TypePackId pack, size_t length)
|
||||
std::vector<TypeId> flatten(TypeArena& arena, TypePackId pack, size_t length)
|
||||
{
|
||||
std::vector<TypeId> result;
|
||||
|
||||
@ -376,7 +384,7 @@ struct TypeChecker2
|
||||
else if (auto etp = get<Unifiable::Error>(tail))
|
||||
{
|
||||
while (result.size() < length)
|
||||
result.push_back(getSingletonTypes().errorRecoveryType());
|
||||
result.push_back(singletonTypes->errorRecoveryType());
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -532,7 +540,7 @@ struct TypeChecker2
|
||||
visit(rhs);
|
||||
TypeId rhsType = lookupType(rhs);
|
||||
|
||||
if (!isSubtype(rhsType, lhsType, stack.back(), ice))
|
||||
if (!isSubtype(rhsType, lhsType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{lhsType, rhsType}, rhs->location);
|
||||
}
|
||||
@ -681,9 +689,9 @@ struct TypeChecker2
|
||||
void visit(AstExprConstantNumber* number)
|
||||
{
|
||||
TypeId actualType = lookupType(number);
|
||||
TypeId numberType = getSingletonTypes().numberType;
|
||||
TypeId numberType = singletonTypes->numberType;
|
||||
|
||||
if (!isSubtype(numberType, actualType, stack.back(), ice))
|
||||
if (!isSubtype(numberType, actualType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{actualType, numberType}, number->location);
|
||||
}
|
||||
@ -692,9 +700,9 @@ struct TypeChecker2
|
||||
void visit(AstExprConstantString* string)
|
||||
{
|
||||
TypeId actualType = lookupType(string);
|
||||
TypeId stringType = getSingletonTypes().stringType;
|
||||
TypeId stringType = singletonTypes->stringType;
|
||||
|
||||
if (!isSubtype(stringType, actualType, stack.back(), ice))
|
||||
if (!isSubtype(stringType, actualType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{actualType, stringType}, string->location);
|
||||
}
|
||||
@ -754,7 +762,7 @@ struct TypeChecker2
|
||||
FunctionTypeVar ftv{argsTp, expectedRetType};
|
||||
TypeId expectedType = arena.addType(ftv);
|
||||
|
||||
if (!isSubtype(expectedType, instantiatedFunctionType, stack.back(), ice))
|
||||
if (!isSubtype(expectedType, instantiatedFunctionType, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
CloneState cloneState;
|
||||
expectedType = clone(expectedType, module->internalTypes, cloneState);
|
||||
@ -773,7 +781,7 @@ struct TypeChecker2
|
||||
getIndexTypeFromType(module->getModuleScope(), leftType, indexName->index.value, indexName->location, /* addErrors */ true);
|
||||
if (ty)
|
||||
{
|
||||
if (!isSubtype(resultType, *ty, stack.back(), ice))
|
||||
if (!isSubtype(resultType, *ty, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{resultType, *ty}, indexName->location);
|
||||
}
|
||||
@ -806,7 +814,7 @@ struct TypeChecker2
|
||||
TypeId inferredArgTy = *argIt;
|
||||
TypeId annotatedArgTy = lookupAnnotation(arg->annotation);
|
||||
|
||||
if (!isSubtype(annotatedArgTy, inferredArgTy, stack.back(), ice))
|
||||
if (!isSubtype(annotatedArgTy, inferredArgTy, stack.back(), singletonTypes, ice))
|
||||
{
|
||||
reportError(TypeMismatch{annotatedArgTy, inferredArgTy}, arg->location);
|
||||
}
|
||||
@ -851,10 +859,10 @@ struct TypeChecker2
|
||||
TypeId computedType = lookupType(expr->expr);
|
||||
|
||||
// Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case.
|
||||
if (isSubtype(annotationType, computedType, stack.back(), ice))
|
||||
if (isSubtype(annotationType, computedType, stack.back(), singletonTypes, ice))
|
||||
return;
|
||||
|
||||
if (isSubtype(computedType, annotationType, stack.back(), ice))
|
||||
if (isSubtype(computedType, annotationType, stack.back(), singletonTypes, ice))
|
||||
return;
|
||||
|
||||
reportError(TypesAreUnrelated{computedType, annotationType}, expr->location);
|
||||
@ -908,7 +916,7 @@ struct TypeChecker2
|
||||
return result;
|
||||
}
|
||||
else if (get<Unifiable::Error>(pack))
|
||||
return singletonTypes.errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
else
|
||||
ice.ice("flattenPack got a weird pack!");
|
||||
}
|
||||
@ -1154,7 +1162,7 @@ struct TypeChecker2
|
||||
ErrorVec tryUnify(NotNull<Scope> scope, const Location& location, TID subTy, TID superTy)
|
||||
{
|
||||
UnifierSharedState sharedState{&ice};
|
||||
Unifier u{&module->internalTypes, Mode::Strict, scope, location, Covariant, sharedState};
|
||||
Unifier u{&module->internalTypes, singletonTypes, Mode::Strict, scope, location, Covariant, sharedState};
|
||||
u.anyIsTop = true;
|
||||
u.tryUnify(subTy, superTy);
|
||||
|
||||
@ -1164,6 +1172,9 @@ struct TypeChecker2
|
||||
void reportError(TypeErrorData data, const Location& location)
|
||||
{
|
||||
module->errors.emplace_back(location, sourceModule->name, std::move(data));
|
||||
|
||||
if (FFlag::DebugLuauLogSolverToJson)
|
||||
logger->captureTypeCheckError(module->errors.back());
|
||||
}
|
||||
|
||||
void reportError(TypeError e)
|
||||
@ -1179,13 +1190,13 @@ struct TypeChecker2
|
||||
|
||||
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, TypeId type, const std::string& prop, const Location& location, bool addErrors)
|
||||
{
|
||||
return Luau::getIndexTypeFromType(scope, module->errors, &module->internalTypes, type, prop, location, addErrors, ice);
|
||||
return Luau::getIndexTypeFromType(scope, module->errors, &module->internalTypes, singletonTypes, type, prop, location, addErrors, ice);
|
||||
}
|
||||
};
|
||||
|
||||
void check(const SourceModule& sourceModule, Module* module)
|
||||
void check(NotNull<SingletonTypes> singletonTypes, DcrLogger* logger, const SourceModule& sourceModule, Module* module)
|
||||
{
|
||||
TypeChecker2 typeChecker{&sourceModule, module};
|
||||
TypeChecker2 typeChecker{singletonTypes, logger, &sourceModule, module};
|
||||
|
||||
typeChecker.visit(sourceModule.root);
|
||||
}
|
||||
|
@ -248,21 +248,22 @@ size_t HashBoolNamePair::operator()(const std::pair<bool, Name>& pair) const
|
||||
return std::hash<bool>()(pair.first) ^ std::hash<Name>()(pair.second);
|
||||
}
|
||||
|
||||
TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHandler)
|
||||
TypeChecker::TypeChecker(ModuleResolver* resolver, NotNull<SingletonTypes> singletonTypes, InternalErrorReporter* iceHandler)
|
||||
: resolver(resolver)
|
||||
, singletonTypes(singletonTypes)
|
||||
, iceHandler(iceHandler)
|
||||
, unifierState(iceHandler)
|
||||
, nilType(getSingletonTypes().nilType)
|
||||
, numberType(getSingletonTypes().numberType)
|
||||
, stringType(getSingletonTypes().stringType)
|
||||
, booleanType(getSingletonTypes().booleanType)
|
||||
, threadType(getSingletonTypes().threadType)
|
||||
, anyType(getSingletonTypes().anyType)
|
||||
, unknownType(getSingletonTypes().unknownType)
|
||||
, neverType(getSingletonTypes().neverType)
|
||||
, anyTypePack(getSingletonTypes().anyTypePack)
|
||||
, neverTypePack(getSingletonTypes().neverTypePack)
|
||||
, uninhabitableTypePack(getSingletonTypes().uninhabitableTypePack)
|
||||
, nilType(singletonTypes->nilType)
|
||||
, numberType(singletonTypes->numberType)
|
||||
, stringType(singletonTypes->stringType)
|
||||
, booleanType(singletonTypes->booleanType)
|
||||
, threadType(singletonTypes->threadType)
|
||||
, anyType(singletonTypes->anyType)
|
||||
, unknownType(singletonTypes->unknownType)
|
||||
, neverType(singletonTypes->neverType)
|
||||
, anyTypePack(singletonTypes->anyTypePack)
|
||||
, neverTypePack(singletonTypes->neverTypePack)
|
||||
, uninhabitableTypePack(singletonTypes->uninhabitableTypePack)
|
||||
, duplicateTypeAliases{{false, {}}}
|
||||
{
|
||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
@ -357,7 +358,7 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
|
||||
|
||||
prepareErrorsForDisplay(currentModule->errors);
|
||||
|
||||
currentModule->clonePublicInterface(*iceHandler);
|
||||
currentModule->clonePublicInterface(singletonTypes, *iceHandler);
|
||||
|
||||
// Clear unifier cache since it's keyed off internal types that get deallocated
|
||||
// This avoids fake cross-module cache hits and keeps cache size at bay when typechecking large module graphs.
|
||||
@ -1606,7 +1607,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
auto [t, ok] = normalize(bindingType, currentModule, *iceHandler);
|
||||
auto [t, ok] = normalize(bindingType, currentModule, singletonTypes, *iceHandler);
|
||||
bindingType = t;
|
||||
if (!ok)
|
||||
reportError(typealias.location, NormalizationTooComplex{});
|
||||
@ -1923,7 +1924,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
||||
std::optional<TypeId> TypeChecker::findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location, bool addErrors)
|
||||
{
|
||||
ErrorVec errors;
|
||||
auto result = Luau::findTablePropertyRespectingMeta(errors, lhsType, name, location);
|
||||
auto result = Luau::findTablePropertyRespectingMeta(singletonTypes, errors, lhsType, name, location);
|
||||
if (addErrors)
|
||||
reportErrors(errors);
|
||||
return result;
|
||||
@ -1932,7 +1933,7 @@ std::optional<TypeId> TypeChecker::findTablePropertyRespectingMeta(TypeId lhsTyp
|
||||
std::optional<TypeId> TypeChecker::findMetatableEntry(TypeId type, std::string entry, const Location& location, bool addErrors)
|
||||
{
|
||||
ErrorVec errors;
|
||||
auto result = Luau::findMetatableEntry(errors, type, entry, location);
|
||||
auto result = Luau::findMetatableEntry(singletonTypes, errors, type, entry, location);
|
||||
if (addErrors)
|
||||
reportErrors(errors);
|
||||
return result;
|
||||
@ -2034,8 +2035,8 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
auto [t, ok] = normalize(addType(UnionTypeVar{std::move(goodOptions)}), currentModule,
|
||||
*iceHandler); // FIXME Inefficient. We craft a UnionTypeVar and immediately throw it away.
|
||||
// FIXME Inefficient. We craft a UnionTypeVar and immediately throw it away.
|
||||
auto [t, ok] = normalize(addType(UnionTypeVar{std::move(goodOptions)}), currentModule, singletonTypes, *iceHandler);
|
||||
|
||||
if (!ok)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
@ -2642,8 +2643,8 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||
|
||||
std::string metamethodName = opToMetaTableEntry(expr.op);
|
||||
|
||||
std::optional<TypeId> leftMetatable = isString(lhsType) ? std::nullopt : getMetatable(follow(lhsType));
|
||||
std::optional<TypeId> rightMetatable = isString(rhsType) ? std::nullopt : getMetatable(follow(rhsType));
|
||||
std::optional<TypeId> leftMetatable = isString(lhsType) ? std::nullopt : getMetatable(follow(lhsType), singletonTypes);
|
||||
std::optional<TypeId> rightMetatable = isString(rhsType) ? std::nullopt : getMetatable(follow(rhsType), singletonTypes);
|
||||
|
||||
if (leftMetatable != rightMetatable)
|
||||
{
|
||||
@ -2654,7 +2655,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||
{
|
||||
for (TypeId leftOption : utv)
|
||||
{
|
||||
if (getMetatable(follow(leftOption)) == rightMetatable)
|
||||
if (getMetatable(follow(leftOption), singletonTypes) == rightMetatable)
|
||||
{
|
||||
matches = true;
|
||||
break;
|
||||
@ -2668,7 +2669,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
||||
{
|
||||
for (TypeId rightOption : utv)
|
||||
{
|
||||
if (getMetatable(follow(rightOption)) == leftMetatable)
|
||||
if (getMetatable(follow(rightOption), singletonTypes) == leftMetatable)
|
||||
{
|
||||
matches = true;
|
||||
break;
|
||||
@ -4113,7 +4114,7 @@ std::optional<WithPredicate<TypePackId>> TypeChecker::checkCallOverload(const Sc
|
||||
std::vector<TypeId> adjustedArgTypes;
|
||||
auto it = begin(argPack);
|
||||
auto endIt = end(argPack);
|
||||
Widen widen{¤tModule->internalTypes};
|
||||
Widen widen{¤tModule->internalTypes, singletonTypes};
|
||||
for (; it != endIt; ++it)
|
||||
{
|
||||
adjustedArgTypes.push_back(addType(ConstrainedTypeVar{level, {widen(*it)}}));
|
||||
@ -4649,7 +4650,7 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
auto [t, ok] = Luau::normalize(ty, currentModule, *iceHandler);
|
||||
auto [t, ok] = Luau::normalize(ty, currentModule, singletonTypes, *iceHandler);
|
||||
if (!ok)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
return t;
|
||||
@ -4664,7 +4665,7 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation && ftv)
|
||||
{
|
||||
auto [t, ok] = Luau::normalize(ty, currentModule, *iceHandler);
|
||||
auto [t, ok] = Luau::normalize(ty, currentModule, singletonTypes, *iceHandler);
|
||||
if (!ok)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
return t;
|
||||
@ -4701,13 +4702,13 @@ TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
||||
{
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
auto [t, ok] = normalize(ty, currentModule, *iceHandler);
|
||||
auto [t, ok] = normalize(ty, currentModule, singletonTypes, *iceHandler);
|
||||
if (!ok)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
ty = t;
|
||||
}
|
||||
|
||||
Anyification anyification{¤tModule->internalTypes, scope, iceHandler, anyType, anyTypePack};
|
||||
Anyification anyification{¤tModule->internalTypes, scope, singletonTypes, iceHandler, anyType, anyTypePack};
|
||||
std::optional<TypeId> any = anyification.substitute(ty);
|
||||
if (anyification.normalizationTooComplex)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
@ -4724,13 +4725,13 @@ TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location lo
|
||||
{
|
||||
if (FFlag::LuauLowerBoundsCalculation)
|
||||
{
|
||||
auto [t, ok] = normalize(ty, currentModule, *iceHandler);
|
||||
auto [t, ok] = normalize(ty, currentModule, singletonTypes, *iceHandler);
|
||||
if (!ok)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
ty = t;
|
||||
}
|
||||
|
||||
Anyification anyification{¤tModule->internalTypes, scope, iceHandler, anyType, anyTypePack};
|
||||
Anyification anyification{¤tModule->internalTypes, scope, singletonTypes, iceHandler, anyType, anyTypePack};
|
||||
std::optional<TypePackId> any = anyification.substitute(ty);
|
||||
if (any.has_value())
|
||||
return *any;
|
||||
@ -4868,7 +4869,8 @@ void TypeChecker::merge(RefinementMap& l, const RefinementMap& r)
|
||||
|
||||
Unifier TypeChecker::mkUnifier(const ScopePtr& scope, const Location& location)
|
||||
{
|
||||
return Unifier{¤tModule->internalTypes, currentModule->mode, NotNull{scope.get()}, location, Variance::Covariant, unifierState};
|
||||
return Unifier{
|
||||
¤tModule->internalTypes, singletonTypes, currentModule->mode, NotNull{scope.get()}, location, Variance::Covariant, unifierState};
|
||||
}
|
||||
|
||||
TypeId TypeChecker::freshType(const ScopePtr& scope)
|
||||
@ -4883,7 +4885,7 @@ TypeId TypeChecker::freshType(TypeLevel level)
|
||||
|
||||
TypeId TypeChecker::singletonType(bool value)
|
||||
{
|
||||
return value ? getSingletonTypes().trueType : getSingletonTypes().falseType;
|
||||
return value ? singletonTypes->trueType : singletonTypes->falseType;
|
||||
}
|
||||
|
||||
TypeId TypeChecker::singletonType(std::string value)
|
||||
@ -4894,22 +4896,22 @@ TypeId TypeChecker::singletonType(std::string value)
|
||||
|
||||
TypeId TypeChecker::errorRecoveryType(const ScopePtr& scope)
|
||||
{
|
||||
return getSingletonTypes().errorRecoveryType();
|
||||
return singletonTypes->errorRecoveryType();
|
||||
}
|
||||
|
||||
TypeId TypeChecker::errorRecoveryType(TypeId guess)
|
||||
{
|
||||
return getSingletonTypes().errorRecoveryType(guess);
|
||||
return singletonTypes->errorRecoveryType(guess);
|
||||
}
|
||||
|
||||
TypePackId TypeChecker::errorRecoveryTypePack(const ScopePtr& scope)
|
||||
{
|
||||
return getSingletonTypes().errorRecoveryTypePack();
|
||||
return singletonTypes->errorRecoveryTypePack();
|
||||
}
|
||||
|
||||
TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess)
|
||||
{
|
||||
return getSingletonTypes().errorRecoveryTypePack(guess);
|
||||
return singletonTypes->errorRecoveryTypePack(guess);
|
||||
}
|
||||
|
||||
TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense)
|
||||
@ -5836,48 +5838,52 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
|
||||
return;
|
||||
}
|
||||
|
||||
using ConditionFunc = bool(TypeId);
|
||||
using SenseToTypeIdPredicate = std::function<TypeIdPredicate(bool)>;
|
||||
auto mkFilter = [](ConditionFunc f, std::optional<TypeId> other = std::nullopt) -> SenseToTypeIdPredicate {
|
||||
return [f, other](bool sense) -> TypeIdPredicate {
|
||||
return [f, other, sense](TypeId ty) -> std::optional<TypeId> {
|
||||
if (FFlag::LuauUnknownAndNeverType && sense && get<UnknownTypeVar>(ty))
|
||||
return other.value_or(ty);
|
||||
auto refine = [this, &lvalue = typeguardP.lvalue, &refis, &scope, sense](bool(f)(TypeId), std::optional<TypeId> mapsTo = std::nullopt) {
|
||||
TypeIdPredicate predicate = [f, mapsTo, sense](TypeId ty) -> std::optional<TypeId> {
|
||||
if (FFlag::LuauUnknownAndNeverType && sense && get<UnknownTypeVar>(ty))
|
||||
return mapsTo.value_or(ty);
|
||||
|
||||
if (f(ty) == sense)
|
||||
return ty;
|
||||
if (f(ty) == sense)
|
||||
return ty;
|
||||
|
||||
if (isUndecidable(ty))
|
||||
return other.value_or(ty);
|
||||
if (isUndecidable(ty))
|
||||
return mapsTo.value_or(ty);
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
refineLValue(lvalue, refis, scope, predicate);
|
||||
};
|
||||
|
||||
// Note: "vector" never happens here at this point, so we don't have to write something for it.
|
||||
// clang-format off
|
||||
static const std::unordered_map<std::string, SenseToTypeIdPredicate> primitives{
|
||||
// Trivial primitives.
|
||||
{"nil", mkFilter(isNil, nilType)}, // This can still happen when sense is false!
|
||||
{"string", mkFilter(isString, stringType)},
|
||||
{"number", mkFilter(isNumber, numberType)},
|
||||
{"boolean", mkFilter(isBoolean, booleanType)},
|
||||
{"thread", mkFilter(isThread, threadType)},
|
||||
|
||||
// Non-trivial primitives.
|
||||
{"table", mkFilter([](TypeId ty) -> bool { return isTableIntersection(ty) || get<TableTypeVar>(ty) || get<MetatableTypeVar>(ty); })},
|
||||
{"function", mkFilter([](TypeId ty) -> bool { return isOverloadedFunction(ty) || get<FunctionTypeVar>(ty); })},
|
||||
|
||||
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
||||
{"userdata", mkFilter([](TypeId ty) -> bool { return get<ClassTypeVar>(ty); })},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
if (auto it = primitives.find(typeguardP.kind); it != primitives.end())
|
||||
if (typeguardP.kind == "nil")
|
||||
return refine(isNil, nilType); // This can still happen when sense is false!
|
||||
else if (typeguardP.kind == "string")
|
||||
return refine(isString, stringType);
|
||||
else if (typeguardP.kind == "number")
|
||||
return refine(isNumber, numberType);
|
||||
else if (typeguardP.kind == "boolean")
|
||||
return refine(isBoolean, booleanType);
|
||||
else if (typeguardP.kind == "thread")
|
||||
return refine(isThread, threadType);
|
||||
else if (typeguardP.kind == "table")
|
||||
{
|
||||
refineLValue(typeguardP.lvalue, refis, scope, it->second(sense));
|
||||
return;
|
||||
return refine([](TypeId ty) -> bool {
|
||||
return isTableIntersection(ty) || get<TableTypeVar>(ty) || get<MetatableTypeVar>(ty);
|
||||
});
|
||||
}
|
||||
else if (typeguardP.kind == "function")
|
||||
{
|
||||
return refine([](TypeId ty) -> bool {
|
||||
return isOverloadedFunction(ty) || get<FunctionTypeVar>(ty);
|
||||
});
|
||||
}
|
||||
else if (typeguardP.kind == "userdata")
|
||||
{
|
||||
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
||||
return refine([](TypeId ty) -> bool {
|
||||
return get<ClassTypeVar>(ty);
|
||||
});
|
||||
}
|
||||
|
||||
if (!typeguardP.isTypeof)
|
||||
|
@ -9,18 +9,19 @@
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, const std::string& entry, Location location)
|
||||
std::optional<TypeId> findMetatableEntry(
|
||||
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId type, const std::string& entry, Location location)
|
||||
{
|
||||
type = follow(type);
|
||||
|
||||
std::optional<TypeId> metatable = getMetatable(type);
|
||||
std::optional<TypeId> metatable = getMetatable(type, singletonTypes);
|
||||
if (!metatable)
|
||||
return std::nullopt;
|
||||
|
||||
TypeId unwrapped = follow(*metatable);
|
||||
|
||||
if (get<AnyTypeVar>(unwrapped))
|
||||
return getSingletonTypes().anyType;
|
||||
return singletonTypes->anyType;
|
||||
|
||||
const TableTypeVar* mtt = getTableType(unwrapped);
|
||||
if (!mtt)
|
||||
@ -36,7 +37,8 @@ std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, const st
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId ty, const std::string& name, Location location)
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(
|
||||
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId ty, const std::string& name, Location location)
|
||||
{
|
||||
if (get<AnyTypeVar>(ty))
|
||||
return ty;
|
||||
@ -48,7 +50,7 @@ std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId t
|
||||
return it->second.type;
|
||||
}
|
||||
|
||||
std::optional<TypeId> mtIndex = findMetatableEntry(errors, ty, "__index", location);
|
||||
std::optional<TypeId> mtIndex = findMetatableEntry(singletonTypes, errors, ty, "__index", location);
|
||||
int count = 0;
|
||||
while (mtIndex)
|
||||
{
|
||||
@ -69,23 +71,23 @@ std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId t
|
||||
{
|
||||
std::optional<TypeId> r = first(follow(itf->retTypes));
|
||||
if (!r)
|
||||
return getSingletonTypes().nilType;
|
||||
return singletonTypes->nilType;
|
||||
else
|
||||
return *r;
|
||||
}
|
||||
else if (get<AnyTypeVar>(index))
|
||||
return getSingletonTypes().anyType;
|
||||
return singletonTypes->anyType;
|
||||
else
|
||||
errors.push_back(TypeError{location, GenericError{"__index should either be a function or table. Got " + toString(index)}});
|
||||
|
||||
mtIndex = findMetatableEntry(errors, *mtIndex, "__index", location);
|
||||
mtIndex = findMetatableEntry(singletonTypes, errors, *mtIndex, "__index", location);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, TypeId type, const std::string& prop,
|
||||
const Location& location, bool addErrors, InternalErrorReporter& handle)
|
||||
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
|
||||
TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle)
|
||||
{
|
||||
type = follow(type);
|
||||
|
||||
@ -97,14 +99,14 @@ std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& erro
|
||||
|
||||
if (isString(type))
|
||||
{
|
||||
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(errors, getSingletonTypes().stringType, "__index", location);
|
||||
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(singletonTypes, errors, singletonTypes->stringType, "__index", location);
|
||||
LUAU_ASSERT(mtIndex);
|
||||
type = *mtIndex;
|
||||
}
|
||||
|
||||
if (getTableType(type))
|
||||
{
|
||||
return findTablePropertyRespectingMeta(errors, type, prop, location);
|
||||
return findTablePropertyRespectingMeta(singletonTypes, errors, type, prop, location);
|
||||
}
|
||||
else if (const ClassTypeVar* cls = get<ClassTypeVar>(type))
|
||||
{
|
||||
@ -125,7 +127,8 @@ std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& erro
|
||||
if (get<AnyTypeVar>(follow(t)))
|
||||
return t;
|
||||
|
||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, errors, arena, t, prop, location, /* addErrors= */ false, handle))
|
||||
if (std::optional<TypeId> ty =
|
||||
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
|
||||
goodOptions.push_back(*ty);
|
||||
else
|
||||
badOptions.push_back(t);
|
||||
@ -144,17 +147,17 @@ std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& erro
|
||||
}
|
||||
|
||||
if (goodOptions.empty())
|
||||
return getSingletonTypes().neverType;
|
||||
return singletonTypes->neverType;
|
||||
|
||||
if (goodOptions.size() == 1)
|
||||
return goodOptions[0];
|
||||
|
||||
// TODO: inefficient.
|
||||
TypeId result = arena->addType(UnionTypeVar{std::move(goodOptions)});
|
||||
auto [ty, ok] = normalize(result, NotNull{scope.get()}, *arena, handle);
|
||||
auto [ty, ok] = normalize(result, NotNull{scope.get()}, *arena, singletonTypes, handle);
|
||||
if (!ok && addErrors)
|
||||
errors.push_back(TypeError{location, NormalizationTooComplex{}});
|
||||
return ok ? ty : getSingletonTypes().anyType;
|
||||
return ok ? ty : singletonTypes->anyType;
|
||||
}
|
||||
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(type))
|
||||
{
|
||||
@ -165,7 +168,8 @@ std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& erro
|
||||
// TODO: we should probably limit recursion here?
|
||||
// RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||
|
||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, errors, arena, t, prop, location, /* addErrors= */ false, handle))
|
||||
if (std::optional<TypeId> ty =
|
||||
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
|
||||
parts.push_back(*ty);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatArgumentErrorFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoMoreGlobalSingletonTypes, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -239,7 +240,7 @@ bool isOverloadedFunction(TypeId ty)
|
||||
return std::all_of(parts.begin(), parts.end(), isFunction);
|
||||
}
|
||||
|
||||
std::optional<TypeId> getMetatable(TypeId type)
|
||||
std::optional<TypeId> getMetatable(TypeId type, NotNull<SingletonTypes> singletonTypes)
|
||||
{
|
||||
type = follow(type);
|
||||
|
||||
@ -249,7 +250,7 @@ std::optional<TypeId> getMetatable(TypeId type)
|
||||
return classType->metatable;
|
||||
else if (isString(type))
|
||||
{
|
||||
auto ptv = get<PrimitiveTypeVar>(getSingletonTypes().stringType);
|
||||
auto ptv = get<PrimitiveTypeVar>(singletonTypes->stringType);
|
||||
LUAU_ASSERT(ptv && ptv->metatable);
|
||||
return ptv->metatable;
|
||||
}
|
||||
@ -707,44 +708,30 @@ TypeId makeFunction(TypeArena& arena, std::optional<TypeId> selfType, std::initi
|
||||
std::initializer_list<TypePackId> genericPacks, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
|
||||
std::initializer_list<TypeId> retTypes);
|
||||
|
||||
static TypeVar nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true};
|
||||
static TypeVar numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true};
|
||||
static TypeVar stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true};
|
||||
static TypeVar booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true};
|
||||
static TypeVar threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true};
|
||||
static TypeVar trueType_{SingletonTypeVar{BooleanSingleton{true}}, /*persistent*/ true};
|
||||
static TypeVar falseType_{SingletonTypeVar{BooleanSingleton{false}}, /*persistent*/ true};
|
||||
static TypeVar anyType_{AnyTypeVar{}, /*persistent*/ true};
|
||||
static TypeVar unknownType_{UnknownTypeVar{}, /*persistent*/ true};
|
||||
static TypeVar neverType_{NeverTypeVar{}, /*persistent*/ true};
|
||||
static TypeVar errorType_{ErrorTypeVar{}, /*persistent*/ true};
|
||||
|
||||
static TypePackVar anyTypePack_{VariadicTypePack{&anyType_}, /*persistent*/ true};
|
||||
static TypePackVar errorTypePack_{Unifiable::Error{}, /*persistent*/ true};
|
||||
static TypePackVar neverTypePack_{VariadicTypePack{&neverType_}, /*persistent*/ true};
|
||||
static TypePackVar uninhabitableTypePack_{TypePack{{&neverType_}, &neverTypePack_}, /*persistent*/ true};
|
||||
|
||||
SingletonTypes::SingletonTypes()
|
||||
: nilType(&nilType_)
|
||||
, numberType(&numberType_)
|
||||
, stringType(&stringType_)
|
||||
, booleanType(&booleanType_)
|
||||
, threadType(&threadType_)
|
||||
, trueType(&trueType_)
|
||||
, falseType(&falseType_)
|
||||
, anyType(&anyType_)
|
||||
, unknownType(&unknownType_)
|
||||
, neverType(&neverType_)
|
||||
, anyTypePack(&anyTypePack_)
|
||||
, neverTypePack(&neverTypePack_)
|
||||
, uninhabitableTypePack(&uninhabitableTypePack_)
|
||||
, arena(new TypeArena)
|
||||
: arena(new TypeArena)
|
||||
, debugFreezeArena(FFlag::DebugLuauFreezeArena)
|
||||
, nilType(arena->addType(TypeVar{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true}))
|
||||
, numberType(arena->addType(TypeVar{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true}))
|
||||
, stringType(arena->addType(TypeVar{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true}))
|
||||
, booleanType(arena->addType(TypeVar{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true}))
|
||||
, threadType(arena->addType(TypeVar{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true}))
|
||||
, trueType(arena->addType(TypeVar{SingletonTypeVar{BooleanSingleton{true}}, /*persistent*/ true}))
|
||||
, falseType(arena->addType(TypeVar{SingletonTypeVar{BooleanSingleton{false}}, /*persistent*/ true}))
|
||||
, anyType(arena->addType(TypeVar{AnyTypeVar{}, /*persistent*/ true}))
|
||||
, unknownType(arena->addType(TypeVar{UnknownTypeVar{}, /*persistent*/ true}))
|
||||
, neverType(arena->addType(TypeVar{NeverTypeVar{}, /*persistent*/ true}))
|
||||
, errorType(arena->addType(TypeVar{ErrorTypeVar{}, /*persistent*/ true}))
|
||||
, anyTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{anyType}, /*persistent*/ true}))
|
||||
, neverTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{neverType}, /*persistent*/ true}))
|
||||
, uninhabitableTypePack(arena->addTypePack({neverType}, neverTypePack))
|
||||
, errorTypePack(arena->addTypePack(TypePackVar{Unifiable::Error{}, /*persistent*/ true}))
|
||||
{
|
||||
TypeId stringMetatable = makeStringMetatable();
|
||||
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, stringMetatable};
|
||||
asMutable(stringType)->ty = PrimitiveTypeVar{PrimitiveTypeVar::String, stringMetatable};
|
||||
persist(stringMetatable);
|
||||
persist(uninhabitableTypePack);
|
||||
|
||||
debugFreezeArena = FFlag::DebugLuauFreezeArena;
|
||||
freeze(*arena);
|
||||
}
|
||||
|
||||
@ -834,12 +821,12 @@ TypeId SingletonTypes::makeStringMetatable()
|
||||
|
||||
TypeId SingletonTypes::errorRecoveryType()
|
||||
{
|
||||
return &errorType_;
|
||||
return errorType;
|
||||
}
|
||||
|
||||
TypePackId SingletonTypes::errorRecoveryTypePack()
|
||||
{
|
||||
return &errorTypePack_;
|
||||
return errorTypePack;
|
||||
}
|
||||
|
||||
TypeId SingletonTypes::errorRecoveryType(TypeId guess)
|
||||
@ -852,7 +839,7 @@ TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess)
|
||||
return guess;
|
||||
}
|
||||
|
||||
SingletonTypes& getSingletonTypes()
|
||||
SingletonTypes& DEPRECATED_getSingletonTypes()
|
||||
{
|
||||
static SingletonTypes singletonTypes;
|
||||
return singletonTypes;
|
||||
|
@ -257,12 +257,12 @@ TypeId Widen::clean(TypeId ty)
|
||||
LUAU_ASSERT(stv);
|
||||
|
||||
if (get<StringSingleton>(stv))
|
||||
return getSingletonTypes().stringType;
|
||||
return singletonTypes->stringType;
|
||||
else
|
||||
{
|
||||
// If this assert trips, it's likely we now have number singletons.
|
||||
LUAU_ASSERT(get<BooleanSingleton>(stv));
|
||||
return getSingletonTypes().booleanType;
|
||||
return singletonTypes->booleanType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,9 +317,10 @@ static std::optional<std::pair<Luau::Name, const SingletonTypeVar*>> getTableMat
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Unifier::Unifier(TypeArena* types, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance, UnifierSharedState& sharedState,
|
||||
TxnLog* parentLog)
|
||||
Unifier::Unifier(TypeArena* types, NotNull<SingletonTypes> singletonTypes, Mode mode, NotNull<Scope> scope, const Location& location,
|
||||
Variance variance, UnifierSharedState& sharedState, TxnLog* parentLog)
|
||||
: types(types)
|
||||
, singletonTypes(singletonTypes)
|
||||
, mode(mode)
|
||||
, scope(scope)
|
||||
, log(parentLog)
|
||||
@ -409,7 +410,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
||||
{
|
||||
promoteTypeLevels(log, types, superFree->level, subTy);
|
||||
|
||||
Widen widen{types};
|
||||
Widen widen{types, singletonTypes};
|
||||
log.replace(superTy, BoundTypeVar(widen(subTy)));
|
||||
}
|
||||
|
||||
@ -1018,7 +1019,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||
{
|
||||
if (!occursCheck(superTp, subTp))
|
||||
{
|
||||
Widen widen{types};
|
||||
Widen widen{types, singletonTypes};
|
||||
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
|
||||
}
|
||||
}
|
||||
@ -1162,13 +1163,13 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||
|
||||
while (superIter.good())
|
||||
{
|
||||
tryUnify_(*superIter, getSingletonTypes().errorRecoveryType());
|
||||
tryUnify_(*superIter, singletonTypes->errorRecoveryType());
|
||||
superIter.advance();
|
||||
}
|
||||
|
||||
while (subIter.good())
|
||||
{
|
||||
tryUnify_(*subIter, getSingletonTypes().errorRecoveryType());
|
||||
tryUnify_(*subIter, singletonTypes->errorRecoveryType());
|
||||
subIter.advance();
|
||||
}
|
||||
|
||||
@ -1613,7 +1614,7 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
|
||||
|
||||
// Given t1 where t1 = { lower: (t1) -> (a, b...) }
|
||||
// It should be the case that `string <: t1` iff `(subtype's metatable).__index <: t1`
|
||||
if (auto metatable = getMetatable(subTy))
|
||||
if (auto metatable = getMetatable(subTy, singletonTypes))
|
||||
{
|
||||
auto mttv = log.get<TableTypeVar>(*metatable);
|
||||
if (!mttv)
|
||||
@ -1658,10 +1659,10 @@ TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> see
|
||||
TableTypeVar* resultTtv = getMutable<TableTypeVar>(result);
|
||||
for (auto& [name, prop] : resultTtv->props)
|
||||
prop.type = deeplyOptional(prop.type, seen);
|
||||
return types->addType(UnionTypeVar{{getSingletonTypes().nilType, result}});
|
||||
return types->addType(UnionTypeVar{{singletonTypes->nilType, result}});
|
||||
}
|
||||
else
|
||||
return types->addType(UnionTypeVar{{getSingletonTypes().nilType, ty}});
|
||||
return types->addType(UnionTypeVar{{singletonTypes->nilType, ty}});
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
|
||||
@ -1951,7 +1952,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
||||
anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
|
||||
else
|
||||
{
|
||||
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{getSingletonTypes().anyType}});
|
||||
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{singletonTypes->anyType}});
|
||||
anyTp = get<AnyTypeVar>(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}});
|
||||
}
|
||||
|
||||
@ -1960,15 +1961,15 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
||||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types,
|
||||
FFlag::LuauUnknownAndNeverType ? anyTy : getSingletonTypes().anyType, anyTp);
|
||||
Luau::tryUnifyWithAny(
|
||||
queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, FFlag::LuauUnknownAndNeverType ? anyTy : singletonTypes->anyType, anyTp);
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
|
||||
{
|
||||
LUAU_ASSERT(get<Unifiable::Error>(anyTp));
|
||||
|
||||
const TypeId anyTy = getSingletonTypes().errorRecoveryType();
|
||||
const TypeId anyTy = singletonTypes->errorRecoveryType();
|
||||
|
||||
std::vector<TypeId> queue;
|
||||
|
||||
@ -1982,7 +1983,7 @@ void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
|
||||
|
||||
std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, Name name)
|
||||
{
|
||||
return Luau::findTablePropertyRespectingMeta(errors, lhsType, name, location);
|
||||
return Luau::findTablePropertyRespectingMeta(singletonTypes, errors, lhsType, name, location);
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithConstrainedSubTypeVar(TypeId subTy, TypeId superTy)
|
||||
@ -2193,7 +2194,7 @@ bool Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId hays
|
||||
if (needle == haystack)
|
||||
{
|
||||
reportError(TypeError{location, OccursCheckFailed{}});
|
||||
log.replace(needle, *getSingletonTypes().errorRecoveryType());
|
||||
log.replace(needle, *singletonTypes->errorRecoveryType());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2250,7 +2251,7 @@ bool Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
|
||||
if (needle == haystack)
|
||||
{
|
||||
reportError(TypeError{location, OccursCheckFailed{}});
|
||||
log.replace(needle, *getSingletonTypes().errorRecoveryTypePack());
|
||||
log.replace(needle, *singletonTypes->errorRecoveryTypePack());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2269,7 +2270,7 @@ bool Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
|
||||
|
||||
Unifier Unifier::makeChildUnifier()
|
||||
{
|
||||
Unifier u = Unifier{types, mode, scope, location, variance, sharedState, &log};
|
||||
Unifier u = Unifier{types, singletonTypes, mode, scope, location, variance, sharedState, &log};
|
||||
u.anyIsTop = anyIsTop;
|
||||
return u;
|
||||
}
|
||||
|
50
CodeGen/include/Luau/CodeAllocator.h
Normal file
50
CodeGen/include/Luau/CodeAllocator.h
Normal file
@ -0,0 +1,50 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
{
|
||||
|
||||
struct CodeAllocator
|
||||
{
|
||||
CodeAllocator(size_t blockSize, size_t maxTotalSize);
|
||||
~CodeAllocator();
|
||||
|
||||
// Places data and code into the executable page area
|
||||
// To allow allocation while previously allocated code is already running, allocation has page granularity
|
||||
// It's important to group functions together so that page alignment won't result in a lot of wasted space
|
||||
bool allocate(uint8_t* data, size_t dataSize, uint8_t* code, size_t codeSize, uint8_t*& result, size_t& resultSize, uint8_t*& resultCodeStart);
|
||||
|
||||
// Provided to callbacks
|
||||
void* context = nullptr;
|
||||
|
||||
// Called when new block is created to create and setup the unwinding information for all the code in the block
|
||||
// Some platforms require this data to be placed inside the block itself, so we also return 'unwindDataSizeInBlock'
|
||||
void* (*createBlockUnwindInfo)(void* context, uint8_t* block, size_t blockSize, size_t& unwindDataSizeInBlock) = nullptr;
|
||||
|
||||
// Called to destroy unwinding information returned by 'createBlockUnwindInfo'
|
||||
void (*destroyBlockUnwindInfo)(void* context, void* unwindData) = nullptr;
|
||||
|
||||
static const size_t kMaxUnwindDataSize = 128;
|
||||
|
||||
bool allocateNewBlock(size_t& unwindInfoSize);
|
||||
|
||||
// Current block we use for allocations
|
||||
uint8_t* blockPos = nullptr;
|
||||
uint8_t* blockEnd = nullptr;
|
||||
|
||||
// All allocated blocks
|
||||
std::vector<uint8_t*> blocks;
|
||||
std::vector<void*> unwindInfos;
|
||||
|
||||
size_t blockSize = 0;
|
||||
size_t maxTotalSize = 0;
|
||||
};
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
@ -1,3 +1,4 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Common.h"
|
||||
|
@ -1,3 +1,4 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Common.h"
|
||||
|
188
CodeGen/src/CodeAllocator.cpp
Normal file
188
CodeGen/src/CodeAllocator.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/CodeAllocator.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
|
||||
const size_t kPageSize = 4096;
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const size_t kPageSize = sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
|
||||
static size_t alignToPageSize(size_t size)
|
||||
{
|
||||
return (size + kPageSize - 1) & ~(kPageSize - 1);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static uint8_t* allocatePages(size_t size)
|
||||
{
|
||||
return (uint8_t*)VirtualAlloc(nullptr, alignToPageSize(size), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
static void freePages(uint8_t* mem, size_t size)
|
||||
{
|
||||
if (VirtualFree(mem, 0, MEM_RELEASE) == 0)
|
||||
LUAU_ASSERT(!"failed to deallocate block memory");
|
||||
}
|
||||
|
||||
static void makePagesExecutable(uint8_t* mem, size_t size)
|
||||
{
|
||||
LUAU_ASSERT((uintptr_t(mem) & (kPageSize - 1)) == 0);
|
||||
LUAU_ASSERT(size == alignToPageSize(size));
|
||||
|
||||
DWORD oldProtect;
|
||||
if (VirtualProtect(mem, size, PAGE_EXECUTE_READ, &oldProtect) == 0)
|
||||
LUAU_ASSERT(!"failed to change page protection");
|
||||
}
|
||||
|
||||
static void flushInstructionCache(uint8_t* mem, size_t size)
|
||||
{
|
||||
if (FlushInstructionCache(GetCurrentProcess(), mem, size) == 0)
|
||||
LUAU_ASSERT(!"failed to flush instruction cache");
|
||||
}
|
||||
#else
|
||||
static uint8_t* allocatePages(size_t size)
|
||||
{
|
||||
return (uint8_t*)mmap(nullptr, alignToPageSize(size), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
}
|
||||
|
||||
static void freePages(uint8_t* mem, size_t size)
|
||||
{
|
||||
if (munmap(mem, alignToPageSize(size)) != 0)
|
||||
LUAU_ASSERT(!"failed to deallocate block memory");
|
||||
}
|
||||
|
||||
static void makePagesExecutable(uint8_t* mem, size_t size)
|
||||
{
|
||||
LUAU_ASSERT((uintptr_t(mem) & (kPageSize - 1)) == 0);
|
||||
LUAU_ASSERT(size == alignToPageSize(size));
|
||||
|
||||
if (mprotect(mem, size, PROT_READ | PROT_EXEC) != 0)
|
||||
LUAU_ASSERT(!"failed to change page protection");
|
||||
}
|
||||
|
||||
static void flushInstructionCache(uint8_t* mem, size_t size)
|
||||
{
|
||||
__builtin___clear_cache((char*)mem, (char*)mem + size);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
{
|
||||
|
||||
CodeAllocator::CodeAllocator(size_t blockSize, size_t maxTotalSize)
|
||||
: blockSize(blockSize)
|
||||
, maxTotalSize(maxTotalSize)
|
||||
{
|
||||
LUAU_ASSERT(blockSize > kMaxUnwindDataSize);
|
||||
LUAU_ASSERT(maxTotalSize >= blockSize);
|
||||
}
|
||||
|
||||
CodeAllocator::~CodeAllocator()
|
||||
{
|
||||
if (destroyBlockUnwindInfo)
|
||||
{
|
||||
for (void* unwindInfo : unwindInfos)
|
||||
destroyBlockUnwindInfo(context, unwindInfo);
|
||||
}
|
||||
|
||||
for (uint8_t* block : blocks)
|
||||
freePages(block, blockSize);
|
||||
}
|
||||
|
||||
bool CodeAllocator::allocate(
|
||||
uint8_t* data, size_t dataSize, uint8_t* code, size_t codeSize, uint8_t*& result, size_t& resultSize, uint8_t*& resultCodeStart)
|
||||
{
|
||||
// 'Round up' to preserve 16 byte alignment
|
||||
size_t alignedDataSize = (dataSize + 15) & ~15;
|
||||
|
||||
size_t totalSize = alignedDataSize + codeSize;
|
||||
|
||||
// Function has to fit into a single block with unwinding information
|
||||
if (totalSize > blockSize - kMaxUnwindDataSize)
|
||||
return false;
|
||||
|
||||
size_t unwindInfoSize = 0;
|
||||
|
||||
// We might need a new block
|
||||
if (totalSize > size_t(blockEnd - blockPos))
|
||||
{
|
||||
if (!allocateNewBlock(unwindInfoSize))
|
||||
return false;
|
||||
|
||||
LUAU_ASSERT(totalSize <= size_t(blockEnd - blockPos));
|
||||
}
|
||||
|
||||
LUAU_ASSERT((uintptr_t(blockPos) & (kPageSize - 1)) == 0); // Allocation starts on page boundary
|
||||
|
||||
size_t dataOffset = unwindInfoSize + alignedDataSize - dataSize;
|
||||
size_t codeOffset = unwindInfoSize + alignedDataSize;
|
||||
|
||||
if (dataSize)
|
||||
memcpy(blockPos + dataOffset, data, dataSize);
|
||||
if (codeSize)
|
||||
memcpy(blockPos + codeOffset, code, codeSize);
|
||||
|
||||
size_t pageSize = alignToPageSize(unwindInfoSize + totalSize);
|
||||
|
||||
makePagesExecutable(blockPos, pageSize);
|
||||
flushInstructionCache(blockPos + codeOffset, codeSize);
|
||||
|
||||
result = blockPos + unwindInfoSize;
|
||||
resultSize = totalSize;
|
||||
resultCodeStart = blockPos + codeOffset;
|
||||
|
||||
blockPos += pageSize;
|
||||
LUAU_ASSERT((uintptr_t(blockPos) & (kPageSize - 1)) == 0); // Allocation ends on page boundary
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CodeAllocator::allocateNewBlock(size_t& unwindInfoSize)
|
||||
{
|
||||
// Stop allocating once we reach a global limit
|
||||
if ((blocks.size() + 1) * blockSize > maxTotalSize)
|
||||
return false;
|
||||
|
||||
uint8_t* block = allocatePages(blockSize);
|
||||
|
||||
if (!block)
|
||||
return false;
|
||||
|
||||
blockPos = block;
|
||||
blockEnd = block + blockSize;
|
||||
|
||||
blocks.push_back(block);
|
||||
|
||||
if (createBlockUnwindInfo)
|
||||
{
|
||||
void* unwindInfo = createBlockUnwindInfo(context, block, blockSize, unwindInfoSize);
|
||||
|
||||
// 'Round up' to preserve 16 byte alignment of the following data and code
|
||||
unwindInfoSize = (unwindInfoSize + 15) & ~15;
|
||||
|
||||
LUAU_ASSERT(unwindInfoSize <= kMaxUnwindDataSize);
|
||||
|
||||
if (!unwindInfo)
|
||||
return false;
|
||||
|
||||
unwindInfos.push_back(unwindInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace CodeGen
|
||||
} // namespace Luau
|
@ -289,17 +289,19 @@ enum LuauOpcode
|
||||
// the first variable is then copied into index; generator/state are immutable, index isn't visible to user code
|
||||
LOP_FORGLOOP,
|
||||
|
||||
// FORGPREP_INEXT/FORGLOOP_INEXT: FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_inext
|
||||
// FORGPREP_INEXT prepares the index variable and jumps to FORGLOOP_INEXT
|
||||
// FORGLOOP_INEXT has identical encoding and semantics to FORGLOOP (except for AUX encoding)
|
||||
// FORGPREP_INEXT: prepare FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_inext, and jump to FORGLOOP
|
||||
// A: target register (see FORGLOOP for register layout)
|
||||
LOP_FORGPREP_INEXT,
|
||||
LOP_FORGLOOP_INEXT,
|
||||
|
||||
// FORGPREP_NEXT/FORGLOOP_NEXT: FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_next
|
||||
// FORGPREP_NEXT prepares the index variable and jumps to FORGLOOP_NEXT
|
||||
// FORGLOOP_NEXT has identical encoding and semantics to FORGLOOP (except for AUX encoding)
|
||||
// removed in v3
|
||||
LOP_DEP_FORGLOOP_INEXT,
|
||||
|
||||
// FORGPREP_NEXT: prepare FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_next, and jump to FORGLOOP
|
||||
// A: target register (see FORGLOOP for register layout)
|
||||
LOP_FORGPREP_NEXT,
|
||||
LOP_FORGLOOP_NEXT,
|
||||
|
||||
// removed in v3
|
||||
LOP_DEP_FORGLOOP_NEXT,
|
||||
|
||||
// GETVARARGS: copy variables into the target register from vararg storage for current function
|
||||
// A: target register
|
||||
@ -343,12 +345,9 @@ enum LuauOpcode
|
||||
// B: source register (for VAL/REF) or upvalue index (for UPVAL/UPREF)
|
||||
LOP_CAPTURE,
|
||||
|
||||
// JUMPIFEQK, JUMPIFNOTEQK: jumps to target offset if the comparison with constant is true (or false, for NOT variants)
|
||||
// A: source register 1
|
||||
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
|
||||
// AUX: constant table index
|
||||
LOP_JUMPIFEQK,
|
||||
LOP_JUMPIFNOTEQK,
|
||||
// removed in v3
|
||||
LOP_DEP_JUMPIFEQK,
|
||||
LOP_DEP_JUMPIFNOTEQK,
|
||||
|
||||
// FASTCALL1: perform a fast call of a built-in function using 1 register argument
|
||||
// A: builtin function id (see LuauBuiltinFunction)
|
||||
|
@ -73,8 +73,6 @@ static int getOpLength(LuauOpcode op)
|
||||
case LOP_SETLIST:
|
||||
case LOP_FORGLOOP:
|
||||
case LOP_LOADKX:
|
||||
case LOP_JUMPIFEQK:
|
||||
case LOP_JUMPIFNOTEQK:
|
||||
case LOP_FASTCALL2:
|
||||
case LOP_FASTCALL2K:
|
||||
case LOP_JUMPXEQKNIL:
|
||||
@ -106,12 +104,8 @@ inline bool isJumpD(LuauOpcode op)
|
||||
case LOP_FORGPREP:
|
||||
case LOP_FORGLOOP:
|
||||
case LOP_FORGPREP_INEXT:
|
||||
case LOP_FORGLOOP_INEXT:
|
||||
case LOP_FORGPREP_NEXT:
|
||||
case LOP_FORGLOOP_NEXT:
|
||||
case LOP_JUMPBACK:
|
||||
case LOP_JUMPIFEQK:
|
||||
case LOP_JUMPIFNOTEQK:
|
||||
case LOP_JUMPXEQKNIL:
|
||||
case LOP_JUMPXEQKB:
|
||||
case LOP_JUMPXEQKN:
|
||||
@ -1247,13 +1241,6 @@ void BytecodeBuilder::validate() const
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
break;
|
||||
|
||||
case LOP_JUMPIFEQK:
|
||||
case LOP_JUMPIFNOTEQK:
|
||||
VREG(LUAU_INSN_A(insn));
|
||||
VCONSTANY(insns[i + 1]);
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
break;
|
||||
|
||||
case LOP_JUMPXEQKNIL:
|
||||
case LOP_JUMPXEQKB:
|
||||
VREG(LUAU_INSN_A(insn));
|
||||
@ -1360,9 +1347,7 @@ void BytecodeBuilder::validate() const
|
||||
break;
|
||||
|
||||
case LOP_FORGPREP_INEXT:
|
||||
case LOP_FORGLOOP_INEXT:
|
||||
case LOP_FORGPREP_NEXT:
|
||||
case LOP_FORGLOOP_NEXT:
|
||||
VREG(LUAU_INSN_A(insn) + 4); // forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, A+4 are loop variables
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
break;
|
||||
@ -1728,18 +1713,10 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result,
|
||||
formatAppend(result, "FORGPREP_INEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_FORGLOOP_INEXT:
|
||||
formatAppend(result, "FORGLOOP_INEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_FORGPREP_NEXT:
|
||||
formatAppend(result, "FORGPREP_NEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_FORGLOOP_NEXT:
|
||||
formatAppend(result, "FORGLOOP_NEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_GETVARARGS:
|
||||
formatAppend(result, "GETVARARGS R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn) - 1);
|
||||
break;
|
||||
@ -1797,14 +1774,6 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result,
|
||||
LUAU_INSN_A(insn) == LCT_UPVAL ? 'U' : 'R', LUAU_INSN_B(insn));
|
||||
break;
|
||||
|
||||
case LOP_JUMPIFEQK:
|
||||
formatAppend(result, "JUMPIFEQK R%d K%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_JUMPIFNOTEQK:
|
||||
formatAppend(result, "JUMPIFNOTEQK R%d K%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
|
||||
break;
|
||||
|
||||
case LOP_JUMPXEQKNIL:
|
||||
formatAppend(result, "JUMPXEQKNIL R%d L%d%s\n", LUAU_INSN_A(insn), targetLabel, *code >> 31 ? " NOT" : "");
|
||||
code++;
|
||||
|
@ -3457,14 +3457,6 @@ struct Compiler
|
||||
return uint8_t(top);
|
||||
}
|
||||
|
||||
void reserveReg(AstNode* node, unsigned int count)
|
||||
{
|
||||
if (regTop + count > kMaxRegisterCount)
|
||||
CompileError::raise(node->location, "Out of registers when trying to allocate %d registers: exceeded limit %d", count, kMaxRegisterCount);
|
||||
|
||||
stackSize = std::max(stackSize, regTop + count);
|
||||
}
|
||||
|
||||
void setDebugLine(AstNode* node)
|
||||
{
|
||||
if (options.debugLevel >= 1)
|
||||
|
6
Makefile
6
Makefile
@ -142,12 +142,16 @@ coverage: $(TESTS_TARGET)
|
||||
llvm-cov export -ignore-filename-regex=\(tests\|extern\|CLI\)/.* -format lcov --instr-profile default.profdata build/coverage/luau-tests >coverage.info
|
||||
|
||||
format:
|
||||
find . -name '*.h' -or -name '*.cpp' | xargs clang-format-11 -i
|
||||
git ls-files '*.h' '*.cpp' | xargs clang-format-11 -i
|
||||
|
||||
luau-size: luau
|
||||
nm --print-size --demangle luau | grep ' t void luau_execute<false>' | awk -F ' ' '{sum += strtonum("0x" $$2)} END {print sum " interpreter" }'
|
||||
nm --print-size --demangle luau | grep ' t luauF_' | awk -F ' ' '{sum += strtonum("0x" $$2)} END {print sum " builtins" }'
|
||||
|
||||
check-source:
|
||||
git ls-files '*.h' '*.cpp' | xargs -I+ sh -c 'grep -L LICENSE +'
|
||||
git ls-files '*.h' ':!:extern' | xargs -I+ sh -c 'grep -L "#pragma once" +'
|
||||
|
||||
# executable target aliases
|
||||
luau: $(REPL_CLI_TARGET)
|
||||
ln -fs $^ $@
|
||||
|
@ -56,12 +56,14 @@ target_sources(Luau.Compiler PRIVATE
|
||||
# Luau.CodeGen Sources
|
||||
target_sources(Luau.CodeGen PRIVATE
|
||||
CodeGen/include/Luau/AssemblyBuilderX64.h
|
||||
CodeGen/include/Luau/CodeAllocator.h
|
||||
CodeGen/include/Luau/Condition.h
|
||||
CodeGen/include/Luau/Label.h
|
||||
CodeGen/include/Luau/OperandX64.h
|
||||
CodeGen/include/Luau/RegisterX64.h
|
||||
|
||||
CodeGen/src/AssemblyBuilderX64.cpp
|
||||
CodeGen/src/CodeAllocator.cpp
|
||||
)
|
||||
|
||||
# Luau.Analysis Sources
|
||||
@ -77,7 +79,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||
Analysis/include/Luau/Constraint.h
|
||||
Analysis/include/Luau/ConstraintGraphBuilder.h
|
||||
Analysis/include/Luau/ConstraintSolver.h
|
||||
Analysis/include/Luau/ConstraintSolverLogger.h
|
||||
Analysis/include/Luau/DcrLogger.h
|
||||
Analysis/include/Luau/Documentation.h
|
||||
Analysis/include/Luau/Error.h
|
||||
Analysis/include/Luau/FileResolver.h
|
||||
@ -127,7 +129,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||
Analysis/src/Constraint.cpp
|
||||
Analysis/src/ConstraintGraphBuilder.cpp
|
||||
Analysis/src/ConstraintSolver.cpp
|
||||
Analysis/src/ConstraintSolverLogger.cpp
|
||||
Analysis/src/DcrLogger.cpp
|
||||
Analysis/src/EmbeddedBuiltinDefinitions.cpp
|
||||
Analysis/src/Error.cpp
|
||||
Analysis/src/Frontend.cpp
|
||||
@ -266,6 +268,7 @@ if(TARGET Luau.UnitTest)
|
||||
tests/AstVisitor.test.cpp
|
||||
tests/Autocomplete.test.cpp
|
||||
tests/BuiltinDefinitions.test.cpp
|
||||
tests/CodeAllocator.test.cpp
|
||||
tests/Compiler.test.cpp
|
||||
tests/Config.test.cpp
|
||||
tests/ConstraintGraphBuilder.test.cpp
|
||||
|
@ -35,6 +35,15 @@ enum lua_Status
|
||||
LUA_BREAK, // yielded for a debug breakpoint
|
||||
};
|
||||
|
||||
enum lua_CoStatus
|
||||
{
|
||||
LUA_CORUN = 0, // running
|
||||
LUA_COSUS, // suspended
|
||||
LUA_CONOR, // 'normal' (it resumed another coroutine)
|
||||
LUA_COFIN, // finished
|
||||
LUA_COERR, // finished with error
|
||||
};
|
||||
|
||||
typedef struct lua_State lua_State;
|
||||
|
||||
typedef int (*lua_CFunction)(lua_State* L);
|
||||
@ -224,6 +233,7 @@ LUA_API int lua_status(lua_State* L);
|
||||
LUA_API int lua_isyieldable(lua_State* L);
|
||||
LUA_API void* lua_getthreaddata(lua_State* L);
|
||||
LUA_API void lua_setthreaddata(lua_State* L, void* data);
|
||||
LUA_API int lua_costatus(lua_State* L, lua_State* co);
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
|
@ -1008,6 +1008,23 @@ int lua_status(lua_State* L)
|
||||
return L->status;
|
||||
}
|
||||
|
||||
int lua_costatus(lua_State* L, lua_State* co)
|
||||
{
|
||||
if (co == L)
|
||||
return LUA_CORUN;
|
||||
if (co->status == LUA_YIELD)
|
||||
return LUA_COSUS;
|
||||
if (co->status == LUA_BREAK)
|
||||
return LUA_CONOR;
|
||||
if (co->status != 0) // some error occurred
|
||||
return LUA_COERR;
|
||||
if (co->ci != co->base_ci) // does it have frames?
|
||||
return LUA_CONOR;
|
||||
if (co->top == co->base)
|
||||
return LUA_COFIN;
|
||||
return LUA_COSUS; // initial state
|
||||
}
|
||||
|
||||
void* lua_getthreaddata(lua_State* L)
|
||||
{
|
||||
return L->userdata;
|
||||
|
@ -5,38 +5,16 @@
|
||||
#include "lstate.h"
|
||||
#include "lvm.h"
|
||||
|
||||
#define CO_RUN 0 // running
|
||||
#define CO_SUS 1 // suspended
|
||||
#define CO_NOR 2 // 'normal' (it resumed another coroutine)
|
||||
#define CO_DEAD 3
|
||||
|
||||
#define CO_STATUS_ERROR -1
|
||||
#define CO_STATUS_BREAK -2
|
||||
|
||||
static const char* const statnames[] = {"running", "suspended", "normal", "dead"};
|
||||
|
||||
static int auxstatus(lua_State* L, lua_State* co)
|
||||
{
|
||||
if (co == L)
|
||||
return CO_RUN;
|
||||
if (co->status == LUA_YIELD)
|
||||
return CO_SUS;
|
||||
if (co->status == LUA_BREAK)
|
||||
return CO_NOR;
|
||||
if (co->status != 0) // some error occurred
|
||||
return CO_DEAD;
|
||||
if (co->ci != co->base_ci) // does it have frames?
|
||||
return CO_NOR;
|
||||
if (co->top == co->base)
|
||||
return CO_DEAD;
|
||||
return CO_SUS; // initial state
|
||||
}
|
||||
static const char* const statnames[] = {"running", "suspended", "normal", "dead", "dead"}; // dead appears twice for LUA_COERR and LUA_COFIN
|
||||
|
||||
static int costatus(lua_State* L)
|
||||
{
|
||||
lua_State* co = lua_tothread(L, 1);
|
||||
luaL_argexpected(L, co, 1, "thread");
|
||||
lua_pushstring(L, statnames[auxstatus(L, co)]);
|
||||
lua_pushstring(L, statnames[lua_costatus(L, co)]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -45,8 +23,8 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
|
||||
// error handling for edge cases
|
||||
if (co->status != LUA_YIELD)
|
||||
{
|
||||
int status = auxstatus(L, co);
|
||||
if (status != CO_SUS)
|
||||
int status = lua_costatus(L, co);
|
||||
if (status != LUA_COSUS)
|
||||
{
|
||||
lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
|
||||
return CO_STATUS_ERROR;
|
||||
@ -236,8 +214,8 @@ static int coclose(lua_State* L)
|
||||
lua_State* co = lua_tothread(L, 1);
|
||||
luaL_argexpected(L, co, 1, "thread");
|
||||
|
||||
int status = auxstatus(L, co);
|
||||
if (status != CO_DEAD && status != CO_SUS)
|
||||
int status = lua_costatus(L, co);
|
||||
if (status != LUA_COFIN && status != LUA_COERR && status != LUA_COSUS)
|
||||
luaL_error(L, "cannot close %s coroutine", statnames[status]);
|
||||
|
||||
if (co->status == LUA_OK || co->status == LUA_YIELD)
|
||||
|
@ -123,6 +123,7 @@
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplerUpval, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoSleepBit, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEagerShrink, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFasterSweep, false)
|
||||
|
||||
#define GC_SWEEPPAGESTEPCOST 16
|
||||
|
||||
@ -848,6 +849,7 @@ static size_t atomic(lua_State* L)
|
||||
|
||||
static bool sweepgco(lua_State* L, lua_Page* page, GCObject* gco)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauFasterSweep);
|
||||
global_State* g = L->global;
|
||||
|
||||
int deadmask = otherwhite(g);
|
||||
@ -890,22 +892,62 @@ static int sweepgcopage(lua_State* L, lua_Page* page)
|
||||
int blockSize;
|
||||
luaM_getpagewalkinfo(page, &start, &end, &busyBlocks, &blockSize);
|
||||
|
||||
for (char* pos = start; pos != end; pos += blockSize)
|
||||
LUAU_ASSERT(busyBlocks > 0);
|
||||
|
||||
if (FFlag::LuauFasterSweep)
|
||||
{
|
||||
GCObject* gco = (GCObject*)pos;
|
||||
LUAU_ASSERT(FFlag::LuauNoSleepBit && FFlag::LuauEagerShrink);
|
||||
|
||||
// skip memory blocks that are already freed
|
||||
if (gco->gch.tt == LUA_TNIL)
|
||||
continue;
|
||||
global_State* g = L->global;
|
||||
|
||||
// when true is returned it means that the element was deleted
|
||||
if (sweepgco(L, page, gco))
|
||||
int deadmask = otherwhite(g);
|
||||
LUAU_ASSERT(testbit(deadmask, FIXEDBIT)); // make sure we never sweep fixed objects
|
||||
|
||||
int newwhite = luaC_white(g);
|
||||
|
||||
for (char* pos = start; pos != end; pos += blockSize)
|
||||
{
|
||||
LUAU_ASSERT(busyBlocks > 0);
|
||||
GCObject* gco = (GCObject*)pos;
|
||||
|
||||
// if the last block was removed, page would be removed as well
|
||||
if (--busyBlocks == 0)
|
||||
return int(pos - start) / blockSize + 1;
|
||||
// skip memory blocks that are already freed
|
||||
if (gco->gch.tt == LUA_TNIL)
|
||||
continue;
|
||||
|
||||
// is the object alive?
|
||||
if ((gco->gch.marked ^ WHITEBITS) & deadmask)
|
||||
{
|
||||
LUAU_ASSERT(!isdead(g, gco));
|
||||
// make it white (for next cycle)
|
||||
gco->gch.marked = cast_byte((gco->gch.marked & maskmarks) | newwhite);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(isdead(g, gco));
|
||||
freeobj(L, gco, page);
|
||||
|
||||
// if the last block was removed, page would be removed as well
|
||||
if (--busyBlocks == 0)
|
||||
return int(pos - start) / blockSize + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (char* pos = start; pos != end; pos += blockSize)
|
||||
{
|
||||
GCObject* gco = (GCObject*)pos;
|
||||
|
||||
// skip memory blocks that are already freed
|
||||
if (gco->gch.tt == LUA_TNIL)
|
||||
continue;
|
||||
|
||||
// when true is returned it means that the element was deleted
|
||||
if (sweepgco(L, page, gco))
|
||||
{
|
||||
// if the last block was removed, page would be removed as well
|
||||
if (--busyBlocks == 0)
|
||||
return int(pos - start) / blockSize + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,10 +1035,19 @@ static size_t gcstep(lua_State* L, size_t limit)
|
||||
// nothing more to sweep?
|
||||
if (g->sweepgcopage == NULL)
|
||||
{
|
||||
// don't forget to visit main thread
|
||||
sweepgco(L, NULL, obj2gco(g->mainthread));
|
||||
// don't forget to visit main thread, it's the only object not allocated in GCO pages
|
||||
if (FFlag::LuauFasterSweep)
|
||||
{
|
||||
LUAU_ASSERT(!isdead(g, obj2gco(g->mainthread)));
|
||||
makewhite(g, obj2gco(g->mainthread)); // make it white (for next cycle)
|
||||
}
|
||||
else
|
||||
{
|
||||
sweepgco(L, NULL, obj2gco(g->mainthread));
|
||||
}
|
||||
|
||||
shrinkbuffers(L);
|
||||
|
||||
g->gcstate = GCSpause; // end collection
|
||||
}
|
||||
break;
|
||||
|
@ -107,10 +107,10 @@ LUAU_FASTFLAG(LuauNoSleepBit)
|
||||
VM_DISPATCH_OP(LOP_POWK), VM_DISPATCH_OP(LOP_AND), VM_DISPATCH_OP(LOP_OR), VM_DISPATCH_OP(LOP_ANDK), VM_DISPATCH_OP(LOP_ORK), \
|
||||
VM_DISPATCH_OP(LOP_CONCAT), VM_DISPATCH_OP(LOP_NOT), VM_DISPATCH_OP(LOP_MINUS), VM_DISPATCH_OP(LOP_LENGTH), VM_DISPATCH_OP(LOP_NEWTABLE), \
|
||||
VM_DISPATCH_OP(LOP_DUPTABLE), VM_DISPATCH_OP(LOP_SETLIST), VM_DISPATCH_OP(LOP_FORNPREP), VM_DISPATCH_OP(LOP_FORNLOOP), \
|
||||
VM_DISPATCH_OP(LOP_FORGLOOP), VM_DISPATCH_OP(LOP_FORGPREP_INEXT), VM_DISPATCH_OP(LOP_FORGLOOP_INEXT), VM_DISPATCH_OP(LOP_FORGPREP_NEXT), \
|
||||
VM_DISPATCH_OP(LOP_FORGLOOP_NEXT), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \
|
||||
VM_DISPATCH_OP(LOP_FORGLOOP), VM_DISPATCH_OP(LOP_FORGPREP_INEXT), VM_DISPATCH_OP(LOP_DEP_FORGLOOP_INEXT), VM_DISPATCH_OP(LOP_FORGPREP_NEXT), \
|
||||
VM_DISPATCH_OP(LOP_DEP_FORGLOOP_NEXT), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \
|
||||
VM_DISPATCH_OP(LOP_LOADKX), VM_DISPATCH_OP(LOP_JUMPX), VM_DISPATCH_OP(LOP_FASTCALL), VM_DISPATCH_OP(LOP_COVERAGE), \
|
||||
VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_JUMPIFEQK), VM_DISPATCH_OP(LOP_JUMPIFNOTEQK), VM_DISPATCH_OP(LOP_FASTCALL1), \
|
||||
VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_DEP_JUMPIFEQK), VM_DISPATCH_OP(LOP_DEP_JUMPIFNOTEQK), VM_DISPATCH_OP(LOP_FASTCALL1), \
|
||||
VM_DISPATCH_OP(LOP_FASTCALL2), VM_DISPATCH_OP(LOP_FASTCALL2K), VM_DISPATCH_OP(LOP_FORGPREP), VM_DISPATCH_OP(LOP_JUMPXEQKNIL), \
|
||||
VM_DISPATCH_OP(LOP_JUMPXEQKB), VM_DISPATCH_OP(LOP_JUMPXEQKN), VM_DISPATCH_OP(LOP_JUMPXEQKS),
|
||||
|
||||
@ -2401,7 +2401,7 @@ static void luau_execute(lua_State* L)
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_FORGLOOP_INEXT)
|
||||
VM_CASE(LOP_DEP_FORGLOOP_INEXT)
|
||||
{
|
||||
VM_INTERRUPT();
|
||||
Instruction insn = *pc++;
|
||||
@ -2473,7 +2473,7 @@ static void luau_execute(lua_State* L)
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_FORGLOOP_NEXT)
|
||||
VM_CASE(LOP_DEP_FORGLOOP_NEXT)
|
||||
{
|
||||
VM_INTERRUPT();
|
||||
Instruction insn = *pc++;
|
||||
@ -2748,7 +2748,7 @@ static void luau_execute(lua_State* L)
|
||||
LUAU_UNREACHABLE();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_JUMPIFEQK)
|
||||
VM_CASE(LOP_DEP_JUMPIFEQK)
|
||||
{
|
||||
Instruction insn = *pc++;
|
||||
uint32_t aux = *pc;
|
||||
@ -2793,7 +2793,7 @@ static void luau_execute(lua_State* L)
|
||||
}
|
||||
}
|
||||
|
||||
VM_CASE(LOP_JUMPIFNOTEQK)
|
||||
VM_CASE(LOP_DEP_JUMPIFNOTEQK)
|
||||
{
|
||||
Instruction insn = *pc++;
|
||||
uint32_t aux = *pc;
|
||||
|
@ -107,8 +107,8 @@ struct ACFixture : ACFixtureImpl<Fixture>
|
||||
ACFixture()
|
||||
: ACFixtureImpl<Fixture>()
|
||||
{
|
||||
addGlobalBinding(frontend.typeChecker, "table", Binding{typeChecker.anyType});
|
||||
addGlobalBinding(frontend.typeChecker, "math", Binding{typeChecker.anyType});
|
||||
addGlobalBinding(frontend, "table", Binding{typeChecker.anyType});
|
||||
addGlobalBinding(frontend, "math", Binding{typeChecker.anyType});
|
||||
addGlobalBinding(frontend.typeCheckerForAutocomplete, "table", Binding{typeChecker.anyType});
|
||||
addGlobalBinding(frontend.typeCheckerForAutocomplete, "math", Binding{typeChecker.anyType});
|
||||
}
|
||||
@ -3200,8 +3200,6 @@ a.@1
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "globals_are_order_independent")
|
||||
{
|
||||
ScopedFastFlag sff("LuauAutocompleteFixGlobalOrder", true);
|
||||
|
||||
check(R"(
|
||||
local myLocal = 4
|
||||
function abc0()
|
||||
|
162
tests/CodeAllocator.test.cpp
Normal file
162
tests/CodeAllocator.test.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/AssemblyBuilderX64.h"
|
||||
#include "Luau/CodeAllocator.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace Luau::CodeGen;
|
||||
|
||||
TEST_SUITE_BEGIN("CodeAllocation");
|
||||
|
||||
TEST_CASE("CodeAllocation")
|
||||
{
|
||||
size_t blockSize = 1024 * 1024;
|
||||
size_t maxTotalSize = 1024 * 1024;
|
||||
CodeAllocator allocator(blockSize, maxTotalSize);
|
||||
|
||||
uint8_t* nativeData = nullptr;
|
||||
size_t sizeNativeData = 0;
|
||||
uint8_t* nativeEntry = nullptr;
|
||||
|
||||
std::vector<uint8_t> code;
|
||||
code.resize(128);
|
||||
|
||||
REQUIRE(allocator.allocate(nullptr, 0, code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
CHECK(nativeData != nullptr);
|
||||
CHECK(sizeNativeData == 128);
|
||||
CHECK(nativeEntry != nullptr);
|
||||
CHECK(nativeEntry == nativeData);
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(8);
|
||||
|
||||
REQUIRE(allocator.allocate(data.data(), data.size(), code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
CHECK(nativeData != nullptr);
|
||||
CHECK(sizeNativeData == 16 + 128);
|
||||
CHECK(nativeEntry != nullptr);
|
||||
CHECK(nativeEntry == nativeData + 16);
|
||||
}
|
||||
|
||||
TEST_CASE("CodeAllocationFailure")
|
||||
{
|
||||
size_t blockSize = 16384;
|
||||
size_t maxTotalSize = 32768;
|
||||
CodeAllocator allocator(blockSize, maxTotalSize);
|
||||
|
||||
uint8_t* nativeData;
|
||||
size_t sizeNativeData;
|
||||
uint8_t* nativeEntry;
|
||||
|
||||
std::vector<uint8_t> code;
|
||||
code.resize(18000);
|
||||
|
||||
// allocation has to fit in a block
|
||||
REQUIRE(!allocator.allocate(nullptr, 0, code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
|
||||
// each allocation exhausts a block, so third allocation fails
|
||||
code.resize(10000);
|
||||
REQUIRE(allocator.allocate(nullptr, 0, code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
REQUIRE(allocator.allocate(nullptr, 0, code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
REQUIRE(!allocator.allocate(nullptr, 0, code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
}
|
||||
|
||||
TEST_CASE("CodeAllocationWithUnwindCallbacks")
|
||||
{
|
||||
struct Info
|
||||
{
|
||||
std::vector<uint8_t> unwind;
|
||||
uint8_t* block = nullptr;
|
||||
bool destroyCalled = false;
|
||||
};
|
||||
Info info;
|
||||
info.unwind.resize(8);
|
||||
|
||||
{
|
||||
size_t blockSize = 1024 * 1024;
|
||||
size_t maxTotalSize = 1024 * 1024;
|
||||
CodeAllocator allocator(blockSize, maxTotalSize);
|
||||
|
||||
uint8_t* nativeData = nullptr;
|
||||
size_t sizeNativeData = 0;
|
||||
uint8_t* nativeEntry = nullptr;
|
||||
|
||||
std::vector<uint8_t> code;
|
||||
code.resize(128);
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(8);
|
||||
|
||||
allocator.context = &info;
|
||||
allocator.createBlockUnwindInfo = [](void* context, uint8_t* block, size_t blockSize, size_t& unwindDataSizeInBlock) -> void* {
|
||||
Info& info = *(Info*)context;
|
||||
|
||||
CHECK(info.unwind.size() == 8);
|
||||
memcpy(block, info.unwind.data(), info.unwind.size());
|
||||
unwindDataSizeInBlock = 8;
|
||||
|
||||
info.block = block;
|
||||
|
||||
return new int(7);
|
||||
};
|
||||
allocator.destroyBlockUnwindInfo = [](void* context, void* unwindData) {
|
||||
Info& info = *(Info*)context;
|
||||
|
||||
info.destroyCalled = true;
|
||||
|
||||
CHECK(*(int*)unwindData == 7);
|
||||
delete (int*)unwindData;
|
||||
};
|
||||
|
||||
REQUIRE(allocator.allocate(data.data(), data.size(), code.data(), code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
CHECK(nativeData != nullptr);
|
||||
CHECK(sizeNativeData == 16 + 128);
|
||||
CHECK(nativeEntry != nullptr);
|
||||
CHECK(nativeEntry == nativeData + 16);
|
||||
CHECK(nativeData == info.block + 16);
|
||||
}
|
||||
|
||||
CHECK(info.destroyCalled);
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
TEST_CASE("GeneratedCodeExecution")
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// Windows x64 ABI
|
||||
constexpr RegisterX64 rArg1 = rcx;
|
||||
constexpr RegisterX64 rArg2 = rdx;
|
||||
#else
|
||||
// System V AMD64 ABI
|
||||
constexpr RegisterX64 rArg1 = rdi;
|
||||
constexpr RegisterX64 rArg2 = rsi;
|
||||
#endif
|
||||
|
||||
AssemblyBuilderX64 build(/* logText= */ false);
|
||||
|
||||
build.mov(rax, rArg1);
|
||||
build.add(rax, rArg2);
|
||||
build.imul(rax, rax, 7);
|
||||
build.ret();
|
||||
|
||||
build.finalize();
|
||||
|
||||
size_t blockSize = 1024 * 1024;
|
||||
size_t maxTotalSize = 1024 * 1024;
|
||||
CodeAllocator allocator(blockSize, maxTotalSize);
|
||||
|
||||
uint8_t* nativeData;
|
||||
size_t sizeNativeData;
|
||||
uint8_t* nativeEntry;
|
||||
REQUIRE(allocator.allocate(build.data.data(), build.data.size(), build.code.data(), build.code.size(), nativeData, sizeNativeData, nativeEntry));
|
||||
REQUIRE(nativeEntry);
|
||||
|
||||
using FunctionType = int64_t(int64_t, int64_t);
|
||||
FunctionType* f = (FunctionType*)nativeEntry;
|
||||
int64_t result = f(10, 20);
|
||||
CHECK(result == 210);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_SUITE_END();
|
@ -6100,6 +6100,7 @@ return
|
||||
math.round(7.6),
|
||||
bit32.extract(-1, 31),
|
||||
bit32.replace(100, 1, 0),
|
||||
math.log(100, 10),
|
||||
(type("fin"))
|
||||
)",
|
||||
0, 2),
|
||||
@ -6153,8 +6154,9 @@ LOADN R45 1
|
||||
LOADN R46 8
|
||||
LOADN R47 1
|
||||
LOADN R48 101
|
||||
LOADK R49 K3
|
||||
RETURN R0 50
|
||||
LOADN R49 2
|
||||
LOADK R50 K3
|
||||
RETURN R0 51
|
||||
)");
|
||||
}
|
||||
|
||||
@ -6166,7 +6168,12 @@ return
|
||||
math.max(1, true),
|
||||
string.byte("abc", 42),
|
||||
bit32.rshift(10, 42),
|
||||
bit32.extract(1, 2, "3")
|
||||
bit32.extract(1, 2, "3"),
|
||||
bit32.bor(1, true),
|
||||
bit32.band(1, true),
|
||||
bit32.bxor(1, true),
|
||||
bit32.btest(1, true),
|
||||
math.min(1, true)
|
||||
)",
|
||||
0, 2),
|
||||
R"(
|
||||
@ -6193,11 +6200,96 @@ LOADN R6 2
|
||||
LOADK R7 K14
|
||||
FASTCALL 34 L4
|
||||
GETIMPORT R4 16
|
||||
CALL R4 3 -1
|
||||
L4: RETURN R0 -1
|
||||
CALL R4 3 1
|
||||
L4: LOADN R6 1
|
||||
FASTCALL2K 31 R6 K3 L5
|
||||
LOADK R7 K3
|
||||
GETIMPORT R5 18
|
||||
CALL R5 2 1
|
||||
L5: LOADN R7 1
|
||||
FASTCALL2K 29 R7 K3 L6
|
||||
LOADK R8 K3
|
||||
GETIMPORT R6 20
|
||||
CALL R6 2 1
|
||||
L6: LOADN R8 1
|
||||
FASTCALL2K 32 R8 K3 L7
|
||||
LOADK R9 K3
|
||||
GETIMPORT R7 22
|
||||
CALL R7 2 1
|
||||
L7: LOADN R9 1
|
||||
FASTCALL2K 33 R9 K3 L8
|
||||
LOADK R10 K3
|
||||
GETIMPORT R8 24
|
||||
CALL R8 2 1
|
||||
L8: LOADN R10 1
|
||||
FASTCALL2K 19 R10 K3 L9
|
||||
LOADK R11 K3
|
||||
GETIMPORT R9 26
|
||||
CALL R9 2 -1
|
||||
L9: RETURN R0 -1
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_CASE("BuiltinFoldingProhibitedCoverage")
|
||||
{
|
||||
const char* builtins[] = {
|
||||
"math.abs",
|
||||
"math.acos",
|
||||
"math.asin",
|
||||
"math.atan2",
|
||||
"math.atan",
|
||||
"math.ceil",
|
||||
"math.cosh",
|
||||
"math.cos",
|
||||
"math.deg",
|
||||
"math.exp",
|
||||
"math.floor",
|
||||
"math.fmod",
|
||||
"math.ldexp",
|
||||
"math.log10",
|
||||
"math.log",
|
||||
"math.max",
|
||||
"math.min",
|
||||
"math.pow",
|
||||
"math.rad",
|
||||
"math.sinh",
|
||||
"math.sin",
|
||||
"math.sqrt",
|
||||
"math.tanh",
|
||||
"math.tan",
|
||||
"bit32.arshift",
|
||||
"bit32.band",
|
||||
"bit32.bnot",
|
||||
"bit32.bor",
|
||||
"bit32.bxor",
|
||||
"bit32.btest",
|
||||
"bit32.extract",
|
||||
"bit32.lrotate",
|
||||
"bit32.lshift",
|
||||
"bit32.replace",
|
||||
"bit32.rrotate",
|
||||
"bit32.rshift",
|
||||
"type",
|
||||
"string.byte",
|
||||
"string.len",
|
||||
"typeof",
|
||||
"math.clamp",
|
||||
"math.sign",
|
||||
"math.round",
|
||||
};
|
||||
|
||||
for (const char* func : builtins)
|
||||
{
|
||||
std::string source = "return ";
|
||||
source += func;
|
||||
source += "()";
|
||||
|
||||
std::string bc = compileFunction(source.c_str(), 0, 2);
|
||||
|
||||
CHECK(bc.find("FASTCALL") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BuiltinFoldingMultret")
|
||||
{
|
||||
CHECK_EQ("\n" + compileFunction(R"(
|
||||
|
@ -496,7 +496,8 @@ TEST_CASE("Types")
|
||||
runConformance("types.lua", [](lua_State* L) {
|
||||
Luau::NullModuleResolver moduleResolver;
|
||||
Luau::InternalErrorReporter iceHandler;
|
||||
Luau::TypeChecker env(&moduleResolver, &iceHandler);
|
||||
Luau::SingletonTypes singletonTypes;
|
||||
Luau::TypeChecker env(&moduleResolver, Luau::NotNull{&singletonTypes}, &iceHandler);
|
||||
|
||||
Luau::registerBuiltinTypes(env);
|
||||
Luau::freeze(env.globalTypes);
|
||||
|
@ -26,10 +26,10 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "hello")
|
||||
)");
|
||||
|
||||
cgb.visit(block);
|
||||
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||
NotNull<Scope> rootScope{cgb.rootScope};
|
||||
|
||||
NullModuleResolver resolver;
|
||||
ConstraintSolver cs{&arena, rootScope, "MainModule", NotNull(&resolver), {}};
|
||||
ConstraintSolver cs{&arena, singletonTypes, rootScope, "MainModule", NotNull(&resolver), {}, &logger};
|
||||
|
||||
cs.run();
|
||||
|
||||
@ -47,10 +47,10 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "generic_function")
|
||||
)");
|
||||
|
||||
cgb.visit(block);
|
||||
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||
NotNull<Scope> rootScope{cgb.rootScope};
|
||||
|
||||
NullModuleResolver resolver;
|
||||
ConstraintSolver cs{&arena, rootScope, "MainModule", NotNull(&resolver), {}};
|
||||
ConstraintSolver cs{&arena, singletonTypes, rootScope, "MainModule", NotNull(&resolver), {}, &logger};
|
||||
|
||||
cs.run();
|
||||
|
||||
@ -74,12 +74,12 @@ TEST_CASE_FIXTURE(ConstraintGraphBuilderFixture, "proper_let_generalization")
|
||||
)");
|
||||
|
||||
cgb.visit(block);
|
||||
NotNull<Scope> rootScope = NotNull(cgb.rootScope);
|
||||
NotNull<Scope> rootScope{cgb.rootScope};
|
||||
|
||||
ToStringOptions opts;
|
||||
|
||||
NullModuleResolver resolver;
|
||||
ConstraintSolver cs{&arena, rootScope, "MainModule", NotNull(&resolver), {}};
|
||||
ConstraintSolver cs{&arena, singletonTypes, rootScope, "MainModule", NotNull(&resolver), {}, &logger};
|
||||
|
||||
cs.run();
|
||||
|
||||
|
@ -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/Parser.h"
|
||||
|
||||
#include "ScopedFlags.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
using namespace Luau;
|
||||
@ -223,4 +225,21 @@ end
|
||||
CHECK_EQ(6, Luau::Compile::computeCost(model, args2, 1));
|
||||
}
|
||||
|
||||
TEST_CASE("InterpString")
|
||||
{
|
||||
ScopedFastFlag sff("LuauInterpolatedStringBaseSupport", true);
|
||||
|
||||
uint64_t model = modelFunction(R"(
|
||||
function test(a)
|
||||
return `hello, {a}!`
|
||||
end
|
||||
)");
|
||||
|
||||
const bool args1[] = {false};
|
||||
const bool args2[] = {true};
|
||||
|
||||
CHECK_EQ(3, Luau::Compile::computeCost(model, args1, 1));
|
||||
CHECK_EQ(3, Luau::Compile::computeCost(model, args2, 1));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -91,6 +91,7 @@ Fixture::Fixture(bool freeze, bool prepareAutocomplete)
|
||||
: sff_DebugLuauFreezeArena("DebugLuauFreezeArena", freeze)
|
||||
, frontend(&fileResolver, &configResolver, {/* retainFullTypeGraphs= */ true})
|
||||
, typeChecker(frontend.typeChecker)
|
||||
, singletonTypes(frontend.singletonTypes)
|
||||
{
|
||||
configResolver.defaultConfig.mode = Mode::Strict;
|
||||
configResolver.defaultConfig.enabledLint.warningMask = ~0ull;
|
||||
@ -367,9 +368,9 @@ void Fixture::dumpErrors(std::ostream& os, const std::vector<TypeError>& errors)
|
||||
|
||||
void Fixture::registerTestTypes()
|
||||
{
|
||||
addGlobalBinding(typeChecker, "game", typeChecker.anyType, "@luau");
|
||||
addGlobalBinding(typeChecker, "workspace", typeChecker.anyType, "@luau");
|
||||
addGlobalBinding(typeChecker, "script", typeChecker.anyType, "@luau");
|
||||
addGlobalBinding(frontend, "game", typeChecker.anyType, "@luau");
|
||||
addGlobalBinding(frontend, "workspace", typeChecker.anyType, "@luau");
|
||||
addGlobalBinding(frontend, "script", typeChecker.anyType, "@luau");
|
||||
}
|
||||
|
||||
void Fixture::dumpErrors(const CheckResult& cr)
|
||||
@ -434,7 +435,7 @@ BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
|
||||
Luau::unfreeze(frontend.typeChecker.globalTypes);
|
||||
Luau::unfreeze(frontend.typeCheckerForAutocomplete.globalTypes);
|
||||
|
||||
registerBuiltinTypes(frontend.typeChecker);
|
||||
registerBuiltinTypes(frontend);
|
||||
if (prepareAutocomplete)
|
||||
registerBuiltinTypes(frontend.typeCheckerForAutocomplete);
|
||||
registerTestTypes();
|
||||
@ -446,7 +447,7 @@ BuiltinsFixture::BuiltinsFixture(bool freeze, bool prepareAutocomplete)
|
||||
ConstraintGraphBuilderFixture::ConstraintGraphBuilderFixture()
|
||||
: Fixture()
|
||||
, mainModule(new Module)
|
||||
, cgb(mainModuleName, mainModule, &arena, NotNull(&moduleResolver), NotNull(&ice), frontend.getGlobalScope())
|
||||
, cgb(mainModuleName, mainModule, &arena, NotNull(&moduleResolver), singletonTypes, NotNull(&ice), frontend.getGlobalScope(), &logger)
|
||||
, forceTheFlag{"DebugLuauDeferredConstraintResolution", true}
|
||||
{
|
||||
BlockedTypeVar::nextIndex = 0;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/TypeInfer.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
|
||||
#include "IostreamOptional.h"
|
||||
#include "ScopedFlags.h"
|
||||
@ -137,6 +138,7 @@ struct Fixture
|
||||
Frontend frontend;
|
||||
InternalErrorReporter ice;
|
||||
TypeChecker& typeChecker;
|
||||
NotNull<SingletonTypes> singletonTypes;
|
||||
|
||||
std::string decorateWithTypes(const std::string& code);
|
||||
|
||||
@ -165,6 +167,7 @@ struct ConstraintGraphBuilderFixture : Fixture
|
||||
TypeArena arena;
|
||||
ModulePtr mainModule;
|
||||
ConstraintGraphBuilder cgb;
|
||||
DcrLogger logger;
|
||||
|
||||
ScopedFastFlag forceTheFlag;
|
||||
|
||||
|
@ -81,8 +81,8 @@ struct FrontendFixture : BuiltinsFixture
|
||||
{
|
||||
FrontendFixture()
|
||||
{
|
||||
addGlobalBinding(typeChecker, "game", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(typeChecker, "script", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "game", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "script", frontend.typeChecker.anyType, "@test");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -34,23 +34,28 @@ static LValue mkSymbol(const std::string& s)
|
||||
return Symbol{AstName{s.data()}};
|
||||
}
|
||||
|
||||
struct LValueFixture
|
||||
{
|
||||
SingletonTypes singletonTypes;
|
||||
};
|
||||
|
||||
TEST_SUITE_BEGIN("LValue");
|
||||
|
||||
TEST_CASE("Luau_merge_hashmap_order")
|
||||
TEST_CASE_FIXTURE(LValueFixture, "Luau_merge_hashmap_order")
|
||||
{
|
||||
std::string a = "a";
|
||||
std::string b = "b";
|
||||
std::string c = "c";
|
||||
|
||||
RefinementMap m{{
|
||||
{mkSymbol(b), getSingletonTypes().stringType},
|
||||
{mkSymbol(c), getSingletonTypes().numberType},
|
||||
{mkSymbol(b), singletonTypes.stringType},
|
||||
{mkSymbol(c), singletonTypes.numberType},
|
||||
}};
|
||||
|
||||
RefinementMap other{{
|
||||
{mkSymbol(a), getSingletonTypes().stringType},
|
||||
{mkSymbol(b), getSingletonTypes().stringType},
|
||||
{mkSymbol(c), getSingletonTypes().booleanType},
|
||||
{mkSymbol(a), singletonTypes.stringType},
|
||||
{mkSymbol(b), singletonTypes.stringType},
|
||||
{mkSymbol(c), singletonTypes.booleanType},
|
||||
}};
|
||||
|
||||
TypeArena arena;
|
||||
@ -66,21 +71,21 @@ TEST_CASE("Luau_merge_hashmap_order")
|
||||
CHECK_EQ("boolean | number", toString(m[mkSymbol(c)]));
|
||||
}
|
||||
|
||||
TEST_CASE("Luau_merge_hashmap_order2")
|
||||
TEST_CASE_FIXTURE(LValueFixture, "Luau_merge_hashmap_order2")
|
||||
{
|
||||
std::string a = "a";
|
||||
std::string b = "b";
|
||||
std::string c = "c";
|
||||
|
||||
RefinementMap m{{
|
||||
{mkSymbol(a), getSingletonTypes().stringType},
|
||||
{mkSymbol(b), getSingletonTypes().stringType},
|
||||
{mkSymbol(c), getSingletonTypes().numberType},
|
||||
{mkSymbol(a), singletonTypes.stringType},
|
||||
{mkSymbol(b), singletonTypes.stringType},
|
||||
{mkSymbol(c), singletonTypes.numberType},
|
||||
}};
|
||||
|
||||
RefinementMap other{{
|
||||
{mkSymbol(b), getSingletonTypes().stringType},
|
||||
{mkSymbol(c), getSingletonTypes().booleanType},
|
||||
{mkSymbol(b), singletonTypes.stringType},
|
||||
{mkSymbol(c), singletonTypes.booleanType},
|
||||
}};
|
||||
|
||||
TypeArena arena;
|
||||
@ -96,7 +101,7 @@ TEST_CASE("Luau_merge_hashmap_order2")
|
||||
CHECK_EQ("boolean | number", toString(m[mkSymbol(c)]));
|
||||
}
|
||||
|
||||
TEST_CASE("one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||
TEST_CASE_FIXTURE(LValueFixture, "one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||
{
|
||||
std::string a = "a";
|
||||
std::string b = "b";
|
||||
@ -105,15 +110,15 @@ TEST_CASE("one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||
std::string e = "e";
|
||||
|
||||
RefinementMap m{{
|
||||
{mkSymbol(a), getSingletonTypes().stringType},
|
||||
{mkSymbol(b), getSingletonTypes().numberType},
|
||||
{mkSymbol(c), getSingletonTypes().booleanType},
|
||||
{mkSymbol(a), singletonTypes.stringType},
|
||||
{mkSymbol(b), singletonTypes.numberType},
|
||||
{mkSymbol(c), singletonTypes.booleanType},
|
||||
}};
|
||||
|
||||
RefinementMap other{{
|
||||
{mkSymbol(c), getSingletonTypes().stringType},
|
||||
{mkSymbol(d), getSingletonTypes().numberType},
|
||||
{mkSymbol(e), getSingletonTypes().booleanType},
|
||||
{mkSymbol(c), singletonTypes.stringType},
|
||||
{mkSymbol(d), singletonTypes.numberType},
|
||||
{mkSymbol(e), singletonTypes.booleanType},
|
||||
}};
|
||||
|
||||
TypeArena arena;
|
||||
@ -133,7 +138,7 @@ TEST_CASE("one_map_has_overlap_at_end_whereas_other_has_it_in_start")
|
||||
CHECK_EQ("boolean", toString(m[mkSymbol(e)]));
|
||||
}
|
||||
|
||||
TEST_CASE("hashing_lvalue_global_prop_access")
|
||||
TEST_CASE_FIXTURE(LValueFixture, "hashing_lvalue_global_prop_access")
|
||||
{
|
||||
std::string t1 = "t";
|
||||
std::string x1 = "x";
|
||||
@ -154,13 +159,13 @@ TEST_CASE("hashing_lvalue_global_prop_access")
|
||||
CHECK_EQ(LValueHasher{}(t_x2), LValueHasher{}(t_x2));
|
||||
|
||||
RefinementMap m;
|
||||
m[t_x1] = getSingletonTypes().stringType;
|
||||
m[t_x2] = getSingletonTypes().numberType;
|
||||
m[t_x1] = singletonTypes.stringType;
|
||||
m[t_x2] = singletonTypes.numberType;
|
||||
|
||||
CHECK_EQ(1, m.size());
|
||||
}
|
||||
|
||||
TEST_CASE("hashing_lvalue_local_prop_access")
|
||||
TEST_CASE_FIXTURE(LValueFixture, "hashing_lvalue_local_prop_access")
|
||||
{
|
||||
std::string t1 = "t";
|
||||
std::string x1 = "x";
|
||||
@ -183,8 +188,8 @@ TEST_CASE("hashing_lvalue_local_prop_access")
|
||||
CHECK_EQ(LValueHasher{}(t_x2), LValueHasher{}(t_x2));
|
||||
|
||||
RefinementMap m;
|
||||
m[t_x1] = getSingletonTypes().stringType;
|
||||
m[t_x2] = getSingletonTypes().numberType;
|
||||
m[t_x1] = singletonTypes.stringType;
|
||||
m[t_x2] = singletonTypes.numberType;
|
||||
|
||||
CHECK_EQ(2, m.size());
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ TEST_CASE_FIXTURE(Fixture, "UnknownGlobal")
|
||||
TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobal")
|
||||
{
|
||||
// Normally this would be defined externally, so hack it in for testing
|
||||
addGlobalBinding(typeChecker, "Wait", Binding{typeChecker.anyType, {}, true, "wait", "@test/global/Wait"});
|
||||
addGlobalBinding(frontend, "Wait", Binding{typeChecker.anyType, {}, true, "wait", "@test/global/Wait"});
|
||||
|
||||
LintResult result = lintTyped("Wait(5)");
|
||||
|
||||
@ -49,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobalNoReplacement")
|
||||
|
||||
// Normally this would be defined externally, so hack it in for testing
|
||||
const char* deprecationReplacementString = "";
|
||||
addGlobalBinding(typeChecker, "Version", Binding{typeChecker.anyType, {}, true, deprecationReplacementString});
|
||||
addGlobalBinding(frontend, "Version", Binding{typeChecker.anyType, {}, true, deprecationReplacementString});
|
||||
|
||||
LintResult result = lintTyped("Version()");
|
||||
|
||||
@ -380,7 +380,7 @@ return bar()
|
||||
TEST_CASE_FIXTURE(Fixture, "ImportUnused")
|
||||
{
|
||||
// Normally this would be defined externally, so hack it in for testing
|
||||
addGlobalBinding(typeChecker, "game", typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "game", typeChecker.anyType, "@test");
|
||||
|
||||
LintResult result = lint(R"(
|
||||
local Roact = require(game.Packages.Roact)
|
||||
@ -1464,7 +1464,7 @@ TEST_CASE_FIXTURE(Fixture, "DeprecatedApi")
|
||||
|
||||
getMutable<TableTypeVar>(colorType)->props = {{"toHSV", {typeChecker.anyType, /* deprecated= */ true, "Color3:ToHSV"}}};
|
||||
|
||||
addGlobalBinding(typeChecker, "Color3", Binding{colorType, {}});
|
||||
addGlobalBinding(frontend, "Color3", Binding{colorType, {}});
|
||||
|
||||
freeze(typeChecker.globalTypes);
|
||||
|
||||
@ -1737,8 +1737,6 @@ local _ = 0x0xffffffffffffffffffffffffffffffffff
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "ComparisonPrecedence")
|
||||
{
|
||||
ScopedFastFlag sff("LuauLintComparisonPrecedence", true);
|
||||
|
||||
LintResult result = lint(R"(
|
||||
local a, b = ...
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||
|
||||
TEST_SUITE_BEGIN("ModuleTests");
|
||||
@ -134,7 +135,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena")
|
||||
REQUIRE(signType != nullptr);
|
||||
|
||||
CHECK(!isInArena(signType, module->interfaceTypes));
|
||||
CHECK(isInArena(signType, typeChecker.globalTypes));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(isInArena(signType, frontend.globalTypes));
|
||||
else
|
||||
CHECK(isInArena(signType, typeChecker.globalTypes));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "deepClone_union")
|
||||
@ -230,7 +234,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_constrained_intersection")
|
||||
{
|
||||
TypeArena src;
|
||||
|
||||
TypeId constrained = src.addType(ConstrainedTypeVar{TypeLevel{}, {getSingletonTypes().numberType, getSingletonTypes().stringType}});
|
||||
TypeId constrained = src.addType(ConstrainedTypeVar{TypeLevel{}, {singletonTypes->numberType, singletonTypes->stringType}});
|
||||
|
||||
TypeArena dest;
|
||||
CloneState cloneState;
|
||||
@ -240,8 +244,8 @@ TEST_CASE_FIXTURE(Fixture, "clone_constrained_intersection")
|
||||
|
||||
const ConstrainedTypeVar* ctv = get<ConstrainedTypeVar>(cloned);
|
||||
REQUIRE_EQ(2, ctv->parts.size());
|
||||
CHECK_EQ(getSingletonTypes().numberType, ctv->parts[0]);
|
||||
CHECK_EQ(getSingletonTypes().stringType, ctv->parts[1]);
|
||||
CHECK_EQ(singletonTypes->numberType, ctv->parts[0]);
|
||||
CHECK_EQ(singletonTypes->stringType, ctv->parts[1]);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property")
|
||||
|
@ -15,13 +15,13 @@ struct NormalizeFixture : Fixture
|
||||
|
||||
bool isSubtype(TypeId a, TypeId b)
|
||||
{
|
||||
return ::Luau::isSubtype(a, b, NotNull{getMainModule()->getModuleScope().get()}, ice);
|
||||
return ::Luau::isSubtype(a, b, NotNull{getMainModule()->getModuleScope().get()}, singletonTypes, ice);
|
||||
}
|
||||
};
|
||||
|
||||
void createSomeClasses(TypeChecker& typeChecker)
|
||||
void createSomeClasses(Frontend& frontend)
|
||||
{
|
||||
auto& arena = typeChecker.globalTypes;
|
||||
auto& arena = frontend.globalTypes;
|
||||
|
||||
unfreeze(arena);
|
||||
|
||||
@ -32,23 +32,23 @@ void createSomeClasses(TypeChecker& typeChecker)
|
||||
|
||||
parentClass->props["virtual_method"] = {makeFunction(arena, parentType, {}, {})};
|
||||
|
||||
addGlobalBinding(typeChecker, "Parent", {parentType});
|
||||
typeChecker.globalScope->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
||||
addGlobalBinding(frontend, "Parent", {parentType});
|
||||
frontend.getGlobalScope()->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
||||
|
||||
TypeId childType = arena.addType(ClassTypeVar{"Child", {}, parentType, std::nullopt, {}, nullptr, "Test"});
|
||||
|
||||
ClassTypeVar* childClass = getMutable<ClassTypeVar>(childType);
|
||||
childClass->props["virtual_method"] = {makeFunction(arena, childType, {}, {})};
|
||||
|
||||
addGlobalBinding(typeChecker, "Child", {childType});
|
||||
typeChecker.globalScope->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
||||
addGlobalBinding(frontend, "Child", {childType});
|
||||
frontend.getGlobalScope()->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
||||
|
||||
TypeId unrelatedType = arena.addType(ClassTypeVar{"Unrelated", {}, std::nullopt, std::nullopt, {}, nullptr, "Test"});
|
||||
|
||||
addGlobalBinding(typeChecker, "Unrelated", {unrelatedType});
|
||||
typeChecker.globalScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
||||
addGlobalBinding(frontend, "Unrelated", {unrelatedType});
|
||||
frontend.getGlobalScope()->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
||||
|
||||
for (const auto& [name, ty] : typeChecker.globalScope->exportedTypeBindings)
|
||||
for (const auto& [name, ty] : frontend.getGlobalScope()->exportedTypeBindings)
|
||||
persist(ty.type);
|
||||
|
||||
freeze(arena);
|
||||
@ -508,7 +508,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_table")
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "classes")
|
||||
{
|
||||
createSomeClasses(typeChecker);
|
||||
createSomeClasses(frontend);
|
||||
|
||||
check(""); // Ensure that we have a main Module.
|
||||
|
||||
@ -596,7 +596,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "union_with_overlapping_field_that_has_a_sub
|
||||
)");
|
||||
|
||||
ModulePtr tempModule{new Module};
|
||||
tempModule->scopes.emplace_back(Location(), std::make_shared<Scope>(getSingletonTypes().anyTypePack));
|
||||
tempModule->scopes.emplace_back(Location(), std::make_shared<Scope>(singletonTypes->anyTypePack));
|
||||
|
||||
// HACK: Normalization is an in-place operation. We need to cheat a little here and unfreeze
|
||||
// the arena that the type lives in.
|
||||
@ -604,7 +604,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "union_with_overlapping_field_that_has_a_sub
|
||||
unfreeze(mainModule->internalTypes);
|
||||
|
||||
TypeId tType = requireType("t");
|
||||
normalize(tType, tempModule, *typeChecker.iceHandler);
|
||||
normalize(tType, tempModule, singletonTypes, *typeChecker.iceHandler);
|
||||
|
||||
CHECK_EQ("{| x: number? |}", toString(tType, {true}));
|
||||
}
|
||||
@ -1085,7 +1085,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_typevars_should_only_be_marked_normal_if_their
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "skip_force_normal_on_external_types")
|
||||
{
|
||||
createSomeClasses(typeChecker);
|
||||
createSomeClasses(frontend);
|
||||
|
||||
CheckResult result = check(R"(
|
||||
export type t0 = { a: Child }
|
||||
|
@ -1,3 +1,4 @@
|
||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/NotNull.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
@ -76,8 +76,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
|
||||
Location{{1, 21}, {1, 26}},
|
||||
getMainSourceModule()->name,
|
||||
TypeMismatch{
|
||||
getSingletonTypes().numberType,
|
||||
getSingletonTypes().stringType,
|
||||
singletonTypes->numberType,
|
||||
singletonTypes->stringType,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -87,8 +87,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
|
||||
Location{{1, 8}, {1, 26}},
|
||||
getMainSourceModule()->name,
|
||||
TypeMismatch{
|
||||
getSingletonTypes().numberType,
|
||||
getSingletonTypes().stringType,
|
||||
singletonTypes->numberType,
|
||||
singletonTypes->stringType,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -501,7 +501,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation")
|
||||
CheckResult result = check("type t10<x> = typeof(table)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
TypeId ty = getGlobalBinding(frontend.typeChecker, "table");
|
||||
TypeId ty = getGlobalBinding(frontend, "table");
|
||||
CHECK_EQ(toString(ty), "table");
|
||||
|
||||
const TableTypeVar* ttv = get<TableTypeVar>(ty);
|
||||
|
@ -557,7 +557,7 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "use_type_required_from_another_file")
|
||||
{
|
||||
addGlobalBinding(frontend.typeChecker, "script", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "script", frontend.typeChecker.anyType, "@test");
|
||||
|
||||
fileResolver.source["Modules/Main"] = R"(
|
||||
--!strict
|
||||
@ -583,7 +583,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "use_type_required_from_another_file")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type")
|
||||
{
|
||||
addGlobalBinding(frontend.typeChecker, "script", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "script", frontend.typeChecker.anyType, "@test");
|
||||
|
||||
fileResolver.source["Modules/Main"] = R"(
|
||||
--!strict
|
||||
@ -609,7 +609,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_are_not_exported")
|
||||
{
|
||||
addGlobalBinding(frontend.typeChecker, "script", frontend.typeChecker.anyType, "@test");
|
||||
addGlobalBinding(frontend, "script", frontend.typeChecker.anyType, "@test");
|
||||
|
||||
fileResolver.source["Modules/Main"] = R"(
|
||||
--!strict
|
||||
|
@ -32,7 +32,7 @@ struct ClassFixture : BuiltinsFixture
|
||||
{"New", {makeFunction(arena, nullopt, {}, {baseClassInstanceType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||
addGlobalBinding(typeChecker, "BaseClass", baseClassType, "@test");
|
||||
addGlobalBinding(frontend, "BaseClass", baseClassType, "@test");
|
||||
|
||||
TypeId childClassInstanceType = arena.addType(ClassTypeVar{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test"});
|
||||
|
||||
@ -45,7 +45,7 @@ struct ClassFixture : BuiltinsFixture
|
||||
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
||||
addGlobalBinding(typeChecker, "ChildClass", childClassType, "@test");
|
||||
addGlobalBinding(frontend, "ChildClass", childClassType, "@test");
|
||||
|
||||
TypeId grandChildInstanceType = arena.addType(ClassTypeVar{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test"});
|
||||
|
||||
@ -58,7 +58,7 @@ struct ClassFixture : BuiltinsFixture
|
||||
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
||||
addGlobalBinding(typeChecker, "GrandChild", childClassType, "@test");
|
||||
addGlobalBinding(frontend, "GrandChild", childClassType, "@test");
|
||||
|
||||
TypeId anotherChildInstanceType = arena.addType(ClassTypeVar{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test"});
|
||||
|
||||
@ -71,7 +71,7 @@ struct ClassFixture : BuiltinsFixture
|
||||
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
||||
addGlobalBinding(typeChecker, "AnotherChild", childClassType, "@test");
|
||||
addGlobalBinding(frontend, "AnotherChild", childClassType, "@test");
|
||||
|
||||
TypeId vector2MetaType = arena.addType(TableTypeVar{});
|
||||
|
||||
@ -89,7 +89,7 @@ struct ClassFixture : BuiltinsFixture
|
||||
{"__add", {makeFunction(arena, nullopt, {vector2InstanceType, vector2InstanceType}, {vector2InstanceType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType};
|
||||
addGlobalBinding(typeChecker, "Vector2", vector2Type, "@test");
|
||||
addGlobalBinding(frontend, "Vector2", vector2Type, "@test");
|
||||
|
||||
for (const auto& [name, tf] : typeChecker.globalScope->exportedTypeBindings)
|
||||
persist(tf.type);
|
||||
|
@ -19,13 +19,13 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_simple")
|
||||
declare foo2: typeof(foo)
|
||||
)");
|
||||
|
||||
TypeId globalFooTy = getGlobalBinding(frontend.typeChecker, "foo");
|
||||
TypeId globalFooTy = getGlobalBinding(frontend, "foo");
|
||||
CHECK_EQ(toString(globalFooTy), "number");
|
||||
|
||||
TypeId globalBarTy = getGlobalBinding(frontend.typeChecker, "bar");
|
||||
TypeId globalBarTy = getGlobalBinding(frontend, "bar");
|
||||
CHECK_EQ(toString(globalBarTy), "(number) -> string");
|
||||
|
||||
TypeId globalFoo2Ty = getGlobalBinding(frontend.typeChecker, "foo2");
|
||||
TypeId globalFoo2Ty = getGlobalBinding(frontend, "foo2");
|
||||
CHECK_EQ(toString(globalFoo2Ty), "number");
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -48,20 +48,20 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_loading")
|
||||
declare function var(...: any): string
|
||||
)");
|
||||
|
||||
TypeId globalFooTy = getGlobalBinding(frontend.typeChecker, "foo");
|
||||
TypeId globalFooTy = getGlobalBinding(frontend, "foo");
|
||||
CHECK_EQ(toString(globalFooTy), "number");
|
||||
|
||||
std::optional<TypeFun> globalAsdfTy = frontend.typeChecker.globalScope->lookupType("Asdf");
|
||||
std::optional<TypeFun> globalAsdfTy = frontend.getGlobalScope()->lookupType("Asdf");
|
||||
REQUIRE(bool(globalAsdfTy));
|
||||
CHECK_EQ(toString(globalAsdfTy->type), "number | string");
|
||||
|
||||
TypeId globalBarTy = getGlobalBinding(frontend.typeChecker, "bar");
|
||||
TypeId globalBarTy = getGlobalBinding(frontend, "bar");
|
||||
CHECK_EQ(toString(globalBarTy), "(number) -> string");
|
||||
|
||||
TypeId globalFoo2Ty = getGlobalBinding(frontend.typeChecker, "foo2");
|
||||
TypeId globalFoo2Ty = getGlobalBinding(frontend, "foo2");
|
||||
CHECK_EQ(toString(globalFoo2Ty), "number");
|
||||
|
||||
TypeId globalVarTy = getGlobalBinding(frontend.typeChecker, "var");
|
||||
TypeId globalVarTy = getGlobalBinding(frontend, "var");
|
||||
|
||||
CHECK_EQ(toString(globalVarTy), "(...any) -> string");
|
||||
|
||||
@ -85,7 +85,7 @@ TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_sc
|
||||
freeze(typeChecker.globalTypes);
|
||||
|
||||
REQUIRE(!parseFailResult.success);
|
||||
std::optional<Binding> fooTy = tryGetGlobalBinding(typeChecker, "foo");
|
||||
std::optional<Binding> fooTy = tryGetGlobalBinding(frontend, "foo");
|
||||
CHECK(!fooTy.has_value());
|
||||
|
||||
LoadDefinitionFileResult checkFailResult = loadDefinitionFile(typeChecker, typeChecker.globalScope, R"(
|
||||
@ -95,7 +95,7 @@ TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_sc
|
||||
"@test");
|
||||
|
||||
REQUIRE(!checkFailResult.success);
|
||||
std::optional<Binding> barTy = tryGetGlobalBinding(typeChecker, "bar");
|
||||
std::optional<Binding> barTy = tryGetGlobalBinding(frontend, "bar");
|
||||
CHECK(!barTy.has_value());
|
||||
}
|
||||
|
||||
|
@ -127,6 +127,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified")
|
||||
return T
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
auto r = first(getMainModule()->getModuleScope()->returnType);
|
||||
REQUIRE(r);
|
||||
|
||||
@ -136,8 +138,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified")
|
||||
REQUIRE(ttv->props.count("f"));
|
||||
TypeId k = ttv->props["f"].type;
|
||||
REQUIRE(k);
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "list_only_alternative_overloads_that_match_argument_count")
|
||||
|
@ -102,4 +102,18 @@ end
|
||||
CHECK_EQ(toString(result.errors[1]), "Type 'number' could not be converted into 'string'");
|
||||
}
|
||||
|
||||
TEST_CASE("singleton_types")
|
||||
{
|
||||
BuiltinsFixture a;
|
||||
|
||||
{
|
||||
BuiltinsFixture b;
|
||||
}
|
||||
|
||||
// Check that Frontend 'a' environment wasn't modified by 'b'
|
||||
CheckResult result = a.check("local s: string = 'hello' local t = s:lower()");
|
||||
|
||||
CHECK(result.errors.empty());
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -353,6 +353,9 @@ TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_type_pack")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{"LuauLowerBoundsCalculation", false},
|
||||
// I'm not sure why this is broken without DCR, but it seems to be fixed
|
||||
// when DCR is enabled.
|
||||
{"DebugLuauDeferredConstraintResolution", false},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -367,6 +370,9 @@ TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_variadic_pack")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{"LuauLowerBoundsCalculation", false},
|
||||
// I'm not sure why this is broken without DCR, but it seems to be fixed
|
||||
// when DCR is enabled.
|
||||
{"DebugLuauDeferredConstraintResolution", false},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -588,9 +594,9 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
|
||||
};
|
||||
|
||||
TypeArena arena;
|
||||
TypeId nilType = getSingletonTypes().nilType;
|
||||
TypeId nilType = singletonTypes->nilType;
|
||||
|
||||
std::unique_ptr scope = std::make_unique<Scope>(getSingletonTypes().anyTypePack);
|
||||
std::unique_ptr scope = std::make_unique<Scope>(singletonTypes->anyTypePack);
|
||||
|
||||
TypeId free1 = arena.addType(FreeTypePack{scope.get()});
|
||||
TypeId option1 = arena.addType(UnionTypeVar{{nilType, free1}});
|
||||
@ -600,7 +606,7 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
|
||||
|
||||
InternalErrorReporter iceHandler;
|
||||
UnifierSharedState sharedState{&iceHandler};
|
||||
Unifier u{&arena, Mode::Strict, NotNull{scope.get()}, Location{}, Variance::Covariant, sharedState};
|
||||
Unifier u{&arena, singletonTypes, Mode::Strict, NotNull{scope.get()}, Location{}, Variance::Covariant, sharedState};
|
||||
|
||||
u.tryUnify(option1, option2);
|
||||
|
||||
|
@ -50,7 +50,7 @@ struct RefinementClassFixture : Fixture
|
||||
{"Y", Property{typeChecker.numberType}},
|
||||
{"Z", Property{typeChecker.numberType}},
|
||||
};
|
||||
normalize(vec3, scope, arena, *typeChecker.iceHandler);
|
||||
normalize(vec3, scope, arena, singletonTypes, *typeChecker.iceHandler);
|
||||
|
||||
TypeId inst = arena.addType(ClassTypeVar{"Instance", {}, std::nullopt, std::nullopt, {}, nullptr, "Test"});
|
||||
|
||||
@ -58,21 +58,21 @@ struct RefinementClassFixture : Fixture
|
||||
TypePackId isARets = arena.addTypePack({typeChecker.booleanType});
|
||||
TypeId isA = arena.addType(FunctionTypeVar{isAParams, isARets});
|
||||
getMutable<FunctionTypeVar>(isA)->magicFunction = magicFunctionInstanceIsA;
|
||||
normalize(isA, scope, arena, *typeChecker.iceHandler);
|
||||
normalize(isA, scope, arena, singletonTypes, *typeChecker.iceHandler);
|
||||
|
||||
getMutable<ClassTypeVar>(inst)->props = {
|
||||
{"Name", Property{typeChecker.stringType}},
|
||||
{"IsA", Property{isA}},
|
||||
};
|
||||
normalize(inst, scope, arena, *typeChecker.iceHandler);
|
||||
normalize(inst, scope, arena, singletonTypes, *typeChecker.iceHandler);
|
||||
|
||||
TypeId folder = typeChecker.globalTypes.addType(ClassTypeVar{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test"});
|
||||
normalize(folder, scope, arena, *typeChecker.iceHandler);
|
||||
normalize(folder, scope, arena, singletonTypes, *typeChecker.iceHandler);
|
||||
TypeId part = typeChecker.globalTypes.addType(ClassTypeVar{"Part", {}, inst, std::nullopt, {}, nullptr, "Test"});
|
||||
getMutable<ClassTypeVar>(part)->props = {
|
||||
{"Position", Property{vec3}},
|
||||
};
|
||||
normalize(part, scope, arena, *typeChecker.iceHandler);
|
||||
normalize(part, scope, arena, singletonTypes, *typeChecker.iceHandler);
|
||||
|
||||
typeChecker.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3};
|
||||
typeChecker.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst};
|
||||
|
@ -18,7 +18,7 @@ struct TryUnifyFixture : Fixture
|
||||
InternalErrorReporter iceHandler;
|
||||
UnifierSharedState unifierState{&iceHandler};
|
||||
|
||||
Unifier state{&arena, Mode::Strict, NotNull{globalScope.get()}, Location{}, Variance::Covariant, unifierState};
|
||||
Unifier state{&arena, singletonTypes, Mode::Strict, NotNull{globalScope.get()}, Location{}, Variance::Covariant, unifierState};
|
||||
};
|
||||
|
||||
TEST_SUITE_BEGIN("TryUnifyTests");
|
||||
|
@ -194,7 +194,7 @@ TEST_CASE_FIXTURE(Fixture, "variadic_packs")
|
||||
TypePackId listOfStrings = arena.addTypePack(TypePackVar{VariadicTypePack{typeChecker.stringType}});
|
||||
|
||||
// clang-format off
|
||||
addGlobalBinding(typeChecker, "foo",
|
||||
addGlobalBinding(frontend, "foo",
|
||||
arena.addType(
|
||||
FunctionTypeVar{
|
||||
listOfNumbers,
|
||||
@ -203,7 +203,7 @@ TEST_CASE_FIXTURE(Fixture, "variadic_packs")
|
||||
),
|
||||
"@test"
|
||||
);
|
||||
addGlobalBinding(typeChecker, "bar",
|
||||
addGlobalBinding(frontend, "bar",
|
||||
arena.addType(
|
||||
FunctionTypeVar{
|
||||
arena.addTypePack({{typeChecker.numberType}, listOfStrings}),
|
||||
|
@ -273,7 +273,7 @@ TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure")
|
||||
TypeId root = &ttvTweenResult;
|
||||
|
||||
typeChecker.currentModule = std::make_shared<Module>();
|
||||
typeChecker.currentModule->scopes.emplace_back(Location{}, std::make_shared<Scope>(getSingletonTypes().anyTypePack));
|
||||
typeChecker.currentModule->scopes.emplace_back(Location{}, std::make_shared<Scope>(singletonTypes->anyTypePack));
|
||||
|
||||
TypeId result = typeChecker.anyify(typeChecker.globalScope, root, Location{});
|
||||
|
||||
|
@ -10,9 +10,6 @@ local ignore =
|
||||
|
||||
-- what follows is a set of mismatches that hopefully eventually will go down to 0
|
||||
"_G.require", -- need to move to Roblox type defs
|
||||
"_G.utf8.nfcnormalize", -- need to move to Roblox type defs
|
||||
"_G.utf8.nfdnormalize", -- need to move to Roblox type defs
|
||||
"_G.utf8.graphemes", -- need to move to Roblox type defs
|
||||
}
|
||||
|
||||
function verify(real, rtti, path)
|
||||
|
@ -163,6 +163,7 @@ BuiltinTests.table_pack_reduce
|
||||
BuiltinTests.table_pack_variadic
|
||||
BuiltinTests.tonumber_returns_optional_number_type
|
||||
BuiltinTests.tonumber_returns_optional_number_type2
|
||||
DefinitionTests.class_definition_overload_metamethods
|
||||
DefinitionTests.declaring_generic_functions
|
||||
DefinitionTests.definition_file_classes
|
||||
FrontendTest.ast_node_at_position
|
||||
@ -199,7 +200,6 @@ GenericsTests.generic_functions_in_types
|
||||
GenericsTests.generic_functions_should_be_memory_safe
|
||||
GenericsTests.generic_table_method
|
||||
GenericsTests.generic_type_pack_parentheses
|
||||
GenericsTests.generic_type_pack_syntax
|
||||
GenericsTests.generic_type_pack_unification1
|
||||
GenericsTests.generic_type_pack_unification2
|
||||
GenericsTests.generic_type_pack_unification3
|
||||
@ -300,10 +300,8 @@ ProvisionalTests.operator_eq_completely_incompatible
|
||||
ProvisionalTests.pcall_returns_at_least_two_value_but_function_returns_nothing
|
||||
ProvisionalTests.setmetatable_constrains_free_type_into_free_table
|
||||
ProvisionalTests.typeguard_inference_incomplete
|
||||
ProvisionalTests.weird_fail_to_unify_type_pack
|
||||
ProvisionalTests.weirditer_should_not_loop_forever
|
||||
ProvisionalTests.while_body_are_also_refined
|
||||
ProvisionalTests.xpcall_returns_what_f_returns
|
||||
RefinementTest.and_constraint
|
||||
RefinementTest.and_or_peephole_refinement
|
||||
RefinementTest.apply_refinements_on_astexprindexexpr_whose_subscript_expr_is_constant_string
|
||||
@ -494,13 +492,10 @@ ToString.function_type_with_argument_names_generic
|
||||
ToString.no_parentheses_around_cyclic_function_type_in_union
|
||||
ToString.toStringDetailed2
|
||||
ToString.toStringErrorPack
|
||||
ToString.toStringNamedFunction_generic_pack
|
||||
ToString.toStringNamedFunction_hide_type_params
|
||||
ToString.toStringNamedFunction_id
|
||||
ToString.toStringNamedFunction_map
|
||||
ToString.toStringNamedFunction_overrides_param_names
|
||||
ToString.toStringNamedFunction_variadics
|
||||
TranspilerTests.type_lists_should_be_emitted_correctly
|
||||
TranspilerTests.types_should_not_be_considered_cyclic_if_they_are_not_recursive
|
||||
TryUnifyTests.cli_41095_concat_log_in_sealed_table_unification
|
||||
TryUnifyTests.members_of_failed_typepack_unification_are_unified_with_errorType
|
||||
@ -616,7 +611,6 @@ TypeInferFunctions.too_few_arguments_variadic_generic2
|
||||
TypeInferFunctions.too_many_arguments
|
||||
TypeInferFunctions.too_many_return_values
|
||||
TypeInferFunctions.vararg_function_is_quantified
|
||||
TypeInferFunctions.vararg_functions_should_allow_calls_of_any_types_and_size
|
||||
TypeInferLoops.for_in_loop_error_on_factory_not_returning_the_right_amount_of_values
|
||||
TypeInferLoops.for_in_loop_with_custom_iterator
|
||||
TypeInferLoops.for_in_loop_with_next
|
||||
@ -641,7 +635,6 @@ TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
||||
TypeInferOOP.inferred_methods_of_free_tables_have_the_same_level_as_the_enclosing_table
|
||||
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
||||
TypeInferOOP.methods_are_topologically_sorted
|
||||
TypeInferOOP.nonstrict_self_mismatch_tail
|
||||
TypeInferOperators.and_adds_boolean
|
||||
TypeInferOperators.and_adds_boolean_no_superfluous_union
|
||||
TypeInferOperators.and_binexps_dont_unify
|
||||
@ -689,6 +682,7 @@ TypeInferOperators.unary_not_is_boolean
|
||||
TypeInferOperators.unknown_type_in_comparison
|
||||
TypeInferOperators.UnknownGlobalCompoundAssign
|
||||
TypeInferPrimitives.CheckMethodsOfNumber
|
||||
TypeInferPrimitives.singleton_types
|
||||
TypeInferPrimitives.string_function_other
|
||||
TypeInferPrimitives.string_index
|
||||
TypeInferPrimitives.string_length
|
||||
@ -730,7 +724,6 @@ TypePackTests.type_alias_type_packs_nested
|
||||
TypePackTests.type_pack_hidden_free_tail_infinite_growth
|
||||
TypePackTests.type_pack_type_parameters
|
||||
TypePackTests.varargs_inference_through_multiple_scopes
|
||||
TypePackTests.variadic_pack_syntax
|
||||
TypePackTests.variadic_packs
|
||||
TypeSingletons.bool_singleton_subtype
|
||||
TypeSingletons.bool_singletons
|
||||
|
Loading…
Reference in New Issue
Block a user